Upstream Paper
This commit is contained in:
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Eclipse stuff
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
|
||||
# netbeans
|
||||
/nbproject
|
||||
nb*.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
|
||||
|
||||
/world
|
||||
/logs
|
||||
|
||||
# Mac filesystem dust
|
||||
.DS_Store
|
||||
|
||||
# intellij
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
/src/main/resources/achievement
|
||||
/src/main/resources/lang
|
||||
51
CONTRIBUTING.md
Normal file
51
CONTRIBUTING.md
Normal file
@@ -0,0 +1,51 @@
|
||||
Creating a Pull Request
|
||||
-----------------------
|
||||
##### Pull Request Title Example
|
||||
The first line in a Pull Request(PR) message is an imperative statement briefly explaining what the PR is achieving.
|
||||
If the PR fixes a bug, or implements a new feature as requested from the [JIRA](http://hub.spigotmc.org/jira/), then it should reference that ticket.
|
||||
This is accomplished by simply type SPIGOT-####, where #### is the ticket number. (i.e. SPIGOT-3510).
|
||||
You can reference multiple tickets in a single commit message; for example: "SPIGOT-1, SPIGOT-2" without the quotes.
|
||||
|
||||
__For Example:__
|
||||
* SPIGOT-3510: Velocity broken for certain entities
|
||||
* MC-111753, SPIGOT-2971: Brewing stand not reloading
|
||||
|
||||
As you can see, Minecraft tickets can be referenced by including the appropriate ticket number (i.e. MC-111753)
|
||||
|
||||
##### Pull Request Message Expectations
|
||||
The body of a PR needs to describe how the ticket was resolved, or if there was no ticket, describe the problem itself.
|
||||
If a PR is for both Bukkit and CraftBukkit it should include a link to the appropriate PR. [Read this to learn how to link to a PR.](https://confluence.atlassian.com/bitbucketserver053/markdown-syntax-guide-938022413.html?utm_campaign=in-app-help&utm_medium=in-app-help&utm_source=stash#Markdownsyntaxguide-Linkingtopullrequests)
|
||||
|
||||
##### 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 Spigot organization.
|
||||
* Make sure your pull request meets our [code requirements.](README.md)
|
||||
* If you are fixing a JIRA ticket, be sure to update the ticket with a link to the pull request.
|
||||
* Ensure all changes (including in patches) have our Minimal Diff Policy in mind.
|
||||
|
||||
##### Pull Request Feedback
|
||||
Spigot has a lot of Pull Requests open. Some of these are old and no longer maintained. As a general rule, we do not delete PRs because they are old or abandoned.
|
||||
|
||||
You will eventually receive feedback on your Pull Requests. If the criticism is rough, do not take it personally. We have all started out, or just made bad PRs.
|
||||
Take the advice and use it to make yourself better. If you think someone is being malicious contact an [admin](https://www.spigotmc.org/XenStaff/#Administrator).
|
||||
|
||||
Feedback goes both ways. If you believe a feature should be present then be prepared to argue your case.
|
||||
Per our Code Requirements, formatting issues are almost always required to be fixed before a PR is accepted.
|
||||
|
||||
Although we have many discussions on Stash, there are also many discussions in the #spigot-dev channel of IRC.
|
||||
Join us there if you plan on contributing to Spigot!
|
||||
|
||||
##### Tips to Get Your Pull Request Accepted
|
||||
Making sure to follow the conventions!
|
||||
|
||||
* Your change should fit with Bukkit's goals.
|
||||
* Make sure you follow our conventions to the letter.
|
||||
* Check for formatting errors. They may be invisible, but [we notice.](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/298/diff)
|
||||
* 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 adequate testing material and/or proof.
|
||||
* For example: adding an event? Test it with a plugin and provide us with the source.
|
||||
* Make sure you follow our conventions to the letter.
|
||||
|
||||
__Note:__ The project is often on a code freeze leading up to the release of a Minecraft update in order to give the team a static code base to work with.
|
||||
165
LGPL.txt
Normal file
165
LGPL.txt
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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.
|
||||
674
LICENCE.txt
Normal file
674
LICENCE.txt
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
284
README.md
Normal file
284
README.md
Normal file
@@ -0,0 +1,284 @@
|
||||
CraftBukkit
|
||||
======
|
||||
An implementation of the [Bukkit](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit) plugin API for [Minecraft](https://minecraft.net/) servers, currently maintained by [SpigotMC](http://www.spigotmc.org/).
|
||||
|
||||
#### Index
|
||||
|
||||
* [Bug Reporting](#bug-reporting)
|
||||
* [Compilation](#compilation)
|
||||
* [Contributing](#contributing)
|
||||
* [Code Requirements](#code-requirements)
|
||||
* [Applying Patches](#applying-patches)
|
||||
* [Making Changes to Minecraft](#making-changes-to-minecraft)
|
||||
* [Minimal Diff Policy](#minimal-diff-policy)
|
||||
* [CraftBukkit Comments](#craftbukkit-comments)
|
||||
* [Creating Pull Requests](#creating-pull-requests)
|
||||
* [Useful Resources](#useful-resources)
|
||||
|
||||
Bug Reporting
|
||||
-------------
|
||||
The development team is very open to both bug and feature requests / suggestions. You can submit these on the [JIRA Issue Tracker](http://hub.spigotmc.org/jira/).
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
CraftBukkit is a Java program which uses [Maven 3](http://maven.apache.org/) for compilation. To compile fresh from Git, simply perform the following steps:
|
||||
|
||||
* Install Git using your preferred installation methods.
|
||||
* Download and run [BuildTools](https://www.spigotmc.org/wiki/buildtools/)
|
||||
|
||||
Some IDEs such as [NetBeans](https://netbeans.org/) can perform these steps for you. Any Maven capable Java IDE can be used to develop with CraftBukkit, however the current team's personal preference is to use NetBeans.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
Contributions of all sorts are welcome. To manage community contributions, we use the pull request functionality of Stash. In to gain access to Stash and create a pull request, you will first need to perform the following steps:
|
||||
|
||||
* Create an account on [JIRA](http://hub.spigotmc.org/jira/).
|
||||
* Fill in the [SpigotMC CLA](http://www.spigotmc.org/go/cla) and wait up to 24 hours for your Stash account to be activated. Please ensure that your username and email addresses match.
|
||||
* Log into Stash using your JIRA credentials.
|
||||
|
||||
Once you have performed these steps you can create a fork, push your code changes, and then submit it for review.
|
||||
|
||||
If you submit a PR involving both Bukkit and CraftBukkit, each PR should link the other.
|
||||
|
||||
Although the minimum requirement for compilation & usage is Java 8, we prefer all contributions to be written in Java 7 style code unless there is a compelling reason otherwise.
|
||||
|
||||
Code Requirements
|
||||
-----------------
|
||||
* For the most part, CraftBukkit and Bukkit use the [Sun/Oracle coding standards](http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-136057.html).
|
||||
* No tabs; use 4 spaces instead.
|
||||
* Empty lines should contain no spaces.
|
||||
* No trailing whitespaces.
|
||||
* No 80 character column limit, or 'weird' mid-statement newlines unless absolutely necessary.
|
||||
* The 80 character column limit still applies to documentation.
|
||||
* No one-line methods.
|
||||
* All major additions should have documentation.
|
||||
* Try to follow test driven development where available.
|
||||
* All code should be free of magic values. If this is not possible, it should be marked with a TODO comment indicating it should be addressed in the future.
|
||||
* If magic values are absolutely necessary for your change, what those values represent should be documented in the code as well as an explanation in the Pull Request description on why those values are necessary.
|
||||
* No unnecessary code changes. Look through all your changes before you submit it.
|
||||
* Do not attempt to fix multiple problems with a single patch or pull request.
|
||||
* Do not submit your personal changes to configuration files.
|
||||
* Avoid moving or renaming classes.
|
||||
|
||||
Bukkit/CraftBukkit employs [JUnit 4](http://www.vogella.com/articles/JUnit/article.html) for testing. Pull Requests(PR) should attempt to integrate within that framework as appropriate.
|
||||
Bukkit is a large project and what seems simple to a PR author at the time of writing may easily be overlooked 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 Spigot team to pull the PR.
|
||||
|
||||
* There needs to be a new line at the end of every file.
|
||||
* Imports should be organised in a logical manner.
|
||||
* Do not group packages unless already grouped.
|
||||
* All new imports should be within existing CraftBukkit comments if any are present. If not, make them.
|
||||
* __Absolutely no wildcard imports.__
|
||||
* If you only use an import once, don't import it. Use the fully qualified name.
|
||||
|
||||
Any questions about these requirements can be asked in #spigot-dev in IRC.
|
||||
|
||||
Applying Patches
|
||||
----------------
|
||||
Any time new patches are created and/or updated in CraftBukkit, you need to apply them to your development environment.
|
||||
|
||||
1. Pull changes from CraftBukkit repo.
|
||||
2. Run the `applyPatches.sh` script in the CraftBukkit directory.
|
||||
- This script requires a decompile directory from BuildTools. (e.g. <BuildTools Directory>/work/decompile-XXXXXX)
|
||||
3. Your development environment is now up to date with the latest CraftBukkit patches!
|
||||
|
||||
Making Changes to Minecraft
|
||||
---------------------------
|
||||
Importing new NMS classes to CraftBukkit is actually very simple.
|
||||
|
||||
1. Find the `work/decompile-XXXXXX` folder in your BuildTools folder.
|
||||
2. Find the class you want to add in the `net/minecraft/server` folder and copy it.
|
||||
3. Copy the selected file to the `src/main/java/net/minecraft/server` folder in CraftBukkit.
|
||||
4. Implement changes.
|
||||
5. Run `makePatches.sh` to create a new patch for the new class.
|
||||
* _Be sure that Git recognizes the new file. This might require manually adding the file._
|
||||
6. Commit new patch.
|
||||
|
||||
Done! You have added a new patch for CraftBukkit!
|
||||
|
||||
**Making Changes to NMS Classes**
|
||||
|
||||
Bukkit/CB employs a Minimal Diff policy to help guide when changes should be changed to Minecraft and what those changes should be.
|
||||
This is to ensure that any changes have the smallest impact possible on the update process whenever a new Minecraft version is released.
|
||||
As well as the Minimal Diff Policy, *every* change made to a Minecraft class must be marked with the appropriate CraftBukkit comment.
|
||||
At no point should you rename an existing/obfuscated field or method. All references to existing/obfusacted fields/methods should be marked with the `// PAIL rename` comment.
|
||||
Mapping of new fields/methods are done when there are enough changes to warrant the work. (Any questions can be asked in #spigot-dev in [IRC](https://www.spigotmc.org/wiki/irc-guide/))
|
||||
|
||||
__*Key Points*__:
|
||||
* All additions to patches must be accompanied by an appropriate comment.
|
||||
* To avoid large patches, avoid adding methods where possible. We prefer making fields public over adding methods in patches.
|
||||
* If you *have* to add a method to a patch, please explain why in the Pull Request description.
|
||||
* __Never__ rename an existing field or method. If you want something renamed, include a ```PAIL rename``` comment
|
||||
* Converting a method/class from one access level to another (i.e. private to public) is fine as long as that method is not overridden in subclasses.
|
||||
* If a method is overridden in its' subclasses, create a new method that calls that method instead (along with appropriate CraftBukkit comments).
|
||||
* If you can use a field to accomplish something, use that over creating a new method.
|
||||
|
||||
Minimal Diff Policy
|
||||
-------------------
|
||||
|
||||
The Minimal Diff Policy is key to any changes made within Minecraft classes. 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 is not what is meant by "minimal diffs". Instead, when trying to understand this policy, it helps to keep in mind its goal: to reduce the impact of changes we make
|
||||
to Minecraft's internals have on our update process.
|
||||
|
||||
To put it 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.
|
||||
* All changes must be surrounded by [CraftBukkit comments](#craftbukkit-comments).
|
||||
* If you only use an import once within a class, don't import it and use a fully qualified name instead.
|
||||
* Try to employ "short-circuiting" of logic if at all possible. This means you should force a conditional to be the value needed to side step the code block to achieve your desired effect.
|
||||
|
||||
__For example, to short circuit this:__
|
||||
```java
|
||||
if (!this.world.isClientSide && !this.isDead && (d0*d0) + d1 + (d2*d2) > 0.0D) {
|
||||
this.die();
|
||||
this.h();
|
||||
}
|
||||
```
|
||||
__You would do this:__
|
||||
```java
|
||||
if (false && !this.world.isClientSide && !this.isDead && (d0*d0) + d1 + (d2*d2) > 0.0D) {
|
||||
this.die();
|
||||
this.h();
|
||||
}
|
||||
```
|
||||
|
||||
* When adding a validation check, this applies everywhere not just in Minecraft classes, see if the Validate package has a better, more concise method you can use instead.
|
||||
* The Preconditions package works just as well. Either are acceptable; though these are, by no means, the only accepted validation strategies.
|
||||
|
||||
__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 trying to make involves removing code, or delegating it somewhere else, instead of removing it, 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, 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 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, 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### CraftBukkit Comments
|
||||
|
||||
Changes to a Minecraft class should be clearly marked using CraftBukkit comments.
|
||||
|
||||
* All CraftBukkit comments should be capitalised appropriately. (i.e. CraftBukkit, not CB/craftBukkit, etc.)
|
||||
* If the change only affects one line of code, use an end of line CraftBukkit comment
|
||||
|
||||
__Examples:__
|
||||
|
||||
If the change is obvious, then you need a simple end of line comment.
|
||||
```java
|
||||
if (true || minecraftserver.getAllowNether()) { // CraftBukkit
|
||||
```
|
||||
|
||||
Every reference to an obfuscated field/method in NMS should be marked with:
|
||||
```java
|
||||
// PAIL rename newName
|
||||
```
|
||||
All PAIL rename comments must include a new name.
|
||||
|
||||
If, however, the change is something important to note or difficult to discern, you should include a reason at the end of the comment
|
||||
```java
|
||||
public int fireTicks; // PAIL private -> public
|
||||
```
|
||||
Changing access levels must include a PAIL comment indicating the previous access level and the new access level.
|
||||
|
||||
If adding the CraftBukkit comment negatively affects the readability of the code, then you should place the 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.
|
||||
|
||||
__Example:__
|
||||
|
||||
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.
|
||||
*If the change is something important to note or difficult to discern, you should include a reason at the end of line CraftBukkit comment.*
|
||||
```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, 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, if the change is obvious, such as firing an event, then you can simply use a multi-line comment.
|
||||
```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);
|
||||
}
|
||||
// CraftBukkit end
|
||||
```
|
||||
* All CraftBukkit comments should be on the same indentation level the code block it is in.
|
||||
|
||||
__Imports in Minecraft Classes__
|
||||
* Do not remove unused imports if they are not marked by CraftBukkit comments.
|
||||
|
||||
Creating Pull Requests
|
||||
----------------------
|
||||
To learn what Spigot expects of a Pull Request please view the [Contributing guidelines](CONTRIBUTING.md)
|
||||
|
||||
Useful Resources
|
||||
----------------
|
||||
|
||||
* [An example pull request demonstrating the things we look out for](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/365/overview)
|
||||
* [JIRA, our bug tracker](http://hub.spigotmc.org/jira/)
|
||||
* [Join us on IRC - #spigot-dev @ irc.spi.gt](https://www.spigotmc.org/wiki/irc-guide/)
|
||||
322
pom.xml
Normal file
322
pom.xml
Normal file
@@ -0,0 +1,322 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>paper</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.13.2-R0.1-SNAPSHOT</version>
|
||||
<name>Paper</name>
|
||||
<url>https://papermc.io</url>
|
||||
|
||||
<properties>
|
||||
<!-- <skipTests>true</skipTests> Paper - This [was] not going to end well -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<api.version>unknown</api.version>
|
||||
<minecraft.version>1.13.2</minecraft.version>
|
||||
<minecraft_version>1_13_R2</minecraft_version>
|
||||
<buildtag.prefix>git-Bukkit-</buildtag.prefix>
|
||||
<buildtag.suffix></buildtag.suffix>
|
||||
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-parent</artifactId>
|
||||
<version>dev-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>minecraft-server</artifactId>
|
||||
<version>${minecraft.version}-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>terminalconsoleappender</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>4.5.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!--
|
||||
Required to add the missing Log4j2Plugins.dat file from log4j-core
|
||||
which has been removed by Mojang. Without it, log4j has to classload
|
||||
all its classes to check if they are plugins.
|
||||
Scanning takes about 1-2 seconds so adding this speeds up the server start.
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-iostreams</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
<!-- Paper - Async loggers -->
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<version>3.4.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>7.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.25.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.47</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<!--
|
||||
If you are a plugin developer, please use https://hub.spigotmc.org/nexus/content/repositories/snapshots/
|
||||
as your repository URL. This will ensure only Bukkit / Spigot-API are pulled from our Maven repository.
|
||||
|
||||
Please see https://www.spigotmc.org/go/maven for more information.
|
||||
-->
|
||||
<repository>
|
||||
<id>spigotmc-public</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spigotmc-public</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<!-- This builds a completely 'ready to start' jar with all dependencies inside -->
|
||||
<build>
|
||||
<finalName>paper-${minecraft.version}</finalName>
|
||||
<defaultGoal>clean install</defaultGoal> <!-- Paper -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.lukegb.mojo</groupId>
|
||||
<artifactId>gitdescribe-maven-plugin</artifactId>
|
||||
<version>1.3</version>
|
||||
<configuration>
|
||||
<outputPrefix>git-Paper-</outputPrefix>
|
||||
<scmDirectory>..</scmDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>gitdescribe</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<forceCreation>true</forceCreation> <!-- Required to prevent shading the jar multiple times -->
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>org.bukkit.craftbukkit.Main</Main-Class>
|
||||
<Implementation-Title>CraftBukkit</Implementation-Title>
|
||||
<!--suppress MavenModelInspection -->
|
||||
<Implementation-Version>${describe}</Implementation-Version>
|
||||
<Implementation-Vendor>${maven.build.timestamp}</Implementation-Vendor>
|
||||
<Specification-Title>Bukkit</Specification-Title>
|
||||
<Specification-Version>${api.version}</Specification-Version>
|
||||
<Specification-Vendor>Bukkit Team</Specification-Vendor>
|
||||
</manifestEntries>
|
||||
<manifestSections>
|
||||
<manifestSection>
|
||||
<name>net/bukkit/</name>
|
||||
<manifestEntries>
|
||||
<Sealed>true</Sealed>
|
||||
</manifestEntries>
|
||||
</manifestSection>
|
||||
<manifestSection>
|
||||
<name>com/bukkit/</name>
|
||||
<manifestEntries>
|
||||
<Sealed>true</Sealed>
|
||||
</manifestEntries>
|
||||
</manifestSection>
|
||||
<manifestSection>
|
||||
<name>org/bukkit/</name>
|
||||
<manifestEntries>
|
||||
<Sealed>true</Sealed>
|
||||
</manifestEntries>
|
||||
</manifestSection>
|
||||
</manifestSections>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper -->
|
||||
<createSourcesJar>${shadeSourcesJar}</createSourcesJar>
|
||||
<relocations>
|
||||
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
|
||||
<!--<relocation>-->
|
||||
<!--<pattern>joptsimple</pattern>-->
|
||||
<!--<shadedPattern>org.bukkit.craftbukkit.libs.joptsimple</shadedPattern>-->
|
||||
<!--</relocation>-->
|
||||
<relocation>
|
||||
<pattern>jline</pattern>
|
||||
<shadedPattern>org.bukkit.craftbukkit.libs.jline</shadedPattern>
|
||||
</relocation>
|
||||
<!-- Paper - Don't relocate fastutil in order to prevent api breakage -->
|
||||
<!--<relocation>-->
|
||||
<!--<pattern>it.unimi</pattern>-->
|
||||
<!--<shadedPattern>org.bukkit.craftbukkit.libs.it.unimi</shadedPattern>-->
|
||||
<!--</relocation>-->
|
||||
<relocation>
|
||||
<pattern>org.bukkit.craftbukkit</pattern>
|
||||
<shadedPattern>org.bukkit.craftbukkit.v${minecraft_version}</shadedPattern>
|
||||
<excludes>
|
||||
<exclude>org.bukkit.craftbukkit.Main*</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.minecraft.server</pattern>
|
||||
<shadedPattern>net.minecraft.server.v${minecraft_version}</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/services/java.sql.Driver</resource>
|
||||
</transformer>
|
||||
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.edwgiz</groupId>
|
||||
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
|
||||
<version>2.8.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<dependencies>
|
||||
<!-- we need our custom version as it fixes some bugs on case sensitive file systems -->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-compiler-eclipse</artifactId>
|
||||
<version>2.8.5-spigotmc</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<workingDirectory>${basedir}/target/test-server</workingDirectory>
|
||||
<excludes>
|
||||
<exclude>org/bukkit/craftbukkit/inventory/ItemStack*Test.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>shadeSourcesJar</id>
|
||||
<properties>
|
||||
<shadeSourcesJar>true</shadeSourcesJar>
|
||||
<shadeSourcesContent>true</shadeSourcesContent>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>development</id>
|
||||
<properties>
|
||||
<skipTests>false</skipTests>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.17</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<signature>
|
||||
<groupId>org.codehaus.mojo.signature</groupId>
|
||||
<artifactId>java18</artifactId>
|
||||
<version>1.0</version>
|
||||
</signature>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
132
src/main/java/co/aikar/timings/MinecraftTimings.java
Normal file
132
src/main/java/co/aikar/timings/MinecraftTimings.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import net.minecraft.server.*;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import org.bukkit.craftbukkit.scheduler.CraftTask;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class MinecraftTimings {
|
||||
|
||||
public static final Timing playerListTimer = Timings.ofSafe("Player List");
|
||||
public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions");
|
||||
public static final Timing connectionTimer = Timings.ofSafe("Connection Handler");
|
||||
public static final Timing tickablesTimer = Timings.ofSafe("Tickables");
|
||||
public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler");
|
||||
public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler");
|
||||
public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending");
|
||||
public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing");
|
||||
public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick");
|
||||
public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update");
|
||||
public static final Timing serverCommandTimer = Timings.ofSafe("Server Command");
|
||||
public static final Timing savePlayers = Timings.ofSafe("Save Players");
|
||||
|
||||
public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity");
|
||||
public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity");
|
||||
public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing");
|
||||
public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks");
|
||||
public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation");
|
||||
|
||||
public static final Timing processQueueTimer = Timings.ofSafe("processQueue");
|
||||
|
||||
public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand");
|
||||
|
||||
public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck");
|
||||
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
|
||||
private static final Map<Class<?>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private MinecraftTimings() {}
|
||||
|
||||
/**
|
||||
* Gets a timer associated with a plugins tasks.
|
||||
* @param bukkitTask
|
||||
* @param period
|
||||
* @return
|
||||
*/
|
||||
public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) {
|
||||
if (!bukkitTask.isSync()) {
|
||||
return null;
|
||||
}
|
||||
Plugin plugin;
|
||||
|
||||
CraftTask craftTask = (CraftTask) bukkitTask;
|
||||
|
||||
final Class<?> taskClass = craftTask.getTaskClass();
|
||||
if (bukkitTask.getOwner() != null) {
|
||||
plugin = bukkitTask.getOwner();
|
||||
} else {
|
||||
plugin = TimingsManager.getPluginByClassloader(taskClass);
|
||||
}
|
||||
|
||||
final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz ->
|
||||
clazz.isAnonymousClass() || clazz.isLocalClass()
|
||||
? clazz.getName()
|
||||
: clazz.getCanonicalName());
|
||||
|
||||
StringBuilder name = new StringBuilder(64);
|
||||
name.append("Task: ").append(taskname);
|
||||
if (period > 0) {
|
||||
name.append(" (interval:").append(period).append(")");
|
||||
} else {
|
||||
name.append(" (Single)");
|
||||
}
|
||||
|
||||
if (plugin == null) {
|
||||
return Timings.ofSafe(null, name.toString());
|
||||
}
|
||||
|
||||
return Timings.ofSafe(plugin, name.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getEntityTimings(Entity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified tile entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getTileEntityTimings(TileEntity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer);
|
||||
}
|
||||
public static Timing getCancelTasksTimer() {
|
||||
return Timings.ofSafe("Cancel Tasks");
|
||||
}
|
||||
public static Timing getCancelTasksTimer(Plugin plugin) {
|
||||
return Timings.ofSafe(plugin, "Cancel Tasks");
|
||||
}
|
||||
|
||||
public static void stopServer() {
|
||||
TimingsManager.stopServer();
|
||||
}
|
||||
|
||||
public static Timing getBlockTiming(Block block) {
|
||||
return Timings.ofSafe("## Scheduled Block: " + block.toString(), scheduledBlocksTimer);
|
||||
}
|
||||
/*
|
||||
public static Timing getStructureTiming(StructureGenerator structureGenerator) {
|
||||
return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer);
|
||||
}*/
|
||||
|
||||
public static Timing getPacketTiming(Packet packet) {
|
||||
return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer);
|
||||
}
|
||||
|
||||
public static Timing getCommandFunctionTiming(CustomFunction function) {
|
||||
return Timings.ofSafe("Command Function - " + function.getMinecraftKey().toString());
|
||||
}
|
||||
}
|
||||
108
src/main/java/co/aikar/timings/WorldTimingsHandler.java
Normal file
108
src/main/java/co/aikar/timings/WorldTimingsHandler.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
/**
|
||||
* Set of timers per world, to track world specific timings.
|
||||
*/
|
||||
public class WorldTimingsHandler {
|
||||
public final Timing mobSpawn;
|
||||
public final Timing doChunkUnload;
|
||||
public final Timing doPortalForcer;
|
||||
public final Timing scheduledBlocks;
|
||||
public final Timing scheduledBlocksCleanup;
|
||||
public final Timing scheduledBlocksTicking;
|
||||
public final Timing chunkTicks;
|
||||
public final Timing lightChunk;
|
||||
public final Timing chunkTicksBlocks;
|
||||
public final Timing doVillages;
|
||||
public final Timing doChunkMap;
|
||||
public final Timing doChunkMapUpdate;
|
||||
public final Timing doChunkMapToUpdate;
|
||||
public final Timing doChunkMapSortMissing;
|
||||
public final Timing doChunkMapSortSendToPlayers;
|
||||
public final Timing doChunkMapPlayersNeedingChunks;
|
||||
public final Timing doChunkMapPendingSendToPlayers;
|
||||
public final Timing doChunkMapUnloadChunks;
|
||||
public final Timing doChunkGC;
|
||||
public final Timing doSounds;
|
||||
public final Timing entityRemoval;
|
||||
public final Timing entityTick;
|
||||
public final Timing tileEntityTick;
|
||||
public final Timing tileEntityPending;
|
||||
public final Timing tracker1;
|
||||
public final Timing tracker2;
|
||||
public final Timing doTick;
|
||||
public final Timing tickEntities;
|
||||
|
||||
public final Timing syncChunkLoadTimer;
|
||||
public final Timing syncChunkLoadDataTimer;
|
||||
public final Timing syncChunkLoadStructuresTimer;
|
||||
public final Timing syncChunkLoadPostTimer;
|
||||
public final Timing syncChunkLoadPopulateTimer;
|
||||
public final Timing chunkLoadLevelTimer;
|
||||
public final Timing chunkGeneration;
|
||||
public final Timing chunkIOStage1;
|
||||
public final Timing chunkIOStage2;
|
||||
public final Timing worldSave;
|
||||
public final Timing worldSaveChunks;
|
||||
public final Timing worldSaveLevel;
|
||||
public final Timing chunkSaveData;
|
||||
|
||||
public final Timing lightingQueueTimer;
|
||||
|
||||
public WorldTimingsHandler(World server) {
|
||||
String name = server.worldData.getName() +" - ";
|
||||
|
||||
mobSpawn = Timings.ofSafe(name + "mobSpawn");
|
||||
doChunkUnload = Timings.ofSafe(name + "doChunkUnload");
|
||||
scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks");
|
||||
scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup");
|
||||
scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking");
|
||||
chunkTicks = Timings.ofSafe(name + "Chunk Ticks");
|
||||
lightChunk = Timings.ofSafe(name + "Light Chunk");
|
||||
chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks");
|
||||
doVillages = Timings.ofSafe(name + "doVillages");
|
||||
doChunkMap = Timings.ofSafe(name + "doChunkMap");
|
||||
doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update");
|
||||
doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update");
|
||||
doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing");
|
||||
doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players");
|
||||
doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks");
|
||||
doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players");
|
||||
doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks");
|
||||
doSounds = Timings.ofSafe(name + "doSounds");
|
||||
doChunkGC = Timings.ofSafe(name + "doChunkGC");
|
||||
doPortalForcer = Timings.ofSafe(name + "doPortalForcer");
|
||||
entityTick = Timings.ofSafe(name + "entityTick");
|
||||
entityRemoval = Timings.ofSafe(name + "entityRemoval");
|
||||
tileEntityTick = Timings.ofSafe(name + "tileEntityTick");
|
||||
tileEntityPending = Timings.ofSafe(name + "tileEntityPending");
|
||||
|
||||
syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad");
|
||||
syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data");
|
||||
syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures");
|
||||
syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post");
|
||||
syncChunkLoadPopulateTimer = Timings.ofSafe(name + "chunkLoad - Populate");
|
||||
chunkLoadLevelTimer = Timings.ofSafe(name + "chunkLoad - Load Level");
|
||||
chunkGeneration = Timings.ofSafe(name + "chunkGeneration");
|
||||
chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO");
|
||||
chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load");
|
||||
worldSave = Timings.ofSafe(name + "World Save");
|
||||
worldSaveLevel = Timings.ofSafe(name + "World Save - Level");
|
||||
worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks");
|
||||
chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data");
|
||||
|
||||
tracker1 = Timings.ofSafe(name + "tracker stage 1");
|
||||
tracker2 = Timings.ofSafe(name + "tracker stage 2");
|
||||
doTick = Timings.ofSafe(name + "doTick");
|
||||
tickEntities = Timings.ofSafe(name + "tickEntities");
|
||||
|
||||
lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue");
|
||||
}
|
||||
|
||||
public static Timing getTickList(WorldServer worldserver, String timingsType) {
|
||||
return Timings.ofSafe(worldserver.getWorldData().getName() + " - Scheduled " + timingsType);
|
||||
}
|
||||
}
|
||||
627
src/main/java/com/destroystokyo/paper/Metrics.java
Normal file
627
src/main/java/com/destroystokyo/paper/Metrics.java
Normal file
@@ -0,0 +1,627 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
*
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
public class Metrics {
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/server-implementation";
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests = false;
|
||||
|
||||
// The logger for the failed requests
|
||||
private static Logger logger = Logger.getLogger("bStats");
|
||||
|
||||
// The name of the server software
|
||||
private final String name;
|
||||
|
||||
// The uuid of the server
|
||||
private final String serverUUID;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param name The name of the server software.
|
||||
* @param serverUUID The uuid of the server.
|
||||
* @param logFailedRequests Whether failed requests should be logged or not.
|
||||
* @param logger The logger for the failed requests.
|
||||
*/
|
||||
public Metrics(String name, String serverUUID, boolean logFailedRequests, Logger logger) {
|
||||
this.name = name;
|
||||
this.serverUUID = serverUUID;
|
||||
Metrics.logFailedRequests = logFailedRequests;
|
||||
Metrics.logger = logger;
|
||||
|
||||
// Start submitting the data
|
||||
startSubmitting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true);
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
submitData();
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
private JSONObject getPluginData() {
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("pluginName", name); // Append the name of the server software
|
||||
JSONArray customCharts = new JSONArray();
|
||||
for (CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
JSONObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.put("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JSONObject getServerData() {
|
||||
// OS specific data
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("serverUUID", serverUUID);
|
||||
|
||||
data.put("osName", osName);
|
||||
data.put("osArch", osArch);
|
||||
data.put("osVersion", osVersion);
|
||||
data.put("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JSONObject data = getServerData();
|
||||
|
||||
JSONArray pluginData = new JSONArray();
|
||||
pluginData.add(getPluginData());
|
||||
data.put("plugins", pluginData);
|
||||
|
||||
try {
|
||||
// We are still in the Thread of the timer, so nothing get blocked :)
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
logger.log(Level.WARNING, "Could not submit stats of " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(JSONObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes("UTF-8"));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JSONObject getRequestJsonObject() {
|
||||
JSONObject chart = new JSONObject();
|
||||
chart.put("chartId", chartId);
|
||||
try {
|
||||
JSONObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.put("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JSONObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JSONObject value = new JSONObject();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
value.put(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.put(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
categoryValues.add(entry.getValue());
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
for (int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(categoryValue);
|
||||
}
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PaperMetrics {
|
||||
static void startMetrics() {
|
||||
// Get the config file
|
||||
File configFile = new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "bStats"), "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly no effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// Load the data
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
// Only start Metrics, if it's enabled in the config
|
||||
if (config.getBoolean("enabled", true)) {
|
||||
Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
|
||||
|
||||
metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
|
||||
String minecraftVersion = Bukkit.getVersion();
|
||||
minecraftVersion = minecraftVersion.substring(minecraftVersion.indexOf("MC: ") + 4, minecraftVersion.length() - 1);
|
||||
return minecraftVersion;
|
||||
}));
|
||||
|
||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline"));
|
||||
metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"));
|
||||
|
||||
metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
||||
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
Map<String, Integer> entry = new HashMap<>();
|
||||
entry.put(javaVersion, 1);
|
||||
|
||||
// http://openjdk.java.net/jeps/223
|
||||
// Java decided to change their versioning scheme and in doing so modified the java.version system
|
||||
// property to return $major[.$minor][.$secuity][-ea], as opposed to 1.$major.0_$identifier
|
||||
// we can handle pre-9 by checking if the "major" is equal to "1", otherwise, 9+
|
||||
String majorVersion = javaVersion.split("\\.")[0];
|
||||
String release;
|
||||
|
||||
int indexOf = javaVersion.lastIndexOf('.');
|
||||
|
||||
if (majorVersion.equals("1")) {
|
||||
release = "Java " + javaVersion.substring(0, indexOf);
|
||||
} else {
|
||||
// of course, it really wouldn't be all that simple if they didn't add a quirk, now would it
|
||||
// valid strings for the major may potentially include values such as -ea to deannotate a pre release
|
||||
Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion);
|
||||
if (versionMatcher.find()) {
|
||||
majorVersion = versionMatcher.group(0);
|
||||
}
|
||||
release = "Java " + majorVersion;
|
||||
}
|
||||
map.put(release, entry);
|
||||
|
||||
return map;
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
238
src/main/java/com/destroystokyo/paper/PaperCommand.java
Normal file
238
src/main/java/com/destroystokyo/paper/PaperCommand.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.minecraft.server.*;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
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.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
this.description = "Paper related commands";
|
||||
this.usageMessage = "/paper [heap | entity | reload | version]";
|
||||
this.setPermission("bukkit.command.paper");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
||||
if (args.length <= 1)
|
||||
return getListMatchingLast(args, "heap", "entity", "reload", "version");
|
||||
|
||||
switch (args[0].toLowerCase(Locale.ENGLISH))
|
||||
{
|
||||
case "entity":
|
||||
if (args.length == 2)
|
||||
return getListMatchingLast(args, "help", "list");
|
||||
if (args.length == 3)
|
||||
return getListMatchingLast(args, EntityTypes.getEntityNameList().stream().map(MinecraftKey::toString).sorted().toArray(String[]::new));
|
||||
break;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// Code from Mojang - copyright them
|
||||
public static List<String> getListMatchingLast(String[] args, String... matches) {
|
||||
return getListMatchingLast(args, (Collection) Arrays.asList(matches));
|
||||
}
|
||||
|
||||
public static boolean matches(String s, String s1) {
|
||||
return s1.regionMatches(true, 0, s, 0, s.length());
|
||||
}
|
||||
|
||||
public static List<String> getListMatchingLast(String[] strings, Collection<?> collection) {
|
||||
String last = strings[strings.length - 1];
|
||||
ArrayList<String> results = Lists.newArrayList();
|
||||
|
||||
if (!collection.isEmpty()) {
|
||||
Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
String s1 = (String) iterator.next();
|
||||
|
||||
if (matches(last, s1)) {
|
||||
results.add(s1);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
iterator = collection.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Object object = iterator.next();
|
||||
|
||||
if (object instanceof MinecraftKey && matches(last, ((MinecraftKey) object).getKey())) {
|
||||
results.add(String.valueOf(object));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
// end copy stuff
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||
if (!testPermission(sender)) return true;
|
||||
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase(Locale.ENGLISH)) {
|
||||
case "heap":
|
||||
dumpHeap(sender);
|
||||
break;
|
||||
case "entity":
|
||||
listEntities(sender, args);
|
||||
break;
|
||||
case "reload":
|
||||
doReload(sender);
|
||||
break;
|
||||
case "ver":
|
||||
case "version":
|
||||
org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version").execute(sender, commandLabel, new String[0]);
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1
|
||||
*/
|
||||
private void listEntities(CommandSender sender, String[] args) {
|
||||
if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
sender.sendMessage(ChatColor.RED + "Use /paper entity [list] help for more information on a specific command.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[1].toLowerCase(Locale.ENGLISH)) {
|
||||
case "list":
|
||||
String filter = "*";
|
||||
if (args.length > 2) {
|
||||
if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
sender.sendMessage(ChatColor.RED + "Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter.");
|
||||
return;
|
||||
}
|
||||
filter = args[2];
|
||||
}
|
||||
final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
|
||||
Set<MinecraftKey> names = EntityTypes.getEntityNameList().stream()
|
||||
.filter(n -> n.toString().matches(cleanfilter))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (names.isEmpty()) {
|
||||
sender.sendMessage(ChatColor.RED + "Invalid filter, does not match any entities. Use /paper entity list for a proper list");
|
||||
return;
|
||||
}
|
||||
|
||||
String worldName;
|
||||
if (args.length > 3) {
|
||||
worldName = args[3];
|
||||
} else if (sender instanceof Player) {
|
||||
worldName = ((Player) sender).getWorld().getName();
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Please specify the name of a world");
|
||||
sender.sendMessage(ChatColor.RED + "To do so without a filter, specify '*' as the filter");
|
||||
return;
|
||||
}
|
||||
|
||||
Map<MinecraftKey, MutablePair<Integer, Map<ChunkCoordIntPair, Integer>>> list = Maps.newHashMap();
|
||||
World bukkitWorld = Bukkit.getWorld(worldName);
|
||||
if (bukkitWorld == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Could not load world for " + worldName + ". Please select a valid world.");
|
||||
return;
|
||||
}
|
||||
WorldServer world = ((CraftWorld) Bukkit.getWorld(worldName)).getHandle();
|
||||
|
||||
List<Entity> entities = world.entityList;
|
||||
entities.forEach(e -> {
|
||||
MinecraftKey key = e.getMinecraftKey();
|
||||
if (e.shouldBeRemoved) return; // Paper
|
||||
|
||||
MutablePair<Integer, Map<ChunkCoordIntPair, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));
|
||||
ChunkCoordIntPair chunk = new ChunkCoordIntPair(e.getChunkX(), e.getChunkZ());
|
||||
info.left++;
|
||||
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
||||
});
|
||||
|
||||
if (names.size() == 1) {
|
||||
MinecraftKey name = names.iterator().next();
|
||||
Pair<Integer, Map<ChunkCoordIntPair, Integer>> info = list.get(name);
|
||||
if (info == null) {
|
||||
sender.sendMessage(ChatColor.RED + "No entities found.");
|
||||
return;
|
||||
}
|
||||
sender.sendMessage("Entity: " + name + " Total: " + info.getLeft());
|
||||
info.getRight().entrySet().stream()
|
||||
.sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString()))
|
||||
.limit(10).forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z));
|
||||
} else {
|
||||
List<Pair<MinecraftKey, Integer>> info = list.entrySet().stream()
|
||||
.filter(e -> names.contains(e.getKey()))
|
||||
.map(e -> Pair.of(e.getKey(), e.getValue().left))
|
||||
.sorted((a, b) -> !a.getRight().equals(b.getRight()) ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (info == null || info.size() == 0) {
|
||||
sender.sendMessage(ChatColor.RED + "No entities found.");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = info.stream().mapToInt(Pair::getRight).sum();
|
||||
sender.sendMessage("Total: " + count);
|
||||
info.forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpHeap(CommandSender sender) {
|
||||
File file = new File(new File(new File("."), "dumps"),
|
||||
"heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + "-server.hprof");
|
||||
Command.broadcastCommandMessage(sender, ChatColor.YELLOW + "Writing JVM heap data to " + file);
|
||||
if (CraftServer.dumpHeap(file)) {
|
||||
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Heap dump complete");
|
||||
} else {
|
||||
Command.broadcastCommandMessage(sender, ChatColor.RED + "Failed to write heap dump, see sever log for details");
|
||||
}
|
||||
}
|
||||
|
||||
private void doReload(CommandSender sender) {
|
||||
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
|
||||
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
|
||||
|
||||
MinecraftServer console = MinecraftServer.getServer();
|
||||
com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings"));
|
||||
for (WorldServer world : console.getWorlds()) {
|
||||
world.paperConfig.init();
|
||||
}
|
||||
console.server.reloadCount++;
|
||||
|
||||
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete.");
|
||||
}
|
||||
}
|
||||
465
src/main/java/com/destroystokyo/paper/PaperConfig.java
Normal file
465
src/main/java/com/destroystokyo/paper/PaperConfig.java
Normal file
@@ -0,0 +1,465 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
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.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import co.aikar.timings.Timings;
|
||||
import co.aikar.timings.TimingsManager;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
import org.spigotmc.WatchdogThread;
|
||||
|
||||
public class PaperConfig {
|
||||
|
||||
private static File CONFIG_FILE;
|
||||
private static final String HEADER = "This is the main configuration file for Paper.\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 Paper,\n"
|
||||
+ "join us in our Discord or IRC channel.\n"
|
||||
+ "\n"
|
||||
+ "Discord: https://paperdiscord.emc.gs\n"
|
||||
+ "IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )\n"
|
||||
+ "Website: https://papermc.io/ \n"
|
||||
+ "Docs: https://paper.readthedocs.org/ \n";
|
||||
/*========================================================================*/
|
||||
public static YamlConfiguration config;
|
||||
static int version;
|
||||
static Map<String, Command> commands;
|
||||
private static boolean verbose;
|
||||
private static boolean fatalError;
|
||||
/*========================================================================*/
|
||||
private static boolean metricsStarted;
|
||||
|
||||
public static void init(File configFile) {
|
||||
CONFIG_FILE = configFile;
|
||||
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);
|
||||
verbose = getBoolean("verbose", false);
|
||||
|
||||
commands = new HashMap<String, Command>();
|
||||
commands.put("paper", new PaperCommand("paper"));
|
||||
|
||||
version = getInt("config-version", 17);
|
||||
set("config-version", 17);
|
||||
readConfig(PaperConfig.class, null);
|
||||
}
|
||||
|
||||
protected static void logError(String s) {
|
||||
Bukkit.getLogger().severe(s);
|
||||
}
|
||||
|
||||
protected static void fatal(String s) {
|
||||
fatalError = true;
|
||||
throw new RuntimeException("Fatal paper.yml config error: " + s);
|
||||
}
|
||||
|
||||
protected static void log(String s) {
|
||||
if (verbose) {
|
||||
Bukkit.getLogger().info(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerCommands() {
|
||||
for (Map.Entry<String, Command> entry : commands.entrySet()) {
|
||||
MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Paper", entry.getValue());
|
||||
}
|
||||
|
||||
if (!metricsStarted) {
|
||||
Metrics.PaperMetrics.startMetrics();
|
||||
metricsStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
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 final Pattern SPACE = Pattern.compile(" ");
|
||||
private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]");
|
||||
public static int getSeconds(String str) {
|
||||
str = SPACE.matcher(str).replaceAll("");
|
||||
final char unit = str.charAt(str.length() - 1);
|
||||
str = NOT_NUMERIC.matcher(str).replaceAll("");
|
||||
double num;
|
||||
try {
|
||||
num = Double.parseDouble(str);
|
||||
} catch (Exception e) {
|
||||
num = 0D;
|
||||
}
|
||||
switch (unit) {
|
||||
case 'd': num *= (double) 60*60*24; break;
|
||||
case 'h': num *= (double) 60*60; break;
|
||||
case 'm': num *= (double) 60; break;
|
||||
default: case 's': break;
|
||||
}
|
||||
return (int) num;
|
||||
}
|
||||
|
||||
protected static String timeSummary(int seconds) {
|
||||
String time = "";
|
||||
|
||||
if (seconds > 60 * 60 * 24) {
|
||||
time += TimeUnit.SECONDS.toDays(seconds) + "d";
|
||||
seconds %= 60 * 60 * 24;
|
||||
}
|
||||
|
||||
if (seconds > 60 * 60) {
|
||||
time += TimeUnit.SECONDS.toHours(seconds) + "h";
|
||||
seconds %= 60 * 60;
|
||||
}
|
||||
|
||||
if (seconds > 0) {
|
||||
time += TimeUnit.SECONDS.toMinutes(seconds) + "m";
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
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) {
|
||||
// TODO: Figure out why getFloat() always returns the default value.
|
||||
return (float) getDouble(path, (double) def);
|
||||
}
|
||||
|
||||
private static int getInt(String path, int def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getInt(path, config.getInt(path));
|
||||
}
|
||||
|
||||
private static <T> List getList(String path, T def) {
|
||||
config.addDefault(path, def);
|
||||
return (List<T>) 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 int maxTickMsLostLightQueue;
|
||||
private static void lightQueue() {
|
||||
int badSetting = config.getInt("queue-light-updates-max-loss", 10);
|
||||
config.set("queue-light-updates-max-loss", null);
|
||||
maxTickMsLostLightQueue = getInt("settings.queue-light-updates-max-loss", badSetting);
|
||||
}
|
||||
|
||||
private static void timings() {
|
||||
boolean timings = getBoolean("timings.enabled", true);
|
||||
boolean verboseTimings = getBoolean("timings.verbose", true);
|
||||
TimingsManager.privacy = getBoolean("timings.server-name-privacy", false);
|
||||
TimingsManager.hiddenConfigs = getList("timings.hidden-config-entries", Lists.newArrayList("database", "settings.bungeecord-addresses"));
|
||||
int timingHistoryInterval = getInt("timings.history-interval", 300);
|
||||
int timingHistoryLength = getInt("timings.history-length", 3600);
|
||||
|
||||
|
||||
Timings.setVerboseTimingsEnabled(verboseTimings);
|
||||
Timings.setTimingsEnabled(timings);
|
||||
Timings.setHistoryInterval(timingHistoryInterval * 20);
|
||||
Timings.setHistoryLength(timingHistoryLength * 20);
|
||||
|
||||
log("Timings: " + timings +
|
||||
" - Verbose: " + verboseTimings +
|
||||
" - Interval: " + timeSummary(Timings.getHistoryInterval() / 20) +
|
||||
" - Length: " + timeSummary(Timings.getHistoryLength() / 20));
|
||||
}
|
||||
|
||||
public static boolean enableFileIOThreadSleep;
|
||||
private static void enableFileIOThreadSleep() {
|
||||
enableFileIOThreadSleep = getBoolean("settings.sleep-between-chunk-saves", false);
|
||||
if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues");
|
||||
}
|
||||
|
||||
public static boolean loadPermsBeforePlugins = true;
|
||||
private static void loadPermsBeforePlugins() {
|
||||
loadPermsBeforePlugins = getBoolean("settings.load-permissions-yml-before-plugins", true);
|
||||
}
|
||||
|
||||
public static int regionFileCacheSize = 256;
|
||||
private static void regionFileCacheSize() {
|
||||
regionFileCacheSize = getInt("settings.region-file-cache-size", 256);
|
||||
}
|
||||
|
||||
public static boolean enablePlayerCollisions = true;
|
||||
private static void enablePlayerCollisions() {
|
||||
enablePlayerCollisions = getBoolean("settings.enable-player-collisions", true);
|
||||
}
|
||||
|
||||
public static boolean saveEmptyScoreboardTeams = false;
|
||||
private static void saveEmptyScoreboardTeams() {
|
||||
saveEmptyScoreboardTeams = getBoolean("settings.save-empty-scoreboard-teams", false);
|
||||
}
|
||||
|
||||
public static boolean bungeeOnlineMode = true;
|
||||
private static void bungeeOnlineMode() {
|
||||
bungeeOnlineMode = getBoolean("settings.bungee-online-mode", true);
|
||||
}
|
||||
|
||||
public static boolean isProxyOnlineMode() {
|
||||
return Bukkit.getOnlineMode() || (SpigotConfig.bungee && bungeeOnlineMode) || (velocitySupport && velocityOnlineMode);
|
||||
}
|
||||
|
||||
public static int packetInSpamThreshold = 300;
|
||||
private static void packetInSpamThreshold() {
|
||||
if (version < 11) {
|
||||
int oldValue = getInt("settings.play-in-use-item-spam-threshold", 300);
|
||||
set("settings.incoming-packet-spam-threshold", oldValue);
|
||||
}
|
||||
packetInSpamThreshold = getInt("settings.incoming-packet-spam-threshold", 300);
|
||||
}
|
||||
|
||||
public static String flyingKickPlayerMessage = "Flying is not enabled on this server";
|
||||
public static String flyingKickVehicleMessage = "Flying is not enabled on this server";
|
||||
private static void flyingKickMessages() {
|
||||
flyingKickPlayerMessage = getString("messages.kick.flying-player", flyingKickPlayerMessage);
|
||||
flyingKickVehicleMessage = getString("messages.kick.flying-vehicle", flyingKickVehicleMessage);
|
||||
}
|
||||
|
||||
public static int playerAutoSaveRate = -1;
|
||||
public static int maxPlayerAutoSavePerTick = 10;
|
||||
private static void playerAutoSaveRate() {
|
||||
playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1);
|
||||
maxPlayerAutoSavePerTick = getInt("settings.max-player-auto-save-per-tick", -1);
|
||||
if (maxPlayerAutoSavePerTick == -1) { // -1 Automatic / "Recommended"
|
||||
// 10 should be safe for everyone unless your mass spamming player auto save
|
||||
maxPlayerAutoSavePerTick = (playerAutoSaveRate == -1 || playerAutoSaveRate > 100) ? 10 : 20;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean suggestPlayersWhenNullTabCompletions = true;
|
||||
private static void suggestPlayersWhenNull() {
|
||||
suggestPlayersWhenNullTabCompletions = getBoolean("settings.suggest-player-names-when-null-tab-completions", suggestPlayersWhenNullTabCompletions);
|
||||
}
|
||||
|
||||
public static String authenticationServersDownKickMessage = ""; // empty = use translatable message
|
||||
private static void authenticationServersDownKickMessage() {
|
||||
authenticationServersDownKickMessage = Strings.emptyToNull(getString("messages.kick.authentication-servers-down", authenticationServersDownKickMessage));
|
||||
}
|
||||
|
||||
public static String connectionThrottleKickMessage = "Connection throttled! Please wait before reconnecting.";
|
||||
private static void connectionThrottleKickMessage() {
|
||||
connectionThrottleKickMessage = getString("messages.kick.connection-throttle", connectionThrottleKickMessage);
|
||||
}
|
||||
|
||||
public static String noPermissionMessage = "&cI'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.";
|
||||
private static void noPermissionMessage() {
|
||||
noPermissionMessage = ChatColor.translateAlternateColorCodes('&', getString("messages.no-permission", noPermissionMessage));
|
||||
}
|
||||
|
||||
public static boolean savePlayerData = true;
|
||||
private static void savePlayerData() {
|
||||
savePlayerData = getBoolean("settings.save-player-data", savePlayerData);
|
||||
if(!savePlayerData) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Player Data Saving is currently disabled. Any changes to your players data, " +
|
||||
"such as inventories, experience points, advancements and the like will not be saved when they log out.");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean useAlternativeLuckFormula = false;
|
||||
private static void useAlternativeLuckFormula() {
|
||||
useAlternativeLuckFormula = getBoolean("settings.use-alternative-luck-formula", false);
|
||||
if (useAlternativeLuckFormula) {
|
||||
Bukkit.getLogger().log(Level.INFO, "Using Aikar's Alternative Luck Formula to apply Luck attribute to all loot pool calculations. See https://luckformula.emc.gs");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean useVersionedWorld = false;
|
||||
private static void useVersionedWorld() {
|
||||
useVersionedWorld = getBoolean("settings.use-versioned-world", false);
|
||||
if (useVersionedWorld) {
|
||||
Logger logger = Bukkit.getLogger();
|
||||
String ver = MinecraftServer.getServer().getVersion();
|
||||
logger.log(Level.INFO, "******************************************************");
|
||||
logger.log(Level.INFO, "*** Using a versioned world folder. Your world will be saved");
|
||||
logger.log(Level.INFO, "*** to into the " + ver + " folder, but copied from your current world.");
|
||||
logger.log(Level.INFO, "*** ");
|
||||
logger.log(Level.INFO, "*** This setting should not be used in your real world!!!");
|
||||
logger.log(Level.INFO, "*** If you want to retain the new world, you need to move ");
|
||||
logger.log(Level.INFO, "*** the folders out of the " + ver + " folder and overwrite existing");
|
||||
logger.log(Level.INFO, "*** ");
|
||||
logger.log(Level.INFO, "*** Deleting the " + ver + " folder will cause it to recreate again");
|
||||
logger.log(Level.INFO, "*** from your unversioned world files.");
|
||||
logger.log(Level.INFO, "*** ");
|
||||
logger.log(Level.INFO, "*** You should backup your original world files incase something goes");
|
||||
logger.log(Level.INFO, "*** wrong with this system! This is not a backup system.");
|
||||
logger.log(Level.INFO, "******************************************************");
|
||||
}
|
||||
}
|
||||
|
||||
public static int watchdogPrintEarlyWarningEvery = 5000;
|
||||
public static int watchdogPrintEarlyWarningDelay = 10000;
|
||||
private static void watchdogEarlyWarning() {
|
||||
watchdogPrintEarlyWarningEvery = getInt("settings.watchdog.early-warning-every", 5000);
|
||||
watchdogPrintEarlyWarningDelay = getInt("settings.watchdog.early-warning-delay", 10000);
|
||||
WatchdogThread.doStart(SpigotConfig.timeoutTime, SpigotConfig.restartOnCrash );
|
||||
}
|
||||
|
||||
public static int tabSpamIncrement = 1;
|
||||
public static int tabSpamLimit = 500;
|
||||
private static void tabSpamLimiters() {
|
||||
tabSpamIncrement = getInt("settings.spam-limiter.tab-spam-increment", tabSpamIncrement);
|
||||
// Older versions used a smaller limit, which is too low for 1.13, we'll bump this up if default
|
||||
if (version < 14) {
|
||||
if (tabSpamIncrement == 10) {
|
||||
set("settings.spam-limiter.tab-spam-increment", 2);
|
||||
tabSpamIncrement = 2;
|
||||
}
|
||||
}
|
||||
tabSpamLimit = getInt("settings.spam-limiter.tab-spam-limit", tabSpamLimit);
|
||||
}
|
||||
|
||||
public static Map<String, Long> seedOverride = new java.util.HashMap<>();
|
||||
private static void worldSeedOverrides() {
|
||||
ConfigurationSection seeds = config.getConfigurationSection("seed-overrides");
|
||||
if (seeds != null) {
|
||||
TimingsManager.hiddenConfigs.add("seed-overrides");
|
||||
for (String key : seeds.getKeys(false)) {
|
||||
String seedString = seeds.getString(key);
|
||||
long seed;
|
||||
try {
|
||||
seed = Long.parseLong(seedString);
|
||||
} catch (Exception e) {
|
||||
seed = (long) seedString.hashCode();
|
||||
}
|
||||
log("Seed Override: " + key + " => " + seed);
|
||||
seedOverride.put(key, seed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean asyncChunks = false;
|
||||
public static boolean asyncChunkGeneration = true;
|
||||
public static boolean asyncChunkGenThreadPerWorld = true;
|
||||
public static int asyncChunkLoadThreads = -1;
|
||||
private static void asyncChunks() {
|
||||
if (version < 15) {
|
||||
boolean enabled = config.getBoolean("settings.async-chunks", true);
|
||||
ConfigurationSection section = config.createSection("settings.async-chunks");
|
||||
section.set("enable", enabled);
|
||||
section.set("load-threads", -1);
|
||||
section.set("generation", true);
|
||||
section.set("thread-per-world-generation", true);
|
||||
}
|
||||
|
||||
asyncChunks = getBoolean("settings.async-chunks.enable", true);
|
||||
asyncChunkGeneration = getBoolean("settings.async-chunks.generation", true);
|
||||
asyncChunkGenThreadPerWorld = getBoolean("settings.async-chunks.thread-per-world-generation", true);
|
||||
asyncChunkLoadThreads = getInt("settings.async-chunks.load-threads", -1);
|
||||
if (asyncChunkLoadThreads <= 0) {
|
||||
asyncChunkLoadThreads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Runtime.getRuntime().availableProcessors() * 1.5);
|
||||
}
|
||||
|
||||
// Let Shared Host set some limits
|
||||
String sharedHostEnvGen = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN");
|
||||
String sharedHostEnvLoad = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_LOAD");
|
||||
if ("1".equals(sharedHostEnvGen)) {
|
||||
log("Async Chunks - Generation: Your host has requested to use a single thread world generation");
|
||||
asyncChunkGenThreadPerWorld = false;
|
||||
} else if ("2".equals(sharedHostEnvGen)) {
|
||||
log("Async Chunks - Generation: Your host has disabled async world generation - You will experience lag from world generation");
|
||||
asyncChunkGeneration = false;
|
||||
}
|
||||
|
||||
if (sharedHostEnvLoad != null) {
|
||||
try {
|
||||
asyncChunkLoadThreads = Math.max(1, Math.min(asyncChunkLoadThreads, Integer.parseInt(sharedHostEnvLoad)));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
if (!asyncChunks) {
|
||||
log("Async Chunks: Disabled - Chunks will be managed synchronosuly, and will cause tremendous lag.");
|
||||
} else {
|
||||
log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag.");
|
||||
if (!asyncChunkGeneration) {
|
||||
log("Async Chunks - Generation: Disabled - Chunks will be generated synchronosuly, and will cause tremendous lag.");
|
||||
} else if (asyncChunkGenThreadPerWorld) {
|
||||
log("Async Chunks - Generation: Enabled - Chunks will be generated much faster, without lag.");
|
||||
} else {
|
||||
log("Async Chunks - Generation: Enabled (Single Thread) - Chunks will be generated much faster, without lag.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean velocitySupport;
|
||||
public static boolean velocityOnlineMode;
|
||||
public static byte[] velocitySecretKey;
|
||||
private static void velocitySupport() {
|
||||
velocitySupport = getBoolean("settings.velocity-support.enabled", false);
|
||||
velocityOnlineMode = getBoolean("settings.velocity-support.online-mode", false);
|
||||
String secret = getString("settings.velocity-support.secret", "");
|
||||
if (velocitySupport && secret.isEmpty()) {
|
||||
fatal("Velocity support is enabled, but no secret key was specified. A secret key is required!");
|
||||
} else {
|
||||
velocitySecretKey = secret.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
public static int maxBookPageSize = 2560;
|
||||
public static double maxBookTotalSizeMultiplier = 0.98D;
|
||||
private static void maxBookSize() {
|
||||
maxBookPageSize = getInt("settings.book-size.page-max", maxBookPageSize);
|
||||
maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier);
|
||||
}
|
||||
}
|
||||
597
src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
Normal file
597
src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
Normal file
@@ -0,0 +1,597 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode;
|
||||
import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.spigotmc.SpigotWorldConfig;
|
||||
|
||||
import static com.destroystokyo.paper.PaperConfig.log;
|
||||
import static com.destroystokyo.paper.PaperConfig.logError;
|
||||
|
||||
public class PaperWorldConfig {
|
||||
|
||||
private final String worldName;
|
||||
private final SpigotWorldConfig spigotConfig;
|
||||
private final YamlConfiguration config;
|
||||
private boolean verbose;
|
||||
|
||||
public PaperWorldConfig(String worldName, SpigotWorldConfig spigotConfig) {
|
||||
this.worldName = worldName;
|
||||
this.spigotConfig = spigotConfig;
|
||||
this.config = PaperConfig.config;
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
log("-------- World Settings For [" + worldName + "] --------");
|
||||
PaperConfig.readConfig(PaperWorldConfig.class, this);
|
||||
}
|
||||
|
||||
private void set(String path, Object val) {
|
||||
config.set("world-settings.default." + path, val);
|
||||
if (config.get("world-settings." + worldName + "." + path) != null) {
|
||||
config.set("world-settings." + worldName + "." + 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) {
|
||||
// TODO: Figure out why getFloat() always returns the default value.
|
||||
return (float) getDouble(path, (double) def);
|
||||
}
|
||||
|
||||
private <T> List<T> getList(String path, List<T> def) {
|
||||
config.addDefault("world-settings.default." + path, def);
|
||||
return (List<T>) 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 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 double babyZombieMovementSpeed;
|
||||
private void babyZombieMovementSpeed() {
|
||||
babyZombieMovementSpeed = getDouble("baby-zombie-movement-speed", 0.5D); // Player moves at 0.1F, for reference
|
||||
log("Baby zombies will move at the speed of " + babyZombieMovementSpeed);
|
||||
}
|
||||
|
||||
public int fishingMinTicks;
|
||||
public int fishingMaxTicks;
|
||||
private void fishingTickRange() {
|
||||
fishingMinTicks = getInt("fishing-time-range.MinimumTicks", 100);
|
||||
fishingMaxTicks = getInt("fishing-time-range.MaximumTicks", 600);
|
||||
log("Fishing time ranges are between " + fishingMinTicks +" and " + fishingMaxTicks + " ticks");
|
||||
}
|
||||
|
||||
public boolean nerfedMobsShouldJump;
|
||||
private void nerfedMobsShouldJump() {
|
||||
nerfedMobsShouldJump = getBoolean("spawner-nerfed-mobs-should-jump", false);
|
||||
}
|
||||
|
||||
public int softDespawnDistance;
|
||||
public int 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 int fallingBlockHeightNerf;
|
||||
public int entityTNTHeightNerf;
|
||||
private void heightNerfs() {
|
||||
fallingBlockHeightNerf = getInt("falling-block-height-nerf", 0);
|
||||
entityTNTHeightNerf = getInt("tnt-entity-height-nerf", 0);
|
||||
|
||||
if (fallingBlockHeightNerf != 0) log("Falling Block Height Limit set to Y: " + fallingBlockHeightNerf);
|
||||
if (entityTNTHeightNerf != 0) log("TNT Entity Height Limit set to Y: " + entityTNTHeightNerf);
|
||||
}
|
||||
|
||||
public boolean netherVoidTopDamage;
|
||||
private void netherVoidTopDamage() {
|
||||
netherVoidTopDamage = getBoolean( "nether-ceiling-void-damage", false );
|
||||
log("Top of the nether void damage: " + netherVoidTopDamage);
|
||||
}
|
||||
|
||||
public boolean queueLightUpdates;
|
||||
private void queueLightUpdates() {
|
||||
queueLightUpdates = getBoolean("queue-light-updates", false);
|
||||
log("Lighting Queue enabled: " + queueLightUpdates);
|
||||
log("Warning: This feature may help reduce TPS loss from light, but comes at the cost of buggy light data");
|
||||
log("We are working to improve this feature.");
|
||||
}
|
||||
|
||||
public boolean disableEndCredits;
|
||||
private void disableEndCredits() {
|
||||
disableEndCredits = getBoolean("game-mechanics.disable-end-credits", false);
|
||||
log("End credits disabled: " + disableEndCredits);
|
||||
}
|
||||
|
||||
public boolean optimizeExplosions;
|
||||
private void optimizeExplosions() {
|
||||
optimizeExplosions = getBoolean("optimize-explosions", false);
|
||||
log("Optimize explosions: " + optimizeExplosions);
|
||||
}
|
||||
|
||||
public boolean disableExplosionKnockback;
|
||||
private void disableExplosionKnockback(){
|
||||
disableExplosionKnockback = getBoolean("disable-explosion-knockback", false);
|
||||
}
|
||||
|
||||
public boolean disableThunder;
|
||||
private void disableThunder() {
|
||||
disableThunder = getBoolean("disable-thunder", false);
|
||||
}
|
||||
|
||||
public boolean disableIceAndSnow;
|
||||
private void disableIceAndSnow(){
|
||||
disableIceAndSnow = getBoolean("disable-ice-and-snow", false);
|
||||
}
|
||||
|
||||
public int mobSpawnerTickRate;
|
||||
private void mobSpawnerTickRate() {
|
||||
mobSpawnerTickRate = getInt("mob-spawner-tick-rate", 1);
|
||||
}
|
||||
|
||||
public int containerUpdateTickRate;
|
||||
private void containerUpdateTickRate() {
|
||||
containerUpdateTickRate = getInt("container-update-tick-rate", 1);
|
||||
}
|
||||
|
||||
public boolean disableChestCatDetection;
|
||||
private void disableChestCatDetection() {
|
||||
disableChestCatDetection = getBoolean("game-mechanics.disable-chest-cat-detection", false);
|
||||
}
|
||||
|
||||
public boolean disablePlayerCrits;
|
||||
private void disablePlayerCrits() {
|
||||
disablePlayerCrits = getBoolean("game-mechanics.disable-player-crits", false);
|
||||
}
|
||||
|
||||
public boolean allChunksAreSlimeChunks;
|
||||
private void allChunksAreSlimeChunks() {
|
||||
allChunksAreSlimeChunks = getBoolean("all-chunks-are-slime-chunks", false);
|
||||
}
|
||||
|
||||
public int portalSearchRadius;
|
||||
private void portalSearchRadius() {
|
||||
portalSearchRadius = getInt("portal-search-radius", 128);
|
||||
}
|
||||
|
||||
public boolean disableTeleportationSuffocationCheck;
|
||||
private void disableTeleportationSuffocationCheck() {
|
||||
disableTeleportationSuffocationCheck = getBoolean("disable-teleportation-suffocation-check", false);
|
||||
}
|
||||
|
||||
public boolean nonPlayerEntitiesOnScoreboards = false;
|
||||
private void nonPlayerEntitiesOnScoreboards() {
|
||||
nonPlayerEntitiesOnScoreboards = getBoolean("allow-non-player-entities-on-scoreboards", false);
|
||||
}
|
||||
|
||||
public boolean allowLeashingUndeadHorse = false;
|
||||
private void allowLeashingUndeadHorse() {
|
||||
allowLeashingUndeadHorse = getBoolean("allow-leashing-undead-horse", false);
|
||||
}
|
||||
|
||||
public int nonPlayerArrowDespawnRate = -1;
|
||||
public int creativeArrowDespawnRate = -1;
|
||||
private void nonPlayerArrowDespawnRate() {
|
||||
nonPlayerArrowDespawnRate = getInt("non-player-arrow-despawn-rate", -1);
|
||||
if (nonPlayerArrowDespawnRate == -1) {
|
||||
nonPlayerArrowDespawnRate = spigotConfig.arrowDespawnRate;
|
||||
}
|
||||
creativeArrowDespawnRate = getInt("creative-arrow-despawn-rate", -1);
|
||||
if (creativeArrowDespawnRate == -1) {
|
||||
creativeArrowDespawnRate = spigotConfig.arrowDespawnRate;
|
||||
}
|
||||
log("Non Player Arrow Despawn Rate: " + nonPlayerArrowDespawnRate);
|
||||
log("Creative Arrow Despawn Rate: " + creativeArrowDespawnRate);
|
||||
}
|
||||
|
||||
public double skeleHorseSpawnChance;
|
||||
private void skeleHorseSpawnChance() {
|
||||
skeleHorseSpawnChance = getDouble("skeleton-horse-thunder-spawn-chance", 0.01D);
|
||||
if (skeleHorseSpawnChance < 0) {
|
||||
skeleHorseSpawnChance = 0.01D; // Vanilla value
|
||||
}
|
||||
}
|
||||
|
||||
public double sqrMaxThunderDistance;
|
||||
public double sqrMaxLightningImpactSoundDistance;
|
||||
public double maxLightningFlashDistance;
|
||||
private void lightningStrikeDistanceLimit() {
|
||||
sqrMaxThunderDistance = getInt("lightning-strike-distance-limit.sound", -1);
|
||||
if (sqrMaxThunderDistance > 0) {
|
||||
sqrMaxThunderDistance *= sqrMaxThunderDistance;
|
||||
}
|
||||
|
||||
sqrMaxLightningImpactSoundDistance = getInt("lightning-strike-distance-limit.impact-sound", -1);
|
||||
if (sqrMaxLightningImpactSoundDistance < 0) {
|
||||
sqrMaxLightningImpactSoundDistance = 32 * 32; //Vanilla value
|
||||
} else {
|
||||
sqrMaxLightningImpactSoundDistance *= sqrMaxLightningImpactSoundDistance;
|
||||
}
|
||||
|
||||
maxLightningFlashDistance = getInt("lightning-strike-distance-limit.flash", -1);
|
||||
if (maxLightningFlashDistance < 0) {
|
||||
maxLightningFlashDistance = 512; // Vanilla value
|
||||
}
|
||||
}
|
||||
|
||||
public boolean firePhysicsEventForRedstone = false;
|
||||
private void firePhysicsEventForRedstone() {
|
||||
firePhysicsEventForRedstone = getBoolean("fire-physics-event-for-redstone", firePhysicsEventForRedstone);
|
||||
}
|
||||
|
||||
public int fixedInhabitedTime;
|
||||
private void fixedInhabitedTime() {
|
||||
if (PaperConfig.version < 16) {
|
||||
if (!config.getBoolean("world-settings.default.use-chunk-inhabited-timer", true)) config.set("world-settings.default.fixed-chunk-inhabited-time", 0);
|
||||
if (!config.getBoolean("world-settings." + worldName + ".use-chunk-inhabited-timer", true)) config.set("world-settings." + worldName + ".fixed-chunk-inhabited-time", 0);
|
||||
set("use-chunk-inhabited-timer", null);
|
||||
}
|
||||
fixedInhabitedTime = getInt("fixed-chunk-inhabited-time", -1);
|
||||
}
|
||||
|
||||
public int grassUpdateRate = 1;
|
||||
private void grassUpdateRate() {
|
||||
grassUpdateRate = Math.max(0, getInt("grass-spread-tick-rate", grassUpdateRate));
|
||||
log("Grass Spread Tick Rate: " + grassUpdateRate);
|
||||
}
|
||||
|
||||
public short keepLoadedRange;
|
||||
private void keepLoadedRange() {
|
||||
keepLoadedRange = (short) (getInt("keep-spawn-loaded-range", Math.min(spigotConfig.viewDistance, 8)) * 16);
|
||||
log( "Keep Spawn Loaded Range: " + (keepLoadedRange/16));
|
||||
}
|
||||
|
||||
public boolean useVanillaScoreboardColoring;
|
||||
private void useVanillaScoreboardColoring() {
|
||||
useVanillaScoreboardColoring = getBoolean("use-vanilla-world-scoreboard-name-coloring", false);
|
||||
}
|
||||
|
||||
public boolean frostedIceEnabled = true;
|
||||
public int frostedIceDelayMin = 20;
|
||||
public int frostedIceDelayMax = 40;
|
||||
private void frostedIce() {
|
||||
this.frostedIceEnabled = this.getBoolean("frosted-ice.enabled", this.frostedIceEnabled);
|
||||
this.frostedIceDelayMin = this.getInt("frosted-ice.delay.min", this.frostedIceDelayMin);
|
||||
this.frostedIceDelayMax = this.getInt("frosted-ice.delay.max", this.frostedIceDelayMax);
|
||||
log("Frosted Ice: " + (this.frostedIceEnabled ? "enabled" : "disabled") + " / delay: min=" + this.frostedIceDelayMin + ", max=" + this.frostedIceDelayMax);
|
||||
}
|
||||
|
||||
public boolean autoReplenishLootables;
|
||||
public boolean restrictPlayerReloot;
|
||||
public boolean changeLootTableSeedOnFill;
|
||||
public int maxLootableRefills;
|
||||
public int lootableRegenMin;
|
||||
public int lootableRegenMax;
|
||||
private void enhancedLootables() {
|
||||
autoReplenishLootables = getBoolean("lootables.auto-replenish", false);
|
||||
restrictPlayerReloot = getBoolean("lootables.restrict-player-reloot", true);
|
||||
changeLootTableSeedOnFill = getBoolean("lootables.reset-seed-on-fill", true);
|
||||
maxLootableRefills = getInt("lootables.max-refills", -1);
|
||||
lootableRegenMin = PaperConfig.getSeconds(getString("lootables.refresh-min", "12h"));
|
||||
lootableRegenMax = PaperConfig.getSeconds(getString("lootables.refresh-max", "2d"));
|
||||
if (autoReplenishLootables) {
|
||||
log("Lootables: Replenishing every " +
|
||||
PaperConfig.timeSummary(lootableRegenMin) + " to " +
|
||||
PaperConfig.timeSummary(lootableRegenMax) +
|
||||
(restrictPlayerReloot ? " (restricting reloot)" : "")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean preventTntFromMovingInWater;
|
||||
private void preventTntFromMovingInWater() {
|
||||
if (PaperConfig.version < 13) {
|
||||
boolean oldVal = getBoolean("enable-old-tnt-cannon-behaviors", false);
|
||||
set("prevent-tnt-from-moving-in-water", oldVal);
|
||||
}
|
||||
preventTntFromMovingInWater = getBoolean("prevent-tnt-from-moving-in-water", false);
|
||||
log("Prevent TNT from moving in water: " + preventTntFromMovingInWater);
|
||||
}
|
||||
|
||||
public long delayChunkUnloadsBy;
|
||||
private void delayChunkUnloadsBy() {
|
||||
delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s"));
|
||||
if (delayChunkUnloadsBy > 0) {
|
||||
log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds");
|
||||
delayChunkUnloadsBy *= 1000;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean skipEntityTickingInChunksScheduledForUnload = true;
|
||||
private void skipEntityTickingInChunksScheduledForUnload() {
|
||||
skipEntityTickingInChunksScheduledForUnload = getBoolean("skip-entity-ticking-in-chunks-scheduled-for-unload", skipEntityTickingInChunksScheduledForUnload);
|
||||
}
|
||||
|
||||
public int autoSavePeriod = -1;
|
||||
private void autoSavePeriod() {
|
||||
autoSavePeriod = getInt("auto-save-interval", -1);
|
||||
if (autoSavePeriod > 0) {
|
||||
log("Auto Save Interval: " +autoSavePeriod + " (" + (autoSavePeriod / 20) + "s)");
|
||||
} else if (autoSavePeriod < 0) {
|
||||
autoSavePeriod = MinecraftServer.getServer().autosavePeriod;
|
||||
}
|
||||
}
|
||||
|
||||
public int maxAutoSaveChunksPerTick = 24;
|
||||
private void maxAutoSaveChunksPerTick() {
|
||||
maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24);
|
||||
}
|
||||
|
||||
public int queueSizeAutoSaveThreshold = 50;
|
||||
private void queueSizeAutoSaveThreshold() {
|
||||
queueSizeAutoSaveThreshold = getInt("save-queue-limit-for-auto-save", 50);
|
||||
}
|
||||
|
||||
public boolean removeCorruptTEs = false;
|
||||
private void removeCorruptTEs() {
|
||||
removeCorruptTEs = getBoolean("remove-corrupt-tile-entities", false);
|
||||
}
|
||||
|
||||
public boolean filterNBTFromSpawnEgg = true;
|
||||
private void fitlerNBTFromSpawnEgg() {
|
||||
filterNBTFromSpawnEgg = getBoolean("filter-nbt-data-from-spawn-eggs-and-related", true);
|
||||
if (!filterNBTFromSpawnEgg) {
|
||||
Bukkit.getLogger().warning("Spawn Egg and Armor Stand NBT filtering disabled, this is a potential security risk");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean enableTreasureMaps = true;
|
||||
public boolean treasureMapsAlreadyDiscovered = false;
|
||||
private void treasureMapsAlreadyDiscovered() {
|
||||
enableTreasureMaps = getBoolean("enable-treasure-maps", true);
|
||||
treasureMapsAlreadyDiscovered = getBoolean("treasure-maps-return-already-discovered", false);
|
||||
if (treasureMapsAlreadyDiscovered) {
|
||||
log("Treasure Maps will return already discovered locations");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean armorStandEntityLookups = true;
|
||||
private void armorStandEntityLookups() {
|
||||
armorStandEntityLookups = getBoolean("armor-stands-do-collision-entity-lookups", true);
|
||||
}
|
||||
|
||||
public int maxCollisionsPerEntity;
|
||||
private void maxEntityCollision() {
|
||||
maxCollisionsPerEntity = getInt( "max-entity-collisions", this.spigotConfig.getInt("max-entity-collisions", 8) );
|
||||
log( "Max Entity Collisions: " + maxCollisionsPerEntity );
|
||||
}
|
||||
|
||||
public boolean parrotsHangOnBetter;
|
||||
private void parrotsHangOnBetter() {
|
||||
parrotsHangOnBetter = getBoolean("parrots-are-unaffected-by-player-movement", false);
|
||||
log("Parrots are unaffected by player movement: " + parrotsHangOnBetter);
|
||||
}
|
||||
|
||||
public boolean disableCreeperLingeringEffect;
|
||||
private void setDisableCreeperLingeringEffect() {
|
||||
disableCreeperLingeringEffect = getBoolean("disable-creeper-lingering-effect", false);
|
||||
log("Creeper lingering effect: " + disableCreeperLingeringEffect);
|
||||
}
|
||||
|
||||
public int expMergeMaxValue;
|
||||
private void expMergeMaxValue() {
|
||||
expMergeMaxValue = getInt("experience-merge-max-value", -1);
|
||||
log("Experience Merge Max Value: " + expMergeMaxValue);
|
||||
}
|
||||
|
||||
public int maxChunkSendsPerTick = 81;
|
||||
private void maxChunkSendsPerTick() {
|
||||
maxChunkSendsPerTick = getInt("max-chunk-sends-per-tick", maxChunkSendsPerTick);
|
||||
if (maxChunkSendsPerTick <= 0) {
|
||||
maxChunkSendsPerTick = 81;
|
||||
}
|
||||
log("Max Chunk Sends Per Tick: " + maxChunkSendsPerTick);
|
||||
}
|
||||
|
||||
public int maxChunkGensPerTick = 10;
|
||||
private void maxChunkGensPerTick() {
|
||||
maxChunkGensPerTick = getInt("max-chunk-gens-per-tick", maxChunkGensPerTick);
|
||||
if (maxChunkGensPerTick <= 0) {
|
||||
maxChunkGensPerTick = Integer.MAX_VALUE;
|
||||
log("Max Chunk Gens Per Tick: Unlimited (NOT RECOMMENDED)");
|
||||
} else {
|
||||
log("Max Chunk Gens Per Tick: " + maxChunkGensPerTick);
|
||||
}
|
||||
}
|
||||
|
||||
public double squidMaxSpawnHeight;
|
||||
private void squidMaxSpawnHeight() {
|
||||
squidMaxSpawnHeight = getDouble("squid-spawn-height.maximum", 0.0D);
|
||||
}
|
||||
|
||||
public boolean cooldownHopperWhenFull = true;
|
||||
public boolean disableHopperMoveEvents = false;
|
||||
private void hopperOptimizations() {
|
||||
cooldownHopperWhenFull = getBoolean("hopper.cooldown-when-full", cooldownHopperWhenFull);
|
||||
log("Cooldown Hoppers when Full: " + (cooldownHopperWhenFull ? "enabled" : "disabled"));
|
||||
disableHopperMoveEvents = getBoolean("hopper.disable-move-event", disableHopperMoveEvents);
|
||||
log("Hopper Move Item Events: " + (disableHopperMoveEvents ? "disabled" : "enabled"));
|
||||
}
|
||||
|
||||
public boolean disableSprintInterruptionOnAttack;
|
||||
private void disableSprintInterruptionOnAttack() {
|
||||
disableSprintInterruptionOnAttack = getBoolean("game-mechanics.disable-sprint-interruption-on-attack", false);
|
||||
}
|
||||
|
||||
public boolean disableEnderpearlExploit = true;
|
||||
private void disableEnderpearlExploit() {
|
||||
disableEnderpearlExploit = getBoolean("game-mechanics.disable-unloaded-chunk-enderpearl-exploit", disableEnderpearlExploit);
|
||||
log("Disable Unloaded Chunk Enderpearl Exploit: " + (disableEnderpearlExploit ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
public boolean villagesLoadChunks = false;
|
||||
private void villagesLoadChunks() {
|
||||
villagesLoadChunks = getBoolean("game-mechanics.villages-load-chunks", false);
|
||||
if (villagesLoadChunks) {
|
||||
log("Villages can load chunks - Warning this can cause intense TPS loss. Strongly consider disabling this.");
|
||||
}
|
||||
}
|
||||
|
||||
public int shieldBlockingDelay = 5;
|
||||
private void shieldBlockingDelay() {
|
||||
shieldBlockingDelay = getInt("game-mechanics.shield-blocking-delay", 5);
|
||||
}
|
||||
|
||||
public boolean scanForLegacyEnderDragon = true;
|
||||
private void scanForLegacyEnderDragon() {
|
||||
scanForLegacyEnderDragon = getBoolean("game-mechanics.scan-for-legacy-ender-dragon", true);
|
||||
}
|
||||
|
||||
public int bedSearchRadius = 1;
|
||||
private void bedSearchRadius() {
|
||||
bedSearchRadius = getInt("bed-search-radius", 1);
|
||||
if (bedSearchRadius < 1) {
|
||||
bedSearchRadius = 1;
|
||||
}
|
||||
if (bedSearchRadius > 1) {
|
||||
log("Bed Search Radius: " + bedSearchRadius);
|
||||
}
|
||||
}
|
||||
|
||||
public int waterOverLavaFlowSpeed;
|
||||
private void waterOverLavaFlowSpeed() {
|
||||
waterOverLavaFlowSpeed = getInt("water-over-lava-flow-speed", 5);
|
||||
log("Water over lava flow speed: " + waterOverLavaFlowSpeed);
|
||||
}
|
||||
|
||||
public enum DuplicateUUIDMode {
|
||||
SAFE_REGEN, DELETE, NOTHING, WARN
|
||||
}
|
||||
public DuplicateUUIDMode duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN;
|
||||
public int duplicateUUIDDeleteRange = 32;
|
||||
private void repairDuplicateUUID() {
|
||||
String desiredMode = getString("duplicate-uuid-resolver", "saferegen").toLowerCase().trim();
|
||||
duplicateUUIDDeleteRange = getInt("duplicate-uuid-saferegen-delete-range", duplicateUUIDDeleteRange);
|
||||
switch (desiredMode.toLowerCase()) {
|
||||
case "regen":
|
||||
case "regenerate":
|
||||
case "saferegen":
|
||||
case "saferegenerate":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.SAFE_REGEN;
|
||||
log("Duplicate UUID Resolve: Regenerate New UUID if distant (Delete likely duplicates within " + duplicateUUIDDeleteRange + " blocks)");
|
||||
break;
|
||||
case "remove":
|
||||
case "delete":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.DELETE;
|
||||
log("Duplicate UUID Resolve: Delete Entity");
|
||||
break;
|
||||
case "silent":
|
||||
case "nothing":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.NOTHING;
|
||||
logError("Duplicate UUID Resolve: Do Nothing (no logs) - Warning, may lose indication of bad things happening");
|
||||
break;
|
||||
case "log":
|
||||
case "warn":
|
||||
duplicateUUIDMode = DuplicateUUIDMode.WARN;
|
||||
log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)");
|
||||
break;
|
||||
default:
|
||||
duplicateUUIDMode = DuplicateUUIDMode.WARN;
|
||||
logError("Warning: Invalid duplicate-uuid-resolver config " + desiredMode + " - must be one of: regen, delete, nothing, warn");
|
||||
log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean armorStandTick = true;
|
||||
private void armorStandTick() {
|
||||
this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick);
|
||||
log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default");
|
||||
}
|
||||
|
||||
public boolean optimizeLight = true;
|
||||
private void optimizeLight() {
|
||||
this.optimizeLight = getBoolean("optimize-light", optimizeLight);
|
||||
}
|
||||
|
||||
public boolean antiXray;
|
||||
public boolean asynchronous;
|
||||
public EngineMode engineMode;
|
||||
public ChunkEdgeMode chunkEdgeMode;
|
||||
public int maxChunkSectionIndex;
|
||||
public int updateRadius;
|
||||
public List<String> hiddenBlocks;
|
||||
public List<String> replacementBlocks;
|
||||
private void antiXray() {
|
||||
antiXray = getBoolean("anti-xray.enabled", false);
|
||||
asynchronous = true;
|
||||
engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId()));
|
||||
engineMode = engineMode == null ? EngineMode.HIDE : engineMode;
|
||||
chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()));
|
||||
chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode;
|
||||
|
||||
if (chunkEdgeMode != ChunkEdgeMode.WAIT) {
|
||||
log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")");
|
||||
chunkEdgeMode = ChunkEdgeMode.WAIT;
|
||||
set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId());
|
||||
}
|
||||
|
||||
maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3);
|
||||
maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex;
|
||||
updateRadius = getInt("anti-xray.update-radius", 2);
|
||||
hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "lit_redstone_ore", "clay", "emerald_ore", "ender_chest"));
|
||||
replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "planks"));
|
||||
log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius);
|
||||
}
|
||||
|
||||
public boolean preventMovingIntoUnloadedChunks = false;
|
||||
private void preventMovingIntoUnloadedChunks() {
|
||||
preventMovingIntoUnloadedChunks = getBoolean("prevent-moving-into-unloaded-chunks", false);
|
||||
}
|
||||
|
||||
public boolean useEigencraftRedstone = false;
|
||||
private void useEigencraftRedstone() {
|
||||
useEigencraftRedstone = this.getBoolean("use-faster-eigencraft-redstone", false);
|
||||
if (useEigencraftRedstone) {
|
||||
log("Using Eigencraft redstone algorithm by theosib.");
|
||||
} else {
|
||||
log("Using vanilla redstone algorithm.");
|
||||
}
|
||||
}
|
||||
}
|
||||
121
src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
Normal file
121
src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityInsentient;
|
||||
import net.minecraft.server.EnumCreatureType;
|
||||
import net.minecraft.server.IAnimal;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PaperWorldEntityList extends ArrayList<Entity> {
|
||||
|
||||
private final WorldServer world;
|
||||
private final int[] entityCounts = new int[EnumCreatureType.values().length];
|
||||
|
||||
|
||||
public PaperWorldEntityList(World world) {
|
||||
this.world = (WorldServer) world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends Entity> c) {
|
||||
for (Entity e : c) {
|
||||
updateEntityCount(e, 1);
|
||||
}
|
||||
|
||||
return super.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
for (Object e : c) {
|
||||
if (e instanceof Entity && ((Entity) e).getWorld() == world) {
|
||||
updateEntityCount((Entity) e, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return super.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Entity e) {
|
||||
updateEntityCount(e, 1);
|
||||
|
||||
return super.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity remove(int index) {
|
||||
guard();
|
||||
Entity entity = super.remove(index);
|
||||
if (entity != null) updateEntityCount(entity, -1);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
guard();
|
||||
if (super.remove(o)) {
|
||||
updateEntityCount((Entity) o, -1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void guard() {
|
||||
if (world.guardEntityList) {
|
||||
throw new java.util.ConcurrentModificationException();
|
||||
}
|
||||
}
|
||||
|
||||
public int getCreatureCount(EnumCreatureType type) {
|
||||
return entityCounts[type.ordinal()];
|
||||
}
|
||||
|
||||
private void updateEntityCount(EnumCreatureType type, int amt) {
|
||||
int count = entityCounts[type.ordinal()];
|
||||
|
||||
count += amt;
|
||||
|
||||
if (count < 0) {
|
||||
MinecraftServer.LOGGER.error("Paper - Entity count cache has gone negative");
|
||||
count = 0;
|
||||
}
|
||||
|
||||
entityCounts[type.ordinal()] = count;
|
||||
}
|
||||
|
||||
public void updateEntityCount(Entity entity, int amt) {
|
||||
if (!(entity instanceof IAnimal)) return;
|
||||
|
||||
if (entity instanceof EntityInsentient) {
|
||||
EntityInsentient entityinsentient = (EntityInsentient) entity;
|
||||
if (amt > 0 && entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (amt < 0) {
|
||||
if (!entity.hasBeenCounted) {
|
||||
return;
|
||||
}
|
||||
// Only remove once, we remove from if the entity list is guarded, but may be called later
|
||||
entity.hasBeenCounted = false;
|
||||
} else {
|
||||
if (entity.hasBeenCounted) {
|
||||
return;
|
||||
}
|
||||
entity.hasBeenCounted = true;
|
||||
}
|
||||
|
||||
for (EnumCreatureType type : EnumCreatureType.values()) {
|
||||
if (type.matches(entity)) {
|
||||
updateEntityCount(type, amt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
191
src/main/java/com/destroystokyo/paper/PaperWorldMap.java
Normal file
191
src/main/java/com/destroystokyo/paper/PaperWorldMap.java
Normal file
@@ -0,0 +1,191 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import net.minecraft.server.DimensionManager;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class PaperWorldMap extends HashMap<DimensionManager, WorldServer> {
|
||||
private final List<WorldServer> worlds = new ArrayList<>();
|
||||
private final List<WorldServer> worldsIterable = new ArrayList<WorldServer>() {
|
||||
@Override
|
||||
public Iterator<WorldServer> iterator() {
|
||||
Iterator<WorldServer> iterator = super.iterator();
|
||||
return new Iterator<WorldServer>() {
|
||||
private WorldServer last;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer next() {
|
||||
this.last = iterator.next();
|
||||
return last;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
worlds.set(last.dimension.getDimensionID()+1, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public int size() {
|
||||
return worldsIterable.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return worldsIterable.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer get(Object key) {
|
||||
// Will hit the below method
|
||||
return key instanceof DimensionManager ? get((DimensionManager) key) : null;
|
||||
}
|
||||
|
||||
public WorldServer get(DimensionManager key) {
|
||||
int id = key.getDimensionID()+1;
|
||||
return worlds.size() > id ? worlds.get(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
// will hit below method
|
||||
return key instanceof DimensionManager && containsKey((DimensionManager) key);
|
||||
}
|
||||
public boolean containsKey(DimensionManager key) {
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer put(DimensionManager key, WorldServer value) {
|
||||
while (worlds.size() <= key.getDimensionID()+1) {
|
||||
worlds.add(null);
|
||||
}
|
||||
WorldServer old = worlds.set(key.getDimensionID()+1, value);
|
||||
if (old != null) {
|
||||
worldsIterable.remove(old);
|
||||
}
|
||||
worldsIterable.add(value);
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends DimensionManager, ? extends WorldServer> m) {
|
||||
for (Entry<? extends DimensionManager, ? extends WorldServer> e : m.entrySet()) {
|
||||
put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldServer remove(Object key) {
|
||||
return key instanceof DimensionManager ? remove((DimensionManager) key) : null;
|
||||
}
|
||||
|
||||
public WorldServer remove(DimensionManager key) {
|
||||
WorldServer old;
|
||||
if (key.getDimensionID()+1 == worlds.size() - 1) {
|
||||
old = worlds.remove(key.getDimensionID()+1);
|
||||
} else {
|
||||
old = worlds.set(key.getDimensionID() + 1, null);
|
||||
}
|
||||
if (old != null) {
|
||||
worldsIterable.remove(old);
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new RuntimeException("What the hell are you doing?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return value instanceof WorldServer && get(((WorldServer) value).dimension) != null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<DimensionManager> keySet() {
|
||||
return new AbstractSet<DimensionManager>() {
|
||||
@Override
|
||||
public Iterator<DimensionManager> iterator() {
|
||||
Iterator<WorldServer> iterator = worldsIterable.iterator();
|
||||
return new Iterator<DimensionManager>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DimensionManager next() {
|
||||
return iterator.next().dimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return worlds.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<WorldServer> values() {
|
||||
return worldsIterable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<DimensionManager, WorldServer>> entrySet() {
|
||||
return new AbstractSet<Entry<DimensionManager, WorldServer>>() {
|
||||
@Override
|
||||
public Iterator<Entry<DimensionManager, WorldServer>> iterator() {
|
||||
Iterator<WorldServer> iterator = worldsIterable.iterator();
|
||||
return new Iterator<Entry<DimensionManager, WorldServer>>() {
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<DimensionManager, WorldServer> next() {
|
||||
WorldServer entry = iterator.next();
|
||||
return new SimpleEntry<>(entry.dimension, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return worldsIterable.size();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.destroystokyo.paper;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.craftbukkit.scheduler.CraftTask;
|
||||
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
|
||||
import com.destroystokyo.paper.exception.ServerSchedulerException;
|
||||
|
||||
/**
|
||||
* Reporting wrapper to catch exceptions not natively
|
||||
*/
|
||||
public class ServerSchedulerReportingWrapper implements Runnable {
|
||||
|
||||
private final CraftTask internalTask;
|
||||
|
||||
public ServerSchedulerReportingWrapper(CraftTask internalTask) {
|
||||
this.internalTask = Preconditions.checkNotNull(internalTask, "internalTask");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
internalTask.run();
|
||||
} catch (RuntimeException e) {
|
||||
internalTask.getOwner().getServer().getPluginManager().callEvent(
|
||||
new ServerExceptionEvent(new ServerSchedulerException(e, internalTask))
|
||||
);
|
||||
throw e;
|
||||
} catch (Throwable t) {
|
||||
internalTask.getOwner().getServer().getPluginManager().callEvent(
|
||||
new ServerExceptionEvent(new ServerSchedulerException(t, internalTask))
|
||||
); //Do not rethrow, since it is not permitted with Runnable#run
|
||||
}
|
||||
}
|
||||
|
||||
public CraftTask getInternalTask() {
|
||||
return internalTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.IChunkAccess;
|
||||
import net.minecraft.server.IWorldReader;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.PlayerInteractManager;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
public class ChunkPacketBlockController {
|
||||
|
||||
public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController();
|
||||
|
||||
protected ChunkPacketBlockController() {
|
||||
|
||||
}
|
||||
|
||||
public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public ChunkPacketInfo<IBlockData> getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
||||
packetPlayOutMapChunk.setReady(true);
|
||||
}
|
||||
|
||||
public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) {
|
||||
|
||||
}
|
||||
|
||||
public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.minecraft.server.IRegistry;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import org.bukkit.World.Environment;
|
||||
|
||||
import com.destroystokyo.paper.PaperWorldConfig;
|
||||
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.GeneratorAccess;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.IChunkAccess;
|
||||
import net.minecraft.server.IWorldReader;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
import net.minecraft.server.PlayerInteractManager;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController {
|
||||
|
||||
private static ExecutorService executorServiceInstance = null;
|
||||
private final ExecutorService executorService;
|
||||
private final boolean asynchronous;
|
||||
private final EngineMode engineMode;
|
||||
private final ChunkEdgeMode chunkEdgeMode;
|
||||
private final int maxChunkSectionIndex;
|
||||
private final int updateRadius;
|
||||
private final IBlockData[] predefinedBlockData;
|
||||
private final IBlockData[] predefinedBlockDataStone;
|
||||
private final IBlockData[] predefinedBlockDataNetherrack;
|
||||
private final IBlockData[] predefinedBlockDataEndStone;
|
||||
private final int[] predefinedBlockDataBitsGlobal;
|
||||
private final int[] predefinedBlockDataBitsStoneGlobal;
|
||||
private final int[] predefinedBlockDataBitsNetherrackGlobal;
|
||||
private final int[] predefinedBlockDataBitsEndStoneGlobal;
|
||||
private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()];
|
||||
private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()];
|
||||
private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION};
|
||||
private final int maxBlockYUpdatePosition;
|
||||
|
||||
public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) {
|
||||
asynchronous = paperWorldConfig.asynchronous;
|
||||
engineMode = paperWorldConfig.engineMode;
|
||||
chunkEdgeMode = paperWorldConfig.chunkEdgeMode;
|
||||
maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex;
|
||||
updateRadius = paperWorldConfig.updateRadius;
|
||||
|
||||
if (asynchronous) {
|
||||
executorService = getExecutorServiceInstance();
|
||||
} else {
|
||||
executorService = null;
|
||||
}
|
||||
|
||||
if (engineMode == EngineMode.HIDE) {
|
||||
predefinedBlockData = null;
|
||||
predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()};
|
||||
predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()};
|
||||
predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()};
|
||||
predefinedBlockDataBitsGlobal = null;
|
||||
predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.STONE.getBlockData())};
|
||||
predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.NETHERRACK.getBlockData())};
|
||||
predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.END_STONE.getBlockData())};
|
||||
} else {
|
||||
Set<IBlockData> predefinedBlockDataSet = new HashSet<IBlockData>();
|
||||
|
||||
for (String id : paperWorldConfig.hiddenBlocks) {
|
||||
Block block = IRegistry.BLOCK.get(new MinecraftKey(id));
|
||||
|
||||
if (block != null && !block.isTileEntity()) {
|
||||
predefinedBlockDataSet.add(block.getBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[predefinedBlockDataSet.size()]);
|
||||
predefinedBlockDataStone = null;
|
||||
predefinedBlockDataNetherrack = null;
|
||||
predefinedBlockDataEndStone = null;
|
||||
predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length];
|
||||
|
||||
for (int i = 0; i < predefinedBlockData.length; i++) {
|
||||
predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]);
|
||||
}
|
||||
|
||||
predefinedBlockDataBitsStoneGlobal = null;
|
||||
predefinedBlockDataBitsNetherrackGlobal = null;
|
||||
predefinedBlockDataBitsEndStoneGlobal = null;
|
||||
}
|
||||
|
||||
for (String id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) {
|
||||
Block block = IRegistry.BLOCK.get(new MinecraftKey(id));
|
||||
|
||||
if (block != null) {
|
||||
obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < solidGlobal.length; i++) {
|
||||
IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i);
|
||||
|
||||
if (blockData != null) {
|
||||
solidGlobal[i] = blockData.getBlock().isOccluding(blockData) && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER;
|
||||
}
|
||||
}
|
||||
|
||||
this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1;
|
||||
}
|
||||
|
||||
private static ExecutorService getExecutorServiceInstance() {
|
||||
if (executorServiceInstance == null) {
|
||||
executorServiceInstance = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
return executorServiceInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) {
|
||||
//Return the block data which should be added to the data palettes so that they can be used for the obfuscation
|
||||
if (chunkSection.getYPosition() >> 4 <= maxChunkSectionIndex) {
|
||||
switch (engineMode) {
|
||||
case HIDE:
|
||||
if (world instanceof GeneratorAccess) {
|
||||
switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) {
|
||||
case NETHER:
|
||||
return predefinedBlockDataNetherrack;
|
||||
case THE_END:
|
||||
return predefinedBlockDataEndStone;
|
||||
default:
|
||||
return predefinedBlockDataStone;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
default:
|
||||
return predefinedBlockData;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) {
|
||||
//Load nearby chunks if necessary
|
||||
if (force) {
|
||||
// if forced, load NOW;
|
||||
chunk.world.getChunkAt(chunk.locX - 1, chunk.locZ);
|
||||
chunk.world.getChunkAt(chunk.locX + 1, chunk.locZ);
|
||||
chunk.world.getChunkAt(chunk.locX, chunk.locZ - 1);
|
||||
chunk.world.getChunkAt(chunk.locX, chunk.locZ + 1);
|
||||
} else if (chunkEdgeMode == ChunkEdgeMode.WAIT && !force) {
|
||||
if (chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1) == null) {
|
||||
//Don't create the chunk packet now, wait until nearby chunks are loaded and create it later
|
||||
return false;
|
||||
}
|
||||
} else if (chunkEdgeMode == ChunkEdgeMode.LOAD) {
|
||||
boolean missingChunk = false;
|
||||
//noinspection ConstantConditions
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null;
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null;
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null;
|
||||
missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null;
|
||||
|
||||
if (missingChunk) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Create the chunk packet now
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
//Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later
|
||||
ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this);
|
||||
chunkPacketInfoAntiXray.setNearbyChunks(chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1));
|
||||
return chunkPacketInfoAntiXray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo<IBlockData> chunkPacketInfo) {
|
||||
if (asynchronous) {
|
||||
executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo);
|
||||
} else {
|
||||
obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo);
|
||||
}
|
||||
}
|
||||
|
||||
//Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay
|
||||
private int[] predefinedBlockDataBits;
|
||||
private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()];
|
||||
private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()];
|
||||
//These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate
|
||||
private boolean[][] current = new boolean[16][16];
|
||||
private boolean[][] next = new boolean[16][16];
|
||||
private boolean[][] nextNext = new boolean[16][16];
|
||||
private final DataBitsReader dataBitsReader = new DataBitsReader();
|
||||
private final DataBitsWriter dataBitsWriter = new DataBitsWriter();
|
||||
private final ChunkSection[] nearbyChunkSections = new ChunkSection[4];
|
||||
|
||||
public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) {
|
||||
boolean[] solidTemp = null;
|
||||
boolean[] obfuscateTemp = null;
|
||||
dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData());
|
||||
dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData());
|
||||
int counter = 0;
|
||||
|
||||
for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) {
|
||||
if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) {
|
||||
int[] predefinedBlockDataBitsTemp;
|
||||
|
||||
if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) {
|
||||
predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal;
|
||||
} else {
|
||||
predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits;
|
||||
|
||||
for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) {
|
||||
predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
|
||||
|
||||
//Check if the chunk section below was not obfuscated
|
||||
if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) {
|
||||
//If so, initialize some stuff
|
||||
dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
|
||||
dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex));
|
||||
solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal);
|
||||
obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal);
|
||||
//Read the blocks of the upper layer of the chunk section below if it exists
|
||||
ChunkSection belowChunkSection = null;
|
||||
boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION;
|
||||
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
current[z][x] = true;
|
||||
next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))];
|
||||
}
|
||||
}
|
||||
|
||||
//Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section
|
||||
dataBitsWriter.setBitsPerObject(0);
|
||||
obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter);
|
||||
}
|
||||
|
||||
dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex));
|
||||
nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex];
|
||||
nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex];
|
||||
nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex];
|
||||
nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex];
|
||||
|
||||
//Obfuscate all layers of the current chunk section except the upper one
|
||||
for (int y = 0; y < 15; y++) {
|
||||
boolean[][] temp = current;
|
||||
current = next;
|
||||
next = nextNext;
|
||||
nextNext = temp;
|
||||
counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
||||
}
|
||||
|
||||
//Check if the chunk section above doesn't need obfuscation
|
||||
if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex + 1) == null) {
|
||||
//If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists
|
||||
ChunkSection aboveChunkSection;
|
||||
|
||||
if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
boolean[][] temp = current;
|
||||
current = next;
|
||||
next = nextNext;
|
||||
nextNext = temp;
|
||||
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) {
|
||||
current[z][x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//There is nothing to read anymore
|
||||
dataBitsReader.setBitsPerObject(0);
|
||||
solid[0] = true;
|
||||
counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
||||
}
|
||||
} else {
|
||||
//If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section
|
||||
dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1));
|
||||
dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex + 1));
|
||||
solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal);
|
||||
obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal);
|
||||
boolean[][] temp = current;
|
||||
current = next;
|
||||
next = nextNext;
|
||||
nextNext = temp;
|
||||
counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter);
|
||||
}
|
||||
|
||||
dataBitsWriter.finish();
|
||||
}
|
||||
}
|
||||
|
||||
chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true);
|
||||
}
|
||||
|
||||
private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) {
|
||||
//First block of first line
|
||||
int dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[0][0] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[0][1] = true;
|
||||
next[1][0] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[0][0] = true;
|
||||
}
|
||||
|
||||
//First line
|
||||
for (int x = 1; x < 15; x++) {
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[0][x] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[0][x - 1] = true;
|
||||
next[0][x + 1] = true;
|
||||
next[1][x] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[0][x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Last block of first line
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[0][15] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[0][14] = true;
|
||||
next[1][15] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[0][15] = true;
|
||||
}
|
||||
|
||||
//All inner lines
|
||||
for (int z = 1; z < 15; z++) {
|
||||
//First block
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[z][0] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[z][1] = true;
|
||||
next[z - 1][0] = true;
|
||||
next[z + 1][0] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[z][0] = true;
|
||||
}
|
||||
|
||||
//All inner blocks
|
||||
for (int x = 1; x < 15; x++) {
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[z][x] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[z][x - 1] = true;
|
||||
next[z][x + 1] = true;
|
||||
next[z - 1][x] = true;
|
||||
next[z + 1][x] = true;
|
||||
} else {
|
||||
if (current[z][x]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[z][x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Last block
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[z][15] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[z][14] = true;
|
||||
next[z - 1][15] = true;
|
||||
next[z + 1][15] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[z][15] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//First block of last line
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[15][0] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[15][1] = true;
|
||||
next[14][0] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[15][0] = true;
|
||||
}
|
||||
|
||||
//Last line
|
||||
for (int x = 1; x < 15; x++) {
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[15][x] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[15][x - 1] = true;
|
||||
next[15][x + 1] = true;
|
||||
next[14][x] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[15][x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Last block of last line
|
||||
dataBits = dataBitsReader.read();
|
||||
|
||||
if (nextNext[15][15] = !solid[dataBits]) {
|
||||
dataBitsWriter.skip();
|
||||
next[15][14] = true;
|
||||
next[14][15] = true;
|
||||
} else {
|
||||
if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) {
|
||||
dataBitsWriter.skip();
|
||||
} else {
|
||||
if (counter >= predefinedBlockDataBits.length) {
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
dataBitsWriter.write(predefinedBlockDataBits[counter++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!obfuscate[dataBits]) {
|
||||
next[15][15] = true;
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
private boolean[] readDataPalette(DataPalette<IBlockData> dataPalette, boolean[] temp, boolean[] global) {
|
||||
if (dataPalette == ChunkSection.GLOBAL_PALETTE) {
|
||||
return global;
|
||||
}
|
||||
|
||||
IBlockData blockData;
|
||||
|
||||
for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) {
|
||||
temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)];
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) {
|
||||
if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) {
|
||||
updateNearbyBlocks(world, blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) {
|
||||
if (blockPosition.getY() <= maxBlockYUpdatePosition) {
|
||||
updateNearbyBlocks(playerInteractManager.world, blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNearbyBlocks(World world, BlockPosition blockPosition) {
|
||||
if (updateRadius >= 2) {
|
||||
BlockPosition temp = blockPosition.west();
|
||||
updateBlock(world, temp);
|
||||
updateBlock(world, temp.west());
|
||||
updateBlock(world, temp.down());
|
||||
updateBlock(world, temp.up());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.east());
|
||||
updateBlock(world, temp.east());
|
||||
updateBlock(world, temp.down());
|
||||
updateBlock(world, temp.up());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.down());
|
||||
updateBlock(world, temp.down());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.up());
|
||||
updateBlock(world, temp.up());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp.south());
|
||||
updateBlock(world, temp = blockPosition.north());
|
||||
updateBlock(world, temp.north());
|
||||
updateBlock(world, temp = blockPosition.south());
|
||||
updateBlock(world, temp.south());
|
||||
} else if (updateRadius == 1) {
|
||||
updateBlock(world, blockPosition.west());
|
||||
updateBlock(world, blockPosition.east());
|
||||
updateBlock(world, blockPosition.down());
|
||||
updateBlock(world, blockPosition.up());
|
||||
updateBlock(world, blockPosition.north());
|
||||
updateBlock(world, blockPosition.south());
|
||||
} else {
|
||||
//Do nothing if updateRadius <= 0 (test mode)
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBlock(World world, BlockPosition blockPosition) {
|
||||
IBlockData blockData = world.getTypeIfLoaded(blockPosition);
|
||||
|
||||
if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) {
|
||||
world.notify(blockPosition, blockData, blockData, 3);
|
||||
}
|
||||
}
|
||||
|
||||
public enum EngineMode {
|
||||
|
||||
HIDE(1, "hide ores"),
|
||||
OBFUSCATE(2, "obfuscate");
|
||||
|
||||
private final int id;
|
||||
private final String description;
|
||||
|
||||
EngineMode(int id, String description) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static EngineMode getById(int id) {
|
||||
for (EngineMode engineMode : values()) {
|
||||
if (engineMode.id == id) {
|
||||
return engineMode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChunkEdgeMode {
|
||||
|
||||
DEFAULT(1, "default"),
|
||||
WAIT(2, "wait until nearby chunks are loaded"),
|
||||
LOAD(3, "load nearby chunks");
|
||||
|
||||
private final int id;
|
||||
private final String description;
|
||||
|
||||
ChunkEdgeMode(int id, String description) {
|
||||
this.id = id;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public static ChunkEdgeMode getById(int id) {
|
||||
for (ChunkEdgeMode chunkEdgeMode : values()) {
|
||||
if (chunkEdgeMode.id == id) {
|
||||
return chunkEdgeMode;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
public class ChunkPacketInfo<T> {
|
||||
|
||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||
private final Chunk chunk;
|
||||
private final int chunkSectionSelector;
|
||||
private byte[] data;
|
||||
private final int[] bitsPerObject = new int[16];
|
||||
private final Object[] dataPalettes = new Object[16];
|
||||
private final int[] dataBitsIndexes = new int[16];
|
||||
private final Object[][] predefinedObjects = new Object[16][];
|
||||
|
||||
public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||
this.chunk = chunk;
|
||||
this.chunkSectionSelector = chunkSectionSelector;
|
||||
}
|
||||
|
||||
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
|
||||
return packetPlayOutMapChunk;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public int getChunkSectionSelector() {
|
||||
return chunkSectionSelector;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBitsPerObject(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) {
|
||||
this.bitsPerObject[chunkSectionIndex] = bitsPerObject;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public DataPalette<T> getDataPalette(int chunkSectionIndex) {
|
||||
return (DataPalette<T>) dataPalettes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette<T> dataPalette) {
|
||||
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||
}
|
||||
|
||||
public int getOrCreateIdForIndex(int chunkSectionIndex) {
|
||||
return dataBitsIndexes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
||||
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T[] getPredefinedObjects(int chunkSectionIndex) {
|
||||
return (T[]) predefinedObjects[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) {
|
||||
this.predefinedObjects[chunkSectionIndex] = predefinedObjects;
|
||||
}
|
||||
|
||||
public boolean isWritten(int chunkSectionIndex) {
|
||||
return bitsPerObject[chunkSectionIndex] != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
public class ChunkPacketInfoAntiXray extends ChunkPacketInfo<IBlockData> implements Runnable {
|
||||
|
||||
private Chunk[] nearbyChunks;
|
||||
private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray;
|
||||
|
||||
public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) {
|
||||
super(packetPlayOutMapChunk, chunk, chunkSectionSelector);
|
||||
this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray;
|
||||
}
|
||||
|
||||
public Chunk[] getNearbyChunks() {
|
||||
return nearbyChunks;
|
||||
}
|
||||
|
||||
public void setNearbyChunks(Chunk... nearbyChunks) {
|
||||
this.nearbyChunks = nearbyChunks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
chunkPacketBlockControllerAntiXray.obfuscate(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
public class DataBitsReader {
|
||||
|
||||
private byte[] dataBits;
|
||||
private int bitsPerObject;
|
||||
private int mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
|
||||
public void setDataBits(byte[] dataBits) {
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.length > longInDataBitsIndex + 7) {
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
}
|
||||
}
|
||||
|
||||
public int read() {
|
||||
int value = (int) (current >>> bitInLongIndex) & mask;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
value |= current << bitsPerObject - bitInLongIndex & mask;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
public class DataBitsWriter {
|
||||
|
||||
private byte[] dataBits;
|
||||
private int bitsPerObject;
|
||||
private long mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
private boolean dirty;
|
||||
|
||||
public void setDataBits(byte[] dataBits) {
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerObject(int bitsPerObject) {
|
||||
this.bitsPerObject = bitsPerObject;
|
||||
mask = (1 << bitsPerObject) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.length > longInDataBitsIndex + 7) {
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
if (dirty && dataBits.length > longInDataBitsIndex + 7) {
|
||||
dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int value) {
|
||||
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
||||
dirty = true;
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
bitInLongIndex += bitsPerObject;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.destroystokyo.paper.console;
|
||||
|
||||
import net.minecraft.server.DedicatedServer;
|
||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
import org.bukkit.craftbukkit.command.ConsoleCommandCompleter;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
|
||||
public final class PaperConsole extends SimpleTerminalConsole {
|
||||
|
||||
private final DedicatedServer server;
|
||||
|
||||
public PaperConsole(DedicatedServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LineReader buildReader(LineReaderBuilder builder) {
|
||||
return super.buildReader(builder
|
||||
.appName("Paper")
|
||||
.completer(new ConsoleCommandCompleter(this.server))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRunning() {
|
||||
return !this.server.isStopped() && this.server.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runCommand(String command) {
|
||||
this.server.issueCommand(command, this.server.getServerCommandListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
this.server.safeShutdown();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.destroystokyo.paper.console;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.craftbukkit.command.CraftConsoleCommandSender;
|
||||
|
||||
public class TerminalConsoleCommandSender extends CraftConsoleCommandSender {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getRootLogger();
|
||||
|
||||
@Override
|
||||
public void sendRawMessage(String message) {
|
||||
// TerminalConsoleAppender supports color codes directly in log messages
|
||||
LOGGER.info(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.destroystokyo.paper.entity;
|
||||
|
||||
import net.minecraft.server.IRangedEntity;
|
||||
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
public interface CraftRangedEntity<T extends IRangedEntity> extends RangedEntity {
|
||||
T getHandle();
|
||||
|
||||
@Override
|
||||
default void rangedAttack(LivingEntity target, float charge) {
|
||||
getHandle().rangedAttack(((CraftLivingEntity) target).getHandle(), charge);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setChargingAttack(boolean raiseHands) {
|
||||
getHandle().setChargingAttack(raiseHands);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.destroystokyo.paper.entity;
|
||||
|
||||
import net.minecraft.server.EntityInsentient;
|
||||
import net.minecraft.server.PathEntity;
|
||||
import net.minecraft.server.PathPoint;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.entity.CraftLivingEntity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Mob;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PaperPathfinder implements com.destroystokyo.paper.entity.Pathfinder {
|
||||
|
||||
private final EntityInsentient entity;
|
||||
|
||||
public PaperPathfinder(EntityInsentient entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mob getEntity() {
|
||||
return entity.getBukkitMob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopPathfinding() {
|
||||
entity.getNavigation().stopPathfinding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPath() {
|
||||
return entity.getNavigation().getPathEntity() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PathResult getCurrentPath() {
|
||||
PathEntity path = entity.getNavigation().getPathEntity();
|
||||
return path != null ? new PaperPathResult(path) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PathResult findPath(Location loc) {
|
||||
Validate.notNull(loc, "Location can not be null");
|
||||
PathEntity path = entity.getNavigation().calculateDestination(loc.getX(), loc.getY(), loc.getZ());
|
||||
return path != null ? new PaperPathResult(path) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PathResult findPath(LivingEntity target) {
|
||||
Validate.notNull(target, "Target can not be null");
|
||||
PathEntity path = entity.getNavigation().calculateDestination(((CraftLivingEntity) target).getHandle());
|
||||
return path != null ? new PaperPathResult(path) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveTo(@Nonnull PathResult path, double speed) {
|
||||
Validate.notNull(path, "PathResult can not be null");
|
||||
PathEntity pathEntity = ((PaperPathResult) path).path;
|
||||
return entity.getNavigation().setDestination(pathEntity, speed);
|
||||
}
|
||||
|
||||
public class PaperPathResult implements com.destroystokyo.paper.entity.PaperPathfinder.PathResult {
|
||||
|
||||
private final PathEntity path;
|
||||
PaperPathResult(PathEntity path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getFinalPoint() {
|
||||
PathPoint point = path.getFinalPoint();
|
||||
return point != null ? toLoc(point) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> getPoints() {
|
||||
int pathCount = path.getPathCount();
|
||||
List<Location> points = new ArrayList<>();
|
||||
PathPoint[] pathPoints = path.getPoints();
|
||||
for (int i = 0; i < pathCount; i++) {
|
||||
points.add(toLoc(pathPoints[i]));
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextPointIndex() {
|
||||
return path.getNextIndex();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getNextPoint() {
|
||||
if (!path.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
return toLoc(path.getPoints()[path.getNextIndex()]);
|
||||
}
|
||||
}
|
||||
|
||||
private Location toLoc(PathPoint point) {
|
||||
return new Location(entity.world.getWorld(), point.getX(), point.getY(), point.getZ());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.TileEntityLootable;
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
public interface PaperLootableBlockInventory extends LootableBlockInventory, PaperLootableInventory {
|
||||
|
||||
TileEntityLootable getTileEntity();
|
||||
|
||||
@Override
|
||||
default LootableInventory getAPILootableInventory() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default World getNMSWorld() {
|
||||
return getTileEntity().getWorld();
|
||||
}
|
||||
|
||||
default Block getBlock() {
|
||||
final BlockPosition position = getTileEntity().getPosition();
|
||||
final Chunk bukkitChunk = getTileEntity().getWorld().getChunkAtWorldCoords(position).bukkitChunk;
|
||||
return bukkitChunk.getBlock(position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
default PaperLootableInventoryData getLootableData() {
|
||||
return getTileEntity().lootableData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
public interface PaperLootableEntityInventory extends LootableEntityInventory, PaperLootableInventory {
|
||||
|
||||
net.minecraft.server.Entity getHandle();
|
||||
|
||||
@Override
|
||||
default LootableInventory getAPILootableInventory() {
|
||||
return this;
|
||||
}
|
||||
|
||||
default Entity getEntity() {
|
||||
return getHandle().getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
default World getNMSWorld() {
|
||||
return getHandle().getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
default PaperLootableInventoryData getLootableData() {
|
||||
return getHandle().lootableData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.loot.Lootable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface PaperLootableInventory extends LootableInventory, Lootable {
|
||||
|
||||
PaperLootableInventoryData getLootableData();
|
||||
LootableInventory getAPILootableInventory();
|
||||
|
||||
World getNMSWorld();
|
||||
|
||||
default org.bukkit.World getBukkitWorld() {
|
||||
return getNMSWorld().getWorld();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isRefillEnabled() {
|
||||
return getNMSWorld().paperConfig.autoReplenishLootables;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasBeenFilled() {
|
||||
return getLastFilled() != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasPlayerLooted(UUID player) {
|
||||
return getLootableData().hasPlayerLooted(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
default Long getLastLooted(UUID player) {
|
||||
return getLootableData().getLastLooted(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean setHasPlayerLooted(UUID player, boolean looted) {
|
||||
final boolean hasLooted = hasPlayerLooted(player);
|
||||
if (hasLooted != looted) {
|
||||
getLootableData().setPlayerLootedState(player, looted);
|
||||
}
|
||||
return hasLooted;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasPendingRefill() {
|
||||
long nextRefill = getLootableData().getNextRefill();
|
||||
return nextRefill != -1 && nextRefill > getLootableData().getLastFill();
|
||||
}
|
||||
|
||||
@Override
|
||||
default long getLastFilled() {
|
||||
return getLootableData().getLastFill();
|
||||
}
|
||||
|
||||
@Override
|
||||
default long getNextRefill() {
|
||||
return getLootableData().getNextRefill();
|
||||
}
|
||||
|
||||
@Override
|
||||
default long setNextRefill(long refillAt) {
|
||||
if (refillAt < -1) {
|
||||
refillAt = -1;
|
||||
}
|
||||
return getLootableData().setNextRefill(refillAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import com.destroystokyo.paper.PaperWorldConfig;
|
||||
import net.minecraft.server.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.loot.LootTable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PaperLootableInventoryData {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private long lastFill = -1;
|
||||
private long nextRefill = -1;
|
||||
private int numRefills = 0;
|
||||
private Map<UUID, Long> lootedPlayers;
|
||||
private final PaperLootableInventory lootable;
|
||||
|
||||
public PaperLootableInventoryData(PaperLootableInventory lootable) {
|
||||
this.lootable = lootable;
|
||||
}
|
||||
|
||||
long getLastFill() {
|
||||
return this.lastFill;
|
||||
}
|
||||
|
||||
long getNextRefill() {
|
||||
return this.nextRefill;
|
||||
}
|
||||
|
||||
long setNextRefill(long nextRefill) {
|
||||
long prev = this.nextRefill;
|
||||
this.nextRefill = nextRefill;
|
||||
return prev;
|
||||
}
|
||||
|
||||
public boolean shouldReplenish(@Nullable EntityHuman player) {
|
||||
LootTable table = this.lootable.getLootTable();
|
||||
|
||||
// No Loot Table associated
|
||||
if (table == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ALWAYS process the first fill or if the feature is disabled
|
||||
if (this.lastFill == -1 || !this.lootable.getNMSWorld().paperConfig.autoReplenishLootables) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only process refills when a player is set
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Chest is not scheduled for refill
|
||||
if (this.nextRefill == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig;
|
||||
|
||||
// Check if max refills has been hit
|
||||
if (paperConfig.maxLootableRefills != -1 && this.numRefills >= paperConfig.maxLootableRefills) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Refill has not been reached
|
||||
if (this.nextRefill > System.currentTimeMillis()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final Player bukkitPlayer = (Player) player.getBukkitEntity();
|
||||
LootableInventoryReplenishEvent event = new LootableInventoryReplenishEvent(bukkitPlayer, lootable.getAPILootableInventory());
|
||||
if (paperConfig.restrictPlayerReloot && hasPlayerLooted(player.getUniqueID())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return event.callEvent();
|
||||
}
|
||||
public void processRefill(@Nullable EntityHuman player) {
|
||||
this.lastFill = System.currentTimeMillis();
|
||||
final PaperWorldConfig paperConfig = this.lootable.getNMSWorld().paperConfig;
|
||||
if (paperConfig.autoReplenishLootables) {
|
||||
int min = paperConfig.lootableRegenMin;
|
||||
int max = paperConfig.lootableRegenMax;
|
||||
this.nextRefill = this.lastFill + (min + RANDOM.nextInt(max - min + 1)) * 1000L;
|
||||
this.numRefills++;
|
||||
if (paperConfig.changeLootTableSeedOnFill) {
|
||||
this.lootable.setSeed(0);
|
||||
}
|
||||
if (player != null) { // This means that numRefills can be incremented without a player being in the lootedPlayers list - Seems to be EntityMinecartChest specific
|
||||
this.setPlayerLootedState(player.getUniqueID(), true);
|
||||
}
|
||||
} else {
|
||||
this.lootable.clearLootTable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void loadNbt(NBTTagCompound base) {
|
||||
if (!base.hasKeyOfType("Paper.LootableData", 10)) { // 10 = compound
|
||||
return;
|
||||
}
|
||||
NBTTagCompound comp = base.getCompound("Paper.LootableData");
|
||||
if (comp.hasKey("lastFill")) {
|
||||
this.lastFill = comp.getLong("lastFill");
|
||||
}
|
||||
if (comp.hasKey("nextRefill")) {
|
||||
this.nextRefill = comp.getLong("nextRefill");
|
||||
}
|
||||
|
||||
if (comp.hasKey("numRefills")) {
|
||||
this.numRefills = comp.getInt("numRefills");
|
||||
}
|
||||
if (comp.hasKeyOfType("lootedPlayers", 9)) { // 9 = list
|
||||
NBTTagList list = comp.getList("lootedPlayers", 10); // 10 = compound
|
||||
final int size = list.size();
|
||||
if (size > 0) {
|
||||
this.lootedPlayers = new HashMap<>(list.size());
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
final NBTTagCompound cmp = list.getCompound(i);
|
||||
lootedPlayers.put(cmp.getUUID("UUID"), cmp.getLong("Time"));
|
||||
}
|
||||
}
|
||||
}
|
||||
public void saveNbt(NBTTagCompound base) {
|
||||
NBTTagCompound comp = new NBTTagCompound();
|
||||
if (this.nextRefill != -1) {
|
||||
comp.setLong("nextRefill", this.nextRefill);
|
||||
}
|
||||
if (this.lastFill != -1) {
|
||||
comp.setLong("lastFill", this.lastFill);
|
||||
}
|
||||
if (this.numRefills != 0) {
|
||||
comp.setInt("numRefills", this.numRefills);
|
||||
}
|
||||
if (this.lootedPlayers != null && !this.lootedPlayers.isEmpty()) {
|
||||
NBTTagList list = new NBTTagList();
|
||||
for (Map.Entry<UUID, Long> entry : this.lootedPlayers.entrySet()) {
|
||||
NBTTagCompound cmp = new NBTTagCompound();
|
||||
cmp.setUUID("UUID", entry.getKey());
|
||||
cmp.setLong("Time", entry.getValue());
|
||||
list.add(cmp);
|
||||
}
|
||||
comp.set("lootedPlayers", list);
|
||||
}
|
||||
|
||||
if (!comp.isEmpty()) {
|
||||
base.set("Paper.LootableData", comp);
|
||||
}
|
||||
}
|
||||
|
||||
void setPlayerLootedState(UUID player, boolean looted) {
|
||||
if (looted && this.lootedPlayers == null) {
|
||||
this.lootedPlayers = new HashMap<>();
|
||||
}
|
||||
if (looted) {
|
||||
if (!this.lootedPlayers.containsKey(player)) {
|
||||
this.lootedPlayers.put(player, System.currentTimeMillis());
|
||||
}
|
||||
} else if (this.lootedPlayers != null) {
|
||||
this.lootedPlayers.remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasPlayerLooted(UUID player) {
|
||||
return this.lootedPlayers != null && this.lootedPlayers.containsKey(player);
|
||||
}
|
||||
|
||||
Long getLastLooted(UUID player) {
|
||||
return lootedPlayers != null ? lootedPlayers.get(player) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityMinecartContainer;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
|
||||
public class PaperMinecartLootableInventory implements PaperLootableEntityInventory {
|
||||
|
||||
private EntityMinecartContainer entity;
|
||||
|
||||
public PaperMinecartLootableInventory(EntityMinecartContainer entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.loot.LootTable getLootTable() {
|
||||
return entity.getLootTableKey() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(entity.getLootTableKey())) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table, long seed) {
|
||||
setLootTable(table);
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
entity.lootTableSeed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return entity.lootTableSeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table) {
|
||||
MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey());
|
||||
entity.setLootTable(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperLootableInventoryData getLootableData() {
|
||||
return entity.lootableData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getHandle() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LootableInventory getAPILootableInventory() {
|
||||
return (LootableInventory) entity.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getNMSWorld() {
|
||||
return entity.world;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.destroystokyo.paper.loottable;
|
||||
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.TileEntityLootable;
|
||||
import net.minecraft.server.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||
|
||||
public class PaperTileEntityLootableInventory implements PaperLootableBlockInventory {
|
||||
private TileEntityLootable tileEntityLootable;
|
||||
|
||||
public PaperTileEntityLootableInventory(TileEntityLootable tileEntityLootable) {
|
||||
this.tileEntityLootable = tileEntityLootable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.loot.LootTable getLootTable() {
|
||||
return tileEntityLootable.getLootTableKey() != null ? Bukkit.getLootTable(CraftNamespacedKey.fromMinecraft(tileEntityLootable.getLootTableKey())) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table, long seed) {
|
||||
setLootTable(table);
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLootTable(org.bukkit.loot.LootTable table) {
|
||||
MinecraftKey newKey = (table == null) ? null : CraftNamespacedKey.toMinecraft(table.getKey());
|
||||
tileEntityLootable.setLootTable(newKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
tileEntityLootable.setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSeed() {
|
||||
return tileEntityLootable.getSeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperLootableInventoryData getLootableData() {
|
||||
return tileEntityLootable.lootableData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntityLootable getTileEntity() {
|
||||
return tileEntityLootable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LootableInventory getAPILootableInventory() {
|
||||
World world = tileEntityLootable.getWorld();
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
return (LootableInventory) getBukkitWorld().getBlockAt(MCUtil.toLocation(world, tileEntityLootable.getPosition())).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getNMSWorld() {
|
||||
return tileEntityLootable.getWorld();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class PaperLegacyStatusClient implements StatusClient {
|
||||
|
||||
private final InetSocketAddress address;
|
||||
private final int protocolVersion;
|
||||
@Nullable private final InetSocketAddress virtualHost;
|
||||
|
||||
private PaperLegacyStatusClient(InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) {
|
||||
this.address = address;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.virtualHost = virtualHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return this.protocolVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost() {
|
||||
return this.virtualHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static PaperServerListPingEvent processRequest(MinecraftServer server,
|
||||
InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) {
|
||||
|
||||
PaperServerListPingEvent event = new PaperServerListPingEventImpl(server,
|
||||
new PaperLegacyStatusClient(address, protocolVersion, virtualHost), Byte.MAX_VALUE, null);
|
||||
server.server.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
public static String getMotd(PaperServerListPingEvent event) {
|
||||
return getFirstLine(event.getMotd());
|
||||
}
|
||||
|
||||
public static String getUnformattedMotd(PaperServerListPingEvent event) {
|
||||
// Strip color codes and all other occurrences of the color char (because it's used as delimiter)
|
||||
return getFirstLine(StringUtils.remove(ChatColor.stripColor(event.getMotd()), ChatColor.COLOR_CHAR));
|
||||
}
|
||||
|
||||
private static String getFirstLine(String s) {
|
||||
int pos = s.indexOf('\n');
|
||||
return pos >= 0 ? s.substring(0, pos) : s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import net.minecraft.server.NetworkManager;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperNetworkClient implements NetworkClient {
|
||||
|
||||
private final NetworkManager networkManager;
|
||||
|
||||
PaperNetworkClient(NetworkManager networkManager) {
|
||||
this.networkManager = networkManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return (InetSocketAddress) this.networkManager.getSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return this.networkManager.protocolVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost() {
|
||||
return this.networkManager.virtualHost;
|
||||
}
|
||||
|
||||
public static InetSocketAddress prepareVirtualHost(String host, int port) {
|
||||
int len = host.length();
|
||||
|
||||
// FML appends a marker to the host to recognize FML clients (\0FML\0)
|
||||
int pos = host.indexOf('\0');
|
||||
if (pos >= 0) {
|
||||
len = pos;
|
||||
}
|
||||
|
||||
// When clients connect with a SRV record, their host contains a trailing '.'
|
||||
if (len > 0 && host.charAt(len - 1) == '.') {
|
||||
len--;
|
||||
}
|
||||
|
||||
return InetSocketAddress.createUnresolved(host.substring(0, len), port);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class PaperServerListPingEventImpl extends PaperServerListPingEvent {
|
||||
|
||||
private final MinecraftServer server;
|
||||
|
||||
PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon) {
|
||||
super(client, server.getMotd(), server.getPlayerCount(), server.getMaxPlayers(),
|
||||
server.getServerModName() + ' ' + server.getVersion(), protocolVersion, icon);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Object[] getOnlinePlayers() {
|
||||
return this.server.getPlayerList().players.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final Player getBukkitPlayer(Object player) {
|
||||
return ((EntityPlayer) player).getBukkitEntity();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import net.minecraft.server.NetworkManager;
|
||||
|
||||
class PaperStatusClient extends PaperNetworkClient implements StatusClient {
|
||||
|
||||
PaperStatusClient(NetworkManager networkManager) {
|
||||
super(networkManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.destroystokyo.paper.network;
|
||||
|
||||
import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.ChatComponentText;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.NetworkManager;
|
||||
import net.minecraft.server.PacketStatusOutServerInfo;
|
||||
import net.minecraft.server.ServerPing;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class StandardPaperServerListPingEventImpl extends PaperServerListPingEventImpl {
|
||||
|
||||
private static final GameProfile[] EMPTY_PROFILES = new GameProfile[0];
|
||||
private static final UUID FAKE_UUID = new UUID(0, 0);
|
||||
|
||||
private GameProfile[] originalSample;
|
||||
|
||||
private StandardPaperServerListPingEventImpl(MinecraftServer server, NetworkManager networkManager, ServerPing ping) {
|
||||
super(server, new PaperStatusClient(networkManager), ping.getServerData() != null ? ping.getServerData().getProtocolVersion() : -1, server.server.getServerIcon());
|
||||
this.originalSample = ping.getPlayers() == null ? null : ping.getPlayers().getSample(); // GH-1473 - pre-tick race condition NPE
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<PlayerProfile> getPlayerSample() {
|
||||
List<PlayerProfile> sample = super.getPlayerSample();
|
||||
|
||||
if (this.originalSample != null) {
|
||||
for (GameProfile profile : this.originalSample) {
|
||||
sample.add(CraftPlayerProfile.asBukkitCopy(profile));
|
||||
}
|
||||
this.originalSample = null;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
private GameProfile[] getPlayerSampleHandle() {
|
||||
if (this.originalSample != null) {
|
||||
return this.originalSample;
|
||||
}
|
||||
|
||||
List<PlayerProfile> entries = super.getPlayerSample();
|
||||
if (entries.isEmpty()) {
|
||||
return EMPTY_PROFILES;
|
||||
}
|
||||
|
||||
GameProfile[] profiles = new GameProfile[entries.size()];
|
||||
for (int i = 0; i < profiles.length; i++) {
|
||||
/*
|
||||
* Avoid null UUIDs/names since that will make the response invalid
|
||||
* on the client.
|
||||
* Instead, fall back to a fake/empty UUID and an empty string as name.
|
||||
* This can be used to create custom lines in the player list that do not
|
||||
* refer to a specific player.
|
||||
*/
|
||||
|
||||
PlayerProfile profile = entries.get(i);
|
||||
if (profile.getId() != null && profile.getName() != null) {
|
||||
profiles[i] = CraftPlayerProfile.asAuthlib(profile);
|
||||
} else {
|
||||
profiles[i] = new GameProfile(MoreObjects.firstNonNull(profile.getId(), FAKE_UUID), Strings.nullToEmpty(profile.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return profiles;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void processRequest(MinecraftServer server, NetworkManager networkManager) {
|
||||
StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getServerPing());
|
||||
server.server.getPluginManager().callEvent(event);
|
||||
|
||||
// Close connection immediately if event is cancelled
|
||||
if (event.isCancelled()) {
|
||||
networkManager.close(null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup response
|
||||
ServerPing ping = new ServerPing();
|
||||
|
||||
// Description
|
||||
ping.setMOTD(new ChatComponentText(event.getMotd()));
|
||||
|
||||
// Players
|
||||
if (!event.shouldHidePlayers()) {
|
||||
ping.setPlayerSample(new ServerPing.ServerPingPlayerSample(event.getMaxPlayers(), event.getNumPlayers()));
|
||||
ping.getPlayers().setSample(event.getPlayerSampleHandle());
|
||||
}
|
||||
|
||||
// Version
|
||||
ping.setServerInfo(new ServerPing.ServerData(event.getVersion(), event.getProtocolVersion()));
|
||||
|
||||
// Favicon
|
||||
if (event.getServerIcon() != null) {
|
||||
ping.setFavicon(event.getServerIcon().getData());
|
||||
}
|
||||
|
||||
// Send response
|
||||
networkManager.sendPacket(new PacketStatusOutServerInfo(ping));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.UserCache;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CraftPlayerProfile implements PlayerProfile {
|
||||
|
||||
private GameProfile profile;
|
||||
private final PropertySet properties = new PropertySet();
|
||||
|
||||
public CraftPlayerProfile(CraftPlayer player) {
|
||||
this.profile = player.getHandle().getProfile();
|
||||
}
|
||||
|
||||
public CraftPlayerProfile(UUID id, String name) {
|
||||
this.profile = new GameProfile(id, name);
|
||||
}
|
||||
|
||||
public CraftPlayerProfile(GameProfile profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasProperty(String property) {
|
||||
return profile.getProperties().containsKey(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(ProfileProperty property) {
|
||||
String name = property.getName();
|
||||
PropertyMap properties = profile.getProperties();
|
||||
properties.removeAll(name);
|
||||
properties.put(name, new Property(name, property.getValue(), property.getSignature()));
|
||||
}
|
||||
|
||||
public GameProfile getGameProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return profile.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID setId(@Nullable UUID uuid) {
|
||||
GameProfile prev = this.profile;
|
||||
this.profile = new GameProfile(uuid, prev.getName());
|
||||
copyProfileProperties(prev, this.profile);
|
||||
return prev.getId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getName() {
|
||||
return profile.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setName(@Nullable String name) {
|
||||
GameProfile prev = this.profile;
|
||||
this.profile = new GameProfile(prev.getId(), name);
|
||||
copyProfileProperties(prev, this.profile);
|
||||
return prev.getName();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<ProfileProperty> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Collection<ProfileProperty> properties) {
|
||||
properties.forEach(this::setProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearProperties() {
|
||||
profile.getProperties().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeProperty(String property) {
|
||||
return !profile.getProperties().removeAll(property).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CraftPlayerProfile that = (CraftPlayerProfile) o;
|
||||
return Objects.equals(profile, that.profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return profile.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return profile.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CraftPlayerProfile clone() {
|
||||
CraftPlayerProfile clone = new CraftPlayerProfile(this.getId(), this.getName());
|
||||
clone.setProperties(getProperties());
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return profile.isComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean completeFromCache() {
|
||||
return completeFromCache(false);
|
||||
}
|
||||
|
||||
public boolean completeFromCache(boolean lookupName) {
|
||||
if (profile.isComplete()) {
|
||||
return true;
|
||||
}
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
String name = profile.getName();
|
||||
UserCache userCache = server.getUserCache();
|
||||
if (profile.getId() == null) {
|
||||
final GameProfile profile;
|
||||
boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode);
|
||||
if (isOnlineMode) {
|
||||
profile = lookupName ? userCache.getProfile(name) : userCache.getProfileIfCached(name);
|
||||
} else {
|
||||
// Make an OfflinePlayer using an offline mode UUID since the name has no profile
|
||||
profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
|
||||
}
|
||||
if (profile != null) {
|
||||
this.profile = profile;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile.getName() == null) {
|
||||
// If we need textures, skip this check, as we will get it below anyways.
|
||||
GameProfile profile = userCache.getProfile(this.profile.getId());
|
||||
if (profile != null) {
|
||||
this.profile = profile;
|
||||
}
|
||||
}
|
||||
return this.profile.isComplete();
|
||||
}
|
||||
|
||||
public boolean complete(boolean textures) {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode);
|
||||
boolean isCompleteFromCache = this.completeFromCache(true);
|
||||
if (isOnlineMode && (!isCompleteFromCache || textures && !hasTextures())) {
|
||||
GameProfile result = server.getSessionService().fillProfileProperties(profile, true);
|
||||
if (result != null) {
|
||||
this.profile = result;
|
||||
}
|
||||
}
|
||||
return profile.isComplete() && (!isOnlineMode || !textures || hasTextures());
|
||||
}
|
||||
|
||||
private static void copyProfileProperties(GameProfile source, GameProfile target) {
|
||||
PropertyMap sourceProperties = source.getProperties();
|
||||
if (sourceProperties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
PropertyMap properties = target.getProperties();
|
||||
properties.clear();
|
||||
|
||||
for (Property property : sourceProperties.values()) {
|
||||
properties.put(property.getName(), property);
|
||||
}
|
||||
}
|
||||
|
||||
private static ProfileProperty toBukkit(Property property) {
|
||||
return new ProfileProperty(property.getName(), property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
public static PlayerProfile asBukkitCopy(GameProfile gameProfile) {
|
||||
CraftPlayerProfile profile = new CraftPlayerProfile(gameProfile.getId(), gameProfile.getName());
|
||||
copyProfileProperties(gameProfile, profile.profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
public static PlayerProfile asBukkitMirror(GameProfile profile) {
|
||||
return new CraftPlayerProfile(profile);
|
||||
}
|
||||
|
||||
public static Property asAuthlib(ProfileProperty property) {
|
||||
return new Property(property.getName(), property.getValue(), property.getSignature());
|
||||
}
|
||||
|
||||
public static GameProfile asAuthlibCopy(PlayerProfile profile) {
|
||||
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
|
||||
return asAuthlib(craft.clone());
|
||||
}
|
||||
|
||||
public static GameProfile asAuthlib(PlayerProfile profile) {
|
||||
CraftPlayerProfile craft = ((CraftPlayerProfile) profile);
|
||||
return craft.getGameProfile();
|
||||
}
|
||||
|
||||
private class PropertySet extends AbstractSet<ProfileProperty> {
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Iterator<ProfileProperty> iterator() {
|
||||
return new ProfilePropertyIterator(profile.getProperties().values().iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return profile.getProperties().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(ProfileProperty property) {
|
||||
setProperty(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends ProfileProperty> c) {
|
||||
//noinspection unchecked
|
||||
setProperties((Collection<ProfileProperty>) c);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return o instanceof ProfileProperty && profile.getProperties().containsKey(((ProfileProperty) o).getName());
|
||||
}
|
||||
|
||||
private class ProfilePropertyIterator implements Iterator<ProfileProperty> {
|
||||
private final Iterator<Property> iterator;
|
||||
|
||||
ProfilePropertyIterator(Iterator<Property> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileProperty next() {
|
||||
return toBukkit(iterator.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.GameProfileRepository;
|
||||
import com.mojang.authlib.UserAuthentication;
|
||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
|
||||
import java.net.Proxy;
|
||||
|
||||
public class PaperAuthenticationService extends YggdrasilAuthenticationService {
|
||||
public PaperAuthenticationService(Proxy proxy, String clientToken) {
|
||||
super(proxy, clientToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAuthentication createUserAuthentication(Agent agent) {
|
||||
return new PaperUserAuthentication(this, agent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftSessionService createMinecraftSessionService() {
|
||||
return new PaperMinecraftSessionService(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfileRepository createProfileRepository() {
|
||||
return new PaperGameProfileRepository(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.destroystokyo.paper.event.profile.LookupProfileEvent;
|
||||
import com.destroystokyo.paper.event.profile.PreLookupProfileEvent;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.ProfileLookupCallback;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class PaperGameProfileRepository extends YggdrasilGameProfileRepository {
|
||||
|
||||
public PaperGameProfileRepository(YggdrasilAuthenticationService authenticationService) {
|
||||
super(authenticationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) {
|
||||
Set<String> unfoundNames = Sets.newHashSet();
|
||||
for (String name : names) {
|
||||
PreLookupProfileEvent event = new PreLookupProfileEvent(name);
|
||||
event.callEvent();
|
||||
if (event.getUUID() != null) {
|
||||
// Plugin provided UUI, we can skip network call.
|
||||
GameProfile gameprofile = new GameProfile(event.getUUID(), name);
|
||||
// We might even have properties!
|
||||
Set<ProfileProperty> profileProperties = event.getProfileProperties();
|
||||
if (!profileProperties.isEmpty()) {
|
||||
for (ProfileProperty property : profileProperties) {
|
||||
gameprofile.getProperties().put(property.getName(), CraftPlayerProfile.asAuthlib(property));
|
||||
}
|
||||
}
|
||||
callback.onProfileLookupSucceeded(gameprofile);
|
||||
} else {
|
||||
unfoundNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Some things were not found.... Proceed to look up.
|
||||
if (!unfoundNames.isEmpty()) {
|
||||
String[] namesArr = unfoundNames.toArray(new String[unfoundNames.size()]);
|
||||
super.findProfilesByNames(namesArr, agent, new PreProfileLookupCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
private static class PreProfileLookupCallback implements ProfileLookupCallback {
|
||||
private final ProfileLookupCallback callback;
|
||||
|
||||
PreProfileLookupCallback(ProfileLookupCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfileLookupSucceeded(GameProfile gameProfile) {
|
||||
PlayerProfile from = CraftPlayerProfile.asBukkitMirror(gameProfile);
|
||||
new LookupProfileEvent(from).callEvent();
|
||||
callback.onProfileLookupSucceeded(gameProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfileLookupFailed(GameProfile gameProfile, Exception e) {
|
||||
callback.onProfileLookupFailed(gameProfile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.destroystokyo.paper.event.profile.FillProfileEvent;
|
||||
import com.destroystokyo.paper.event.profile.PreFillProfileEvent;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PaperMinecraftSessionService extends YggdrasilMinecraftSessionService {
|
||||
protected PaperMinecraftSessionService(YggdrasilAuthenticationService authenticationService) {
|
||||
super(authenticationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
|
||||
return super.getTextures(profile, requireSecure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
|
||||
CraftPlayerProfile playerProfile = (CraftPlayerProfile) CraftPlayerProfile.asBukkitMirror(profile);
|
||||
new PreFillProfileEvent(playerProfile).callEvent();
|
||||
profile = playerProfile.getGameProfile();
|
||||
if (profile.isComplete() && profile.getProperties().containsKey("textures")) {
|
||||
return profile;
|
||||
}
|
||||
GameProfile gameProfile = super.fillProfileProperties(profile, requireSecure);
|
||||
new FillProfileEvent(CraftPlayerProfile.asBukkitMirror(gameProfile)).callEvent();
|
||||
return gameProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GameProfile fillGameProfile(GameProfile profile, boolean requireSecure) {
|
||||
return super.fillGameProfile(profile, requireSecure);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.destroystokyo.paper.profile;
|
||||
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
|
||||
|
||||
public class PaperUserAuthentication extends YggdrasilUserAuthentication {
|
||||
public PaperUserAuthentication(YggdrasilAuthenticationService authenticationService, Agent agent) {
|
||||
super(authenticationService, agent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.destroystokyo.paper.proxy;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import net.minecraft.server.MinecraftKey;
|
||||
import net.minecraft.server.PacketDataSerializer;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class VelocityProxy {
|
||||
private static final int SUPPORTED_FORWARDING_VERSION = 1;
|
||||
public static final MinecraftKey PLAYER_INFO_CHANNEL = new MinecraftKey("velocity", "player_info");
|
||||
|
||||
public static boolean checkIntegrity(final PacketDataSerializer buf) {
|
||||
final byte[] signature = new byte[32];
|
||||
buf.readBytes(signature);
|
||||
|
||||
final byte[] data = new byte[buf.readableBytes()];
|
||||
buf.getBytes(buf.readerIndex(), data);
|
||||
|
||||
try {
|
||||
final Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(new SecretKeySpec(PaperConfig.velocitySecretKey, "HmacSHA256"));
|
||||
final byte[] mySignature = mac.doFinal(data);
|
||||
if (!MessageDigest.isEqual(signature, mySignature)) {
|
||||
return false;
|
||||
}
|
||||
} catch (final InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
int version = buf.readVarInt();
|
||||
if (version != SUPPORTED_FORWARDING_VERSION) {
|
||||
throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + SUPPORTED_FORWARDING_VERSION);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static InetAddress readAddress(final PacketDataSerializer buf) {
|
||||
return InetAddresses.forString(buf.readUTF(Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
public static GameProfile createProfile(final PacketDataSerializer buf) {
|
||||
final GameProfile profile = new GameProfile(buf.readUUID(), buf.readUTF(16));
|
||||
readProperties(buf, profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private static void readProperties(final PacketDataSerializer buf, final GameProfile profile) {
|
||||
final int properties = buf.readVarInt();
|
||||
for (int i1 = 0; i1 < properties; i1++) {
|
||||
final String name = buf.readUTF(Short.MAX_VALUE);
|
||||
final String value = buf.readUTF(Short.MAX_VALUE);
|
||||
final String signature = buf.readBoolean() ? buf.readUTF(Short.MAX_VALUE) : null;
|
||||
profile.getProperties().put(name, new Property(name, value, signature));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
package com.destroystokyo.paper.util;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.AbstractExecutorService;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Implements an Executor Service that allows specifying Task Priority
|
||||
* and bumping of task priority.
|
||||
*
|
||||
* This is a non blocking executor with 3 priority levels.
|
||||
*
|
||||
* URGENT: Rarely used, something that is critical to take action now.
|
||||
* HIGH: Something with more importance than the base tasks
|
||||
*
|
||||
* @author Daniel Ennis <aikar@aikar.co>
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"})
|
||||
public class PriorityQueuedExecutor extends AbstractExecutorService {
|
||||
|
||||
private final ConcurrentLinkedQueue<Runnable> urgent = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Runnable> high = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Runnable> normal = new ConcurrentLinkedQueue<>();
|
||||
private final List<Thread> threads = new ArrayList<>();
|
||||
private final RejectionHandler handler;
|
||||
|
||||
private volatile boolean shuttingDown = false;
|
||||
private volatile boolean shuttingDownNow = false;
|
||||
|
||||
public PriorityQueuedExecutor(String name) {
|
||||
this(name, Math.max(1, Runtime.getRuntime().availableProcessors() - 1));
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads) {
|
||||
this(name, threads, Thread.NORM_PRIORITY, null);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads, int threadPriority) {
|
||||
this(name, threads, threadPriority, null);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads, RejectionHandler handler) {
|
||||
this(name, threads, Thread.NORM_PRIORITY, handler);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor(String name, int threads, int threadPriority, RejectionHandler handler) {
|
||||
for (int i = 0; i < threads; i++) {
|
||||
ExecutorThread thread = new ExecutorThread(this::processQueues);
|
||||
thread.setDaemon(true);
|
||||
thread.setName(threads == 1 ? name : name + "-" + (i + 1));
|
||||
thread.setPriority(threadPriority);
|
||||
thread.start();
|
||||
this.threads.add(thread);
|
||||
}
|
||||
if (handler == null) {
|
||||
handler = ABORT_POLICY;
|
||||
}
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the Current thread belongs to a PriorityQueuedExecutor, return that Executro
|
||||
* @return The executor that controls this thread
|
||||
*/
|
||||
public static PriorityQueuedExecutor getExecutor() {
|
||||
if (!(Thread.currentThread() instanceof ExecutorThread)) {
|
||||
return null;
|
||||
}
|
||||
return ((ExecutorThread) Thread.currentThread()).getExecutor();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
shuttingDown = true;
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<Runnable> shutdownNow() {
|
||||
shuttingDown = true;
|
||||
shuttingDownNow = true;
|
||||
List<Runnable> tasks = new ArrayList<>(high.size() + normal.size());
|
||||
Runnable run;
|
||||
while ((run = getTask()) != null) {
|
||||
tasks.add(run);
|
||||
}
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShutdown() {
|
||||
return shuttingDown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminated() {
|
||||
if (!shuttingDown) {
|
||||
return false;
|
||||
}
|
||||
return high.isEmpty() && normal.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) {
|
||||
synchronized (this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
final long wait = unit.toNanos(timeout);
|
||||
final long max = System.nanoTime() + wait;
|
||||
for (;!threads.isEmpty() && System.nanoTime() < max;) {
|
||||
threads.removeIf(thread -> !thread.isAlive());
|
||||
}
|
||||
return isTerminated();
|
||||
}
|
||||
|
||||
|
||||
public PendingTask<Void> createPendingTask(Runnable task) {
|
||||
return createPendingTask(task, Priority.NORMAL);
|
||||
}
|
||||
public PendingTask<Void> createPendingTask(Runnable task, Priority priority) {
|
||||
return createPendingTask(() -> {
|
||||
task.run();
|
||||
return null;
|
||||
}, priority);
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> createPendingTask(Supplier<T> task) {
|
||||
return createPendingTask(task, Priority.NORMAL);
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> createPendingTask(Supplier<T> task, Priority priority) {
|
||||
return new PendingTask<>(task, priority);
|
||||
}
|
||||
|
||||
public PendingTask<Void> submitTask(Runnable run) {
|
||||
return createPendingTask(run).submit();
|
||||
}
|
||||
|
||||
public PendingTask<Void> submitTask(Runnable run, Priority priority) {
|
||||
return createPendingTask(run, priority).submit();
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> submitTask(Supplier<T> run) {
|
||||
return createPendingTask(run).submit();
|
||||
}
|
||||
|
||||
public <T> PendingTask<T> submitTask(Supplier<T> run, Priority priority) {
|
||||
PendingTask<T> task = createPendingTask(run, priority);
|
||||
return task.submit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@Nonnull Runnable command) {
|
||||
submitTask(command);
|
||||
}
|
||||
|
||||
public boolean isCurrentThread() {
|
||||
final Thread thread = Thread.currentThread();
|
||||
if (!(thread instanceof ExecutorThread)) {
|
||||
return false;
|
||||
}
|
||||
return ((ExecutorThread) thread).getExecutor() == this;
|
||||
}
|
||||
|
||||
public Runnable getUrgentTask() {
|
||||
return urgent.poll();
|
||||
}
|
||||
|
||||
public Runnable getTask() {
|
||||
Runnable run = urgent.poll();
|
||||
if (run != null) {
|
||||
return run;
|
||||
}
|
||||
run = high.poll();
|
||||
if (run != null) {
|
||||
return run;
|
||||
}
|
||||
return normal.poll();
|
||||
}
|
||||
|
||||
private void processQueues() {
|
||||
Runnable run = null;
|
||||
while (true) {
|
||||
if (run != null) {
|
||||
run.run();
|
||||
}
|
||||
if (shuttingDownNow) {
|
||||
return;
|
||||
}
|
||||
if ((run = getTask()) != null) {
|
||||
continue;
|
||||
}
|
||||
synchronized (PriorityQueuedExecutor.this) {
|
||||
if ((run = getTask()) != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shuttingDown || shuttingDownNow) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
PriorityQueuedExecutor.this.wait();
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean processUrgentTasks() {
|
||||
Runnable run;
|
||||
boolean hadTask = false;
|
||||
while ((run = getUrgentTask()) != null) {
|
||||
run.run();
|
||||
hadTask = true;
|
||||
}
|
||||
return hadTask;
|
||||
}
|
||||
|
||||
public enum Priority {
|
||||
NORMAL, HIGH, URGENT
|
||||
}
|
||||
|
||||
public class ExecutorThread extends Thread {
|
||||
public ExecutorThread(Runnable runnable) {
|
||||
super(runnable);
|
||||
}
|
||||
|
||||
public PriorityQueuedExecutor getExecutor() {
|
||||
return PriorityQueuedExecutor.this;
|
||||
}
|
||||
}
|
||||
|
||||
public class PendingTask <T> implements Runnable {
|
||||
|
||||
private final AtomicBoolean hasRan = new AtomicBoolean();
|
||||
private final AtomicInteger submitted = new AtomicInteger(-1);
|
||||
private final AtomicInteger priority;
|
||||
private final Supplier<T> run;
|
||||
private final CompletableFuture<T> future = new CompletableFuture<>();
|
||||
private volatile PriorityQueuedExecutor executor;
|
||||
|
||||
public PendingTask(Supplier<T> run) {
|
||||
this(run, Priority.NORMAL);
|
||||
}
|
||||
|
||||
public PendingTask(Supplier<T> run, Priority priority) {
|
||||
this.priority = new AtomicInteger(priority.ordinal());
|
||||
this.run = run;
|
||||
}
|
||||
|
||||
public boolean cancel() {
|
||||
return hasRan.compareAndSet(false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!hasRan.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
future.complete(run.get());
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void bumpPriority() {
|
||||
bumpPriority(Priority.HIGH);
|
||||
}
|
||||
|
||||
public void bumpPriority(Priority newPriority) {
|
||||
for (;;) {
|
||||
int current = this.priority.get();
|
||||
int ordinal = newPriority.ordinal();
|
||||
if (current >= ordinal || priority.compareAndSet(current, ordinal)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this.submitted.get() == -1 || this.hasRan.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only resubmit if it hasnt ran yet and has been submitted
|
||||
submit();
|
||||
}
|
||||
|
||||
public CompletableFuture<T> onDone() {
|
||||
return future;
|
||||
}
|
||||
|
||||
public PendingTask<T> submit() {
|
||||
if (shuttingDown) {
|
||||
handler.onRejection(this, PriorityQueuedExecutor.this);
|
||||
return this;
|
||||
}
|
||||
for (;;) {
|
||||
final int submitted = this.submitted.get();
|
||||
final int priority = this.priority.get();
|
||||
if (submitted == priority) {
|
||||
return this;
|
||||
}
|
||||
if (this.submitted.compareAndSet(submitted, priority)) {
|
||||
if (priority == Priority.URGENT.ordinal()) {
|
||||
urgent.add(this);
|
||||
} else if (priority == Priority.HIGH.ordinal()) {
|
||||
high.add(this);
|
||||
} else {
|
||||
normal.add(this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (PriorityQueuedExecutor.this) {
|
||||
// Wake up a thread to take this work
|
||||
PriorityQueuedExecutor.this.notify();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public interface RejectionHandler {
|
||||
void onRejection(Runnable run, PriorityQueuedExecutor executor);
|
||||
}
|
||||
|
||||
public static final RejectionHandler ABORT_POLICY = (run, executor) -> {
|
||||
throw new RejectedExecutionException("Executor has been shutdown");
|
||||
};
|
||||
public static final RejectionHandler CALLER_RUNS_POLICY = (run, executor) -> {
|
||||
run.run();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,910 @@
|
||||
package com.destroystokyo.paper.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.BlockRedstoneWire;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
/**
|
||||
* Used for the faster redstone algorithm.
|
||||
* Original author: theosib
|
||||
* Original license: MIT
|
||||
*
|
||||
* Ported to Paper and updated to 1.13 by egg82
|
||||
*/
|
||||
public class RedstoneWireTurbo {
|
||||
/*
|
||||
* This is Helper class for BlockRedstoneWire. It implements a minimally-invasive
|
||||
* bolt-on accelerator that performs a breadth-first search through redstone wire blocks
|
||||
* in order to more efficiently and deterministically compute new redstone wire power levels
|
||||
* and determine the order in which other blocks should be updated.
|
||||
*
|
||||
* Features:
|
||||
* - Changes to BlockRedstoneWire are very limited, no other classes are affected, and the
|
||||
* choice between old and new redstone wire update algorithms is switchable on-line.
|
||||
* - The vanilla implementation relied on World.notifyNeighborsOfStateChange for redstone
|
||||
* wire blocks to communicate power level changes to each other, generating 36 block
|
||||
* updates per call. This improved implementation propagates power level changes directly
|
||||
* between redstone wire blocks. Redstone wire power levels are therefore computed more quickly,
|
||||
* and block updates are sent only to non-redstone blocks, many of which may perform an
|
||||
* action when informed of a change in redstone power level. (Note: Block updates are not
|
||||
* the same as state changes to redstone wire. Wire block states are updated as soon
|
||||
* as they are computed.)
|
||||
* - Of the 36 block updates generated by a call to World.notifyNeighborsOfStateChange,
|
||||
* 12 of them are obviously redundant (e.g. the west neighbor of the east neighbor).
|
||||
* These are eliminated.
|
||||
* - Updates to redstone wire and other connected blocks are propagated in a breath-first
|
||||
* manner, radiating out from the initial trigger (a block update to a redstone wire
|
||||
* from something other than redstone wire).
|
||||
* - Updates are scheduled both deterministically and in an intuitive order, addressing bug
|
||||
* MC-11193.
|
||||
* - All redstone behavior that used to be locational now works the same in all locations.
|
||||
* - All behaviors of redstone wire that used to be orientational now work the same in all
|
||||
* orientations, as long as orientation can be determined; random otherwise. Some other
|
||||
* redstone components still update directionally (e.g. switches), and this code can't
|
||||
* compensate for that.
|
||||
* - Information that is otherwise computed over and over again or which is expensive to
|
||||
* to compute is cached for faster lookup. This includes coordinates of block position
|
||||
* neighbors and block states that won't change behind our backs during the execution of
|
||||
* this search algorithm.
|
||||
* - Redundant block updates (both to redstone wire and to other blocks) are heavily
|
||||
* consolidated. For worst-case scenarios (depowering of redstone wire) this results
|
||||
* in a reduction of block updates by as much as 95% (factor of 1/21). Due to overheads,
|
||||
* empirical testing shows a speedup better than 10x. This addresses bug MC-81098.
|
||||
*
|
||||
* Extensive testing has been performed to ensure that existing redstone contraptions still
|
||||
* behave as expected. Results of early testing that identified undesirable behavior changes
|
||||
* were addressed. Additionally, real-time performance testing revealed compute inefficiencies
|
||||
* With earlier implementations of this accelerator. Some compatibility adjustments and
|
||||
* performance optimizations resulted in harmless increases in block updates above the
|
||||
* theoretical minimum.
|
||||
*
|
||||
* Only a single redstone machine was found to break: An instant dropper line hack that
|
||||
* relies on powered rails and quasi-connectivity but doesn't work in all directions. The
|
||||
* replacement is to lay redstone wire directly on top of the dropper line, which now works
|
||||
* reliably in any direction.
|
||||
*
|
||||
* There are numerous other optimization that can be made, but those will be provided later in
|
||||
* separate updates. This version is designed to be minimalistic.
|
||||
*
|
||||
* Many thanks to the following individuals for their help in testing this functionality:
|
||||
* - pokechu22, _MethodZz_, WARBEN, NarcolepticFrog, CommandHelper (nessie), ilmango,
|
||||
* OreoLamp, Xcom6000, tryashtar, RedCMD, Smokey95Dog, EDDxample, Rays Works,
|
||||
* Nodnam, BlockyPlays, Grumm, NeunEinser, HelVince.
|
||||
*/
|
||||
|
||||
/* Reference to BlockRedstoneWire object, which uses this accelerator */
|
||||
private final BlockRedstoneWire wire;
|
||||
|
||||
/*
|
||||
* Implementation:
|
||||
*
|
||||
* RedstoneWire Blocks are updated in concentric rings or "layers" radiating out from the
|
||||
* initial block update that came from a call to BlockRedstoneWire.neighborChanged().
|
||||
* All nodes put in Layer N are those with Manhattan distance N from the trigger
|
||||
* position, reachable through connected redstone wire blocks.
|
||||
*
|
||||
* Layer 0 represents the trigger block position that was input to neighborChanged.
|
||||
* Layer 1 contains the immediate neighbors of that position.
|
||||
* Layer N contains the neighbors of blocks in layer N-1, not including
|
||||
* those in previous layers.
|
||||
*
|
||||
* Layers enforce an update order that is a function of Manhattan distance
|
||||
* from the initial coordinates input to neighborChanged. The same
|
||||
* coordinates may appear in multiple layers, but redundant updates are minimized.
|
||||
* Block updates are sent layer-by-layer. If multiple of a block's neighbors experience
|
||||
* redstone wire changes before its layer is processed, then those updates will be merged.
|
||||
* If a block's update has been sent, but its neighboring redstone changes
|
||||
* after that, then another update will be sent. This preserves compatibility with
|
||||
* machines that rely on zero-tick behavior, except that the new functionality is non-
|
||||
* locational.
|
||||
*
|
||||
* Within each layer, updates are ordered left-to-right relative to the direction of
|
||||
* information flow. This makes the implementation non-orientational. Only when
|
||||
* this direction is ambiguous is randomness applied (intentionally).
|
||||
*/
|
||||
private List<UpdateNode> updateQueue0 = Lists.newArrayList();
|
||||
private List<UpdateNode> updateQueue1 = Lists.newArrayList();
|
||||
private List<UpdateNode> updateQueue2 = Lists.newArrayList();
|
||||
|
||||
public RedstoneWireTurbo(BlockRedstoneWire wire) {
|
||||
this.wire = wire;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute neighbors of a block. When a redstone wire value changes, previously it called
|
||||
* World.notifyNeighborsOfStateChange. That lists immediately neighboring blocks in
|
||||
* west, east, down, up, north, south order. For each of those neighbors, their own
|
||||
* neighbors are updated in the same order. This generates 36 updates, but 12 of them are
|
||||
* redundant; for instance the west neighbor of a block's east neighbor.
|
||||
*
|
||||
* Note that this ordering is only used to create the initial list of neighbors. Once
|
||||
* the direction of signal flow is identified, the ordering of updates is completely
|
||||
* reorganized.
|
||||
*/
|
||||
public static BlockPosition[] computeAllNeighbors(final BlockPosition pos) {
|
||||
final int x = pos.getX();
|
||||
final int y = pos.getY();
|
||||
final int z = pos.getZ();
|
||||
final BlockPosition[] n = new BlockPosition[24];
|
||||
|
||||
// Immediate neighbors, in the same order as
|
||||
// World.notifyNeighborsOfStateChange, etc.:
|
||||
// west, east, down, up, north, south
|
||||
n[0] = new BlockPosition(x - 1, y, z);
|
||||
n[1] = new BlockPosition(x + 1, y, z);
|
||||
n[2] = new BlockPosition(x, y - 1, z);
|
||||
n[3] = new BlockPosition(x, y + 1, z);
|
||||
n[4] = new BlockPosition(x, y, z - 1);
|
||||
n[5] = new BlockPosition(x, y, z + 1);
|
||||
|
||||
// Neighbors of neighbors, in the same order,
|
||||
// except that duplicates are not included
|
||||
n[6] = new BlockPosition(x - 2, y, z);
|
||||
n[7] = new BlockPosition(x - 1, y - 1, z);
|
||||
n[8] = new BlockPosition(x - 1, y + 1, z);
|
||||
n[9] = new BlockPosition(x - 1, y, z - 1);
|
||||
n[10] = new BlockPosition(x - 1, y, z + 1);
|
||||
n[11] = new BlockPosition(x + 2, y, z);
|
||||
n[12] = new BlockPosition(x + 1, y - 1, z);
|
||||
n[13] = new BlockPosition(x + 1, y + 1, z);
|
||||
n[14] = new BlockPosition(x + 1, y, z - 1);
|
||||
n[15] = new BlockPosition(x + 1, y, z + 1);
|
||||
n[16] = new BlockPosition(x, y - 2, z);
|
||||
n[17] = new BlockPosition(x, y - 1, z - 1);
|
||||
n[18] = new BlockPosition(x, y - 1, z + 1);
|
||||
n[19] = new BlockPosition(x, y + 2, z);
|
||||
n[20] = new BlockPosition(x, y + 1, z - 1);
|
||||
n[21] = new BlockPosition(x, y + 1, z + 1);
|
||||
n[22] = new BlockPosition(x, y, z - 2);
|
||||
n[23] = new BlockPosition(x, y, z + 2);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only want redstone wires to update redstone wires that are
|
||||
* immediately adjacent. Some more distant updates can result
|
||||
* in cross-talk that (a) wastes time and (b) can make the update
|
||||
* order unintuitive. Therefore (relative to the neighbor order
|
||||
* computed by computeAllNeighbors), updates are not scheduled
|
||||
* for redstone wire in those non-connecting positions. On the
|
||||
* other hand, updates will always be sent to *other* types of blocks
|
||||
* in any of the 24 neighboring positions.
|
||||
*/
|
||||
private static final boolean[] update_redstone = {
|
||||
true, true, false, false, true, true, // 0 to 5
|
||||
false, true, true, false, false, false, // 6 to 11
|
||||
true, true, false, false, false, true, // 12 to 17
|
||||
true, false, true, true, false, false // 18 to 23
|
||||
};
|
||||
|
||||
// Internal numbering for cardinal directions
|
||||
private static final int North = 0;
|
||||
private static final int East = 1;
|
||||
private static final int South = 2;
|
||||
private static final int West = 3;
|
||||
|
||||
/*
|
||||
* These lookup tables completely remap neighbor positions into a left-to-right
|
||||
* ordering, based on the cardinal direction that is determined to be forward.
|
||||
* See below for more explanation.
|
||||
*/
|
||||
private static final int[] forward_is_north = {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15};
|
||||
private static final int[] forward_is_east = {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10};
|
||||
private static final int[] forward_is_south = {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9};
|
||||
private static final int[] forward_is_west = {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14};
|
||||
|
||||
/* For any orientation, we end up with the update order defined below. This order is relative to any redstone wire block
|
||||
* that is itself having an update computed, and this center position is marked with C.
|
||||
* - The update position marked 0 is computed first, and the one marked 23 is last.
|
||||
* - Forward is determined by the local direction of information flow into position C from prior updates.
|
||||
* - The first updates are scheduled for the four positions below and above C.
|
||||
* - Then updates are scheduled for the four horizontal neighbors of C, followed by the positions below and above those neighbors.
|
||||
* - Finally, updates are scheduled for the remaining positions with Manhattan distance 2 from C (at the same Y coordinate).
|
||||
* - For a given horizontal distance from C, updates are scheduled starting from directly left and stepping clockwise to directly
|
||||
* right. The remaining positions behind C are scheduled counterclockwise so as to maintain the left-to-right ordering.
|
||||
* - If C is in layer N of the update schedule, then all 24 positions may be scheduled for layer N+1. For redstone wire, no
|
||||
* updates are scheduled for positions that cannot directly connect. Additionally, the four positions above and below C
|
||||
* are ALSO scheduled for layer N+2.
|
||||
* - This update order was selected after experimenting with a number of alternative schedules, based on its compatibility
|
||||
* with existing redstone designs and behaviors that were considered to be intuitive by various testers. WARBEN in particular
|
||||
* made some of the most challenging test cases, but the 3-tick clocks (made by RedCMD) were also challenging to fix,
|
||||
* along with the rail-based instant dropper line built by ilmango. Numerous others made test cases as well, including
|
||||
* NarcolepticFrog, nessie, and Pokechu22.
|
||||
*
|
||||
* - The forward direction is determined locally. So when there are branches in the redstone wire, the left one will get updated
|
||||
* before the right one. Each branch can have its own relative forward direction, resulting in the left side of a left branch
|
||||
* having priority over the right branch of a left branch, which has priority over the left branch of a right branch, followed
|
||||
* by the right branch of a right branch. And so forth. Since redstone power reduces to zero after a path distance of 15,
|
||||
* that imposes a practical limit on the branching. Note that the branching is not tracked explicitly -- relative forward
|
||||
* directions dictate relative sort order, which maintains the proper global ordering. This also makes it unnecessary to be
|
||||
* concerned about branches meeting up with each other.
|
||||
*
|
||||
* ^
|
||||
* |
|
||||
* Forward
|
||||
* <-- Left Right -->
|
||||
*
|
||||
* 18
|
||||
* 10 17 5 19 11
|
||||
* 2 8 0 12 16 4 C 6 20 9 1 13 3
|
||||
* 14 21 7 23 15
|
||||
* Further 22 Further
|
||||
* Down Down Up Up
|
||||
*
|
||||
* Backward
|
||||
* |
|
||||
* V
|
||||
*/
|
||||
|
||||
// This allows the above remapping tables to be looked up by cardial direction index
|
||||
private static final int[][] reordering = { forward_is_north, forward_is_east, forward_is_south, forward_is_west };
|
||||
|
||||
/*
|
||||
* Input: Array of UpdateNode objects in an order corresponding to the positions
|
||||
* computed by computeAllNeighbors above.
|
||||
* Output: Array of UpdateNode objects oriented using the above remapping tables
|
||||
* corresponding to the identified heading (direction of information flow).
|
||||
*/
|
||||
private static void orientNeighbors(final UpdateNode[] src, final UpdateNode[] dst, final int heading) {
|
||||
final int[] re = reordering[heading];
|
||||
for (int i = 0; i < 24; i++) {
|
||||
dst[i] = src[re[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure to keep track of redstone wire blocks and
|
||||
* neighbors that will receive updates.
|
||||
*/
|
||||
private static class UpdateNode {
|
||||
public static enum Type {
|
||||
UNKNOWN, REDSTONE, OTHER
|
||||
}
|
||||
|
||||
IBlockData currentState; // Keep track of redstone wire value
|
||||
UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges)
|
||||
BlockPosition self; // UpdateNode's own position
|
||||
BlockPosition parent; // Which block pos spawned/updated this node
|
||||
Type type = Type.UNKNOWN; // unknown, redstone wire, other type of block
|
||||
int layer; // Highest layer this node is scheduled in
|
||||
boolean visited; // To keep track of information flow direction, visited restone wire is marked
|
||||
int xbias, zbias; // Remembers directionality of ancestor nodes; helps eliminate directional ambiguities.
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep track of all block positions discovered during search and their current states.
|
||||
* We want to remember one entry for each position.
|
||||
*/
|
||||
private final Map<BlockPosition, UpdateNode> nodeCache = Maps.newHashMap();
|
||||
|
||||
/*
|
||||
* For a newly created UpdateNode object, determine what type of block it is.
|
||||
*/
|
||||
private void identifyNode(final World worldIn, final UpdateNode upd1) {
|
||||
final BlockPosition pos = upd1.self;
|
||||
final IBlockData oldState = worldIn.getType(pos);
|
||||
upd1.currentState = oldState;
|
||||
|
||||
// Some neighbors of redstone wire are other kinds of blocks.
|
||||
// These need to receive block updates to inform them that
|
||||
// redstone wire values have changed.
|
||||
final Block block = oldState.getBlock();
|
||||
if (block != wire) {
|
||||
// Mark this block as not redstone wire and therefore
|
||||
// requiring updates
|
||||
upd1.type = UpdateNode.Type.OTHER;
|
||||
|
||||
// Non-redstone blocks may propagate updates, but those updates
|
||||
// are not handled by this accelerator. Therefore, we do not
|
||||
// expand this position's neighbors.
|
||||
return;
|
||||
}
|
||||
|
||||
// One job of BlockRedstoneWire.neighborChanged is to convert
|
||||
// redstone wires to items if the block beneath was removed.
|
||||
// With this accelerator, BlockRedstoneWire.neighborChanged
|
||||
// is only typically called for a single wire block, while
|
||||
// others are processed internally by the breadth first search
|
||||
// algorithm. To preserve this game behavior, this check must
|
||||
// be replicated here.
|
||||
if (!wire.canPlace(null, worldIn, pos)) {
|
||||
// Pop off the redstone dust
|
||||
oldState.dropNaturally(worldIn, pos, 0);
|
||||
worldIn.setAir(pos);
|
||||
|
||||
// Mark this position as not being redstone wire
|
||||
upd1.type = UpdateNode.Type.OTHER;
|
||||
|
||||
// Note: Sending updates to air blocks leads to an empty method.
|
||||
// Testing shows this to be faster than explicitly avoiding updates to
|
||||
// air blocks.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the above conditions fail, then this is a redstone wire block.
|
||||
upd1.type = UpdateNode.Type.REDSTONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given which redstone wire blocks have been visited and not visited
|
||||
* around the position currently being updated, compute the cardinal
|
||||
* direction that is "forward."
|
||||
*
|
||||
* rx is the forward direction along the West/East axis
|
||||
* rz is the forward direction along the North/South axis
|
||||
*/
|
||||
static private int computeHeading(final int rx, final int rz) {
|
||||
// rx and rz can only take on values -1, 0, and 1, so we can
|
||||
// compute a code number that allows us to use a single switch
|
||||
// to determine the heading.
|
||||
final int code = (rx + 1) + 3 * (rz + 1);
|
||||
switch (code) {
|
||||
case 0: {
|
||||
// Both rx and rz are -1 (northwest)
|
||||
// Randomly choose one to be forward.
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? North : West;
|
||||
}
|
||||
case 1: {
|
||||
// rx=0, rz=-1
|
||||
// Definitively North
|
||||
return North;
|
||||
}
|
||||
case 2: {
|
||||
// rx=1, rz=-1 (northeast)
|
||||
// Choose randomly between north and east
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? North : East;
|
||||
}
|
||||
case 3: {
|
||||
// rx=-1, rz=0
|
||||
// Definitively West
|
||||
return West;
|
||||
}
|
||||
case 4: {
|
||||
// rx=0, rz=0
|
||||
// Heading is completely ambiguous. Choose
|
||||
// randomly among the four cardinal directions.
|
||||
return ThreadLocalRandom.current().nextInt(0, 4);
|
||||
}
|
||||
case 5: {
|
||||
// rx=1, rz=0
|
||||
// Definitively East
|
||||
return East;
|
||||
}
|
||||
case 6: {
|
||||
// rx=-1, rz=1 (southwest)
|
||||
// Choose randomly between south and west
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? South : West;
|
||||
}
|
||||
case 7: {
|
||||
// rx=0, rz=1
|
||||
// Definitively South
|
||||
return South;
|
||||
}
|
||||
case 8: {
|
||||
// rx=1, rz=1 (southeast)
|
||||
// Choose randomly between south and east
|
||||
final int j = ThreadLocalRandom.current().nextInt(0, 1);
|
||||
return (j == 0) ? South : East;
|
||||
}
|
||||
}
|
||||
|
||||
// We should never get here
|
||||
return ThreadLocalRandom.current().nextInt(0, 4);
|
||||
}
|
||||
|
||||
// Select whether to use updateSurroundingRedstone from BlockRedstoneWire (old)
|
||||
// or this helper class (new)
|
||||
private static final boolean old_current_change = false;
|
||||
|
||||
/*
|
||||
* Process a node whose neighboring redstone wire has experienced value changes.
|
||||
*/
|
||||
private void updateNode(final World worldIn, final UpdateNode upd1, final int layer) {
|
||||
final BlockPosition pos = upd1.self;
|
||||
|
||||
// Mark this redstone wire as having been visited so that it can be used
|
||||
// to calculate direction of information flow.
|
||||
upd1.visited = true;
|
||||
|
||||
// Look up the last known state.
|
||||
// Due to the way other redstone components are updated, we do not
|
||||
// have to worry about a state changing behind our backs. The rare
|
||||
// exception is handled by scheduleReentrantNeighborChanged.
|
||||
final IBlockData oldState = upd1.currentState;
|
||||
|
||||
// Ask the wire block to compute its power level from its neighbors.
|
||||
// This will also update the wire's power level and return a new
|
||||
// state if it has changed. When a wire power level is changed,
|
||||
// calculateCurrentChanges will immediately update the block state in the world
|
||||
// and return the same value here to be cached in the corresponding
|
||||
// UpdateNode object.
|
||||
IBlockData newState;
|
||||
if (old_current_change) {
|
||||
newState = wire.calculateCurrentChanges(worldIn, pos, pos, oldState);
|
||||
} else {
|
||||
// Looking up block state is slow. This accelerator includes a version of
|
||||
// calculateCurrentChanges that uses cahed wire values for a
|
||||
// significant performance boost.
|
||||
newState = this.calculateCurrentChanges(worldIn, upd1);
|
||||
}
|
||||
|
||||
// Only inform neighbors if the state has changed
|
||||
if (newState != oldState) {
|
||||
// Store the new state
|
||||
upd1.currentState = newState;
|
||||
|
||||
// Inform neighbors of the change
|
||||
propagateChanges(worldIn, upd1, layer);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This identifies the neighboring positions of a new UpdateNode object,
|
||||
* determines their types, and links those to into the graph. Then based on
|
||||
* what nodes in the redstone wire graph have been visited, the neighbors
|
||||
* are reordered left-to-right relative to the direction of information flow.
|
||||
*/
|
||||
private void findNeighbors(final World worldIn, final UpdateNode upd1) {
|
||||
final BlockPosition pos = upd1.self;
|
||||
|
||||
// Get the list of neighbor coordinates
|
||||
final BlockPosition[] neighbors = computeAllNeighbors(pos);
|
||||
|
||||
// Temporary array of neighbors in cardinal ordering
|
||||
final UpdateNode[] neighbor_nodes = new UpdateNode[24];
|
||||
|
||||
// Target array of neighbors sorted left-to-right
|
||||
upd1.neighbor_nodes = new UpdateNode[24];
|
||||
|
||||
for (int i=0; i<24; i++) {
|
||||
// Look up each neighbor in the node cache
|
||||
final BlockPosition pos2 = neighbors[i];
|
||||
UpdateNode upd2 = nodeCache.get(pos2);
|
||||
if (upd2 == null) {
|
||||
// If this is a previously unreached position, create
|
||||
// a new update node, add it to the cache, and identify what it is.
|
||||
upd2 = new UpdateNode();
|
||||
upd2.self = pos2;
|
||||
upd2.parent = pos;
|
||||
nodeCache.put(pos2, upd2);
|
||||
identifyNode(worldIn, upd2);
|
||||
}
|
||||
|
||||
// For non-redstone blocks, any of the 24 neighboring positions
|
||||
// should receive a block update. However, some block coordinates
|
||||
// may contain a redstone wire that does not directly connect to the
|
||||
// one being expanded. To avoid redundant calculations and confusing
|
||||
// cross-talk, those neighboring positions are not included.
|
||||
if (update_redstone[i] || upd2.type != UpdateNode.Type.REDSTONE) {
|
||||
neighbor_nodes[i] = upd2;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the directions from which the redstone signal may have come from. This
|
||||
// checks for redstone wire at the same Y level and also Y+1 and Y-1, relative to the
|
||||
// block being expanded.
|
||||
final boolean fromWest = (neighbor_nodes[0].visited || neighbor_nodes[7].visited || neighbor_nodes[8].visited);
|
||||
final boolean fromEast = (neighbor_nodes[1].visited || neighbor_nodes[12].visited || neighbor_nodes[13].visited);
|
||||
final boolean fromNorth = (neighbor_nodes[4].visited || neighbor_nodes[17].visited || neighbor_nodes[20].visited);
|
||||
final boolean fromSouth = (neighbor_nodes[5].visited || neighbor_nodes[18].visited || neighbor_nodes[21].visited);
|
||||
|
||||
int cx = 0, cz = 0;
|
||||
if (fromWest) cx += 1;
|
||||
if (fromEast) cx -= 1;
|
||||
if (fromNorth) cz += 1;
|
||||
if (fromSouth) cz -= 1;
|
||||
|
||||
int heading;
|
||||
if (cx==0 && cz==0) {
|
||||
// If there is no clear direction, try to inherit the heading from ancestor nodes.
|
||||
heading = computeHeading(upd1.xbias, upd1.zbias);
|
||||
|
||||
// Propagate that heading to descendant nodes.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode nn = neighbor_nodes[i];
|
||||
if (nn != null) {
|
||||
nn.xbias = upd1.xbias;
|
||||
nn.zbias = upd1.zbias;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cx != 0 && cz != 0) {
|
||||
// If the heading is somewhat ambiguous, try to disambiguate based on
|
||||
// ancestor nodes.
|
||||
if (upd1.xbias != 0) cz = 0;
|
||||
if (upd1.zbias != 0) cx = 0;
|
||||
}
|
||||
heading = computeHeading(cx, cz);
|
||||
|
||||
// Propagate that heading to descendant nodes.
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode nn = neighbor_nodes[i];
|
||||
if (nn != null) {
|
||||
nn.xbias = cx;
|
||||
nn.zbias = cz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reorder neighboring UpdateNode objects according to the forward direction
|
||||
// determined above.
|
||||
orientNeighbors(neighbor_nodes, upd1.neighbor_nodes, heading);
|
||||
}
|
||||
|
||||
/*
|
||||
* For any redstone wire block in layer N, inform neighbors to recompute their states
|
||||
* in layers N+1 and N+2;
|
||||
*/
|
||||
private void propagateChanges(final World worldIn, final UpdateNode upd1, final int layer) {
|
||||
if (upd1.neighbor_nodes == null) {
|
||||
// If this node has not been expanded yet, find its neighbors
|
||||
findNeighbors(worldIn, upd1);
|
||||
}
|
||||
|
||||
final BlockPosition pos = upd1.self;
|
||||
|
||||
// All neighbors may be scheduled for layer N+1
|
||||
final int layer1 = layer + 1;
|
||||
|
||||
// If the node being updated (upd1) has already been expanded, then merely
|
||||
// schedule updates to its neighbors.
|
||||
for (int i = 0; i < 24; i++) {
|
||||
final UpdateNode upd2 = upd1.neighbor_nodes[i];
|
||||
|
||||
// This test ensures that an UpdateNode is never scheduled to the same layer
|
||||
// more than once. Also, skip non-connecting redstone wire blocks
|
||||
if (upd2 != null && layer1 > upd2.layer) {
|
||||
upd2.layer = layer1;
|
||||
updateQueue1.add(upd2);
|
||||
|
||||
// Keep track of which block updated this neighbor
|
||||
upd2.parent = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes above and below are scheduled ALSO for layer N+2
|
||||
final int layer2 = layer + 2;
|
||||
|
||||
// Repeat of the loop above, but only for the first four (above and below) neighbors
|
||||
// and for layer N+2;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final UpdateNode upd2 = upd1.neighbor_nodes[i];
|
||||
if (upd2 != null && layer2 > upd2.layer) {
|
||||
upd2.layer = layer2;
|
||||
updateQueue2.add(upd2);
|
||||
upd2.parent = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The breadth-first search below will send block updates to blocks
|
||||
// that are not redstone wire. If one of those updates results in
|
||||
// a distant redstone wire getting an update, then this.neighborChanged
|
||||
// will get called. This would be a reentrant call, and
|
||||
// it is necessary to properly integrate those updates into the
|
||||
// on-going search through redstone wire. Thus, we make the layer
|
||||
// currently being processed visible at the object level.
|
||||
|
||||
// The current layer being processed by the breadth-first search
|
||||
private int currentWalkLayer = 0;
|
||||
|
||||
private void shiftQueue() {
|
||||
final List<UpdateNode> t = updateQueue0;
|
||||
t.clear();
|
||||
updateQueue0 = updateQueue1;
|
||||
updateQueue1 = updateQueue2;
|
||||
updateQueue2 = t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a breadth-first (layer by layer) traversal through redstone
|
||||
* wire blocks, propagating value changes to neighbors in an order
|
||||
* that is a function of distance from the initial call to
|
||||
* this.neighborChanged.
|
||||
*/
|
||||
private void breadthFirstWalk(final World worldIn) {
|
||||
shiftQueue();
|
||||
currentWalkLayer = 1;
|
||||
|
||||
// Loop over all layers
|
||||
while (updateQueue0.size()>0 || updateQueue1.size()>0) {
|
||||
// Get the set of blocks in this layer
|
||||
final List<UpdateNode> thisLayer = updateQueue0;
|
||||
|
||||
// Loop over all blocks in the layer. Recall that
|
||||
// this is a List, preserving the insertion order of
|
||||
// left-to-right based on direction of information flow.
|
||||
for (UpdateNode upd : thisLayer) {
|
||||
if (upd.type == UpdateNode.Type.REDSTONE) {
|
||||
// If the node is is redstone wire,
|
||||
// schedule updates to neighbors if its value
|
||||
// has changed.
|
||||
updateNode(worldIn, upd, currentWalkLayer);
|
||||
} else {
|
||||
// If this block is not redstone wire, send a block update.
|
||||
// Redstone wire blocks get state updates, but they don't
|
||||
// need block updates. Only non-redstone neighbors need updates.
|
||||
|
||||
// World.neighborChanged is called from
|
||||
// World.notifyNeighborsOfStateChange, and
|
||||
// notifyNeighborsOfStateExcept. We don't use
|
||||
// World.notifyNeighborsOfStateChange here, since we are
|
||||
// already keeping track of all of the neighbor positions
|
||||
// that need to be updated. All on its own, handling neighbors
|
||||
// this way reduces block updates by 1/3 (24 instead of 36).
|
||||
worldIn.neighborChanged(upd.self, wire, upd.parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Move on to the next layer
|
||||
shiftQueue();
|
||||
currentWalkLayer++;
|
||||
}
|
||||
|
||||
currentWalkLayer = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normally, when Minecraft is computing redstone wire power changes, and a wire power level
|
||||
* change sends a block update to a neighboring functional component (e.g. piston, repeater, etc.),
|
||||
* those updates are queued. Only once all redstone wire updates are complete will any component
|
||||
* action generate any further block updates to redstone wire. Instant repeater lines, for instance,
|
||||
* will process all wire updates for one redstone line, after which the pistons will zero-tick,
|
||||
* after which the next redstone line performs all of its updates. Thus, each wire is processed in its
|
||||
* own discrete wave.
|
||||
*
|
||||
* However, there are some corner cases where this pattern breaks, with a proof of concept discovered
|
||||
* by Rays Works, which works the same in vanilla. The scenario is as follows:
|
||||
* (1) A redstone wire is conducting a signal.
|
||||
* (2) Part-way through that wave of updates, a neighbor is updated that causes an update to a completely
|
||||
* separate redstone wire.
|
||||
* (3) This results in a call to BlockRedstoneWire.neighborChanged for that other wire, in the middle of
|
||||
* an already on-going propagation through the first wire.
|
||||
*
|
||||
* The vanilla code, being depth-first, would end up fully processing the second wire before going back
|
||||
* to finish processing the first one. (Although technically, vanilla has no special concept of "being
|
||||
* in the middle" of processing updates to a wire.) For the breadth-first algorithm, we give this
|
||||
* situation special handling, where the updates for the second wire are incorporated into the schedule
|
||||
* for the first wire, and then the callstack is allowed to unwind back to the on-going search loop in
|
||||
* order to continue processing both the first and second wire in the order of distance from the initial
|
||||
* trigger.
|
||||
*/
|
||||
private IBlockData scheduleReentrantNeighborChanged(final World worldIn, final BlockPosition pos, final IBlockData newState, final BlockPosition source) {
|
||||
if (source != null) {
|
||||
// If the cause of the redstone wire update is known, we can use that to help determine
|
||||
// direction of information flow.
|
||||
UpdateNode src = nodeCache.get(source);
|
||||
if (src == null) {
|
||||
src = new UpdateNode();
|
||||
src.self = source;
|
||||
src.parent = source;
|
||||
src.visited = true;
|
||||
identifyNode(worldIn, src);
|
||||
nodeCache.put(source, src);
|
||||
}
|
||||
}
|
||||
|
||||
// Find or generate a node for the redstone block position receiving the update
|
||||
UpdateNode upd = nodeCache.get(pos);
|
||||
if (upd == null) {
|
||||
upd = new UpdateNode();
|
||||
upd.self = pos;
|
||||
upd.parent = pos;
|
||||
upd.visited = true;
|
||||
identifyNode(worldIn, upd);
|
||||
nodeCache.put(pos, upd);
|
||||
}
|
||||
upd.currentState = newState;
|
||||
|
||||
// Receiving this block update may mean something in the world changed.
|
||||
// Therefore we clear the cached block info about all neighbors of
|
||||
// the position receiving the update and then re-identify what they are.
|
||||
if (upd.neighbor_nodes != null) {
|
||||
for (int i=0; i<24; i++) {
|
||||
final UpdateNode upd2 = upd.neighbor_nodes[i];
|
||||
if (upd2 == null) continue;
|
||||
upd2.type = UpdateNode.Type.UNKNOWN;
|
||||
upd2.currentState = null;
|
||||
identifyNode(worldIn, upd2);
|
||||
}
|
||||
}
|
||||
|
||||
// The block at 'pos' is a redstone wire and has been updated already by calling
|
||||
// wire.calculateCurrentChanges, so we don't schedule that. However, we do need
|
||||
// to schedule its neighbors. By passing the current value of 'currentWalkLayer' to
|
||||
// propagateChanges, the neighbors of 'pos' are scheduled for layers currentWalkLayer+1
|
||||
// and currentWalkLayer+2.
|
||||
propagateChanges(worldIn, upd, currentWalkLayer);
|
||||
|
||||
// Return here. The call stack will unwind back to the first call to
|
||||
// updateSurroundingRedstone, whereupon the new updates just scheduled will
|
||||
// be propagated. This also facilitates elimination of superfluous and
|
||||
// redundant block updates.
|
||||
return newState;
|
||||
}
|
||||
|
||||
/*
|
||||
* New version of pre-existing updateSurroundingRedstone, which is called from
|
||||
* wire.updateSurroundingRedstone, which is called from wire.neighborChanged and a
|
||||
* few other methods in BlockRedstoneWire. This sets off the breadth-first
|
||||
* walk through all redstone dust connected to the initial position triggered.
|
||||
*/
|
||||
public IBlockData updateSurroundingRedstone(final World worldIn, final BlockPosition pos, final IBlockData state, final BlockPosition source) {
|
||||
// Check this block's neighbors and see if its power level needs to change
|
||||
// Use the calculateCurrentChanges method in BlockRedstoneWire since we have no
|
||||
// cached block states at this point.
|
||||
final IBlockData newState = wire.calculateCurrentChanges(worldIn, pos, pos, state);
|
||||
|
||||
// If no change, exit
|
||||
if (newState == state) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// Check to see if this update was received during an on-going breadth first search
|
||||
if (currentWalkLayer > 0 || nodeCache.size() > 0) {
|
||||
// As breadthFirstWalk progresses, it sends block updates to neighbors. Some of those
|
||||
// neighbors may affect the world so as to cause yet another redstone wire block to receive
|
||||
// an update. If that happens, we need to integrate those redstone wire updates into the
|
||||
// already on-going graph walk being performed by breadthFirstWalk.
|
||||
return scheduleReentrantNeighborChanged(worldIn, pos, newState, source);
|
||||
}
|
||||
// If there are no on-going walks through redstone wire, then start a new walk.
|
||||
|
||||
// If the source of the block update to the redstone wire at 'pos' is known, we can use
|
||||
// that to help determine the direction of information flow.
|
||||
if (source != null) {
|
||||
final UpdateNode src = new UpdateNode();
|
||||
src.self = source;
|
||||
src.parent = source;
|
||||
src.visited = true;
|
||||
nodeCache.put(source, src);
|
||||
identifyNode(worldIn, src);
|
||||
}
|
||||
|
||||
// Create a node representing the block at 'pos', and then propagate updates
|
||||
// to its neighbors. As stated above, the call to wire.calculateCurrentChanges
|
||||
// already performs the update to the block at 'pos', so it is not added to the schedule.
|
||||
final UpdateNode upd = new UpdateNode();
|
||||
upd.self = pos;
|
||||
upd.parent = source!=null ? source : pos;
|
||||
upd.currentState = newState;
|
||||
upd.type = UpdateNode.Type.REDSTONE;
|
||||
upd.visited = true;
|
||||
nodeCache.put(pos, upd);
|
||||
propagateChanges(worldIn, upd, 0);
|
||||
|
||||
// Perform the walk over all directly reachable redstone wire blocks, propagating wire value
|
||||
// updates in a breadth first order out from the initial update received for the block at 'pos'.
|
||||
breadthFirstWalk(worldIn);
|
||||
|
||||
// With the whole search completed, clear the list of all known blocks.
|
||||
// We do not want to keep around state information that may be changed by other code.
|
||||
// In theory, we could cache the neighbor block positions, but that is a separate
|
||||
// optimization.
|
||||
nodeCache.clear();
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
// For any array of neighbors in an UpdateNode object, these are always
|
||||
// the indices of the four immediate neighbors at the same Y coordinate.
|
||||
private static final int[] rs_neighbors = {4, 5, 6, 7};
|
||||
private static final int[] rs_neighbors_up = {9, 11, 13, 15};
|
||||
private static final int[] rs_neighbors_dn = {8, 10, 12, 14};
|
||||
|
||||
/*
|
||||
* Updated calculateCurrentChanges that is optimized for speed and uses
|
||||
* the UpdateNode's neighbor array to find the redstone states of neighbors
|
||||
* that might power it.
|
||||
*/
|
||||
private IBlockData calculateCurrentChanges(final World worldIn, final UpdateNode upd) {
|
||||
IBlockData state = upd.currentState;
|
||||
final int i = state.get(BlockRedstoneWire.POWER).intValue();
|
||||
int j = 0;
|
||||
j = getMaxCurrentStrength(upd, j);
|
||||
int l = 0;
|
||||
|
||||
wire.setCanProvidePower(false);
|
||||
// Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
|
||||
// and I'm not ready to try to replicate even more functionality from
|
||||
// elsewhere in Minecraft into this accelerator. So sadly, we must
|
||||
// suffer the performance hit of this very expensive call. If there
|
||||
// is consistency to what this call returns, we may be able to cache it.
|
||||
final int k = worldIn.isBlockIndirectlyGettingPowered(upd.self);
|
||||
wire.setCanProvidePower(true);
|
||||
|
||||
// The variable 'k' holds the maximum redstone power value of any adjacent blocks.
|
||||
// If 'k' has the highest level of all neighbors, then the power level of this
|
||||
// redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the
|
||||
// following loop can affect the power level of the wire. Therefore, the loop is
|
||||
// skipped if k is already 15.
|
||||
if (k < 15) {
|
||||
if (upd.neighbor_nodes == null) {
|
||||
// If this node's neighbors are not known, expand the node
|
||||
findNeighbors(worldIn, upd);
|
||||
}
|
||||
|
||||
// These remain constant, so pull them out of the loop.
|
||||
// Regardless of which direction is forward, the UpdateNode for the
|
||||
// position directly above the node being calculated is always
|
||||
// at index 1.
|
||||
UpdateNode center_up = upd.neighbor_nodes[1];
|
||||
boolean center_up_is_cube = center_up.currentState.isOccluding();
|
||||
|
||||
for (int m = 0; m < 4; m++) {
|
||||
// Get the neighbor array index of each of the four cardinal
|
||||
// neighbors.
|
||||
int n = rs_neighbors[m];
|
||||
|
||||
// Get the max redstone power level of each of the cardinal
|
||||
// neighbors
|
||||
UpdateNode neighbor = upd.neighbor_nodes[n];
|
||||
l = getMaxCurrentStrength(neighbor, l);
|
||||
|
||||
// Also check the positions above and below the cardinal
|
||||
// neighbors
|
||||
boolean neighbor_is_cube = neighbor.currentState.isOccluding();
|
||||
if (!neighbor_is_cube) {
|
||||
UpdateNode neighbor_down = upd.neighbor_nodes[rs_neighbors_dn[m]];
|
||||
l = getMaxCurrentStrength(neighbor_down, l);
|
||||
} else
|
||||
if (!center_up_is_cube) {
|
||||
UpdateNode neighbor_up = upd.neighbor_nodes[rs_neighbors_up[m]];
|
||||
l = getMaxCurrentStrength(neighbor_up, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The new code sets this RedstoneWire block's power level to the highest neighbor
|
||||
// minus 1. This usually results in wire power levels dropping by 2 at a time.
|
||||
// This optimization alone has no impact on update order, only the number of updates.
|
||||
j = l - 1;
|
||||
|
||||
// If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will
|
||||
// always be in the range of 0 to 15, the following if will correct that.
|
||||
if (k > j) j = k;
|
||||
|
||||
// egg82's amendment
|
||||
// Adding Bukkit's BlockRedstoneEvent - er.. event.
|
||||
if (i != j) {
|
||||
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(upd.self.getX(), upd.self.getY(), upd.self.getZ()), i, j);
|
||||
worldIn.getServer().getPluginManager().callEvent(event);
|
||||
j = event.getNewCurrent();
|
||||
}
|
||||
|
||||
if (i != j) {
|
||||
// If the power level has changed from its previous value, compute a new state
|
||||
// and set it in the world.
|
||||
// Possible optimization: Don't commit state changes to the world until they
|
||||
// need to be known by some nearby non-redstone-wire block.
|
||||
state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(j));
|
||||
worldIn.setTypeAndData(upd.self, state, 2);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimized function to compute a redstone wire's power level based on cached
|
||||
* state.
|
||||
*/
|
||||
private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) {
|
||||
if (upd.type != UpdateNode.Type.REDSTONE) return strength;
|
||||
final int i = upd.currentState.get(BlockRedstoneWire.POWER).intValue();
|
||||
return i > strength ? i : strength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.mojang.authlib.yggdrasil;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.GameProfileRepository;
|
||||
import com.mojang.authlib.HttpAuthenticationService;
|
||||
import com.mojang.authlib.ProfileLookupCallback;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.yggdrasil.response.ProfileSearchResultsResponse;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class YggdrasilGameProfileRepository implements GameProfileRepository {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final String BASE_URL = "https://api.mojang.com/";
|
||||
private static final String SEARCH_PAGE_URL = BASE_URL + "profiles/";
|
||||
private static final int ENTRIES_PER_PAGE = 2;
|
||||
private static final int MAX_FAIL_COUNT = 3;
|
||||
private static final int DELAY_BETWEEN_PAGES = 100;
|
||||
private static final int DELAY_BETWEEN_FAILURES = 750;
|
||||
|
||||
private final YggdrasilAuthenticationService authenticationService;
|
||||
|
||||
public YggdrasilGameProfileRepository(final YggdrasilAuthenticationService authenticationService) {
|
||||
this.authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findProfilesByNames(final String[] names, final Agent agent, final ProfileLookupCallback callback) {
|
||||
final Set<String> criteria = Sets.newHashSet();
|
||||
|
||||
for (final String name : names) {
|
||||
if (!Strings.isNullOrEmpty(name)) {
|
||||
criteria.add(name.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
final int page = 0;
|
||||
boolean hasRequested = false; // Paper
|
||||
|
||||
for (final List<String> request : Iterables.partition(criteria, ENTRIES_PER_PAGE)) {
|
||||
int failCount = 0;
|
||||
boolean failed;
|
||||
|
||||
do {
|
||||
failed = false;
|
||||
|
||||
try {
|
||||
final ProfileSearchResultsResponse response = authenticationService.makeRequest(HttpAuthenticationService.constantURL(SEARCH_PAGE_URL + agent.getName().toLowerCase()), request, ProfileSearchResultsResponse.class);
|
||||
failCount = 0;
|
||||
|
||||
LOGGER.debug("Page {} returned {} results, parsing", page, response.getProfiles().length);
|
||||
|
||||
final Set<String> missing = Sets.newHashSet(request);
|
||||
for (final GameProfile profile : response.getProfiles()) {
|
||||
LOGGER.debug("Successfully looked up profile {}", profile);
|
||||
missing.remove(profile.getName().toLowerCase());
|
||||
callback.onProfileLookupSucceeded(profile);
|
||||
}
|
||||
|
||||
for (final String name : missing) {
|
||||
LOGGER.debug("Couldn't find profile {}", name);
|
||||
callback.onProfileLookupFailed(new GameProfile(null, name), new ProfileNotFoundException("Server did not find the requested profile"));
|
||||
}
|
||||
// Paper start
|
||||
if (!hasRequested) {
|
||||
hasRequested = true;
|
||||
continue;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
try {
|
||||
Thread.sleep(DELAY_BETWEEN_PAGES);
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
} catch (final AuthenticationException e) {
|
||||
failCount++;
|
||||
|
||||
if (failCount == MAX_FAIL_COUNT) {
|
||||
for (final String name : request) {
|
||||
LOGGER.debug("Couldn't find profile {} because of a server error", name);
|
||||
callback.onProfileLookupFailed(new GameProfile(null, name), e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(DELAY_BETWEEN_FAILURES);
|
||||
} catch (final InterruptedException ignored) {
|
||||
}
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
} while (failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
207
src/main/java/com/mojang/brigadier/tree/CommandNode.java
Normal file
207
src/main/java/com/mojang/brigadier/tree/CommandNode.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package com.mojang.brigadier.tree;
|
||||
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.AmbiguityConsumer;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.server.CommandListenerWrapper; // CraftBukkit
|
||||
|
||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||
private Map<String, CommandNode<S>> children = Maps.newLinkedHashMap();
|
||||
private Map<String, LiteralCommandNode<S>> literals = Maps.newLinkedHashMap();
|
||||
private Map<String, ArgumentCommandNode<S, ?>> arguments = Maps.newLinkedHashMap();
|
||||
private final Predicate<S> requirement;
|
||||
private final CommandNode<S> redirect;
|
||||
private final RedirectModifier<S> modifier;
|
||||
private final boolean forks;
|
||||
private Command<S> command;
|
||||
// CraftBukkit start
|
||||
public void removeCommand(String name) {
|
||||
children.remove(name);
|
||||
literals.remove(name);
|
||||
arguments.remove(name);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||
this.command = command;
|
||||
this.requirement = requirement;
|
||||
this.redirect = redirect;
|
||||
this.modifier = modifier;
|
||||
this.forks = forks;
|
||||
}
|
||||
|
||||
public Command<S> getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public Collection<CommandNode<S>> getChildren() {
|
||||
return children.values();
|
||||
}
|
||||
|
||||
public CommandNode<S> getChild(final String name) {
|
||||
return children.get(name);
|
||||
}
|
||||
|
||||
public CommandNode<S> getRedirect() {
|
||||
return redirect;
|
||||
}
|
||||
|
||||
public RedirectModifier<S> getRedirectModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
public boolean canUse(final S source) {
|
||||
// CraftBukkit start
|
||||
if (source instanceof CommandListenerWrapper) {
|
||||
try {
|
||||
((CommandListenerWrapper) source).currentCommand = this;
|
||||
return requirement.test(source);
|
||||
} finally {
|
||||
((CommandListenerWrapper) source).currentCommand = null;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
return requirement.test(source);
|
||||
}
|
||||
|
||||
public void addChild(final CommandNode<S> node) {
|
||||
if (node instanceof RootCommandNode) {
|
||||
throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
|
||||
}
|
||||
|
||||
final CommandNode<S> child = children.get(node.getName());
|
||||
if (child != null) {
|
||||
// We've found something to merge onto
|
||||
if (node.getCommand() != null) {
|
||||
child.command = node.getCommand();
|
||||
}
|
||||
for (final CommandNode<S> grandchild : node.getChildren()) {
|
||||
child.addChild(grandchild);
|
||||
}
|
||||
} else {
|
||||
children.put(node.getName(), node);
|
||||
if (node instanceof LiteralCommandNode) {
|
||||
literals.put(node.getName(), (LiteralCommandNode<S>) node);
|
||||
} else if (node instanceof ArgumentCommandNode) {
|
||||
arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
||||
}
|
||||
}
|
||||
|
||||
children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
||||
Set<String> matches = Sets.newHashSet();
|
||||
|
||||
for (final CommandNode<S> child : children.values()) {
|
||||
for (final CommandNode<S> sibling : children.values()) {
|
||||
if (child == sibling) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (final String input : child.getExamples()) {
|
||||
if (sibling.isValidInput(input)) {
|
||||
matches.add(input);
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.size() > 0) {
|
||||
consumer.ambiguous(this, child, sibling, matches);
|
||||
matches = Sets.newHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
child.findAmbiguities(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isValidInput(final String input);
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CommandNode)) return false;
|
||||
|
||||
final CommandNode<S> that = (CommandNode<S>) o;
|
||||
|
||||
if (!children.equals(that.children)) return false;
|
||||
if (command != null ? !command.equals(that.command) : that.command != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * children.hashCode() + (command != null ? command.hashCode() : 0);
|
||||
}
|
||||
|
||||
public Predicate<S> getRequirement() {
|
||||
return requirement;
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
|
||||
public abstract String getUsageText();
|
||||
|
||||
public abstract void parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException;
|
||||
|
||||
public abstract CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) throws CommandSyntaxException;
|
||||
|
||||
public abstract ArgumentBuilder<S, ?> createBuilder();
|
||||
|
||||
protected abstract String getSortedKey();
|
||||
|
||||
public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input) {
|
||||
if (literals.size() > 0) {
|
||||
final int cursor = input.getCursor();
|
||||
while (input.canRead() && input.peek() != ' ') {
|
||||
input.skip();
|
||||
}
|
||||
final String text = input.getString().substring(cursor, input.getCursor());
|
||||
input.setCursor(cursor);
|
||||
final LiteralCommandNode<S> literal = literals.get(text);
|
||||
if (literal != null) {
|
||||
return Collections.singleton(literal);
|
||||
} else {
|
||||
return arguments.values();
|
||||
}
|
||||
} else {
|
||||
return arguments.values();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final CommandNode<S> o) {
|
||||
return ComparisonChain
|
||||
.start()
|
||||
.compareTrueFirst(this instanceof LiteralCommandNode, o instanceof LiteralCommandNode)
|
||||
.compare(getSortedKey(), o.getSortedKey())
|
||||
.result();
|
||||
}
|
||||
|
||||
public boolean isFork() {
|
||||
return forks;
|
||||
}
|
||||
|
||||
public abstract Collection<String> getExamples();
|
||||
}
|
||||
436
src/main/java/net/minecraft/server/Advancement.java
Normal file
436
src/main/java/net/minecraft/server/Advancement.java
Normal file
@@ -0,0 +1,436 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
public class Advancement {
|
||||
|
||||
private final Advancement a;
|
||||
private final AdvancementDisplay b;
|
||||
private final AdvancementRewards c;
|
||||
private final MinecraftKey d;
|
||||
private final Map<String, Criterion> e;
|
||||
private final String[][] f;
|
||||
private final Set<Advancement> g = Sets.newLinkedHashSet();
|
||||
private final IChatBaseComponent h;
|
||||
public final org.bukkit.advancement.Advancement bukkit = new org.bukkit.craftbukkit.advancement.CraftAdvancement(this); // CraftBukkit
|
||||
|
||||
public Advancement(MinecraftKey minecraftkey, @Nullable Advancement advancement, @Nullable AdvancementDisplay advancementdisplay, AdvancementRewards advancementrewards, Map<String, Criterion> map, String[][] astring) {
|
||||
this.d = minecraftkey;
|
||||
this.b = advancementdisplay;
|
||||
this.e = ImmutableMap.copyOf(map);
|
||||
this.a = advancement;
|
||||
this.c = advancementrewards;
|
||||
this.f = astring;
|
||||
if (advancement != null) {
|
||||
advancement.a(this);
|
||||
}
|
||||
|
||||
if (advancementdisplay == null) {
|
||||
this.h = new ChatComponentText(minecraftkey.toString());
|
||||
} else {
|
||||
IChatBaseComponent ichatbasecomponent = advancementdisplay.a();
|
||||
EnumChatFormat enumchatformat = advancementdisplay.e().c();
|
||||
IChatBaseComponent ichatbasecomponent1 = ichatbasecomponent.h().a(enumchatformat).a("\n").addSibling(advancementdisplay.b());
|
||||
IChatBaseComponent ichatbasecomponent2 = ichatbasecomponent.h().a((chatmodifier) -> {
|
||||
chatmodifier.setChatHoverable(new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_TEXT, ichatbasecomponent1));
|
||||
});
|
||||
|
||||
this.h = (new ChatComponentText("[")).addSibling(ichatbasecomponent2).a("]").a(enumchatformat);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a() {
|
||||
return new Advancement.SerializedAdvancement(this.a == null ? null : this.a.getName(), this.b, this.c, this.e, this.f);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Advancement b() {
|
||||
return this.a;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AdvancementDisplay c() {
|
||||
return this.b;
|
||||
}
|
||||
|
||||
public AdvancementRewards d() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "SimpleAdvancement{id=" + this.getName() + ", parent=" + (this.a == null ? "null" : this.a.getName()) + ", display=" + this.b + ", rewards=" + this.c + ", criteria=" + this.e + ", requirements=" + Arrays.deepToString(this.f) + '}';
|
||||
}
|
||||
|
||||
public Iterable<Advancement> e() {
|
||||
return this.g;
|
||||
}
|
||||
|
||||
public Map<String, Criterion> getCriteria() {
|
||||
return this.e;
|
||||
}
|
||||
|
||||
public void a(Advancement advancement) {
|
||||
this.g.add(advancement);
|
||||
}
|
||||
|
||||
public MinecraftKey getName() {
|
||||
return this.d;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof Advancement)) {
|
||||
return false;
|
||||
} else {
|
||||
Advancement advancement = (Advancement) object;
|
||||
|
||||
return this.d.equals(advancement.d);
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.d.hashCode();
|
||||
}
|
||||
|
||||
public String[][] i() {
|
||||
return this.f;
|
||||
}
|
||||
|
||||
public IChatBaseComponent j() {
|
||||
return this.h;
|
||||
}
|
||||
|
||||
public static class SerializedAdvancement {
|
||||
|
||||
private MinecraftKey a;
|
||||
private Advancement b;
|
||||
private AdvancementDisplay c;
|
||||
private AdvancementRewards d;
|
||||
private Map<String, Criterion> e;
|
||||
private String[][] f;
|
||||
private AdvancementRequirements g;
|
||||
|
||||
private SerializedAdvancement(@Nullable MinecraftKey minecraftkey, @Nullable AdvancementDisplay advancementdisplay, AdvancementRewards advancementrewards, Map<String, Criterion> map, String[][] astring) {
|
||||
this.d = AdvancementRewards.a;
|
||||
this.e = Maps.newLinkedHashMap();
|
||||
this.g = AdvancementRequirements.AND;
|
||||
this.a = minecraftkey;
|
||||
this.c = advancementdisplay;
|
||||
this.d = advancementrewards;
|
||||
this.e = map;
|
||||
this.f = astring;
|
||||
}
|
||||
|
||||
private SerializedAdvancement() {
|
||||
this.d = AdvancementRewards.a;
|
||||
this.e = Maps.newLinkedHashMap();
|
||||
this.g = AdvancementRequirements.AND;
|
||||
}
|
||||
|
||||
public static Advancement.SerializedAdvancement a() {
|
||||
return new Advancement.SerializedAdvancement();
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(Advancement advancement) {
|
||||
this.b = advancement;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(MinecraftKey minecraftkey) {
|
||||
this.a = minecraftkey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(IMaterial imaterial, IChatBaseComponent ichatbasecomponent, IChatBaseComponent ichatbasecomponent1, @Nullable MinecraftKey minecraftkey, AdvancementFrameType advancementframetype, boolean flag, boolean flag1, boolean flag2) {
|
||||
return this.a(new AdvancementDisplay(new ItemStack(imaterial.getItem()), ichatbasecomponent, ichatbasecomponent1, minecraftkey, advancementframetype, flag, flag1, flag2));
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementDisplay advancementdisplay) {
|
||||
this.c = advancementdisplay;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementRewards.a advancementrewards_a) {
|
||||
return this.a(advancementrewards_a.a());
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementRewards advancementrewards) {
|
||||
this.d = advancementrewards;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(String s, CriterionInstance criterioninstance) {
|
||||
return this.a(s, new Criterion(criterioninstance));
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(String s, Criterion criterion) {
|
||||
if (this.e.containsKey(s)) {
|
||||
throw new IllegalArgumentException("Duplicate criterion " + s);
|
||||
} else {
|
||||
this.e.put(s, criterion);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement.SerializedAdvancement a(AdvancementRequirements advancementrequirements) {
|
||||
this.g = advancementrequirements;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean a(Function<MinecraftKey, Advancement> function) {
|
||||
if (this.a == null) {
|
||||
return true;
|
||||
} else {
|
||||
if (this.b == null) {
|
||||
this.b = (Advancement) function.apply(this.a);
|
||||
}
|
||||
|
||||
return this.b != null;
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement b(MinecraftKey minecraftkey) {
|
||||
if (!this.a((Function<MinecraftKey, Advancement>) (minecraftkey1) -> { // CraftBukkit - decompile error
|
||||
return null;
|
||||
})) {
|
||||
throw new IllegalStateException("Tried to build incomplete advancement!");
|
||||
} else {
|
||||
if (this.f == null) {
|
||||
this.f = this.g.createRequirements(this.e.keySet());
|
||||
}
|
||||
|
||||
return new Advancement(minecraftkey, this.b, this.c, this.d, this.e, this.f);
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement a(Consumer<Advancement> consumer, String s) {
|
||||
Advancement advancement = this.b(new MinecraftKey(s));
|
||||
|
||||
consumer.accept(advancement);
|
||||
return advancement;
|
||||
}
|
||||
|
||||
public JsonObject b() {
|
||||
if (this.f == null) {
|
||||
this.f = this.g.createRequirements(this.e.keySet());
|
||||
}
|
||||
|
||||
JsonObject jsonobject = new JsonObject();
|
||||
|
||||
if (this.b != null) {
|
||||
jsonobject.addProperty("parent", this.b.getName().toString());
|
||||
} else if (this.a != null) {
|
||||
jsonobject.addProperty("parent", this.a.toString());
|
||||
}
|
||||
|
||||
if (this.c != null) {
|
||||
jsonobject.add("display", this.c.k());
|
||||
}
|
||||
|
||||
jsonobject.add("rewards", this.d.b());
|
||||
JsonObject jsonobject1 = new JsonObject();
|
||||
Iterator iterator = this.e.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Criterion> entry = (Entry) iterator.next();
|
||||
|
||||
jsonobject1.add((String) entry.getKey(), ((Criterion) entry.getValue()).b());
|
||||
}
|
||||
|
||||
jsonobject.add("criteria", jsonobject1);
|
||||
JsonArray jsonarray = new JsonArray();
|
||||
String[][] astring = this.f;
|
||||
int i = astring.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
String[] astring1 = astring[j];
|
||||
JsonArray jsonarray1 = new JsonArray();
|
||||
String[] astring2 = astring1;
|
||||
int k = astring1.length;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
String s = astring2[l];
|
||||
|
||||
jsonarray1.add(s);
|
||||
}
|
||||
|
||||
jsonarray.add(jsonarray1);
|
||||
}
|
||||
|
||||
jsonobject.add("requirements", jsonarray);
|
||||
return jsonobject;
|
||||
}
|
||||
|
||||
public void a(PacketDataSerializer packetdataserializer) {
|
||||
if (this.a == null) {
|
||||
packetdataserializer.writeBoolean(false);
|
||||
} else {
|
||||
packetdataserializer.writeBoolean(true);
|
||||
packetdataserializer.a(this.a);
|
||||
}
|
||||
|
||||
if (this.c == null) {
|
||||
packetdataserializer.writeBoolean(false);
|
||||
} else {
|
||||
packetdataserializer.writeBoolean(true);
|
||||
this.c.a(packetdataserializer);
|
||||
}
|
||||
|
||||
Criterion.a(this.e, packetdataserializer);
|
||||
packetdataserializer.d(this.f.length);
|
||||
String[][] astring = this.f;
|
||||
int i = astring.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
String[] astring1 = astring[j];
|
||||
|
||||
packetdataserializer.d(astring1.length);
|
||||
String[] astring2 = astring1;
|
||||
int k = astring1.length;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
String s = astring2[l];
|
||||
|
||||
packetdataserializer.a(s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Task Advancement{parentId=" + this.a + ", display=" + this.c + ", rewards=" + this.d + ", criteria=" + this.e + ", requirements=" + Arrays.deepToString(this.f) + '}';
|
||||
}
|
||||
|
||||
public static Advancement.SerializedAdvancement a(JsonObject jsonobject, JsonDeserializationContext jsondeserializationcontext) {
|
||||
MinecraftKey minecraftkey = jsonobject.has("parent") ? new MinecraftKey(ChatDeserializer.h(jsonobject, "parent")) : null;
|
||||
AdvancementDisplay advancementdisplay = jsonobject.has("display") ? AdvancementDisplay.a(ChatDeserializer.t(jsonobject, "display"), jsondeserializationcontext) : null;
|
||||
AdvancementRewards advancementrewards = (AdvancementRewards) ChatDeserializer.a(jsonobject, "rewards", AdvancementRewards.a, jsondeserializationcontext, AdvancementRewards.class);
|
||||
Map<String, Criterion> map = Criterion.b(ChatDeserializer.t(jsonobject, "criteria"), jsondeserializationcontext);
|
||||
|
||||
if (map.isEmpty()) {
|
||||
throw new JsonSyntaxException("Advancement criteria cannot be empty");
|
||||
} else {
|
||||
JsonArray jsonarray = ChatDeserializer.a(jsonobject, "requirements", new JsonArray());
|
||||
String[][] astring = new String[jsonarray.size()][];
|
||||
|
||||
int i;
|
||||
int j;
|
||||
|
||||
for (i = 0; i < jsonarray.size(); ++i) {
|
||||
JsonArray jsonarray1 = ChatDeserializer.n(jsonarray.get(i), "requirements[" + i + "]");
|
||||
|
||||
astring[i] = new String[jsonarray1.size()];
|
||||
|
||||
for (j = 0; j < jsonarray1.size(); ++j) {
|
||||
astring[i][j] = ChatDeserializer.a(jsonarray1.get(j), "requirements[" + i + "][" + j + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (astring.length == 0) {
|
||||
astring = new String[map.size()][];
|
||||
i = 0;
|
||||
|
||||
String s;
|
||||
|
||||
for (Iterator iterator = map.keySet().iterator(); iterator.hasNext(); astring[i++] = new String[] { s}) {
|
||||
s = (String) iterator.next();
|
||||
}
|
||||
}
|
||||
|
||||
String[][] astring1 = astring;
|
||||
int k = astring.length;
|
||||
|
||||
int l;
|
||||
|
||||
for (j = 0; j < k; ++j) {
|
||||
String[] astring2 = astring1[j];
|
||||
|
||||
if (astring2.length == 0 && map.isEmpty()) {
|
||||
throw new JsonSyntaxException("Requirement entry cannot be empty");
|
||||
}
|
||||
|
||||
String[] astring3 = astring2;
|
||||
|
||||
l = astring2.length;
|
||||
|
||||
for (int i1 = 0; i1 < l; ++i1) {
|
||||
String s1 = astring3[i1];
|
||||
|
||||
if (!map.containsKey(s1)) {
|
||||
throw new JsonSyntaxException("Unknown required criterion '" + s1 + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterator iterator1 = map.keySet().iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
String s2 = (String) iterator1.next();
|
||||
boolean flag = false;
|
||||
String[][] astring4 = astring;
|
||||
int j1 = astring.length;
|
||||
|
||||
l = 0;
|
||||
|
||||
while (true) {
|
||||
if (l < j1) {
|
||||
String[] astring5 = astring4[l];
|
||||
|
||||
if (!ArrayUtils.contains(astring5, s2)) {
|
||||
++l;
|
||||
continue;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
throw new JsonSyntaxException("Criterion '" + s2 + "' isn't a requirement for completion. This isn't supported behaviour, all criteria must be required.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Advancement.SerializedAdvancement(minecraftkey, advancementdisplay, advancementrewards, map, astring);
|
||||
}
|
||||
}
|
||||
|
||||
public static Advancement.SerializedAdvancement b(PacketDataSerializer packetdataserializer) {
|
||||
MinecraftKey minecraftkey = packetdataserializer.readBoolean() ? packetdataserializer.l() : null;
|
||||
AdvancementDisplay advancementdisplay = packetdataserializer.readBoolean() ? AdvancementDisplay.b(packetdataserializer) : null;
|
||||
Map<String, Criterion> map = Criterion.c(packetdataserializer);
|
||||
String[][] astring = new String[packetdataserializer.g()][];
|
||||
|
||||
for (int i = 0; i < astring.length; ++i) {
|
||||
astring[i] = new String[packetdataserializer.g()];
|
||||
|
||||
for (int j = 0; j < astring[i].length; ++j) {
|
||||
astring[i][j] = packetdataserializer.e(32767);
|
||||
}
|
||||
}
|
||||
|
||||
return new Advancement.SerializedAdvancement(minecraftkey, advancementdisplay, AdvancementRewards.a, map, astring);
|
||||
}
|
||||
|
||||
public Map<String, Criterion> c() {
|
||||
return this.e;
|
||||
}
|
||||
}
|
||||
}
|
||||
466
src/main/java/net/minecraft/server/AdvancementDataPlayer.java
Normal file
466
src/main/java/net/minecraft/server/AdvancementDataPlayer.java
Normal file
@@ -0,0 +1,466 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.mojang.datafixers.DataFixTypes;
|
||||
import com.mojang.datafixers.Dynamic;
|
||||
import com.mojang.datafixers.types.JsonOps;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class AdvancementDataPlayer {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
private static final Gson b = (new GsonBuilder()).registerTypeAdapter(AdvancementProgress.class, new AdvancementProgress.a()).registerTypeAdapter(MinecraftKey.class, new MinecraftKey.a()).setPrettyPrinting().create();
|
||||
private static final TypeToken<Map<MinecraftKey, AdvancementProgress>> c = new TypeToken<Map<MinecraftKey, AdvancementProgress>>() {
|
||||
};
|
||||
private final MinecraftServer d;
|
||||
private final File e;
|
||||
public final Map<Advancement, AdvancementProgress> data = Maps.newLinkedHashMap();
|
||||
private final Set<Advancement> g = Sets.newLinkedHashSet();
|
||||
private final Set<Advancement> h = Sets.newLinkedHashSet();
|
||||
private final Set<Advancement> i = Sets.newLinkedHashSet();
|
||||
private EntityPlayer player;
|
||||
@Nullable
|
||||
private Advancement k;
|
||||
private boolean l = true;
|
||||
|
||||
public AdvancementDataPlayer(MinecraftServer minecraftserver, File file, EntityPlayer entityplayer) {
|
||||
this.d = minecraftserver;
|
||||
this.e = file;
|
||||
this.player = entityplayer;
|
||||
this.g();
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer) {
|
||||
this.player = entityplayer;
|
||||
}
|
||||
|
||||
public void a() {
|
||||
Iterator iterator = CriterionTriggers.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
CriterionTrigger<?> criteriontrigger = (CriterionTrigger) iterator.next();
|
||||
|
||||
criteriontrigger.a(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void b() {
|
||||
this.a();
|
||||
this.data.clear();
|
||||
this.g.clear();
|
||||
this.h.clear();
|
||||
this.i.clear();
|
||||
this.l = true;
|
||||
this.k = null;
|
||||
this.g();
|
||||
}
|
||||
|
||||
private void d() {
|
||||
Iterator iterator = this.d.getAdvancementData().b().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
this.c(advancement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void e() {
|
||||
List<Advancement> list = Lists.newArrayList();
|
||||
Iterator iterator = this.data.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Advancement, AdvancementProgress> entry = (Entry) iterator.next();
|
||||
|
||||
if (((AdvancementProgress) entry.getValue()).isDone()) {
|
||||
list.add(entry.getKey());
|
||||
this.i.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
this.e(advancement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void f() {
|
||||
Iterator iterator = this.d.getAdvancementData().b().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
if (advancement.getCriteria().isEmpty()) {
|
||||
this.grantCriteria(advancement, "");
|
||||
advancement.d().a(this.player);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void g() {
|
||||
if (this.e.isFile()) {
|
||||
try {
|
||||
JsonReader jsonreader = new JsonReader(new StringReader(Files.toString(this.e, StandardCharsets.UTF_8)));
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
jsonreader.setLenient(false);
|
||||
Dynamic<JsonElement> dynamic = new Dynamic(JsonOps.INSTANCE, Streams.parse(jsonreader));
|
||||
|
||||
if (!dynamic.get("DataVersion").flatMap(Dynamic::getNumberValue).isPresent()) {
|
||||
dynamic = dynamic.set("DataVersion", dynamic.createInt(1343));
|
||||
}
|
||||
|
||||
dynamic = this.d.az().update(DataFixTypes.ADVANCEMENTS, dynamic, dynamic.getInt("DataVersion"), 1631);
|
||||
dynamic = dynamic.remove("DataVersion");
|
||||
Map<MinecraftKey, AdvancementProgress> map = (Map) AdvancementDataPlayer.b.getAdapter(AdvancementDataPlayer.c).fromJsonTree((JsonElement) dynamic.getValue());
|
||||
|
||||
if (map == null) {
|
||||
throw new JsonParseException("Found null for advancements");
|
||||
}
|
||||
|
||||
Stream<Entry<MinecraftKey, AdvancementProgress>> stream = map.entrySet().stream().sorted(Comparator.comparing(Entry::getValue));
|
||||
Iterator iterator = ((List) stream.collect(Collectors.toList())).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<MinecraftKey, AdvancementProgress> entry = (Entry) iterator.next();
|
||||
Advancement advancement = this.d.getAdvancementData().a((MinecraftKey) entry.getKey());
|
||||
|
||||
if (advancement == null) {
|
||||
// CraftBukkit start
|
||||
if (((MinecraftKey) entry.getKey()).b().equals("minecraft")) {
|
||||
AdvancementDataPlayer.a.warn("Ignored advancement '{}' in progress file {} - it doesn't exist anymore?", entry.getKey(), this.e);
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else {
|
||||
this.a(advancement, (AdvancementProgress) entry.getValue());
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (jsonreader != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
jsonreader.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
jsonreader.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (JsonParseException jsonparseexception) {
|
||||
AdvancementDataPlayer.a.error("Couldn't parse player advancements in {}", this.e, jsonparseexception);
|
||||
} catch (IOException ioexception) {
|
||||
AdvancementDataPlayer.a.error("Couldn't access player advancements in {}", this.e, ioexception);
|
||||
}
|
||||
}
|
||||
|
||||
this.f();
|
||||
this.e();
|
||||
this.d();
|
||||
}
|
||||
|
||||
public void c() {
|
||||
if (org.spigotmc.SpigotConfig.disableAdvancementSaving) return;
|
||||
Map<MinecraftKey, AdvancementProgress> map = Maps.newHashMap();
|
||||
Iterator iterator = this.data.entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Advancement, AdvancementProgress> entry = (Entry) iterator.next();
|
||||
AdvancementProgress advancementprogress = (AdvancementProgress) entry.getValue();
|
||||
|
||||
if (advancementprogress.b()) {
|
||||
map.put(((Advancement) entry.getKey()).getName(), advancementprogress);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.e.getParentFile() != null) {
|
||||
this.e.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
try {
|
||||
Files.write(AdvancementDataPlayer.b.toJson(map), this.e, StandardCharsets.UTF_8);
|
||||
} catch (IOException ioexception) {
|
||||
AdvancementDataPlayer.a.error("Couldn't save player advancements to {}", this.e, ioexception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean grantCriteria(Advancement advancement, String s) {
|
||||
boolean flag = false;
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
boolean flag1 = advancementprogress.isDone();
|
||||
|
||||
if (advancementprogress.a(s)) {
|
||||
// Paper start
|
||||
if (!new com.destroystokyo.paper.event.player.PlayerAdvancementCriterionGrantEvent(this.player.getBukkitEntity(), advancement.bukkit, s).callEvent()) {
|
||||
advancementprogress.b(s);
|
||||
return false;
|
||||
}
|
||||
// Paper end
|
||||
this.d(advancement);
|
||||
this.i.add(advancement);
|
||||
flag = true;
|
||||
if (!flag1 && advancementprogress.isDone()) {
|
||||
this.player.world.getServer().getPluginManager().callEvent(new org.bukkit.event.player.PlayerAdvancementDoneEvent(this.player.getBukkitEntity(), advancement.bukkit)); // CraftBukkit
|
||||
advancement.d().a(this.player);
|
||||
if (advancement.c() != null && advancement.c().i() && this.player.world.getGameRules().getBoolean("announceAdvancements")) {
|
||||
this.d.getPlayerList().sendMessage(new ChatMessage("chat.type.advancement." + advancement.c().e().a(), new Object[] { this.player.getScoreboardDisplayName(), advancement.j()}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (advancementprogress.isDone()) {
|
||||
this.e(advancement);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
public boolean revokeCritera(Advancement advancement, String s) {
|
||||
boolean flag = false;
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (advancementprogress.b(s)) {
|
||||
this.c(advancement);
|
||||
this.i.add(advancement);
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (!advancementprogress.b()) {
|
||||
this.e(advancement);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
private void c(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (!advancementprogress.isDone()) {
|
||||
Iterator iterator = advancement.getCriteria().entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Criterion> entry = (Entry) iterator.next();
|
||||
CriterionProgress criterionprogress = advancementprogress.getCriterionProgress((String) entry.getKey());
|
||||
|
||||
if (criterionprogress != null && !criterionprogress.a()) {
|
||||
CriterionInstance criterioninstance = ((Criterion) entry.getValue()).a();
|
||||
|
||||
if (criterioninstance != null) {
|
||||
CriterionTrigger<CriterionInstance> criteriontrigger = CriterionTriggers.a(criterioninstance.a());
|
||||
|
||||
if (criteriontrigger != null) {
|
||||
criteriontrigger.a(this, new CriterionTrigger.a<>(criterioninstance, advancement, (String) entry.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void d(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
Iterator iterator = advancement.getCriteria().entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry<String, Criterion> entry = (Entry) iterator.next();
|
||||
CriterionProgress criterionprogress = advancementprogress.getCriterionProgress((String) entry.getKey());
|
||||
|
||||
if (criterionprogress != null && (criterionprogress.a() || advancementprogress.isDone())) {
|
||||
CriterionInstance criterioninstance = ((Criterion) entry.getValue()).a();
|
||||
|
||||
if (criterioninstance != null) {
|
||||
CriterionTrigger<CriterionInstance> criteriontrigger = CriterionTriggers.a(criterioninstance.a());
|
||||
|
||||
if (criteriontrigger != null) {
|
||||
criteriontrigger.b(this, new CriterionTrigger.a<>(criterioninstance, advancement, (String) entry.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void b(EntityPlayer entityplayer) {
|
||||
if (this.l || !this.h.isEmpty() || !this.i.isEmpty()) {
|
||||
Map<MinecraftKey, AdvancementProgress> map = Maps.newHashMap();
|
||||
Set<Advancement> set = Sets.newLinkedHashSet();
|
||||
Set<MinecraftKey> set1 = Sets.newLinkedHashSet();
|
||||
Iterator iterator = this.i.iterator();
|
||||
|
||||
Advancement advancement;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
advancement = (Advancement) iterator.next();
|
||||
if (this.g.contains(advancement)) {
|
||||
map.put(advancement.getName(), this.data.get(advancement));
|
||||
}
|
||||
}
|
||||
|
||||
iterator = this.h.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
advancement = (Advancement) iterator.next();
|
||||
if (this.g.contains(advancement)) {
|
||||
set.add(advancement);
|
||||
} else {
|
||||
set1.add(advancement.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.l || !map.isEmpty() || !set.isEmpty() || !set1.isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutAdvancements(this.l, set, set1, map));
|
||||
this.h.clear();
|
||||
this.i.clear();
|
||||
}
|
||||
}
|
||||
|
||||
this.l = false;
|
||||
}
|
||||
|
||||
public void a(@Nullable Advancement advancement) {
|
||||
Advancement advancement1 = this.k;
|
||||
|
||||
if (advancement != null && advancement.b() == null && advancement.c() != null) {
|
||||
this.k = advancement;
|
||||
} else {
|
||||
this.k = null;
|
||||
}
|
||||
|
||||
if (advancement1 != this.k) {
|
||||
this.player.playerConnection.sendPacket(new PacketPlayOutSelectAdvancementTab(this.k == null ? null : this.k.getName()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AdvancementProgress getProgress(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = (AdvancementProgress) this.data.get(advancement);
|
||||
|
||||
if (advancementprogress == null) {
|
||||
advancementprogress = new AdvancementProgress();
|
||||
this.a(advancement, advancementprogress);
|
||||
}
|
||||
|
||||
return advancementprogress;
|
||||
}
|
||||
|
||||
private void a(Advancement advancement, AdvancementProgress advancementprogress) {
|
||||
advancementprogress.a(advancement.getCriteria(), advancement.i());
|
||||
this.data.put(advancement, advancementprogress);
|
||||
}
|
||||
|
||||
private void e(Advancement advancement) {
|
||||
boolean flag = this.f(advancement);
|
||||
boolean flag1 = this.g.contains(advancement);
|
||||
|
||||
if (flag && !flag1) {
|
||||
this.g.add(advancement);
|
||||
this.h.add(advancement);
|
||||
if (this.data.containsKey(advancement)) {
|
||||
this.i.add(advancement);
|
||||
}
|
||||
} else if (!flag && flag1) {
|
||||
this.g.remove(advancement);
|
||||
this.h.add(advancement);
|
||||
}
|
||||
|
||||
if (flag != flag1 && advancement.b() != null) {
|
||||
this.e(advancement.b());
|
||||
}
|
||||
|
||||
Iterator iterator = advancement.e().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement1 = (Advancement) iterator.next();
|
||||
|
||||
this.e(advancement1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean f(Advancement advancement) {
|
||||
for (int i = 0; advancement != null && i <= 2; ++i) {
|
||||
if (i == 0 && this.g(advancement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (advancement.c() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (advancementprogress.isDone()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (advancement.c().j()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
advancement = advancement.b();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean g(Advancement advancement) {
|
||||
AdvancementProgress advancementprogress = this.getProgress(advancement);
|
||||
|
||||
if (advancementprogress.isDone()) {
|
||||
return true;
|
||||
} else {
|
||||
Iterator iterator = advancement.e().iterator();
|
||||
|
||||
Advancement advancement1;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
advancement1 = (Advancement) iterator.next();
|
||||
} while (!this.g(advancement1));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
src/main/java/net/minecraft/server/AdvancementDataWorld.java
Normal file
119
src/main/java/net/minecraft/server/AdvancementDataWorld.java
Normal file
@@ -0,0 +1,119 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class AdvancementDataWorld implements IResourcePackListener {
|
||||
|
||||
private static final Logger c = LogManager.getLogger();
|
||||
public static final Gson DESERIALIZER = (new GsonBuilder()).registerTypeHierarchyAdapter(Advancement.SerializedAdvancement.class, (com.google.gson.JsonDeserializer) (jsonelement, type, jsondeserializationcontext) -> {
|
||||
JsonObject jsonobject = ChatDeserializer.m(jsonelement, "advancement");
|
||||
|
||||
return Advancement.SerializedAdvancement.a(jsonobject, jsondeserializationcontext);
|
||||
}).registerTypeAdapter(AdvancementRewards.class, new AdvancementRewards.b()).registerTypeHierarchyAdapter(IChatBaseComponent.class, new IChatBaseComponent.ChatSerializer()).registerTypeHierarchyAdapter(ChatModifier.class, new ChatModifier.ChatModifierSerializer()).registerTypeAdapterFactory(new ChatTypeAdapterFactory()).create();
|
||||
public static final Advancements REGISTRY = new Advancements();
|
||||
public static final int a = "advancements/".length();
|
||||
public static final int b = ".json".length();
|
||||
private boolean f;
|
||||
|
||||
public AdvancementDataWorld() {}
|
||||
|
||||
private Map<MinecraftKey, Advancement.SerializedAdvancement> b(IResourceManager iresourcemanager) {
|
||||
Map<MinecraftKey, Advancement.SerializedAdvancement> map = Maps.newHashMap();
|
||||
Iterator iterator = iresourcemanager.a("advancements", (s) -> {
|
||||
return s.endsWith(".json");
|
||||
}).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
MinecraftKey minecraftkey = (MinecraftKey) iterator.next();
|
||||
String s = minecraftkey.getKey();
|
||||
MinecraftKey minecraftkey1 = new MinecraftKey(minecraftkey.b(), s.substring(AdvancementDataWorld.a, s.length() - AdvancementDataWorld.b));
|
||||
|
||||
try {
|
||||
IResource iresource = iresourcemanager.a(minecraftkey);
|
||||
Throwable throwable = null;
|
||||
// Spigot start
|
||||
if (org.spigotmc.SpigotConfig.disabledAdvancements != null && (org.spigotmc.SpigotConfig.disabledAdvancements.contains("*") || org.spigotmc.SpigotConfig.disabledAdvancements.contains(minecraftkey.toString()))) {
|
||||
continue;
|
||||
}
|
||||
// Spigot end
|
||||
|
||||
try {
|
||||
Advancement.SerializedAdvancement advancement_serializedadvancement = (Advancement.SerializedAdvancement) ChatDeserializer.a(AdvancementDataWorld.DESERIALIZER, IOUtils.toString(iresource.b(), StandardCharsets.UTF_8), Advancement.SerializedAdvancement.class);
|
||||
|
||||
if (advancement_serializedadvancement == null) {
|
||||
AdvancementDataWorld.c.error("Couldn't load custom advancement {} from {} as it's empty or null", minecraftkey1, minecraftkey);
|
||||
} else {
|
||||
map.put(minecraftkey1, advancement_serializedadvancement);
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (iresource != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
iresource.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
iresource.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (IllegalArgumentException | JsonParseException jsonparseexception) {
|
||||
AdvancementDataWorld.c.error("Parsing error loading custom advancement {}: {}", minecraftkey1, jsonparseexception.getMessage());
|
||||
this.f = true;
|
||||
} catch (IOException ioexception) {
|
||||
AdvancementDataWorld.c.error("Couldn't read custom advancement {} from {}", minecraftkey1, minecraftkey, ioexception);
|
||||
this.f = true;
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Advancement a(MinecraftKey minecraftkey) {
|
||||
return AdvancementDataWorld.REGISTRY.a(minecraftkey);
|
||||
}
|
||||
|
||||
public Collection<Advancement> b() {
|
||||
return AdvancementDataWorld.REGISTRY.c();
|
||||
}
|
||||
|
||||
public void a(IResourceManager iresourcemanager) {
|
||||
this.f = false;
|
||||
AdvancementDataWorld.REGISTRY.a();
|
||||
Map<MinecraftKey, Advancement.SerializedAdvancement> map = this.b(iresourcemanager);
|
||||
|
||||
AdvancementDataWorld.REGISTRY.a(map);
|
||||
Iterator iterator = AdvancementDataWorld.REGISTRY.b().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Advancement advancement = (Advancement) iterator.next();
|
||||
|
||||
if (advancement.c() != null) {
|
||||
AdvancementTree.a(advancement);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
109
src/main/java/net/minecraft/server/Advancements.java
Normal file
109
src/main/java/net/minecraft/server/Advancements.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class Advancements {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
public final Map<MinecraftKey, Advancement> advancements = Maps.newHashMap();
|
||||
private final Set<Advancement> c = Sets.newLinkedHashSet();
|
||||
private final Set<Advancement> d = Sets.newLinkedHashSet();
|
||||
private Advancements.a e;
|
||||
|
||||
public Advancements() {}
|
||||
|
||||
public void a(Map<MinecraftKey, Advancement.SerializedAdvancement> map) {
|
||||
Function function = Functions.forMap(this.advancements, (Object) null);
|
||||
|
||||
label42:
|
||||
while (!map.isEmpty()) {
|
||||
boolean flag = false;
|
||||
Iterator iterator = map.entrySet().iterator();
|
||||
|
||||
Entry entry;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
entry = (Entry) iterator.next();
|
||||
MinecraftKey minecraftkey = (MinecraftKey) entry.getKey();
|
||||
Advancement.SerializedAdvancement advancement_serializedadvancement = (Advancement.SerializedAdvancement) entry.getValue();
|
||||
|
||||
if (advancement_serializedadvancement.a((java.util.function.Function) function)) {
|
||||
Advancement advancement = advancement_serializedadvancement.b(minecraftkey);
|
||||
|
||||
this.advancements.put(minecraftkey, advancement);
|
||||
flag = true;
|
||||
iterator.remove();
|
||||
if (advancement.b() == null) {
|
||||
this.c.add(advancement);
|
||||
if (this.e != null) {
|
||||
this.e.a(advancement);
|
||||
}
|
||||
} else {
|
||||
this.d.add(advancement);
|
||||
if (this.e != null) {
|
||||
this.e.c(advancement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
iterator = map.entrySet().iterator();
|
||||
|
||||
while (true) {
|
||||
if (!iterator.hasNext()) {
|
||||
break label42;
|
||||
}
|
||||
|
||||
entry = (Entry) iterator.next();
|
||||
Advancements.a.error("Couldn't load advancement {}: {}", entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advancements.a.info("Loaded {} advancements", this.advancements.size()); // CraftBukkit - moved to AdvancementDataWorld#reload
|
||||
}
|
||||
|
||||
public void a() {
|
||||
this.advancements.clear();
|
||||
this.c.clear();
|
||||
this.d.clear();
|
||||
if (this.e != null) {
|
||||
this.e.a();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Iterable<Advancement> b() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public Collection<Advancement> c() {
|
||||
return this.advancements.values();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Advancement a(MinecraftKey minecraftkey) {
|
||||
return (Advancement) this.advancements.get(minecraftkey);
|
||||
}
|
||||
|
||||
public interface a {
|
||||
|
||||
void a(Advancement advancement);
|
||||
|
||||
void c(Advancement advancement);
|
||||
|
||||
void a();
|
||||
}
|
||||
}
|
||||
541
src/main/java/net/minecraft/server/ArgumentBlock.java
Normal file
541
src/main/java/net/minecraft/server/ArgumentBlock.java
Normal file
@@ -0,0 +1,541 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.UnmodifiableIterator;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.Dynamic3CommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ArgumentBlock {
|
||||
|
||||
public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.block.tag.disallowed", new Object[0]));
|
||||
public static final DynamicCommandExceptionType b = new DynamicCommandExceptionType((object) -> {
|
||||
return new ChatMessage("argument.block.id.invalid", new Object[] { object});
|
||||
});
|
||||
public static final Dynamic2CommandExceptionType c = new Dynamic2CommandExceptionType((object, object1) -> {
|
||||
return new ChatMessage("argument.block.property.unknown", new Object[] { object, object1});
|
||||
});
|
||||
public static final Dynamic2CommandExceptionType d = new Dynamic2CommandExceptionType((object, object1) -> {
|
||||
return new ChatMessage("argument.block.property.duplicate", new Object[] { object1, object});
|
||||
});
|
||||
public static final Dynamic3CommandExceptionType e = new Dynamic3CommandExceptionType((object, object1, object2) -> {
|
||||
return new ChatMessage("argument.block.property.invalid", new Object[] { object, object2, object1});
|
||||
});
|
||||
public static final Dynamic2CommandExceptionType f = new Dynamic2CommandExceptionType((object, object1) -> {
|
||||
return new ChatMessage("argument.block.property.novalue", new Object[] { object, object1});
|
||||
});
|
||||
public static final SimpleCommandExceptionType g = new SimpleCommandExceptionType(new ChatMessage("argument.block.property.unclosed", new Object[0]));
|
||||
private static final Function<SuggestionsBuilder, CompletableFuture<Suggestions>> h = SuggestionsBuilder::buildFuture;
|
||||
private final StringReader i;
|
||||
private final boolean j;
|
||||
private final Map<IBlockState<?>, Comparable<?>> k = Maps.newLinkedHashMap(); // CraftBukkit - stable
|
||||
private final Map<String, String> l = Maps.newHashMap();
|
||||
private MinecraftKey m = new MinecraftKey(""); public MinecraftKey getBlockKey() { return this.m; } // Paper - OBFHELPER
|
||||
private BlockStateList<Block, IBlockData> n;
|
||||
private IBlockData o;
|
||||
@Nullable
|
||||
private NBTTagCompound p;
|
||||
private MinecraftKey q = new MinecraftKey("");
|
||||
private int r;
|
||||
private Function<SuggestionsBuilder, CompletableFuture<Suggestions>> s;
|
||||
|
||||
public ArgumentBlock(StringReader stringreader, boolean flag) {
|
||||
this.s = ArgumentBlock.h;
|
||||
this.i = stringreader;
|
||||
this.j = flag;
|
||||
}
|
||||
|
||||
public Map<IBlockState<?>, Comparable<?>> getStateMap() {
|
||||
return this.k;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getBlockData() {
|
||||
return this.o;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public NBTTagCompound c() {
|
||||
return this.p;
|
||||
}
|
||||
|
||||
public @Nullable MinecraftKey getTagKey() { return d(); } // Paper - OBFHELPER
|
||||
@Nullable
|
||||
public MinecraftKey d() {
|
||||
return this.q;
|
||||
}
|
||||
|
||||
public ArgumentBlock parse(boolean parseTile) throws CommandSyntaxException { return this.a(parseTile); } // Paper - OBFHELPER
|
||||
public ArgumentBlock a(boolean flag) throws CommandSyntaxException {
|
||||
this.s = this::l;
|
||||
if (this.i.canRead() && this.i.peek() == '#') {
|
||||
this.f();
|
||||
this.s = this::i;
|
||||
if (this.i.canRead() && this.i.peek() == '[') {
|
||||
this.h();
|
||||
this.s = this::f;
|
||||
}
|
||||
} else {
|
||||
this.e();
|
||||
this.s = this::j;
|
||||
if (this.i.canRead() && this.i.peek() == '[') {
|
||||
this.g();
|
||||
this.s = this::f;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag && this.i.canRead() && this.i.peek() == '{') {
|
||||
this.s = ArgumentBlock.h;
|
||||
this.i();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> b(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
}
|
||||
|
||||
return this.d(suggestionsbuilder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> c(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
}
|
||||
|
||||
return this.e(suggestionsbuilder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> d(SuggestionsBuilder suggestionsbuilder) {
|
||||
String s = suggestionsbuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
Iterator iterator = this.o.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
IBlockState<?> iblockstate = (IBlockState) iterator.next();
|
||||
|
||||
if (!this.k.containsKey(iblockstate) && iblockstate.a().startsWith(s)) {
|
||||
suggestionsbuilder.suggest(iblockstate.a() + '=');
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> e(SuggestionsBuilder suggestionsbuilder) {
|
||||
String s = suggestionsbuilder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (this.q != null && !this.q.getKey().isEmpty()) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
Iterator iterator1 = block.getStates().d().iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
IBlockState<?> iblockstate = (IBlockState) iterator1.next();
|
||||
|
||||
if (!this.l.containsKey(iblockstate.a()) && iblockstate.a().startsWith(s)) {
|
||||
suggestionsbuilder.suggest(iblockstate.a() + '=');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> f(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty() && this.k()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('{'));
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private boolean k() {
|
||||
if (this.o != null) {
|
||||
return this.o.getBlock().isTileEntity();
|
||||
} else {
|
||||
if (this.q != null) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
|
||||
if (block.isTileEntity()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> g(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('='));
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> h(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
}
|
||||
|
||||
if (suggestionsbuilder.getRemaining().isEmpty() && this.k.size() < this.o.a().size()) {
|
||||
suggestionsbuilder.suggest(String.valueOf(','));
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> SuggestionsBuilder a(SuggestionsBuilder suggestionsbuilder, IBlockState<T> iblockstate) {
|
||||
Iterator iterator = iblockstate.d().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
T t0 = (T) iterator.next(); // CraftBukkit - decompile error
|
||||
|
||||
if (t0 instanceof Integer) {
|
||||
suggestionsbuilder.suggest((Integer) t0);
|
||||
} else {
|
||||
suggestionsbuilder.suggest(iblockstate.a(t0));
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder;
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> a(SuggestionsBuilder suggestionsbuilder, String s) {
|
||||
boolean flag = false;
|
||||
|
||||
if (this.q != null && !this.q.getKey().isEmpty()) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
IBlockState<?> iblockstate = block.getStates().a(s);
|
||||
|
||||
if (iblockstate != null) {
|
||||
a(suggestionsbuilder, iblockstate);
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
Iterator iterator1 = block.getStates().d().iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
IBlockState<?> iblockstate1 = (IBlockState) iterator1.next();
|
||||
|
||||
if (!this.l.containsKey(iblockstate1.a())) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
suggestionsbuilder.suggest(String.valueOf(','));
|
||||
}
|
||||
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> i(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
Tag<Block> tag = TagsBlock.a().a(this.q);
|
||||
|
||||
if (tag != null) {
|
||||
boolean flag = false;
|
||||
boolean flag1 = false;
|
||||
Iterator iterator = tag.a().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Block block = (Block) iterator.next();
|
||||
|
||||
flag |= !block.getStates().d().isEmpty();
|
||||
flag1 |= block.isTileEntity();
|
||||
if (flag && flag1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
suggestionsbuilder.suggest(String.valueOf('['));
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
suggestionsbuilder.suggest(String.valueOf('{'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.k(suggestionsbuilder);
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> j(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (suggestionsbuilder.getRemaining().isEmpty()) {
|
||||
if (!this.o.getBlock().getStates().d().isEmpty()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('['));
|
||||
}
|
||||
|
||||
if (this.o.getBlock().isTileEntity()) {
|
||||
suggestionsbuilder.suggest(String.valueOf('{'));
|
||||
}
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> k(SuggestionsBuilder suggestionsbuilder) {
|
||||
return ICompletionProvider.a((Iterable) TagsBlock.a().a(), suggestionsbuilder.createOffset(this.r).add(suggestionsbuilder));
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> l(SuggestionsBuilder suggestionsbuilder) {
|
||||
if (this.j) {
|
||||
ICompletionProvider.a((Iterable) TagsBlock.a().a(), suggestionsbuilder, String.valueOf('#'));
|
||||
}
|
||||
|
||||
ICompletionProvider.a((Iterable) IRegistry.BLOCK.keySet(), suggestionsbuilder);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
public void e() throws CommandSyntaxException {
|
||||
int i = this.i.getCursor();
|
||||
|
||||
this.m = MinecraftKey.a(this.i);
|
||||
if (IRegistry.BLOCK.c(this.m)) {
|
||||
Block block = (Block) IRegistry.BLOCK.getOrDefault(this.m);
|
||||
|
||||
this.n = block.getStates();
|
||||
this.o = block.getBlockData();
|
||||
} else {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.b.createWithContext(this.i, this.m.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void f() throws CommandSyntaxException {
|
||||
if (!this.j) {
|
||||
throw ArgumentBlock.a.create();
|
||||
} else {
|
||||
this.s = this::k;
|
||||
this.i.expect('#');
|
||||
this.r = this.i.getCursor();
|
||||
this.q = MinecraftKey.a(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void g() throws CommandSyntaxException {
|
||||
this.i.skip();
|
||||
this.s = this::b;
|
||||
this.i.skipWhitespace();
|
||||
|
||||
while (true) {
|
||||
if (this.i.canRead() && this.i.peek() != ']') {
|
||||
this.i.skipWhitespace();
|
||||
int i = this.i.getCursor();
|
||||
String s = this.i.readString();
|
||||
IBlockState<?> iblockstate = this.n.a(s);
|
||||
|
||||
if (iblockstate == null) {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.c.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
if (this.k.containsKey(iblockstate)) {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.d.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skipWhitespace();
|
||||
this.s = this::g;
|
||||
if (!this.i.canRead() || this.i.peek() != '=') {
|
||||
throw ArgumentBlock.f.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skip();
|
||||
this.i.skipWhitespace();
|
||||
this.s = (suggestionsbuilder) -> {
|
||||
return a(suggestionsbuilder, iblockstate).buildFuture();
|
||||
};
|
||||
int j = this.i.getCursor();
|
||||
|
||||
this.a(iblockstate, this.i.readString(), j);
|
||||
this.s = this::h;
|
||||
this.i.skipWhitespace();
|
||||
if (!this.i.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.i.peek() == ',') {
|
||||
this.i.skip();
|
||||
this.s = this::d;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.i.peek() != ']') {
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.i.canRead()) {
|
||||
this.i.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void h() throws CommandSyntaxException {
|
||||
this.i.skip();
|
||||
this.s = this::c;
|
||||
int i = -1;
|
||||
|
||||
this.i.skipWhitespace();
|
||||
|
||||
while (true) {
|
||||
if (this.i.canRead() && this.i.peek() != ']') {
|
||||
this.i.skipWhitespace();
|
||||
int j = this.i.getCursor();
|
||||
String s = this.i.readString();
|
||||
|
||||
if (this.l.containsKey(s)) {
|
||||
this.i.setCursor(j);
|
||||
throw ArgumentBlock.d.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skipWhitespace();
|
||||
if (!this.i.canRead() || this.i.peek() != '=') {
|
||||
this.i.setCursor(j);
|
||||
throw ArgumentBlock.f.createWithContext(this.i, this.m.toString(), s);
|
||||
}
|
||||
|
||||
this.i.skip();
|
||||
this.i.skipWhitespace();
|
||||
this.s = (suggestionsbuilder) -> {
|
||||
return this.a(suggestionsbuilder, s);
|
||||
};
|
||||
i = this.i.getCursor();
|
||||
String s1 = this.i.readString();
|
||||
|
||||
this.l.put(s, s1);
|
||||
this.i.skipWhitespace();
|
||||
if (!this.i.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i = -1;
|
||||
if (this.i.peek() == ',') {
|
||||
this.i.skip();
|
||||
this.s = this::e;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.i.peek() != ']') {
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.i.canRead()) {
|
||||
this.i.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
this.i.setCursor(i);
|
||||
}
|
||||
|
||||
throw ArgumentBlock.g.createWithContext(this.i);
|
||||
}
|
||||
}
|
||||
|
||||
public void i() throws CommandSyntaxException {
|
||||
this.p = (new MojangsonParser(this.i)).f();
|
||||
}
|
||||
|
||||
private <T extends Comparable<T>> void a(IBlockState<T> iblockstate, String s, int i) throws CommandSyntaxException {
|
||||
Optional<T> optional = iblockstate.b(s);
|
||||
|
||||
if (optional.isPresent()) {
|
||||
this.o = (IBlockData) this.o.set(iblockstate, (T) optional.get()); // CraftBukkit - decompile error
|
||||
this.k.put(iblockstate, optional.get());
|
||||
} else {
|
||||
this.i.setCursor(i);
|
||||
throw ArgumentBlock.e.createWithContext(this.i, this.m.toString(), iblockstate.a(), s);
|
||||
}
|
||||
}
|
||||
|
||||
public static String a(IBlockData iblockdata, @Nullable NBTTagCompound nbttagcompound) {
|
||||
StringBuilder stringbuilder = new StringBuilder(IRegistry.BLOCK.getKey(iblockdata.getBlock()).toString());
|
||||
|
||||
if (!iblockdata.a().isEmpty()) {
|
||||
stringbuilder.append('[');
|
||||
boolean flag = false;
|
||||
|
||||
for (UnmodifiableIterator unmodifiableiterator = iblockdata.getStateMap().entrySet().iterator(); unmodifiableiterator.hasNext(); flag = true) {
|
||||
Entry<IBlockState<?>, Comparable<?>> entry = (Entry) unmodifiableiterator.next();
|
||||
|
||||
if (flag) {
|
||||
stringbuilder.append(',');
|
||||
}
|
||||
|
||||
a(stringbuilder, (IBlockState) entry.getKey(), (Comparable) entry.getValue());
|
||||
}
|
||||
|
||||
stringbuilder.append(']');
|
||||
}
|
||||
|
||||
if (nbttagcompound != null) {
|
||||
stringbuilder.append(nbttagcompound);
|
||||
}
|
||||
|
||||
return stringbuilder.toString();
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> void a(StringBuilder stringbuilder, IBlockState<T> iblockstate, Comparable<?> comparable) {
|
||||
stringbuilder.append(iblockstate.a());
|
||||
stringbuilder.append('=');
|
||||
stringbuilder.append(iblockstate.a((T) comparable)); // CraftBukkit - decompile error
|
||||
}
|
||||
|
||||
public CompletableFuture<Suggestions> a(SuggestionsBuilder suggestionsbuilder) {
|
||||
return (CompletableFuture) this.s.apply(suggestionsbuilder.createOffset(this.i.getCursor()));
|
||||
}
|
||||
|
||||
public Map<String, String> j() {
|
||||
return this.l;
|
||||
}
|
||||
}
|
||||
171
src/main/java/net/minecraft/server/ArgumentEntity.java
Normal file
171
src/main/java/net/minecraft/server/ArgumentEntity.java
Normal file
@@ -0,0 +1,171 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class ArgumentEntity implements ArgumentType<EntitySelector> {
|
||||
|
||||
private static final Collection<String> g = Arrays.asList("Player", "0123", "@e", "@e[type=foo]", "dd12be42-52a9-4a91-a8a1-11c01849e498");
|
||||
public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.entity.toomany", new Object[0]));
|
||||
public static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("argument.player.toomany", new Object[0]));
|
||||
public static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("argument.player.entities", new Object[0]));
|
||||
public static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("argument.entity.notfound.entity", new Object[0]));
|
||||
public static final SimpleCommandExceptionType e = new SimpleCommandExceptionType(new ChatMessage("argument.entity.notfound.player", new Object[0]));
|
||||
public static final SimpleCommandExceptionType f = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.not_allowed", new Object[0]));
|
||||
private final boolean h;
|
||||
private final boolean i;
|
||||
|
||||
protected ArgumentEntity(boolean flag, boolean flag1) {
|
||||
this.h = flag;
|
||||
this.i = flag1;
|
||||
}
|
||||
|
||||
public static ArgumentEntity a() {
|
||||
return new ArgumentEntity(true, false);
|
||||
}
|
||||
|
||||
public static Entity a(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).a((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static ArgumentEntity b() {
|
||||
return new ArgumentEntity(false, false);
|
||||
}
|
||||
|
||||
public static Collection<? extends Entity> b(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> collection = c(commandcontext, s);
|
||||
|
||||
if (collection.isEmpty()) {
|
||||
throw ArgumentEntity.d.create();
|
||||
} else {
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<? extends Entity> c(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).b((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static Collection<EntityPlayer> d(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).d((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static ArgumentEntity c() {
|
||||
return new ArgumentEntity(true, true);
|
||||
}
|
||||
|
||||
public static EntityPlayer e(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
return ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).c((CommandListenerWrapper) commandcontext.getSource());
|
||||
}
|
||||
|
||||
public static ArgumentEntity d() {
|
||||
return new ArgumentEntity(false, true);
|
||||
}
|
||||
|
||||
public static Collection<EntityPlayer> f(CommandContext<CommandListenerWrapper> commandcontext, String s) throws CommandSyntaxException {
|
||||
List<EntityPlayer> list = ((EntitySelector) commandcontext.getArgument(s, EntitySelector.class)).d((CommandListenerWrapper) commandcontext.getSource());
|
||||
|
||||
if (list.isEmpty()) {
|
||||
throw ArgumentEntity.e.create();
|
||||
} else {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public EntitySelector parse(StringReader stringreader) throws CommandSyntaxException {
|
||||
// CraftBukkit start
|
||||
return parse(stringreader, false);
|
||||
}
|
||||
|
||||
public EntitySelector parse(StringReader stringreader, boolean overridePermissions) throws CommandSyntaxException {
|
||||
// CraftBukkit end
|
||||
boolean flag = false;
|
||||
ArgumentParserSelector argumentparserselector = new ArgumentParserSelector(stringreader);
|
||||
EntitySelector entityselector = argumentparserselector.s(overridePermissions); // CraftBukkit
|
||||
|
||||
if (entityselector.a() > 1 && this.h) {
|
||||
if (this.i) {
|
||||
stringreader.setCursor(0);
|
||||
throw ArgumentEntity.b.createWithContext(stringreader);
|
||||
} else {
|
||||
stringreader.setCursor(0);
|
||||
throw ArgumentEntity.a.createWithContext(stringreader);
|
||||
}
|
||||
} else if (entityselector.b() && this.i && !entityselector.c()) {
|
||||
stringreader.setCursor(0);
|
||||
throw ArgumentEntity.c.createWithContext(stringreader);
|
||||
} else {
|
||||
return entityselector;
|
||||
}
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> commandcontext, SuggestionsBuilder suggestionsbuilder) {
|
||||
if (commandcontext.getSource() instanceof ICompletionProvider) {
|
||||
StringReader stringreader = new StringReader(suggestionsbuilder.getInput());
|
||||
|
||||
stringreader.setCursor(suggestionsbuilder.getStart());
|
||||
ICompletionProvider icompletionprovider = (ICompletionProvider) commandcontext.getSource();
|
||||
ArgumentParserSelector argumentparserselector = new ArgumentParserSelector(stringreader, icompletionprovider.hasPermission(2));
|
||||
|
||||
try {
|
||||
argumentparserselector.s();
|
||||
} catch (CommandSyntaxException commandsyntaxexception) {
|
||||
;
|
||||
}
|
||||
|
||||
return argumentparserselector.a(suggestionsbuilder, (suggestionsbuilder1) -> {
|
||||
Collection<String> collection = icompletionprovider.l();
|
||||
Iterable<String> iterable = this.i ? collection : Iterables.concat(collection, icompletionprovider.p());
|
||||
|
||||
ICompletionProvider.b((Iterable) iterable, suggestionsbuilder1);
|
||||
});
|
||||
} else {
|
||||
return Suggestions.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getExamples() {
|
||||
return ArgumentEntity.g;
|
||||
}
|
||||
|
||||
public static class a implements ArgumentSerializer<ArgumentEntity> {
|
||||
|
||||
public a() {}
|
||||
|
||||
public void a(ArgumentEntity argumententity, PacketDataSerializer packetdataserializer) {
|
||||
byte b0 = 0;
|
||||
|
||||
if (argumententity.h) {
|
||||
b0 = (byte) (b0 | 1);
|
||||
}
|
||||
|
||||
if (argumententity.i) {
|
||||
b0 = (byte) (b0 | 2);
|
||||
}
|
||||
|
||||
packetdataserializer.writeByte(b0);
|
||||
}
|
||||
|
||||
public ArgumentEntity b(PacketDataSerializer packetdataserializer) {
|
||||
byte b0 = packetdataserializer.readByte();
|
||||
|
||||
return new ArgumentEntity((b0 & 1) != 0, (b0 & 2) != 0);
|
||||
}
|
||||
|
||||
public void a(ArgumentEntity argumententity, JsonObject jsonobject) {
|
||||
jsonobject.addProperty("amount", argumententity.h ? "single" : "multiple");
|
||||
jsonobject.addProperty("type", argumententity.i ? "players" : "entities");
|
||||
}
|
||||
}
|
||||
}
|
||||
630
src/main/java/net/minecraft/server/ArgumentParserSelector.java
Normal file
630
src/main/java/net/minecraft/server/ArgumentParserSelector.java
Normal file
@@ -0,0 +1,630 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.primitives.Doubles;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ArgumentParserSelector {
|
||||
|
||||
public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("argument.entity.invalid", new Object[0]));
|
||||
public static final DynamicCommandExceptionType b = new DynamicCommandExceptionType((object) -> {
|
||||
return new ChatMessage("argument.entity.selector.unknown", new Object[] { object});
|
||||
});
|
||||
public static final SimpleCommandExceptionType c = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.not_allowed", new Object[0]));
|
||||
public static final SimpleCommandExceptionType d = new SimpleCommandExceptionType(new ChatMessage("argument.entity.selector.missing", new Object[0]));
|
||||
public static final SimpleCommandExceptionType e = new SimpleCommandExceptionType(new ChatMessage("argument.entity.options.unterminated", new Object[0]));
|
||||
public static final DynamicCommandExceptionType f = new DynamicCommandExceptionType((object) -> {
|
||||
return new ChatMessage("argument.entity.options.valueless", new Object[] { object});
|
||||
});
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> g = (vec3d, list) -> {
|
||||
};
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> h = (vec3d, list) -> {
|
||||
list.sort((entity, entity1) -> {
|
||||
return Doubles.compare(entity.a(vec3d), entity1.a(vec3d));
|
||||
});
|
||||
};
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> i = (vec3d, list) -> {
|
||||
list.sort((entity, entity1) -> {
|
||||
return Doubles.compare(entity1.a(vec3d), entity.a(vec3d));
|
||||
});
|
||||
};
|
||||
public static final BiConsumer<Vec3D, List<? extends Entity>> j = (vec3d, list) -> {
|
||||
Collections.shuffle(list);
|
||||
};
|
||||
public static final BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> k = (suggestionsbuilder, consumer) -> {
|
||||
return suggestionsbuilder.buildFuture();
|
||||
};
|
||||
private final StringReader l;
|
||||
private final boolean m;
|
||||
private int n;
|
||||
private boolean o;
|
||||
private boolean p;
|
||||
private CriterionConditionValue.c q;
|
||||
private CriterionConditionValue.d r;
|
||||
@Nullable
|
||||
private Double s;
|
||||
@Nullable
|
||||
private Double t;
|
||||
@Nullable
|
||||
private Double u;
|
||||
@Nullable
|
||||
private Double v;
|
||||
@Nullable
|
||||
private Double w;
|
||||
@Nullable
|
||||
private Double x;
|
||||
private CriterionConditionRange y;
|
||||
private CriterionConditionRange z;
|
||||
private Predicate<Entity> A;
|
||||
private BiConsumer<Vec3D, List<? extends Entity>> B;
|
||||
private boolean C;
|
||||
@Nullable
|
||||
private String D;
|
||||
private int E;
|
||||
@Nullable
|
||||
private UUID F;
|
||||
private BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> G;
|
||||
private boolean H;
|
||||
private boolean I;
|
||||
private boolean J;
|
||||
private boolean K;
|
||||
private boolean L;
|
||||
private boolean M;
|
||||
private boolean N;
|
||||
private boolean O;
|
||||
private Class<? extends Entity> P;
|
||||
private boolean Q;
|
||||
private boolean R;
|
||||
private boolean S;
|
||||
private boolean T;
|
||||
|
||||
public ArgumentParserSelector(StringReader stringreader) {
|
||||
this(stringreader, true);
|
||||
}
|
||||
|
||||
// CraftBukkit start - decompile error
|
||||
private static final CriterionConditionValue.c DEFAULT_q;
|
||||
private static final CriterionConditionValue.d DEFAULT_r;
|
||||
|
||||
static {
|
||||
try {
|
||||
DEFAULT_q = (CriterionConditionValue.c) Class.forName("net.minecraft.server.CriterionConditionValue$c").getDeclaredField("e").get(null);
|
||||
DEFAULT_r = (CriterionConditionValue.d) Class.forName("net.minecraft.server.CriterionConditionValue$d").getDeclaredField("e").get(null);
|
||||
} catch (Exception ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ArgumentParserSelector(StringReader stringreader, boolean flag) {
|
||||
this.q = DEFAULT_q;
|
||||
this.r = DEFAULT_r;
|
||||
// CraftBukkit end
|
||||
this.y = CriterionConditionRange.a;
|
||||
this.z = CriterionConditionRange.a;
|
||||
this.A = (entity) -> {
|
||||
return true;
|
||||
};
|
||||
this.B = ArgumentParserSelector.g;
|
||||
this.G = ArgumentParserSelector.k;
|
||||
this.l = stringreader;
|
||||
this.m = flag;
|
||||
}
|
||||
|
||||
public EntitySelector a() {
|
||||
AxisAlignedBB axisalignedbb;
|
||||
|
||||
if (this.v == null && this.w == null && this.x == null) {
|
||||
if (this.q.b() != null) {
|
||||
float f = (Float) this.q.b();
|
||||
|
||||
axisalignedbb = new AxisAlignedBB((double) (-f), (double) (-f), (double) (-f), (double) (f + 1.0F), (double) (f + 1.0F), (double) (f + 1.0F));
|
||||
} else {
|
||||
axisalignedbb = null;
|
||||
}
|
||||
} else {
|
||||
axisalignedbb = this.a(this.v == null ? 0.0D : this.v, this.w == null ? 0.0D : this.w, this.x == null ? 0.0D : this.x);
|
||||
}
|
||||
|
||||
Function<Vec3D, Vec3D> function; // CraftBukkit - decompile error
|
||||
|
||||
if (this.s == null && this.t == null && this.u == null) {
|
||||
function = (vec3d) -> {
|
||||
return vec3d;
|
||||
};
|
||||
} else {
|
||||
function = (vec3d) -> {
|
||||
return new Vec3D(this.s == null ? vec3d.x : this.s, this.t == null ? vec3d.y : this.t, this.u == null ? vec3d.z : this.u);
|
||||
};
|
||||
}
|
||||
|
||||
return new EntitySelector(this.n, this.o, this.p, this.A, this.q, function, axisalignedbb, this.B, this.C, this.D, this.F, this.P == null ? Entity.class : this.P, this.T);
|
||||
}
|
||||
|
||||
private AxisAlignedBB a(double d0, double d1, double d2) {
|
||||
boolean flag = d0 < 0.0D;
|
||||
boolean flag1 = d1 < 0.0D;
|
||||
boolean flag2 = d2 < 0.0D;
|
||||
double d3 = flag ? d0 : 0.0D;
|
||||
double d4 = flag1 ? d1 : 0.0D;
|
||||
double d5 = flag2 ? d2 : 0.0D;
|
||||
double d6 = (flag ? 0.0D : d0) + 1.0D;
|
||||
double d7 = (flag1 ? 0.0D : d1) + 1.0D;
|
||||
double d8 = (flag2 ? 0.0D : d2) + 1.0D;
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
private void I() {
|
||||
if (this.y != CriterionConditionRange.a) {
|
||||
this.A = this.A.and(this.a(this.y, (entity) -> {
|
||||
return (double) entity.pitch;
|
||||
}));
|
||||
}
|
||||
|
||||
if (this.z != CriterionConditionRange.a) {
|
||||
this.A = this.A.and(this.a(this.z, (entity) -> {
|
||||
return (double) entity.yaw;
|
||||
}));
|
||||
}
|
||||
|
||||
if (!this.r.c()) {
|
||||
this.A = this.A.and((entity) -> {
|
||||
return !(entity instanceof EntityPlayer) ? false : this.r.d(((EntityPlayer) entity).expLevel);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Predicate<Entity> a(CriterionConditionRange criterionconditionrange, ToDoubleFunction<Entity> todoublefunction) {
|
||||
double d0 = (double) MathHelper.g(criterionconditionrange.a() == null ? 0.0F : criterionconditionrange.a());
|
||||
double d1 = (double) MathHelper.g(criterionconditionrange.b() == null ? 359.0F : criterionconditionrange.b());
|
||||
|
||||
return (entity) -> {
|
||||
double d2 = MathHelper.g(todoublefunction.applyAsDouble(entity));
|
||||
|
||||
return d0 > d1 ? d2 >= d0 || d2 <= d1 : d2 >= d0 && d2 <= d1;
|
||||
};
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
protected void b(boolean overridePermissions) throws CommandSyntaxException {
|
||||
this.T = !overridePermissions;
|
||||
// CraftBukkit end
|
||||
this.G = this::d;
|
||||
if (!this.l.canRead()) {
|
||||
throw ArgumentParserSelector.d.createWithContext(this.l);
|
||||
} else {
|
||||
int i = this.l.getCursor();
|
||||
char c0 = this.l.read();
|
||||
|
||||
if (c0 == 'p') {
|
||||
this.n = 1;
|
||||
this.o = false;
|
||||
this.B = ArgumentParserSelector.h;
|
||||
this.a(EntityPlayer.class);
|
||||
} else if (c0 == 'a') {
|
||||
this.n = Integer.MAX_VALUE;
|
||||
this.o = false;
|
||||
this.B = ArgumentParserSelector.g;
|
||||
this.a(EntityPlayer.class);
|
||||
} else if (c0 == 'r') {
|
||||
this.n = 1;
|
||||
this.o = false;
|
||||
this.B = ArgumentParserSelector.j;
|
||||
this.a(EntityPlayer.class);
|
||||
} else if (c0 == 's') {
|
||||
this.n = 1;
|
||||
this.o = true;
|
||||
this.C = true;
|
||||
} else {
|
||||
if (c0 != 'e') {
|
||||
this.l.setCursor(i);
|
||||
throw ArgumentParserSelector.b.createWithContext(this.l, '@' + String.valueOf(c0));
|
||||
}
|
||||
|
||||
this.n = Integer.MAX_VALUE;
|
||||
this.o = true;
|
||||
this.B = ArgumentParserSelector.g;
|
||||
this.A = Entity::isAlive;
|
||||
}
|
||||
|
||||
this.G = this::e;
|
||||
if (this.l.canRead() && this.l.peek() == '[') {
|
||||
this.l.skip();
|
||||
this.G = this::f;
|
||||
this.d();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void c() throws CommandSyntaxException {
|
||||
if (this.l.canRead()) {
|
||||
this.G = this::c;
|
||||
}
|
||||
|
||||
int i = this.l.getCursor();
|
||||
String s = this.l.readString();
|
||||
|
||||
try {
|
||||
this.F = UUID.fromString(s);
|
||||
this.o = true;
|
||||
} catch (IllegalArgumentException illegalargumentexception) {
|
||||
if (s.isEmpty() || s.length() > 16) {
|
||||
this.l.setCursor(i);
|
||||
throw ArgumentParserSelector.a.createWithContext(this.l);
|
||||
}
|
||||
|
||||
this.o = false;
|
||||
this.D = s;
|
||||
}
|
||||
|
||||
this.n = 1;
|
||||
}
|
||||
|
||||
protected void d() throws CommandSyntaxException {
|
||||
this.G = this::g;
|
||||
this.l.skipWhitespace();
|
||||
|
||||
while (true) {
|
||||
if (this.l.canRead() && this.l.peek() != ']') {
|
||||
this.l.skipWhitespace();
|
||||
int i = this.l.getCursor();
|
||||
String s = this.l.readString();
|
||||
PlayerSelector.a playerselector_a = PlayerSelector.a(this, s, i);
|
||||
|
||||
this.l.skipWhitespace();
|
||||
if (!this.l.canRead() || this.l.peek() != '=') {
|
||||
this.l.setCursor(i);
|
||||
throw ArgumentParserSelector.f.createWithContext(this.l, s);
|
||||
}
|
||||
|
||||
this.l.skip();
|
||||
this.l.skipWhitespace();
|
||||
this.G = ArgumentParserSelector.k;
|
||||
playerselector_a.handle(this);
|
||||
this.l.skipWhitespace();
|
||||
this.G = this::h;
|
||||
if (!this.l.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.l.peek() == ',') {
|
||||
this.l.skip();
|
||||
this.G = this::g;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.l.peek() != ']') {
|
||||
throw ArgumentParserSelector.e.createWithContext(this.l);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.l.canRead()) {
|
||||
this.l.skip();
|
||||
this.G = ArgumentParserSelector.k;
|
||||
return;
|
||||
}
|
||||
|
||||
throw ArgumentParserSelector.e.createWithContext(this.l);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean e() {
|
||||
this.l.skipWhitespace();
|
||||
if (this.l.canRead() && this.l.peek() == '!') {
|
||||
this.l.skip();
|
||||
this.l.skipWhitespace();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public StringReader f() {
|
||||
return this.l;
|
||||
}
|
||||
|
||||
public void a(Predicate<Entity> predicate) {
|
||||
this.A = this.A.and(predicate);
|
||||
}
|
||||
|
||||
public void g() {
|
||||
this.p = true;
|
||||
}
|
||||
|
||||
public CriterionConditionValue.c h() {
|
||||
return this.q;
|
||||
}
|
||||
|
||||
public void a(CriterionConditionValue.c criterionconditionvalue_c) {
|
||||
this.q = criterionconditionvalue_c;
|
||||
}
|
||||
|
||||
public CriterionConditionValue.d i() {
|
||||
return this.r;
|
||||
}
|
||||
|
||||
public void a(CriterionConditionValue.d criterionconditionvalue_d) {
|
||||
this.r = criterionconditionvalue_d;
|
||||
}
|
||||
|
||||
public CriterionConditionRange j() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public void a(CriterionConditionRange criterionconditionrange) {
|
||||
this.y = criterionconditionrange;
|
||||
}
|
||||
|
||||
public CriterionConditionRange k() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public void b(CriterionConditionRange criterionconditionrange) {
|
||||
this.z = criterionconditionrange;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double l() {
|
||||
return this.s;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double m() {
|
||||
return this.t;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double n() {
|
||||
return this.u;
|
||||
}
|
||||
|
||||
public void a(double d0) {
|
||||
this.s = d0;
|
||||
}
|
||||
|
||||
public void b(double d0) {
|
||||
this.t = d0;
|
||||
}
|
||||
|
||||
public void c(double d0) {
|
||||
this.u = d0;
|
||||
}
|
||||
|
||||
public void d(double d0) {
|
||||
this.v = d0;
|
||||
}
|
||||
|
||||
public void e(double d0) {
|
||||
this.w = d0;
|
||||
}
|
||||
|
||||
public void f(double d0) {
|
||||
this.x = d0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double o() {
|
||||
return this.v;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double p() {
|
||||
return this.w;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double q() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public void a(int i) {
|
||||
this.n = i;
|
||||
}
|
||||
|
||||
public void a(boolean flag) {
|
||||
this.o = flag;
|
||||
}
|
||||
|
||||
public void a(BiConsumer<Vec3D, List<? extends Entity>> biconsumer) {
|
||||
this.B = biconsumer;
|
||||
}
|
||||
|
||||
public EntitySelector s() throws CommandSyntaxException {
|
||||
// CraftBukkit start
|
||||
return s(false);
|
||||
}
|
||||
|
||||
public EntitySelector s(boolean overridePermissions) throws CommandSyntaxException {
|
||||
// CraftBukkit end
|
||||
this.E = this.l.getCursor();
|
||||
this.G = this::b;
|
||||
if (this.l.canRead() && this.l.peek() == '@') {
|
||||
if (!this.m) {
|
||||
throw ArgumentParserSelector.c.createWithContext(this.l);
|
||||
}
|
||||
|
||||
this.l.skip();
|
||||
this.b(overridePermissions); // CraftBukkit
|
||||
} else {
|
||||
this.c();
|
||||
}
|
||||
|
||||
this.I();
|
||||
return this.a();
|
||||
}
|
||||
|
||||
private static void a(SuggestionsBuilder suggestionsbuilder) {
|
||||
suggestionsbuilder.suggest("@p", new ChatMessage("argument.entity.selector.nearestPlayer", new Object[0]));
|
||||
suggestionsbuilder.suggest("@a", new ChatMessage("argument.entity.selector.allPlayers", new Object[0]));
|
||||
suggestionsbuilder.suggest("@r", new ChatMessage("argument.entity.selector.randomPlayer", new Object[0]));
|
||||
suggestionsbuilder.suggest("@s", new ChatMessage("argument.entity.selector.self", new Object[0]));
|
||||
suggestionsbuilder.suggest("@e", new ChatMessage("argument.entity.selector.allEntities", new Object[0]));
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> b(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
consumer.accept(suggestionsbuilder);
|
||||
if (this.m) {
|
||||
a(suggestionsbuilder);
|
||||
}
|
||||
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> c(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
SuggestionsBuilder suggestionsbuilder1 = suggestionsbuilder.createOffset(this.E);
|
||||
|
||||
consumer.accept(suggestionsbuilder1);
|
||||
return suggestionsbuilder.add(suggestionsbuilder1).buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> d(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
SuggestionsBuilder suggestionsbuilder1 = suggestionsbuilder.createOffset(suggestionsbuilder.getStart() - 1);
|
||||
|
||||
a(suggestionsbuilder1);
|
||||
suggestionsbuilder.add(suggestionsbuilder1);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> e(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
suggestionsbuilder.suggest(String.valueOf('['));
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> f(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
PlayerSelector.a(this, suggestionsbuilder);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> g(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
PlayerSelector.a(this, suggestionsbuilder);
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
private CompletableFuture<Suggestions> h(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
suggestionsbuilder.suggest(String.valueOf(','));
|
||||
suggestionsbuilder.suggest(String.valueOf(']'));
|
||||
return suggestionsbuilder.buildFuture();
|
||||
}
|
||||
|
||||
public boolean t() {
|
||||
return this.C;
|
||||
}
|
||||
|
||||
public void a(BiFunction<SuggestionsBuilder, Consumer<SuggestionsBuilder>, CompletableFuture<Suggestions>> bifunction) {
|
||||
this.G = bifunction;
|
||||
}
|
||||
|
||||
public CompletableFuture<Suggestions> a(SuggestionsBuilder suggestionsbuilder, Consumer<SuggestionsBuilder> consumer) {
|
||||
return (CompletableFuture) this.G.apply(suggestionsbuilder.createOffset(this.l.getCursor()), consumer);
|
||||
}
|
||||
|
||||
public boolean u() {
|
||||
return this.H;
|
||||
}
|
||||
|
||||
public void c(boolean flag) {
|
||||
this.H = flag;
|
||||
}
|
||||
|
||||
public boolean v() {
|
||||
return this.I;
|
||||
}
|
||||
|
||||
public void d(boolean flag) {
|
||||
this.I = flag;
|
||||
}
|
||||
|
||||
public boolean w() {
|
||||
return this.J;
|
||||
}
|
||||
|
||||
public void e(boolean flag) {
|
||||
this.J = flag;
|
||||
}
|
||||
|
||||
public boolean x() {
|
||||
return this.K;
|
||||
}
|
||||
|
||||
public void f(boolean flag) {
|
||||
this.K = flag;
|
||||
}
|
||||
|
||||
public boolean y() {
|
||||
return this.L;
|
||||
}
|
||||
|
||||
public void g(boolean flag) {
|
||||
this.L = flag;
|
||||
}
|
||||
|
||||
public boolean z() {
|
||||
return this.M;
|
||||
}
|
||||
|
||||
public void h(boolean flag) {
|
||||
this.M = flag;
|
||||
}
|
||||
|
||||
public boolean A() {
|
||||
return this.N;
|
||||
}
|
||||
|
||||
public void i(boolean flag) {
|
||||
this.N = flag;
|
||||
}
|
||||
|
||||
public void j(boolean flag) {
|
||||
this.O = flag;
|
||||
}
|
||||
|
||||
public void a(Class<? extends Entity> oclass) {
|
||||
this.P = oclass;
|
||||
}
|
||||
|
||||
public void C() {
|
||||
this.Q = true;
|
||||
}
|
||||
|
||||
public boolean E() {
|
||||
return this.P != null;
|
||||
}
|
||||
|
||||
public boolean F() {
|
||||
return this.Q;
|
||||
}
|
||||
|
||||
public boolean G() {
|
||||
return this.R;
|
||||
}
|
||||
|
||||
public void k(boolean flag) {
|
||||
this.R = flag;
|
||||
}
|
||||
|
||||
public boolean H() {
|
||||
return this.S;
|
||||
}
|
||||
|
||||
public void l(boolean flag) {
|
||||
this.S = flag;
|
||||
}
|
||||
}
|
||||
33
src/main/java/net/minecraft/server/AttributeInstance.java
Normal file
33
src/main/java/net/minecraft/server/AttributeInstance.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface AttributeInstance {
|
||||
|
||||
IAttribute getAttribute();
|
||||
|
||||
double b();
|
||||
|
||||
void setValue(double d0);
|
||||
|
||||
Collection<AttributeModifier> a(int i);
|
||||
|
||||
Collection<AttributeModifier> c();
|
||||
|
||||
boolean a(AttributeModifier attributemodifier);
|
||||
|
||||
@Nullable
|
||||
AttributeModifier a(UUID uuid);
|
||||
|
||||
default void addModifier(AttributeModifier modifier) { b(modifier); } // Paper - OBFHELPER
|
||||
void b(AttributeModifier attributemodifier);
|
||||
|
||||
default void removeModifier(AttributeModifier modifier) { c(modifier); } // Paper - OBFHELPER
|
||||
void c(AttributeModifier attributemodifier);
|
||||
|
||||
void b(UUID uuid);
|
||||
|
||||
double getValue();
|
||||
}
|
||||
39
src/main/java/net/minecraft/server/AttributeRanged.java
Normal file
39
src/main/java/net/minecraft/server/AttributeRanged.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AttributeRanged extends AttributeBase {
|
||||
|
||||
private final double a;
|
||||
public double maximum; // Spigot
|
||||
private String c;
|
||||
|
||||
public AttributeRanged(@Nullable IAttribute iattribute, String s, double d0, double d1, double d2) {
|
||||
super(iattribute, s, d0);
|
||||
this.a = d1;
|
||||
this.maximum = 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 g() {
|
||||
return this.c;
|
||||
}
|
||||
|
||||
public double a(double d0) {
|
||||
if (d0 != d0) return getDefault(); // CraftBukkit
|
||||
|
||||
d0 = MathHelper.a(d0, this.a, this.maximum);
|
||||
return d0;
|
||||
}
|
||||
}
|
||||
303
src/main/java/net/minecraft/server/AxisAlignedBB.java
Normal file
303
src/main/java/net/minecraft/server/AxisAlignedBB.java
Normal file
@@ -0,0 +1,303 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AxisAlignedBB {
|
||||
|
||||
public final double minX; public double getMinX() { return this.minX; } // Paper - OBFHELPER
|
||||
public final double minY; public double getMinY() { return this.minY; } // Paper - OBFHELPER
|
||||
public final double minZ; public double getMinZ() { return this.minZ; } // Paper - OBFHELPER
|
||||
public final double maxX; public double getMaxX() { return this.maxX; } // Paper - OBFHELPER
|
||||
public final double maxY; public double getMaxY() { return this.maxY; } // Paper - OBFHELPER
|
||||
public final double maxZ; public double getMaxZ() { return this.maxZ; } // Paper - OBFHELPER
|
||||
|
||||
public AxisAlignedBB(double d0, double d1, double d2, double d3, double d4, double d5) {
|
||||
this.minX = Math.min(d0, d3);
|
||||
this.minY = Math.min(d1, d4);
|
||||
this.minZ = Math.min(d2, d5);
|
||||
this.maxX = Math.max(d0, d3);
|
||||
this.maxY = Math.max(d1, d4);
|
||||
this.maxZ = Math.max(d2, d5);
|
||||
}
|
||||
|
||||
public AxisAlignedBB(BlockPosition blockposition) {
|
||||
this((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 1), (double) (blockposition.getZ() + 1));
|
||||
}
|
||||
|
||||
public AxisAlignedBB(BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
this((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), (double) blockposition1.getX(), (double) blockposition1.getY(), (double) blockposition1.getZ());
|
||||
}
|
||||
|
||||
public double a(EnumDirection.EnumAxis enumdirection_enumaxis) {
|
||||
return enumdirection_enumaxis.a(this.minX, this.minY, this.minZ);
|
||||
}
|
||||
|
||||
public double b(EnumDirection.EnumAxis enumdirection_enumaxis) {
|
||||
return enumdirection_enumaxis.a(this.maxX, this.maxY, this.maxZ);
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof AxisAlignedBB)) {
|
||||
return false;
|
||||
} else {
|
||||
AxisAlignedBB axisalignedbb = (AxisAlignedBB) object;
|
||||
|
||||
return Double.compare(axisalignedbb.minX, this.minX) != 0 ? false : (Double.compare(axisalignedbb.minY, this.minY) != 0 ? false : (Double.compare(axisalignedbb.minZ, this.minZ) != 0 ? false : (Double.compare(axisalignedbb.maxX, this.maxX) != 0 ? false : (Double.compare(axisalignedbb.maxY, this.maxY) != 0 ? false : Double.compare(axisalignedbb.maxZ, this.maxZ) == 0))));
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
long i = Double.doubleToLongBits(this.minX);
|
||||
int j = (int) (i ^ i >>> 32);
|
||||
|
||||
i = Double.doubleToLongBits(this.minY);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.minZ);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.maxX);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.maxY);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
i = Double.doubleToLongBits(this.maxZ);
|
||||
j = 31 * j + (int) (i ^ i >>> 32);
|
||||
return j;
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(double d0, double d1, double d2) {
|
||||
double d3 = this.minX;
|
||||
double d4 = this.minY;
|
||||
double d5 = this.minZ;
|
||||
double d6 = this.maxX;
|
||||
double d7 = this.maxY;
|
||||
double d8 = this.maxZ;
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d3 -= d0;
|
||||
} else if (d0 > 0.0D) {
|
||||
d6 -= d0;
|
||||
}
|
||||
|
||||
if (d1 < 0.0D) {
|
||||
d4 -= d1;
|
||||
} else if (d1 > 0.0D) {
|
||||
d7 -= d1;
|
||||
}
|
||||
|
||||
if (d2 < 0.0D) {
|
||||
d5 -= d2;
|
||||
} else if (d2 > 0.0D) {
|
||||
d8 -= d2;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
public AxisAlignedBB expand(double x, double y, double z) { return b(x, y, z); } // Paper - OBFHELPER
|
||||
public AxisAlignedBB b(double d0, double d1, double d2) {
|
||||
double d3 = this.minX;
|
||||
double d4 = this.minY;
|
||||
double d5 = this.minZ;
|
||||
double d6 = this.maxX;
|
||||
double d7 = this.maxY;
|
||||
double d8 = this.maxZ;
|
||||
|
||||
if (d0 < 0.0D) {
|
||||
d3 += d0;
|
||||
} else if (d0 > 0.0D) {
|
||||
d6 += d0;
|
||||
}
|
||||
|
||||
if (d1 < 0.0D) {
|
||||
d4 += d1;
|
||||
} else if (d1 > 0.0D) {
|
||||
d7 += d1;
|
||||
}
|
||||
|
||||
if (d2 < 0.0D) {
|
||||
d5 += d2;
|
||||
} else if (d2 > 0.0D) {
|
||||
d8 += d2;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public AxisAlignedBB grow(double d0) {
|
||||
return grow(d0, d0, d0);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public AxisAlignedBB grow(double d0, double d1, double d2) {
|
||||
double d3 = this.minX - d0;
|
||||
double d4 = this.minY - d1;
|
||||
double d5 = this.minZ - d2;
|
||||
double d6 = this.maxX + d0;
|
||||
double d7 = this.maxY + d1;
|
||||
double d8 = this.maxZ + d2;
|
||||
|
||||
return new AxisAlignedBB(d3, d4, d5, d6, d7, d8);
|
||||
}
|
||||
|
||||
public AxisAlignedBB g(double d0) {
|
||||
return this.grow(d0, d0, d0);
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(AxisAlignedBB axisalignedbb) {
|
||||
double d0 = Math.max(this.minX, axisalignedbb.minX);
|
||||
double d1 = Math.max(this.minY, axisalignedbb.minY);
|
||||
double d2 = Math.max(this.minZ, axisalignedbb.minZ);
|
||||
double d3 = Math.min(this.maxX, axisalignedbb.maxX);
|
||||
double d4 = Math.min(this.maxY, axisalignedbb.maxY);
|
||||
double d5 = Math.min(this.maxZ, axisalignedbb.maxZ);
|
||||
|
||||
return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
|
||||
}
|
||||
|
||||
public AxisAlignedBB b(AxisAlignedBB axisalignedbb) {
|
||||
double d0 = Math.min(this.minX, axisalignedbb.minX);
|
||||
double d1 = Math.min(this.minY, axisalignedbb.minY);
|
||||
double d2 = Math.min(this.minZ, axisalignedbb.minZ);
|
||||
double d3 = Math.max(this.maxX, axisalignedbb.maxX);
|
||||
double d4 = Math.max(this.maxY, axisalignedbb.maxY);
|
||||
double d5 = Math.max(this.maxZ, axisalignedbb.maxZ);
|
||||
|
||||
return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
|
||||
}
|
||||
|
||||
public AxisAlignedBB d(double d0, double d1, double d2) {
|
||||
return new AxisAlignedBB(this.minX + d0, this.minY + d1, this.minZ + d2, this.maxX + d0, this.maxY + d1, this.maxZ + d2);
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(BlockPosition blockposition) {
|
||||
return new AxisAlignedBB(this.minX + (double) blockposition.getX(), this.minY + (double) blockposition.getY(), this.minZ + (double) blockposition.getZ(), this.maxX + (double) blockposition.getX(), this.maxY + (double) blockposition.getY(), this.maxZ + (double) blockposition.getZ());
|
||||
}
|
||||
|
||||
public AxisAlignedBB a(Vec3D vec3d) {
|
||||
return this.d(vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
public boolean c(AxisAlignedBB axisalignedbb) {
|
||||
return this.a(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.maxY, axisalignedbb.maxZ);
|
||||
}
|
||||
|
||||
public boolean a(double d0, double d1, double d2, double d3, double d4, double d5) {
|
||||
return this.minX < d3 && this.maxX > d0 && this.minY < d4 && this.maxY > d1 && this.minZ < d5 && this.maxZ > d2;
|
||||
}
|
||||
|
||||
public boolean contains(Vec3D vec3d) { return b(vec3d); } // Paper - OBFHELPER
|
||||
public boolean b(Vec3D vec3d) {
|
||||
return this.e(vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
public boolean e(double d0, double d1, double d2) {
|
||||
return d0 >= this.minX && d0 < this.maxX && d1 >= this.minY && d1 < this.maxY && d2 >= this.minZ && d2 < this.maxZ;
|
||||
}
|
||||
|
||||
public double a() {
|
||||
double d0 = this.maxX - this.minX;
|
||||
double d1 = this.maxY - this.minY;
|
||||
double d2 = this.maxZ - this.minZ;
|
||||
|
||||
return (d0 + d1 + d2) / 3.0D;
|
||||
}
|
||||
|
||||
public AxisAlignedBB f(double d0, double d1, double d2) {
|
||||
return this.grow(-d0, -d1, -d2);
|
||||
}
|
||||
|
||||
public AxisAlignedBB shrink(double d0) {
|
||||
return this.g(-d0);
|
||||
}
|
||||
|
||||
public MovingObjectPosition calculateIntercept(Vec3D vec3d, Vec3D vec3d1) { return b(vec3d, vec3d1); } // Paper - OBFHELPER
|
||||
@Nullable
|
||||
public MovingObjectPosition b(Vec3D vec3d, Vec3D vec3d1) {
|
||||
return this.a(vec3d, vec3d1, (BlockPosition) null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MovingObjectPosition a(Vec3D vec3d, Vec3D vec3d1, @Nullable BlockPosition blockposition) {
|
||||
double[] adouble = new double[] { 1.0D};
|
||||
EnumDirection enumdirection = null;
|
||||
double d0 = vec3d1.x - vec3d.x;
|
||||
double d1 = vec3d1.y - vec3d.y;
|
||||
double d2 = vec3d1.z - vec3d.z;
|
||||
|
||||
enumdirection = a(blockposition == null ? this : this.a(blockposition), vec3d, adouble, enumdirection, d0, d1, d2);
|
||||
if (enumdirection == null) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = adouble[0];
|
||||
|
||||
return new MovingObjectPosition(vec3d.add(d3 * d0, d3 * d1, d3 * d2), enumdirection, blockposition == null ? BlockPosition.ZERO : blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MovingObjectPosition a(Iterable<AxisAlignedBB> iterable, Vec3D vec3d, Vec3D vec3d1, BlockPosition blockposition) {
|
||||
double[] adouble = new double[] { 1.0D};
|
||||
EnumDirection enumdirection = null;
|
||||
double d0 = vec3d1.x - vec3d.x;
|
||||
double d1 = vec3d1.y - vec3d.y;
|
||||
double d2 = vec3d1.z - vec3d.z;
|
||||
|
||||
AxisAlignedBB axisalignedbb;
|
||||
|
||||
for (Iterator iterator = iterable.iterator(); iterator.hasNext(); enumdirection = a(axisalignedbb.a(blockposition), vec3d, adouble, enumdirection, d0, d1, d2)) {
|
||||
axisalignedbb = (AxisAlignedBB) iterator.next();
|
||||
}
|
||||
|
||||
if (enumdirection == null) {
|
||||
return null;
|
||||
} else {
|
||||
double d3 = adouble[0];
|
||||
|
||||
return new MovingObjectPosition(vec3d.add(d3 * d0, d3 * d1, d3 * d2), enumdirection, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EnumDirection a(AxisAlignedBB axisalignedbb, Vec3D vec3d, double[] adouble, @Nullable EnumDirection enumdirection, double d0, double d1, double d2) {
|
||||
if (d0 > 1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d0, d1, d2, axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, EnumDirection.WEST, vec3d.x, vec3d.y, vec3d.z);
|
||||
} else if (d0 < -1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d0, d1, d2, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, EnumDirection.EAST, vec3d.x, vec3d.y, vec3d.z);
|
||||
}
|
||||
|
||||
if (d1 > 1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d1, d2, d0, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, EnumDirection.DOWN, vec3d.y, vec3d.z, vec3d.x);
|
||||
} else if (d1 < -1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d1, d2, d0, axisalignedbb.maxY, axisalignedbb.minZ, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, EnumDirection.UP, vec3d.y, vec3d.z, vec3d.x);
|
||||
}
|
||||
|
||||
if (d2 > 1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d2, d0, d1, axisalignedbb.minZ, axisalignedbb.minX, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, EnumDirection.NORTH, vec3d.z, vec3d.x, vec3d.y);
|
||||
} else if (d2 < -1.0E-7D) {
|
||||
enumdirection = a(adouble, enumdirection, d2, d0, d1, axisalignedbb.maxZ, axisalignedbb.minX, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxY, EnumDirection.SOUTH, vec3d.z, vec3d.x, vec3d.y);
|
||||
}
|
||||
|
||||
return enumdirection;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EnumDirection a(double[] adouble, @Nullable EnumDirection enumdirection, double d0, double d1, double d2, double d3, double d4, double d5, double d6, double d7, EnumDirection enumdirection1, double d8, double d9, double d10) {
|
||||
double d11 = (d3 - d8) / d0;
|
||||
double d12 = d9 + d11 * d1;
|
||||
double d13 = d10 + d11 * d2;
|
||||
|
||||
if (0.0D < d11 && d11 < adouble[0] && d4 - 1.0E-7D < d12 && d12 < d5 + 1.0E-7D && d6 - 1.0E-7D < d13 && d13 < d7 + 1.0E-7D) {
|
||||
adouble[0] = d11;
|
||||
return enumdirection1;
|
||||
} else {
|
||||
return enumdirection;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "box[" + this.minX + ", " + this.minY + ", " + this.minZ + " -> " + this.maxX + ", " + this.maxY + ", " + this.maxZ + "]";
|
||||
}
|
||||
}
|
||||
105
src/main/java/net/minecraft/server/BaseBlockPosition.java
Normal file
105
src/main/java/net/minecraft/server/BaseBlockPosition.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
public class BaseBlockPosition implements Comparable<BaseBlockPosition> {
|
||||
|
||||
public static final BaseBlockPosition ZERO = new BaseBlockPosition(0, 0, 0);
|
||||
// Paper start
|
||||
protected int x;
|
||||
protected int y;
|
||||
protected int z;
|
||||
public boolean isValidLocation() {
|
||||
return x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000 && y >= 0 && y < 256;
|
||||
}
|
||||
public boolean isInvalidYLocation() {
|
||||
return y < 0 || y >= 256;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public BaseBlockPosition(int i, int j, int k) {
|
||||
this.x = i;
|
||||
this.y = j;
|
||||
this.z = k;
|
||||
}
|
||||
|
||||
public BaseBlockPosition(double d0, double d1, double d2) {
|
||||
this(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2));
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof BaseBlockPosition)) {
|
||||
return false;
|
||||
} else {
|
||||
BaseBlockPosition baseblockposition = (BaseBlockPosition) object;
|
||||
|
||||
return this.getX() != baseblockposition.getX() ? false : (this.getY() != baseblockposition.getY() ? false : this.getZ() == baseblockposition.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
|
||||
}
|
||||
|
||||
public int compareTo(BaseBlockPosition baseblockposition) {
|
||||
return this.getY() == baseblockposition.getY() ? (this.getZ() == baseblockposition.getZ() ? this.getX() - baseblockposition.getX() : this.getZ() - baseblockposition.getZ()) : this.getY() - baseblockposition.getY();
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public final int getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return this.z;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public BaseBlockPosition d(BaseBlockPosition baseblockposition) {
|
||||
return new BaseBlockPosition(this.getY() * baseblockposition.getZ() - this.getZ() * baseblockposition.getY(), this.getZ() * baseblockposition.getX() - this.getX() * baseblockposition.getZ(), this.getX() * baseblockposition.getY() - this.getY() * baseblockposition.getX());
|
||||
}
|
||||
|
||||
public double h(int i, int j, int k) {
|
||||
double d0 = (double) (this.getX() - i);
|
||||
double d1 = (double) (this.getY() - j);
|
||||
double d2 = (double) (this.getZ() - k);
|
||||
|
||||
return Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
||||
}
|
||||
|
||||
public double m(BaseBlockPosition baseblockposition) {
|
||||
return this.h(baseblockposition.getX(), baseblockposition.getY(), baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public double distanceSquared(double d0, double d1, double d2) {
|
||||
double d3 = (double) this.getX() - d0;
|
||||
double d4 = (double) this.getY() - d1;
|
||||
double d5 = (double) this.getZ() - d2;
|
||||
|
||||
return d3 * d3 + d4 * d4 + d5 * d5;
|
||||
}
|
||||
|
||||
public double g(double d0, double d1, double d2) {
|
||||
double d3 = (double) this.getX() + 0.5D - d0;
|
||||
double d4 = (double) this.getY() + 0.5D - d1;
|
||||
double d5 = (double) this.getZ() + 0.5D - d2;
|
||||
|
||||
return d3 * d3 + d4 * d4 + d5 * d5;
|
||||
}
|
||||
|
||||
public double n(BaseBlockPosition baseblockposition) {
|
||||
return this.distanceSquared((double) baseblockposition.getX(), (double) baseblockposition.getY(), (double) baseblockposition.getZ());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this).add("x", this.getX()).add("y", this.getY()).add("z", this.getZ()).toString();
|
||||
}
|
||||
}
|
||||
626
src/main/java/net/minecraft/server/BiomeBase.java
Normal file
626
src/main/java/net/minecraft/server/BiomeBase.java
Normal file
@@ -0,0 +1,626 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public abstract class BiomeBase {
|
||||
|
||||
public static final Logger a = LogManager.getLogger();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> b = new WorldGenCaves();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> c = new WorldGenCavesHell();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> d = new WorldGenCanyon();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> e = new WorldGenCanyonOcean();
|
||||
public static final WorldGenCarverAbstract<WorldGenFeatureConfigurationChance> f = new WorldGenCavesOcean();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> g = new WorldGenDecoratorHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> h = new WorldGenDecoratorSkyVisible();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> i = new WorldGenDecoratorHeight32();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> j = new WorldGenDecoratorHeightDouble();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> k = new WorldGenDecoratorHeight64();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorNoiseConfiguration> l = new WorldGenDecoratorNoiseHeight32();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorNoiseConfiguration> m = new WorldGenDecoratorNoiseHeightDouble();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> n = new WorldGenDecoratorEmpty();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> o = new WorldGenDecoratorChance();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> p = new WorldGenDecoratorChanceHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> q = new WorldGenDecoratorChancePass();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> r = new WorldGenDecoratorSkyVisibleBiased();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyExtraChanceConfiguration> s = new WorldGenDecoratorHeightExtraChance();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> t = new WorldGenDecoratorNetherHeight();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> u = new WorldGenDecoratorHeightBiased();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> v = new WorldGenDecoratorHeightBiased2();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorCountConfiguration> w = new WorldGenDecoratorNetherRandomCount();
|
||||
public static final WorldGenDecorator<WorldGenFeatureChanceDecoratorRangeConfiguration> x = new WorldGenDecoratorNetherChance();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyChanceConfiguration> y = new WorldGenFeatureChanceDecorator();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyChanceConfiguration> z = new WorldGenFeatureChanceDecoratorHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorHeightAverageConfiguration> A = new WorldGenDecoratorHeightAverage();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> B = new WorldGenDecoratorSolidTop();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorRangeConfiguration> C = new WorldGenDecoratorSolidTopHeight();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorNoiseConfiguration> D = new WorldGenDecoratorSolidTopNoise();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorCarveMaskConfiguration> E = new WorldGenDecoratorCarveMask();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> F = new WorldGenDecoratorForestRock();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> G = new WorldGenDecoratorNetherFire();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> H = new WorldGenDecoratorNetherMagma();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> I = new WorldGenDecoratorEmerald();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorLakeChanceConfiguration> J = new WorldGenDecoratorLakeLava();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorLakeChanceConfiguration> K = new WorldGenDecoratorLakeWater();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorDungeonConfiguration> L = new WorldGenDecoratorDungeon();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> M = new WorldGenDecoratorRoofedTree();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorChanceConfiguration> N = new WorldGenDecoratorIceburg();
|
||||
public static final WorldGenDecorator<WorldGenDecoratorFrequencyConfiguration> O = new WorldGenDecoratorNetherGlowstone();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> P = new WorldGenDecoratorSpike();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> Q = new WorldGenDecoratorEndIsland();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> R = new WorldGenDecoratorChorusPlant();
|
||||
public static final WorldGenDecorator<WorldGenFeatureDecoratorEmptyConfiguration> S = new WorldGenDecoratorEndGateway();
|
||||
protected static final IBlockData T = Blocks.AIR.getBlockData();
|
||||
protected static final IBlockData U = Blocks.DIRT.getBlockData();
|
||||
protected static final IBlockData V = Blocks.GRASS_BLOCK.getBlockData();
|
||||
protected static final IBlockData W = Blocks.PODZOL.getBlockData();
|
||||
protected static final IBlockData X = Blocks.GRAVEL.getBlockData();
|
||||
protected static final IBlockData Y = Blocks.STONE.getBlockData();
|
||||
protected static final IBlockData Z = Blocks.COARSE_DIRT.getBlockData();
|
||||
protected static final IBlockData aa = Blocks.SAND.getBlockData();
|
||||
protected static final IBlockData ab = Blocks.RED_SAND.getBlockData();
|
||||
protected static final IBlockData ac = Blocks.WHITE_TERRACOTTA.getBlockData();
|
||||
protected static final IBlockData ad = Blocks.MYCELIUM.getBlockData();
|
||||
protected static final IBlockData ae = Blocks.NETHERRACK.getBlockData();
|
||||
protected static final IBlockData af = Blocks.END_STONE.getBlockData();
|
||||
public static final WorldGenSurfaceConfigurationBase ag = new WorldGenSurfaceConfigurationBase(BiomeBase.T, BiomeBase.T, BiomeBase.T);
|
||||
public static final WorldGenSurfaceConfigurationBase ah = new WorldGenSurfaceConfigurationBase(BiomeBase.U, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase ai = new WorldGenSurfaceConfigurationBase(BiomeBase.V, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase aj = new WorldGenSurfaceConfigurationBase(BiomeBase.Y, BiomeBase.Y, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase ak = new WorldGenSurfaceConfigurationBase(BiomeBase.X, BiomeBase.X, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase al = new WorldGenSurfaceConfigurationBase(BiomeBase.Z, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase am = new WorldGenSurfaceConfigurationBase(BiomeBase.W, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase an = new WorldGenSurfaceConfigurationBase(BiomeBase.aa, BiomeBase.aa, BiomeBase.aa);
|
||||
public static final WorldGenSurfaceConfigurationBase ao = new WorldGenSurfaceConfigurationBase(BiomeBase.V, BiomeBase.U, BiomeBase.aa);
|
||||
public static final WorldGenSurfaceConfigurationBase ap = new WorldGenSurfaceConfigurationBase(BiomeBase.aa, BiomeBase.aa, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase aq = new WorldGenSurfaceConfigurationBase(BiomeBase.ab, BiomeBase.ac, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase ar = new WorldGenSurfaceConfigurationBase(BiomeBase.ad, BiomeBase.U, BiomeBase.X);
|
||||
public static final WorldGenSurfaceConfigurationBase as = new WorldGenSurfaceConfigurationBase(BiomeBase.ae, BiomeBase.ae, BiomeBase.ae);
|
||||
public static final WorldGenSurfaceConfigurationBase at = new WorldGenSurfaceConfigurationBase(BiomeBase.af, BiomeBase.af, BiomeBase.af);
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> au = new WorldGenSurfaceDefaultBlock();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> av = new WorldGenSurfaceExtremeHills();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aw = new WorldGenSurfaceSavannaMutated();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> ax = new WorldGenSurfaceExtremeHillMutated();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> ay = new WorldGenSurfaceTaigaMega();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> az = new WorldGenSurfaceSwamp();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aA = new WorldGenSurfaceMesa();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aB = new WorldGenSurfaceMesaForest();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aC = new WorldGenSurfaceMesaBryce();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aD = new WorldGenSurfaceFrozenOcean();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aE = new WorldGenSurfaceNether();
|
||||
public static final WorldGenSurface<WorldGenSurfaceConfigurationBase> aF = new WorldGenSurfaceEmpty();
|
||||
public static final Set<BiomeBase> aG = Sets.newHashSet();
|
||||
public static final RegistryBlockID<BiomeBase> aH = new RegistryBlockID<>();
|
||||
protected static final NoiseGenerator3 aI = new NoiseGenerator3(new Random(1234L), 1);
|
||||
public static final NoiseGenerator3 aJ = new NoiseGenerator3(new Random(2345L), 1);
|
||||
@Nullable
|
||||
protected String aK;
|
||||
protected final float aL;
|
||||
protected final float aM;
|
||||
protected final float aN;
|
||||
protected final float aO;
|
||||
protected final int aP;
|
||||
protected final int aQ;
|
||||
@Nullable
|
||||
protected final String aR;
|
||||
protected final WorldGenSurfaceComposite<?> aS;
|
||||
protected final BiomeBase.Geography aT;
|
||||
protected final BiomeBase.Precipitation aU;
|
||||
protected final Map<WorldGenStage.Features, List<WorldGenCarverWrapper<?>>> aV = Maps.newHashMap();
|
||||
protected final Map<WorldGenStage.Decoration, List<WorldGenFeatureComposite<?, ?>>> aW = Maps.newHashMap();
|
||||
protected final List<WorldGenFeatureCompositeFlower<?>> aX = Lists.newArrayList();
|
||||
protected final Map<StructureGenerator<?>, WorldGenFeatureConfiguration> aY = Maps.newHashMap();
|
||||
private final java.util.EnumMap<EnumCreatureType, List<BiomeBase.BiomeMeta>> aZ = Maps.newEnumMap(EnumCreatureType.class); // Paper
|
||||
|
||||
@Nullable
|
||||
public static BiomeBase a(BiomeBase biomebase) {
|
||||
return (BiomeBase) BiomeBase.aH.fromId(IRegistry.BIOME.a(biomebase)); // Paper - decompile fix
|
||||
}
|
||||
|
||||
public static <C extends WorldGenFeatureConfiguration> WorldGenCarverWrapper<C> a(WorldGenCarver<C> worldgencarver, C c0) {
|
||||
return new WorldGenCarverWrapper<>(worldgencarver, c0);
|
||||
}
|
||||
|
||||
public static <F extends WorldGenFeatureConfiguration, D extends WorldGenFeatureDecoratorConfiguration> WorldGenFeatureComposite<F, D> a(WorldGenerator<F> worldgenerator, F f0, WorldGenDecorator<D> worldgendecorator, D d0) {
|
||||
return new WorldGenFeatureComposite<>(worldgenerator, f0, worldgendecorator, d0);
|
||||
}
|
||||
|
||||
public static <D extends WorldGenFeatureDecoratorConfiguration> WorldGenFeatureCompositeFlower<D> a(WorldGenFlowers worldgenflowers, WorldGenDecorator<D> worldgendecorator, D d0) {
|
||||
return new WorldGenFeatureCompositeFlower<>(worldgenflowers, worldgendecorator, d0);
|
||||
}
|
||||
|
||||
protected BiomeBase(BiomeBase.a biomebase_a) {
|
||||
if (biomebase_a.a != null && biomebase_a.b != null && biomebase_a.c != null && biomebase_a.d != null && biomebase_a.e != null && biomebase_a.f != null && biomebase_a.g != null && biomebase_a.h != null && biomebase_a.i != null) {
|
||||
this.aS = biomebase_a.a;
|
||||
this.aU = biomebase_a.b;
|
||||
this.aT = biomebase_a.c;
|
||||
this.aL = biomebase_a.d;
|
||||
this.aM = biomebase_a.e;
|
||||
this.aN = biomebase_a.f;
|
||||
this.aO = biomebase_a.g;
|
||||
this.aP = biomebase_a.h;
|
||||
this.aQ = biomebase_a.i;
|
||||
this.aR = biomebase_a.j;
|
||||
WorldGenStage.Decoration[] aworldgenstage_decoration = WorldGenStage.Decoration.values();
|
||||
int i = aworldgenstage_decoration.length;
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
WorldGenStage.Decoration worldgenstage_decoration = aworldgenstage_decoration[j];
|
||||
|
||||
this.aW.put(worldgenstage_decoration, Lists.newArrayList());
|
||||
}
|
||||
|
||||
EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values();
|
||||
|
||||
i = aenumcreaturetype.length;
|
||||
|
||||
for (j = 0; j < i; ++j) {
|
||||
EnumCreatureType enumcreaturetype = aenumcreaturetype[j];
|
||||
|
||||
this.aZ.put(enumcreaturetype, new MobList()); // Paper
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("You are missing parameters to build a proper biome for " + this.getClass().getSimpleName() + "\n" + biomebase_a);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a() {
|
||||
this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.f, new WorldGenMineshaftConfiguration(0.004000000189989805D, WorldGenMineshaft.Type.NORMAL), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.e, new WorldGenFeatureVillageConfiguration(0, WorldGenVillagePieces.Material.OAK), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.m, new WorldGenFeatureStrongholdConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.l, new WorldGenFeatureSwampHutConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.i, new WorldGenFeatureDesertPyramidConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.h, new WorldGenFeatureJunglePyramidConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.j, new WorldGenFeatureIglooConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.k, new WorldGenFeatureShipwreckConfiguration(false), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.n, new WorldGenMonumentConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.g, new WorldGenMansionConfiguration(), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.SURFACE_STRUCTURES, a(WorldGenerator.o, new WorldGenFeatureOceanRuinConfiguration(WorldGenFeatureOceanRuin.Temperature.COLD, 0.3F, 0.9F), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
this.a(WorldGenStage.Decoration.UNDERGROUND_STRUCTURES, a(WorldGenerator.r, new WorldGenBuriedTreasureConfiguration(0.01F), BiomeBase.n, WorldGenFeatureDecoratorConfiguration.e));
|
||||
}
|
||||
|
||||
public boolean b() {
|
||||
return this.aR != null;
|
||||
}
|
||||
|
||||
protected void a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta) {
|
||||
((List) this.aZ.get(enumcreaturetype)).add(biomebase_biomemeta);
|
||||
}
|
||||
|
||||
public List<BiomeBase.BiomeMeta> getMobs(EnumCreatureType enumcreaturetype) {
|
||||
return (List) this.aZ.get(enumcreaturetype);
|
||||
}
|
||||
|
||||
public BiomeBase.Precipitation c() {
|
||||
return this.aU;
|
||||
}
|
||||
|
||||
public boolean d() {
|
||||
return this.getHumidity() > 0.85F;
|
||||
}
|
||||
|
||||
public float e() {
|
||||
return 0.1F;
|
||||
}
|
||||
|
||||
public float getAdjustedTemperature(BlockPosition blockposition) {
|
||||
if (blockposition.getY() > 64) {
|
||||
float f = (float) (BiomeBase.aI.a((double) ((float) blockposition.getX() / 8.0F), (double) ((float) blockposition.getZ() / 8.0F)) * 4.0D);
|
||||
|
||||
return this.getTemperature() - (f + (float) blockposition.getY() - 64.0F) * 0.05F / 30.0F;
|
||||
} else {
|
||||
return this.getTemperature();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return this.a(iworldreader, blockposition, true);
|
||||
}
|
||||
|
||||
public boolean a(IWorldReader iworldreader, BlockPosition blockposition, boolean flag) {
|
||||
if (this.getAdjustedTemperature(blockposition) >= 0.15F) {
|
||||
return false;
|
||||
} else {
|
||||
if (blockposition.getY() >= 0 && blockposition.getY() < 256 && iworldreader.getBrightness(EnumSkyBlock.BLOCK, blockposition) < 10) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
Fluid fluid = iworldreader.getFluid(blockposition);
|
||||
|
||||
if (fluid.c() == FluidTypes.WATER && iblockdata.getBlock() instanceof BlockFluids) {
|
||||
if (!flag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean flag1 = iworldreader.B(blockposition.west()) && iworldreader.B(blockposition.east()) && iworldreader.B(blockposition.north()) && iworldreader.B(blockposition.south());
|
||||
|
||||
if (!flag1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean b(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
if (this.getAdjustedTemperature(blockposition) >= 0.15F) {
|
||||
return false;
|
||||
} else {
|
||||
if (blockposition.getY() >= 0 && blockposition.getY() < 256 && iworldreader.getBrightness(EnumSkyBlock.BLOCK, blockposition) < 10) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
|
||||
if (iblockdata.isAir() && Blocks.SNOW.getBlockData().canPlace(iworldreader, blockposition)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(WorldGenStage.Decoration worldgenstage_decoration, WorldGenFeatureComposite<?, ?> worldgenfeaturecomposite) {
|
||||
if (worldgenfeaturecomposite instanceof WorldGenFeatureCompositeFlower) {
|
||||
this.aX.add((WorldGenFeatureCompositeFlower) worldgenfeaturecomposite);
|
||||
}
|
||||
|
||||
((List) this.aW.get(worldgenstage_decoration)).add(worldgenfeaturecomposite);
|
||||
}
|
||||
|
||||
public <C extends WorldGenFeatureConfiguration> void a(WorldGenStage.Features worldgenstage_features, WorldGenCarverWrapper<C> worldgencarverwrapper) {
|
||||
((List) this.aV.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> {
|
||||
return Lists.newArrayList();
|
||||
})).add(worldgencarverwrapper);
|
||||
}
|
||||
|
||||
public List<WorldGenCarverWrapper<?>> a(WorldGenStage.Features worldgenstage_features) {
|
||||
return (List) this.aV.computeIfAbsent(worldgenstage_features, (worldgenstage_features1) -> {
|
||||
return Lists.newArrayList();
|
||||
});
|
||||
}
|
||||
|
||||
public <C extends WorldGenFeatureConfiguration> void a(StructureGenerator<C> structuregenerator, C c0) {
|
||||
this.aY.put(structuregenerator, c0);
|
||||
}
|
||||
|
||||
public <C extends WorldGenFeatureConfiguration> boolean a(StructureGenerator<C> structuregenerator) {
|
||||
return this.aY.containsKey(structuregenerator);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <C extends WorldGenFeatureConfiguration> WorldGenFeatureConfiguration b(StructureGenerator<C> structuregenerator) {
|
||||
return (WorldGenFeatureConfiguration) this.aY.get(structuregenerator);
|
||||
}
|
||||
|
||||
public List<WorldGenFeatureCompositeFlower<?>> f() {
|
||||
return this.aX;
|
||||
}
|
||||
|
||||
public List<WorldGenFeatureComposite<?, ?>> a(WorldGenStage.Decoration worldgenstage_decoration) {
|
||||
return (List) this.aW.get(worldgenstage_decoration);
|
||||
}
|
||||
|
||||
public void a(WorldGenStage.Decoration worldgenstage_decoration, ChunkGenerator<? extends GeneratorSettings> chunkgenerator, GeneratorAccess generatoraccess, long i, SeededRandom seededrandom, BlockPosition blockposition) {
|
||||
int j = 0;
|
||||
|
||||
for (Iterator iterator = ((List) this.aW.get(worldgenstage_decoration)).iterator(); iterator.hasNext(); ++j) {
|
||||
WorldGenFeatureComposite<?, ?> worldgenfeaturecomposite = (WorldGenFeatureComposite) iterator.next();
|
||||
|
||||
seededrandom.b(i, j, worldgenstage_decoration.ordinal());
|
||||
worldgenfeaturecomposite.a(generatoraccess, chunkgenerator, seededrandom, blockposition, WorldGenFeatureConfiguration.e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(Random random, IChunkAccess ichunkaccess, int i, int j, int k, double d0, IBlockData iblockdata, IBlockData iblockdata1, int l, long i1) {
|
||||
this.aS.a(i1);
|
||||
this.aS.a(random, ichunkaccess, this, i, j, k, d0, iblockdata, iblockdata1, l, i1, BiomeBase.ag);
|
||||
}
|
||||
|
||||
public BiomeBase.EnumTemperature g() {
|
||||
return this.aT == BiomeBase.Geography.OCEAN ? BiomeBase.EnumTemperature.OCEAN : ((double) this.getTemperature() < 0.2D ? BiomeBase.EnumTemperature.COLD : ((double) this.getTemperature() < 1.0D ? BiomeBase.EnumTemperature.MEDIUM : BiomeBase.EnumTemperature.WARM));
|
||||
}
|
||||
|
||||
public static BiomeBase getBiome(int i, BiomeBase biomebase) {
|
||||
BiomeBase biomebase1 = (BiomeBase) IRegistry.BIOME.fromId(i);
|
||||
|
||||
return biomebase1 == null ? biomebase : biomebase1;
|
||||
}
|
||||
|
||||
public final float h() {
|
||||
return this.aL;
|
||||
}
|
||||
|
||||
public final float getHumidity() {
|
||||
return this.aO;
|
||||
}
|
||||
|
||||
public String k() {
|
||||
if (this.aK == null) {
|
||||
this.aK = SystemUtils.a("biome", IRegistry.BIOME.getKey(this));
|
||||
}
|
||||
|
||||
return this.aK;
|
||||
}
|
||||
|
||||
public final float l() {
|
||||
return this.aM;
|
||||
}
|
||||
|
||||
public final float getTemperature() {
|
||||
return this.aN;
|
||||
}
|
||||
|
||||
public final int n() {
|
||||
return this.aP;
|
||||
}
|
||||
|
||||
public final int o() {
|
||||
return this.aQ;
|
||||
}
|
||||
|
||||
public final BiomeBase.Geography p() {
|
||||
return this.aT;
|
||||
}
|
||||
|
||||
public WorldGenSurfaceComposite<?> q() {
|
||||
return this.aS;
|
||||
}
|
||||
|
||||
public WorldGenSurfaceConfiguration r() {
|
||||
return this.aS.a();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String s() {
|
||||
return this.aR;
|
||||
}
|
||||
|
||||
public static void t() {
|
||||
a(0, "ocean", new BiomeOcean());
|
||||
a(1, "plains", new BiomePlains());
|
||||
a(2, "desert", new BiomeDesert());
|
||||
a(3, "mountains", new BiomeBigHills());
|
||||
a(4, "forest", new BiomeForest());
|
||||
a(5, "taiga", new BiomeTaiga());
|
||||
a(6, "swamp", new BiomeSwamp());
|
||||
a(7, "river", new BiomeRiver());
|
||||
a(8, "nether", new BiomeHell());
|
||||
a(9, "the_end", new BiomeTheEnd());
|
||||
a(10, "frozen_ocean", new BiomeFrozenOcean());
|
||||
a(11, "frozen_river", new BiomeFrozenRiver());
|
||||
a(12, "snowy_tundra", new BiomeIcePlains());
|
||||
a(13, "snowy_mountains", new BiomeIceMountains());
|
||||
a(14, "mushroom_fields", new BiomeMushrooms());
|
||||
a(15, "mushroom_field_shore", new BiomeMushroomIslandShore());
|
||||
a(16, "beach", new BiomeBeach());
|
||||
a(17, "desert_hills", new BiomeDesertHills());
|
||||
a(18, "wooded_hills", new BiomeForestHills());
|
||||
a(19, "taiga_hills", new BiomeTaigaHills());
|
||||
a(20, "mountain_edge", new BiomeExtremeHillsEdge());
|
||||
a(21, "jungle", new BiomeJungle());
|
||||
a(22, "jungle_hills", new BiomeJungleHills());
|
||||
a(23, "jungle_edge", new BiomeJungleEdge());
|
||||
a(24, "deep_ocean", new BiomeDeepOcean());
|
||||
a(25, "stone_shore", new BiomeStoneBeach());
|
||||
a(26, "snowy_beach", new BiomeColdBeach());
|
||||
a(27, "birch_forest", new BiomeBirchForest());
|
||||
a(28, "birch_forest_hills", new BiomeBirchForestHills());
|
||||
a(29, "dark_forest", new BiomeRoofedForest());
|
||||
a(30, "snowy_taiga", new BiomeColdTaiga());
|
||||
a(31, "snowy_taiga_hills", new BiomeColdTaigaHills());
|
||||
a(32, "giant_tree_taiga", new BiomeMegaTaiga());
|
||||
a(33, "giant_tree_taiga_hills", new BiomeMegaTaigaHills());
|
||||
a(34, "wooded_mountains", new BiomeExtremeHillsWithTrees());
|
||||
a(35, "savanna", new BiomeSavanna());
|
||||
a(36, "savanna_plateau", new BiomeSavannaPlateau());
|
||||
a(37, "badlands", new BiomeMesa());
|
||||
a(38, "wooded_badlands_plateau", new BiomeMesaPlataeu());
|
||||
a(39, "badlands_plateau", new BiomeMesaPlataeuClear());
|
||||
a(40, "small_end_islands", new BiomeTheEndFloatingIslands());
|
||||
a(41, "end_midlands", new BiomeTheEndMediumIsland());
|
||||
a(42, "end_highlands", new BiomeTheEndHighIsland());
|
||||
a(43, "end_barrens", new BiomeTheEndBarrenIsland());
|
||||
a(44, "warm_ocean", new BiomeWarmOcean());
|
||||
a(45, "lukewarm_ocean", new BiomeLukewarmOcean());
|
||||
a(46, "cold_ocean", new BiomeColdOcean());
|
||||
a(47, "deep_warm_ocean", new BiomeWarmDeepOcean());
|
||||
a(48, "deep_lukewarm_ocean", new BiomeLukewarmDeepOcean());
|
||||
a(49, "deep_cold_ocean", new BiomeColdDeepOcean());
|
||||
a(50, "deep_frozen_ocean", new BiomeFrozenDeepOcean());
|
||||
a(127, "the_void", new BiomeVoid());
|
||||
a(129, "sunflower_plains", new BiomeSunflowerPlains());
|
||||
a(130, "desert_lakes", new BiomeDesertMutated());
|
||||
a(131, "gravelly_mountains", new BiomeExtremeHillsMutated());
|
||||
a(132, "flower_forest", new BiomeFlowerForest());
|
||||
a(133, "taiga_mountains", new BiomeTaigaMutated());
|
||||
a(134, "swamp_hills", new BiomeSwamplandMutated());
|
||||
a(140, "ice_spikes", new BiomeIcePlainsSpikes());
|
||||
a(149, "modified_jungle", new BiomeJungleMutated());
|
||||
a(151, "modified_jungle_edge", new BiomeJungleEdgeMutated());
|
||||
a(155, "tall_birch_forest", new BiomeBirchForestMutated());
|
||||
a(156, "tall_birch_hills", new BiomeBirchForestHillsMutated());
|
||||
a(157, "dark_forest_hills", new BiomeRoofedForestMutated());
|
||||
a(158, "snowy_taiga_mountains", new BiomeColdTaigaMutated());
|
||||
a(160, "giant_spruce_taiga", new BiomeMegaSpruceTaiga());
|
||||
a(161, "giant_spruce_taiga_hills", new BiomeRedwoodTaigaHillsMutated());
|
||||
a(162, "modified_gravelly_mountains", new BiomeExtremeHillsWithTreesMutated());
|
||||
a(163, "shattered_savanna", new BiomeSavannaMutated());
|
||||
a(164, "shattered_savanna_plateau", new BiomeSavannaPlateauMutated());
|
||||
a(165, "eroded_badlands", new BiomeMesaBryce());
|
||||
a(166, "modified_wooded_badlands_plateau", new BiomeMesaPlateauMutated());
|
||||
a(167, "modified_badlands_plateau", new BiomeMesaPlateauClearMutated());
|
||||
Collections.addAll(BiomeBase.aG, new BiomeBase[] { Biomes.OCEAN, Biomes.PLAINS, Biomes.DESERT, Biomes.MOUNTAINS, Biomes.FOREST, Biomes.TAIGA, Biomes.SWAMP, Biomes.RIVER, Biomes.FROZEN_RIVER, Biomes.SNOWY_TUNDRA, Biomes.SNOWY_MOUNTAINS, Biomes.MUSHROOM_FIELDS, Biomes.MUSHROOM_FIELD_SHORE, Biomes.BEACH, Biomes.DESERT_HILLS, Biomes.WOODED_HILLS, Biomes.TAIGA_HILLS, Biomes.JUNGLE, Biomes.JUNGLE_HILLS, Biomes.JUNGLE_EDGE, Biomes.DEEP_OCEAN, Biomes.STONE_SHORE, Biomes.SNOWY_BEACH, Biomes.BIRCH_FOREST, Biomes.BIRCH_FOREST_HILLS, Biomes.DARK_FOREST, Biomes.SNOWY_TAIGA, Biomes.SNOWY_TAIGA_HILLS, Biomes.GIANT_TREE_TAIGA, Biomes.GIANT_TREE_TAIGA_HILLS, Biomes.WOODED_MOUNTAINS, Biomes.SAVANNA, Biomes.SAVANNA_PLATEAU, Biomes.BADLANDS, Biomes.WOODED_BADLANDS_PLATEAU, Biomes.BADLANDS_PLATEAU});
|
||||
}
|
||||
|
||||
private static void a(int i, String s, BiomeBase biomebase) {
|
||||
IRegistry.BIOME.a(i, new MinecraftKey(s), biomebase);
|
||||
if (biomebase.b()) {
|
||||
BiomeBase.aH.a(biomebase, IRegistry.BIOME.a(IRegistry.BIOME.get(new MinecraftKey(biomebase.aR))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Paper start - keep track of data in a pair set to give O(1) contains calls - we have to hook removals incase plugins mess with it
|
||||
public static class MobList extends java.util.ArrayList<BiomeMeta> {
|
||||
java.util.Set<BiomeMeta> biomes = new java.util.HashSet<>();
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return biomes.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(BiomeMeta biomeMeta) {
|
||||
biomes.add(biomeMeta);
|
||||
return super.add(biomeMeta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeMeta remove(int index) {
|
||||
BiomeMeta removed = super.remove(index);
|
||||
if (removed != null) {
|
||||
biomes.remove(removed);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
biomes.clear();
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public static class a {
|
||||
|
||||
@Nullable
|
||||
private WorldGenSurfaceComposite<?> a;
|
||||
@Nullable
|
||||
private BiomeBase.Precipitation b;
|
||||
@Nullable
|
||||
private BiomeBase.Geography c;
|
||||
@Nullable
|
||||
private Float d;
|
||||
@Nullable
|
||||
private Float e;
|
||||
@Nullable
|
||||
private Float f;
|
||||
@Nullable
|
||||
private Float g;
|
||||
@Nullable
|
||||
private Integer h;
|
||||
@Nullable
|
||||
private Integer i;
|
||||
@Nullable
|
||||
private String j;
|
||||
|
||||
public a() {}
|
||||
|
||||
public BiomeBase.a a(WorldGenSurfaceComposite<?> worldgensurfacecomposite) {
|
||||
this.a = worldgensurfacecomposite;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(BiomeBase.Precipitation biomebase_precipitation) {
|
||||
this.b = biomebase_precipitation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(BiomeBase.Geography biomebase_geography) {
|
||||
this.c = biomebase_geography;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(float f) {
|
||||
this.d = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a b(float f) {
|
||||
this.e = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a c(float f) {
|
||||
this.f = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a d(float f) {
|
||||
this.g = f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(int i) {
|
||||
this.h = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a b(int i) {
|
||||
this.i = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BiomeBase.a a(@Nullable String s) {
|
||||
this.j = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "BiomeBuilder{\nsurfaceBuilder=" + this.a + ",\nprecipitation=" + this.b + ",\nbiomeCategory=" + this.c + ",\ndepth=" + this.d + ",\nscale=" + this.e + ",\ntemperature=" + this.f + ",\ndownfall=" + this.g + ",\nwaterColor=" + this.h + ",\nwaterFogColor=" + this.i + ",\nparent='" + this.j + '\'' + "\n" + '}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class BiomeMeta extends WeightedRandom.WeightedRandomChoice {
|
||||
|
||||
public EntityTypes<? extends EntityInsentient> b;
|
||||
public int c;
|
||||
public int d;
|
||||
|
||||
public BiomeMeta(EntityTypes<? extends EntityInsentient> entitytypes, int i, int j, int k) {
|
||||
super(i);
|
||||
this.b = entitytypes;
|
||||
this.c = j;
|
||||
this.d = k;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return EntityTypes.getName(this.b) + "*(" + this.c + "-" + this.d + "):" + this.a;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Precipitation {
|
||||
|
||||
NONE, RAIN, SNOW;
|
||||
|
||||
private Precipitation() {}
|
||||
}
|
||||
|
||||
public static enum Geography {
|
||||
|
||||
NONE, TAIGA, EXTREME_HILLS, JUNGLE, MESA, PLAINS, SAVANNA, ICY, THEEND, BEACH, FOREST, OCEAN, DESERT, RIVER, SWAMP, MUSHROOM, NETHER;
|
||||
|
||||
private Geography() {}
|
||||
}
|
||||
|
||||
public static enum EnumTemperature {
|
||||
|
||||
OCEAN, COLD, MEDIUM, WARM;
|
||||
|
||||
private EnumTemperature() {}
|
||||
}
|
||||
}
|
||||
1574
src/main/java/net/minecraft/server/Block.java
Normal file
1574
src/main/java/net/minecraft/server/Block.java
Normal file
File diff suppressed because it is too large
Load Diff
78
src/main/java/net/minecraft/server/BlockBeacon.java
Normal file
78
src/main/java/net/minecraft/server/BlockBeacon.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockBeacon extends BlockTileEntity {
|
||||
|
||||
public BlockBeacon(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityBeacon();
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityBeacon) {
|
||||
entityhuman.openContainer((TileEntityBeacon) tileentity);
|
||||
entityhuman.a(StatisticList.INTERACT_WITH_BEACON);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityBeacon) {
|
||||
((TileEntityBeacon) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public static void a(World world, BlockPosition blockposition) {
|
||||
//HttpUtilities.a.submit(() -> { // Paper
|
||||
Chunk chunk = world.getChunkAtWorldCoords(blockposition);
|
||||
|
||||
for (int i = blockposition.getY() - 1; i >= 0; --i) {
|
||||
BlockPosition blockposition1 = new BlockPosition(blockposition.getX(), i, blockposition.getZ());
|
||||
|
||||
if (!chunk.c(blockposition1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
IBlockData iblockdata = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata.getBlock() == Blocks.BEACON) {
|
||||
((WorldServer) world).postToMainThread(() -> {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity instanceof TileEntityBeacon) {
|
||||
((TileEntityBeacon) tileentity).p();
|
||||
world.playBlockAction(blockposition1, Blocks.BEACON, 1, 0);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
// }); // Paper
|
||||
}
|
||||
}
|
||||
310
src/main/java/net/minecraft/server/BlockBed.java
Normal file
310
src/main/java/net/minecraft/server/BlockBed.java
Normal file
@@ -0,0 +1,310 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockBed extends BlockFacingHorizontal implements ITileEntity {
|
||||
|
||||
public static final BlockStateEnum<BlockPropertyBedPart> PART = BlockProperties.ao;
|
||||
public static final BlockStateBoolean OCCUPIED = BlockProperties.q;
|
||||
protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
|
||||
private final EnumColor color;
|
||||
|
||||
public BlockBed(EnumColor enumcolor, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.color = enumcolor;
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockBed.PART, BlockPropertyBedPart.FOOT)).set(BlockBed.OCCUPIED, false));
|
||||
}
|
||||
|
||||
public MaterialMapColor c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.FOOT ? this.color.e() : MaterialMapColor.e;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
if (iblockdata.get(BlockBed.PART) != BlockPropertyBedPart.HEAD) {
|
||||
blockposition = blockposition.shift((EnumDirection) iblockdata.get(BlockBed.FACING));
|
||||
iblockdata = world.getType(blockposition);
|
||||
if (iblockdata.getBlock() != this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit - moved world and biome check into EntityHuman
|
||||
if (true || world.worldProvider.canRespawn() && world.getBiome(blockposition) != Biomes.NETHER) {
|
||||
if ((Boolean) iblockdata.get(BlockBed.OCCUPIED)) {
|
||||
EntityHuman entityhuman1 = this.a(world, blockposition);
|
||||
|
||||
if (entityhuman1 != null) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.occupied", new Object[0])), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, false);
|
||||
world.setTypeAndData(blockposition, iblockdata, 4);
|
||||
}
|
||||
|
||||
EntityHuman.EnumBedResult entityhuman_enumbedresult = entityhuman.a(blockposition);
|
||||
|
||||
if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.OK) {
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockBed.OCCUPIED, true);
|
||||
world.setTypeAndData(blockposition, iblockdata, 4);
|
||||
return true;
|
||||
} else {
|
||||
if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_POSSIBLE_NOW) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.no_sleep", new Object[0])), true);
|
||||
} else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_SAFE) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.not_safe", new Object[0])), true);
|
||||
} else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.TOO_FAR_AWAY) {
|
||||
entityhuman.a((IChatBaseComponent) (new ChatMessage("block.minecraft.bed.too_far_away", new Object[0])), true);
|
||||
}
|
||||
// CraftBukkit start - handling bed explosion from below here
|
||||
else if (entityhuman_enumbedresult == EntityHuman.EnumBedResult.NOT_POSSIBLE_HERE) {
|
||||
this.explodeBed(iblockdata, world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit start - moved bed explosion into separate method
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean explodeBed(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
world.setAir(blockposition);
|
||||
BlockPosition blockposition1 = blockposition.shift(((EnumDirection) iblockdata.get(BlockBed.FACING)).opposite());
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == this) {
|
||||
world.setAir(blockposition1);
|
||||
}
|
||||
|
||||
world.createExplosion((Entity) null, DamageSource.a(), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, 5.0F, true, true);
|
||||
return true;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private EntityHuman a(World world, BlockPosition blockposition) {
|
||||
Iterator iterator = world.players.iterator();
|
||||
|
||||
EntityHuman entityhuman;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
entityhuman = (EntityHuman) iterator.next();
|
||||
} while (!entityhuman.isSleeping() || !entityhuman.bedPosition.equals(blockposition));
|
||||
|
||||
return entityhuman;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void fallOn(World world, BlockPosition blockposition, Entity entity, float f) {
|
||||
super.fallOn(world, blockposition, entity, f * 0.5F);
|
||||
}
|
||||
|
||||
public void a(IBlockAccess iblockaccess, Entity entity) {
|
||||
if (entity.isSneaking()) {
|
||||
super.a(iblockaccess, entity);
|
||||
} else if (entity.motY < 0.0D) {
|
||||
entity.motY = -entity.motY * 0.6600000262260437D;
|
||||
if (!(entity instanceof EntityLiving)) {
|
||||
entity.motY *= 0.8D;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == a((BlockPropertyBedPart) iblockdata.get(BlockBed.PART), (EnumDirection) iblockdata.get(BlockBed.FACING)) ? (iblockdata1.getBlock() == this && iblockdata1.get(BlockBed.PART) != iblockdata.get(BlockBed.PART) ? (IBlockData) iblockdata.set(BlockBed.OCCUPIED, iblockdata1.get(BlockBed.OCCUPIED)) : Blocks.AIR.getBlockData()) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
private static EnumDirection a(BlockPropertyBedPart blockpropertybedpart, EnumDirection enumdirection) {
|
||||
return blockpropertybedpart == BlockPropertyBedPart.FOOT ? enumdirection : enumdirection.opposite();
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack);
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
world.n(blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) {
|
||||
BlockPropertyBedPart blockpropertybedpart = (BlockPropertyBedPart) iblockdata.get(BlockBed.PART);
|
||||
boolean flag = blockpropertybedpart == BlockPropertyBedPart.HEAD;
|
||||
BlockPosition blockposition1 = blockposition.shift(a(blockpropertybedpart, (EnumDirection) iblockdata.get(BlockBed.FACING)));
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata1.getBlock() == this && iblockdata1.get(BlockBed.PART) != blockpropertybedpart) {
|
||||
world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35);
|
||||
world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1));
|
||||
if (!world.isClientSide && !entityhuman.u()) {
|
||||
if (flag) {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
} else {
|
||||
iblockdata1.a(world, blockposition1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
entityhuman.b(StatisticList.BLOCK_MINED.b(this));
|
||||
}
|
||||
|
||||
super.a(world, blockposition, iblockdata, entityhuman);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
EnumDirection enumdirection = blockactioncontext.f();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
|
||||
return blockactioncontext.getWorld().getType(blockposition1).a(blockactioncontext) ? (IBlockData) this.getBlockData().set(BlockBed.FACING, enumdirection) : null;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return (IMaterial) (iblockdata.get(BlockBed.PART) == BlockPropertyBedPart.FOOT ? Items.AIR : super.getDropType(iblockdata, world, blockposition, i));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockBed.c;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BlockPosition a(IBlockAccess iblockaccess, BlockPosition blockposition, int i) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockaccess.getType(blockposition).get(BlockBed.FACING);
|
||||
// Paper - replace whole method
|
||||
World world = (World) iblockaccess;
|
||||
int radius = world.paperConfig.bedSearchRadius;
|
||||
for (int r = 1; r <= radius; r++) {
|
||||
int x = -r;
|
||||
int z = r;
|
||||
|
||||
// Iterates the edge of half of the box; then negates for other half.
|
||||
while (x <= r && z > -r) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
BlockPosition pos = blockposition.add(x, y, z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
pos = blockposition.add(-x, y, -z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos = blockposition.add(enumdirection.getAdjacentX() + x, y, enumdirection.getAdjacentZ() + z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
pos = blockposition.add(enumdirection.getAdjacentX() - x, y, enumdirection.getAdjacentZ() - z);
|
||||
if (isSafeRespawn(world, pos)) {
|
||||
if (i-- <= 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x < r) {
|
||||
x++;
|
||||
} else {
|
||||
z--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null; /* // Paper comment out
|
||||
int j = blockposition.getX();
|
||||
int k = blockposition.getY();
|
||||
int l = blockposition.getZ();
|
||||
|
||||
for (int i1 = 0; i1 <= 1; ++i1) {
|
||||
int j1 = j - enumdirection.getAdjacentX() * i1 - 1;
|
||||
int k1 = l - enumdirection.getAdjacentZ() * i1 - 1;
|
||||
int l1 = j1 + 2;
|
||||
int i2 = k1 + 2;
|
||||
|
||||
for (int j2 = j1; j2 <= l1; ++j2) {
|
||||
for (int k2 = k1; k2 <= i2; ++k2) {
|
||||
BlockPosition blockposition1 = new BlockPosition(j2, k, k2);
|
||||
|
||||
if (a(iblockaccess, blockposition1)) {
|
||||
if (i <= 0) {
|
||||
return blockposition1;
|
||||
}
|
||||
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;*/ // Paper
|
||||
}
|
||||
|
||||
protected static boolean isSafeRespawn(IBlockAccess iblockaccess, BlockPosition blockposition) { // Paper - OBFHELPER + behavior improvement
|
||||
return a(iblockaccess, blockposition) && iblockaccess.getType(blockposition.down()).getMaterial().isBuildable(); // Paper - ensure solid block
|
||||
}
|
||||
protected static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockaccess.getType(blockposition.down()).q() && !iblockaccess.getType(blockposition).getMaterial().isBuildable() && !iblockaccess.getType(blockposition.up()).getMaterial().isBuildable();
|
||||
}
|
||||
|
||||
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
|
||||
return EnumPistonReaction.DESTROY;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockBed.FACING, BlockBed.PART, BlockBed.OCCUPIED);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityBed(this.color);
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, @Nullable EntityLiving entityliving, ItemStack itemstack) {
|
||||
super.postPlace(world, blockposition, iblockdata, entityliving, itemstack);
|
||||
if (!world.isClientSide) {
|
||||
BlockPosition blockposition1 = blockposition.shift((EnumDirection) iblockdata.get(BlockBed.FACING));
|
||||
|
||||
world.setTypeAndData(blockposition1, (IBlockData) iblockdata.set(BlockBed.PART, BlockPropertyBedPart.HEAD), 3);
|
||||
world.update(blockposition, Blocks.AIR);
|
||||
iblockdata.a((GeneratorAccess) world, blockposition, 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
224
src/main/java/net/minecraft/server/BlockButtonAbstract.java
Normal file
224
src/main/java/net/minecraft/server/BlockButtonAbstract.java
Normal file
@@ -0,0 +1,224 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.event.block.BlockRedstoneEvent;
|
||||
import org.bukkit.event.entity.EntityInteractEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public abstract class BlockButtonAbstract extends BlockAttachable {
|
||||
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
protected static final VoxelShape b = Block.a(6.0D, 14.0D, 5.0D, 10.0D, 16.0D, 11.0D);
|
||||
protected static final VoxelShape c = Block.a(5.0D, 14.0D, 6.0D, 11.0D, 16.0D, 10.0D);
|
||||
protected static final VoxelShape o = Block.a(6.0D, 0.0D, 5.0D, 10.0D, 2.0D, 11.0D);
|
||||
protected static final VoxelShape p = Block.a(5.0D, 0.0D, 6.0D, 11.0D, 2.0D, 10.0D);
|
||||
protected static final VoxelShape q = Block.a(5.0D, 6.0D, 14.0D, 11.0D, 10.0D, 16.0D);
|
||||
protected static final VoxelShape r = Block.a(5.0D, 6.0D, 0.0D, 11.0D, 10.0D, 2.0D);
|
||||
protected static final VoxelShape s = Block.a(14.0D, 6.0D, 5.0D, 16.0D, 10.0D, 11.0D);
|
||||
protected static final VoxelShape t = Block.a(0.0D, 6.0D, 5.0D, 2.0D, 10.0D, 11.0D);
|
||||
protected static final VoxelShape u = Block.a(6.0D, 15.0D, 5.0D, 10.0D, 16.0D, 11.0D);
|
||||
protected static final VoxelShape v = Block.a(5.0D, 15.0D, 6.0D, 11.0D, 16.0D, 10.0D);
|
||||
protected static final VoxelShape w = Block.a(6.0D, 0.0D, 5.0D, 10.0D, 1.0D, 11.0D);
|
||||
protected static final VoxelShape x = Block.a(5.0D, 0.0D, 6.0D, 11.0D, 1.0D, 10.0D);
|
||||
protected static final VoxelShape y = Block.a(5.0D, 6.0D, 15.0D, 11.0D, 10.0D, 16.0D);
|
||||
protected static final VoxelShape z = Block.a(5.0D, 6.0D, 0.0D, 11.0D, 10.0D, 1.0D);
|
||||
protected static final VoxelShape A = Block.a(15.0D, 6.0D, 5.0D, 16.0D, 10.0D, 11.0D);
|
||||
protected static final VoxelShape B = Block.a(0.0D, 6.0D, 5.0D, 1.0D, 10.0D, 11.0D);
|
||||
private final boolean E;
|
||||
|
||||
protected BlockButtonAbstract(boolean flag, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockButtonAbstract.FACING, EnumDirection.NORTH)).set(BlockButtonAbstract.POWERED, false)).set(BlockButtonAbstract.FACE, BlockPropertyAttachPosition.WALL));
|
||||
this.E = flag;
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return this.E ? 30 : 20;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockButtonAbstract.FACING);
|
||||
boolean flag = (Boolean) iblockdata.get(BlockButtonAbstract.POWERED);
|
||||
|
||||
switch ((BlockPropertyAttachPosition) iblockdata.get(BlockButtonAbstract.FACE)) {
|
||||
case FLOOR:
|
||||
if (enumdirection.k() == EnumDirection.EnumAxis.X) {
|
||||
return flag ? BlockButtonAbstract.w : BlockButtonAbstract.o;
|
||||
}
|
||||
|
||||
return flag ? BlockButtonAbstract.x : BlockButtonAbstract.p;
|
||||
case WALL:
|
||||
switch (enumdirection) {
|
||||
case EAST:
|
||||
return flag ? BlockButtonAbstract.B : BlockButtonAbstract.t;
|
||||
case WEST:
|
||||
return flag ? BlockButtonAbstract.A : BlockButtonAbstract.s;
|
||||
case SOUTH:
|
||||
return flag ? BlockButtonAbstract.z : BlockButtonAbstract.r;
|
||||
case NORTH:
|
||||
default:
|
||||
return flag ? BlockButtonAbstract.y : BlockButtonAbstract.q;
|
||||
}
|
||||
case CEILING:
|
||||
default:
|
||||
return enumdirection.k() == EnumDirection.EnumAxis.X ? (flag ? BlockButtonAbstract.u : BlockButtonAbstract.b) : (flag ? BlockButtonAbstract.v : BlockButtonAbstract.c);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
return true;
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
boolean powered = ((Boolean) iblockdata.get(POWERED));
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = (powered) ? 15 : 0;
|
||||
int current = (!powered) ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if ((eventRedstone.getNewCurrent() > 0) != (!powered)) {
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, true), 3);
|
||||
this.a(entityhuman, world, blockposition, true);
|
||||
this.c(iblockdata, world, blockposition);
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(@Nullable EntityHuman entityhuman, GeneratorAccess generatoraccess, BlockPosition blockposition, boolean flag) {
|
||||
generatoraccess.a(flag ? entityhuman : null, blockposition, this.a(flag), SoundCategory.BLOCKS, 0.3F, flag ? 0.6F : 0.5F);
|
||||
}
|
||||
|
||||
protected abstract SoundEffect a(boolean flag);
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
if ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
this.c(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockButtonAbstract.POWERED) ? 15 : 0;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockButtonAbstract.POWERED) && k(iblockdata) == enumdirection ? 15 : 0;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide && (Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
if (this.E) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if (eventRedstone.getNewCurrent() > 0) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, false), 3);
|
||||
this.c(iblockdata, world, blockposition);
|
||||
this.a((EntityHuman) null, world, blockposition, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!world.isClientSide && this.E && !(Boolean) iblockdata.get(BlockButtonAbstract.POWERED)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
private void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
List<? extends Entity> list = world.a(EntityArrow.class, iblockdata.getShape(world, blockposition).getBoundingBox().a(blockposition));
|
||||
boolean flag = !list.isEmpty();
|
||||
boolean flag1 = (Boolean) iblockdata.get(BlockButtonAbstract.POWERED);
|
||||
|
||||
// CraftBukkit start - Call interact event when arrows turn on wooden buttons
|
||||
if (flag1 != flag && flag) {
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
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 (flag != flag1) {
|
||||
// CraftBukkit start
|
||||
boolean powered = flag1;
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = (powered) ? 15 : 0;
|
||||
int current = (!powered) ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if ((flag && eventRedstone.getNewCurrent() <= 0) || (!flag && eventRedstone.getNewCurrent() > 0)) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockButtonAbstract.POWERED, flag), 3);
|
||||
this.c(iblockdata, world, blockposition);
|
||||
this.a((EntityHuman) null, world, blockposition, flag);
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
world.getBlockTickList().a(new BlockPosition(blockposition), this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void c(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.shift(k(iblockdata).opposite()), this);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockButtonAbstract.FACING, BlockButtonAbstract.POWERED, BlockButtonAbstract.FACE);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
117
src/main/java/net/minecraft/server/BlockCactus.java
Normal file
117
src/main/java/net/minecraft/server/BlockCactus.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockCactus extends Block {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.X;
|
||||
protected static final VoxelShape b = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 15.0D, 15.0D);
|
||||
protected static final VoxelShape c = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
|
||||
protected BlockCactus(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCactus.AGE, 0));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
world.setAir(blockposition, true);
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (world.isEmpty(blockposition1)) {
|
||||
int i;
|
||||
|
||||
for (i = 1; world.getType(blockposition.down(i)).getBlock() == this; ++i) {
|
||||
;
|
||||
}
|
||||
|
||||
if (i < world.paperConfig.cactusMaxHeight) { // Paper - Configurable growth height
|
||||
int j = (Integer) iblockdata.get(BlockCactus.AGE);
|
||||
|
||||
if (j >= (byte) range(3, ((100.0F / world.spigotConfig.cactusModifier) * 15) + 0.5F, 15)) { // Spigot
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition1, this.getBlockData()); // CraftBukkit
|
||||
IBlockData iblockdata1 = (IBlockData) iblockdata.set(BlockCactus.AGE, 0);
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata1, 4);
|
||||
iblockdata1.doPhysics(world, blockposition1, this, blockposition);
|
||||
} else {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCactus.AGE, j + 1), 4);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelShape f(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCactus.b;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCactus.c;
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
EnumDirection enumdirection;
|
||||
Material material;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
Block block = iworldreader.getType(blockposition.down()).getBlock();
|
||||
|
||||
return (block == Blocks.CACTUS || block == Blocks.SAND || block == Blocks.RED_SAND) && !iworldreader.getType(blockposition.up()).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
enumdirection = (EnumDirection) iterator.next();
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition.shift(enumdirection));
|
||||
|
||||
material = iblockdata1.getMaterial();
|
||||
} while (!material.isBuildable() && !iworldreader.getFluid(blockposition.shift(enumdirection)).a(TagsFluid.LAVA));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit
|
||||
entity.damageEntity(DamageSource.CACTUS, 1.0F);
|
||||
CraftEventFactory.blockDamage = null; // CraftBukkit
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCactus.AGE);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
91
src/main/java/net/minecraft/server/BlockCake.java
Normal file
91
src/main/java/net/minecraft/server/BlockCake.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockCake extends Block {
|
||||
|
||||
public static final BlockStateInteger BITES = BlockProperties.Z;
|
||||
protected static final VoxelShape[] b = new VoxelShape[] { Block.a(1.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(3.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(5.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(7.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(9.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(11.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D), Block.a(13.0D, 0.0D, 1.0D, 15.0D, 8.0D, 15.0D)};
|
||||
|
||||
protected BlockCake(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCake.BITES, 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCake.b[(Integer) iblockdata.get(BlockCake.BITES)];
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (!world.isClientSide) {
|
||||
return this.a((GeneratorAccess) world, blockposition, iblockdata, entityhuman);
|
||||
} else {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
return this.a((GeneratorAccess) world, blockposition, iblockdata, entityhuman) || itemstack.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) {
|
||||
if (!entityhuman.q(false)) {
|
||||
return false;
|
||||
} else {
|
||||
entityhuman.a(StatisticList.EAT_CAKE_SLICE);
|
||||
// CraftBukkit start
|
||||
// entityhuman.getFoodData().eat(2, 0.1F);
|
||||
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).getBukkitEntity().sendHealthUpdate();
|
||||
// CraftBukkit end
|
||||
int i = (Integer) iblockdata.get(BlockCake.BITES);
|
||||
|
||||
if (i < 6) {
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCake.BITES, i + 1), 3);
|
||||
} else {
|
||||
generatoraccess.setAir(blockposition);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return iworldreader.getType(blockposition.down()).getMaterial().isBuildable();
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCake.BITES);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return (7 - (Integer) iblockdata.get(BlockCake.BITES)) * 2;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
267
src/main/java/net/minecraft/server/BlockCauldron.java
Normal file
267
src/main/java/net/minecraft/server/BlockCauldron.java
Normal file
@@ -0,0 +1,267 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import org.bukkit.event.block.CauldronLevelChangeEvent; // CraftBukkit
|
||||
|
||||
public class BlockCauldron extends Block {
|
||||
|
||||
public static final BlockStateInteger LEVEL = BlockProperties.af;
|
||||
protected static final VoxelShape b = Block.a(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D);
|
||||
protected static final VoxelShape c = VoxelShapes.a(VoxelShapes.b(), BlockCauldron.b, OperatorBoolean.ONLY_FIRST);
|
||||
|
||||
public BlockCauldron(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCauldron.LEVEL, 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCauldron.c;
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape h(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCauldron.b;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
int i = (Integer) iblockdata.get(BlockCauldron.LEVEL);
|
||||
float f = (float) blockposition.getY() + (6.0F + (float) (3 * i)) / 16.0F;
|
||||
|
||||
if (!world.isClientSide && entity.isBurning() && i > 0 && entity.getBoundingBox().minY <= (double) f) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entity, CauldronLevelChangeEvent.ChangeReason.EXTINGUISH)) {
|
||||
return;
|
||||
}
|
||||
entity.extinguish();
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
int i = (Integer) iblockdata.get(BlockCauldron.LEVEL);
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
if (item == Items.WATER_BUCKET) {
|
||||
if (i < 3 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, 3, entityhuman, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
entityhuman.a(enumhand, new ItemStack(Items.BUCKET));
|
||||
}
|
||||
|
||||
entityhuman.a(StatisticList.FILL_CAULDRON);
|
||||
// this.a(world, blockposition, iblockdata, 3);
|
||||
// CraftBukkit end
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BUCKET_EMPTY, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (item == Items.BUCKET) {
|
||||
if (i == 3 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, 0, entityhuman, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, new ItemStack(Items.WATER_BUCKET));
|
||||
} else if (!entityhuman.inventory.pickup(new ItemStack(Items.WATER_BUCKET))) {
|
||||
entityhuman.drop(new ItemStack(Items.WATER_BUCKET), false);
|
||||
}
|
||||
}
|
||||
|
||||
entityhuman.a(StatisticList.USE_CAULDRON);
|
||||
// this.a(world, blockposition, iblockdata, 0);
|
||||
// CraftBukkit end
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BUCKET_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ItemStack itemstack1;
|
||||
|
||||
if (item == Items.GLASS_BOTTLE) {
|
||||
if (i > 0 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack1 = PotionUtil.a(new ItemStack(Items.POTION), Potions.b);
|
||||
entityhuman.a(StatisticList.USE_CAULDRON);
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
} else if (!entityhuman.inventory.pickup(itemstack1)) {
|
||||
entityhuman.drop(itemstack1, false);
|
||||
} else if (entityhuman instanceof EntityPlayer) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BOTTLE_FILL, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (item == Items.POTION && PotionUtil.d(itemstack) == Potions.b) {
|
||||
if (i < 3 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i + 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_EMPTY)) {
|
||||
return true;
|
||||
}
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack1 = new ItemStack(Items.GLASS_BOTTLE);
|
||||
entityhuman.a(StatisticList.USE_CAULDRON);
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
if (entityhuman instanceof EntityPlayer) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.ITEM_BOTTLE_EMPTY, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
// this.a(world, blockposition, iblockdata, i + 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (i > 0 && item instanceof ItemArmorColorable) {
|
||||
ItemArmorColorable itemarmorcolorable = (ItemArmorColorable) item;
|
||||
|
||||
if (itemarmorcolorable.e(itemstack) && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.ARMOR_WASH)) {
|
||||
return true;
|
||||
}
|
||||
itemarmorcolorable.g(itemstack);
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
entityhuman.a(StatisticList.CLEAN_ARMOR);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0 && item instanceof ItemBanner) {
|
||||
if (TileEntityBanner.a(itemstack) > 0 && !world.isClientSide) {
|
||||
// CraftBukkit start
|
||||
if (!this.changeLevel(world, blockposition, iblockdata, i - 1, entityhuman, CauldronLevelChangeEvent.ChangeReason.BANNER_WASH)) {
|
||||
return true;
|
||||
}
|
||||
itemstack1 = itemstack.cloneItemStack();
|
||||
itemstack1.setCount(1);
|
||||
TileEntityBanner.b(itemstack1);
|
||||
entityhuman.a(StatisticList.CLEAN_BANNER);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
// this.a(world, blockposition, iblockdata, i - 1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, itemstack1);
|
||||
} else if (!entityhuman.inventory.pickup(itemstack1)) {
|
||||
entityhuman.drop(itemstack1, false);
|
||||
} else if (entityhuman instanceof EntityPlayer) {
|
||||
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (i > 0 && item instanceof ItemBlock) {
|
||||
Block block = ((ItemBlock) item).getBlock();
|
||||
|
||||
if (block instanceof BlockShulkerBox && !world.e()) {
|
||||
ItemStack itemstack2 = new ItemStack(Blocks.SHULKER_BOX, 1);
|
||||
|
||||
if (itemstack.hasTag()) {
|
||||
itemstack2.setTag(itemstack.getTag().clone());
|
||||
}
|
||||
|
||||
entityhuman.a(enumhand, itemstack2);
|
||||
this.a(world, blockposition, iblockdata, i - 1);
|
||||
entityhuman.a(StatisticList.CLEAN_SHULKER_BOX);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) {
|
||||
this.changeLevel(world, blockposition, iblockdata, i, null, CauldronLevelChangeEvent.ChangeReason.UNKNOWN);
|
||||
}
|
||||
|
||||
private boolean changeLevel(World world, BlockPosition blockposition, IBlockData iblockdata, int i, Entity entity, CauldronLevelChangeEvent.ChangeReason reason) {
|
||||
int newLevel = Integer.valueOf(MathHelper.clamp(i, 0, 3));
|
||||
CauldronLevelChangeEvent event = new CauldronLevelChangeEvent(
|
||||
world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()),
|
||||
(entity == null) ? null : entity.getBukkitEntity(), reason, iblockdata.get(BlockCauldron.LEVEL), newLevel
|
||||
);
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockCauldron.LEVEL, event.getNewLevel()), 2);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
return true;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public void c(World world, BlockPosition blockposition) {
|
||||
if (world.random.nextInt(20) == 1) {
|
||||
float f = world.getBiome(blockposition).getAdjustedTemperature(blockposition);
|
||||
|
||||
if (f >= 0.15F) {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
|
||||
if ((Integer) iblockdata.get(BlockCauldron.LEVEL) < 3) {
|
||||
this.a(world, blockposition, (IBlockData) iblockdata.a((IBlockState) BlockCauldron.LEVEL), 2); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return (Integer) iblockdata.get(BlockCauldron.LEVEL);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCauldron.LEVEL);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.UP ? EnumBlockFaceShape.BOWL : (enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.UNDEFINED : EnumBlockFaceShape.SOLID);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
289
src/main/java/net/minecraft/server/BlockChest.java
Normal file
289
src/main/java/net/minecraft/server/BlockChest.java
Normal file
@@ -0,0 +1,289 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockChest extends BlockTileEntity implements IFluidSource, IFluidContainer {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING;
|
||||
public static final BlockStateEnum<BlockPropertyChestType> b = BlockProperties.ap;
|
||||
public static final BlockStateBoolean c = BlockProperties.y;
|
||||
protected static final VoxelShape o = Block.a(1.0D, 0.0D, 0.0D, 15.0D, 14.0D, 15.0D);
|
||||
protected static final VoxelShape p = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 16.0D);
|
||||
protected static final VoxelShape q = Block.a(0.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D);
|
||||
protected static final VoxelShape r = Block.a(1.0D, 0.0D, 1.0D, 16.0D, 14.0D, 15.0D);
|
||||
protected static final VoxelShape s = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 15.0D);
|
||||
|
||||
protected BlockChest(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockChest.FACING, EnumDirection.NORTH)).set(BlockChest.b, BlockPropertyChestType.SINGLE)).set(BlockChest.c, false));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if ((Boolean) iblockdata.get(BlockChest.c)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
if (iblockdata1.getBlock() == this && enumdirection.k().c()) {
|
||||
BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) iblockdata1.get(BlockChest.b);
|
||||
|
||||
if (iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE && blockpropertychesttype != BlockPropertyChestType.SINGLE && iblockdata.get(BlockChest.FACING) == iblockdata1.get(BlockChest.FACING) && k(iblockdata1) == enumdirection.opposite()) {
|
||||
return (IBlockData) iblockdata.set(BlockChest.b, blockpropertychesttype.a());
|
||||
}
|
||||
} else if (k(iblockdata) == enumdirection) {
|
||||
return (IBlockData) iblockdata.set(BlockChest.b, BlockPropertyChestType.SINGLE);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
if (iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE) {
|
||||
return BlockChest.s;
|
||||
} else {
|
||||
switch (k(iblockdata)) {
|
||||
case NORTH:
|
||||
default:
|
||||
return BlockChest.o;
|
||||
case SOUTH:
|
||||
return BlockChest.p;
|
||||
case WEST:
|
||||
return BlockChest.q;
|
||||
case EAST:
|
||||
return BlockChest.r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EnumDirection k(IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockChest.FACING);
|
||||
|
||||
return iblockdata.get(BlockChest.b) == BlockPropertyChestType.LEFT ? enumdirection.e() : enumdirection.f();
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
BlockPropertyChestType blockpropertychesttype = BlockPropertyChestType.SINGLE;
|
||||
EnumDirection enumdirection = blockactioncontext.f().opposite();
|
||||
Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition());
|
||||
boolean flag = blockactioncontext.isSneaking();
|
||||
EnumDirection enumdirection1 = blockactioncontext.getClickedFace();
|
||||
|
||||
if (enumdirection1.k().c() && flag) {
|
||||
EnumDirection enumdirection2 = this.a(blockactioncontext, enumdirection1.opposite());
|
||||
|
||||
if (enumdirection2 != null && enumdirection2.k() != enumdirection1.k()) {
|
||||
enumdirection = enumdirection2;
|
||||
blockpropertychesttype = enumdirection2.f() == enumdirection1.opposite() ? BlockPropertyChestType.RIGHT : BlockPropertyChestType.LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockpropertychesttype == BlockPropertyChestType.SINGLE && !flag) {
|
||||
if (enumdirection == this.a(blockactioncontext, enumdirection.e())) {
|
||||
blockpropertychesttype = BlockPropertyChestType.LEFT;
|
||||
} else if (enumdirection == this.a(blockactioncontext, enumdirection.f())) {
|
||||
blockpropertychesttype = BlockPropertyChestType.RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
return (IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockChest.FACING, enumdirection)).set(BlockChest.b, blockpropertychesttype)).set(BlockChest.c, fluid.c() == FluidTypes.WATER);
|
||||
}
|
||||
|
||||
public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if ((Boolean) iblockdata.get(BlockChest.c)) {
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockChest.c, false), 3);
|
||||
return FluidTypes.WATER;
|
||||
} else {
|
||||
return FluidTypes.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public Fluid h(IBlockData iblockdata) {
|
||||
return (Boolean) iblockdata.get(BlockChest.c) ? FluidTypes.WATER.a(false) : super.h(iblockdata);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) {
|
||||
return !(Boolean) iblockdata.get(BlockChest.c) && fluidtype == FluidTypes.WATER;
|
||||
}
|
||||
|
||||
public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) {
|
||||
if (!(Boolean) iblockdata.get(BlockChest.c) && fluid.c() == FluidTypes.WATER) {
|
||||
if (!generatoraccess.e()) {
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockChest.c, true), 3);
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private EnumDirection a(BlockActionContext blockactioncontext, EnumDirection enumdirection) {
|
||||
IBlockData iblockdata = blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition().shift(enumdirection));
|
||||
|
||||
return iblockdata.getBlock() == this && iblockdata.get(BlockChest.b) == BlockPropertyChestType.SINGLE ? (EnumDirection) iblockdata.get(BlockChest.FACING) : null;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityChest) {
|
||||
((TileEntityChest) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof IInventory) {
|
||||
InventoryUtils.dropInventory(world, blockposition, (IInventory) tileentity);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
ITileInventory itileinventory = this.getInventory(iblockdata, world, blockposition, false);
|
||||
|
||||
if (itileinventory != null) {
|
||||
entityhuman.openContainer(itileinventory);
|
||||
entityhuman.b(this.d());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected Statistic<MinecraftKey> d() {
|
||||
return StatisticList.CUSTOM.b(StatisticList.OPEN_CHEST);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ITileInventory getInventory(IBlockData iblockdata, World world, BlockPosition blockposition, boolean flag) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (!(tileentity instanceof TileEntityChest)) {
|
||||
return null;
|
||||
} else if (!flag && this.a(world, blockposition)) {
|
||||
return null;
|
||||
} else {
|
||||
Object object = (TileEntityChest) tileentity;
|
||||
BlockPropertyChestType blockpropertychesttype = (BlockPropertyChestType) iblockdata.get(BlockChest.b);
|
||||
|
||||
if (blockpropertychesttype == BlockPropertyChestType.SINGLE) {
|
||||
return (ITileInventory) object;
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.shift(k(iblockdata));
|
||||
// Paper start - don't load chunks if the other side of the chest is in unloaded chunk
|
||||
final IBlockData iblockdata1 = world.getTypeIfLoaded(blockposition1); // Paper
|
||||
if (iblockdata1 == null) {
|
||||
return null;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
if (iblockdata1.getBlock() == this) {
|
||||
BlockPropertyChestType blockpropertychesttype1 = (BlockPropertyChestType) iblockdata1.get(BlockChest.b);
|
||||
|
||||
if (blockpropertychesttype1 != BlockPropertyChestType.SINGLE && blockpropertychesttype != blockpropertychesttype1 && iblockdata1.get(BlockChest.FACING) == iblockdata.get(BlockChest.FACING)) {
|
||||
if (!flag && this.a(world, blockposition1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TileEntity tileentity1 = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity1 instanceof TileEntityChest) {
|
||||
Object object1 = blockpropertychesttype == BlockPropertyChestType.RIGHT ? object : (ITileInventory) tileentity1;
|
||||
Object object2 = blockpropertychesttype == BlockPropertyChestType.RIGHT ? (ITileInventory) tileentity1 : object;
|
||||
|
||||
object = new InventoryLargeChest(new ChatMessage("container.chestDouble", new Object[0]), (ITileInventory) object1, (ITileInventory) object2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ITileInventory) object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityChest();
|
||||
}
|
||||
|
||||
private boolean a(World world, BlockPosition blockposition) {
|
||||
return this.a((IBlockAccess) world, blockposition) || this.b(world, blockposition);
|
||||
}
|
||||
|
||||
private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockaccess.getType(blockposition.up()).isOccluding();
|
||||
}
|
||||
|
||||
private boolean b(World world, BlockPosition blockposition) {
|
||||
// Paper start - Option to disable chest cat detection
|
||||
if (world.paperConfig.disableChestCatDetection) {
|
||||
return false;
|
||||
}
|
||||
// Paper end
|
||||
List<EntityOcelot> list = world.a(EntityOcelot.class, new AxisAlignedBB((double) blockposition.getX(), (double) (blockposition.getY() + 1), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 2), (double) (blockposition.getZ() + 1)));
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityOcelot entityocelot = (EntityOcelot) iterator.next();
|
||||
|
||||
if (entityocelot.isSitting()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return Container.b((IInventory) this.getInventory(iblockdata, world, blockposition, false));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockChest.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockChest.FACING, BlockChest.b, BlockChest.c);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
259
src/main/java/net/minecraft/server/BlockChorusFlower.java
Normal file
259
src/main/java/net/minecraft/server/BlockChorusFlower.java
Normal file
@@ -0,0 +1,259 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockChorusFlower extends Block {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.V;
|
||||
private final BlockChorusFruit b;
|
||||
|
||||
protected BlockChorusFlower(BlockChorusFruit blockchorusfruit, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.b = blockchorusfruit;
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockChorusFlower.AGE, 0));
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
world.setAir(blockposition, true);
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (world.isEmpty(blockposition1) && blockposition1.getY() < 256) {
|
||||
int i = (Integer) iblockdata.get(BlockChorusFlower.AGE);
|
||||
|
||||
if (i < 5) {
|
||||
boolean flag = false;
|
||||
boolean flag1 = false;
|
||||
IBlockData iblockdata1 = world.getType(blockposition.down());
|
||||
Block block = iblockdata1.getBlock();
|
||||
int j;
|
||||
|
||||
if (block == Blocks.END_STONE) {
|
||||
flag = true;
|
||||
} else if (block == this.b) {
|
||||
j = 1;
|
||||
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
Block block1 = world.getType(blockposition.down(j + 1)).getBlock();
|
||||
|
||||
if (block1 != this.b) {
|
||||
if (block1 == Blocks.END_STONE) {
|
||||
flag1 = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
|
||||
if (j < 2 || j <= random.nextInt(flag1 ? 5 : 4)) {
|
||||
flag = true;
|
||||
}
|
||||
} else if (iblockdata1.isAir()) {
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if (flag && a((IWorldReader) world, blockposition1, (EnumDirection) null) && world.isEmpty(blockposition.up(2))) {
|
||||
// CraftBukkit start - add event
|
||||
if (CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(i)), 2)) {
|
||||
world.setTypeAndData(blockposition, this.b.a((IBlockAccess) world, blockposition), 2);
|
||||
this.b(world, blockposition1, i);
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else if (i < 4) {
|
||||
j = random.nextInt(4);
|
||||
if (flag1) {
|
||||
++j;
|
||||
}
|
||||
|
||||
boolean flag2 = false;
|
||||
|
||||
for (int l = 0; l < j; ++l) {
|
||||
EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random);
|
||||
BlockPosition blockposition2 = blockposition.shift(enumdirection);
|
||||
|
||||
if (world.isEmpty(blockposition2) && world.isEmpty(blockposition2.down()) && a((IWorldReader) world, blockposition2, enumdirection.opposite())) {
|
||||
// CraftBukkit start - add event
|
||||
if (CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(i + 1)), 2)) {
|
||||
this.b(world, blockposition2, i + 1);
|
||||
flag2 = true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
if (flag2) {
|
||||
world.setTypeAndData(blockposition, this.b.a((IBlockAccess) world, blockposition), 2);
|
||||
} else {
|
||||
// CraftBukkit - add event
|
||||
if (CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(5)), 2)) {
|
||||
this.a(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
} else {
|
||||
// CraftBukkit - add event
|
||||
if (CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.getBlockData().set(BlockChorusFlower.AGE, Integer.valueOf(5)), 2)) {
|
||||
this.a(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void b(World world, BlockPosition blockposition, int i) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockChorusFlower.AGE, i), 2);
|
||||
world.triggerEffect(1033, blockposition, 0);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.getBlockData().set(BlockChorusFlower.AGE, 5), 2);
|
||||
world.triggerEffect(1034, blockposition, 0);
|
||||
}
|
||||
|
||||
private static boolean a(IWorldReader iworldreader, BlockPosition blockposition, @Nullable EnumDirection enumdirection) {
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
EnumDirection enumdirection1;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
enumdirection1 = (EnumDirection) iterator.next();
|
||||
} while (enumdirection1 == enumdirection || iworldreader.isEmpty(blockposition.shift(enumdirection1)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection != EnumDirection.UP && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition.down());
|
||||
Block block = iblockdata1.getBlock();
|
||||
|
||||
if (block != this.b && block != Blocks.END_STONE) {
|
||||
if (!iblockdata1.isAir()) {
|
||||
return false;
|
||||
} else {
|
||||
boolean flag = false;
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EnumDirection enumdirection = (EnumDirection) iterator.next();
|
||||
IBlockData iblockdata2 = iworldreader.getType(blockposition.shift(enumdirection));
|
||||
|
||||
if (iblockdata2.getBlock() == this.b) {
|
||||
if (flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
flag = true;
|
||||
} else if (!iblockdata2.isAir()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack);
|
||||
a(world, blockposition, new ItemStack(this));
|
||||
}
|
||||
|
||||
protected ItemStack t(IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockChorusFlower.AGE);
|
||||
}
|
||||
|
||||
public static void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Random random, int i) {
|
||||
generatoraccess.setTypeAndData(blockposition, ((BlockChorusFruit) Blocks.CHORUS_PLANT).a((IBlockAccess) generatoraccess, blockposition), 2);
|
||||
a(generatoraccess, blockposition, random, blockposition, i, 0);
|
||||
}
|
||||
|
||||
private static void a(GeneratorAccess generatoraccess, BlockPosition blockposition, Random random, BlockPosition blockposition1, int i, int j) {
|
||||
BlockChorusFruit blockchorusfruit = (BlockChorusFruit) Blocks.CHORUS_PLANT;
|
||||
int k = random.nextInt(4) + 1;
|
||||
|
||||
if (j == 0) {
|
||||
++k;
|
||||
}
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
BlockPosition blockposition2 = blockposition.up(l + 1);
|
||||
|
||||
if (!a((IWorldReader) generatoraccess, blockposition2, (EnumDirection) null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
generatoraccess.setTypeAndData(blockposition2, blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition2), 2);
|
||||
generatoraccess.setTypeAndData(blockposition2.down(), blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition2.down()), 2);
|
||||
}
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
if (j < 4) {
|
||||
int i1 = random.nextInt(4);
|
||||
|
||||
if (j == 0) {
|
||||
++i1;
|
||||
}
|
||||
|
||||
for (int j1 = 0; j1 < i1; ++j1) {
|
||||
EnumDirection enumdirection = EnumDirection.EnumDirectionLimit.HORIZONTAL.a(random);
|
||||
BlockPosition blockposition3 = blockposition.up(k).shift(enumdirection);
|
||||
|
||||
if (Math.abs(blockposition3.getX() - blockposition1.getX()) < i && Math.abs(blockposition3.getZ() - blockposition1.getZ()) < i && generatoraccess.isEmpty(blockposition3) && generatoraccess.isEmpty(blockposition3.down()) && a((IWorldReader) generatoraccess, blockposition3, enumdirection.opposite())) {
|
||||
flag = true;
|
||||
generatoraccess.setTypeAndData(blockposition3, blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition3), 2);
|
||||
generatoraccess.setTypeAndData(blockposition3.shift(enumdirection.opposite()), blockchorusfruit.a((IBlockAccess) generatoraccess, blockposition3.shift(enumdirection.opposite())), 2);
|
||||
a(generatoraccess, blockposition3, random, blockposition1, i, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
generatoraccess.setTypeAndData(blockposition.up(k), (IBlockData) Blocks.CHORUS_FLOWER.getBlockData().set(BlockChorusFlower.AGE, 5), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
125
src/main/java/net/minecraft/server/BlockCocoa.java
Normal file
125
src/main/java/net/minecraft/server/BlockCocoa.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockCocoa extends BlockFacingHorizontal implements IBlockFragilePlantElement {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.T;
|
||||
protected static final VoxelShape[] b = new VoxelShape[] { Block.a(11.0D, 7.0D, 6.0D, 15.0D, 12.0D, 10.0D), Block.a(9.0D, 5.0D, 5.0D, 15.0D, 12.0D, 11.0D), Block.a(7.0D, 3.0D, 4.0D, 15.0D, 12.0D, 12.0D)};
|
||||
protected static final VoxelShape[] c = new VoxelShape[] { Block.a(1.0D, 7.0D, 6.0D, 5.0D, 12.0D, 10.0D), Block.a(1.0D, 5.0D, 5.0D, 7.0D, 12.0D, 11.0D), Block.a(1.0D, 3.0D, 4.0D, 9.0D, 12.0D, 12.0D)};
|
||||
protected static final VoxelShape[] o = new VoxelShape[] { Block.a(6.0D, 7.0D, 1.0D, 10.0D, 12.0D, 5.0D), Block.a(5.0D, 5.0D, 1.0D, 11.0D, 12.0D, 7.0D), Block.a(4.0D, 3.0D, 1.0D, 12.0D, 12.0D, 9.0D)};
|
||||
protected static final VoxelShape[] p = new VoxelShape[] { Block.a(6.0D, 7.0D, 11.0D, 10.0D, 12.0D, 15.0D), Block.a(5.0D, 5.0D, 9.0D, 11.0D, 12.0D, 15.0D), Block.a(4.0D, 3.0D, 7.0D, 12.0D, 12.0D, 15.0D)};
|
||||
|
||||
public BlockCocoa(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCocoa.FACING, EnumDirection.NORTH)).set(BlockCocoa.AGE, 0));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.cocoaModifier) * 5)) == 0) { // Spigot
|
||||
int i = (Integer) iblockdata.get(BlockCocoa.AGE);
|
||||
|
||||
if (i < 2) {
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, (IBlockData) iblockdata.set(BlockCocoa.AGE, i + 1), 2); // CraftBukkkit
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
Block block = iworldreader.getType(blockposition.shift((EnumDirection) iblockdata.get(BlockCocoa.FACING))).getBlock();
|
||||
|
||||
return block.a(TagsBlock.JUNGLE_LOGS);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
int i = (Integer) iblockdata.get(BlockCocoa.AGE);
|
||||
|
||||
switch ((EnumDirection) iblockdata.get(BlockCocoa.FACING)) {
|
||||
case SOUTH:
|
||||
return BlockCocoa.p[i];
|
||||
case NORTH:
|
||||
default:
|
||||
return BlockCocoa.o[i];
|
||||
case WEST:
|
||||
return BlockCocoa.c[i];
|
||||
case EAST:
|
||||
return BlockCocoa.b[i];
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
IBlockData iblockdata = this.getBlockData();
|
||||
World world = blockactioncontext.getWorld();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
EnumDirection[] aenumdirection = blockactioncontext.e();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (enumdirection.k().c()) {
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockCocoa.FACING, enumdirection);
|
||||
if (iblockdata.canPlace(world, blockposition)) {
|
||||
return iblockdata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == iblockdata.get(BlockCocoa.FACING) && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
int j = (Integer) iblockdata.get(BlockCocoa.AGE);
|
||||
byte b0 = 1;
|
||||
|
||||
if (j >= 2) {
|
||||
b0 = 3;
|
||||
}
|
||||
|
||||
for (int k = 0; k < b0; ++k) {
|
||||
a(world, blockposition, new ItemStack(Items.COCOA_BEANS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(Items.COCOA_BEANS);
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return (Integer) iblockdata.get(BlockCocoa.AGE) < 2;
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, (IBlockData) iblockdata.set(BlockCocoa.AGE, (Integer) iblockdata.get(BlockCocoa.AGE) + 1), 2); // CraftBukkit
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCocoa.FACING, BlockCocoa.AGE);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
229
src/main/java/net/minecraft/server/BlockCommand.java
Normal file
229
src/main/java/net/minecraft/server/BlockCommand.java
Normal file
@@ -0,0 +1,229 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockCommand extends BlockTileEntity {
|
||||
|
||||
private static final Logger c = LogManager.getLogger();
|
||||
public static final BlockStateDirection a = BlockDirectional.FACING;
|
||||
public static final BlockStateBoolean b = BlockProperties.b;
|
||||
|
||||
public BlockCommand(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockCommand.a, EnumDirection.NORTH)).set(BlockCommand.b, false));
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
TileEntityCommand tileentitycommand = new TileEntityCommand();
|
||||
|
||||
tileentitycommand.b(this == Blocks.CHAIN_COMMAND_BLOCK);
|
||||
return tileentitycommand;
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (!world.isClientSide) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand) {
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition);
|
||||
boolean flag1 = tileentitycommand.d();
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = flag1 ? 15 : 0;
|
||||
int current = flag ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
flag = eventRedstone.getNewCurrent() > 0;
|
||||
// CraftBukkit end
|
||||
|
||||
tileentitycommand.a(flag);
|
||||
if (!flag1 && !tileentitycommand.e() && tileentitycommand.j() != TileEntityCommand.Type.SEQUENCE) {
|
||||
if (flag) {
|
||||
tileentitycommand.h();
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand) {
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock();
|
||||
boolean flag = !UtilColor.b(commandblocklistenerabstract.getCommand());
|
||||
TileEntityCommand.Type tileentitycommand_type = tileentitycommand.j();
|
||||
boolean flag1 = tileentitycommand.f();
|
||||
|
||||
if (tileentitycommand_type == TileEntityCommand.Type.AUTO) {
|
||||
tileentitycommand.h();
|
||||
if (flag1) {
|
||||
this.a(iblockdata, world, blockposition, commandblocklistenerabstract, flag);
|
||||
} else if (tileentitycommand.k()) {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
|
||||
if (tileentitycommand.d() || tileentitycommand.e()) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
} else if (tileentitycommand_type == TileEntityCommand.Type.REDSTONE) {
|
||||
if (flag1) {
|
||||
this.a(iblockdata, world, blockposition, commandblocklistenerabstract, flag);
|
||||
} else if (tileentitycommand.k()) {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void a(IBlockData iblockdata, World world, BlockPosition blockposition, CommandBlockListenerAbstract commandblocklistenerabstract, boolean flag) {
|
||||
if (flag) {
|
||||
commandblocklistenerabstract.a(world);
|
||||
} else {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
|
||||
a(world, blockposition, (EnumDirection) iblockdata.get(BlockCommand.a));
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand && entityhuman.isCreativeAndOp()) {
|
||||
entityhuman.a((TileEntityCommand) tileentity);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
return tileentity instanceof TileEntityCommand ? ((TileEntityCommand) tileentity).getCommandBlock().i() : 0;
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityCommand) {
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock();
|
||||
|
||||
if (itemstack.hasName()) {
|
||||
commandblocklistenerabstract.setName(itemstack.getName());
|
||||
}
|
||||
|
||||
if (!world.isClientSide) {
|
||||
if (itemstack.b("BlockEntityTag") == null) {
|
||||
commandblocklistenerabstract.a(world.getGameRules().getBoolean("sendCommandFeedback"));
|
||||
tileentitycommand.b(this == Blocks.CHAIN_COMMAND_BLOCK);
|
||||
}
|
||||
|
||||
if (tileentitycommand.j() == TileEntityCommand.Type.SEQUENCE) {
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition);
|
||||
|
||||
tileentitycommand.a(flag);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockCommand.a, enumblockrotation.a((EnumDirection) iblockdata.get(BlockCommand.a)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockCommand.a)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCommand.a, BlockCommand.b);
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockCommand.a, blockactioncontext.d().opposite());
|
||||
}
|
||||
|
||||
private static void a(World world, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(blockposition);
|
||||
GameRules gamerules = world.getGameRules();
|
||||
|
||||
IBlockData iblockdata;
|
||||
int i;
|
||||
|
||||
for (i = gamerules.c("maxCommandChainLength"); i-- > 0; enumdirection = (EnumDirection) iblockdata.get(BlockCommand.a)) {
|
||||
blockposition_mutableblockposition.c(enumdirection);
|
||||
iblockdata = world.getType(blockposition_mutableblockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block != Blocks.CHAIN_COMMAND_BLOCK) {
|
||||
break;
|
||||
}
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(blockposition_mutableblockposition);
|
||||
|
||||
if (!(tileentity instanceof TileEntityCommand)) {
|
||||
break;
|
||||
}
|
||||
|
||||
TileEntityCommand tileentitycommand = (TileEntityCommand) tileentity;
|
||||
|
||||
if (tileentitycommand.j() != TileEntityCommand.Type.SEQUENCE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tileentitycommand.d() || tileentitycommand.e()) {
|
||||
CommandBlockListenerAbstract commandblocklistenerabstract = tileentitycommand.getCommandBlock();
|
||||
|
||||
if (tileentitycommand.h()) {
|
||||
if (!commandblocklistenerabstract.a(world)) {
|
||||
break;
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition_mutableblockposition, block);
|
||||
} else if (tileentitycommand.k()) {
|
||||
commandblocklistenerabstract.a(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i <= 0) {
|
||||
int j = Math.max(gamerules.c("maxCommandChainLength"), 0);
|
||||
|
||||
BlockCommand.c.warn("Command Block chain tried to execute more than {} steps!", j);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
92
src/main/java/net/minecraft/server/BlockConcretePowder.java
Normal file
92
src/main/java/net/minecraft/server/BlockConcretePowder.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.block.CraftBlockState;
|
||||
import org.bukkit.event.block.BlockFormEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockConcretePowder extends BlockFalling {
|
||||
|
||||
private final IBlockData a;
|
||||
|
||||
public BlockConcretePowder(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block.getBlockData();
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) {
|
||||
if (x(iblockdata1)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, this.a, 3); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
World world = blockactioncontext.getWorld();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
|
||||
// CraftBukkit start
|
||||
if (!x(world.getType(blockposition)) && !a((IBlockAccess) world, blockposition)) {
|
||||
return super.getPlacedState(blockactioncontext);
|
||||
}
|
||||
|
||||
// TODO: An event factory call for methods like this
|
||||
CraftBlockState blockState = CraftBlockState.getBlockState(world, blockposition);
|
||||
blockState.setData(this.a);
|
||||
|
||||
BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState);
|
||||
world.getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
return blockState.getHandle();
|
||||
}
|
||||
|
||||
return super.getPlacedState(blockactioncontext);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
private static boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
boolean flag = false;
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(blockposition);
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
IBlockData iblockdata = iblockaccess.getType(blockposition_mutableblockposition);
|
||||
|
||||
if (enumdirection != EnumDirection.DOWN || x(iblockdata)) {
|
||||
blockposition_mutableblockposition.g(blockposition).c(enumdirection);
|
||||
iblockdata = iblockaccess.getType(blockposition_mutableblockposition);
|
||||
if (x(iblockdata) && !Block.a(iblockdata.getCollisionShape(iblockaccess, blockposition), enumdirection.opposite())) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
private static boolean x(IBlockData iblockdata) {
|
||||
return iblockdata.s().a(TagsFluid.WATER);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
if (a((IBlockAccess) generatoraccess, blockposition)) {
|
||||
CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition);
|
||||
blockState.setData(this.a);
|
||||
|
||||
BlockFormEvent event = new BlockFormEvent(blockState.getBlock(), blockState);
|
||||
generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
return blockState.getHandle();
|
||||
}
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
67
src/main/java/net/minecraft/server/BlockCoral.java
Normal file
67
src/main/java/net/minecraft/server/BlockCoral.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockCoral extends Block {
|
||||
|
||||
private final Block a;
|
||||
|
||||
public BlockCoral(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!this.a((IBlockAccess) world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.a.getBlockData()).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, this.a.getBlockData(), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (!this.a((IBlockAccess) generatoraccess, blockposition)) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 60 + generatoraccess.m().nextInt(40));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
protected boolean a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
Fluid fluid = iblockaccess.getFluid(blockposition.shift(enumdirection));
|
||||
|
||||
if (fluid.a(TagsFluid.WATER)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
if (!this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition())) {
|
||||
blockactioncontext.getWorld().getBlockTickList().a(blockactioncontext.getClickPosition(), this, 60 + blockactioncontext.getWorld().m().nextInt(40));
|
||||
}
|
||||
|
||||
return this.getBlockData();
|
||||
}
|
||||
|
||||
protected boolean X_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return this.a;
|
||||
}
|
||||
}
|
||||
42
src/main/java/net/minecraft/server/BlockCoralFan.java
Normal file
42
src/main/java/net/minecraft/server/BlockCoralFan.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCoralFan extends BlockCoralFanAbstract {
|
||||
|
||||
private final Block a;
|
||||
|
||||
protected BlockCoralFan(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.a(iblockdata, (GeneratorAccess) world, blockposition);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!b_(iblockdata, world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.a.getBlockData().set(BlockCoralFan.b, false)).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.a.getBlockData().set(BlockCoralFan.b, false), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
} else {
|
||||
this.a(iblockdata, generatoraccess, blockposition);
|
||||
if ((Boolean) iblockdata.get(BlockCoralFan.b)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/main/java/net/minecraft/server/BlockCoralFanWall.java
Normal file
42
src/main/java/net/minecraft/server/BlockCoralFanWall.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCoralFanWall extends BlockCoralFanWallAbstract {
|
||||
|
||||
private final Block c;
|
||||
|
||||
protected BlockCoralFanWall(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.c = block;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.a(iblockdata, (GeneratorAccess) world, blockposition);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!b_(iblockdata, world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.c.getBlockData().set(BlockCoralFanWall.b, false).set(BlockCoralFanWall.a, iblockdata.get(BlockCoralFanWall.a))).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) this.c.getBlockData().set(BlockCoralFanWall.b, false)).set(BlockCoralFanWall.a, iblockdata.get(BlockCoralFanWall.a)), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection.opposite() == iblockdata.get(BlockCoralFanWall.a) && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
} else {
|
||||
if ((Boolean) iblockdata.get(BlockCoralFanWall.b)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
this.a(iblockdata, generatoraccess, blockposition);
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/main/java/net/minecraft/server/BlockCoralPlant.java
Normal file
47
src/main/java/net/minecraft/server/BlockCoralPlant.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCoralPlant extends BlockCoralBase {
|
||||
|
||||
private final Block c;
|
||||
protected static final VoxelShape a = Block.a(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
|
||||
|
||||
protected BlockCoralPlant(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.c = block;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.a(iblockdata, (GeneratorAccess) world, blockposition);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!b_(iblockdata, world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, this.c.getBlockData().set(BlockCoralPlant.b, false)).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.c.getBlockData().set(BlockCoralPlant.b, false), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
} else {
|
||||
this.a(iblockdata, generatoraccess, blockposition);
|
||||
if ((Boolean) iblockdata.get(BlockCoralPlant.b)) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCoralPlant.a;
|
||||
}
|
||||
}
|
||||
186
src/main/java/net/minecraft/server/BlockCrops.java
Normal file
186
src/main/java/net/minecraft/server/BlockCrops.java
Normal file
@@ -0,0 +1,186 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockCrops extends BlockPlant implements IBlockFragilePlantElement {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.W;
|
||||
private static final VoxelShape[] a = new VoxelShape[] { Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 4.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 10.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D)};
|
||||
|
||||
protected BlockCrops(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(this.d(), 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockCrops.a[(Integer) iblockdata.get(this.d())];
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.getBlock() == Blocks.FARMLAND;
|
||||
}
|
||||
|
||||
public BlockStateInteger d() {
|
||||
return BlockCrops.AGE;
|
||||
}
|
||||
|
||||
public int e() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
protected int k(IBlockData iblockdata) {
|
||||
return (Integer) iblockdata.get(this.d());
|
||||
}
|
||||
|
||||
public IBlockData setAge(int i) {
|
||||
return (IBlockData) this.getBlockData().set(this.d(), i);
|
||||
}
|
||||
|
||||
public boolean w(IBlockData iblockdata) {
|
||||
return (Integer) iblockdata.get(this.d()) >= this.e();
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
super.a(iblockdata, world, blockposition, random);
|
||||
if (world.isLightLevel(blockposition.up(), 9)) { // Paper
|
||||
int i = this.k(iblockdata);
|
||||
|
||||
if (i < this.e()) {
|
||||
float f = a((Block) this, (IBlockAccess) world, blockposition);
|
||||
|
||||
// Spigot start
|
||||
int modifier;
|
||||
if (this == Blocks.BEETROOTS) {
|
||||
modifier = world.spigotConfig.beetrootModifier;
|
||||
} else if (this == Blocks.CARROTS) {
|
||||
modifier = world.spigotConfig.carrotModifier;
|
||||
} else if (this == Blocks.POTATOES) {
|
||||
modifier = world.spigotConfig.potatoModifier;
|
||||
} else {
|
||||
modifier = world.spigotConfig.wheatModifier;
|
||||
}
|
||||
|
||||
if (random.nextInt((int) ((100.0F / modifier) * (25.0F / f)) + 1) == 0) {
|
||||
// Spigot end
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.setAge(i + 1), 2); // CraftBukkit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
int i = this.k(iblockdata) + this.a(world);
|
||||
int j = this.e();
|
||||
|
||||
if (i > j) {
|
||||
i = j;
|
||||
}
|
||||
|
||||
CraftEventFactory.handleBlockGrowEvent(world, blockposition, this.setAge(i), 2); // CraftBukkit
|
||||
}
|
||||
|
||||
protected int a(World world) {
|
||||
return MathHelper.nextInt(world.random, 2, 5);
|
||||
}
|
||||
|
||||
protected static float a(Block block, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
float f = 1.0F;
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
|
||||
for (int i = -1; i <= 1; ++i) {
|
||||
for (int j = -1; j <= 1; ++j) {
|
||||
float f1 = 0.0F;
|
||||
IBlockData iblockdata = iblockaccess.getType(blockposition1.a(i, 0, j));
|
||||
|
||||
if (iblockdata.getBlock() == Blocks.FARMLAND) {
|
||||
f1 = 1.0F;
|
||||
if ((Integer) iblockdata.get(BlockSoil.MOISTURE) > 0) {
|
||||
f1 = 3.0F;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 0 || j != 0) {
|
||||
f1 /= 4.0F;
|
||||
}
|
||||
|
||||
f += f1;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPosition blockposition2 = blockposition.north();
|
||||
BlockPosition blockposition3 = blockposition.south();
|
||||
BlockPosition blockposition4 = blockposition.west();
|
||||
BlockPosition blockposition5 = blockposition.east();
|
||||
boolean flag = block == iblockaccess.getType(blockposition4).getBlock() || block == iblockaccess.getType(blockposition5).getBlock();
|
||||
boolean flag1 = block == iblockaccess.getType(blockposition2).getBlock() || block == iblockaccess.getType(blockposition3).getBlock();
|
||||
|
||||
if (flag && flag1) {
|
||||
f /= 2.0F;
|
||||
} else {
|
||||
boolean flag2 = block == iblockaccess.getType(blockposition4.north()).getBlock() || block == iblockaccess.getType(blockposition5.north()).getBlock() || block == iblockaccess.getType(blockposition5.south()).getBlock() || block == iblockaccess.getType(blockposition4.south()).getBlock();
|
||||
|
||||
if (flag2) {
|
||||
f /= 2.0F;
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return (iworldreader.getLightLevel(blockposition, 0) >= 8 || iworldreader.e(blockposition)) && super.canPlace(iblockdata, iworldreader, blockposition);
|
||||
}
|
||||
|
||||
protected IMaterial f() {
|
||||
return Items.WHEAT_SEEDS;
|
||||
}
|
||||
|
||||
protected IMaterial g() {
|
||||
return Items.WHEAT;
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, 0);
|
||||
if (!world.isClientSide) {
|
||||
int j = this.k(iblockdata);
|
||||
|
||||
if (j >= this.e()) {
|
||||
int k = 3 + i;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
if (world.random.nextInt(2 * this.e()) <= j) {
|
||||
a(world, blockposition, new ItemStack(this.f()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return this.w(iblockdata) ? this.g() : this.f();
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(this.f());
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return !this.w(iblockdata);
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockCrops.AGE);
|
||||
}
|
||||
}
|
||||
25
src/main/java/net/minecraft/server/BlockData.java
Normal file
25
src/main/java/net/minecraft/server/BlockData.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||
|
||||
public class BlockData extends BlockDataAbstract<Block, IBlockData> implements IBlockData {
|
||||
|
||||
public BlockData(Block block, ImmutableMap<IBlockState<?>, Comparable<?>> immutablemap) {
|
||||
super(block, immutablemap);
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return (Block) this.e_;
|
||||
}
|
||||
|
||||
// Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time
|
||||
private CraftBlockData cachedCraftBlockData;
|
||||
|
||||
@Override
|
||||
public CraftBlockData createCraftBlockData() {
|
||||
if(cachedCraftBlockData == null) cachedCraftBlockData = CraftBlockData.createData(this);
|
||||
return (CraftBlockData) cachedCraftBlockData.clone();
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockDaylightDetector extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateInteger POWER = BlockProperties.al;
|
||||
public static final BlockStateBoolean b = BlockProperties.m;
|
||||
protected static final VoxelShape c = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 6.0D, 16.0D);
|
||||
|
||||
public BlockDaylightDetector(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDaylightDetector.POWER, 0)).set(BlockDaylightDetector.b, false));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockDaylightDetector.c;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Integer) iblockdata.get(BlockDaylightDetector.POWER);
|
||||
}
|
||||
|
||||
public static void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
if (world.worldProvider.g()) {
|
||||
int i = world.getBrightness(EnumSkyBlock.SKY, blockposition) - world.c();
|
||||
float f = world.c(1.0F);
|
||||
boolean flag = (Boolean) iblockdata.get(BlockDaylightDetector.b);
|
||||
|
||||
if (flag) {
|
||||
i = 15 - i;
|
||||
} else if (i > 0) {
|
||||
float f1 = f < 3.1415927F ? 0.0F : 6.2831855F;
|
||||
|
||||
f += (f1 - f) * 0.2F;
|
||||
i = Math.round((float) i * MathHelper.cos(f));
|
||||
}
|
||||
|
||||
i = MathHelper.clamp(i, 0, 15);
|
||||
if ((Integer) iblockdata.get(BlockDaylightDetector.POWER) != i) {
|
||||
i = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, blockposition, ((Integer) iblockdata.get(POWER)), i).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDaylightDetector.POWER, i), 3);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (entityhuman.dy()) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
IBlockData iblockdata1 = (IBlockData) iblockdata.a((IBlockState) BlockDaylightDetector.b);
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata1, 4);
|
||||
b(iblockdata1, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return super.interact(iblockdata, world, blockposition, entityhuman, enumhand, enumdirection, f, f1, f2);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityLightDetector();
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockDaylightDetector.POWER, BlockDaylightDetector.b);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
207
src/main/java/net/minecraft/server/BlockDiodeAbstract.java
Normal file
207
src/main/java/net/minecraft/server/BlockDiodeAbstract.java
Normal file
@@ -0,0 +1,207 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public abstract class BlockDiodeAbstract extends BlockFacingHorizontal {
|
||||
|
||||
protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 2.0D, 16.0D);
|
||||
public static final BlockStateBoolean c = BlockProperties.t;
|
||||
|
||||
protected BlockDiodeAbstract(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockDiodeAbstract.b;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return iworldreader.getType(blockposition.down()).q();
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!this.a((IWorldReader) world, blockposition, iblockdata)) {
|
||||
boolean flag = (Boolean) iblockdata.get(BlockDiodeAbstract.c);
|
||||
boolean flag1 = this.a(world, blockposition, iblockdata);
|
||||
|
||||
if (flag && !flag1) {
|
||||
// CraftBukkit start
|
||||
if (CraftEventFactory.callRedstoneChange(world, blockposition, 15, 0).getNewCurrent() != 0) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDiodeAbstract.c, false), 2);
|
||||
} else if (!flag) {
|
||||
// CraftBukkit start
|
||||
if (CraftEventFactory.callRedstoneChange(world, blockposition, 0, 15).getNewCurrent() != 15) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDiodeAbstract.c, true), 2);
|
||||
if (!flag1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.k(iblockdata), TickListPriority.HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return iblockdata.a(iblockaccess, blockposition, enumdirection);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return !(Boolean) iblockdata.get(BlockDiodeAbstract.c) ? 0 : (iblockdata.get(BlockDiodeAbstract.FACING) == enumdirection ? this.b(iblockaccess, blockposition, iblockdata) : 0);
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (iblockdata.canPlace(world, blockposition)) {
|
||||
this.c(world, blockposition, iblockdata);
|
||||
} else {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
world.setAir(blockposition);
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
world.applyPhysics(blockposition.shift(enumdirection), this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void c(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if (!this.a((IWorldReader) world, blockposition, iblockdata)) {
|
||||
boolean flag = (Boolean) iblockdata.get(BlockDiodeAbstract.c);
|
||||
boolean flag1 = this.a(world, blockposition, iblockdata);
|
||||
|
||||
if (flag != flag1 && !world.getBlockTickList().b(blockposition, this)) {
|
||||
TickListPriority ticklistpriority = TickListPriority.HIGH;
|
||||
|
||||
if (this.c((IBlockAccess) world, blockposition, iblockdata)) {
|
||||
ticklistpriority = TickListPriority.EXTREMELY_HIGH;
|
||||
} else if (flag) {
|
||||
ticklistpriority = TickListPriority.VERY_HIGH;
|
||||
}
|
||||
|
||||
world.getBlockTickList().a(blockposition, this, this.k(iblockdata), ticklistpriority);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return this.b(world, blockposition, iblockdata) > 0;
|
||||
}
|
||||
|
||||
protected int b(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING);
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
int i = world.getBlockFacePower(blockposition1, enumdirection);
|
||||
|
||||
if (i >= 15) {
|
||||
return i;
|
||||
} else {
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
return Math.max(i, iblockdata1.getBlock() == Blocks.REDSTONE_WIRE ? (Integer) iblockdata1.get(BlockRedstoneWire.POWER) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected int b(IWorldReader iworldreader, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING);
|
||||
EnumDirection enumdirection1 = enumdirection.e();
|
||||
EnumDirection enumdirection2 = enumdirection.f();
|
||||
|
||||
return Math.max(this.a(iworldreader, blockposition.shift(enumdirection1), enumdirection1), this.a(iworldreader, blockposition.shift(enumdirection2), enumdirection2));
|
||||
}
|
||||
|
||||
protected int a(IWorldReader iworldreader, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
return this.w(iblockdata) ? (block == Blocks.REDSTONE_BLOCK ? 15 : (block == Blocks.REDSTONE_WIRE ? (Integer) iblockdata.get(BlockRedstoneWire.POWER) : iworldreader.a(blockposition, enumdirection))) : 0;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockDiodeAbstract.FACING, blockactioncontext.f().opposite());
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (this.a(world, blockposition, iblockdata)) {
|
||||
world.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
this.d(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
this.a(world, blockposition);
|
||||
this.d(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(World world, BlockPosition blockposition) {}
|
||||
|
||||
protected void d(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING);
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection.opposite());
|
||||
|
||||
world.a(blockposition1, (Block) this, blockposition);
|
||||
world.a(blockposition1, (Block) this, enumdirection);
|
||||
}
|
||||
|
||||
protected boolean w(IBlockData iblockdata) {
|
||||
return iblockdata.isPowerSource();
|
||||
}
|
||||
|
||||
protected int b(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
public static boolean isDiode(IBlockData iblockdata) {
|
||||
return iblockdata.getBlock() instanceof BlockDiodeAbstract;
|
||||
}
|
||||
|
||||
public boolean c(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = ((EnumDirection) iblockdata.get(BlockDiodeAbstract.FACING)).opposite();
|
||||
IBlockData iblockdata1 = iblockaccess.getType(blockposition.shift(enumdirection));
|
||||
|
||||
return isDiode(iblockdata1) && iblockdata1.get(BlockDiodeAbstract.FACING) != enumdirection;
|
||||
}
|
||||
|
||||
protected abstract int k(IBlockData iblockdata);
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.DOWN ? EnumBlockFaceShape.SOLID : EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public abstract class BlockDirtSnowSpreadable extends BlockDirtSnow {
|
||||
|
||||
protected BlockDirtSnowSpreadable(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
private static boolean a(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
return iworldreader.getLightLevel(blockposition1) >= 4 || iworldreader.getType(blockposition1).b(iworldreader, blockposition1) < iworldreader.K();
|
||||
}
|
||||
|
||||
private static boolean b(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
return iworldreader.getLightLevel(blockposition1) >= 4 && iworldreader.getType(blockposition1).b(iworldreader, blockposition1) < iworldreader.K() && !iworldreader.getFluid(blockposition1).a(TagsFluid.WATER);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (this instanceof BlockGrass && world.paperConfig.grassUpdateRate != 1 && (world.paperConfig.grassUpdateRate < 1 || (MinecraftServer.currentTick + blockposition.hashCode()) % world.paperConfig.grassUpdateRate != 0)) { return; } // Paper
|
||||
if (!world.isClientSide) {
|
||||
if (!a((IWorldReader) world, blockposition)) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, Blocks.DIRT.getBlockData()).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData());
|
||||
} else {
|
||||
if (world.getLightLevel(blockposition.up()) >= 9) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
BlockPosition blockposition1 = blockposition.a(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
|
||||
|
||||
if (!world.p(blockposition1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == Blocks.DIRT && b(world, blockposition1)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, this.getBlockData()); // CraftBukkit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
155
src/main/java/net/minecraft/server/BlockDispenser.java
Normal file
155
src/main/java/net/minecraft/server/BlockDispenser.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockDispenser extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockDirectional.FACING;
|
||||
public static final BlockStateBoolean TRIGGERED = BlockProperties.w;
|
||||
public static final Map<Item, IDispenseBehavior> REGISTRY = (Map) SystemUtils.a((new Object2ObjectOpenHashMap()), (object2objectopenhashmap) -> { // CraftBukkit - decompile error
|
||||
object2objectopenhashmap.defaultReturnValue(new DispenseBehaviorItem());
|
||||
});
|
||||
public static boolean eventFired = false; // CraftBukkit
|
||||
|
||||
public static void a(IMaterial imaterial, IDispenseBehavior idispensebehavior) {
|
||||
BlockDispenser.REGISTRY.put(imaterial.getItem(), idispensebehavior);
|
||||
}
|
||||
|
||||
protected BlockDispenser(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDispenser.FACING, EnumDirection.NORTH)).set(BlockDispenser.TRIGGERED, false));
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityDispenser) {
|
||||
entityhuman.openContainer((TileEntityDispenser) tileentity);
|
||||
if (tileentity instanceof TileEntityDropper) {
|
||||
entityhuman.a(StatisticList.INSPECT_DROPPER);
|
||||
} else {
|
||||
entityhuman.a(StatisticList.INSPECT_DISPENSER);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispense(World world, BlockPosition blockposition) {
|
||||
SourceBlock sourceblock = new SourceBlock(world, blockposition);
|
||||
TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity();
|
||||
int i = tileentitydispenser.p();
|
||||
|
||||
if (i < 0) {
|
||||
world.triggerEffect(1001, blockposition, 0);
|
||||
} else {
|
||||
ItemStack itemstack = tileentitydispenser.getItem(i);
|
||||
IDispenseBehavior idispensebehavior = this.a(itemstack);
|
||||
|
||||
if (idispensebehavior != IDispenseBehavior.NONE) {
|
||||
eventFired = false; // CraftBukkit - reset event status
|
||||
tileentitydispenser.setItem(i, idispensebehavior.dispense(sourceblock, itemstack));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected IDispenseBehavior a(ItemStack itemstack) {
|
||||
return (IDispenseBehavior) BlockDispenser.REGISTRY.get(itemstack.getItem());
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition.up());
|
||||
boolean flag1 = (Boolean) iblockdata.get(BlockDispenser.TRIGGERED);
|
||||
|
||||
if (flag && !flag1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDispenser.TRIGGERED, true), 4);
|
||||
} else if (!flag && flag1) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDispenser.TRIGGERED, false), 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide) {
|
||||
this.dispense(world, blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityDispenser();
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockDispenser.FACING, blockactioncontext.d().opposite());
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityDispenser) {
|
||||
((TileEntityDispenser) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityDispenser) {
|
||||
InventoryUtils.dropInventory(world, blockposition, (TileEntityDispenser) tileentity);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public static IPosition a(ISourceBlock isourceblock) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
double d0 = isourceblock.getX() + 0.7D * (double) enumdirection.getAdjacentX();
|
||||
double d1 = isourceblock.getY() + 0.7D * (double) enumdirection.getAdjacentY();
|
||||
double d2 = isourceblock.getZ() + 0.7D * (double) enumdirection.getAdjacentZ();
|
||||
|
||||
return new Position(d0, d1, d2);
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return Container.a(world.getTileEntity(blockposition));
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockDispenser.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockDispenser.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockDispenser.FACING)));
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockDispenser.FACING, BlockDispenser.TRIGGERED);
|
||||
}
|
||||
}
|
||||
232
src/main/java/net/minecraft/server/BlockDoor.java
Normal file
232
src/main/java/net/minecraft/server/BlockDoor.java
Normal file
@@ -0,0 +1,232 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockDoor extends Block {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING;
|
||||
public static final BlockStateBoolean OPEN = BlockProperties.r;
|
||||
public static final BlockStateEnum<BlockPropertyDoorHinge> HINGE = BlockProperties.ar;
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
public static final BlockStateEnum<BlockPropertyDoubleBlockHalf> HALF = BlockProperties.P;
|
||||
protected static final VoxelShape q = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 3.0D);
|
||||
protected static final VoxelShape r = Block.a(0.0D, 0.0D, 13.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape s = Block.a(13.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
|
||||
protected static final VoxelShape t = Block.a(0.0D, 0.0D, 0.0D, 3.0D, 16.0D, 16.0D);
|
||||
|
||||
protected BlockDoor(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockDoor.FACING, EnumDirection.NORTH)).set(BlockDoor.OPEN, false)).set(BlockDoor.HINGE, BlockPropertyDoorHinge.LEFT)).set(BlockDoor.POWERED, false)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.LOWER));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockDoor.FACING);
|
||||
boolean flag = !(Boolean) iblockdata.get(BlockDoor.OPEN);
|
||||
boolean flag1 = iblockdata.get(BlockDoor.HINGE) == BlockPropertyDoorHinge.RIGHT;
|
||||
|
||||
switch (enumdirection) {
|
||||
case EAST:
|
||||
default:
|
||||
return flag ? BlockDoor.t : (flag1 ? BlockDoor.r : BlockDoor.q);
|
||||
case SOUTH:
|
||||
return flag ? BlockDoor.q : (flag1 ? BlockDoor.t : BlockDoor.s);
|
||||
case WEST:
|
||||
return flag ? BlockDoor.s : (flag1 ? BlockDoor.q : BlockDoor.r);
|
||||
case NORTH:
|
||||
return flag ? BlockDoor.r : (flag1 ? BlockDoor.s : BlockDoor.t);
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockDoor.HALF);
|
||||
|
||||
return enumdirection.k() == EnumDirection.EnumAxis.Y && blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER == (enumdirection == EnumDirection.UP) ? (iblockdata1.getBlock() == this && iblockdata1.get(BlockDoor.HALF) != blockpropertydoubleblockhalf ? (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) iblockdata.set(BlockDoor.FACING, iblockdata1.get(BlockDoor.FACING))).set(BlockDoor.OPEN, iblockdata1.get(BlockDoor.OPEN))).set(BlockDoor.HINGE, iblockdata1.get(BlockDoor.HINGE))).set(BlockDoor.POWERED, iblockdata1.get(BlockDoor.POWERED)) : Blocks.AIR.getBlockData()) : (blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER && enumdirection == EnumDirection.DOWN && !iblockdata.canPlace(generatoraccess, blockposition) ? Blocks.AIR.getBlockData() : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1));
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
super.a(world, entityhuman, blockposition, Blocks.AIR.getBlockData(), tileentity, itemstack);
|
||||
}
|
||||
|
||||
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) {
|
||||
BlockPropertyDoubleBlockHalf blockpropertydoubleblockhalf = (BlockPropertyDoubleBlockHalf) iblockdata.get(BlockDoor.HALF);
|
||||
boolean flag = blockpropertydoubleblockhalf == BlockPropertyDoubleBlockHalf.LOWER;
|
||||
BlockPosition blockposition1 = flag ? blockposition.up() : blockposition.down();
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata1.getBlock() == this && iblockdata1.get(BlockDoor.HALF) != blockpropertydoubleblockhalf) {
|
||||
world.setTypeAndData(blockposition1, Blocks.AIR.getBlockData(), 35);
|
||||
world.a(entityhuman, 2001, blockposition1, Block.getCombinedId(iblockdata1));
|
||||
if (!world.isClientSide && !entityhuman.u()) {
|
||||
if (flag) {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
} else {
|
||||
iblockdata1.a(world, blockposition1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.a(world, blockposition, iblockdata, entityhuman);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
switch (pathmode) {
|
||||
case LAND:
|
||||
return (Boolean) iblockdata.get(BlockDoor.OPEN);
|
||||
case WATER:
|
||||
return false;
|
||||
case AIR:
|
||||
return (Boolean) iblockdata.get(BlockDoor.OPEN);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private int d() {
|
||||
return this.material == Material.ORE ? 1011 : 1012;
|
||||
}
|
||||
|
||||
private int e() {
|
||||
return this.material == Material.ORE ? 1005 : 1006;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
|
||||
if (blockposition.getY() < 255 && blockactioncontext.getWorld().getType(blockposition.up()).a(blockactioncontext)) {
|
||||
World world = blockactioncontext.getWorld();
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition.up());
|
||||
|
||||
return (IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.getBlockData().set(BlockDoor.FACING, blockactioncontext.f())).set(BlockDoor.HINGE, this.b(blockactioncontext))).set(BlockDoor.POWERED, flag)).set(BlockDoor.OPEN, flag)).set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.LOWER);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
world.setTypeAndData(blockposition.up(), (IBlockData) iblockdata.set(BlockDoor.HALF, BlockPropertyDoubleBlockHalf.UPPER), 3);
|
||||
}
|
||||
|
||||
private BlockPropertyDoorHinge b(BlockActionContext blockactioncontext) {
|
||||
World world = blockactioncontext.getWorld();
|
||||
BlockPosition blockposition = blockactioncontext.getClickPosition();
|
||||
EnumDirection enumdirection = blockactioncontext.f();
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
EnumDirection enumdirection1 = enumdirection.f();
|
||||
IBlockData iblockdata = world.getType(blockposition.shift(enumdirection1));
|
||||
IBlockData iblockdata1 = world.getType(blockposition1.shift(enumdirection1));
|
||||
EnumDirection enumdirection2 = enumdirection.e();
|
||||
IBlockData iblockdata2 = world.getType(blockposition.shift(enumdirection2));
|
||||
IBlockData iblockdata3 = world.getType(blockposition1.shift(enumdirection2));
|
||||
int i = (iblockdata.k() ? -1 : 0) + (iblockdata1.k() ? -1 : 0) + (iblockdata2.k() ? 1 : 0) + (iblockdata3.k() ? 1 : 0);
|
||||
boolean flag = iblockdata.getBlock() == this && iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER;
|
||||
boolean flag1 = iblockdata2.getBlock() == this && iblockdata2.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER;
|
||||
|
||||
if ((!flag || flag1) && i <= 0) {
|
||||
if ((!flag1 || flag) && i >= 0) {
|
||||
int j = enumdirection.getAdjacentX();
|
||||
int k = enumdirection.getAdjacentZ();
|
||||
float f = blockactioncontext.m();
|
||||
float f1 = blockactioncontext.o();
|
||||
|
||||
return (j >= 0 || f1 >= 0.5F) && (j <= 0 || f1 <= 0.5F) && (k >= 0 || f <= 0.5F) && (k <= 0 || f >= 0.5F) ? BlockPropertyDoorHinge.LEFT : BlockPropertyDoorHinge.RIGHT;
|
||||
} else {
|
||||
return BlockPropertyDoorHinge.LEFT;
|
||||
}
|
||||
} else {
|
||||
return BlockPropertyDoorHinge.RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (this.material == Material.ORE) {
|
||||
return false;
|
||||
} else {
|
||||
iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockDoor.OPEN);
|
||||
world.setTypeAndData(blockposition, iblockdata, 10);
|
||||
world.a(entityhuman, (Boolean) iblockdata.get(BlockDoor.OPEN) ? this.e() : this.d(), blockposition, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDoor(World world, BlockPosition blockposition, boolean flag) {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
|
||||
if (iblockdata.getBlock() == this && (Boolean) iblockdata.get(BlockDoor.OPEN) != flag) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockDoor.OPEN, flag), 10);
|
||||
this.b(world, blockposition, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
BlockPosition otherHalf = blockposition.shift(iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? EnumDirection.UP : EnumDirection.DOWN);
|
||||
|
||||
org.bukkit.World bworld = world.getWorld();
|
||||
org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
org.bukkit.block.Block blockTop = bworld.getBlockAt(otherHalf.getX(), otherHalf.getY(), otherHalf.getZ());
|
||||
|
||||
int power = bukkitBlock.getBlockPower();
|
||||
int powerTop = blockTop.getBlockPower();
|
||||
if (powerTop > power) power = powerTop;
|
||||
int oldPower = (Boolean) iblockdata.get(BlockDoor.POWERED) ? 15 : 0;
|
||||
|
||||
if (oldPower == 0 ^ power == 0) {
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
boolean flag = eventRedstone.getNewCurrent() > 0;
|
||||
// CraftBukkit end
|
||||
if (flag != (Boolean) iblockdata.get(BlockDoor.OPEN)) {
|
||||
this.b(world, blockposition, flag);
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) ((IBlockData) iblockdata.set(BlockDoor.POWERED, flag)).set(BlockDoor.OPEN, flag), 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition.down());
|
||||
|
||||
return iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.LOWER ? iblockdata1.q() : iblockdata1.getBlock() == this;
|
||||
}
|
||||
|
||||
private void b(World world, BlockPosition blockposition, boolean flag) {
|
||||
world.a((EntityHuman) null, flag ? this.e() : this.d(), blockposition, 0);
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return (IMaterial) (iblockdata.get(BlockDoor.HALF) == BlockPropertyDoubleBlockHalf.UPPER ? Items.AIR : super.getDropType(iblockdata, world, blockposition, i));
|
||||
}
|
||||
|
||||
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
|
||||
return EnumPistonReaction.DESTROY;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return (IBlockData) iblockdata.set(BlockDoor.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockDoor.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return enumblockmirror == EnumBlockMirror.NONE ? iblockdata : (IBlockData) iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockDoor.FACING))).a((IBlockState) BlockDoor.HINGE);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockDoor.HALF, BlockDoor.FACING, BlockDoor.OPEN, BlockDoor.HINGE, BlockDoor.POWERED);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
81
src/main/java/net/minecraft/server/BlockDragonEgg.java
Normal file
81
src/main/java/net/minecraft/server/BlockDragonEgg.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit
|
||||
|
||||
public class BlockDragonEgg extends BlockFalling {
|
||||
|
||||
protected static final VoxelShape a = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 16.0D, 15.0D);
|
||||
|
||||
public BlockDragonEgg(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockDragonEgg.a;
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
private void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
BlockPosition blockposition1 = blockposition.a(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16));
|
||||
|
||||
if (world.getType(blockposition1).isAir()) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block from = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
org.bukkit.block.Block to = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ());
|
||||
BlockFromToEvent event = new BlockFromToEvent(from, to);
|
||||
org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockposition1 = new BlockPosition(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ());
|
||||
// CraftBukkit end
|
||||
if (world.isClientSide) {
|
||||
for (int j = 0; j < 128; ++j) {
|
||||
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) blockposition1.getX() + (double) (blockposition.getX() - blockposition1.getX()) * d0 + (world.random.nextDouble() - 0.5D) + 0.5D;
|
||||
double d2 = (double) blockposition1.getY() + (double) (blockposition.getY() - blockposition1.getY()) * d0 + world.random.nextDouble() - 0.5D;
|
||||
double d3 = (double) blockposition1.getZ() + (double) (blockposition.getZ() - blockposition1.getZ()) * d0 + (world.random.nextDouble() - 0.5D) + 0.5D;
|
||||
|
||||
world.addParticle(Particles.K, d1, d2, d3, (double) f, (double) f1, (double) f2);
|
||||
}
|
||||
} else {
|
||||
world.setTypeAndData(blockposition1, iblockdata, 2);
|
||||
world.setAir(blockposition);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
72
src/main/java/net/minecraft/server/BlockDropper.java
Normal file
72
src/main/java/net/minecraft/server/BlockDropper.java
Normal file
@@ -0,0 +1,72 @@
|
||||
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 static final IDispenseBehavior c = new DispenseBehaviorItem();
|
||||
|
||||
public BlockDropper(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
protected IDispenseBehavior a(ItemStack itemstack) {
|
||||
return BlockDropper.c;
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityDropper();
|
||||
}
|
||||
|
||||
public void dispense(World world, BlockPosition blockposition) {
|
||||
SourceBlock sourceblock = new SourceBlock(world, blockposition);
|
||||
TileEntityDispenser tileentitydispenser = (TileEntityDispenser) sourceblock.getTileEntity();
|
||||
int i = tileentitydispenser.p();
|
||||
|
||||
if (i < 0) {
|
||||
world.triggerEffect(1001, blockposition, 0);
|
||||
} else {
|
||||
ItemStack itemstack = tileentitydispenser.getItem(i);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
EnumDirection enumdirection = (EnumDirection) world.getType(blockposition).get(BlockDropper.FACING);
|
||||
IInventory iinventory = TileEntityHopper.a(world, blockposition.shift(enumdirection));
|
||||
ItemStack itemstack1;
|
||||
|
||||
if (iinventory == null) {
|
||||
itemstack1 = BlockDropper.c.dispense(sourceblock, itemstack);
|
||||
} else {
|
||||
// CraftBukkit start - Fire event when pushing items into other inventories
|
||||
CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.cloneItemStack().cloneAndSubtract(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(tileentitydispenser, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection.opposite());
|
||||
if (event.getItem().equals(oitemstack) && itemstack1.isEmpty()) {
|
||||
// CraftBukkit end
|
||||
itemstack1 = itemstack.cloneItemStack();
|
||||
itemstack1.subtract(1);
|
||||
} else {
|
||||
itemstack1 = itemstack.cloneItemStack();
|
||||
}
|
||||
}
|
||||
|
||||
tileentitydispenser.setItem(i, itemstack1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/main/java/net/minecraft/server/BlockEnderPortal.java
Normal file
49
src/main/java/net/minecraft/server/BlockEnderPortal.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit
|
||||
|
||||
public class BlockEnderPortal extends BlockTileEntity {
|
||||
|
||||
protected static final VoxelShape a = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 12.0D, 16.0D);
|
||||
|
||||
protected BlockEnderPortal(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityEnderPortal();
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockEnderPortal.a;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!world.isClientSide && !entity.isPassenger() && !entity.isVehicle() && entity.bm() && VoxelShapes.c(VoxelShapes.a(entity.getBoundingBox().d((double) (-blockposition.getX()), (double) (-blockposition.getY()), (double) (-blockposition.getZ()))), iblockdata.getShape(world, blockposition), OperatorBoolean.AND)) {
|
||||
// CraftBukkit start - Entity in portal
|
||||
EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
entity.a(DimensionManager.THE_END);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
459
src/main/java/net/minecraft/server/BlockFire.java
Normal file
459
src/main/java/net/minecraft/server/BlockFire.java
Normal file
@@ -0,0 +1,459 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import com.destroystokyo.paper.event.block.TNTPrimeEvent; // Paper - TNTPrimeEvent
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.event.block.BlockBurnEvent;
|
||||
import org.bukkit.event.block.BlockFadeEvent;
|
||||
import org.bukkit.event.block.BlockSpreadEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockFire extends Block {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.X;
|
||||
public static final BlockStateBoolean NORTH = BlockSprawling.a;
|
||||
public static final BlockStateBoolean EAST = BlockSprawling.b;
|
||||
public static final BlockStateBoolean SOUTH = BlockSprawling.c;
|
||||
public static final BlockStateBoolean WEST = BlockSprawling.o;
|
||||
public static final BlockStateBoolean UPPER = BlockSprawling.p;
|
||||
private static final Map<EnumDirection, BlockStateBoolean> r = (Map) BlockSprawling.r.entrySet().stream().filter((entry) -> {
|
||||
return entry.getKey() != EnumDirection.DOWN;
|
||||
}).collect(SystemUtils.a());
|
||||
private final Object2IntMap<Block> flameChances = new Object2IntOpenHashMap();
|
||||
private final Object2IntMap<Block> t = new Object2IntOpenHashMap();
|
||||
|
||||
protected BlockFire(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockFire.AGE, 0)).set(BlockFire.NORTH, false)).set(BlockFire.EAST, false)).set(BlockFire.SOUTH, false)).set(BlockFire.WEST, false)).set(BlockFire.UPPER, false));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return VoxelShapes.a();
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
// CraftBukkit start
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
CraftBlockState blockState = CraftBlockState.getBlockState(generatoraccess, blockposition);
|
||||
blockState.setData(Blocks.AIR.getBlockData());
|
||||
|
||||
BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState);
|
||||
generatoraccess.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
return blockState.getHandle();
|
||||
}
|
||||
}
|
||||
return this.a((IBlockAccess) generatoraccess, blockposition).set(BlockFire.AGE, iblockdata.get(BlockFire.AGE));
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return this.a((IBlockAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition());
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
IBlockData iblockdata = iblockaccess.getType(blockposition.down());
|
||||
|
||||
if (!iblockdata.q() && !this.k(iblockdata)) {
|
||||
IBlockData iblockdata1 = this.getBlockData();
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
BlockStateBoolean blockstateboolean = (BlockStateBoolean) BlockFire.r.get(enumdirection);
|
||||
|
||||
if (blockstateboolean != null) {
|
||||
iblockdata1 = (IBlockData) iblockdata1.set(blockstateboolean, this.k(iblockaccess.getType(blockposition.shift(enumdirection))));
|
||||
}
|
||||
}
|
||||
|
||||
return iblockdata1;
|
||||
} else {
|
||||
return this.getBlockData();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
return iworldreader.getType(blockposition.down()).q() || this.d(iworldreader, blockposition);
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 30;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.getGameRules().getBoolean("doFireTick")) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit - invalid place location
|
||||
}
|
||||
|
||||
Block block = world.getType(blockposition.down()).getBlock();
|
||||
boolean flag = world.worldProvider instanceof WorldProviderTheEnd && block == Blocks.BEDROCK || block == Blocks.NETHERRACK || block == Blocks.MAGMA_BLOCK;
|
||||
int i = (Integer) iblockdata.get(BlockFire.AGE);
|
||||
|
||||
if (!flag && world.isRaining() && this.a(world, blockposition) && random.nextFloat() < 0.2F + (float) i * 0.03F) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit - extinguished by rain
|
||||
} else {
|
||||
int j = Math.min(15, i + random.nextInt(3) / 2);
|
||||
|
||||
if (i != j) {
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockFire.AGE, j);
|
||||
world.setTypeAndData(blockposition, iblockdata, 4);
|
||||
}
|
||||
|
||||
if (!flag) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world) + random.nextInt(10));
|
||||
if (!this.d(world, blockposition)) {
|
||||
if (!world.getType(blockposition.down()).q() || i > 3) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (i == 15 && random.nextInt(4) == 0 && !this.k(world.getType(blockposition.down()))) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag1 = world.x(blockposition);
|
||||
int k = flag1 ? -50 : 0;
|
||||
|
||||
// CraftBukkit start - add source blockposition to burn calls
|
||||
this.a(world, blockposition.east(), 300 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.west(), 300 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.down(), 250 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.up(), 250 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.north(), 300 + k, random, i, blockposition);
|
||||
this.a(world, blockposition.south(), 300 + k, random, i, blockposition);
|
||||
// CraftBukkit end
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
|
||||
|
||||
for (int l = -1; l <= 1; ++l) {
|
||||
for (int i1 = -1; i1 <= 1; ++i1) {
|
||||
for (int j1 = -1; j1 <= 4; ++j1) {
|
||||
if (l != 0 || j1 != 0 || i1 != 0) {
|
||||
int k1 = 100;
|
||||
|
||||
if (j1 > 1) {
|
||||
k1 += (j1 - 1) * 100;
|
||||
}
|
||||
|
||||
blockposition_mutableblockposition.g(blockposition).d(l, j1, i1);
|
||||
if (!world.isLoaded(blockposition_mutableblockposition)) continue; // Paper
|
||||
int l1 = this.a((IWorldReader) world, (BlockPosition) blockposition_mutableblockposition);
|
||||
|
||||
if (l1 > 0) {
|
||||
int i2 = (l1 + 40 + world.getDifficulty().a() * 7) / (i + 30);
|
||||
|
||||
if (flag1) {
|
||||
i2 /= 2;
|
||||
}
|
||||
|
||||
if (i2 > 0 && random.nextInt(k1) <= i2 && (!world.isRaining() || !this.a(world, (BlockPosition) blockposition_mutableblockposition))) {
|
||||
int j2 = Math.min(15, i + random.nextInt(5) / 4);
|
||||
|
||||
// CraftBukkit start - Call to stop spread of fire
|
||||
if (world.getType(blockposition_mutableblockposition) != Blocks.FIRE) {
|
||||
if (CraftEventFactory.callBlockIgniteEvent(world, blockposition_mutableblockposition, blockposition).isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition_mutableblockposition, (IBlockData) this.a((IBlockAccess) world, (BlockPosition) blockposition_mutableblockposition).set(BlockFire.AGE, j2), 3); // CraftBukkit
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean a(World world, BlockPosition blockposition) {
|
||||
return world.isRainingAt(blockposition) || world.isRainingAt(blockposition.west()) || world.isRainingAt(blockposition.east()) || world.isRainingAt(blockposition.north()) || world.isRainingAt(blockposition.south());
|
||||
}
|
||||
|
||||
private int f(Block block) {
|
||||
return this.t.getInt(block);
|
||||
}
|
||||
|
||||
private int g(Block block) {
|
||||
return this.flameChances.getInt(block);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition, int i, Random random, int j, BlockPosition sourceposition) { // CraftBukkit add sourceposition
|
||||
// Paper start
|
||||
final IBlockData iblockdata = world.getTypeIfLoaded(blockposition);
|
||||
if (iblockdata == null) return;
|
||||
int k = this.f(iblockdata.getBlock());
|
||||
// Paper end
|
||||
|
||||
if (random.nextInt(i) < k) {
|
||||
//IBlockData iblockdata = world.getType(blockposition); // Paper
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
org.bukkit.block.Block sourceBlock = world.getWorld().getBlockAt(sourceposition.getX(), sourceposition.getY(), sourceposition.getZ());
|
||||
|
||||
BlockBurnEvent event = new BlockBurnEvent(theBlock, sourceBlock);
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (random.nextInt(j + 10) < 5 && !world.isRainingAt(blockposition)) {
|
||||
int l = Math.min(j + random.nextInt(5) / 4, 15);
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) this.a((IBlockAccess) world, blockposition).set(BlockFire.AGE, l), 3);
|
||||
} else {
|
||||
if(iblockdata.getBlock() != Blocks.TNT) world.setAir(blockposition); // Paper - TNTPrimeEvent - We might be cancelling it below, move the setAir down
|
||||
}
|
||||
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block instanceof BlockTNT) {
|
||||
// Paper start - TNTPrimeEvent
|
||||
org.bukkit.block.Block tntBlock = MCUtil.toBukkitBlock(world, blockposition);
|
||||
if (!new TNTPrimeEvent(tntBlock, TNTPrimeEvent.PrimeReason.FIRE, null).callEvent()) {
|
||||
return;
|
||||
}
|
||||
world.setAir(blockposition); // setair after non cancelled event, it would usually be air by now
|
||||
// Paper end
|
||||
((BlockTNT) block).a(world, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean d(IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (this.k(iblockaccess.getType(blockposition.shift(enumdirection)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int a(IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
if (!iworldreader.isEmpty(blockposition)) {
|
||||
return 0;
|
||||
} else {
|
||||
int i = 0;
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int j = aenumdirection.length;
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
EnumDirection enumdirection = aenumdirection[k];
|
||||
|
||||
// Paper start
|
||||
final IBlockData type = ((World)iworldreader).getTypeIfLoaded(blockposition.shift(enumdirection));
|
||||
if (type == null) continue;
|
||||
i = Math.max(this.g(type.getBlock()), i);
|
||||
// Paper end
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean j() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean k(IBlockData iblockdata) {
|
||||
return this.g(iblockdata.getBlock()) > 0;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (iblockdata1.getBlock() != iblockdata.getBlock()) {
|
||||
if (world.worldProvider.getDimensionManager() != DimensionManager.OVERWORLD && world.worldProvider.getDimensionManager() != DimensionManager.NETHER || !((BlockPortal) Blocks.NETHER_PORTAL).a((GeneratorAccess) world, blockposition)) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
fireExtinguished(world, blockposition); // CraftBukkit - fuel block broke
|
||||
} else {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world) + world.random.nextInt(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockFire.AGE, BlockFire.NORTH, BlockFire.EAST, BlockFire.SOUTH, BlockFire.WEST, BlockFire.UPPER);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public void a(Block block, int i, int j) {
|
||||
this.flameChances.put(block, i);
|
||||
this.t.put(block, j);
|
||||
}
|
||||
|
||||
public static void d() {
|
||||
BlockFire blockfire = (BlockFire) Blocks.FIRE;
|
||||
|
||||
blockfire.a(Blocks.OAK_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_PLANKS, 5, 20);
|
||||
blockfire.a(Blocks.OAK_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_SLAB, 5, 20);
|
||||
blockfire.a(Blocks.OAK_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_FENCE_GATE, 5, 20);
|
||||
blockfire.a(Blocks.OAK_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_FENCE, 5, 20);
|
||||
blockfire.a(Blocks.OAK_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.BIRCH_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.SPRUCE_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.JUNGLE_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.ACACIA_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.DARK_OAK_STAIRS, 5, 20);
|
||||
blockfire.a(Blocks.OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.SPRUCE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.BIRCH_LOG, 5, 5);
|
||||
blockfire.a(Blocks.JUNGLE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.ACACIA_LOG, 5, 5);
|
||||
blockfire.a(Blocks.DARK_OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_SPRUCE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_BIRCH_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_JUNGLE_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_ACACIA_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_DARK_OAK_LOG, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_SPRUCE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_BIRCH_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_JUNGLE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_ACACIA_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.STRIPPED_DARK_OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.SPRUCE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.BIRCH_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.JUNGLE_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.ACACIA_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.DARK_OAK_WOOD, 5, 5);
|
||||
blockfire.a(Blocks.OAK_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.SPRUCE_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.BIRCH_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.JUNGLE_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.ACACIA_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.DARK_OAK_LEAVES, 30, 60);
|
||||
blockfire.a(Blocks.BOOKSHELF, 30, 20);
|
||||
blockfire.a(Blocks.TNT, 15, 100);
|
||||
blockfire.a(Blocks.GRASS, 60, 100);
|
||||
blockfire.a(Blocks.FERN, 60, 100);
|
||||
blockfire.a(Blocks.DEAD_BUSH, 60, 100);
|
||||
blockfire.a(Blocks.SUNFLOWER, 60, 100);
|
||||
blockfire.a(Blocks.LILAC, 60, 100);
|
||||
blockfire.a(Blocks.ROSE_BUSH, 60, 100);
|
||||
blockfire.a(Blocks.PEONY, 60, 100);
|
||||
blockfire.a(Blocks.TALL_GRASS, 60, 100);
|
||||
blockfire.a(Blocks.LARGE_FERN, 60, 100);
|
||||
blockfire.a(Blocks.DANDELION, 60, 100);
|
||||
blockfire.a(Blocks.POPPY, 60, 100);
|
||||
blockfire.a(Blocks.BLUE_ORCHID, 60, 100);
|
||||
blockfire.a(Blocks.ALLIUM, 60, 100);
|
||||
blockfire.a(Blocks.AZURE_BLUET, 60, 100);
|
||||
blockfire.a(Blocks.RED_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.ORANGE_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.WHITE_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.PINK_TULIP, 60, 100);
|
||||
blockfire.a(Blocks.OXEYE_DAISY, 60, 100);
|
||||
blockfire.a(Blocks.WHITE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.ORANGE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.MAGENTA_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.LIGHT_BLUE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.YELLOW_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.LIME_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.PINK_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.GRAY_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.LIGHT_GRAY_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.CYAN_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.PURPLE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.BLUE_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.BROWN_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.GREEN_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.RED_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.BLACK_WOOL, 30, 60);
|
||||
blockfire.a(Blocks.VINE, 15, 100);
|
||||
blockfire.a(Blocks.COAL_BLOCK, 5, 5);
|
||||
blockfire.a(Blocks.HAY_BLOCK, 60, 20);
|
||||
blockfire.a(Blocks.WHITE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.ORANGE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.MAGENTA_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.LIGHT_BLUE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.YELLOW_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.LIME_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.PINK_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.GRAY_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.LIGHT_GRAY_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.CYAN_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.PURPLE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.BLUE_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.BROWN_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.GREEN_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.RED_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.BLACK_CARPET, 60, 20);
|
||||
blockfire.a(Blocks.DRIED_KELP_BLOCK, 30, 60);
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
private void fireExtinguished(GeneratorAccess world, BlockPosition position) {
|
||||
if (!CraftEventFactory.callBlockFadeEvent(world, position, Blocks.AIR.getBlockData()).isCancelled()) {
|
||||
world.setAir(position);
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
186
src/main/java/net/minecraft/server/BlockFluids.java
Normal file
186
src/main/java/net/minecraft/server/BlockFluids.java
Normal file
@@ -0,0 +1,186 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockFluids extends Block implements IFluidSource {
|
||||
|
||||
public static final BlockStateInteger LEVEL = BlockProperties.ah;
|
||||
protected final FluidTypeFlowing b;
|
||||
private final List<Fluid> c;
|
||||
private final Map<IBlockData, VoxelShape> o = Maps.newIdentityHashMap();
|
||||
|
||||
protected BlockFluids(FluidTypeFlowing fluidtypeflowing, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.b = fluidtypeflowing;
|
||||
this.c = Lists.newArrayList();
|
||||
this.c.add(fluidtypeflowing.a(false));
|
||||
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
this.c.add(fluidtypeflowing.a(8 - i, false));
|
||||
}
|
||||
|
||||
this.c.add(fluidtypeflowing.a(8, true));
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockFluids.LEVEL, 0));
|
||||
}
|
||||
|
||||
public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
world.getFluid(blockposition).b(world, blockposition, random);
|
||||
}
|
||||
|
||||
public boolean a_(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, PathMode pathmode) {
|
||||
return !this.b.a(TagsFluid.LAVA);
|
||||
}
|
||||
|
||||
public Fluid h(IBlockData iblockdata) {
|
||||
int i = (Integer) iblockdata.get(BlockFluids.LEVEL);
|
||||
|
||||
return (Fluid) this.c.get(Math.min(i, 8));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCollidable(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
Fluid fluid = iblockaccess.getFluid(blockposition.up());
|
||||
|
||||
return fluid.c().a((FluidType) this.b) ? VoxelShapes.b() : (VoxelShape) this.o.computeIfAbsent(iblockdata, (iblockdata1) -> {
|
||||
Fluid fluid1 = iblockdata1.s();
|
||||
|
||||
return VoxelShapes.create(0.0D, 0.0D, 0.0D, 1.0D, (double) fluid1.getHeight(), 1.0D);
|
||||
});
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.INVISIBLE;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return this.b.a(iworldreader);
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (this.a(world, blockposition, iblockdata)) {
|
||||
world.getFluidTickList().a(blockposition, iblockdata.s().c(), this.getFlowSpeed(world, blockposition)); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Paper start - Get flow speed. Throttle if its water and flowing adjacent to lava
|
||||
public int getFlowSpeed(World world, BlockPosition blockposition) {
|
||||
if (this.material == Material.WATER) {
|
||||
if (
|
||||
world.getMaterialIfLoaded(blockposition.north(1)) == Material.LAVA ||
|
||||
world.getMaterialIfLoaded(blockposition.south(1)) == Material.LAVA ||
|
||||
world.getMaterialIfLoaded(blockposition.west(1)) == Material.LAVA ||
|
||||
world.getMaterialIfLoaded(blockposition.east(1)) == Material.LAVA
|
||||
) {
|
||||
return world.paperConfig.waterOverLavaFlowSpeed;
|
||||
}
|
||||
}
|
||||
return this.a(world);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (iblockdata.s().d() || iblockdata1.s().d()) {
|
||||
generatoraccess.getFluidTickList().a(blockposition, iblockdata.s().c(), this.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (this.a(world, blockposition, iblockdata)) {
|
||||
world.getFluidTickList().a(blockposition, iblockdata.s().c(), this.getFlowSpeed(world, blockposition)); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if (this.b.a(TagsFluid.LAVA)) {
|
||||
boolean flag = false;
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (enumdirection != EnumDirection.DOWN && world.getFluid(blockposition.shift(enumdirection)).a(TagsFluid.WATER)) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
Fluid fluid = world.getFluid(blockposition);
|
||||
|
||||
if (fluid.d()) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, Blocks.OBSIDIAN.getBlockData())) {
|
||||
this.fizz(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fluid.getHeight() >= 0.44444445F) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(world, blockposition, Blocks.COBBLESTONE.getBlockData())) {
|
||||
this.fizz(world, blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void fizz(GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
double d0 = (double) blockposition.getX();
|
||||
double d1 = (double) blockposition.getY();
|
||||
double d2 = (double) blockposition.getZ();
|
||||
|
||||
generatoraccess.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LAVA_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (generatoraccess.m().nextFloat() - generatoraccess.m().nextFloat()) * 0.8F);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
generatoraccess.addParticle(Particles.F, d0 + Math.random(), d1 + 1.2D, d2 + Math.random(), 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockFluids.LEVEL);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public FluidType removeFluid(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if ((Integer) iblockdata.get(BlockFluids.LEVEL) == 0) {
|
||||
generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 11);
|
||||
return this.b;
|
||||
} else {
|
||||
return FluidTypes.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/main/java/net/minecraft/server/BlockGrass.java
Normal file
81
src/main/java/net/minecraft/server/BlockGrass.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockGrass extends BlockDirtSnowSpreadable implements IBlockFragilePlantElement {
|
||||
|
||||
public BlockGrass(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return iblockaccess.getType(blockposition.up()).isAir();
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
IBlockData iblockdata1 = Blocks.GRASS.getBlockData();
|
||||
int i = 0;
|
||||
|
||||
while (i < 128) {
|
||||
BlockPosition blockposition2 = blockposition1;
|
||||
int j = 0;
|
||||
|
||||
while (true) {
|
||||
if (j < i / 16) {
|
||||
blockposition2 = blockposition2.a(random.nextInt(3) - 1, (random.nextInt(3) - 1) * random.nextInt(3) / 2, random.nextInt(3) - 1);
|
||||
if (world.getType(blockposition2.down()).getBlock() == this && !world.getType(blockposition2).k()) {
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
IBlockData iblockdata2 = world.getType(blockposition2);
|
||||
|
||||
if (iblockdata2.getBlock() == iblockdata1.getBlock() && random.nextInt(10) == 0) {
|
||||
((IBlockFragilePlantElement) iblockdata1.getBlock()).b(world, random, blockposition2, iblockdata2);
|
||||
}
|
||||
|
||||
if (iblockdata2.isAir()) {
|
||||
label38:
|
||||
{
|
||||
IBlockData iblockdata3;
|
||||
|
||||
if (random.nextInt(8) == 0) {
|
||||
List<WorldGenFeatureCompositeFlower<?>> list = world.getBiome(blockposition2).f();
|
||||
|
||||
if (list.isEmpty()) {
|
||||
break label38;
|
||||
}
|
||||
|
||||
iblockdata3 = ((WorldGenFeatureCompositeFlower) list.get(0)).a(random, blockposition2);
|
||||
} else {
|
||||
iblockdata3 = iblockdata1;
|
||||
}
|
||||
|
||||
if (iblockdata3.canPlace(world, blockposition2)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition2, iblockdata3, 3); // CraftBukkit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean f(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT_MIPPED;
|
||||
}
|
||||
}
|
||||
72
src/main/java/net/minecraft/server/BlockIce.java
Normal file
72
src/main/java/net/minecraft/server/BlockIce.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockIce extends BlockHalfTransparent {
|
||||
|
||||
public BlockIce(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return Blocks.WATER.getBlockData().b(iblockaccess, blockposition);
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.TRANSLUCENT;
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
entityhuman.b(StatisticList.BLOCK_MINED.b(this));
|
||||
entityhuman.applyExhaustion(0.005F);
|
||||
if (this.X_() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0) {
|
||||
a(world, blockposition, this.t(iblockdata));
|
||||
} else {
|
||||
if (world.worldProvider.isNether()) {
|
||||
world.setAir(blockposition);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack);
|
||||
|
||||
iblockdata.a(world, blockposition, i);
|
||||
Material material = world.getType(blockposition.down()).getMaterial();
|
||||
|
||||
if (material.isSolid() || material.isLiquid()) {
|
||||
world.setTypeUpdate(blockposition, Blocks.WATER.getBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (world.getBrightness(EnumSkyBlock.BLOCK, blockposition) > 11 - iblockdata.b(world, blockposition)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
// CraftBukkit start
|
||||
if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, blockposition, world.worldProvider.isNether() ? Blocks.AIR.getBlockData() : Blocks.WATER.getBlockData()).isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
if (world.worldProvider.isNether()) {
|
||||
world.setAir(blockposition);
|
||||
} else {
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
world.setTypeUpdate(blockposition, Blocks.WATER.getBlockData());
|
||||
world.a(blockposition, Blocks.WATER, blockposition);
|
||||
}
|
||||
}
|
||||
|
||||
public EnumPistonReaction getPushReaction(IBlockData iblockdata) {
|
||||
return EnumPistonReaction.NORMAL;
|
||||
}
|
||||
}
|
||||
128
src/main/java/net/minecraft/server/BlockIceFrost.java
Normal file
128
src/main/java/net/minecraft/server/BlockIceFrost.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockIceFrost extends BlockIce {
|
||||
|
||||
public static final BlockStateInteger a = BlockProperties.U;
|
||||
|
||||
public BlockIceFrost(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockIceFrost.a, 0));
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.paperConfig.frostedIceEnabled) return; // Paper - add ability to disable frosted ice
|
||||
if ((random.nextInt(3) == 0 || this.a(world, blockposition, 4)) && world.getLightLevel(blockposition) > 11 - (Integer) iblockdata.get(BlockIceFrost.a) - iblockdata.b(world, blockposition) && this.c(iblockdata, world, blockposition)) {
|
||||
BlockPosition.b blockposition_b = BlockPosition.b.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
blockposition_b.g(blockposition).c(enumdirection);
|
||||
IBlockData iblockdata1 = world.getTypeIfLoaded(blockposition_b); // Paper - don't load chunks
|
||||
if (iblockdata1 == null) continue; // Paper
|
||||
|
||||
if (iblockdata1.getBlock() == this && !this.c(iblockdata1, world, blockposition_b)) {
|
||||
world.getBlockTickList().a(blockposition_b, this, MathHelper.nextInt(random, world.paperConfig.frostedIceDelayMin, world.paperConfig.frostedIceDelayMax)); // Paper - use configurable min/max delay
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_b != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_b.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_b.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
world.getBlockTickList().a(blockposition, this, MathHelper.nextInt(random, world.paperConfig.frostedIceDelayMin, world.paperConfig.frostedIceDelayMax)); // Paper - use configurable min/max delay
|
||||
}
|
||||
}
|
||||
|
||||
private boolean c(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
int i = (Integer) iblockdata.get(BlockIceFrost.a);
|
||||
|
||||
if (i < 3) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockIceFrost.a, i + 1), 2);
|
||||
return false;
|
||||
} else {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (block == this && this.a(world, blockposition, 2)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
super.doPhysics(iblockdata, world, blockposition, block, blockposition1);
|
||||
}
|
||||
|
||||
private boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, int i) {
|
||||
int j = 0;
|
||||
BlockPosition.b blockposition_b = BlockPosition.b.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int k = aenumdirection.length;
|
||||
|
||||
for (int l = 0; l < k; ++l) {
|
||||
EnumDirection enumdirection = aenumdirection[l];
|
||||
|
||||
blockposition_b.g(blockposition).c(enumdirection);
|
||||
if (((World) iblockaccess).getBlockIfLoaded(blockposition_b) == this) { // Paper - don't load chunks
|
||||
++j;
|
||||
if (j >= i) {
|
||||
boolean flag = false;
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_b != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_b.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_b.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockIceFrost.a);
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
}
|
||||
106
src/main/java/net/minecraft/server/BlockJukeBox.java
Normal file
106
src/main/java/net/minecraft/server/BlockJukeBox.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockJukeBox extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateBoolean HAS_RECORD = BlockProperties.l;
|
||||
|
||||
protected BlockJukeBox(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockJukeBox.HAS_RECORD, false));
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if ((Boolean) iblockdata.get(BlockJukeBox.HAS_RECORD)) {
|
||||
this.dropRecord(world, blockposition);
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockJukeBox.HAS_RECORD, false);
|
||||
world.setTypeAndData(blockposition, iblockdata, 2);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, ItemStack itemstack) {
|
||||
TileEntity tileentity = generatoraccess.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityJukeBox) {
|
||||
// CraftBukkit start - There can only be one
|
||||
itemstack = itemstack.cloneItemStack();
|
||||
if (!itemstack.isEmpty()) {
|
||||
itemstack.setCount(1);
|
||||
}
|
||||
((TileEntityJukeBox) tileentity).setRecord(itemstack);
|
||||
// CraftBukkit end
|
||||
generatoraccess.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockJukeBox.HAS_RECORD, true), 2);
|
||||
}
|
||||
}
|
||||
|
||||
public void dropRecord(World world, BlockPosition blockposition) {
|
||||
if (!world.isClientSide) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityJukeBox) {
|
||||
TileEntityJukeBox tileentityjukebox = (TileEntityJukeBox) tileentity;
|
||||
ItemStack itemstack = tileentityjukebox.getRecord();
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
world.triggerEffect(1010, blockposition, 0);
|
||||
world.a(blockposition, (SoundEffect) null);
|
||||
tileentityjukebox.setRecord(ItemStack.a);
|
||||
float f = 0.7F;
|
||||
double d0 = (double) (world.random.nextFloat() * 0.7F) + 0.15000000596046448D;
|
||||
double d1 = (double) (world.random.nextFloat() * 0.7F) + 0.06000000238418579D + 0.6D;
|
||||
double d2 = (double) (world.random.nextFloat() * 0.7F) + 0.15000000596046448D;
|
||||
ItemStack itemstack1 = itemstack.cloneItemStack();
|
||||
EntityItem entityitem = new EntityItem(world, (double) blockposition.getX() + d0, (double) blockposition.getY() + d1, (double) blockposition.getZ() + d2, itemstack1);
|
||||
|
||||
entityitem.n();
|
||||
world.addEntity(entityitem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
this.dropRecord(world, blockposition);
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityJukeBox();
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityJukeBox) {
|
||||
Item item = ((TileEntityJukeBox) tileentity).getRecord().getItem();
|
||||
|
||||
if (item instanceof ItemRecord) {
|
||||
return ((ItemRecord) item).d();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockJukeBox.HAS_RECORD);
|
||||
}
|
||||
}
|
||||
97
src/main/java/net/minecraft/server/BlockKelp.java
Normal file
97
src/main/java/net/minecraft/server/BlockKelp.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockKelp extends Block implements IFluidContainer {
|
||||
|
||||
public static final BlockStateInteger a = BlockProperties.Y;
|
||||
protected static final VoxelShape b = Block.a(0.0D, 0.0D, 0.0D, 16.0D, 9.0D, 16.0D);
|
||||
|
||||
protected BlockKelp(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockKelp.a, 0));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockKelp.b;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
Fluid fluid = blockactioncontext.getWorld().getFluid(blockactioncontext.getClickPosition());
|
||||
|
||||
return fluid.a(TagsFluid.WATER) && fluid.g() == 8 ? this.a((GeneratorAccess) blockactioncontext.getWorld()) : null;
|
||||
}
|
||||
|
||||
public IBlockData a(GeneratorAccess generatoraccess) {
|
||||
return (IBlockData) this.getBlockData().set(BlockKelp.a, generatoraccess.m().nextInt(25));
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public Fluid h(IBlockData iblockdata) {
|
||||
return FluidTypes.WATER.a(false);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!iblockdata.canPlace(world, blockposition)) {
|
||||
world.setAir(blockposition, true);
|
||||
} else {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
if (iblockdata1.getBlock() == Blocks.WATER && (Integer) iblockdata.get(BlockKelp.a) < 25 && random.nextDouble() < 0.14D) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition1, (IBlockData) iblockdata.a((IBlockState) BlockKelp.a)); // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition1);
|
||||
Block block = iblockdata1.getBlock();
|
||||
|
||||
return block == Blocks.MAGMA_BLOCK ? false : block == this || block == Blocks.KELP_PLANT || Block.a(iblockdata1.getCollisionShape(iworldreader, blockposition1), EnumDirection.UP);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (!iblockdata.canPlace(generatoraccess, blockposition)) {
|
||||
if (enumdirection == EnumDirection.DOWN) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
}
|
||||
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
if (enumdirection == EnumDirection.UP && iblockdata1.getBlock() == this) {
|
||||
return Blocks.KELP_PLANT.getBlockData();
|
||||
} else {
|
||||
generatoraccess.getFluidTickList().a(blockposition, FluidTypes.WATER, FluidTypes.WATER.a((IWorldReader) generatoraccess));
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockKelp.a);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, FluidType fluidtype) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean place(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
174
src/main/java/net/minecraft/server/BlockLeaves.java
Normal file
174
src/main/java/net/minecraft/server/BlockLeaves.java
Normal file
@@ -0,0 +1,174 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit
|
||||
|
||||
public class BlockLeaves extends Block {
|
||||
|
||||
public static final BlockStateInteger DISTANCE = BlockProperties.ab;
|
||||
public static final BlockStateBoolean PERSISTENT = BlockProperties.s;
|
||||
protected static boolean c;
|
||||
|
||||
public BlockLeaves(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockLeaves.DISTANCE, 7)).set(BlockLeaves.PERSISTENT, false));
|
||||
}
|
||||
|
||||
public boolean isTicking(IBlockData iblockdata) {
|
||||
return (Integer) iblockdata.get(BlockLeaves.DISTANCE) == 7 && !(Boolean) iblockdata.get(BlockLeaves.PERSISTENT);
|
||||
}
|
||||
|
||||
public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!(Boolean) iblockdata.get(BlockLeaves.PERSISTENT) && (Integer) iblockdata.get(BlockLeaves.DISTANCE) == 7) {
|
||||
// CraftBukkit start
|
||||
LeavesDecayEvent event = new LeavesDecayEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled() || world.getType(blockposition).getBlock() != this) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
iblockdata.a(world, blockposition, 0);
|
||||
world.setAir(blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
world.setTypeAndData(blockposition, a(iblockdata, (GeneratorAccess) world, blockposition), 3);
|
||||
}
|
||||
|
||||
public int j(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
int i = w(iblockdata1) + 1;
|
||||
|
||||
if (i != 1 || (Integer) iblockdata.get(BlockLeaves.DISTANCE) != i) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, 1);
|
||||
}
|
||||
|
||||
return iblockdata;
|
||||
}
|
||||
|
||||
private static IBlockData a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition) {
|
||||
int i = 7;
|
||||
BlockPosition.b blockposition_b = BlockPosition.b.r();
|
||||
Throwable throwable = null;
|
||||
|
||||
try {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int j = aenumdirection.length;
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
EnumDirection enumdirection = aenumdirection[k];
|
||||
|
||||
blockposition_b.g(blockposition).c(enumdirection);
|
||||
i = Math.min(i, w(generatoraccess.getType(blockposition_b)) + 1);
|
||||
if (i == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Throwable throwable1) {
|
||||
throwable = throwable1;
|
||||
throw throwable1;
|
||||
} finally {
|
||||
if (blockposition_b != null) {
|
||||
if (throwable != null) {
|
||||
try {
|
||||
blockposition_b.close();
|
||||
} catch (Throwable throwable2) {
|
||||
throwable.addSuppressed(throwable2);
|
||||
}
|
||||
} else {
|
||||
blockposition_b.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (IBlockData) iblockdata.set(BlockLeaves.DISTANCE, i);
|
||||
}
|
||||
|
||||
private static int w(IBlockData iblockdata) {
|
||||
return TagsBlock.LOGS.isTagged(iblockdata.getBlock()) ? 0 : (iblockdata.getBlock() instanceof BlockLeaves ? (Integer) iblockdata.get(BlockLeaves.DISTANCE) : 7);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return random.nextInt(20) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
return block == Blocks.OAK_LEAVES ? Blocks.OAK_SAPLING : (block == Blocks.SPRUCE_LEAVES ? Blocks.SPRUCE_SAPLING : (block == Blocks.BIRCH_LEAVES ? Blocks.BIRCH_SAPLING : (block == Blocks.JUNGLE_LEAVES ? Blocks.JUNGLE_SAPLING : (block == Blocks.ACACIA_LEAVES ? Blocks.ACACIA_SAPLING : (block == Blocks.DARK_OAK_LEAVES ? Blocks.DARK_OAK_SAPLING : Blocks.OAK_SAPLING)))));
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide) {
|
||||
int j = this.k(iblockdata);
|
||||
|
||||
if (i > 0) {
|
||||
j -= 2 << i;
|
||||
if (j < 10) {
|
||||
j = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (world.random.nextInt(j) == 0) {
|
||||
a(world, blockposition, new ItemStack(this.getDropType(iblockdata, world, blockposition, i)));
|
||||
}
|
||||
|
||||
j = 200;
|
||||
if (i > 0) {
|
||||
j -= 10 << i;
|
||||
if (j < 40) {
|
||||
j = 40;
|
||||
}
|
||||
}
|
||||
|
||||
this.a(world, blockposition, iblockdata, j);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void a(World world, BlockPosition blockposition, IBlockData iblockdata, int i) {
|
||||
if ((iblockdata.getBlock() == Blocks.OAK_LEAVES || iblockdata.getBlock() == Blocks.DARK_OAK_LEAVES) && world.random.nextInt(i) == 0) {
|
||||
a(world, blockposition, new ItemStack(Items.APPLE));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected int k(IBlockData iblockdata) {
|
||||
return iblockdata.getBlock() == Blocks.JUNGLE_LEAVES ? 40 : 20;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return BlockLeaves.c ? TextureType.CUTOUT_MIPPED : TextureType.SOLID;
|
||||
}
|
||||
|
||||
public boolean q(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) {
|
||||
if (!world.isClientSide && itemstack.getItem() == Items.SHEARS) {
|
||||
entityhuman.b(StatisticList.BLOCK_MINED.b(this));
|
||||
entityhuman.applyExhaustion(0.005F);
|
||||
a(world, blockposition, new ItemStack(this));
|
||||
} else {
|
||||
super.a(world, entityhuman, blockposition, iblockdata, tileentity, itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockLeaves.DISTANCE, BlockLeaves.PERSISTENT);
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return a((IBlockData) this.getBlockData().set(BlockLeaves.PERSISTENT, true), (GeneratorAccess) blockactioncontext.getWorld(), blockactioncontext.getClickPosition());
|
||||
}
|
||||
}
|
||||
138
src/main/java/net/minecraft/server/BlockLever.java
Normal file
138
src/main/java/net/minecraft/server/BlockLever.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockLever extends BlockAttachable {
|
||||
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
protected static final VoxelShape b = Block.a(5.0D, 4.0D, 10.0D, 11.0D, 12.0D, 16.0D);
|
||||
protected static final VoxelShape c = Block.a(5.0D, 4.0D, 0.0D, 11.0D, 12.0D, 6.0D);
|
||||
protected static final VoxelShape o = Block.a(10.0D, 4.0D, 5.0D, 16.0D, 12.0D, 11.0D);
|
||||
protected static final VoxelShape p = Block.a(0.0D, 4.0D, 5.0D, 6.0D, 12.0D, 11.0D);
|
||||
protected static final VoxelShape q = Block.a(5.0D, 0.0D, 4.0D, 11.0D, 6.0D, 12.0D);
|
||||
protected static final VoxelShape r = Block.a(4.0D, 0.0D, 5.0D, 12.0D, 6.0D, 11.0D);
|
||||
protected static final VoxelShape s = Block.a(5.0D, 10.0D, 4.0D, 11.0D, 16.0D, 12.0D);
|
||||
protected static final VoxelShape t = Block.a(4.0D, 10.0D, 5.0D, 12.0D, 16.0D, 11.0D);
|
||||
|
||||
protected BlockLever(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockLever.FACING, EnumDirection.NORTH)).set(BlockLever.POWERED, false)).set(BlockLever.FACE, BlockPropertyAttachPosition.WALL));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
switch ((BlockPropertyAttachPosition) iblockdata.get(BlockLever.FACE)) {
|
||||
case FLOOR:
|
||||
switch (((EnumDirection) iblockdata.get(BlockLever.FACING)).k()) {
|
||||
case X:
|
||||
return BlockLever.r;
|
||||
case Z:
|
||||
default:
|
||||
return BlockLever.q;
|
||||
}
|
||||
case WALL:
|
||||
switch ((EnumDirection) iblockdata.get(BlockLever.FACING)) {
|
||||
case EAST:
|
||||
return BlockLever.p;
|
||||
case WEST:
|
||||
return BlockLever.o;
|
||||
case SOUTH:
|
||||
return BlockLever.c;
|
||||
case NORTH:
|
||||
default:
|
||||
return BlockLever.b;
|
||||
}
|
||||
case CEILING:
|
||||
default:
|
||||
switch (((EnumDirection) iblockdata.get(BlockLever.FACING)).k()) {
|
||||
case X:
|
||||
return BlockLever.t;
|
||||
case Z:
|
||||
default:
|
||||
return BlockLever.s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockLever.POWERED);
|
||||
boolean flag = (Boolean) iblockdata.get(BlockLever.POWERED);
|
||||
|
||||
if (world.isClientSide) {
|
||||
if (flag) {
|
||||
a(iblockdata, world, blockposition, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// CraftBukkit start - Interact Lever
|
||||
boolean powered = !flag; // Old powered state
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
int old = (powered) ? 15 : 0;
|
||||
int current = (!powered) ? 15 : 0;
|
||||
|
||||
BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current);
|
||||
world.getServer().getPluginManager().callEvent(eventRedstone);
|
||||
|
||||
if ((eventRedstone.getNewCurrent() > 0) != (!powered)) {
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
float f3 = flag ? 0.6F : 0.5F;
|
||||
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 0.3F, f3);
|
||||
this.b(iblockdata, world, blockposition);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void a(IBlockData iblockdata, GeneratorAccess generatoraccess, BlockPosition blockposition, float f) {
|
||||
EnumDirection enumdirection = ((EnumDirection) iblockdata.get(BlockLever.FACING)).opposite();
|
||||
EnumDirection enumdirection1 = k(iblockdata).opposite();
|
||||
double d0 = (double) blockposition.getX() + 0.5D + 0.1D * (double) enumdirection.getAdjacentX() + 0.2D * (double) enumdirection1.getAdjacentX();
|
||||
double d1 = (double) blockposition.getY() + 0.5D + 0.1D * (double) enumdirection.getAdjacentY() + 0.2D * (double) enumdirection1.getAdjacentY();
|
||||
double d2 = (double) blockposition.getZ() + 0.5D + 0.1D * (double) enumdirection.getAdjacentZ() + 0.2D * (double) enumdirection1.getAdjacentZ();
|
||||
|
||||
generatoraccess.addParticle(new ParticleParamRedstone(1.0F, 0.0F, 0.0F, f), d0, d1, d2, 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
public void remove(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1, boolean flag) {
|
||||
if (!flag && iblockdata.getBlock() != iblockdata1.getBlock()) {
|
||||
if ((Boolean) iblockdata.get(BlockLever.POWERED)) {
|
||||
this.b(iblockdata, world, blockposition);
|
||||
}
|
||||
|
||||
super.remove(iblockdata, world, blockposition, iblockdata1, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockLever.POWERED) ? 15 : 0;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockLever.POWERED) && k(iblockdata) == enumdirection ? 15 : 0;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void b(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.shift(k(iblockdata).opposite()), this);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockLever.FACE, BlockLever.FACING, BlockLever.POWERED);
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
}
|
||||
60
src/main/java/net/minecraft/server/BlockMagma.java
Normal file
60
src/main/java/net/minecraft/server/BlockMagma.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockMagma extends Block {
|
||||
|
||||
public BlockMagma(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public void stepOn(World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!entity.isFireProof() && entity instanceof EntityLiving && !EnchantmentManager.i((EntityLiving) entity)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit
|
||||
entity.damageEntity(DamageSource.HOT_FLOOR, 1.0F);
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.blockDamage = null; // CraftBukkit
|
||||
}
|
||||
|
||||
super.stepOn(world, blockposition, entity);
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
BlockBubbleColumn.a(world, blockposition.up(), true);
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
if (enumdirection == EnumDirection.UP && iblockdata1.getBlock() == Blocks.WATER) {
|
||||
generatoraccess.getBlockTickList().a(blockposition, this, this.a((IWorldReader) generatoraccess));
|
||||
}
|
||||
|
||||
return super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void b(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (world.getFluid(blockposition).a(TagsFluid.WATER)) {
|
||||
world.a((EntityHuman) null, blockposition, SoundEffects.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F);
|
||||
if (world instanceof WorldServer) {
|
||||
((WorldServer) world).a(Particles.F, (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.25D, (double) blockposition1.getZ() + 0.5D, 8, 0.5D, 0.25D, 0.5D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, Entity entity) {
|
||||
return entity.isFireProof();
|
||||
}
|
||||
|
||||
public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
271
src/main/java/net/minecraft/server/BlockMinecartDetector.java
Normal file
271
src/main/java/net/minecraft/server/BlockMinecartDetector.java
Normal file
@@ -0,0 +1,271 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Predicate;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit
|
||||
|
||||
public class BlockMinecartDetector extends BlockMinecartTrackAbstract {
|
||||
|
||||
public static final BlockStateEnum<BlockPropertyTrackPosition> SHAPE = BlockProperties.S;
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
|
||||
public BlockMinecartDetector(Block.Info block_info) {
|
||||
super(true, block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockMinecartDetector.POWERED, false)).set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH));
|
||||
}
|
||||
|
||||
public int a(IWorldReader iworldreader) {
|
||||
return 20;
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Entity entity) {
|
||||
if (!world.isClientSide) {
|
||||
if (!(Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (!world.isClientSide && (Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) {
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return (Boolean) iblockdata.get(BlockMinecartDetector.POWERED) ? 15 : 0;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return !(Boolean) iblockdata.get(BlockMinecartDetector.POWERED) ? 0 : (enumdirection == EnumDirection.UP ? 15 : 0);
|
||||
}
|
||||
|
||||
private void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
boolean flag = (Boolean) iblockdata.get(BlockMinecartDetector.POWERED);
|
||||
boolean flag1 = false;
|
||||
List<EntityMinecartAbstract> list = this.a(world, blockposition, EntityMinecartAbstract.class, (Predicate) null);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
flag1 = true;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
if (flag != flag1) {
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
|
||||
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.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockMinecartDetector.POWERED, true), 3);
|
||||
this.b(world, blockposition, iblockdata, true);
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.down(), this);
|
||||
world.a(blockposition, blockposition);
|
||||
}
|
||||
|
||||
if (!flag1 && flag) {
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockMinecartDetector.POWERED, false), 3);
|
||||
this.b(world, blockposition, iblockdata, false);
|
||||
world.applyPhysics(blockposition, this);
|
||||
world.applyPhysics(blockposition.down(), this);
|
||||
world.a(blockposition, blockposition);
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
world.getBlockTickList().a(blockposition, this, this.a((IWorldReader) world));
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
protected void b(World world, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
MinecartTrackLogic minecarttracklogic = new MinecartTrackLogic(world, blockposition, iblockdata);
|
||||
List<BlockPosition> list = minecarttracklogic.a();
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
BlockPosition blockposition1 = (BlockPosition) iterator.next();
|
||||
IBlockData iblockdata1 = world.getType(blockposition1);
|
||||
|
||||
iblockdata1.doPhysics(world, blockposition1, iblockdata1.getBlock(), blockposition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) {
|
||||
if (iblockdata1.getBlock() != iblockdata.getBlock()) {
|
||||
super.onPlace(iblockdata, world, blockposition, iblockdata1);
|
||||
this.a(world, blockposition, iblockdata);
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockState<BlockPropertyTrackPosition> e() {
|
||||
return BlockMinecartDetector.SHAPE;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
if ((Boolean) iblockdata.get(BlockMinecartDetector.POWERED)) {
|
||||
List<EntityMinecartCommandBlock> list = this.a(world, blockposition, EntityMinecartCommandBlock.class, (Predicate) null);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
return ((EntityMinecartCommandBlock) list.get(0)).getCommandBlock().i();
|
||||
}
|
||||
|
||||
List<EntityMinecartAbstract> list1 = this.a(world, blockposition, EntityMinecartAbstract.class, IEntitySelector.d);
|
||||
|
||||
if (!list1.isEmpty()) {
|
||||
return Container.b((IInventory) list1.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected <T extends EntityMinecartAbstract> List<T> a(World world, BlockPosition blockposition, Class<T> oclass, @Nullable Predicate<Entity> predicate) {
|
||||
return world.a(oclass, this.a(blockposition), predicate);
|
||||
}
|
||||
|
||||
private AxisAlignedBB a(BlockPosition blockposition) {
|
||||
float f = 0.2F;
|
||||
|
||||
return new AxisAlignedBB((double) ((float) blockposition.getX() + 0.2F), (double) blockposition.getY(), (double) ((float) blockposition.getZ() + 0.2F), (double) ((float) (blockposition.getX() + 1) - 0.2F), (double) ((float) (blockposition.getY() + 1) - 0.2F), (double) ((float) (blockposition.getZ() + 1) - 0.2F));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
switch (enumblockrotation) {
|
||||
case CLOCKWISE_180:
|
||||
switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
}
|
||||
case COUNTERCLOCKWISE_90:
|
||||
switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case NORTH_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.EAST_WEST);
|
||||
case EAST_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH);
|
||||
}
|
||||
case CLOCKWISE_90:
|
||||
switch ((BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE)) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.EAST_WEST);
|
||||
case EAST_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_SOUTH);
|
||||
}
|
||||
default:
|
||||
return iblockdata;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
BlockPropertyTrackPosition blockpropertytrackposition = (BlockPropertyTrackPosition) iblockdata.get(BlockMinecartDetector.SHAPE);
|
||||
|
||||
switch (enumblockmirror) {
|
||||
case LEFT_RIGHT:
|
||||
switch (blockpropertytrackposition) {
|
||||
case ASCENDING_NORTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_SOUTH);
|
||||
case ASCENDING_SOUTH:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_NORTH);
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
default:
|
||||
return super.a(iblockdata, enumblockmirror);
|
||||
}
|
||||
case FRONT_BACK:
|
||||
switch (blockpropertytrackposition) {
|
||||
case ASCENDING_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_WEST);
|
||||
case ASCENDING_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.ASCENDING_EAST);
|
||||
case ASCENDING_NORTH:
|
||||
case ASCENDING_SOUTH:
|
||||
default:
|
||||
break;
|
||||
case SOUTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_WEST);
|
||||
case SOUTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.SOUTH_EAST);
|
||||
case NORTH_WEST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_EAST);
|
||||
case NORTH_EAST:
|
||||
return (IBlockData) iblockdata.set(BlockMinecartDetector.SHAPE, BlockPropertyTrackPosition.NORTH_WEST);
|
||||
}
|
||||
}
|
||||
|
||||
return super.a(iblockdata, enumblockmirror);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockMinecartDetector.SHAPE, BlockMinecartDetector.POWERED);
|
||||
}
|
||||
}
|
||||
45
src/main/java/net/minecraft/server/BlockMobSpawner.java
Normal file
45
src/main/java/net/minecraft/server/BlockMobSpawner.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockMobSpawner extends BlockTileEntity {
|
||||
|
||||
protected BlockMobSpawner(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public TileEntity a(IBlockAccess iblockaccess) {
|
||||
return new TileEntityMobSpawner();
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
super.dropNaturally(iblockdata, world, blockposition, f, i);
|
||||
/* CraftBukkit start - Delegate to getExpDrop
|
||||
int j = 15 + world.random.nextInt(15) + world.random.nextInt(15);
|
||||
|
||||
this.dropExperience(world, blockposition, j);
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpDrop(IBlockData iblockdata, World world, BlockPosition blockposition, int enchantmentLevel) {
|
||||
int j = 15 + world.random.nextInt(15) + world.random.nextInt(15);
|
||||
|
||||
return j;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public EnumRenderType c(IBlockData iblockdata) {
|
||||
return EnumRenderType.MODEL;
|
||||
}
|
||||
|
||||
public TextureType c() {
|
||||
return TextureType.CUTOUT;
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
}
|
||||
50
src/main/java/net/minecraft/server/BlockMonsterEggs.java
Normal file
50
src/main/java/net/minecraft/server/BlockMonsterEggs.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit
|
||||
|
||||
public class BlockMonsterEggs extends Block {
|
||||
|
||||
private final Block a;
|
||||
private static final Map<Block, Block> b = Maps.newIdentityHashMap();
|
||||
|
||||
public BlockMonsterEggs(Block block, Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.a = block;
|
||||
BlockMonsterEggs.b.put(block, this);
|
||||
}
|
||||
|
||||
public int a(IBlockData iblockdata, Random random) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Block d() {
|
||||
return this.a;
|
||||
}
|
||||
|
||||
public static boolean k(IBlockData iblockdata) {
|
||||
return BlockMonsterEggs.b.containsKey(iblockdata.getBlock());
|
||||
}
|
||||
|
||||
protected ItemStack t(IBlockData iblockdata) {
|
||||
return new ItemStack(this.a);
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide && world.getGameRules().getBoolean("doTileDrops")) {
|
||||
EntitySilverfish entitysilverfish = EntityTypes.SILVERFISH.create(world); // Paper
|
||||
|
||||
entitysilverfish.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, 0.0F, 0.0F);
|
||||
world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason
|
||||
entitysilverfish.doSpawnEffect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static IBlockData f(Block block) {
|
||||
return ((Block) BlockMonsterEggs.b.get(block)).getBlockData();
|
||||
}
|
||||
}
|
||||
103
src/main/java/net/minecraft/server/BlockMushroom.java
Normal file
103
src/main/java/net/minecraft/server/BlockMushroom.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.TreeType;
|
||||
// CraftBukkit end
|
||||
|
||||
public class BlockMushroom extends BlockPlant implements IBlockFragilePlantElement {
|
||||
|
||||
protected static final VoxelShape a = Block.a(5.0D, 0.0D, 5.0D, 11.0D, 6.0D, 11.0D);
|
||||
|
||||
public BlockMushroom(Block.Info block_info) {
|
||||
super(block_info);
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockMushroom.a;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
if (random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.mushroomModifier) * 25)) == 0) { // Spigot
|
||||
int i = 5;
|
||||
boolean flag = true;
|
||||
Iterator iterator = BlockPosition.b(blockposition.a(-4, -1, -4), blockposition.a(4, 1, 4)).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
BlockPosition blockposition1 = (BlockPosition) iterator.next();
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == this) {
|
||||
--i;
|
||||
if (i <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1);
|
||||
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if (world.isEmpty(blockposition2) && iblockdata.canPlace(world, blockposition2)) {
|
||||
blockposition = blockposition2;
|
||||
}
|
||||
|
||||
blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1);
|
||||
}
|
||||
|
||||
if (world.isEmpty(blockposition2) && iblockdata.canPlace(world, blockposition2)) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, blockposition, blockposition2, iblockdata, 2); // CraftBukkit
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.f(iblockaccess, blockposition);
|
||||
}
|
||||
|
||||
public boolean canPlace(IBlockData iblockdata, IWorldReader iworldreader, BlockPosition blockposition) {
|
||||
BlockPosition blockposition1 = blockposition.down();
|
||||
IBlockData iblockdata1 = iworldreader.getType(blockposition1);
|
||||
Block block = iblockdata1.getBlock();
|
||||
|
||||
return block != Blocks.MYCELIUM && block != Blocks.PODZOL ? iworldreader.getLightLevel(blockposition, 0) < 13 && this.b(iblockdata1, (IBlockAccess) iworldreader, blockposition1) : true;
|
||||
}
|
||||
|
||||
public boolean a(GeneratorAccess generatoraccess, BlockPosition blockposition, IBlockData iblockdata, Random random) {
|
||||
generatoraccess.setAir(blockposition);
|
||||
WorldGenerator<WorldGenFeatureEmptyConfiguration> worldgenerator = null;
|
||||
|
||||
if (this == Blocks.BROWN_MUSHROOM) {
|
||||
BlockSapling.treeType = TreeType.BROWN_MUSHROOM; // CraftBukkit
|
||||
worldgenerator = WorldGenerator.U;
|
||||
} else if (this == Blocks.RED_MUSHROOM) {
|
||||
BlockSapling.treeType = TreeType.RED_MUSHROOM; // CraftBukkit
|
||||
worldgenerator = WorldGenerator.T;
|
||||
}
|
||||
|
||||
if (worldgenerator != null && worldgenerator.generate(generatoraccess, generatoraccess.getChunkProvider().getChunkGenerator(), random, blockposition, WorldGenFeatureConfiguration.e)) {
|
||||
return true;
|
||||
} else {
|
||||
generatoraccess.setTypeAndData(blockposition, iblockdata, 3);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, boolean flag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean a(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return (double) random.nextFloat() < 0.4D;
|
||||
}
|
||||
|
||||
public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
this.a((GeneratorAccess) world, blockposition, iblockdata, random);
|
||||
}
|
||||
|
||||
public boolean e(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
63
src/main/java/net/minecraft/server/BlockNetherWart.java
Normal file
63
src/main/java/net/minecraft/server/BlockNetherWart.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockNetherWart extends BlockPlant {
|
||||
|
||||
public static final BlockStateInteger AGE = BlockProperties.U;
|
||||
private static final VoxelShape[] b = new VoxelShape[] { Block.a(0.0D, 0.0D, 0.0D, 16.0D, 5.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 8.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 11.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 14.0D, 16.0D)};
|
||||
|
||||
protected BlockNetherWart(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockNetherWart.AGE, 0));
|
||||
}
|
||||
|
||||
public VoxelShape a(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return BlockNetherWart.b[(Integer) iblockdata.get(BlockNetherWart.AGE)];
|
||||
}
|
||||
|
||||
protected boolean b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockdata.getBlock() == Blocks.SOUL_SAND;
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Random random) {
|
||||
int i = (Integer) iblockdata.get(BlockNetherWart.AGE);
|
||||
|
||||
if (i < 3 && random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.wartModifier) * 10)) == 0) { // Spigot
|
||||
iblockdata = (IBlockData) iblockdata.set(BlockNetherWart.AGE, i + 1);
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition, iblockdata, 2); // CraftBukkit
|
||||
}
|
||||
|
||||
super.a(iblockdata, world, blockposition, random);
|
||||
}
|
||||
|
||||
public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) {
|
||||
if (!world.isClientSide) {
|
||||
int j = 1;
|
||||
|
||||
if ((Integer) iblockdata.get(BlockNetherWart.AGE) >= 3) {
|
||||
j = 2 + world.random.nextInt(3);
|
||||
if (i > 0) {
|
||||
j += world.random.nextInt(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
a(world, blockposition, new ItemStack(Items.NETHER_WART));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public IMaterial getDropType(IBlockData iblockdata, World world, BlockPosition blockposition, int i) {
|
||||
return Items.AIR;
|
||||
}
|
||||
|
||||
public ItemStack a(IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
return new ItemStack(Items.NETHER_WART);
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockNetherWart.AGE);
|
||||
}
|
||||
}
|
||||
78
src/main/java/net/minecraft/server/BlockNote.java
Normal file
78
src/main/java/net/minecraft/server/BlockNote.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
public class BlockNote extends Block {
|
||||
|
||||
public static final BlockStateEnum<BlockPropertyInstrument> INSTRUMENT = BlockProperties.as;
|
||||
public static final BlockStateBoolean POWERED = BlockProperties.t;
|
||||
public static final BlockStateInteger NOTE = BlockProperties.aj;
|
||||
|
||||
public BlockNote(Block.Info block_info) {
|
||||
super(block_info);
|
||||
this.v((IBlockData) ((IBlockData) ((IBlockData) ((IBlockData) this.blockStateList.getBlockData()).set(BlockNote.INSTRUMENT, BlockPropertyInstrument.HARP)).set(BlockNote.NOTE, 0)).set(BlockNote.POWERED, false));
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(BlockActionContext blockactioncontext) {
|
||||
return (IBlockData) this.getBlockData().set(BlockNote.INSTRUMENT, BlockPropertyInstrument.a(blockactioncontext.getWorld().getType(blockactioncontext.getClickPosition().down())));
|
||||
}
|
||||
|
||||
public IBlockData updateState(IBlockData iblockdata, EnumDirection enumdirection, IBlockData iblockdata1, GeneratorAccess generatoraccess, BlockPosition blockposition, BlockPosition blockposition1) {
|
||||
return enumdirection == EnumDirection.DOWN ? (IBlockData) iblockdata.set(BlockNote.INSTRUMENT, BlockPropertyInstrument.a(iblockdata1)) : super.updateState(iblockdata, enumdirection, iblockdata1, generatoraccess, blockposition, blockposition1);
|
||||
}
|
||||
|
||||
public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
boolean flag = world.isBlockIndirectlyPowered(blockposition);
|
||||
|
||||
if (flag != (Boolean) iblockdata.get(BlockNote.POWERED)) {
|
||||
if (flag) {
|
||||
this.play(world, blockposition, iblockdata); // CraftBukkit
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, (IBlockData) iblockdata.set(BlockNote.POWERED, flag), 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void play(World world, BlockPosition blockposition, IBlockData data) { // CraftBukkit
|
||||
if (world.getType(blockposition.up()).isAir()) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(world, blockposition, data.get(BlockNote.INSTRUMENT), data.get(BlockNote.NOTE));
|
||||
if (!event.isCancelled()) {
|
||||
world.playBlockAction(blockposition, this, 0, 0);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean interact(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
iblockdata = (IBlockData) iblockdata.a((IBlockState) BlockNote.NOTE);
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
this.play(world, blockposition, iblockdata); // CraftBukkit
|
||||
entityhuman.a(StatisticList.TUNE_NOTEBLOCK);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void attack(IBlockData iblockdata, World world, BlockPosition blockposition, EntityHuman entityhuman) {
|
||||
if (!world.isClientSide) {
|
||||
this.play(world, blockposition, iblockdata); // CraftBukkit
|
||||
entityhuman.a(StatisticList.PLAY_NOTEBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean a(IBlockData iblockdata, World world, BlockPosition blockposition, int i, int j) {
|
||||
int k = (Integer) iblockdata.get(BlockNote.NOTE);
|
||||
float f = (float) Math.pow(2.0D, (double) (k - 12) / 12.0D);
|
||||
|
||||
world.a((EntityHuman) null, blockposition, ((BlockPropertyInstrument) iblockdata.get(BlockNote.INSTRUMENT)).a(), SoundCategory.RECORDS, 3.0F, f);
|
||||
world.addParticle(Particles.I, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 1.2D, (double) blockposition.getZ() + 0.5D, (double) k / 24.0D, 0.0D, 0.0D);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void a(BlockStateList.a<Block, IBlockData> blockstatelist_a) {
|
||||
blockstatelist_a.a(BlockNote.INSTRUMENT, BlockNote.POWERED, BlockNote.NOTE);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user