Compare commits

...

7 commits

Author SHA1 Message Date
MiniDigger | Martin
bee74680e6 add remapped patches as a test 2024-01-14 11:04:49 +01:00
MiniDigger | Martin
0a44692ef6 add example lib patch 2024-01-08 20:18:38 +01:00
MiniDigger | Martin
e6137f7f1c add example feature patch 2024-01-06 16:18:13 +01:00
MiniDigger | Martin
7d1c30bc57 revert later, make stuff compile clean 2024-01-01 18:07:18 +01:00
MiniDigger | Martin
be328f1d29 revert later, move around a ton of projects to the new desired layout 2024-01-01 17:21:52 +01:00
MiniDigger | Martin
ae3378abc9 revert later, initial softspoon setup
AT
2023-12-23 09:16:01 +01:00
MiniDigger | Martin
4afd50fa36 revert later, initial softspoon setup
this just copies over stuff without fixing history
2023-12-22 16:51:19 +01:00
4596 changed files with 646015 additions and 61781 deletions

4
.gitignore vendored
View file

@ -62,8 +62,6 @@ out/
# other stuff
run/
Paper-Server
Paper-API
Paperclip.jar
paperclip.jar
paperclip-*.jar
@ -71,5 +69,5 @@ paperclip.properties
!gradle/wrapper/gradle-wrapper.jar
test-plugin.settings.gradle.kts
paper-test-plugin.settings.gradle.kts
paper-api-generator.settings.gradle.kts

View file

@ -1,36 +0,0 @@
plugins {
`java-library`
`maven-publish`
}
java {
withSourcesJar()
withJavadocJar()
}
dependencies {
implementation(project(":paper-api"))
api("com.mojang:brigadier:1.0.18")
compileOnly("it.unimi.dsi:fastutil:8.5.6")
compileOnly("org.jetbrains:annotations:23.0.0")
testImplementation("junit:junit:4.13.2")
testImplementation("org.hamcrest:hamcrest-library:1.3")
testImplementation("org.ow2.asm:asm-tree:9.2")
}
configure<PublishingExtension> {
publications.create<MavenPublication>("maven") {
from(components["java"])
}
}
val scanJar = tasks.register("scanJarForBadCalls", io.papermc.paperweight.tasks.ScanJarForBadCalls::class) {
badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;")
jarToScan.set(tasks.jar.flatMap { it.archiveFile })
classpath.from(configurations.compileClasspath)
}
tasks.check {
dependsOn(scanJar)
}

View file

@ -1,18 +1,9 @@
# You can use this file to change the access modifiers on a member
# This line would make the field rollAmount public in Bee
#public net.minecraft.world.entity.animal.Bee rollAmount
# This line would make the field public and remove the final modifier
#public-f net.minecraft.network.protocol.game.ClientboundChatPacket sender
# Leave out the member and it will apply to the class itself
# More info, see here https://mcforge.readthedocs.io/en/latest/advanced/accesstransformers/#access-modifiers
# Remap/Decompile fix (unclear why this is happening)
public net.minecraft.server.MinecraftServer doRunTask(Lnet/minecraft/server/TickTask;)V
# AT remap issue? todo 1.18
public net.minecraft.world.level.dimension.end.EndDragonFight findExitPortal()Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch;
public net.minecraft.nbt.TagParser readArrayTag()Lnet/minecraft/nbt/Tag;
# TODO 1.20 remapSpigotAt.at doesn't remap the return type for this method for some reason
public net/minecraft/world/entity/Display$TextDisplay getText()Lnet/minecraft/network/chat/Component;
public net/minecraft/world/entity/Display$BlockDisplay getBlockState()Lnet/minecraft/world/level/block/state/BlockState;
public net.minecraft.world.entity.Display$BlockDisplay getBlockState()Lnet/minecraft/world/level/block/state/BlockState;
public net.minecraft.world.level.dimension.end.EndDragonFight findExitPortal()Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch;
public net.minecraft.server.MinecraftServer doRunTask(Lnet/minecraft/server/TickTask;)V
public net.minecraft.CrashReport saveFile
public+f net.minecraft.CrashReport getDetails()Ljava/lang/String;
public+f net.minecraft.CrashReport getTitle()Ljava/lang/String;
public+f net.minecraft.CrashReport getExceptionMessage()Ljava/lang/String;
public net.minecraft.world.entity.Display$TextDisplay getText()Lnet/minecraft/network/chat/Component;

View file

@ -11,7 +11,7 @@ plugins {
java
`maven-publish`
id("com.github.johnrengelman.shadow") version "8.1.1" apply false
id("io.papermc.paperweight.core") version "1.5.11"
id("io.papermc.paperweight.core") version "2.0.0-SNAPSHOT"
}
allprojects {
@ -67,43 +67,39 @@ repositories {
}
dependencies {
paramMappings("net.fabricmc:yarn:1.20.4+build.1:mergedv2")
remapper("net.fabricmc:tiny-remapper:0.8.10:fat")
paperclip("io.papermc:paperclip:3.0.3")
mache("io.papermc:mache:1.20.4+build.1")
// only for patch remap
paramMappings("net.fabricmc:yarn:1.20.2+build.1:mergedv2")
remapper("net.fabricmc:tiny-remapper:0.8.6:fat")
decompiler("net.minecraftforge:forgeflower:2.0.627.2")
spigotDecompiler("io.papermc:patched-spigot-fernflower:0.1+build.6")
paperclip("io.papermc:paperclip:3.0.3")
}
paperweight {
minecraftVersion = providers.gradleProperty("mcVersion")
serverProject = project(":paper-server")
softSpoon.set(true)
paramMappingsRepo = paperMavenPublicUrl
remapRepo = paperMavenPublicUrl
decompileRepo = paperMavenPublicUrl
// todo we shouldn't need this anymore, this should come via mache?
minecraftVersion.set(providers.gradleProperty("mcVersion"))
serverProject.set(project(":paper-server"))
// only for patch remap
paramMappingsRepo.set(paperMavenPublicUrl)
remapRepo.set(paperMavenPublicUrl)
decompileRepo.set(paperMavenPublicUrl)
craftBukkit {
fernFlowerJar = layout.file(spigotDecompiler.elements.map { it.single().asFile })
fernFlowerJar.set(layout.file(spigotDecompiler.elements.map { it.single().asFile }))
}
paper {
spigotApiPatchDir = layout.projectDirectory.dir("patches/api")
spigotServerPatchDir = layout.projectDirectory.dir("patches/server")
mappingsPatch.set(layout.projectDirectory.file("build-data/mappings-patch.tiny"))
reobfMappingsPatch.set(layout.projectDirectory.file("build-data/reobf-mappings-patch.tiny"))
mappingsPatch = layout.projectDirectory.file("build-data/mappings-patch.tiny")
reobfMappingsPatch = layout.projectDirectory.file("build-data/reobf-mappings-patch.tiny")
reobfPackagesToFix.addAll(
"co.aikar.timings",
"com.destroystokyo.paper",
"com.mojang",
"io.papermc.paper",
"ca.spottedleaf",
"net.kyori.adventure.bossbar",
"net.minecraft",
"org.bukkit.craftbukkit",
"org.spigotmc",
)
paperApiDir.set(layout.projectDirectory.dir("paper-api"))
paperServerDir.set(layout.projectDirectory.dir("paper-server"))
}
}
@ -211,7 +207,7 @@ abstract class RebasePatches : BaseTask() {
val f = redirect(proc.inputStream, out)
val exit = proc.waitFor()
f.get()
f.join()
if (exit != 0) {
val outStr = String(out.toByteArray())
@ -255,7 +251,7 @@ abstract class RebasePatches : BaseTask() {
val f1 = redirect(apply2.inputStream, System.out)
apply2.waitFor()
f1.get()
f1.join()
logger.lifecycle(outStr)
logger.lifecycle("Patch failed at $failed; See Git output above.")

40
paper-api/.gitignore vendored Normal file
View file

@ -0,0 +1,40 @@
.gradle/
# Eclipse stuff
/.classpath
/.project
/.settings
/.checkstyle
/.factorypath
# netbeans
/nbproject
/nb-configuration.xml
# we use maven!
/build.xml
# maven
/target
# vim
.*.sw[a-p]
# various other potential build files
/build
/bin
/dist
/manifest.mf
# Mac filesystem dust
.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
# vs code
/.vscode
/.factorypath

674
paper-api/LICENCE.txt Normal file
View 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>.

203
paper-api/build.gradle.kts Normal file
View file

@ -0,0 +1,203 @@
plugins {
`java-library`
`maven-publish`
idea // Paper
}
java {
withSourcesJar()
withJavadocJar()
}
val annotationsVersion = "24.0.1"
val bungeeCordChatVersion = "1.20-R0.1"
val adventureVersion = "4.14.0"
val slf4jVersion = "2.0.9"
val log4jVersion = "2.17.1"
val apiAndDocs: Configuration by configurations.creating {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
}
}
configurations.api {
extendsFrom(apiAndDocs)
}
dependencies {
// api dependencies are listed transitively to API consumers
api("com.google.guava:guava:32.1.2-jre")
api("com.google.code.gson:gson:2.10.1")
// Paper start - adventure
api("net.md-5:bungeecord-chat:$bungeeCordChatVersion-deprecated+build.14") {
exclude("com.google.guava", "guava")
}
// Paper - adventure
api("org.yaml:snakeyaml:2.2")
api("org.joml:joml:1.10.5")
// Paper start
api("com.googlecode.json-simple:json-simple:1.1.1") {
isTransitive = false // includes junit
}
api("it.unimi.dsi:fastutil:8.5.6")
apiAndDocs(platform("net.kyori:adventure-bom:$adventureVersion"))
apiAndDocs("net.kyori:adventure-api")
apiAndDocs("net.kyori:adventure-text-minimessage")
apiAndDocs("net.kyori:adventure-text-serializer-gson")
apiAndDocs("net.kyori:adventure-text-serializer-legacy")
apiAndDocs("net.kyori:adventure-text-serializer-plain")
apiAndDocs("net.kyori:adventure-text-logger-slf4j")
api("org.apache.logging.log4j:log4j-api:$log4jVersion")
api("org.slf4j:slf4j-api:$slf4jVersion")
implementation("org.ow2.asm:asm:9.5")
implementation("org.ow2.asm:asm-commons:9.5")
// Paper end
api("org.apache.maven:maven-resolver-provider:3.9.6") // Paper - make API dependency for Paper Plugins
compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18")
val annotations = "org.jetbrains:annotations:$annotationsVersion" // Paper - we don't want Java 5 annotations...
compileOnly(annotations)
testCompileOnly(annotations)
// Paper start - add checker
val checkerQual = "org.checkerframework:checker-qual:3.33.0"
compileOnlyApi(checkerQual)
testCompileOnly(checkerQual)
// Paper end
api("com.mojang:brigadier:1.0.18")
testImplementation("org.apache.commons:commons-lang3:3.12.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testImplementation("org.hamcrest:hamcrest:2.2")
testImplementation("org.mockito:mockito-core:5.5.0")
testImplementation("org.ow2.asm:asm-tree:9.5")
}
// Paper start
val generatedApiPath: java.nio.file.Path = rootProject.projectDir.toPath().resolve("paper-api-generator/generated")
idea {
module {
generatedSourceDirs.add(generatedApiPath.toFile())
}
}
sourceSets {
main {
java {
srcDir(generatedApiPath)
}
}
}
// Paper end
configure<PublishingExtension> {
publications.create<MavenPublication>("maven") {
from(components["java"])
}
}
val generateApiVersioningFile by tasks.registering {
inputs.property("version", project.version)
val pomProps = layout.buildDirectory.file("pom.properties")
outputs.file(pomProps)
val projectVersion = project.version
doLast {
pomProps.get().asFile.writeText("version=$projectVersion")
}
}
tasks.jar {
from(generateApiVersioningFile.map { it.outputs.files.singleFile }) {
into("META-INF/maven/${project.group}/${project.name}")
}
manifest {
attributes(
"Automatic-Module-Name" to "org.bukkit"
)
}
}
tasks.withType<Javadoc> {
val options = options as StandardJavadocDocletOptions
options.overview = "src/main/javadoc/overview.html"
options.use()
options.isDocFilesSubDirs = true
options.links(
"https://guava.dev/releases/32.1.2-jre/api/docs/",
"https://javadoc.io/doc/org.yaml/snakeyaml/2.2/",
"https://javadoc.io/doc/org.jetbrains/annotations/$annotationsVersion/", // Paper - we don't want Java 5 annotations
// "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/", // Paper - don't link to bungee chat
// Paper start - add missing javadoc links
"https://javadoc.io/doc/org.joml/joml/1.10.5/index.html",
"https://www.javadoc.io/doc/com.google.code.gson/gson/2.10.1",
// Paper end
// Paper start
"https://jd.advntr.dev/api/$adventureVersion/",
"https://jd.advntr.dev/text-minimessage/$adventureVersion/",
"https://jd.advntr.dev/text-serializer-gson/$adventureVersion/",
"https://jd.advntr.dev/text-serializer-legacy/$adventureVersion/",
"https://jd.advntr.dev/text-serializer-plain/$adventureVersion/",
"https://jd.advntr.dev/text-logger-slf4j/$adventureVersion/",
"https://javadoc.io/doc/org.slf4j/slf4j-api/$slf4jVersion/",
"https://javadoc.io/doc/org.apache.logging.log4j/log4j-api/$log4jVersion/",
// Paper end
"https://javadoc.io/doc/org.apache.maven.resolver/maven-resolver-api/1.7.3", // Paper
)
options.tags("apiNote:a:API Note:")
inputs.files(apiAndDocs).ignoreEmptyDirectories().withPropertyName(apiAndDocs.name + "-configuration")
doFirst {
options.addStringOption(
"sourcepath",
apiAndDocs.resolvedConfiguration.files.joinToString(separator = File.pathSeparator, transform = File::getPath)
)
}
// workaround for https://github.com/gradle/gradle/issues/4046
inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset")
doLast {
copy {
from("src/main/javadoc") {
include("**/doc-files/**")
}
into("build/docs/javadoc")
}
}
}
tasks.test {
useJUnitPlatform()
}
// Paper start - compile tests with -parameters for better junit parameterized test names
tasks.compileTestJava {
options.compilerArgs.add("-parameters")
}
// Paper end
// Paper start
val scanJar = tasks.register("scanJarForBadCalls", io.papermc.paperweight.tasks.ScanJarForBadCalls::class) {
badAnnotations.add("Lio/papermc/paper/annotation/DoNotUse;")
jarToScan.set(tasks.jar.flatMap { it.archiveFile })
classpath.from(configurations.compileClasspath)
}
tasks.check {
dependsOn(scanJar)
}
// Paper end
// Paper start
val scanJarForOldGeneratedCode = tasks.register("scanJarForOldGeneratedCode", io.papermc.paperweight.tasks.ScanJarForOldGeneratedCode::class) {
mcVersion.set(providers.gradleProperty("mcVersion"))
annotation.set("Lio/papermc/paper/generated/GeneratedFrom;")
jarToScan.set(tasks.jar.flatMap { it.archiveFile })
classpath.from(configurations.compileClasspath)
}
tasks.check {
dependsOn(scanJarForOldGeneratedCode)
}
// Paper end

101
paper-api/checkstyle.xml Normal file
View file

@ -0,0 +1,101 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<!-- See http://checkstyle.sourceforge.net/config_misc.html#NewlineAtEndOfFile -->
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf_cr_crlf"/>
</module>
<!-- See http://checkstyle.sourceforge.net/config_whitespace.html -->
<module name="FileTabCharacter"/>
<!-- See http://checkstyle.sourceforge.net/config_misc.html -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="minimum" value="0"/>
<property name="maximum" value="0"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<!-- See https://checkstyle.sourceforge.io/config_javadoc.html#JavadocPackage-->
<module name="JavadocPackage"/>
<module name="SuppressionSingleFilter">
<property name="message" value="'(implNote|implSpec|apiNote)'\."/>
</module>
<!-- Don't check for missing package-info in tests -->
<module name="SuppressionSingleFilter">
<property name="checks" value="JavadocPackage"/>
<property name="files" value=".*[\\/]src[\\/]test[\\/].*\.java$"/>
</module>
<module name="TreeWalker">
<!-- See https://checkstyle.org/config_javadoc.html -->
<module name="AtclauseOrder"/>
<module name="InvalidJavadocPosition"/>
<module name="JavadocBlockTagLocation"/>
<module name="JavadocContentLocationCheck"/>
<module name="JavadocMethod"/>
<module name="JavadocType"/>
<module name="MissingJavadocPackage"/>
<module name="NonEmptyAtclauseDescription"/>
<!-- See http://checkstyle.sourceforge.net/config_filters.html -->
<module name="SuppressionCommentFilter"/>
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="Spigot start"/>
<property name="onCommentFormat" value="Spigot end"/>
</module>
<!-- See http://checkstyle.sourceforge.net/config_imports.html -->
<module name="AvoidStarImport">
<property name="allowStaticMemberImports" value="true"/>
</module>
<module name="ImportOrder">
<property name="option" value="above"/>
<property name="ordered" value="true"/>
<property name="separatedStaticGroups" value="true"/>
<property name="sortStaticImportsAlphabetically" value="true"/>
</module>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
<!-- See https://checkstyle.org/config_whitespace.html -->
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoLineWrap"/>
<module name="NoWhitespaceAfter"/>
<module name="NoWhitespaceBefore">
<property name="allowLineBreaks" value="true"/>
</module>
<module name="OperatorWrap"/>
<module name="ParenPad"/>
<module name="SingleSpaceSeparator"/>
<module name="TypecastParenPad"/>
<module name="WhitespaceAfter"/>
<module name="WhitespaceAround">
<property name="allowEmptyCatches" value="true"/>
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
</module>
<!-- See http://checkstyle.sourceforge.net/config_modifiers.html -->
<module name="ModifierOrder"/>
<!-- See https://checkstyle.org/config_blocks.html -->
<module name="AvoidNestedBlocks"/>
<module name="LeftCurly"/>
<module name="RightCurly"/>
<!-- See http://checkstyle.sourceforge.net/config_design.html -->
<module name="FinalClass"/>
<!-- See http://checkstyle.sourceforge.net/config_misc.html -->
<module name="ArrayTypeStyle"/>
<module name="UpperEll"/>
</module>
</module>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,226 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import co.aikar.util.LoadingIntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class TimingHandler implements Timing {
private static AtomicInteger idPool = new AtomicInteger(1);
private static Deque<TimingHandler> TIMING_STACK = new ArrayDeque<>();
final int id = idPool.getAndIncrement();
final TimingIdentifier identifier;
private final boolean verbose;
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
final TimingData record;
private TimingHandler startParent;
private final TimingHandler groupHandler;
private long start = 0;
private int timingDepth = 0;
private boolean added;
private boolean timed;
private boolean enabled;
TimingHandler(@NotNull TimingIdentifier id) {
this.identifier = id;
this.verbose = id.name.startsWith("##");
this.record = new TimingData(this.id);
this.groupHandler = id.groupHandler;
TimingIdentifier.getGroup(id.group).handlers.add(this);
checkEnabled();
}
final void checkEnabled() {
enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
}
void processTick(boolean violated) {
if (timingDepth != 0 || record.getCurTickCount() == 0) {
timingDepth = 0;
start = 0;
return;
}
record.processTick(violated);
for (TimingData handler : children.values()) {
handler.processTick(violated);
}
}
@NotNull
@Override
public Timing startTimingIfSync() {
startTiming();
return this;
}
@Override
public void stopTimingIfSync() {
stopTiming();
}
@NotNull
public Timing startTiming() {
if (!enabled || !Bukkit.isPrimaryThread()) {
return this;
}
if (++timingDepth == 1) {
startParent = TIMING_STACK.peekLast();
start = System.nanoTime();
}
TIMING_STACK.addLast(this);
return this;
}
public void stopTiming() {
if (!enabled || timingDepth <= 0 || start == 0 || !Bukkit.isPrimaryThread()) {
return;
}
popTimingStack();
if (--timingDepth == 0) {
addDiff(System.nanoTime() - start, startParent);
startParent = null;
start = 0;
}
}
private void popTimingStack() {
TimingHandler last;
while ((last = TIMING_STACK.removeLast()) != this) {
last.timingDepth = 0;
if ("Minecraft".equalsIgnoreCase(last.identifier.group)) {
Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Look above this for any errors and report this to Paper unless it has a plugin in the stack trace (" + last.identifier + " did not stopTiming)");
} else {
Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Report this to the plugin " + last.identifier.group + " (Look for errors above this in the logs) (" + last.identifier + " did not stopTiming)", new Throwable());
}
boolean found = TIMING_STACK.contains(this);
if (!found) {
// We aren't even in the stack... Don't pop everything
TIMING_STACK.addLast(last);
break;
}
}
}
@Override
public final void abort() {
}
void addDiff(long diff, @Nullable TimingHandler parent) {
if (parent != null) {
parent.children.get(id).add(diff);
}
record.add(diff);
if (!added) {
added = true;
timed = true;
TimingsManager.HANDLERS.add(this);
}
if (groupHandler != null) {
groupHandler.addDiff(diff, parent);
groupHandler.children.get(id).add(diff);
}
}
/**
* Reset this timer, setting all values to zero.
*/
void reset(boolean full) {
record.reset();
if (full) {
timed = false;
}
start = 0;
timingDepth = 0;
added = false;
children.clear();
checkEnabled();
}
@NotNull
@Override
public TimingHandler getTimingHandler() {
return this;
}
@Override
public boolean equals(Object o) {
return (this == o);
}
@Override
public int hashCode() {
return id;
}
/**
* This is simply for the Closeable interface so it can be used with try-with-resources ()
*/
@Override
public void close() {
stopTimingIfSync();
}
public boolean isSpecial() {
return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
}
boolean isTimed() {
return timed;
}
public boolean isEnabled() {
return enabled;
}
@NotNull
TimingData[] cloneChildren() {
final TimingData[] clonedChildren = new TimingData[children.size()];
int i = 0;
for (TimingData child : children.values()) {
clonedChildren[i++] = child.clone();
}
return clonedChildren;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,124 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import static net.kyori.adventure.text.Component.text;
@Deprecated(forRemoval = true)
public class TimingsCommand extends BukkitCommand {
private static final List<String> TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste", "verbon", "verboff");
private long lastResetAttempt = 0;
public TimingsCommand(@NotNull String name) {
super(name);
this.description = "Manages Spigot Timings data to see performance of the server.";
this.usageMessage = "/timings <reset|report|on|off|verbon|verboff>";
this.setPermission("bukkit.command.timings");
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) {
if (!testPermission(sender)) {
return true;
}
if (false) {
sender.sendMessage(Timings.deprecationMessage());
}
if (args.length < 1) {
sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED));
return true;
}
final String arg = args[0];
if ("on".equalsIgnoreCase(arg)) {
Timings.setTimingsEnabled(true);
sender.sendMessage(text("Enabled Timings & Reset"));
return true;
} else if ("off".equalsIgnoreCase(arg)) {
Timings.setTimingsEnabled(false);
sender.sendMessage(text("Disabled Timings"));
return true;
}
if (!Timings.isTimingsEnabled()) {
sender.sendMessage(text("Please enable timings by typing /timings on"));
return true;
}
long now = System.currentTimeMillis();
if ("verbon".equalsIgnoreCase(arg)) {
Timings.setVerboseTimingsEnabled(true);
sender.sendMessage(text("Enabled Verbose Timings"));
return true;
} else if ("verboff".equalsIgnoreCase(arg)) {
Timings.setVerboseTimingsEnabled(false);
sender.sendMessage(text("Disabled Verbose Timings"));
return true;
} else if ("reset".equalsIgnoreCase(arg)) {
if (now - lastResetAttempt < 30000) {
TimingsManager.reset();
sender.sendMessage(text("Timings reset. Please wait 5-10 minutes before using /timings report.", NamedTextColor.RED));
} else {
lastResetAttempt = now;
sender.sendMessage(text("WARNING: Timings v2 should not be reset. If you are experiencing lag, please wait 3 minutes and then issue a report. The best timings will include 10+ minutes, with data before and after your lag period. If you really want to reset, run this command again within 30 seconds.", NamedTextColor.RED));
}
} else if (
"paste".equalsIgnoreCase(arg) ||
"report".equalsIgnoreCase(arg) ||
"get".equalsIgnoreCase(arg) ||
"merged".equalsIgnoreCase(arg) ||
"separate".equalsIgnoreCase(arg)
) {
Timings.generateReport(sender);
} else {
sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED));
}
return true;
}
@NotNull
@Override
public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) {
Preconditions.checkNotNull(sender, "Sender cannot be null");
Preconditions.checkNotNull(args, "Arguments cannot be null");
Preconditions.checkNotNull(alias, "Alias cannot be null");
if (args.length == 1) {
return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS,
new ArrayList<String>(TIMINGS_SUBCOMMANDS.size()));
}
return ImmutableList.of();
}
}

View file

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

View file

@ -0,0 +1,87 @@
package co.aikar.timings;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.MessageCommandSender;
import org.bukkit.command.RemoteConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@Deprecated(forRemoval = true)
@SuppressWarnings("WeakerAccess")
public class TimingsReportListener implements net.kyori.adventure.audience.ForwardingAudience, MessageCommandSender {
private final List<CommandSender> senders;
private final Runnable onDone;
private String timingsURL;
public TimingsReportListener(@NotNull CommandSender senders) {
this(senders, null);
}
public TimingsReportListener(@NotNull CommandSender sender, @Nullable Runnable onDone) {
this(Lists.newArrayList(sender), onDone);
}
public TimingsReportListener(@NotNull List<CommandSender> senders) {
this(senders, null);
}
public TimingsReportListener(@NotNull List<CommandSender> senders, @Nullable Runnable onDone) {
Preconditions.checkNotNull(senders);
Preconditions.checkArgument(!senders.isEmpty(), "senders is empty");
this.senders = Lists.newArrayList(senders);
this.onDone = onDone;
}
@Nullable
public String getTimingsURL() {
return timingsURL;
}
public void done() {
done(null);
}
public void done(@Nullable String url) {
this.timingsURL = url;
if (onDone != null) {
onDone.run();
}
for (CommandSender sender : senders) {
if (sender instanceof TimingsReportListener) {
((TimingsReportListener) sender).done();
}
}
}
@Override
public void sendMessage(final @NotNull net.kyori.adventure.identity.Identity source, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) {
net.kyori.adventure.audience.ForwardingAudience.super.sendMessage(source, message, type);
}
@NotNull
@Override
public Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
return this.senders;
}
@Override
public void sendMessage(@NotNull String message) {
senders.forEach((sender) -> sender.sendMessage(message));
}
public void addConsoleIfNeeded() {
boolean hasConsole = false;
for (CommandSender sender : this.senders) {
if (sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
hasConsole = true;
}
}
if (!hasConsole) {
this.senders.add(Bukkit.getConsoleSender());
}
}
}

View file

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

View file

@ -0,0 +1,39 @@
package co.aikar.util;
import com.google.common.collect.ForwardingMap;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Deprecated(forRemoval = true)
public class Counter <T> extends ForwardingMap<T, Long> {
private final Map<T, Long> counts = new HashMap<>();
public long decrement(@Nullable T key) {
return increment(key, -1);
}
public long increment(@Nullable T key) {
return increment(key, 1);
}
public long decrement(@Nullable T key, long amount) {
return increment(key, -amount);
}
public long increment(@Nullable T key, long amount) {
Long count = this.getCount(key);
count += amount;
this.counts.put(key, count);
return count;
}
public long getCount(@Nullable T key) {
return this.counts.getOrDefault(key, 0L);
}
@NotNull
@Override
protected Map<T, Long> delegate() {
return this.counts;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,52 @@
package com.destroystokyo.paper;
import net.kyori.adventure.translation.Translatable;
import net.kyori.adventure.util.Index;
import org.jetbrains.annotations.NotNull;
import org.bukkit.inventory.MainHand;
public final class ClientOption<T> {
public static final ClientOption<SkinParts> SKIN_PARTS = new ClientOption<>(SkinParts.class);
public static final ClientOption<Boolean> CHAT_COLORS_ENABLED = new ClientOption<>(Boolean.class);
public static final ClientOption<ChatVisibility> CHAT_VISIBILITY = new ClientOption<>(ChatVisibility.class);
public static final ClientOption<String> LOCALE = new ClientOption<>(String.class);
public static final ClientOption<MainHand> MAIN_HAND = new ClientOption<>(MainHand.class);
public static final ClientOption<Integer> VIEW_DISTANCE = new ClientOption<>(Integer.class);
public static final ClientOption<Boolean> ALLOW_SERVER_LISTINGS = new ClientOption<>(Boolean.class);
public static final ClientOption<Boolean> TEXT_FILTERING_ENABLED = new ClientOption<>(Boolean.class);
private final Class<T> type;
private ClientOption(@NotNull Class<T> type) {
this.type = type;
}
@NotNull
public Class<T> getType() {
return type;
}
public enum ChatVisibility implements Translatable {
FULL("full"),
SYSTEM("system"),
HIDDEN("hidden"),
UNKNOWN("unknown");
public static Index<String, ChatVisibility> NAMES = Index.create(ChatVisibility.class, chatVisibility -> chatVisibility.name);
private final String name;
ChatVisibility(String name) {
this.name = name;
}
@Override
public @NotNull String translationKey() {
if (this == UNKNOWN) {
throw new UnsupportedOperationException(this.name + " doesn't have a translation key");
}
return "options.chat.visibility." + this.name;
}
}
}

View file

@ -0,0 +1,39 @@
package com.destroystokyo.paper;
import org.jetbrains.annotations.ApiStatus;
/**
* Enumeration of different heightmap types maintained by the server. Generally using these maps is much faster
* than using an iterative search for a block in a given x, z coordinate.
*
* @deprecated Upstream has added their own API for using the game heightmaps. See {@link org.bukkit.HeightMap} and the
* non-deprecated getHighestBlock methods on World such as {@link org.bukkit.World#getHighestBlockAt(org.bukkit.Location, org.bukkit.HeightMap)}.
*/
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21")
public enum HeightmapType {
/**
* The highest block used for lighting in the world. Also the block returned by {@link org.bukkit.World#getHighestBlockYAt(int, int)}}
*/
LIGHT_BLOCKING,
/**
* References the highest block in the world.
*/
ANY,
/**
* References the highest solid block in a world.
*/
SOLID,
/**
* References the highest solid or liquid block in a world.
*/
SOLID_OR_LIQUID,
/**
* References the highest solid or liquid block in a world, excluding leaves.
*/
SOLID_OR_LIQUID_NO_LEAVES;
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
*/
package com.destroystokyo.paper;
import com.google.common.collect.Lists;
import io.papermc.paper.tag.BaseTag;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
import java.util.Collection;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MaterialSetTag extends BaseTag<Material, MaterialSetTag> {
/**
* @deprecated Use NamespacedKey version of constructor
*/
@Deprecated
public MaterialSetTag(@NotNull Predicate<Material> filter) {
this(null, Stream.of(Material.values()).filter(filter).collect(Collectors.toList()));
}
/**
* @deprecated Use NamespacedKey version of constructor
*/
@Deprecated
public MaterialSetTag(@NotNull Collection<Material> materials) {
this(null, materials);
}
/**
* @deprecated Use NamespacedKey version of constructor
*/
@Deprecated
public MaterialSetTag(@NotNull Material... materials) {
this(null, materials);
}
public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Predicate<Material> filter) {
this(key, Stream.of(Material.values()).filter(filter).collect(Collectors.toList()));
}
public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Material... materials) {
this(key, Lists.newArrayList(materials));
}
public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection<Material> materials) {
this(key != null ? key : NamespacedKey.randomKey(), materials, ((Predicate<Material>) Material::isLegacy).negate());
}
public MaterialSetTag(@Nullable NamespacedKey key, @NotNull Collection<Material> materials, @NotNull Predicate<Material>...globalPredicates) {
super(Material.class, key != null ? key : NamespacedKey.randomKey(), materials, globalPredicates);
}
@NotNull
@Override
protected Set<Material> getAllPossibleValues() {
return Stream.of(Material.values()).collect(Collectors.toSet());
}
@Override
@NotNull
protected String getName(@NotNull Material value) {
return value.name();
}
public boolean isTagged(@NotNull BlockData block) {
return isTagged(block.getMaterial());
}
public boolean isTagged(@NotNull BlockState block) {
return isTagged(block.getType());
}
public boolean isTagged(@NotNull Block block) {
return isTagged(block.getType());
}
public boolean isTagged(@NotNull ItemStack item) {
return isTagged(item.getType());
}
public boolean isTagged(@NotNull Material material) {
return this.tagged.contains(material);
}
}

View file

@ -0,0 +1,717 @@
/*
* Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.destroystokyo.paper;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Tag;
/**
* Represents a collection tags to identify materials that share common properties.
* Will map to minecraft for missing tags, as well as custom ones that may be useful.
* <p>
* All tags in this class are unmodifiable, attempting to modify them will throw an
* {@link UnsupportedOperationException}.
*/
@SuppressWarnings({"NonFinalUtilityClass", "unused", "WeakerAccess"})
public class MaterialTags {
private static NamespacedKey keyFor(String key) {
//noinspection deprecation
return new NamespacedKey("paper", key + "_settag");
}
public static final MaterialSetTag ARROWS = new MaterialSetTag(keyFor("arrows"))
.endsWith("ARROW")
.ensureSize("ARROWS", 3).lock();
/**
* Covers all colors of beds.
*/
public static final MaterialSetTag BEDS = new MaterialSetTag(keyFor("beds"))
.endsWith("_BED")
.ensureSize("BEDS", 16).lock();
/**
* Covers all bucket items.
*/
public static final MaterialSetTag BUCKETS = new MaterialSetTag(keyFor("buckets"))
.endsWith("BUCKET")
.ensureSize("BUCKETS", 11).lock();
/**
* Covers coal and charcoal.
*/
public static final MaterialSetTag COALS = new MaterialSetTag(keyFor("coals"))
.add(Material.COAL, Material.CHARCOAL).lock();
/**
* Covers both cobblestone wall variants.
*/
public static final MaterialSetTag COBBLESTONE_WALLS = new MaterialSetTag(keyFor("cobblestone_walls"))
.endsWith("COBBLESTONE_WALL")
.ensureSize("COBBLESTONE_WALLS", 2).lock();
/**
* Covers both cobblestone and mossy Cobblestone.
*/
public static final MaterialSetTag COBBLESTONES = new MaterialSetTag(keyFor("cobblestones"))
.add(Material.COBBLESTONE, Material.MOSSY_COBBLESTONE).lock();
/**
* Covers all colors of concrete.
*/
public static final MaterialSetTag CONCRETES = new MaterialSetTag(keyFor("concretes"))
.endsWith("_CONCRETE")
.ensureSize("CONCRETES", 16).lock();
/**
* Covers all colors of concrete powder.
*/
public static final MaterialSetTag CONCRETE_POWDER = new MaterialSetTag(keyFor("concrete_powder"))
.endsWith("_CONCRETE_POWDER")
.ensureSize("CONCRETE_POWDER", 16).lock();
/**
* Covers the two types of cooked fish.
*/
public static final MaterialSetTag COOKED_FISH = new MaterialSetTag(keyFor("cooked_fish"))
.add(Material.COOKED_COD, Material.COOKED_SALMON).lock();
/**
* Covers all variants of doors.
*/
public static final MaterialSetTag DOORS = new MaterialSetTag(keyFor("doors"))
.endsWith("_DOOR")
.ensureSize("DOORS", 20).lock();
/**
* Covers all dyes.
*/
public static final MaterialSetTag DYES = new MaterialSetTag(keyFor("dyes"))
.endsWith("_DYE")
.ensureSize("DYES", 16).lock();
/**
* Covers all variants of gates.
*/
public static final MaterialSetTag FENCE_GATES = new MaterialSetTag(keyFor("fence_gates"))
.endsWith("_GATE")
.ensureSize("FENCE_GATES", 11).lock();
/**
* Covers all variants of fences.
*/
public static final MaterialSetTag FENCES = new MaterialSetTag(keyFor("fences"))
.endsWith("_FENCE")
.ensureSize("FENCES", 12).lock();
/**
* Covers all variants of fish buckets.
*/
public static final MaterialSetTag FISH_BUCKETS = new MaterialSetTag(keyFor("fish_buckets"))
.add(Material.COD_BUCKET, Material.PUFFERFISH_BUCKET, Material.SALMON_BUCKET, Material.TROPICAL_FISH_BUCKET).lock();
/**
* Covers the non-colored glass and 16 stained glass (not panes).
*/
public static final MaterialSetTag GLASS = new MaterialSetTag(keyFor("glass"))
.endsWith("_GLASS")
.add(Material.GLASS)
.ensureSize("GLASS", 18).lock();
/**
* Covers the non-colored glass panes and stained glass panes (panes only).
*/
public static final MaterialSetTag GLASS_PANES = new MaterialSetTag(keyFor("glass_panes"))
.endsWith("GLASS_PANE")
.ensureSize("GLASS_PANES", 17).lock();
/**
* Covers all glazed terracotta blocks.
*/
public static final MaterialSetTag GLAZED_TERRACOTTA = new MaterialSetTag(keyFor("glazed_terracotta"))
.endsWith("GLAZED_TERRACOTTA")
.ensureSize("GLAZED_TERRACOTTA", 16).lock();
/**
* Covers the colors of stained terracotta.
*/
public static final MaterialSetTag STAINED_TERRACOTTA = new MaterialSetTag(keyFor("stained_terracotta"))
.endsWith("TERRACOTTA")
.not(Material.TERRACOTTA)
.notEndsWith("GLAZED_TERRACOTTA")
.ensureSize("STAINED_TERRACOTTA", 16).lock();
/**
* Covers terracotta along with the stained variants.
*/
public static final MaterialSetTag TERRACOTTA = new MaterialSetTag(keyFor("terracotta"))
.endsWith("TERRACOTTA")
.ensureSize("TERRACOTTA", 33).lock();
/**
* Covers both golden apples.
*/
public static final MaterialSetTag GOLDEN_APPLES = new MaterialSetTag(keyFor("golden_apples"))
.endsWith("GOLDEN_APPLE")
.ensureSize("GOLDEN_APPLES", 2).lock();
/**
* Covers the variants of horse armor.
*/
public static final MaterialSetTag HORSE_ARMORS = new MaterialSetTag(keyFor("horse_armors"))
.endsWith("_HORSE_ARMOR")
.ensureSize("HORSE_ARMORS", 4).lock();
/**
* Covers the variants of infested blocks.
*/
public static final MaterialSetTag INFESTED_BLOCKS = new MaterialSetTag(keyFor("infested_blocks"))
.startsWith("INFESTED_")
.ensureSize("INFESTED_BLOCKS", 7).lock();
/**
* Covers the variants of mushroom blocks.
*/
public static final MaterialSetTag MUSHROOM_BLOCKS = new MaterialSetTag(keyFor("mushroom_blocks"))
.endsWith("MUSHROOM_BLOCK")
.add(Material.MUSHROOM_STEM)
.ensureSize("MUSHROOM_BLOCKS", 3).lock();
/**
* Covers all mushrooms.
*/
public static final MaterialSetTag MUSHROOMS = new MaterialSetTag(keyFor("mushrooms"))
.add(Material.BROWN_MUSHROOM, Material.RED_MUSHROOM).lock();
/**
* Covers all music disc items.
*/
public static final MaterialSetTag MUSIC_DISCS = new MaterialSetTag(keyFor("music_discs"))
.startsWith("MUSIC_DISC_").lock();
/**
* Covers all ores.
*/
public static final MaterialSetTag ORES = new MaterialSetTag(keyFor("ores"))
.add(Material.ANCIENT_DEBRIS)
.endsWith("_ORE")
.ensureSize("ORES", 19).lock();
/**
* Covers all piston typed items and blocks including the piston head and moving piston.
*/
public static final MaterialSetTag PISTONS = new MaterialSetTag(keyFor("pistons"))
.contains("PISTON")
.ensureSize("PISTONS", 4).lock();
/**
* Covers all potato items.
*/
public static final MaterialSetTag POTATOES = new MaterialSetTag(keyFor("potatoes"))
.endsWith("POTATO")
.ensureSize("POTATOES", 3).lock();
/**
* Covers all wooden pressure plates and the weighted pressure plates and the stone pressure plate.
*/
public static final MaterialSetTag PRESSURE_PLATES = new MaterialSetTag(keyFor("pressure_plates"))
.endsWith("_PRESSURE_PLATE")
.ensureSize("PRESSURE_PLATES", 15).lock();
/**
* Covers the variants of prismarine blocks.
*/
public static final MaterialSetTag PRISMARINE = new MaterialSetTag(keyFor("prismarine"))
.add(Material.PRISMARINE, Material.PRISMARINE_BRICKS, Material.DARK_PRISMARINE).lock();
/**
* Covers the variants of prismarine slabs.
*/
public static final MaterialSetTag PRISMARINE_SLABS = new MaterialSetTag(keyFor("prismarine_slabs"))
.add(Material.PRISMARINE_SLAB, Material.PRISMARINE_BRICK_SLAB, Material.DARK_PRISMARINE_SLAB).lock();
/**
* Covers the variants of prismarine stairs.
*/
public static final MaterialSetTag PRISMARINE_STAIRS = new MaterialSetTag(keyFor("prismarine_stairs"))
.add(Material.PRISMARINE_STAIRS, Material.PRISMARINE_BRICK_STAIRS, Material.DARK_PRISMARINE_STAIRS).lock();
/**
* Covers the variants of pumpkins.
*/
public static final MaterialSetTag PUMPKINS = new MaterialSetTag(keyFor("pumpkins"))
.add(Material.CARVED_PUMPKIN, Material.JACK_O_LANTERN, Material.PUMPKIN).lock();
/**
* Covers the variants of quartz blocks.
*/
public static final MaterialSetTag QUARTZ_BLOCKS = new MaterialSetTag(keyFor("quartz_blocks"))
.add(Material.QUARTZ_BLOCK, Material.QUARTZ_PILLAR, Material.CHISELED_QUARTZ_BLOCK, Material.SMOOTH_QUARTZ).lock();
/**
* Covers all uncooked fish items.
*/
public static final MaterialSetTag RAW_FISH = new MaterialSetTag(keyFor("raw_fish"))
.add(Material.COD, Material.PUFFERFISH, Material.SALMON, Material.TROPICAL_FISH).lock();
/**
* Covers the variants of red sandstone blocks.
*/
public static final MaterialSetTag RED_SANDSTONES = new MaterialSetTag(keyFor("red_sandstones"))
.endsWith("RED_SANDSTONE")
.ensureSize("RED_SANDSTONES", 4).lock();
/**
* Covers the variants of sandstone blocks.
*/
public static final MaterialSetTag SANDSTONES = new MaterialSetTag(keyFor("sandstones"))
.add(Material.SANDSTONE, Material.CHISELED_SANDSTONE, Material.CUT_SANDSTONE, Material.SMOOTH_SANDSTONE).lock();
/**
* Covers sponge and wet sponge.
*/
public static final MaterialSetTag SPONGES = new MaterialSetTag(keyFor("sponges"))
.endsWith("SPONGE")
.ensureSize("SPONGES", 2).lock();
/**
* Covers the non-colored and colored shulker boxes.
*/
public static final MaterialSetTag SHULKER_BOXES = new MaterialSetTag(keyFor("shulker_boxes"))
.endsWith("SHULKER_BOX")
.ensureSize("SHULKER_BOXES", 17).lock();
/**
* Covers zombie, creeper, skeleton, dragon, and player heads.
*/
public static final MaterialSetTag SKULLS = new MaterialSetTag(keyFor("skulls"))
.endsWith("_HEAD")
.endsWith("_SKULL")
.not(Material.PISTON_HEAD)
.ensureSize("SKULLS", 14).lock();
/**
* Covers all spawn egg items.
*/
public static final MaterialSetTag SPAWN_EGGS = new MaterialSetTag(keyFor("spawn_eggs"))
.endsWith("_SPAWN_EGG")
.ensureSize("SPAWN_EGGS", 78).lock();
/**
* Covers all colors of stained glass.
*/
public static final MaterialSetTag STAINED_GLASS = new MaterialSetTag(keyFor("stained_glass"))
.endsWith("_STAINED_GLASS")
.ensureSize("STAINED_GLASS", 16).lock();
/**
* Covers all colors of stained glass panes.
*/
public static final MaterialSetTag STAINED_GLASS_PANES = new MaterialSetTag(keyFor("stained_glass_panes"))
.endsWith("STAINED_GLASS_PANE")
.ensureSize("STAINED_GLASS_PANES", 16).lock();
/**
* Covers all variants of trapdoors.
*/
public static final MaterialSetTag TRAPDOORS = new MaterialSetTag(keyFor("trapdoors"))
.endsWith("_TRAPDOOR")
.ensureSize("TRAPDOORS", 20).lock();
/**
* Covers all wood variants of doors.
*/
public static final MaterialSetTag WOODEN_DOORS = new MaterialSetTag(keyFor("wooden_doors"))
.endsWith("_DOOR")
.not(Material.IRON_DOOR)
.notContains("COPPER")
.ensureSize("WOODEN_DOORS", 11).lock();
/**
* Covers all wood variants of fences.
*/
public static final MaterialSetTag WOODEN_FENCES = new MaterialSetTag(keyFor("wooden_fences"))
.endsWith("_FENCE")
.not(Material.NETHER_BRICK_FENCE)
.ensureSize("WOODEN_FENCES", 11).lock();
/**
* Covers all wood variants of trapdoors.
*/
public static final MaterialSetTag WOODEN_TRAPDOORS = new MaterialSetTag(keyFor("wooden_trapdoors"))
.endsWith("_TRAPDOOR")
.not(Material.IRON_TRAPDOOR)
.notContains("COPPER")
.ensureSize("WOODEN_TRAPDOORS", 11).lock();
/**
* Covers the wood variants of gates.
*/
public static final MaterialSetTag WOODEN_GATES = new MaterialSetTag(keyFor("wooden_gates"))
.endsWith("_GATE")
.ensureSize("WOODEN_GATES", 11).lock();
/**
* Covers the variants of purpur.
*/
public static final MaterialSetTag PURPUR = new MaterialSetTag(keyFor("purpur"))
.startsWith("PURPUR_")
.ensureSize("PURPUR", 4).lock();
/**
* Covers the variants of signs.
*/
public static final MaterialSetTag SIGNS = new MaterialSetTag(keyFor("signs"))
.endsWith("_SIGN")
.ensureSize("SIGNS", 44).lock();
/**
* Covers the variants of a regular torch.
*/
public static final MaterialSetTag TORCH = new MaterialSetTag(keyFor("torch"))
.add(Material.TORCH, Material.WALL_TORCH)
.ensureSize("TORCH", 2).lock();
/**
* Covers the variants of a redstone torch.
*/
public static final MaterialSetTag REDSTONE_TORCH = new MaterialSetTag(keyFor("restone_torch"))
.add(Material.REDSTONE_TORCH, Material.REDSTONE_WALL_TORCH)
.ensureSize("REDSTONE_TORCH", 2).lock();
/**
* Covers the variants of a soul torch.
*/
public static final MaterialSetTag SOUL_TORCH = new MaterialSetTag(keyFor("soul_torch"))
.add(Material.SOUL_TORCH, Material.SOUL_WALL_TORCH)
.ensureSize("SOUL_TORCH", 2).lock();
/**
* Covers the variants of torches.
*/
public static final MaterialSetTag TORCHES = new MaterialSetTag(keyFor("torches"))
.add(TORCH, REDSTONE_TORCH, SOUL_TORCH)
.ensureSize("TORCHES", 6).lock();
/**
* Covers the variants of lanterns.
*/
public static final MaterialSetTag LANTERNS = new MaterialSetTag(keyFor("lanterns"))
.add(Material.LANTERN, Material.SOUL_LANTERN)
.ensureSize("LANTERNS", 2).lock();
/**
* Covers the variants of rails.
*/
public static final MaterialSetTag RAILS = new MaterialSetTag(keyFor("rails"))
.endsWith("RAIL")
.ensureSize("RAILS", 4).lock();
/**
* Covers the variants of swords.
*/
public static final MaterialSetTag SWORDS = new MaterialSetTag(keyFor("swords"))
.endsWith("_SWORD")
.ensureSize("SWORDS", 6).lock();
/**
* Covers the variants of shovels.
*/
public static final MaterialSetTag SHOVELS = new MaterialSetTag(keyFor("shovels"))
.endsWith("_SHOVEL")
.ensureSize("SHOVELS", 6).lock();
/**
* Covers the variants of pickaxes.
*/
public static final MaterialSetTag PICKAXES = new MaterialSetTag(keyFor("pickaxes"))
.endsWith("_PICKAXE")
.ensureSize("PICKAXES", 6).lock();
/**
* Covers the variants of axes.
*/
public static final MaterialSetTag AXES = new MaterialSetTag(keyFor("axes"))
.endsWith("_AXE")
.ensureSize("AXES", 6).lock();
/**
* Covers the variants of hoes.
*/
public static final MaterialSetTag HOES = new MaterialSetTag(keyFor("hoes"))
.endsWith("_HOE")
.ensureSize("HOES", 6).lock();
/**
* Covers the variants of helmets.
*/
public static final MaterialSetTag HELMETS = new MaterialSetTag(keyFor("helmets"))
.endsWith("_HELMET")
.ensureSize("HELMETS", 7).lock();
/**
* Covers the variants of items that can be equipped in the helmet slot.
*/
public static final MaterialSetTag HEAD_EQUIPPABLE = new MaterialSetTag(keyFor("head_equippable"))
.endsWith("_HELMET")
.add(SKULLS)
.add(Material.CARVED_PUMPKIN)
.ensureSize("HEAD_EQUIPPABLE", 22).lock();
/**
* Covers the variants of chestplate.
*/
public static final MaterialSetTag CHESTPLATES = new MaterialSetTag(keyFor("chestplates"))
.endsWith("_CHESTPLATE")
.ensureSize("CHESTPLATES", 6).lock();
/**
* Covers the variants of items that can be equipped in the chest slot.
*/
public static final MaterialSetTag CHEST_EQUIPPABLE = new MaterialSetTag(keyFor("chest_equippable"))
.endsWith("_CHESTPLATE")
.add(Material.ELYTRA)
.ensureSize("CHEST_EQUIPPABLE", 7).lock();
/**
* Covers the variants of leggings.
*/
public static final MaterialSetTag LEGGINGS = new MaterialSetTag(keyFor("leggings"))
.endsWith("_LEGGINGS")
.ensureSize("LEGGINGS", 6).lock();
/**
* Covers the variants of boots.
*/
public static final MaterialSetTag BOOTS = new MaterialSetTag(keyFor("boots"))
.endsWith("_BOOTS")
.ensureSize("BOOTS", 6).lock();
/**
* Covers all variants of armor.
*/
public static final MaterialSetTag ARMOR = new MaterialSetTag(keyFor("armor")).add(HELMETS, CHESTPLATES, LEGGINGS, BOOTS)
.ensureSize("ARMOR", 25).lock();
/**
* Covers the variants of bows.
*/
public static final MaterialSetTag BOWS = new MaterialSetTag(keyFor("bows"))
.add(Material.BOW)
.add(Material.CROSSBOW)
.ensureSize("BOWS", 2).lock();
/**
* Covers the variants of player-throwable projectiles (not requiring a bow or any other "assistance").
*/
public static final MaterialSetTag THROWABLE_PROJECTILES = new MaterialSetTag(keyFor("throwable_projectiles"))
.add(Material.EGG, Material.SNOWBALL, Material.SPLASH_POTION, Material.TRIDENT, Material.ENDER_PEARL, Material.EXPERIENCE_BOTTLE, Material.FIREWORK_ROCKET).lock();
/**
* Covers materials that can be colored, such as wool, shulker boxes, stained glass etc.
*/
@SuppressWarnings("unchecked")
public static final MaterialSetTag COLORABLE = new MaterialSetTag(keyFor("colorable"))
.add(Tag.WOOL, Tag.CARPETS).add(SHULKER_BOXES, STAINED_GLASS, STAINED_GLASS_PANES, CONCRETES, BEDS).lock();
//.ensureSize("COLORABLE", 81).lock(); unit test don't have the vanilla item tags, so counts don't line up for real
/**
* Covers the variants of coral.
*/
public static final MaterialSetTag CORAL = new MaterialSetTag(keyFor("coral"))
.endsWith("_CORAL")
.ensureSize("CORAL", 10).lock();
/**
* Covers the variants of coral fans.
*/
public static final MaterialSetTag CORAL_FANS = new MaterialSetTag(keyFor("coral_fans"))
.endsWith("_CORAL_FAN")
.endsWith("_CORAL_WALL_FAN")
.ensureSize("CORAL_FANS", 20).lock();
/**
* Covers the variants of coral blocks.
*/
public static final MaterialSetTag CORAL_BLOCKS = new MaterialSetTag(keyFor("coral_blocks"))
.endsWith("_CORAL_BLOCK")
.ensureSize("CORAL_BLOCKS", 10).lock();
/**
* Covers all items that can be enchanted from the enchantment table or anvil.
*/
public static final MaterialSetTag ENCHANTABLE = new MaterialSetTag(keyFor("enchantable"))
.add(PICKAXES, SWORDS, SHOVELS, AXES, HOES, HELMETS, CHEST_EQUIPPABLE, LEGGINGS, BOOTS, BOWS)
.add(Material.TRIDENT, Material.SHIELD, Material.FISHING_ROD, Material.SHEARS,
Material.FLINT_AND_STEEL, Material.CARROT_ON_A_STICK, Material.WARPED_FUNGUS_ON_A_STICK,
Material.BRUSH, Material.CARVED_PUMPKIN, Material.COMPASS, Material.SKELETON_SKULL,
Material.WITHER_SKELETON_SKULL, Material.PLAYER_HEAD, Material.ZOMBIE_HEAD,
Material.CREEPER_HEAD, Material.DRAGON_HEAD, Material.PIGLIN_HEAD)
.ensureSize("ENCHANTABLE", 75).lock();
/**
* Covers the variants of raw ores.
*/
public static final MaterialSetTag RAW_ORES = new MaterialSetTag(keyFor("raw_ores"))
.add(Material.RAW_COPPER, Material.RAW_GOLD, Material.RAW_IRON).lock();
/**
* Covers the variants of deepslate ores.
*/
public static final MaterialSetTag DEEPSLATE_ORES = new MaterialSetTag(keyFor("deepslate_ores"))
.add(material -> material.name().startsWith("DEEPSLATE_") && material.name().endsWith("_ORE"))
.ensureSize("DEEPSLATE_ORES", 8).lock();
/**
* Covers the variants of raw ore blocks.
*/
public static final MaterialSetTag RAW_ORE_BLOCKS = new MaterialSetTag(keyFor("raw_ore_blocks"))
.add(Material.RAW_COPPER_BLOCK, Material.RAW_GOLD_BLOCK, Material.RAW_IRON_BLOCK).lock();
/**
* Covers all oxidized copper blocks.
*/
public static final MaterialSetTag OXIDIZED_COPPER_BLOCKS = new MaterialSetTag(keyFor("oxidized_copper_blocks"))
.startsWith("OXIDIZED_").startsWith("WAXED_OXIDIZED_").ensureSize("OXIDIZED_COPPER_BLOCKS", 18).lock();
/**
* Covers all weathered copper blocks.
*/
public static final MaterialSetTag WEATHERED_COPPER_BLOCKS = new MaterialSetTag(keyFor("weathered_copper_blocks"))
.startsWith("WEATHERED_").startsWith("WAXED_WEATHERED_").ensureSize("WEATHERED_COPPER_BLOCKS", 18).lock();
/**
* Covers all exposed copper blocks.
*/
public static final MaterialSetTag EXPOSED_COPPER_BLOCKS = new MaterialSetTag(keyFor("exposed_copper_blocks"))
.startsWith("EXPOSED_").startsWith("WAXED_EXPOSED_").ensureSize("EXPOSED_COPPER_BLOCKS", 18).lock();
/**
* Covers all un-weathered copper blocks.
*/
public static final MaterialSetTag UNAFFECTED_COPPER_BLOCKS = new MaterialSetTag(keyFor("unaffected_copper_blocks"))
.startsWith("CUT_COPPER").startsWith("WAXED_CUT_COPPER")
.startsWith("WAXED_COPPER_").startsWith("COPPER_")
.add(Material.CHISELED_COPPER, Material.WAXED_CHISELED_COPPER)
.not(Material.COPPER_INGOT, Material.COPPER_ORE)
.ensureSize("UNAFFECTED_COPPER_BLOCKS", 18).lock();
/**
* Covers all waxed copper blocks.
* <p>
* Combine with other copper-related tags to filter is-waxed or not.
*/
public static final MaterialSetTag WAXED_COPPER_BLOCKS = new MaterialSetTag(keyFor("waxed_copper_blocks"))
.add(m -> m.name().startsWith("WAXED_") && m.name().contains("COPPER")).ensureSize("WAXED_COPPER_BLOCKS", 36).lock();
/**
* Covers all un-waxed copper blocks.
* <p>
* Combine with other copper-related tags to filter is-un-waxed or not.
*/
public static final MaterialSetTag UNWAXED_COPPER_BLOCKS = new MaterialSetTag(keyFor("unwaxed_copper_blocks"))
.startsWith("EXPOSED_").startsWith("WEATHERED_").startsWith("OXIDIZED_")
.startsWith("CUT_COPPER")
.add(Material.COPPER_BLOCK, Material.CHISELED_COPPER, Material.COPPER_DOOR, Material.COPPER_TRAPDOOR, Material.COPPER_GRATE, Material.COPPER_BULB)
.ensureSize("UNWAXED_COPPER_BLOCKS", 36).lock();
/**
* Covers all copper block variants.
*/
public static final MaterialSetTag COPPER_BLOCKS = new MaterialSetTag(keyFor("copper_blocks"))
.add(WAXED_COPPER_BLOCKS).add(UNWAXED_COPPER_BLOCKS).ensureSize("COPPER_BLOCKS", 72).lock();
/**
* Covers all weathering/waxed states of the plain copper block.
*/
public static final MaterialSetTag FULL_COPPER_BLOCKS = new MaterialSetTag(keyFor("full_copper_blocks"))
.endsWith("OXIDIZED_COPPER")
.endsWith("WEATHERED_COPPER")
.endsWith("EXPOSED_COPPER")
.endsWith("COPPER_BLOCK")
.not(Material.RAW_COPPER_BLOCK)
.ensureSize("FULL_COPPER_BLOCKS", 8).lock();
/**
* Covers all weathering/waxed states of the cut copper block.
*/
public static final MaterialSetTag CUT_COPPER_BLOCKS = new MaterialSetTag(keyFor("cut_copper_blocks"))
.endsWith("CUT_COPPER").ensureSize("CUT_COPPER_BLOCKS", 8).lock();
/**
* Covers all weathering/waxed states of the cut copper stairs.
*/
public static final MaterialSetTag CUT_COPPER_STAIRS = new MaterialSetTag(keyFor("cut_copper_stairs"))
.endsWith("CUT_COPPER_STAIRS").ensureSize("CUT_COPPER_STAIRS", 8).lock();
/**
* Covers all weathering/waxed states of the cut copper slab.
*/
public static final MaterialSetTag CUT_COPPER_SLABS = new MaterialSetTag(keyFor("cut_copper_slabs"))
.endsWith("CUT_COPPER_SLAB").ensureSize("CUT_COPPER_SLABS", 8).lock();
/**
* Covers all Wooden Tools.
*/
public static final MaterialSetTag WOODEN_TOOLS = new MaterialSetTag(keyFor("wooden_tools"))
.add(Material.WOODEN_AXE, Material.WOODEN_HOE, Material.WOODEN_PICKAXE, Material.WOODEN_SHOVEL, Material.WOODEN_SWORD)
.ensureSize("WOODEN_TOOLS", 5).lock();
/**
* Covers all Stone Tools.
*/
public static final MaterialSetTag STONE_TOOLS = new MaterialSetTag(keyFor("stone_tools"))
.add(Material.STONE_AXE, Material.STONE_HOE, Material.STONE_PICKAXE, Material.STONE_SHOVEL, Material.STONE_SWORD)
.ensureSize("STONE_TOOLS", 5).lock();
/**
* Covers all Iron Tools.
*/
public static final MaterialSetTag IRON_TOOLS = new MaterialSetTag(keyFor("iron_tools"))
.add(Material.IRON_AXE, Material.IRON_HOE, Material.IRON_PICKAXE, Material.IRON_SHOVEL, Material.IRON_SWORD)
.ensureSize("IRON_TOOLS", 5).lock();
/**
* Covers all Gold Tools.
*/
public static final MaterialSetTag GOLDEN_TOOLS = new MaterialSetTag(keyFor("golden_tools"))
.add(Material.GOLDEN_AXE, Material.GOLDEN_HOE, Material.GOLDEN_PICKAXE, Material.GOLDEN_SHOVEL, Material.GOLDEN_SWORD)
.ensureSize("GOLDEN_TOOLS", 5).lock();
/**
* Covers all Diamond Tools.
*/
public static final MaterialSetTag DIAMOND_TOOLS = new MaterialSetTag(keyFor("diamond_tools"))
.add(Material.DIAMOND_AXE, Material.DIAMOND_HOE, Material.DIAMOND_PICKAXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_SWORD)
.ensureSize("DIAMOND_TOOLS", 5).lock();
/**
* Covers all Netherite Tools.
*/
public static final MaterialSetTag NETHERITE_TOOLS = new MaterialSetTag(keyFor("netherite_tools"))
.add(Material.NETHERITE_AXE, Material.NETHERITE_HOE, Material.NETHERITE_PICKAXE, Material.NETHERITE_SHOVEL, Material.NETHERITE_SWORD)
.ensureSize("NETHERITE_TOOLS", 5).lock();
}

View file

@ -0,0 +1,40 @@
package com.destroystokyo.paper;
import org.jetbrains.annotations.NotNull;
/**
* Represents a namespaced resource, see {@link org.bukkit.NamespacedKey} for single elements
* or {@link com.destroystokyo.paper.NamespacedTag} for a collection of elements
*
* Namespaces may only contain lowercase alphanumeric characters, periods,
* underscores, and hyphens.
* <p>
* Keys may only contain lowercase alphanumeric characters, periods,
* underscores, hyphens, and forward slashes.
* <p>
* You should not be implementing this interface yourself, use {@link org.bukkit.NamespacedKey}
* or {@link com.destroystokyo.paper.NamespacedTag} as needed instead.
*/
public interface Namespaced {
/**
* Gets the namespace this resource is a part of
* <p>
* This is contractually obligated to only contain lowercase alphanumeric characters,
* periods, underscores, and hyphens.
*
* @return resource namespace
*/
@NotNull
String getNamespace();
/**
* Gets the key corresponding to this resource
* <p>
* This is contractually obligated to only contain lowercase alphanumeric characters,
* periods, underscores, hyphens, and forward slashes.
*
* @return resource key
*/
@NotNull
String getKey();
}

View file

@ -0,0 +1,142 @@
package com.destroystokyo.paper;
import com.google.common.base.Preconditions;
import java.util.Locale;
import java.util.UUID;
import java.util.regex.Pattern;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
/**
* Represents a String based key pertaining to a tagged entry. Consists of two components - a namespace
* and a key.
* <p>
* Namespaces may only contain lowercase alphanumeric characters, periods,
* underscores, and hyphens.
* <p>
* Keys may only contain lowercase alphanumeric characters, periods,
* underscores, hyphens, and forward slashes.
*
*/
// Paper - entire class, based on org.bukkit.NamespacedKey
public final class NamespacedTag implements com.destroystokyo.paper.Namespaced {
/**
* The namespace representing all inbuilt keys.
*/
public static final String MINECRAFT = "minecraft";
/**
* The namespace representing all keys generated by Bukkit for backwards
* compatibility measures.
*/
public static final String BUKKIT = "bukkit";
//
private static final Pattern VALID_NAMESPACE = Pattern.compile("[a-z0-9._-]+");
private static final Pattern VALID_KEY = Pattern.compile("[a-z0-9/._-]+");
//
private final String namespace;
private final String key;
/**
* Create a key in a specific namespace.
*
* @param namespace String representing a grouping of keys
* @param key Name for this specific key
* @deprecated should never be used by plugins, for internal use only!!
*/
@Deprecated
public NamespacedTag(@NotNull String namespace, @NotNull String key) {
Preconditions.checkArgument(namespace != null && VALID_NAMESPACE.matcher(namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", namespace);
Preconditions.checkArgument(key != null && VALID_KEY.matcher(key).matches(), "Invalid key. Must be [a-z0-9/._-]: %s", key);
this.namespace = namespace;
this.key = key;
String string = toString();
Preconditions.checkArgument(string.length() < 256, "NamespacedTag must be less than 256 characters", string);
}
/**
* Create a key in the plugin's namespace.
* <p>
* Namespaces may only contain lowercase alphanumeric characters, periods,
* underscores, and hyphens.
* <p>
* Keys may only contain lowercase alphanumeric characters, periods,
* underscores, hyphens, and forward slashes.
*
* @param plugin the plugin to use for the namespace
* @param key the key to create
*/
public NamespacedTag(@NotNull Plugin plugin, @NotNull String key) {
Preconditions.checkArgument(plugin != null, "Plugin cannot be null");
Preconditions.checkArgument(key != null, "Key cannot be null");
this.namespace = plugin.getName().toLowerCase(Locale.ROOT);
this.key = key.toLowerCase().toLowerCase(Locale.ROOT);
// Check validity after normalization
Preconditions.checkArgument(VALID_NAMESPACE.matcher(this.namespace).matches(), "Invalid namespace. Must be [a-z0-9._-]: %s", this.namespace);
Preconditions.checkArgument(VALID_KEY.matcher(this.key).matches(), "Invalid key. Must be [a-z0-9/._-]: %s", this.key);
String string = toString();
Preconditions.checkArgument(string.length() < 256, "NamespacedTag must be less than 256 characters (%s)", string);
}
@NotNull
public String getNamespace() {
return namespace;
}
@NotNull
public String getKey() {
return key;
}
@Override
public int hashCode() {
int hash = 7;
hash = 47 * hash + this.namespace.hashCode();
hash = 47 * hash + this.key.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NamespacedTag other = (NamespacedTag) obj;
return this.namespace.equals(other.namespace) && this.key.equals(other.key);
}
@Override
public String toString() {
return "#" + this.namespace + ":" + this.key;
}
/**
* Return a new random key in the {@link #BUKKIT} namespace.
*
* @return new key
* @deprecated should never be used by plugins, for internal use only!!
*/
@Deprecated
public static NamespacedTag randomKey() {
return new NamespacedTag(BUKKIT, UUID.randomUUID().toString());
}
/**
* Get a key in the Minecraft namespace.
*
* @param key the key to use
* @return new key in the Minecraft namespace
*/
@NotNull
public static NamespacedTag minecraft(@NotNull String key) {
return new NamespacedTag(MINECRAFT, key);
}
}

View file

@ -0,0 +1,485 @@
package com.destroystokyo.paper;
import com.google.common.collect.Lists;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.util.NumberConversions;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Helps prepare a particle to be sent to players.
*
* Usage of the builder is preferred over the super long {@link World#spawnParticle(Particle, Location, int, double, double, double, double, Object)} API
*/
public class ParticleBuilder {
private Particle particle;
private List<Player> receivers;
private Player source;
private Location location;
private int count = 1;
private double offsetX = 0, offsetY = 0, offsetZ = 0;
private double extra = 1;
private Object data;
private boolean force = true;
public ParticleBuilder(@NotNull Particle particle) {
this.particle = particle;
}
/**
* Sends the particle to all receiving players (or all). This method is safe to use
* Asynchronously
*
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder spawn() {
if (this.location == null) {
throw new IllegalStateException("Please specify location for this particle");
}
location.getWorld().spawnParticle(particle, receivers, source,
location.getX(), location.getY(), location.getZ(),
count, offsetX, offsetY, offsetZ, extra, data, force
);
return this;
}
/**
* @return The particle going to be sent
*/
@NotNull
public Particle particle() {
return particle;
}
/**
* Changes what particle will be sent
*
* @param particle The particle
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder particle(@NotNull Particle particle) {
this.particle = particle;
return this;
}
/**
* @return List of players who will receive the particle, or null for all in world
*/
@Nullable
public List<Player> receivers() {
return receivers;
}
/**
* Example use:
*
* builder.receivers(16); if (builder.hasReceivers()) { sendParticleAsync(builder); }
*
* @return If this particle is going to be sent to someone
*/
public boolean hasReceivers() {
return (receivers == null && !location.getWorld().getPlayers().isEmpty()) || (
receivers != null && !receivers.isEmpty());
}
/**
* Sends this particle to all players in the world. This is rather silly and you should likely not
* be doing this.
*
* Just be a logical person and use receivers by radius or collection.
*
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder allPlayers() {
this.receivers = null;
return this;
}
/**
* @param receivers List of players to receive this particle, or null for all players in the
* world
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(@Nullable List<Player> receivers) {
// Had to keep this as we first made API List<> and not Collection, but removing this may break plugins compiled on older jars
// TODO: deprecate?
this.receivers = receivers != null ? Lists.newArrayList(receivers) : null;
return this;
}
/**
* @param receivers List of players to receive this particle, or null for all players in the
* world
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(@Nullable Collection<Player> receivers) {
this.receivers = receivers != null ? Lists.newArrayList(receivers) : null;
return this;
}
/**
* @param receivers List of players to be receive this particle, or null for all players in the
* world
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(@Nullable Player... receivers) {
this.receivers = receivers != null ? Lists.newArrayList(receivers) : null;
return this;
}
/**
* Selects all players within a cuboid selection around the particle location, within the
* specified bounding box. If you want a more spherical check, see {@link #receivers(int,
* boolean)}
*
* @param radius amount to add on all axis
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(int radius) {
return receivers(radius, radius);
}
/**
* Selects all players within the specified radius around the particle location. If byDistance is
* false, behavior uses cuboid selection the same as {@link #receivers(int, int)} If byDistance is
* true, radius is tested by distance in a spherical shape
*
* @param radius amount to add on each axis
* @param byDistance true to use a spherical radius, false to use a cuboid
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(int radius, boolean byDistance) {
if (!byDistance) {
return receivers(radius, radius, radius);
} else {
this.receivers = Lists.newArrayList();
for (Player nearbyPlayer : location.getWorld()
.getNearbyPlayers(location, radius, radius, radius)) {
Location loc = nearbyPlayer.getLocation();
double x = NumberConversions.square(location.getX() - loc.getX());
double y = NumberConversions.square(location.getY() - loc.getY());
double z = NumberConversions.square(location.getZ() - loc.getZ());
if (Math.sqrt(x + y + z) > radius) {
continue;
}
this.receivers.add(nearbyPlayer);
}
return this;
}
}
/**
* Selects all players within a cuboid selection around the particle location, within the
* specified bounding box. Allows specifying a different Y size than X and Z If you want a more
* cylinder check, see {@link #receivers(int, int, boolean)} If you want a more spherical check,
* see {@link #receivers(int, boolean)}
*
* @param xzRadius amount to add on the x/z axis
* @param yRadius amount to add on the y axis
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(int xzRadius, int yRadius) {
return receivers(xzRadius, yRadius, xzRadius);
}
/**
* Selects all players within the specified radius around the particle location. If byDistance is
* false, behavior uses cuboid selection the same as {@link #receivers(int, int)} If byDistance is
* true, radius is tested by distance on the y plane and on the x/z plane, in a cylinder shape.
*
* @param xzRadius amount to add on the x/z axis
* @param yRadius amount to add on the y axis
* @param byDistance true to use a cylinder shape, false to use cuboid
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(int xzRadius, int yRadius, boolean byDistance) {
if (!byDistance) {
return receivers(xzRadius, yRadius, xzRadius);
} else {
this.receivers = Lists.newArrayList();
for (Player nearbyPlayer : location.getWorld()
.getNearbyPlayers(location, xzRadius, yRadius, xzRadius)) {
Location loc = nearbyPlayer.getLocation();
if (Math.abs(loc.getY() - this.location.getY()) > yRadius) {
continue;
}
double x = NumberConversions.square(location.getX() - loc.getX());
double z = NumberConversions.square(location.getZ() - loc.getZ());
if (x + z > NumberConversions.square(xzRadius)) {
continue;
}
this.receivers.add(nearbyPlayer);
}
return this;
}
}
/**
* Selects all players within a cuboid selection around the particle location, within the
* specified bounding box. If you want a more cylinder check, see {@link #receivers(int, int,
* boolean)} If you want a more spherical check, see {@link #receivers(int, boolean)}
*
* @param xRadius amount to add on the x axis
* @param yRadius amount to add on the y axis
* @param zRadius amount to add on the z axis
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder receivers(int xRadius, int yRadius, int zRadius) {
if (location == null) {
throw new IllegalStateException("Please set location first");
}
return receivers(location.getWorld().getNearbyPlayers(location, xRadius, yRadius, zRadius));
}
/**
* @return The player considered the source of this particle (for Visibility concerns), or null
*/
@Nullable
public Player source() {
return source;
}
/**
* Sets the source of this particle for visibility concerns (Vanish API)
*
* @param source The player who is considered the source
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder source(@Nullable Player source) {
this.source = source;
return this;
}
/**
* @return Location of where the particle will spawn
*/
@Nullable
public Location location() {
return location;
}
/**
* Sets the location of where to spawn the particle
*
* @param location The location of the particle
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder location(@NotNull Location location) {
this.location = location.clone();
return this;
}
/**
* Sets the location of where to spawn the particle
*
* @param world World to spawn particle in
* @param x X location
* @param y Y location
* @param z Z location
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder location(@NotNull World world, double x, double y, double z) {
this.location = new Location(world, x, y, z);
return this;
}
/**
* @return Number of particles to spawn
*/
public int count() {
return count;
}
/**
* Sets the number of particles to spawn
*
* @param count Number of particles
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder count(int count) {
this.count = count;
return this;
}
/**
* Particle offset X. Varies by particle on how this is used
*
* @return Particle offset X.
*/
public double offsetX() {
return offsetX;
}
/**
* Particle offset Y. Varies by particle on how this is used
*
* @return Particle offset Y.
*/
public double offsetY() {
return offsetY;
}
/**
* Particle offset Z. Varies by particle on how this is used
*
* @return Particle offset Z.
*/
public double offsetZ() {
return offsetZ;
}
/**
* Sets the particle offset. Varies by particle on how this is used
*
* @param offsetX Particle offset X
* @param offsetY Particle offset Y
* @param offsetZ Particle offset Z
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder offset(double offsetX, double offsetY, double offsetZ) {
this.offsetX = offsetX;
this.offsetY = offsetY;
this.offsetZ = offsetZ;
return this;
}
/**
* Gets the Particle extra data. Varies by particle on how this is used
*
* @return the extra particle data
*/
public double extra() {
return extra;
}
/**
* Sets the particle extra data. Varies by particle on how this is used
*
* @param extra the extra particle data
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder extra(double extra) {
this.extra = extra;
return this;
}
/**
* Gets the particle custom data. Varies by particle on how this is used
*
* @param <T> The Particle data type
* @return the ParticleData for this particle
*/
@Nullable
public <T> T data() {
//noinspection unchecked
return (T) data;
}
/**
* Sets the particle custom data. Varies by particle on how this is used
*
* @param data The new particle data
* @param <T> The Particle data type
* @return a reference to this object.
*/
@NotNull
public <T> ParticleBuilder data(@Nullable T data) {
this.data = data;
return this;
}
/**
* @return whether the particle is forcefully shown to players.
*/
public boolean force() {
return force;
}
/**
* Sets whether the particle is forcefully shown to the player. If forced, the particle will show
* faraway, as far as the player's view distance allows. If false, the particle will show
* according to the client's particle settings.
*
* @param force true to force, false for normal
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder force(boolean force) {
this.force = force;
return this;
}
/**
* Sets the particle Color. Only valid for REDSTONE.
*
* @param color the new particle color
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder color(@Nullable Color color) {
return color(color, 1);
}
/**
* Sets the particle Color and size. Only valid for REDSTONE.
*
* @param color the new particle color
* @param size the size of the particle
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder color(@Nullable Color color, float size) {
if (particle != Particle.REDSTONE && color != null) {
throw new IllegalStateException("Color may only be set on REDSTONE");
}
// We don't officially support reusing these objects, but here we go
if (color == null) {
if (data instanceof Particle.DustOptions) {
return data(null);
} else {
return this;
}
}
return data(new Particle.DustOptions(color, size));
}
/**
* Sets the particle Color.
* Only valid for REDSTONE.
* @param r red color component
* @param g green color component
* @param b blue color component
* @return a reference to this object.
*/
@NotNull
public ParticleBuilder color(int r, int g, int b) {
return color(Color.fromRGB(r, g, b));
}
}

View file

@ -0,0 +1,20 @@
package com.destroystokyo.paper;
public interface SkinParts {
boolean hasCapeEnabled();
boolean hasJacketEnabled();
boolean hasLeftSleeveEnabled();
boolean hasRightSleeveEnabled();
boolean hasLeftPantsEnabled();
boolean hasRightPantsEnabled();
boolean hasHatsEnabled();
int getRaw();
}

View file

@ -0,0 +1,420 @@
package com.destroystokyo.paper;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* Represents a title to may be sent to a {@link Player}.
*
* <p>A title can be sent without subtitle text.</p>
*
* @deprecated use {@link net.kyori.adventure.title.Title}
*/
@Deprecated
public final class Title {
/**
* The default number of ticks for the title to fade in.
*/
public static final int DEFAULT_FADE_IN = 20;
/**
* The default number of ticks for the title to stay.
*/
public static final int DEFAULT_STAY = 200;
/**
* The default number of ticks for the title to fade out.
*/
public static final int DEFAULT_FADE_OUT = 20;
private final BaseComponent[] title;
private final BaseComponent[] subtitle;
private final int fadeIn;
private final int stay;
private final int fadeOut;
/**
* Create a title with the default time values and no subtitle.
*
* <p>Times use default values.</p>
*
* @param title the main text of the title
* @throws NullPointerException if the title is null
*/
public Title(@NotNull BaseComponent title) {
this(title, null);
}
/**
* Create a title with the default time values and no subtitle.
*
* <p>Times use default values.</p>
*
* @param title the main text of the title
* @throws NullPointerException if the title is null
*/
public Title(@NotNull BaseComponent[] title) {
this(title, null);
}
/**
* Create a title with the default time values and no subtitle.
*
* <p>Times use default values.</p>
*
* @param title the main text of the title
* @throws NullPointerException if the title is null
*/
public Title(@NotNull String title) {
this(title, null);
}
/**
* Create a title with the default time values.
*
* <p>Times use default values.</p>
*
* @param title the main text of the title
* @param subtitle the secondary text of the title
*/
public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle) {
this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
}
/**
* Create a title with the default time values.
*
* <p>Times use default values.</p>
*
* @param title the main text of the title
* @param subtitle the secondary text of the title
*/
public Title(@NotNull BaseComponent[] title, @Nullable BaseComponent[] subtitle) {
this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
}
/**
* Create a title with the default time values.
*
* <p>Times use default values.</p>
*
* @param title the main text of the title
* @param subtitle the secondary text of the title
*/
public Title(@NotNull String title, @Nullable String subtitle) {
this(title, subtitle, DEFAULT_FADE_IN, DEFAULT_STAY, DEFAULT_FADE_OUT);
}
/**
* Creates a new title.
*
* @param title the main text of the title
* @param subtitle the secondary text of the title
* @param fadeIn the number of ticks for the title to fade in
* @param stay the number of ticks for the title to stay on screen
* @param fadeOut the number of ticks for the title to fade out
* @throws IllegalArgumentException if any of the times are negative
*/
public Title(@NotNull BaseComponent title, @Nullable BaseComponent subtitle, int fadeIn, int stay, int fadeOut) {
this(
new BaseComponent[]{checkNotNull(title, "title")},
subtitle == null ? null : new BaseComponent[]{subtitle},
fadeIn,
stay,
fadeOut
);
}
/**
* Creates a new title.
*
* @param title the main text of the title
* @param subtitle the secondary text of the title
* @param fadeIn the number of ticks for the title to fade in
* @param stay the number of ticks for the title to stay on screen
* @param fadeOut the number of ticks for the title to fade out
* @throws IllegalArgumentException if any of the times are negative
*/
public Title(@Nullable BaseComponent[] title, @NotNull BaseComponent[] subtitle, int fadeIn, int stay, int fadeOut) {
checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
checkArgument(stay >= 0, "Negative stay: %s", stay);
checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
this.title = checkNotNull(title, "title");
this.subtitle = subtitle;
this.fadeIn = fadeIn;
this.stay = stay;
this.fadeOut = fadeOut;
}
/**
* Creates a new title.
*
* <p>It is recommended to the {@link BaseComponent} constrctors.</p>
*
* @param title the main text of the title
* @param subtitle the secondary text of the title
* @param fadeIn the number of ticks for the title to fade in
* @param stay the number of ticks for the title to stay on screen
* @param fadeOut the number of ticks for the title to fade out
*/
public Title(@NotNull String title, @Nullable String subtitle, int fadeIn, int stay, int fadeOut) {
this(
TextComponent.fromLegacyText(checkNotNull(title, "title")),
subtitle == null ? null : TextComponent.fromLegacyText(subtitle),
fadeIn,
stay,
fadeOut
);
}
/**
* Gets the text of this title
*
* @return the text
*/
@NotNull
public BaseComponent[] getTitle() {
return this.title;
}
/**
* Gets the text of this title's subtitle
*
* @return the text
*/
@Nullable
public BaseComponent[] getSubtitle() {
return this.subtitle;
}
/**
* Gets the number of ticks to fade in.
*
* <p>The returned value is never negative.</p>
*
* @return the number of ticks to fade in
*/
public int getFadeIn() {
return this.fadeIn;
}
/**
* Gets the number of ticks to stay.
*
* <p>The returned value is never negative.</p>
*
* @return the number of ticks to stay
*/
public int getStay() {
return this.stay;
}
/**
* Gets the number of ticks to fade out.
*
* <p>The returned value is never negative.</p>
*
* @return the number of ticks to fade out
*/
public int getFadeOut() {
return this.fadeOut;
}
/**
* Sends the title directly to an player
*
* @param player the receiver of the title
*/
public void send(@NotNull Player player) {
player.sendTitle(this);
}
/**
* Sends the title directly to the defined players
*
* @param players the receivers of the title
*/
public void send(@NotNull Collection<? extends Player> players) {
for (Player player : players) {
player.sendTitle(this);
}
}
/**
* Sends the title directly to the defined players
*
* @param players the receivers of the title
*/
public void send(@NotNull Player[] players) {
for (Player player : players) {
player.sendTitle(this);
}
}
/**
* Sends the title directly to all online players
*/
public void broadcast() {
send(Bukkit.getOnlinePlayers());
}
@NotNull
public static Builder builder() {
return new Builder();
}
/**
* A builder for creating titles
*/
public static final class Builder {
private BaseComponent[] title;
private BaseComponent[] subtitle;
private int fadeIn = DEFAULT_FADE_IN;
private int stay = DEFAULT_STAY;
private int fadeOut = DEFAULT_FADE_OUT;
/**
* Sets the title to the given text.
*
* @param title the title text
* @return this builder instance
* @throws NullPointerException if the title is null
*/
@NotNull
public Builder title(@NotNull BaseComponent title) {
return this.title(new BaseComponent[]{checkNotNull(title, "title")});
}
/**
* Sets the title to the given text.
*
* @param title the title text
* @return this builder instance
* @throws NullPointerException if the title is null
*/
@NotNull
public Builder title(@NotNull BaseComponent[] title) {
this.title = checkNotNull(title, "title");
return this;
}
/**
* Sets the title to the given text.
*
* <p>It is recommended to the {@link BaseComponent} methods.</p>
*
* @param title the title text
* @return this builder instance
* @throws NullPointerException if the title is null
*/
@NotNull
public Builder title(@NotNull String title) {
return this.title(TextComponent.fromLegacyText(checkNotNull(title, "title")));
}
/**
* Sets the subtitle to the given text.
*
* @param subtitle the title text
* @return this builder instance
*/
@NotNull
public Builder subtitle(@Nullable BaseComponent subtitle) {
return this.subtitle(subtitle == null ? null : new BaseComponent[]{subtitle});
}
/**
* Sets the subtitle to the given text.
*
* @param subtitle the title text
* @return this builder instance
*/
@NotNull
public Builder subtitle(@Nullable BaseComponent[] subtitle) {
this.subtitle = subtitle;
return this;
}
/**
* Sets the subtitle to the given text.
*
* <p>It is recommended to the {@link BaseComponent} methods.</p>
*
* @param subtitle the title text
* @return this builder instance
*/
@NotNull
public Builder subtitle(@Nullable String subtitle) {
return this.subtitle(subtitle == null ? null : TextComponent.fromLegacyText(subtitle));
}
/**
* Sets the number of ticks for the title to fade in
*
* @param fadeIn the number of ticks to fade in
* @return this builder instance
* @throws IllegalArgumentException if it is negative
*/
@NotNull
public Builder fadeIn(int fadeIn) {
checkArgument(fadeIn >= 0, "Negative fadeIn: %s", fadeIn);
this.fadeIn = fadeIn;
return this;
}
/**
* Sets the number of ticks for the title to stay.
*
* @param stay the number of ticks to stay
* @return this builder instance
* @throws IllegalArgumentException if it is negative
*/
@NotNull
public Builder stay(int stay) {
checkArgument(stay >= 0, "Negative stay: %s", stay);
this.stay = stay;
return this;
}
/**
* Sets the number of ticks for the title to fade out.
*
* @param fadeOut the number of ticks to fade out
* @return this builder instance
* @throws IllegalArgumentException if it is negative
*/
@NotNull
public Builder fadeOut(int fadeOut) {
checkArgument(fadeOut >= 0, "Negative fadeOut: %s", fadeOut);
this.fadeOut = fadeOut;
return this;
}
/**
* Create a title based on the values in the builder.
*
* @return a title from the values in this builder
* @throws IllegalStateException if title isn't specified
*/
@NotNull
public Title build() {
checkState(title != null, "Title not specified");
return new Title(this.title, this.subtitle, this.fadeIn, this.stay, this.fadeOut);
}
}
}

View file

@ -0,0 +1,64 @@
package com.destroystokyo.paper.block;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
/**
* Represents the sounds that a {@link Block} makes in certain situations
* <p>
* The sound group includes break, step, place, hit, and fall sounds.
* @deprecated use {@link org.bukkit.SoundGroup}
*/
@Deprecated(forRemoval = true)
public interface BlockSoundGroup {
/**
* Gets the sound that plays when breaking this block
*
* @return The break sound
* @deprecated use {@link org.bukkit.SoundGroup#getBreakSound()}
*/
@NotNull
@Deprecated(forRemoval = true)
Sound getBreakSound();
/**
* Gets the sound that plays when stepping on this block
*
* @return The step sound
* @deprecated use {@link org.bukkit.SoundGroup#getStepSound()}
*/
@NotNull
@Deprecated(forRemoval = true)
Sound getStepSound();
/**
* Gets the sound that plays when placing this block
*
* @return The place sound
* @deprecated use {@link org.bukkit.SoundGroup#getPlaceSound()}
*/
@NotNull
@Deprecated(forRemoval = true)
Sound getPlaceSound();
/**
* Gets the sound that plays when hitting this block
*
* @return The hit sound
* @deprecated use {@link org.bukkit.SoundGroup#getHitSound()}
*/
@NotNull
@Deprecated(forRemoval = true)
Sound getHitSound();
/**
* Gets the sound that plays when this block falls
*
* @return The fall sound
* @deprecated use {@link org.bukkit.SoundGroup#getFallSound()}
*/
@NotNull
@Deprecated(forRemoval = true)
Sound getFallSound();
}

View file

@ -0,0 +1,67 @@
package com.destroystokyo.paper.block;
import org.bukkit.FluidCollisionMode;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.jetbrains.annotations.NotNull;
/**
* Represents information about a targeted block
* @deprecated use {@link org.bukkit.util.RayTraceResult}
*/
@Deprecated(forRemoval = true)
public class TargetBlockInfo {
private final Block block;
private final BlockFace blockFace;
public TargetBlockInfo(@NotNull Block block, @NotNull BlockFace blockFace) {
this.block = block;
this.blockFace = blockFace;
}
/**
* Get the block that is targeted
*
* @return Targeted block
*/
@NotNull
public Block getBlock() {
return block;
}
/**
* Get the targeted BlockFace
*
* @return Targeted blockface
*/
@NotNull
public BlockFace getBlockFace() {
return blockFace;
}
/**
* Get the relative Block to the targeted block on the side it is targeted at
*
* @return Block relative to targeted block
*/
@NotNull
public Block getRelativeBlock() {
return block.getRelative(blockFace);
}
/**
* @deprecated use {@link org.bukkit.FluidCollisionMode}
*/
@Deprecated(forRemoval = true)
public enum FluidMode {
NEVER(FluidCollisionMode.NEVER),
SOURCE_ONLY(FluidCollisionMode.SOURCE_ONLY),
ALWAYS(FluidCollisionMode.ALWAYS);
public final FluidCollisionMode bukkit;
FluidMode(FluidCollisionMode bukkit) {
this.bukkit = bukkit;
}
}
}

View file

@ -0,0 +1,212 @@
package com.destroystokyo.paper.entity;
import org.bukkit.Location;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Mob;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Handles pathfinding operations for an Entity
*/
public interface Pathfinder {
/**
*
* @return The entity that is controlled by this pathfinder
*/
@NotNull
Mob getEntity();
/**
* Instructs the Entity to stop trying to navigate to its current desired location
*/
void stopPathfinding();
/**
* If the entity is currently trying to navigate to a destination, this will return true
* @return true if the entity is navigating to a destination
*/
boolean hasPath();
/**
* @return The location the entity is trying to navigate to, or null if there is no destination
*/
@Nullable
PathResult getCurrentPath();
/**
* Calculates a destination for the Entity to navigate to, but does not set it
* as the current target. Useful for calculating what would happen before setting it.
* @param loc Location to navigate to
* @return The closest Location the Entity can get to for this navigation, or null if no path could be calculated
*/
@Nullable PathResult findPath(@NotNull Location loc);
/**
* Calculates a destination for the Entity to navigate to to reach the target entity,
* but does not set it as the current target.
* Useful for calculating what would happen before setting it.
*
* The behavior of this PathResult is subject to the games pathfinding rules, and may
* result in the pathfinding automatically updating to follow the target Entity.
*
* However, this behavior is not guaranteed, and is subject to the games behavior.
*
* @param target the Entity to navigate to
* @return The closest Location the Entity can get to for this navigation, or null if no path could be calculated
*/
@Nullable PathResult findPath(@NotNull LivingEntity target);
/**
* Calculates a destination for the Entity to navigate to, and sets it with default speed
* as the current target.
* @param loc Location to navigate to
* @return If the pathfinding was successfully started
*/
default boolean moveTo(@NotNull Location loc) {
return moveTo(loc, 1);
}
/**
* Calculates a destination for the Entity to navigate to, with desired speed
* as the current target.
* @param loc Location to navigate to
* @param speed Speed multiplier to navigate at, where 1 is 'normal'
* @return If the pathfinding was successfully started
*/
default boolean moveTo(@NotNull Location loc, double speed) {
PathResult path = findPath(loc);
return path != null && moveTo(path, speed);
}
/**
* Calculates a destination for the Entity to navigate to to reach the target entity,
* and sets it with default speed.
*
* The behavior of this PathResult is subject to the games pathfinding rules, and may
* result in the pathfinding automatically updating to follow the target Entity.
*
* However, this behavior is not guaranteed, and is subject to the games behavior.
*
* @param target the Entity to navigate to
* @return If the pathfinding was successfully started
*/
default boolean moveTo(@NotNull LivingEntity target) {
return moveTo(target, 1);
}
/**
* Calculates a destination for the Entity to navigate to to reach the target entity,
* and sets it with specified speed.
*
* The behavior of this PathResult is subject to the games pathfinding rules, and may
* result in the pathfinding automatically updating to follow the target Entity.
*
* However, this behavior is not guaranteed, and is subject to the games behavior.
*
* @param target the Entity to navigate to
* @param speed Speed multiplier to navigate at, where 1 is 'normal'
* @return If the pathfinding was successfully started
*/
default boolean moveTo(@NotNull LivingEntity target, double speed) {
PathResult path = findPath(target);
return path != null && moveTo(path, speed);
}
/**
* Takes the result of a previous pathfinding calculation and sets it
* as the active pathfinding with default speed.
*
* @param path The Path to start following
* @return If the pathfinding was successfully started
*/
default boolean moveTo(@NotNull PathResult path) {
return moveTo(path, 1);
}
/**
* Takes the result of a previous pathfinding calculation and sets it
* as the active pathfinding,
*
* @param path The Path to start following
* @param speed Speed multiplier to navigate at, where 1 is 'normal'
* @return If the pathfinding was successfully started
*/
boolean moveTo(@NotNull PathResult path, double speed);
/**
* Checks if this pathfinder allows passing through closed doors.
*
* @return if this pathfinder allows passing through closed doors
*/
boolean canOpenDoors();
/**
* Allows this pathfinder to pass through closed doors, or not
*
* @param canOpenDoors if the mob can pass through closed doors, or not
*/
void setCanOpenDoors(boolean canOpenDoors);
/**
* Checks if this pathfinder allows passing through open doors.
*
* @return if this pathfinder allows passing through open doors
*/
boolean canPassDoors();
/**
* Allows this pathfinder to pass through open doors, or not
*
* @param canPassDoors if the mob can pass through open doors, or not
*/
void setCanPassDoors(boolean canPassDoors);
/**
* Checks if this pathfinder assumes that the mob can float
*
* @return if this pathfinder assumes that the mob can float
*/
boolean canFloat();
/**
* Makes this pathfinder assume that the mob can float, or not
*
* @param canFloat if the mob can float, or not
*/
void setCanFloat(boolean canFloat);
/**
* Represents the result of a pathfinding calculation
*/
interface PathResult {
/**
* All currently calculated points to follow along the path to reach the destination location
*
* Will return points the entity has already moved past, see {@link #getNextPointIndex()}
* @return List of points
*/
@NotNull
List<Location> getPoints();
/**
* @return Returns the index of the current point along the points returned in {@link #getPoints()} the entity
* is trying to reach. This value will be higher than the maximum index of {@link #getPoints()} if this path finding is done.
*/
int getNextPointIndex();
/**
* @return The next location in the path points the entity is trying to reach, or null if there is no next point
*/
@Nullable Location getNextPoint();
/**
* @return The closest point the path can get to the target location
*/
@Nullable Location getFinalPoint();
}
}

View file

@ -0,0 +1,35 @@
package com.destroystokyo.paper.entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Mob;
import org.jetbrains.annotations.NotNull;
public interface RangedEntity extends Mob {
/**
* Attack the specified entity using a ranged attack.
*
* @param target the entity to target
* @param charge How "charged" the attack is (how far back the bow was pulled for Bow attacks).
* This should be a value between 0 and 1, represented as targetDistance/maxDistance.
*/
void rangedAttack(@NotNull LivingEntity target, float charge);
/**
* Sets that the Entity is "charging" up an attack, by raising its hands
*
* @param raiseHands Whether the entities hands are raised to charge attack
* @deprecated use {@link #setAggressive(boolean)}
*/
@Deprecated
void setChargingAttack(boolean raiseHands);
/**
* Alias to {@link LivingEntity#isHandRaised()}, if the entity is charging an attack
* @return If entities hands are raised
* @deprecated use {@link #isHandRaised()}
*/
@Deprecated
default boolean isChargingAttack() {
return isHandRaised();
}
}

View file

@ -0,0 +1,40 @@
package com.destroystokyo.paper.entity;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
/**
* Represents information about a targeted entity
* @deprecated use {@link org.bukkit.util.RayTraceResult}
*/
@Deprecated(forRemoval = true)
public class TargetEntityInfo {
private final Entity entity;
private final Vector hitVec;
public TargetEntityInfo(@NotNull Entity entity, @NotNull Vector hitVec) {
this.entity = entity;
this.hitVec = hitVec;
}
/**
* Get the entity that is targeted
*
* @return Targeted entity
*/
@NotNull
public Entity getEntity() {
return entity;
}
/**
* Get the position the entity is targeted at
*
* @return Targeted position
*/
@NotNull
public Vector getHitVector() {
return hitVec;
}
}

View file

@ -0,0 +1,66 @@
package com.destroystokyo.paper.entity.ai;
import org.jetbrains.annotations.NotNull;
import java.util.EnumSet;
import org.bukkit.entity.Mob;
/**
* Represents an AI goal of an entity
*/
public interface Goal<T extends Mob> {
/**
* Checks if this goal should be activated
*
* @return if this goal should be activated
*/
boolean shouldActivate();
/**
* Checks if this goal should stay active, defaults to {@link Goal#shouldActivate()}
*
* @return if this goal should stay active
*/
default boolean shouldStayActive() {
return shouldActivate();
}
/**
* Called when this goal gets activated
*/
default void start() {
}
/**
* Called when this goal gets stopped
*/
default void stop() {
}
/**
* Called each tick the goal is activated
*/
default void tick() {
}
/**
* A unique key that identifies this type of goal. Plugins should use their own namespace, not the minecraft
* namespace. Additionally, this key also specifies to what mobs this goal can be applied to
*
* @return the goal key
*/
@NotNull
GoalKey<T> getKey();
/**
* Returns a list of all applicable flags for this goal.<br>
*
* This method is only called on construction.
*
* @return the subtypes.
*/
@NotNull
EnumSet<GoalType> getTypes();
}

View file

@ -0,0 +1,64 @@
package com.destroystokyo.paper.entity.ai;
import com.google.common.base.Objects;
import org.jetbrains.annotations.NotNull;
import java.util.StringJoiner;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Mob;
/**
*
* Used to identify a Goal. Consists of a {@link NamespacedKey} and the type of mob the goal can be applied to
*
* @param <T> the type of mob the goal can be applied to
*/
public class GoalKey<T extends Mob> {
private final Class<T> entityClass;
private final NamespacedKey namespacedKey;
private GoalKey(@NotNull Class<T> entityClass, @NotNull NamespacedKey namespacedKey) {
this.entityClass = entityClass;
this.namespacedKey = namespacedKey;
}
@NotNull
public Class<T> getEntityClass() {
return entityClass;
}
@NotNull
public NamespacedKey getNamespacedKey() {
return namespacedKey;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GoalKey<?> goalKey = (GoalKey<?>) o;
return Objects.equal(entityClass, goalKey.entityClass) &&
Objects.equal(namespacedKey, goalKey.namespacedKey);
}
@Override
public int hashCode() {
return Objects.hashCode(entityClass, namespacedKey);
}
@Override
public String toString() {
return new StringJoiner(", ", GoalKey.class.getSimpleName() + "[", "]")
.add("entityClass=" + entityClass)
.add("namespacedKey=" + namespacedKey)
.toString();
}
@NotNull
public static <A extends Mob> GoalKey<A> of(@NotNull Class<A> entityClass, @NotNull NamespacedKey namespacedKey) {
return new GoalKey<>(entityClass, namespacedKey);
}
}

View file

@ -0,0 +1,17 @@
package com.destroystokyo.paper.entity.ai;
/**
* Represents the subtype of a goal. Used by minecraft to disable certain types of goals if needed.
*/
public enum GoalType {
MOVE,
LOOK,
JUMP,
TARGET,
/**
* Used to map vanilla goals, that are a behavior goal but don't have a type set...
*/
UNKNOWN_BEHAVIOR,
}

View file

@ -0,0 +1,50 @@
package com.destroystokyo.paper.entity.ai;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import org.bukkit.entity.Mob;
/**
* Represents a part of the "brain" of a mob. It tracks all tasks (running or not), allows adding and removing goals
*/
public interface MobGoals {
<T extends Mob> void addGoal(@NotNull T mob, int priority, @NotNull Goal<T> goal);
<T extends Mob> void removeGoal(@NotNull T mob, @NotNull Goal<T> goal);
<T extends Mob> void removeAllGoals(@NotNull T mob);
<T extends Mob> void removeAllGoals(@NotNull T mob, @NotNull GoalType type);
<T extends Mob> void removeGoal(@NotNull T mob, @NotNull GoalKey<T> key);
<T extends Mob> boolean hasGoal(@NotNull T mob, @NotNull GoalKey<T> key);
@Nullable
<T extends Mob> Goal<T> getGoal(@NotNull T mob, @NotNull GoalKey<T> key);
@NotNull
<T extends Mob> Collection<Goal<T>> getGoals(@NotNull T mob, @NotNull GoalKey<T> key);
@NotNull
<T extends Mob> Collection<Goal<T>> getAllGoals(@NotNull T mob);
@NotNull
<T extends Mob> Collection<Goal<T>> getAllGoals(@NotNull T mob, @NotNull GoalType type);
@NotNull
<T extends Mob> Collection<Goal<T>> getAllGoalsWithout(@NotNull T mob, @NotNull GoalType type);
@NotNull
<T extends Mob> Collection<Goal<T>> getRunningGoals(@NotNull T mob);
@NotNull
<T extends Mob> Collection<Goal<T>> getRunningGoals(@NotNull T mob, @NotNull GoalType type);
@NotNull
<T extends Mob> Collection<Goal<T>> getRunningGoalsWithout(@NotNull T mob, @NotNull GoalType type);
}

View file

@ -0,0 +1,303 @@
package com.destroystokyo.paper.entity.ai;
import com.destroystokyo.paper.entity.RangedEntity;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.*;
import org.jetbrains.annotations.ApiStatus;
/**
* Represents a vanilla goal. Plugins should never implement this.<br>
* Generated by VanillaPathfinderTest in paper-server
*/
public interface VanillaGoal<T extends Mob> extends Goal<T> {
GoalKey<Creature> AVOID_ENTITY = GoalKey.of(Creature.class, NamespacedKey.minecraft("avoid_entity"));
GoalKey<Wolf> BEG = GoalKey.of(Wolf.class, NamespacedKey.minecraft("beg"));
GoalKey<Mob> BREAK_DOOR = GoalKey.of(Mob.class, NamespacedKey.minecraft("break_door"));
GoalKey<Creature> BREATH_AIR = GoalKey.of(Creature.class, NamespacedKey.minecraft("breath_air"));
GoalKey<Animals> BREED = GoalKey.of(Animals.class, NamespacedKey.minecraft("breed"));
GoalKey<Cat> CAT_LIE_ON_BED = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_lie_on_bed"));
GoalKey<Cat> CAT_SIT_ON_BLOCK = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_sit_on_block"));
GoalKey<Dolphin> DOLPHIN_JUMP = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_jump"));
GoalKey<Mob> EAT_BLOCK = GoalKey.of(Mob.class, NamespacedKey.minecraft("eat_block"));
GoalKey<Creature> FLEE_SUN = GoalKey.of(Creature.class, NamespacedKey.minecraft("flee_sun"));
GoalKey<Mob> FLOAT = GoalKey.of(Mob.class, NamespacedKey.minecraft("float"));
GoalKey<Creature> FOLLOW_BOAT = GoalKey.of(Creature.class, NamespacedKey.minecraft("follow_boat"));
GoalKey<io.papermc.paper.entity.SchoolableFish> FOLLOW_FLOCK_LEADER = GoalKey.of(io.papermc.paper.entity.SchoolableFish.class, NamespacedKey.minecraft("follow_flock_leader"));
GoalKey<Mob> FOLLOW_MOB = GoalKey.of(Mob.class, NamespacedKey.minecraft("follow_mob"));
GoalKey<Tameable> FOLLOW_OWNER = GoalKey.of(Tameable.class, NamespacedKey.minecraft("follow_owner"));
GoalKey<Animals> FOLLOW_PARENT = GoalKey.of(Animals.class, NamespacedKey.minecraft("follow_parent"));
GoalKey<Creature> GOLEM_RANDOM_STROLL_IN_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("golem_random_stroll_in_village"));
GoalKey<Mob> INTERACT = GoalKey.of(Mob.class, NamespacedKey.minecraft("interact"));
GoalKey<Parrot> LAND_ON_OWNERS_SHOULDER = GoalKey.of(Parrot.class, NamespacedKey.minecraft("land_on_owners_shoulder"));
GoalKey<Mob> LEAP_AT = GoalKey.of(Mob.class, NamespacedKey.minecraft("leap_at"));
GoalKey<Llama> LLAMA_FOLLOW_CARAVAN = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_follow_caravan"));
GoalKey<Mob> LOOK_AT_PLAYER = GoalKey.of(Mob.class, NamespacedKey.minecraft("look_at_player"));
GoalKey<AbstractVillager> LOOK_AT_TRADING_PLAYER = GoalKey.of(AbstractVillager.class, NamespacedKey.minecraft("look_at_trading_player"));
GoalKey<Creature> MELEE_ATTACK = GoalKey.of(Creature.class, NamespacedKey.minecraft("melee_attack"));
GoalKey<Creature> MOVE_BACK_TO_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_back_to_village"));
GoalKey<Creature> MOVE_THROUGH_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_through_village"));
GoalKey<Creature> MOVE_TOWARDS_RESTRICTION = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_towards_restriction"));
GoalKey<Creature> MOVE_TOWARDS = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_towards"));
GoalKey<Mob> OCELOT_ATTACK = GoalKey.of(Mob.class, NamespacedKey.minecraft("ocelot_attack"));
GoalKey<IronGolem> OFFER_FLOWER = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("offer_flower"));
GoalKey<Mob> OPEN_DOOR = GoalKey.of(Mob.class, NamespacedKey.minecraft("open_door"));
GoalKey<Creature> PANIC = GoalKey.of(Creature.class, NamespacedKey.minecraft("panic"));
GoalKey<Raider> PATHFIND_TO_RAID = GoalKey.of(Raider.class, NamespacedKey.minecraft("pathfind_to_raid"));
GoalKey<Mob> RANDOM_LOOK_AROUND = GoalKey.of(Mob.class, NamespacedKey.minecraft("random_look_around"));
GoalKey<AbstractHorse> RANDOM_STAND = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("random_stand"));
GoalKey<Creature> RANDOM_STROLL = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_stroll"));
GoalKey<Creature> RANDOM_SWIMMING = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_swimming"));
GoalKey<RangedEntity> RANGED_ATTACK = GoalKey.of(RangedEntity.class, NamespacedKey.minecraft("ranged_attack"));
GoalKey<Monster> RANGED_BOW_ATTACK = GoalKey.of(Monster.class, NamespacedKey.minecraft("ranged_bow_attack"));
GoalKey<Monster> RANGED_CROSSBOW_ATTACK = GoalKey.of(Monster.class, NamespacedKey.minecraft("ranged_crossbow_attack"));
GoalKey<Creature> REMOVE_BLOCK = GoalKey.of(Creature.class, NamespacedKey.minecraft("remove_block"));
GoalKey<Creature> RESTRICT_SUN = GoalKey.of(Creature.class, NamespacedKey.minecraft("restrict_sun"));
GoalKey<AbstractHorse> RUN_AROUND_LIKE_CRAZY = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("run_around_like_crazy"));
GoalKey<Tameable> SIT_WHEN_ORDERED_TO = GoalKey.of(Tameable.class, NamespacedKey.minecraft("sit_when_ordered_to"));
GoalKey<Creature> STROLL_THROUGH_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("stroll_through_village"));
GoalKey<Creeper> SWELL = GoalKey.of(Creeper.class, NamespacedKey.minecraft("swell"));
GoalKey<Creature> TEMPT = GoalKey.of(Creature.class, NamespacedKey.minecraft("tempt"));
GoalKey<AbstractVillager> TRADE_WITH_PLAYER = GoalKey.of(AbstractVillager.class, NamespacedKey.minecraft("trade_with_player"));
GoalKey<Creature> TRY_FIND_WATER = GoalKey.of(Creature.class, NamespacedKey.minecraft("try_find_water"));
GoalKey<Mob> USE_ITEM = GoalKey.of(Mob.class, NamespacedKey.minecraft("use_item"));
GoalKey<Creature> WATER_AVOIDING_RANDOM_FLYING = GoalKey.of(Creature.class, NamespacedKey.minecraft("water_avoiding_random_flying"));
GoalKey<Creature> WATER_AVOIDING_RANDOM_STROLL = GoalKey.of(Creature.class, NamespacedKey.minecraft("water_avoiding_random_stroll"));
GoalKey<Zombie> ZOMBIE_ATTACK = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack"));
GoalKey<IronGolem> DEFEND_VILLAGE = GoalKey.of(IronGolem.class, NamespacedKey.minecraft("defend_village"));
GoalKey<Creature> HURT_BY = GoalKey.of(Creature.class, NamespacedKey.minecraft("hurt_by"));
GoalKey<Mob> NEAREST_ATTACKABLE = GoalKey.of(Mob.class, NamespacedKey.minecraft("nearest_attackable"));
GoalKey<Raider> NEAREST_ATTACKABLE_WITCH = GoalKey.of(Raider.class, NamespacedKey.minecraft("nearest_attackable_witch"));
GoalKey<Raider> NEAREST_HEALABLE_RAIDER = GoalKey.of(Raider.class, NamespacedKey.minecraft("nearest_healable_raider"));
GoalKey<Tameable> NON_TAME_RANDOM = GoalKey.of(Tameable.class, NamespacedKey.minecraft("non_tame_random"));
GoalKey<Tameable> OWNER_HURT_BY = GoalKey.of(Tameable.class, NamespacedKey.minecraft("owner_hurt_by"));
GoalKey<Tameable> OWNER_HURT = GoalKey.of(Tameable.class, NamespacedKey.minecraft("owner_hurt"));
GoalKey<Mob> RESET_UNIVERSAL_ANGER = GoalKey.of(Mob.class, NamespacedKey.minecraft("reset_universal_anger"));
GoalKey<Fish> FISH_SWIM = GoalKey.of(Fish.class, NamespacedKey.minecraft("fish_swim"));
GoalKey<Bee> BEE_ATTACK = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_attack"));
GoalKey<Bee> BEE_BECOME_ANGRY = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_become_angry"));
GoalKey<Bee> BEE_ENTER_HIVE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_enter_hive"));
GoalKey<Bee> BEE_GO_TO_HIVE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_go_to_hive"));
GoalKey<Bee> BEE_GO_TO_KNOWN_FLOWER = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_go_to_known_flower"));
GoalKey<Bee> BEE_GROW_CROP = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_grow_crop"));
GoalKey<Bee> BEE_HURT_BY_OTHER = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_hurt_by_other"));
GoalKey<Bee> BEE_LOCATE_HIVE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_locate_hive"));
GoalKey<Bee> BEE_POLLINATE = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_pollinate"));
GoalKey<Bee> BEE_WANDER = GoalKey.of(Bee.class, NamespacedKey.minecraft("bee_wander"));
GoalKey<Cat> CAT_AVOID_ENTITY = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_avoid_entity"));
GoalKey<Cat> CAT_RELAX_ON_OWNER = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_relax_on_owner"));
GoalKey<Cat> CAT_TEMPT = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_tempt"));
GoalKey<Dolphin> DOLPHIN_SWIM_TO_TREASURE = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_swim_to_treasure"));
GoalKey<Dolphin> DOLPHIN_SWIM_WITH_PLAYER = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_swim_with_player"));
GoalKey<Dolphin> PLAY_WITH_ITEMS = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("play_with_items"));
GoalKey<Fox> DEFEND_TRUSTED = GoalKey.of(Fox.class, NamespacedKey.minecraft("defend_trusted"));
GoalKey<Fox> FACEPLANT = GoalKey.of(Fox.class, NamespacedKey.minecraft("faceplant"));
GoalKey<Fox> FOX_BREED = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_breed"));
GoalKey<Fox> FOX_EAT_BERRIES = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_eat_berries"));
GoalKey<Fox> FOX_FLOAT = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_float"));
GoalKey<Fox> FOX_FOLLOW_PARENT = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_follow_parent"));
GoalKey<Fox> FOX_LOOK_AT_PLAYER = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_look_at_player"));
GoalKey<Fox> FOX_MELEE_ATTACK = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_melee_attack"));
GoalKey<Fox> FOX_PANIC = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_panic"));
GoalKey<Fox> FOX_POUNCE = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_pounce"));
GoalKey<Fox> FOX_SEARCH_FOR_ITEMS = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_search_for_items"));
GoalKey<Fox> FOX_STROLL_THROUGH_VILLAGE = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_stroll_through_village"));
GoalKey<Fox> PERCH_AND_SEARCH = GoalKey.of(Fox.class, NamespacedKey.minecraft("perch_and_search"));
GoalKey<Fox> SEEK_SHELTER = GoalKey.of(Fox.class, NamespacedKey.minecraft("seek_shelter"));
GoalKey<Fox> SLEEP = GoalKey.of(Fox.class, NamespacedKey.minecraft("sleep"));
GoalKey<Fox> STALK_PREY = GoalKey.of(Fox.class, NamespacedKey.minecraft("stalk_prey"));
GoalKey<Ocelot> OCELOT_AVOID_ENTITY = GoalKey.of(Ocelot.class, NamespacedKey.minecraft("ocelot_avoid_entity"));
GoalKey<Ocelot> OCELOT_TEMPT = GoalKey.of(Ocelot.class, NamespacedKey.minecraft("ocelot_tempt"));
GoalKey<Panda> PANDA_ATTACK = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_attack"));
GoalKey<Panda> PANDA_AVOID = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_avoid"));
GoalKey<Panda> PANDA_BREED = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_breed"));
GoalKey<Panda> PANDA_HURT_BY = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_hurt_by"));
GoalKey<Panda> PANDA_LIE_ON_BACK = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_lie_on_back"));
GoalKey<Panda> PANDA_LOOK_AT_PLAYER = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_look_at_player"));
GoalKey<Panda> PANDA_PANIC = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_panic"));
GoalKey<Panda> PANDA_ROLL = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_roll"));
GoalKey<Panda> PANDA_SIT = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_sit"));
GoalKey<Panda> PANDA_SNEEZE = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_sneeze"));
GoalKey<PolarBear> POLAR_BEAR_ATTACK_PLAYERS = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polar_bear_attack_players"));
GoalKey<PolarBear> POLAR_BEAR_HURT_BY = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polar_bear_hurt_by"));
GoalKey<PolarBear> POLAR_BEAR_MELEE_ATTACK = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polar_bear_melee_attack"));
GoalKey<PolarBear> POLAR_BEAR_PANIC = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polar_bear_panic"));
GoalKey<PufferFish> PUFFERFISH_PUFF = GoalKey.of(PufferFish.class, NamespacedKey.minecraft("pufferfish_puff"));
GoalKey<Rabbit> RABBIT_AVOID_ENTITY = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("rabbit_avoid_entity"));
GoalKey<Rabbit> RABBIT_PANIC = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("rabbit_panic"));
GoalKey<Rabbit> RAID_GARDEN = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("raid_garden"));
GoalKey<Squid> SQUID_FLEE = GoalKey.of(Squid.class, NamespacedKey.minecraft("squid_flee"));
GoalKey<Squid> SQUID_RANDOM_MOVEMENT = GoalKey.of(Squid.class, NamespacedKey.minecraft("squid_random_movement"));
GoalKey<Turtle> TURTLE_BREED = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_breed"));
GoalKey<Turtle> TURTLE_GO_HOME = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_go_home"));
GoalKey<Turtle> TURTLE_GO_TO_WATER = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_go_to_water"));
GoalKey<Turtle> TURTLE_LAY_EGG = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_lay_egg"));
GoalKey<Turtle> TURTLE_PANIC = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_panic"));
GoalKey<Turtle> TURTLE_RANDOM_STROLL = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_random_stroll"));
GoalKey<Turtle> TURTLE_TRAVEL = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_travel"));
GoalKey<Wolf> WOLF_AVOID_ENTITY = GoalKey.of(Wolf.class, NamespacedKey.minecraft("wolf_avoid_entity"));
GoalKey<Llama> LLAMA_ATTACK_WOLF = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_attack_wolf"));
GoalKey<Llama> LLAMA_HURT_BY = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_hurt_by"));
GoalKey<SkeletonHorse> SKELETON_TRAP = GoalKey.of(SkeletonHorse.class, NamespacedKey.minecraft("skeleton_trap"));
GoalKey<Llama> TRADER_LLAMA_DEFEND_WANDERING_TRADER = GoalKey.of(Llama.class, NamespacedKey.minecraft("trader_llama_defend_wandering_trader"));
GoalKey<Wither> WITHER_DO_NOTHING = GoalKey.of(Wither.class, NamespacedKey.minecraft("wither_do_nothing"));
GoalKey<Illager> RAIDER_OPEN_DOOR = GoalKey.of(Illager.class, NamespacedKey.minecraft("raider_open_door"));
GoalKey<AbstractSkeleton> SKELETON_MELEE = GoalKey.of(AbstractSkeleton.class, NamespacedKey.minecraft("abstract_skeleton_melee"));
GoalKey<Blaze> BLAZE_ATTACK = GoalKey.of(Blaze.class, NamespacedKey.minecraft("blaze_attack"));
GoalKey<Drowned> DROWNED_ATTACK = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_attack"));
GoalKey<Drowned> DROWNED_GO_TO_BEACH = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_go_to_beach"));
GoalKey<Creature> DROWNED_GO_TO_WATER = GoalKey.of(Creature.class, NamespacedKey.minecraft("drowned_go_to_water"));
GoalKey<Drowned> DROWNED_SWIM_UP = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_swim_up"));
GoalKey<RangedEntity> DROWNED_TRIDENT_ATTACK = GoalKey.of(RangedEntity.class, NamespacedKey.minecraft("drowned_trident_attack"));
GoalKey<Enderman> ENDERMAN_FREEZE_WHEN_LOOKED_AT = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_freeze_when_looked_at"));
GoalKey<Enderman> ENDERMAN_LEAVE_BLOCK = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_leave_block"));
GoalKey<Enderman> ENDERMAN_LOOK_FOR_PLAYER = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_look_for_player"));
GoalKey<Enderman> ENDERMAN_TAKE_BLOCK = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_take_block"));
GoalKey<Evoker> EVOKER_ATTACK_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_attack_spell"));
GoalKey<Evoker> EVOKER_CASTING_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_casting_spell"));
GoalKey<Evoker> EVOKER_SUMMON_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_summon_spell"));
GoalKey<Evoker> EVOKER_WOLOLO_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_wololo_spell"));
GoalKey<Ghast> GHAST_LOOK = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_look"));
GoalKey<Ghast> GHAST_SHOOT_FIREBALL = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_shoot_fireball"));
GoalKey<Ghast> RANDOM_FLOAT_AROUND = GoalKey.of(Ghast.class, NamespacedKey.minecraft("random_float_around"));
GoalKey<Guardian> GUARDIAN_ATTACK = GoalKey.of(Guardian.class, NamespacedKey.minecraft("guardian_attack"));
GoalKey<Illusioner> ILLUSIONER_BLINDNESS_SPELL = GoalKey.of(Illusioner.class, NamespacedKey.minecraft("illusioner_blindness_spell"));
GoalKey<Illusioner> ILLUSIONER_MIRROR_SPELL = GoalKey.of(Illusioner.class, NamespacedKey.minecraft("illusioner_mirror_spell"));
GoalKey<Raider> LONG_DISTANCE_PATROL = GoalKey.of(Raider.class, NamespacedKey.minecraft("long_distance_patrol"));
GoalKey<Phantom> PHANTOM_ATTACK_PLAYER = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_attack_player"));
GoalKey<Phantom> PHANTOM_ATTACK_STRATEGY = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_attack_strategy"));
GoalKey<Phantom> PHANTOM_CIRCLE_AROUND_ANCHOR = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_circle_around_anchor"));
GoalKey<Phantom> PHANTOM_SWEEP_ATTACK = GoalKey.of(Phantom.class, NamespacedKey.minecraft("phantom_sweep_attack"));
GoalKey<Shulker> SHULKER_ATTACK = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_attack"));
GoalKey<Shulker> SHULKER_DEFENSE_ATTACK = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_defense_attack"));
GoalKey<Shulker> SHULKER_NEAREST_ATTACK = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_nearest_attack"));
GoalKey<Shulker> SHULKER_PEEK = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_peek"));
GoalKey<Silverfish> SILVERFISH_MERGE_WITH_STONE = GoalKey.of(Silverfish.class, NamespacedKey.minecraft("silverfish_merge_with_stone"));
GoalKey<Silverfish> SILVERFISH_WAKE_UP_FRIENDS = GoalKey.of(Silverfish.class, NamespacedKey.minecraft("silverfish_wake_up_friends"));
GoalKey<Slime> SLIME_ATTACK = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_attack"));
GoalKey<Slime> SLIME_FLOAT = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_float"));
GoalKey<Slime> SLIME_KEEP_ON_JUMPING = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_keep_on_jumping"));
GoalKey<Slime> SLIME_RANDOM_DIRECTION = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_random_direction"));
GoalKey<Spellcaster> SPELLCASTER_CASTING_SPELL = GoalKey.of(Spellcaster.class, NamespacedKey.minecraft("spellcaster_casting_spell"));
GoalKey<Spider> SPIDER_ATTACK = GoalKey.of(Spider.class, NamespacedKey.minecraft("spider_attack"));
GoalKey<Spider> SPIDER = GoalKey.of(Spider.class, NamespacedKey.minecraft("spider"));
GoalKey<Strider> STRIDER_GO_TO_LAVA = GoalKey.of(Strider.class, NamespacedKey.minecraft("strider_go_to_lava"));
GoalKey<Vex> VEX_CHARGE_ATTACK = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_charge_attack"));
GoalKey<Vex> VEX_COPY_OWNER = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_copy_owner"));
GoalKey<Vex> VEX_RANDOM_MOVE = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_random_move"));
GoalKey<Mob> VINDICATOR_BREAK_DOOR = GoalKey.of(Mob.class, NamespacedKey.minecraft("vindicator_break_door"));
GoalKey<Vindicator> VINDICATOR_JOHNNY_ATTACK = GoalKey.of(Vindicator.class, NamespacedKey.minecraft("vindicator_johnny_attack"));
GoalKey<Zombie> ZOMBIE_ATTACK_TURTLE_EGG = GoalKey.of(Zombie.class, NamespacedKey.minecraft("zombie_attack_turtle_egg"));
GoalKey<WanderingTrader> WANDER_TO_POSITION = GoalKey.of(WanderingTrader.class, NamespacedKey.minecraft("wander_to_position"));
GoalKey<Raider> HOLD_GROUND_ATTACK = GoalKey.of(Raider.class, NamespacedKey.minecraft("hold_ground_attack"));
GoalKey<Raider> OBTAIN_RAID_LEADER_BANNER = GoalKey.of(Raider.class, NamespacedKey.minecraft("obtain_raid_leader_banner"));
GoalKey<Raider> RAIDER_CELEBRATION = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_celebration"));
GoalKey<Raider> RAIDER_MOVE_THROUGH_VILLAGE = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_move_through_village"));
GoalKey<Creature> PARROT_WANDER = GoalKey.of(Creature.class, NamespacedKey.minecraft("parrot_wander"));
GoalKey<Mob> CLIMB_ON_TOP_OF_POWDER_SNOW = GoalKey.of(Mob.class, NamespacedKey.minecraft("climb_on_top_of_powder_snow"));
GoalKey<Wolf> WOLF_PANIC = GoalKey.of(Wolf.class, NamespacedKey.minecraft("wolf_panic"));
/**
* @deprecated removed in 1.20.2
*/
@Deprecated GoalKey<Vindicator> VINDICATOR_MELEE_ATTACK = GoalKey.of(Vindicator.class, NamespacedKey.minecraft("vindicator_melee_attack"));
/**
* @deprecated removed in 1.20.2
*/
@Deprecated GoalKey<Ravager> RAVAGER_MELEE_ATTACK = GoalKey.of(Ravager.class, NamespacedKey.minecraft("ravager_melee_attack"));
/**
* @deprecated removed in 1.20.2
*/
@Deprecated GoalKey<Rabbit> EVIL_RABBIT_ATTACK = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("evil_rabbit_attack"));
/**
* @deprecated removed in 1.16
*/
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<PigZombie> ANGER = GoalKey.of(PigZombie.class, NamespacedKey.minecraft("anger"));
/**
* @deprecated removed in 1.16
*/
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<PigZombie> ANGER_OTHER = GoalKey.of(PigZombie.class, NamespacedKey.minecraft("anger_other"));
// the constants below use spigot names, they no longer work
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Blaze> BLAZE_FIREBALL = GoalKey.of(Blaze.class, NamespacedKey.minecraft("blaze_fireball"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Cat> TEMPT_CHANCE = GoalKey.of(Cat.class, NamespacedKey.minecraft("tempt_chance"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Dolphin> DOLPHIN_PLAY_WITH_ITEMS = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("dolphin_play_with_items"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Drowned> DROWNED_GOTO_BEACH = GoalKey.of(Drowned.class, NamespacedKey.minecraft("drowned_goto_beach"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> DROWNED_GOTO_WATER = GoalKey.of(Creature.class, NamespacedKey.minecraft("drowned_goto_water"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Enderman> ENDERMAN_PICKUP_BLOCK = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_pickup_block"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Enderman> ENDERMAN_PLACE_BLOCK = GoalKey.of(Enderman.class, NamespacedKey.minecraft("enderman_place_block"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Enderman> PLAYER_WHO_LOOKED_AT_TARGET = GoalKey.of(Enderman.class, NamespacedKey.minecraft("player_who_looked_at_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Evoker> EVOKER_CAST_SPELL = GoalKey.of(Evoker.class, NamespacedKey.minecraft("evoker_cast_spell"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fox> FOX_DEFEND_TRUSTED = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_defend_trusted"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fox> FOX_FACEPLANT = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_faceplant"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fox> FOX_PERCH_AND_SEARCH = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_perch_and_search"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fox> FOX_SLEEP = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_sleep"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fox> FOX_SEEK_SHELTER = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_seek_shelter"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fox> FOX_STALK_PREY = GoalKey.of(Fox.class, NamespacedKey.minecraft("fox_stalk_prey"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Ghast> GHAST_ATTACK_TARGET = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_attack_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Ghast> GHAST_IDLE_MOVE = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_idle_move"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Ghast> GHAST_MOVE_TOWARDS_TARGET = GoalKey.of(Ghast.class, NamespacedKey.minecraft("ghast_move_towards_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Spellcaster> SPELLCASTER_CAST_SPELL = GoalKey.of(Spellcaster.class, NamespacedKey.minecraft("spellcaster_cast_spell"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<TraderLlama> LLAMATRADER_DEFENDED_WANDERING_TRADER = GoalKey.of(TraderLlama.class, NamespacedKey.minecraft("llamatrader_defended_wandering_trader"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Panda> PANDA_HURT_BY_TARGET = GoalKey.of(Panda.class, NamespacedKey.minecraft("panda_hurt_by_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<PolarBear> POLARBEAR_ATTACK_PLAYERS = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_attack_players"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<PolarBear> POLARBEAR_HURT_BY = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_hurt_by"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<PolarBear> POLARBEAR_MELEE = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_melee"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<PolarBear> POLARBEAR_PANIC = GoalKey.of(PolarBear.class, NamespacedKey.minecraft("polarbear_panic"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Rabbit> EAT_CARROTS = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("eat_carrots"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Rabbit> KILLER_RABBIT_MELEE_ATTACK = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("killer_rabbit_melee_attack"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Rabbit> RABBIT_AVOID_TARGET = GoalKey.of(Rabbit.class, NamespacedKey.minecraft("rabbit_avoid_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Raider> RAIDER_HOLD_GROUND = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_hold_ground"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Raider> RAIDER_OBTAIN_BANNER = GoalKey.of(Raider.class, NamespacedKey.minecraft("raider_obtain_banner"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Shulker> SHULKER_DEFENSE = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_defense"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Shulker> SHULKER_NEAREST = GoalKey.of(Shulker.class, NamespacedKey.minecraft("shulker_nearest"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Silverfish> SILVERFISH_HIDE_IN_BLOCK = GoalKey.of(Silverfish.class, NamespacedKey.minecraft("silverfish_hide_in_block"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Silverfish> SILVERFISH_WAKE_OTHERS = GoalKey.of(Silverfish.class, NamespacedKey.minecraft("silverfish_wake_others"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Slime> SLIME_IDLE = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_idle"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Slime> SLIME_NEAREST_PLAYER = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_nearest_player"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Slime> SLIME_RANDOM_JUMP = GoalKey.of(Slime.class, NamespacedKey.minecraft("slime_random_jump"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Spider> SPIDER_MELEE_ATTACK = GoalKey.of(Spider.class, NamespacedKey.minecraft("spider_melee_attack"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Spider> SPIDER_NEAREST_ATTACKABLE_TARGET = GoalKey.of(Spider.class, NamespacedKey.minecraft("spider_nearest_attackable_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Squid> SQUID = GoalKey.of(Squid.class, NamespacedKey.minecraft("squid"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Turtle> TURTLE_GOTO_WATER = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_goto_water"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Turtle> TURTLE_TEMPT = GoalKey.of(Turtle.class, NamespacedKey.minecraft("turtle_tempt"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Vex> VEX_COPY_TARGET_OF_OWNER = GoalKey.of(Vex.class, NamespacedKey.minecraft("vex_copy_target_of_owner"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<WanderingTrader> VILLAGERTRADER_WANDER_TO_POSITION = GoalKey.of(WanderingTrader.class, NamespacedKey.minecraft("villagertrader_wander_to_position"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<RangedEntity> ARROW_ATTACK = GoalKey.of(RangedEntity.class, NamespacedKey.minecraft("arrow_attack"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> AVOID_TARGET = GoalKey.of(Creature.class, NamespacedKey.minecraft("avoid_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Monster> BOW_SHOOT = GoalKey.of(Monster.class, NamespacedKey.minecraft("bow_shoot"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> BREATH = GoalKey.of(Creature.class, NamespacedKey.minecraft("breath"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Cat> CAT_SIT_ON_BED = GoalKey.of(Cat.class, NamespacedKey.minecraft("cat_sit_on_bed"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Monster> CROSSBOW_ATTACK = GoalKey.of(Monster.class, NamespacedKey.minecraft("crossbow_attack"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> DOOR_OPEN = GoalKey.of(Mob.class, NamespacedKey.minecraft("door_open"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> EAT_TILE = GoalKey.of(Mob.class, NamespacedKey.minecraft("eat_tile"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Fish> FISH_SCHOOL = GoalKey.of(Fish.class, NamespacedKey.minecraft("fish_school"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> FOLLOW_ENTITY = GoalKey.of(Mob.class, NamespacedKey.minecraft("follow_entity"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<SkeletonHorse> HORSE_TRAP = GoalKey.of(SkeletonHorse.class, NamespacedKey.minecraft("horse_trap"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> HURT_BY_TARGET = GoalKey.of(Creature.class, NamespacedKey.minecraft("hurt_by_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Cat> JUMP_ON_BLOCK = GoalKey.of(Cat.class, NamespacedKey.minecraft("jump_on_block"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> LEAP_AT_TARGET = GoalKey.of(Mob.class, NamespacedKey.minecraft("leap_at_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Llama> LLAMA_FOLLOW = GoalKey.of(Llama.class, NamespacedKey.minecraft("llama_follow"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> MOVE_TOWARDS_TARGET = GoalKey.of(Creature.class, NamespacedKey.minecraft("move_towards_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> NEAREST_ATTACKABLE_TARGET = GoalKey.of(Mob.class, NamespacedKey.minecraft("nearest_attackable_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Raider> NEAREST_ATTACKABLE_TARGET_WITCH = GoalKey.of(Raider.class, NamespacedKey.minecraft("nearest_attackable_target_witch"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> NEAREST_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("nearest_village"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Tameable> OWNER_HURT_BY_TARGET = GoalKey.of(Tameable.class, NamespacedKey.minecraft("owner_hurt_by_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Tameable> OWNER_HURT_TARGET = GoalKey.of(Tameable.class, NamespacedKey.minecraft("owner_hurt_target"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Parrot> PERCH = GoalKey.of(Parrot.class, NamespacedKey.minecraft("perch"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Raider> RAID = GoalKey.of(Raider.class, NamespacedKey.minecraft("raid"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> RANDOM_FLY = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_fly"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> RANDOM_LOOKAROUND = GoalKey.of(Mob.class, NamespacedKey.minecraft("random_lookaround"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> RANDOM_STROLL_LAND = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_stroll_land"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> RANDOM_SWIM = GoalKey.of(Creature.class, NamespacedKey.minecraft("random_swim"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Tameable> RANDOM_TARGET_NON_TAMED = GoalKey.of(Tameable.class, NamespacedKey.minecraft("random_target_non_tamed"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Tameable> SIT = GoalKey.of(Tameable.class, NamespacedKey.minecraft("sit"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> STROLL_VILLAGE = GoalKey.of(Creature.class, NamespacedKey.minecraft("stroll_village"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<AbstractHorse> TAME = GoalKey.of(AbstractHorse.class, NamespacedKey.minecraft("tame"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> WATER = GoalKey.of(Creature.class, NamespacedKey.minecraft("water"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Dolphin> WATER_JUMP = GoalKey.of(Dolphin.class, NamespacedKey.minecraft("water_jump"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Creature> STROLL_VILLAGE_GOLEM = GoalKey.of(Creature.class, NamespacedKey.minecraft("stroll_village_golem"));
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21") GoalKey<Mob> UNIVERSAL_ANGER_RESET = GoalKey.of(Mob.class, NamespacedKey.minecraft("universal_anger_reset"));
}

View file

@ -0,0 +1,57 @@
package com.destroystokyo.paper.entity.villager;
import com.google.common.base.Preconditions;
import java.util.EnumMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
/**
* A reputation score for a player on a villager.
*/
public final class Reputation {
@NotNull
private final Map<ReputationType, Integer> reputation;
public Reputation() {
this(new EnumMap<>(ReputationType.class));
}
public Reputation(@NotNull Map<ReputationType, Integer> reputation) {
Preconditions.checkNotNull(reputation, "reputation cannot be null");
this.reputation = reputation;
}
/**
* Gets the reputation value for a specific {@link ReputationType}.
*
* @param type The {@link ReputationType type} of reputation to get.
* @return The value of the {@link ReputationType type}.
*/
public int getReputation(@NotNull ReputationType type) {
Preconditions.checkNotNull(type, "the reputation type cannot be null");
return this.reputation.getOrDefault(type, 0);
}
/**
* Sets the reputation value for a specific {@link ReputationType}.
*
* @param type The {@link ReputationType type} of reputation to set.
* @param value The value of the {@link ReputationType type}.
*/
public void setReputation(@NotNull ReputationType type, int value) {
Preconditions.checkNotNull(type, "the reputation type cannot be null");
this.reputation.put(type, value);
}
/**
* Gets if a reputation value is currently set for a specific {@link ReputationType}.
*
* @param type The {@link ReputationType type} to check
* @return If there is a value for this {@link ReputationType type} set.
*/
public boolean hasReputationSet(@NotNull ReputationType type) {
return this.reputation.containsKey(type);
}
}

View file

@ -0,0 +1,36 @@
package com.destroystokyo.paper.entity.villager;
/**
* A type of reputation gained with a {@link org.bukkit.entity.Villager Villager}.
* <p>
* All types but {@link #MAJOR_POSITIVE} are shared to other villagers.
*/
public enum ReputationType {
/**
* A gossip with a majorly negative effect. This is only gained through killing a nearby
* villager.
*/
MAJOR_NEGATIVE,
/**
* A gossip with a minor negative effect. This is only gained through damaging a villager.
*/
MINOR_NEGATIVE,
/**
* A gossip with a minor positive effect. This is only gained through curing a zombie
* villager.
*/
MINOR_POSITIVE,
/**
* A gossip with a major positive effect. This is only gained through curing a zombie
* villager.
*/
MAJOR_POSITIVE,
/**
* A gossip with a minor positive effect. This is only gained through trading with a villager.
*/
TRADING,
}

View file

@ -0,0 +1,151 @@
package com.destroystokyo.paper.event.block;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.inventory.InventoryEvent;
import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when an anvil is damaged from being used
*/
public class AnvilDamagedEvent extends InventoryEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancel;
private DamageState damageState;
@ApiStatus.Internal
public AnvilDamagedEvent(@NotNull InventoryView inventory, @Nullable BlockData blockData) {
super(inventory);
this.damageState = DamageState.getState(blockData);
}
@NotNull
@Override
public AnvilInventory getInventory() {
return (AnvilInventory) super.getInventory();
}
/**
* Gets the new state of damage on the anvil
*
* @return Damage state
*/
@NotNull
public DamageState getDamageState() {
return damageState;
}
/**
* Sets the new state of damage on the anvil
*
* @param damageState Damage state
*/
public void setDamageState(@NotNull DamageState damageState) {
this.damageState = damageState;
}
/**
* Gets if anvil is breaking on this use
*
* @return True if breaking
*/
public boolean isBreaking() {
return damageState == DamageState.BROKEN;
}
/**
* Sets if anvil is breaking on this use
*
* @param breaking True if breaking
*/
public void setBreaking(boolean breaking) {
if (breaking) {
damageState = DamageState.BROKEN;
} else if (damageState == DamageState.BROKEN) {
damageState = DamageState.DAMAGED;
}
}
public boolean isCancelled() {
return cancel;
}
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@NotNull
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
/**
* Represents the amount of damage on an anvil block
*/
public enum DamageState {
FULL(Material.ANVIL),
CHIPPED(Material.CHIPPED_ANVIL),
DAMAGED(Material.DAMAGED_ANVIL),
BROKEN(Material.AIR);
private final Material material;
DamageState(@NotNull Material material) {
this.material = material;
}
/**
* Get block material of this state
*
* @return Material
*/
@NotNull
public Material getMaterial() {
return material;
}
/**
* Get damaged state by block data
*
* @param blockData Block data
* @return DamageState
* @throws IllegalArgumentException If non anvil block data is given
*/
@NotNull
public static DamageState getState(@Nullable BlockData blockData) {
return blockData == null ? BROKEN : getState(blockData.getMaterial());
}
/**
* Get damaged state by block material
*
* @param material Block material
* @return DamageState
* @throws IllegalArgumentException If non anvil material is given
*/
@NotNull
public static DamageState getState(@Nullable Material material) {
if (material == null) {
return BROKEN;
}
for (DamageState state : values()) {
if (state.material == material) {
return state;
}
}
throw new IllegalArgumentException("Material is not an anvil state");
}
}
}

View file

@ -0,0 +1,86 @@
package com.destroystokyo.paper.event.block;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.potion.PotionEffect;
import org.jetbrains.annotations.NotNull;
/**
* Called when a beacon effect is being applied to a player.
*/
public class BeaconEffectEvent extends BlockEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private PotionEffect effect;
private Player player;
private boolean primary;
public BeaconEffectEvent(@NotNull Block block, @NotNull PotionEffect effect, @NotNull Player player, boolean primary) {
super(block);
this.effect = effect;
this.player = player;
this.primary = primary;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Gets the potion effect being applied.
*
* @return Potion effect
*/
@NotNull
public PotionEffect getEffect() {
return effect;
}
/**
* Sets the potion effect that will be applied.
*
* @param effect Potion effect
*/
public void setEffect(@NotNull PotionEffect effect) {
this.effect = effect;
}
/**
* Gets the player who the potion effect is being applied to.
*
* @return Affected player
*/
@NotNull
public Player getPlayer() {
return player;
}
/**
* Gets whether the effect is a primary beacon effect.
*
* @return true if this event represents a primary effect
*/
public boolean isPrimary() {
return primary;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,99 @@
package com.destroystokyo.paper.event.block;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired anytime the server intends to 'destroy' a block through some triggering reason.
* This does not fire anytime a block is set to air, but only with more direct triggers such
* as physics updates, pistons, Entities changing blocks, commands set to "Destroy".
*
* This event is associated with the game playing a sound effect at the block in question, when
* something can be described as "intend to destroy what is there",
*
* Events such as leaves decaying, pistons retracting (where the block is moving), does NOT fire this event.
*
*/
public class BlockDestroyEvent extends BlockEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
@NotNull private final BlockData newState;
private boolean willDrop;
private boolean playEffect = true;
private boolean cancelled = false;
public BlockDestroyEvent(@NotNull Block block, @NotNull BlockData newState, boolean willDrop) {
super(block);
this.newState = newState;
this.willDrop = willDrop;
}
/**
* @return The new state of this block (Air, or a Fluid type)
*/
@NotNull
public BlockData getNewState() {
return newState;
}
/**
* @return If the server is going to drop the block in question with this destroy event
*/
public boolean willDrop() {
return this.willDrop;
}
/**
* @param willDrop If the server is going to drop the block in question with this destroy event
*/
public void setWillDrop(boolean willDrop) {
this.willDrop = willDrop;
}
/**
* @return If the server is going to play the sound effect for this destruction
*/
public boolean playEffect() {
return this.playEffect;
}
/**
* @param playEffect If the server should play the sound effect for this destruction
*/
public void setPlayEffect(boolean playEffect) {
this.playEffect = playEffect;
}
/**
* @return If the event is cancelled, meaning the block will not be destroyed
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* If the event is cancelled, the block will remain in its previous state.
* @param cancel true if you wish to cancel this event
*/
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,116 @@
package com.destroystokyo.paper.event.block;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when TNT block is about to turn into {@link org.bukkit.entity.TNTPrimed}
* <p>
* Cancelling it won't turn TNT into {@link org.bukkit.entity.TNTPrimed} and leaves
* the TNT block as-is
*
* @author Mark Vainomaa
* @deprecated use {@link org.bukkit.event.block.TNTPrimeEvent}
*/
@Deprecated(forRemoval = true)
public class TNTPrimeEvent extends BlockEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
@NotNull private PrimeReason reason;
@Nullable private Entity primerEntity;
public TNTPrimeEvent(@NotNull Block theBlock, @NotNull PrimeReason reason, @Nullable Entity primerEntity) {
super(theBlock);
this.reason = reason;
this.primerEntity = primerEntity;
}
/**
* Gets the TNT prime reason
*
* @return Prime reason
*/
@NotNull
public PrimeReason getReason() {
return this.reason;
}
/**
* Gets the TNT primer {@link Entity}.
*
* It's null if {@link #getReason()} is {@link PrimeReason#REDSTONE} or {@link PrimeReason#FIRE}.
* It's not null if {@link #getReason()} is {@link PrimeReason#ITEM} or {@link PrimeReason#PROJECTILE}
* It might be null if {@link #getReason()} is {@link PrimeReason#EXPLOSION}
*
* @return The {@link Entity} who primed the TNT
*/
@Nullable
public Entity getPrimerEntity() {
return this.primerEntity;
}
/**
* Gets whether spawning {@link org.bukkit.entity.TNTPrimed} should be cancelled or not
*
* @return Whether spawning {@link org.bukkit.entity.TNTPrimed} should be cancelled or not
*/
@Override
public boolean isCancelled() {
return this.cancelled;
}
/**
* Sets whether to cancel spawning {@link org.bukkit.entity.TNTPrimed} or not
*
* @param cancel whether spawning {@link org.bukkit.entity.TNTPrimed} should be cancelled or not
*/
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Nullable
@Override
public HandlerList getHandlers() {
return handlers;
}
@Nullable
public static HandlerList getHandlerList() {
return handlers;
}
public enum PrimeReason {
/**
* When TNT prime was caused by other explosion (chain reaction)
*/
EXPLOSION,
/**
* When TNT prime was caused by fire
*/
FIRE,
/**
* When {@link org.bukkit.entity.Player} used {@link org.bukkit.Material#FLINT_AND_STEEL} or
* {@link org.bukkit.Material#FIRE_CHARGE} on given TNT block
*/
ITEM,
/**
* When TNT prime was caused by an {@link Entity} shooting TNT
* using a bow with {@link org.bukkit.enchantments.Enchantment#ARROW_FIRE} enchantment
*/
PROJECTILE,
/**
* When redstone power triggered the TNT prime
*/
REDSTONE
}
}

View file

@ -0,0 +1,56 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Creeper;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when a Creeper is ignited either by a
* flint and steel, {@link Creeper#ignite()} or
* {@link Creeper#setIgnited(boolean)}.
*/
public class CreeperIgniteEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean canceled;
private boolean ignited;
public CreeperIgniteEvent(@NotNull Creeper creeper, boolean ignited) {
super(creeper);
this.ignited = ignited;
}
@NotNull
@Override
public Creeper getEntity() {
return (Creeper) entity;
}
public boolean isIgnited() {
return ignited;
}
public void setIgnited(boolean ignited) {
this.ignited = ignited;
}
public boolean isCancelled() {
return canceled;
}
public void setCancelled(boolean cancel) {
canceled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,77 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.AreaEffectCloud;
import org.bukkit.entity.DragonFireball;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import java.util.Collection;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a DragonFireball collides with a block/entity and spawns an AreaEffectCloud
*/
public class EnderDragonFireballHitEvent extends EntityEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
@NotNull private final Collection<LivingEntity> targets;
@NotNull private final AreaEffectCloud areaEffectCloud;
private boolean cancelled = false;
@ApiStatus.Internal
public EnderDragonFireballHitEvent(@NotNull DragonFireball fireball, @NotNull Collection<LivingEntity> targets, @NotNull AreaEffectCloud areaEffectCloud) {
super(fireball);
this.targets = targets;
this.areaEffectCloud = areaEffectCloud;
}
/**
* The fireball involved in this event
*/
@NotNull
@Override
public DragonFireball getEntity() {
return (DragonFireball) super.getEntity();
}
/**
* The living entities hit by fireball
*
* @return the targets
*/
@NotNull
public Collection<LivingEntity> getTargets() {
return targets;
}
/**
* @return The area effect cloud spawned in this collision
*/
@NotNull
public AreaEffectCloud getAreaEffectCloud() {
return areaEffectCloud;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
@NotNull
public HandlerList getHandlers() {
return HANDLER_LIST;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

View file

@ -0,0 +1,61 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.AreaEffectCloud;
import org.bukkit.entity.EnderDragon;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when an EnderDragon spawns an AreaEffectCloud by shooting flames
*/
public class EnderDragonFlameEvent extends EntityEvent implements Cancellable {
@NotNull private final AreaEffectCloud areaEffectCloud;
public EnderDragonFlameEvent(@NotNull EnderDragon enderDragon, @NotNull AreaEffectCloud areaEffectCloud) {
super(enderDragon);
this.areaEffectCloud = areaEffectCloud;
}
/**
* The enderdragon involved in this event
*/
@NotNull
@Override
public EnderDragon getEntity() {
return (EnderDragon) super.getEntity();
}
/**
* @return The area effect cloud spawned in this collision
*/
@NotNull
public AreaEffectCloud getAreaEffectCloud() {
return areaEffectCloud;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,61 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.DragonFireball;
import org.bukkit.entity.EnderDragon;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when an EnderDragon shoots a fireball
*/
public class EnderDragonShootFireballEvent extends EntityEvent implements Cancellable {
@NotNull private final DragonFireball fireball;
public EnderDragonShootFireballEvent(@NotNull EnderDragon entity, @NotNull DragonFireball fireball) {
super(entity);
this.fireball = fireball;
}
/**
* The enderdragon shooting the fireball
*/
@NotNull
@Override
public EnderDragon getEntity() {
return (EnderDragon) super.getEntity();
}
/**
* @return The fireball being shot
*/
@NotNull
public DragonFireball getFireball() {
return fireball;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2018 Daniel Ennis (Aikar) MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Enderman;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when an Enderman determines if it should attack a player or not.
* Starts off cancelled if the player is wearing a pumpkin head or is not looking
* at the Enderman, according to Vanilla rules.
*
*/
public class EndermanAttackPlayerEvent extends EntityEvent implements Cancellable {
@NotNull private final Player player;
public EndermanAttackPlayerEvent(@NotNull Enderman entity, @NotNull Player player) {
super(entity);
this.player = player;
}
/**
* The enderman considering attacking
*
* @return The enderman considering attacking
*/
@NotNull
@Override
public Enderman getEntity() {
return (Enderman) super.getEntity();
}
/**
* The player the Enderman is considering attacking
*
* @return The player the Enderman is considering attacking
*/
@NotNull
public Player getPlayer() {
return player;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
/**
*
* @return If cancelled, the enderman will not attack
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Cancels if the Enderman will attack this player
* @param cancel true if you wish to cancel this event
*/
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,87 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Enderman;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
public class EndermanEscapeEvent extends EntityEvent implements Cancellable {
@NotNull private final Reason reason;
public EndermanEscapeEvent(@NotNull Enderman entity, @NotNull Reason reason) {
super(entity);
this.reason = reason;
}
@NotNull
@Override
public Enderman getEntity() {
return (Enderman) super.getEntity();
}
/**
* @return The reason the enderman is trying to escape
*/
@NotNull
public Reason getReason() {
return reason;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Cancels the escape.
*
* If this escape normally would of resulted in damage avoidance such as indirect,
* the enderman will now take damage.
*
* @param cancel true if you wish to cancel this event
*/
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
public enum Reason {
/**
* The enderman has stopped attacking and ran away
*/
RUNAWAY,
/**
* The enderman has teleported away due to indirect damage (ranged)
*/
INDIRECT,
/**
* The enderman has teleported away due to a critical hit
*/
CRITICAL_HIT,
/**
* The enderman has teleported away due to the player staring at it during combat
*/
STARE,
/**
* Specific case for CRITICAL_HIT where the enderman is taking rain damage
*/
DROWN
}
}

View file

@ -0,0 +1,32 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired any time an entity is being added to the world for any reason.
*
* Not to be confused with {@link org.bukkit.event.entity.CreatureSpawnEvent}
* This will fire anytime a chunk is reloaded too.
*/
public class EntityAddToWorldEvent extends EntityEvent {
public EntityAddToWorldEvent(@NotNull Entity entity) {
super(entity);
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,46 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when an entity jumps
* <p>
* Cancelling the event will stop the entity from jumping
*/
public class EntityJumpEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean canceled;
public EntityJumpEvent(@NotNull LivingEntity entity) {
super(entity);
}
@NotNull
@Override
public LivingEntity getEntity() {
return (LivingEntity) entity;
}
public boolean isCancelled() {
return canceled;
}
public void setCancelled(boolean cancel) {
canceled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,46 @@
package com.destroystokyo.paper.event.entity;
import io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
/**
* Fired when an Entity is knocked back by the hit of another Entity. The acceleration
* vector can be modified. If this event is cancelled, the entity is not knocked back.
*
*/
public class EntityKnockbackByEntityEvent extends EntityPushedByEntityAttackEvent {
private final float knockbackStrength;
public EntityKnockbackByEntityEvent(@NotNull LivingEntity entity, @NotNull Entity hitBy, float knockbackStrength, @NotNull Vector acceleration) {
super(entity, hitBy, acceleration);
this.knockbackStrength = knockbackStrength;
}
/**
* @return the entity which was knocked back
*/
@NotNull
@Override
public LivingEntity getEntity() {
return (LivingEntity) super.getEntity();
}
/**
* @return the original knockback strength.
*/
public float getKnockbackStrength() {
return knockbackStrength;
}
/**
* @return the Entity which hit
*/
@NotNull
public Entity getHitBy() {
return super.getPushedBy();
}
}

View file

@ -0,0 +1,82 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Fired when an Entity decides to start moving towards a location.
*
* This event does not fire for the entities actual movement. Only when it
* is choosing to start moving to a location.
*/
public class EntityPathfindEvent extends EntityEvent implements Cancellable {
@Nullable private final Entity targetEntity;
@NotNull private final Location loc;
public EntityPathfindEvent(@NotNull Entity entity, @NotNull Location loc, @Nullable Entity targetEntity) {
super(entity);
this.targetEntity = targetEntity;
this.loc = loc;
}
/**
* The Entity that is pathfinding.
* @return The Entity that is pathfinding.
*/
@NotNull
public Entity getEntity() {
return entity;
}
/**
* If the Entity is trying to pathfind to an entity, this is the entity in relation.
*
* Otherwise this will return null.
*
* @return The entity target or null
*/
@Nullable
public Entity getTargetEntity() {
return targetEntity;
}
/**
* The Location of where the entity is about to move to.
*
* Note that if the target happened to of been an entity
* @return Location of where the entity is trying to pathfind to.
*/
@NotNull
public Location getLoc() {
return loc;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,29 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired any time an entity is being removed from a world for any reason
*/
public class EntityRemoveFromWorldEvent extends EntityEvent {
public EntityRemoveFromWorldEvent(@NotNull Entity entity) {
super(entity);
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,31 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.Location;
import org.bukkit.block.EndGateway;
import org.bukkit.entity.Entity;
import org.bukkit.event.entity.EntityTeleportEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired any time an entity attempts to teleport in an end gateway
*/
public class EntityTeleportEndGatewayEvent extends EntityTeleportEvent {
@NotNull private final EndGateway gateway;
public EntityTeleportEndGatewayEvent(@NotNull Entity what, @NotNull Location from, @NotNull Location to, @NotNull EndGateway gateway) {
super(what, from, to);
this.gateway = gateway;
}
/**
* The gateway triggering the teleport
*
* @return EndGateway used
*/
@NotNull
public EndGateway getGateway() {
return gateway;
}
}

View file

@ -0,0 +1,93 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.event.entity.EntityTransformEvent;
import org.jetbrains.annotations.ApiStatus;
/**
* Fired when an entity transforms into another entity
* <p>
* If the event is cancelled, the entity will not transform
* @deprecated Bukkit has added {@link EntityTransformEvent}, you should start using that
*/
@Deprecated(forRemoval = true) @ApiStatus.ScheduledForRemoval(inVersion = "1.21")
public class EntityTransformedEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private final Entity transformed;
private final TransformedReason reason;
public EntityTransformedEvent(Entity entity, Entity transformed, TransformedReason reason) {
super(entity);
this.transformed = transformed;
this.reason = reason;
}
/**
* The entity after it has transformed
*
* @return Transformed entity
* @deprecated see {@link EntityTransformEvent#getTransformedEntity()}
*/
@Deprecated
public Entity getTransformed() {
return transformed;
}
/**
* @return The reason for the transformation
* @deprecated see {@link EntityTransformEvent#getTransformReason()}
*/
@Deprecated
public TransformedReason getReason() {
return reason;
}
@Override
public HandlerList getHandlers(){
return handlers;
}
public static HandlerList getHandlerList(){
return handlers;
}
@Override
public boolean isCancelled(){
return cancelled;
}
@Override
public void setCancelled(boolean cancel){
cancelled = cancel;
}
public enum TransformedReason {
/**
* When a zombie drowns
*/
DROWNED,
/**
* When a zombie villager is cured
*/
CURED,
/**
* When a villager turns to a zombie villager
*/
INFECTED,
/**
* When a mooshroom turns to a cow
*/
SHEARED,
/**
* When a pig turns to a zombiepigman
*/
LIGHTNING
}
}

View file

@ -0,0 +1,64 @@
package com.destroystokyo.paper.event.entity;
import com.google.common.base.Preconditions;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LightningStrike;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityTransformEvent;
import java.util.Collections;
import org.jetbrains.annotations.NotNull;
/**
* Fired when lightning strikes an entity
*/
public class EntityZapEvent extends EntityTransformEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
@NotNull private final LightningStrike bolt;
public EntityZapEvent(@NotNull final Entity entity, @NotNull final LightningStrike bolt, @NotNull final Entity replacementEntity) {
super(entity, Collections.singletonList(replacementEntity), TransformReason.LIGHTNING);
Preconditions.checkNotNull(bolt);
Preconditions.checkNotNull(replacementEntity);
this.bolt = bolt;
}
public boolean isCancelled() {
return cancelled;
}
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
/**
* Gets the lightning bolt that is striking the entity.
* @return The lightning bolt responsible for this event
*/
@NotNull
public LightningStrike getBolt() {
return bolt;
}
/**
* Gets the entity that will replace the struck entity.
* @return The entity that will replace the struck entity
*/
@NotNull
public Entity getReplacementEntity() {
return getTransformedEntity();
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2017 Daniel Ennis (Aikar) MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired anytime the server is about to merge 2 experience orbs into one
*/
public class ExperienceOrbMergeEvent extends EntityEvent implements Cancellable {
@NotNull private final ExperienceOrb mergeTarget;
@NotNull private final ExperienceOrb mergeSource;
public ExperienceOrbMergeEvent(@NotNull ExperienceOrb mergeTarget, @NotNull ExperienceOrb mergeSource) {
super(mergeTarget);
this.mergeTarget = mergeTarget;
this.mergeSource = mergeSource;
}
/**
* @return The orb that will absorb the other experience orb
*/
@NotNull
public ExperienceOrb getMergeTarget() {
return mergeTarget;
}
/**
* @return The orb that is subject to being removed and merged into the target orb
*/
@NotNull
public ExperienceOrb getMergeSource() {
return mergeSource;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* @param cancel true if you wish to cancel this event, and prevent the orbs from merging
*/
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,33 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when a phantom is spawned for an exhausted player
*/
public class PhantomPreSpawnEvent extends PreCreatureSpawnEvent {
@NotNull private final Entity entity;
@ApiStatus.Internal
public PhantomPreSpawnEvent(@NotNull Location location, @NotNull Entity entity, @NotNull CreatureSpawnEvent.SpawnReason reason) {
super(location, EntityType.PHANTOM, reason);
this.entity = entity;
}
/**
* Get the entity this phantom is spawning for
*
* @return Entity
*/
@NotNull
public Entity getSpawningEntity() {
return entity;
}
}

View file

@ -0,0 +1,64 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when the server is calculating what chunks to try to spawn monsters in every Monster Spawn Tick event
*/
public class PlayerNaturallySpawnCreaturesEvent extends PlayerEvent implements Cancellable {
private byte radius;
public PlayerNaturallySpawnCreaturesEvent(@NotNull Player player, byte radius) {
super(player);
this.radius = radius;
}
/**
* @return The radius of chunks around this player to be included in natural spawn selection
*/
public byte getSpawnRadius() {
return radius;
}
/**
* @param radius The radius of chunks around this player to be included in natural spawn selection
*/
public void setSpawnRadius(byte radius) {
this.radius = radius;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
/**
* @return If this players chunks will be excluded from natural spawns
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* @param cancel true if you wish to cancel this event, and not include this players chunks for natural spawning
*/
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,105 @@
package com.destroystokyo.paper.event.entity;
import com.google.common.base.Preconditions;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.jetbrains.annotations.NotNull;
/**
* WARNING: This event only fires for a limited number of cases, and not for every case that CreatureSpawnEvent does.
*
* You should still listen to CreatureSpawnEvent as a backup, and only use this event as an "enhancement".
* The intent of this event is to improve server performance, so it fires even if the spawning might fail later, for
* example when the entity would be unable to spawn due to limited space or lighting.
*
* Currently: NATURAL and SPAWNER based reasons. Please submit a Pull Request for future additions.
* Also, Plugins that replace Entity Registrations with their own custom entities might not fire this event.
*/
public class PreCreatureSpawnEvent extends Event implements Cancellable {
@NotNull private final Location location;
@NotNull private final EntityType type;
@NotNull private final CreatureSpawnEvent.SpawnReason reason;
private boolean shouldAbortSpawn;
public PreCreatureSpawnEvent(@NotNull Location location, @NotNull EntityType type, @NotNull CreatureSpawnEvent.SpawnReason reason) {
this.location = Preconditions.checkNotNull(location, "Location may not be null");
this.type = Preconditions.checkNotNull(type, "Type may not be null");
this.reason = Preconditions.checkNotNull(reason, "Reason may not be null");
}
/**
* @return The location this creature is being spawned at
*/
@NotNull
public Location getSpawnLocation() {
return location;
}
/**
* @return The type of creature being spawned
*/
@NotNull
public EntityType getType() {
return type;
}
/**
* @return Reason this creature is spawning (ie, NATURAL vs SPAWNER)
*/
@NotNull
public CreatureSpawnEvent.SpawnReason getReason() {
return reason;
}
/**
* @return If the spawn process should be aborted vs trying more attempts
*/
public boolean shouldAbortSpawn() {
return shouldAbortSpawn;
}
/**
* Set this if you are more blanket blocking all types of these spawns, and wish to abort the spawn process from
* trying more attempts after this cancellation.
*
* @param shouldAbortSpawn Set if the spawn process should be aborted vs trying more attempts
*/
public void setShouldAbortSpawn(boolean shouldAbortSpawn) {
this.shouldAbortSpawn = shouldAbortSpawn;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
/**
* @return If the spawn of this creature is cancelled or not
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Cancelling this event is more efficient than cancelling CreatureSpawnEvent
* @param cancel true if you wish to cancel this event, and abort the spawn of this creature
*/
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,29 @@
package com.destroystokyo.paper.event.entity;
import com.google.common.base.Preconditions;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called before an entity is spawned into a world by a spawner.
*
* This only includes the spawner's location and not the full BlockState snapshot for performance reasons.
* If you really need it you have to get the spawner yourself.
*/
public class PreSpawnerSpawnEvent extends PreCreatureSpawnEvent {
@NotNull private final Location spawnerLocation;
public PreSpawnerSpawnEvent(@NotNull Location location, @NotNull EntityType type, @NotNull Location spawnerLocation) {
super(location, type, CreatureSpawnEvent.SpawnReason.SPAWNER);
this.spawnerLocation = Preconditions.checkNotNull(spawnerLocation, "Spawner location may not be null");
}
@NotNull
public Location getSpawnerLocation() {
return spawnerLocation;
}
}

View file

@ -0,0 +1,69 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when a projectile collides with an entity
* <p>
* This event is called <b>before</b> {@link org.bukkit.event.entity.EntityDamageByEntityEvent}, and cancelling it will allow the projectile to continue flying
* @deprecated Deprecated, use {@link org.bukkit.event.entity.ProjectileHitEvent} and check if there is a hit entity
*/
@Deprecated
public class ProjectileCollideEvent extends EntityEvent implements Cancellable {
@NotNull private final Entity collidedWith;
/**
* Get the entity the projectile collided with
*
* @return the entity collided with
*/
@NotNull
public Entity getCollidedWith() {
return collidedWith;
}
public ProjectileCollideEvent(@NotNull Projectile what, @NotNull Entity collidedWith) {
super(what);
this.collidedWith = collidedWith;
}
/**
* Get the projectile that collided
*
* @return the projectile that collided
*/
@NotNull
public Projectile getEntity() {
return (Projectile) super.getEntity();
}
private static final HandlerList handlerList = new HandlerList();
@NotNull
public static HandlerList getHandlerList() {
return handlerList;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlerList;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
}

View file

@ -0,0 +1,62 @@
package com.destroystokyo.paper.event.entity;
import com.google.common.collect.ImmutableList;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.SkeletonHorse;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Event called when a player gets close to a skeleton horse and triggers the lightning trap
*/
public class SkeletonHorseTrapEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private final List<HumanEntity> eligibleHumans;
public SkeletonHorseTrapEvent(@NotNull SkeletonHorse horse) {
this(horse, ImmutableList.of());
}
public SkeletonHorseTrapEvent(@NotNull SkeletonHorse horse, @NotNull List<HumanEntity> eligibleHumans) {
super(horse);
this.eligibleHumans = eligibleHumans;
}
@NotNull
@Override
public SkeletonHorse getEntity() {
return (SkeletonHorse) super.getEntity();
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@NotNull
public List<HumanEntity> getEligibleHumans() {
return eligibleHumans;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,38 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Slime;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Slime decides to change its facing direction.
* <p>
* This event does not fire for the entity's actual movement. Only when it
* is choosing to change direction.
*/
public class SlimeChangeDirectionEvent extends SlimePathfindEvent implements Cancellable {
private float yaw;
public SlimeChangeDirectionEvent(@NotNull Slime slime, float yaw) {
super(slime);
this.yaw = yaw;
}
/**
* Get the new chosen yaw
*
* @return Chosen yaw
*/
public float getNewYaw() {
return yaw;
}
/**
* Set the new chosen yaw
*
* @param yaw Chosen yaw
*/
public void setNewYaw(float yaw) {
this.yaw = yaw;
}
}

View file

@ -0,0 +1,53 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Slime;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Slime decides to start pathfinding.
* <p>
* This event does not fire for the entity's actual movement. Only when it
* is choosing to start moving.
*/
public class SlimePathfindEvent extends EntityEvent implements Cancellable {
public SlimePathfindEvent(@NotNull Slime slime) {
super(slime);
}
/**
* The Slime that is pathfinding.
*
* @return The Slime that is pathfinding.
*/
@NotNull
public Slime getEntity() {
return (Slime) entity;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,17 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Slime;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Slime decides to start jumping while swimming in water/lava.
* <p>
* This event does not fire for the entity's actual movement. Only when it
* is choosing to start jumping.
*/
public class SlimeSwimEvent extends SlimeWanderEvent implements Cancellable {
public SlimeSwimEvent(@NotNull Slime slime) {
super(slime);
}
}

View file

@ -0,0 +1,31 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Slime;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Slime decides to change direction to target a LivingEntity.
* <p>
* This event does not fire for the entity's actual movement. Only when it
* is choosing to start moving.
*/
public class SlimeTargetLivingEntityEvent extends SlimePathfindEvent implements Cancellable {
@NotNull private final LivingEntity target;
public SlimeTargetLivingEntityEvent(@NotNull Slime slime, @NotNull LivingEntity target) {
super(slime);
this.target = target;
}
/**
* Get the targeted entity
*
* @return Targeted entity
*/
@NotNull
public LivingEntity getTarget() {
return target;
}
}

View file

@ -0,0 +1,17 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Slime;
import org.bukkit.event.Cancellable;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Slime decides to start wandering.
* <p>
* This event does not fire for the entity's actual movement. Only when it
* is choosing to start moving.
*/
public class SlimeWanderEvent extends SlimePathfindEvent implements Cancellable {
public SlimeWanderEvent(@NotNull Slime slime) {
super(slime);
}
}

View file

@ -0,0 +1,115 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Egg;
import org.bukkit.entity.EntityType;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* Called when a thrown egg might hatch.
* <p>
* This event fires for all thrown eggs that may hatch, players, dispensers, etc.
*/
public class ThrownEggHatchEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final Egg egg;
private boolean hatching;
private EntityType hatchType;
private byte numHatches;
public ThrownEggHatchEvent(@NotNull final Egg egg, final boolean hatching, final byte numHatches, @NotNull final EntityType hatchingType) {
this.egg = egg;
this.hatching = hatching;
this.numHatches = numHatches;
this.hatchType = hatchingType;
}
/**
* Gets the egg involved in this event.
*
* @return the egg involved in this event
*/
@NotNull
public Egg getEgg() {
return egg;
}
/**
* Gets whether the egg is hatching or not. Will be what the server
* would've done without interaction.
*
* @return boolean Whether the egg is going to hatch or not
*/
public boolean isHatching() {
return hatching;
}
/**
* Sets whether the egg will hatch or not.
*
* @param hatching true if you want the egg to hatch, false if you want it
* not to
*/
public void setHatching(boolean hatching) {
this.hatching = hatching;
}
/**
* Get the type of the mob being hatched (EntityType.CHICKEN by default)
*
* @return The type of the mob being hatched by the egg
*/
@NotNull
public EntityType getHatchingType() {
return hatchType;
}
/**
* Change the type of mob being hatched by the egg
*
* @param hatchType The type of the mob being hatched by the egg
*/
public void setHatchingType(@NotNull EntityType hatchType) {
if (!hatchType.isSpawnable()) throw new IllegalArgumentException("Can't spawn that entity type from an egg!");
this.hatchType = hatchType;
}
/**
* Get the number of mob hatches from the egg. By default the number will
* be the number the server would've done
* <ul>
* <li>7/8 chance of being 0
* <li>31/256 ~= 1/8 chance to be 1
* <li>1/256 chance to be 4
* </ul>
*
* @return The number of mobs going to be hatched by the egg
*/
public byte getNumHatches() {
return numHatches;
}
/**
* Change the number of mobs coming out of the hatched egg
* <p>
* The boolean hatching will override this number. Ie. If hatching =
* false, this number will not matter
*
* @param numHatches The number of mobs coming out of the egg
*/
public void setNumHatches(byte numHatches) {
this.numHatches = numHatches;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,49 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Turtle;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Turtle decides to go home
*/
public class TurtleGoHomeEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false;
public TurtleGoHomeEvent(@NotNull Turtle turtle) {
super(turtle);
}
/**
* The turtle going home
*
* @return The turtle
*/
@NotNull
public Turtle getEntity() {
return (Turtle) entity;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,87 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.Location;
import org.bukkit.entity.Turtle;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Turtle lays eggs
*/
public class TurtleLayEggEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false;
@NotNull
private final Location location;
private int eggCount;
public TurtleLayEggEvent(@NotNull Turtle turtle, @NotNull Location location, int eggCount) {
super(turtle);
this.location = location;
this.eggCount = eggCount;
}
/**
* The turtle laying the eggs
*
* @return The turtle
*/
@NotNull
public Turtle getEntity() {
return (Turtle) entity;
}
/**
* Get the location where the eggs are being laid
*
* @return Location of eggs
*/
@NotNull
public Location getLocation() {
return location;
}
/**
* Get the number of eggs being laid
*
* @return Number of eggs
*/
public int getEggCount() {
return eggCount;
}
/**
* Set the number of eggs being laid
*
* @param eggCount Number of eggs
*/
public void setEggCount(int eggCount) {
if (eggCount < 1) {
cancelled = true;
return;
}
this.eggCount = Math.min(eggCount, 4);
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,62 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.Location;
import org.bukkit.entity.Turtle;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.jetbrains.annotations.NotNull;
/**
* Fired when a Turtle starts digging to lay eggs
*/
public class TurtleStartDiggingEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false;
@NotNull private final Location location;
public TurtleStartDiggingEvent(@NotNull Turtle turtle, @NotNull Location location) {
super(turtle);
this.location = location;
}
/**
* The turtle digging
*
* @return The turtle
*/
@NotNull
public Turtle getEntity() {
return (Turtle) entity;
}
/**
* Get the location where the turtle is digging
*
* @return Location where digging
*/
@NotNull
public Location getLocation() {
return location;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,70 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.Witch;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Fired when a witch consumes the potion in their hand to buff themselves.
*/
public class WitchConsumePotionEvent extends EntityEvent implements Cancellable {
@Nullable private ItemStack potion;
public WitchConsumePotionEvent(@NotNull Witch witch, @Nullable ItemStack potion) {
super(witch);
this.potion = potion;
}
@NotNull
@Override
public Witch getEntity() {
return (Witch) super.getEntity();
}
/**
* @return the potion the witch will consume and have the effects applied.
*/
@Nullable
public ItemStack getPotion() {
return potion;
}
/**
* Sets the potion to be consumed and applied to the witch.
* @param potion The potion
*/
public void setPotion(@Nullable ItemStack potion) {
this.potion = potion != null ? potion.clone() : null;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
/**
* @return Event was cancelled or potion was null
*/
@Override
public boolean isCancelled() {
return cancelled || potion == null;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,80 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.Material;
import org.bukkit.entity.Witch;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class WitchReadyPotionEvent extends EntityEvent implements Cancellable {
private ItemStack potion;
public WitchReadyPotionEvent(@NotNull Witch witch, @Nullable ItemStack potion) {
super(witch);
this.potion = potion;
}
/**
* Fires the event, returning the desired potion, or air of cancelled
* @param witch the witch whom is readying to use a potion
* @param potion the potion to be used
* @return The ItemStack to be used
*/
@Nullable
public static ItemStack process(@NotNull Witch witch, @Nullable ItemStack potion) {
WitchReadyPotionEvent event = new WitchReadyPotionEvent(witch, potion);
if (!event.callEvent() || event.getPotion() == null) {
return new ItemStack(Material.AIR);
}
return event.getPotion();
}
@NotNull
@Override
public Witch getEntity() {
return (Witch) super.getEntity();
}
/**
* @return the potion the witch is readying to use
*/
@Nullable
public ItemStack getPotion() {
return potion;
}
/**
* Sets the potion the which is going to hold and use
* @param potion The potion
*/
public void setPotion(@Nullable ItemStack potion) {
this.potion = potion != null ? potion.clone() : null;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,81 @@
package com.destroystokyo.paper.event.entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Witch;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Fired when a witch throws a potion at a player
*/
public class WitchThrowPotionEvent extends EntityEvent implements Cancellable {
@NotNull private final LivingEntity target;
@Nullable private ItemStack potion;
public WitchThrowPotionEvent(@NotNull Witch witch, @NotNull LivingEntity target, @Nullable ItemStack potion) {
super(witch);
this.target = target;
this.potion = potion;
}
@NotNull
@Override
public Witch getEntity() {
return (Witch) super.getEntity();
}
/**
* @return The target of the potion
*/
@NotNull
public LivingEntity getTarget() {
return target;
}
/**
* @return The potion the witch will throw at a player
*/
@Nullable
public ItemStack getPotion() {
return potion;
}
/**
* Sets the potion to be thrown at a player
* @param potion The potion
*/
public void setPotion(@Nullable ItemStack potion) {
this.potion = potion != null ? potion.clone() : null;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
private boolean cancelled = false;
/**
* @return Event was cancelled or potion was null
*/
@Override
public boolean isCancelled() {
return cancelled || potion == null;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

View file

@ -0,0 +1,51 @@
package com.destroystokyo.paper.event.executor;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import com.destroystokyo.paper.util.SneakyThrow;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.bukkit.plugin.EventExecutor;
import org.jetbrains.annotations.NotNull;
public class MethodHandleEventExecutor implements EventExecutor {
private final Class<? extends Event> eventClass;
private final MethodHandle handle;
private final Method method;
public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull MethodHandle handle) {
this.eventClass = eventClass;
this.handle = handle;
this.method = null;
}
public MethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
this.eventClass = eventClass;
try {
m.setAccessible(true);
this.handle = MethodHandles.lookup().unreflect(m);
} catch (IllegalAccessException e) {
throw new AssertionError("Unable to set accessible", e);
}
this.method = m;
}
@Override
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
if (!eventClass.isInstance(event)) return;
try {
handle.invoke(listener, event);
} catch (Throwable t) {
SneakyThrow.sneaky(t);
}
}
@Override
@NotNull
public String toString() {
return "MethodHandleEventExecutor['" + this.method + "']";
}
}

View file

@ -0,0 +1,51 @@
package com.destroystokyo.paper.event.executor;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import com.destroystokyo.paper.util.SneakyThrow;
import com.google.common.base.Preconditions;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.bukkit.plugin.EventExecutor;
import org.jetbrains.annotations.NotNull;
public class StaticMethodHandleEventExecutor implements EventExecutor {
private final Class<? extends Event> eventClass;
private final MethodHandle handle;
private final Method method;
public StaticMethodHandleEventExecutor(@NotNull Class<? extends Event> eventClass, @NotNull Method m) {
Preconditions.checkArgument(Modifier.isStatic(m.getModifiers()), "Not a static method: %s", m);
Preconditions.checkArgument(eventClass != null, "eventClass is null");
this.eventClass = eventClass;
try {
m.setAccessible(true);
this.handle = MethodHandles.lookup().unreflect(m);
} catch (IllegalAccessException e) {
throw new AssertionError("Unable to set accessible", e);
}
this.method = m;
}
@Override
public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException {
if (!eventClass.isInstance(event)) return;
try {
handle.invoke(event);
} catch (Throwable throwable) {
SneakyThrow.sneaky(throwable);
}
}
@Override
@NotNull
public String toString() {
return "StaticMethodHandleEventExecutor['" + this.method + "']";
}
}

View file

@ -0,0 +1,54 @@
package com.destroystokyo.paper.event.executor.asm;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;
import org.bukkit.plugin.EventExecutor;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import static org.objectweb.asm.Opcodes.*;
public class ASMEventExecutorGenerator {
private static final String EXECUTE_DESCRIPTOR = "(Lorg/bukkit/event/Listener;Lorg/bukkit/event/Event;)V";
@NotNull
public static byte[] generateEventExecutor(@NotNull Method m, @NotNull String name) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
writer.visit(V1_8, ACC_PUBLIC, name.replace('.', '/'), null, Type.getInternalName(Object.class), new String[] {Type.getInternalName(EventExecutor.class)});
// Generate constructor
GeneratorAdapter methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null), ACC_PUBLIC, "<init>", "()V");
methodGenerator.loadThis();
methodGenerator.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false); // Invoke the super class (Object) constructor
methodGenerator.returnValue();
methodGenerator.endMethod();
// Generate the execute method
methodGenerator = new GeneratorAdapter(writer.visitMethod(ACC_PUBLIC, "execute", EXECUTE_DESCRIPTOR, null, null), ACC_PUBLIC, "execute", EXECUTE_DESCRIPTOR);
methodGenerator.loadArg(0);
methodGenerator.checkCast(Type.getType(m.getDeclaringClass()));
methodGenerator.loadArg(1);
methodGenerator.checkCast(Type.getType(m.getParameterTypes()[0]));
methodGenerator.visitMethodInsn(m.getDeclaringClass().isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL, Type.getInternalName(m.getDeclaringClass()), m.getName(), Type.getMethodDescriptor(m), m.getDeclaringClass().isInterface());
// The only purpose of this switch statement is to generate the correct pop instruction, should the event handler method return something other than void.
// Non-void event handlers will be unsupported in a future release.
switch (Type.getType(m.getReturnType()).getSize()) {
// case 0 is omitted because the only type that has size 0 is void - no pop instruction needed.
case 1 -> methodGenerator.pop(); // handles reference types and most primitives
case 2 -> methodGenerator.pop2(); // handles long and double
}
methodGenerator.returnValue();
methodGenerator.endMethod();
writer.visitEnd();
return writer.toByteArray();
}
public static AtomicInteger NEXT_ID = new AtomicInteger(1);
@NotNull
public static String generateName() {
int id = NEXT_ID.getAndIncrement();
return "com.destroystokyo.paper.event.executor.asm.generated.GeneratedEventExecutor" + id;
}
}

View file

@ -0,0 +1,34 @@
package com.destroystokyo.paper.event.executor.asm;
import org.jetbrains.annotations.NotNull;
public interface ClassDefiner {
/**
* Returns if the defined classes can bypass access checks
*
* @return if classes bypass access checks
*/
public default boolean isBypassAccessChecks() {
return false;
}
/**
* Define a class
*
* @param parentLoader the parent classloader
* @param name the name of the class
* @param data the class data to load
* @return the defined class
* @throws ClassFormatError if the class data is invalid
* @throws NullPointerException if any of the arguments are null
*/
@NotNull
public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data);
@NotNull
public static ClassDefiner getInstance() {
return SafeClassDefiner.INSTANCE;
}
}

View file

@ -0,0 +1,66 @@
package com.destroystokyo.paper.event.executor.asm;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.google.common.base.Preconditions;
import com.google.common.collect.MapMaker;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.Type;
public class SafeClassDefiner implements ClassDefiner {
/* default */ static final SafeClassDefiner INSTANCE = new SafeClassDefiner();
private SafeClassDefiner() {}
private final ConcurrentMap<ClassLoader, GeneratedClassLoader> loaders = new MapMaker().weakKeys().makeMap();
@NotNull
@Override
public Class<?> defineClass(@NotNull ClassLoader parentLoader, @NotNull String name, @NotNull byte[] data) {
GeneratedClassLoader loader = loaders.computeIfAbsent(parentLoader, GeneratedClassLoader::new);
synchronized (loader.getClassLoadingLock(name)) {
Preconditions.checkState(!loader.hasClass(name), "%s already defined", name);
Class<?> c = loader.define(name, data);
assert c.getName().equals(name);
return c;
}
}
private static class GeneratedClassLoader extends ClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
protected GeneratedClassLoader(@NotNull ClassLoader parent) {
super(parent);
}
private Class<?> define(@NotNull String name, byte[] data) {
synchronized (getClassLoadingLock(name)) {
assert !hasClass(name);
Class<?> c = defineClass(name, data, 0, data.length);
resolveClass(c);
return c;
}
}
@Override
@NotNull
public Object getClassLoadingLock(@NotNull String name) {
return super.getClassLoadingLock(name);
}
public boolean hasClass(@NotNull String name) {
synchronized (getClassLoadingLock(name)) {
try {
Class.forName(name);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
}
}

View file

@ -0,0 +1,28 @@
package com.destroystokyo.paper.event.inventory;
import org.bukkit.Warning;
import org.bukkit.inventory.GrindstoneInventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when an item is put in a slot for grinding in a Grindstone
* @deprecated use {@link org.bukkit.event.inventory.PrepareGrindstoneEvent}
*/
@Deprecated
@Warning(false)
public class PrepareGrindstoneEvent extends PrepareResultEvent {
public PrepareGrindstoneEvent(@NotNull InventoryView inventory, @Nullable ItemStack result) {
super(inventory, result);
}
@NotNull
@Override
public GrindstoneInventory getInventory() {
return (GrindstoneInventory) super.getInventory();
}
}

View file

@ -0,0 +1,37 @@
package com.destroystokyo.paper.event.inventory;
import org.bukkit.event.inventory.PrepareInventoryResultEvent;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Called when an item is put in an inventory containing a result slot
*/
public class PrepareResultEvent extends PrepareInventoryResultEvent {
// HandlerList on PrepareInventoryResultEvent to ensure api compat
public PrepareResultEvent(@NotNull InventoryView inventory, @Nullable ItemStack result) {
super(inventory, result);
}
/**
* Get result item, may be null.
*
* @return result item
*/
@Nullable
public ItemStack getResult() {
return super.getResult();
}
/**
* Set result item, may be null.
*
* @param result result item
*/
public void setResult(@Nullable ItemStack result) {
super.setResult(result);
}
}

View file

@ -0,0 +1,74 @@
package com.destroystokyo.paper.event.player;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @deprecated Not used
*/
@Deprecated
public class IllegalPacketEvent extends PlayerEvent {
@Nullable private final String type;
@Nullable private final String ex;
@Nullable private String kickMessage;
private boolean shouldKick = true;
public IllegalPacketEvent(@NotNull Player player, @Nullable String type, @Nullable String kickMessage, @NotNull Exception e) {
super(player);
this.type = type;
this.kickMessage = kickMessage;
this.ex = e.getMessage();
}
public boolean isShouldKick() {
return shouldKick;
}
public void setShouldKick(boolean shouldKick) {
this.shouldKick = shouldKick;
}
@Nullable
public String getKickMessage() {
return kickMessage;
}
public void setKickMessage(@Nullable String kickMessage) {
this.kickMessage = kickMessage;
}
@Nullable
public String getType() {
return type;
}
@Nullable
public String getExceptionMessage() {
return ex;
}
private static final HandlerList handlers = new HandlerList();
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
public static void process(@NotNull Player player, @Nullable String type, @Nullable String kickMessage, @NotNull Exception exception) {
IllegalPacketEvent event = new IllegalPacketEvent(player, type, kickMessage, exception);
event.callEvent();
if (event.shouldKick) {
player.kickPlayer(kickMessage);
}
Bukkit.getLogger().severe(player.getName() + "/" + type + ": " + exception.getMessage());
}
}

View file

@ -0,0 +1,78 @@
package com.destroystokyo.paper.event.player;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called after a player is granted a criteria in an advancement.
* If cancelled the criteria will be revoked.
*/
public class PlayerAdvancementCriterionGrantEvent extends PlayerEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
@NotNull private final Advancement advancement;
@NotNull private final String criterion;
@NotNull private final AdvancementProgress advancementProgress;
private boolean cancel = false;
public PlayerAdvancementCriterionGrantEvent(@NotNull Player who, @NotNull Advancement advancement, @NotNull String criterion) {
super(who);
this.advancement = advancement;
this.criterion = criterion;
this.advancementProgress = who.getAdvancementProgress(advancement);
}
/**
* Get the advancement which has been affected.
*
* @return affected advancement
*/
@NotNull
public Advancement getAdvancement() {
return advancement;
}
/**
* Get the criterion which has been granted.
*
* @return granted criterion
*/
@NotNull
public String getCriterion() {
return criterion;
}
public boolean isCancelled() {
return cancel;
}
/**
* Gets the current AdvancementProgress.
* See {@link PlayerAdvancementCriterionGrantEvent}
*
* @return advancement progress
*/
@NotNull
public AdvancementProgress getAdvancementProgress() {
return advancementProgress;
}
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,139 @@
package com.destroystokyo.paper.event.player;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.bukkit.Material.*;
/**
* Called when the player themselves change their armor items
* <p>
* Not currently called for environmental factors though it <strong>MAY BE IN THE FUTURE</strong>
*/
public class PlayerArmorChangeEvent extends PlayerEvent {
private static final HandlerList HANDLERS = new HandlerList();
@NotNull private final SlotType slotType;
@NotNull private final ItemStack oldItem;
@NotNull private final ItemStack newItem;
@ApiStatus.Internal
public PlayerArmorChangeEvent(@NotNull Player player, @NotNull SlotType slotType, @NotNull ItemStack oldItem, @NotNull ItemStack newItem) {
super(player);
this.slotType = slotType;
this.oldItem = oldItem;
this.newItem = newItem;
}
/**
* Gets the type of slot being altered.
*
* @return type of slot being altered
*/
@NotNull
public SlotType getSlotType() {
return this.slotType;
}
/**
* Gets the existing item that's being replaced
*
* @return old item
*/
@NotNull
public ItemStack getOldItem() {
return this.oldItem;
}
/**
* Gets the new item that's replacing the old
*
* @return new item
*/
@NotNull
public ItemStack getNewItem() {
return this.newItem;
}
@Override
public String toString() {
return "ArmorChangeEvent{" + "player=" + player + ", slotType=" + slotType + ", oldItem=" + oldItem + ", newItem=" + newItem + '}';
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLERS;
}
public enum SlotType {
HEAD(NETHERITE_HELMET, DIAMOND_HELMET, GOLDEN_HELMET, IRON_HELMET, CHAINMAIL_HELMET, LEATHER_HELMET, CARVED_PUMPKIN, PLAYER_HEAD, SKELETON_SKULL, ZOMBIE_HEAD, CREEPER_HEAD, WITHER_SKELETON_SKULL, TURTLE_HELMET, DRAGON_HEAD, PIGLIN_HEAD),
CHEST(NETHERITE_CHESTPLATE, DIAMOND_CHESTPLATE, GOLDEN_CHESTPLATE, IRON_CHESTPLATE, CHAINMAIL_CHESTPLATE, LEATHER_CHESTPLATE, ELYTRA),
LEGS(NETHERITE_LEGGINGS, DIAMOND_LEGGINGS, GOLDEN_LEGGINGS, IRON_LEGGINGS, CHAINMAIL_LEGGINGS, LEATHER_LEGGINGS),
FEET(NETHERITE_BOOTS, DIAMOND_BOOTS, GOLDEN_BOOTS, IRON_BOOTS, CHAINMAIL_BOOTS, LEATHER_BOOTS);
private final Set<Material> mutableTypes = new HashSet<>();
private Set<Material> immutableTypes;
SlotType(Material... types) {
this.mutableTypes.addAll(Arrays.asList(types));
}
/**
* Gets an immutable set of all allowed material types that can be placed in an
* armor slot.
*
* @return immutable set of material types
*/
@NotNull
public Set<Material> getTypes() {
if (immutableTypes == null) {
immutableTypes = Collections.unmodifiableSet(mutableTypes);
}
return immutableTypes;
}
/**
* Gets the type of slot via the specified material
*
* @param material material to get slot by
* @return slot type the material will go in, or null if it won't
*/
@Nullable
public static SlotType getByMaterial(@NotNull Material material) {
for (SlotType slotType : values()) {
if (slotType.getTypes().contains(material)) {
return slotType;
}
}
return null;
}
/**
* Gets whether this material can be equipped to a slot
*
* @param material material to check
* @return whether this material can be equipped
*/
public static boolean isEquipable(@NotNull Material material) {
return getByMaterial(material) != null;
}
}
}

View file

@ -0,0 +1,76 @@
package com.destroystokyo.paper.event.player;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when processing a player's attack on an entity when the player's attack strength cooldown is reset
*/
public class PlayerAttackEntityCooldownResetEvent extends PlayerEvent implements Cancellable {
private final float cooledAttackStrength;
private boolean cancel = false;
private static final HandlerList handlers = new HandlerList();
@NotNull private final Entity attackedEntity;
public PlayerAttackEntityCooldownResetEvent(@NotNull Player who, @NotNull Entity attackedEntity, float cooledAttackStrength) {
super(who);
this.attackedEntity = attackedEntity;
this.cooledAttackStrength = cooledAttackStrength;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public static @NotNull HandlerList getHandlerList() {
return handlers;
}
/**
* Gets the cancellation state of this event. A cancelled event will not
* be executed in the server, but will still pass to other plugins
* <p>
* If an attack cooldown event is cancelled, the players attack strength will remain at the same value instead of being reset.
*
* @return true if this event is cancelled
*/
@Override
public boolean isCancelled() {
return cancel;
}
/**
* Cancelling this event will prevent the target player from having their cooldown reset from attacking this entity
*
* @param cancel true if you wish to cancel this event
*/
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
/**
* Get the value of the players cooldown attack strength when they initiated the attack
*
* @return returns the original player cooldown value
*/
public float getCooledAttackStrength() {
return cooledAttackStrength;
}
/**
* Returns the entity attacked by the player
*
* @return the entity attacked by the player
*/
@NotNull
public Entity getAttackedEntity() {
return attackedEntity;
}
}

View file

@ -0,0 +1,134 @@
package com.destroystokyo.paper.event.player;
import com.destroystokyo.paper.ClientOption;
import com.destroystokyo.paper.ClientOption.ChatVisibility;
import com.destroystokyo.paper.SkinParts;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.bukkit.inventory.MainHand;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* Called when the player changes their client settings
*/
public class PlayerClientOptionsChangeEvent extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
private final String locale;
private final int viewDistance;
private final ChatVisibility chatVisibility;
private final boolean chatColors;
private final SkinParts skinparts;
private final MainHand mainHand;
private final boolean allowsServerListings;
private final boolean textFilteringEnabled;
@Deprecated
public PlayerClientOptionsChangeEvent(@NotNull Player player, @NotNull String locale, int viewDistance, @NotNull ChatVisibility chatVisibility, boolean chatColors, @NotNull SkinParts skinParts, @NotNull MainHand mainHand) {
super(player);
this.locale = locale;
this.viewDistance = viewDistance;
this.chatVisibility = chatVisibility;
this.chatColors = chatColors;
this.skinparts = skinParts;
this.mainHand = mainHand;
this.allowsServerListings = false;
this.textFilteringEnabled = false;
}
public PlayerClientOptionsChangeEvent(@NotNull Player player, @NotNull Map<ClientOption<?>, ?> options) {
super(player);
this.locale = (String) options.get(ClientOption.LOCALE);
this.viewDistance = (int) options.get(ClientOption.VIEW_DISTANCE);
this.chatVisibility = (ChatVisibility) options.get(ClientOption.CHAT_VISIBILITY);
this.chatColors = (boolean) options.get(ClientOption.CHAT_COLORS_ENABLED);
this.skinparts = (SkinParts) options.get(ClientOption.SKIN_PARTS);
this.mainHand = (MainHand) options.get(ClientOption.MAIN_HAND);
this.allowsServerListings = (boolean) options.get(ClientOption.ALLOW_SERVER_LISTINGS);
this.textFilteringEnabled = (boolean) options.get(ClientOption.TEXT_FILTERING_ENABLED);
}
@NotNull
public String getLocale() {
return locale;
}
public boolean hasLocaleChanged() {
return !locale.equals(player.getClientOption(ClientOption.LOCALE));
}
public int getViewDistance() {
return viewDistance;
}
public boolean hasViewDistanceChanged() {
return viewDistance != player.getClientOption(ClientOption.VIEW_DISTANCE);
}
@NotNull
public ChatVisibility getChatVisibility() {
return chatVisibility;
}
public boolean hasChatVisibilityChanged() {
return chatVisibility != player.getClientOption(ClientOption.CHAT_VISIBILITY);
}
public boolean hasChatColorsEnabled() {
return chatColors;
}
public boolean hasChatColorsEnabledChanged() {
return chatColors != player.getClientOption(ClientOption.CHAT_COLORS_ENABLED);
}
@NotNull
public SkinParts getSkinParts() {
return skinparts;
}
public boolean hasSkinPartsChanged() {
return skinparts.getRaw() != player.getClientOption(ClientOption.SKIN_PARTS).getRaw();
}
@NotNull
public MainHand getMainHand() {
return mainHand;
}
public boolean hasMainHandChanged() {
return mainHand != player.getClientOption(ClientOption.MAIN_HAND);
}
public boolean allowsServerListings() {
return allowsServerListings;
}
public boolean hasAllowServerListingsChanged() {
return allowsServerListings != player.getClientOption(ClientOption.ALLOW_SERVER_LISTINGS);
}
public boolean hasTextFilteringEnabled() {
return textFilteringEnabled;
}
public boolean hasTextFilteringChanged() {
return textFilteringEnabled != player.getClientOption(ClientOption.TEXT_FILTERING_ENABLED);
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,95 @@
package com.destroystokyo.paper.event.player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.net.InetAddress;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
/**
* <p>
* This event is invoked when a player has disconnected. It is guaranteed that,
* if the server is in online-mode, that the provided uuid and username have been
* validated.
* </p>
*
* <p>
* The event is invoked for players who have not yet logged into the world, whereas
* {@link org.bukkit.event.player.PlayerQuitEvent} is only invoked on players who have logged into the world.
* </p>
*
* <p>
* The event is invoked for players who have already logged into the world,
* although whether or not the player exists in the world at the time of
* firing is undefined. (That is, whether the plugin can retrieve a Player object
* using the event parameters is undefined). However, it is guaranteed that this
* event is invoked AFTER {@link org.bukkit.event.player.PlayerQuitEvent}, if the player has already logged into the world.
* </p>
*
* <p>
* This event is guaranteed to never fire unless {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} has
* been fired beforehand, and this event may not be called in parallel with
* {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} for the same connection.
* </p>
*
* <p>
* Cancelling the {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} guarantees the corresponding
* {@code PlayerConnectionCloseEvent} is never called.
* </p>
*
* <p>
* The event may be invoked asynchronously or synchronously. Plugins should check
* {@link Event#isAsynchronous()} and handle accordingly.
* </p>
*/
public class PlayerConnectionCloseEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
@NotNull private final UUID playerUniqueId;
@NotNull private final String playerName;
@NotNull private final InetAddress ipAddress;
public PlayerConnectionCloseEvent(@NotNull final UUID playerUniqueId, @NotNull final String playerName, @NotNull final InetAddress ipAddress, final boolean async) {
super(async);
this.playerUniqueId = playerUniqueId;
this.playerName = playerName;
this.ipAddress = ipAddress;
}
/**
* Returns the {@code UUID} of the player disconnecting.
*/
@NotNull
public UUID getPlayerUniqueId() {
return this.playerUniqueId;
}
/**
* Returns the name of the player disconnecting.
*/
@NotNull
public String getPlayerName() {
return this.playerName;
}
/**
* Returns the player's IP address.
*/
@NotNull
public InetAddress getIpAddress() {
return this.ipAddress;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
@NotNull
public static HandlerList getHandlerList() {
return HANDLERS;
}
}

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