Compare commits
28 commits
main
...
1.20.1_maz
Author | SHA1 | Date | |
---|---|---|---|
4193119202 | |||
ee775869ba | |||
2a5d1e2cd7 | |||
0f01475ec3 | |||
f0fa9fbcbd | |||
a4e3ef44ed | |||
c82c15f48b | |||
5214d31d21 | |||
d9113f357c | |||
d68fa58bf9 | |||
0cfede6187 | |||
b47d24c8b2 | |||
3c58a662ca | |||
dc99713d30 | |||
86d1e7a26a | |||
fc430f1e11 | |||
c5472aef68 | |||
112e0d39d1 | |||
ee8f7386ee | |||
aec2a22a5a | |||
da7bc47e29 | |||
a488e0671f | |||
6b90951ab2 | |||
f9b221f5e7 | |||
e5ef571346 | |||
9c4d5ff6db | |||
8c9fa42619 | |||
c5316a2763 |
47 changed files with 6859 additions and 682 deletions
65
CREDITS.txt
65
CREDITS.txt
|
@ -1,65 +0,0 @@
|
||||||
Minecraft Forge: Credits/Thank You
|
|
||||||
|
|
||||||
Forge is a set of tools and modifications to the Minecraft base game code to assist
|
|
||||||
mod developers in creating new and exciting content. It has been in development for
|
|
||||||
several years now, but I would like to take this time thank a few people who have
|
|
||||||
helped it along it's way.
|
|
||||||
|
|
||||||
First, the people who originally created the Forge projects way back in Minecraft
|
|
||||||
alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance
|
|
||||||
of me taking over the project, who knows what Minecraft modding would be today.
|
|
||||||
|
|
||||||
Secondly, someone who has worked with me, and developed some of the core features
|
|
||||||
that allow modding to be as functional, and as simple as it is, cpw. For developing
|
|
||||||
FML, which stabelized the client and server modding ecosystem. As well as the base
|
|
||||||
loading system that allows us to modify Minecraft's code as elegently as possible.
|
|
||||||
|
|
||||||
Mezz, who has stepped up as the issue and pull request manager. Helping to keep me
|
|
||||||
sane as well as guiding the community into creating better additions to Forge.
|
|
||||||
|
|
||||||
Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which
|
|
||||||
I am a part}. For creating some of the core tools needed to make Minecraft modding
|
|
||||||
both possible, and as stable as can be.
|
|
||||||
On that note, here is some specific information of the MCP data we use:
|
|
||||||
* Minecraft Coder Pack (MCP) *
|
|
||||||
Forge Mod Loader and Minecraft Forge have permission to distribute and automatically
|
|
||||||
download components of MCP and distribute MCP data files. This permission is not
|
|
||||||
transitive and others wishing to redistribute the Minecraft Forge source independently
|
|
||||||
should seek permission of MCP or remove the MCP data files and request their users
|
|
||||||
to download MCP separately.
|
|
||||||
|
|
||||||
And lastly, the countless community members who have spent time submitting bug reports,
|
|
||||||
pull requests, and just helping out the community in general. Thank you.
|
|
||||||
|
|
||||||
--LexManos
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
This is Forge Mod Loader.
|
|
||||||
|
|
||||||
You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml
|
|
||||||
|
|
||||||
This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
|
|
||||||
and minecraft clients.
|
|
||||||
|
|
||||||
The code is authored by cpw.
|
|
||||||
|
|
||||||
It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
|
|
||||||
http://www.minecraftforum.net/topic/75440-
|
|
||||||
This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
|
|
||||||
|
|
||||||
It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
|
|
||||||
http://www.minecraftforge.net/
|
|
||||||
|
|
||||||
Additionally, it contains an implementation of topological sort based on that
|
|
||||||
published at http://keithschwarz.com/interesting/code/?dir=topological-sort
|
|
||||||
|
|
||||||
It also contains code from the Maven project for performing versioned dependency
|
|
||||||
resolution. http://maven.apache.org/
|
|
||||||
|
|
||||||
It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
|
|
||||||
with credit to it's authors.
|
|
||||||
|
|
||||||
Forge Mod Loader downloads components from the Minecraft Coder Pack
|
|
||||||
(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
|
|
||||||
|
|
520
LICENSE.txt
520
LICENSE.txt
|
@ -1,520 +0,0 @@
|
||||||
Unless noted below, Minecraft Forge, Forge Mod Loader, and all
|
|
||||||
parts herein are licensed under the terms of the LGPL 2.1 found
|
|
||||||
here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
|
|
||||||
copied below.
|
|
||||||
|
|
||||||
Homepage: http://minecraftforge.net/
|
|
||||||
https://github.com/MinecraftForge/MinecraftForge
|
|
||||||
|
|
||||||
|
|
||||||
A note on authorship:
|
|
||||||
All source artifacts are property of their original author, with
|
|
||||||
the exclusion of the contents of the patches directory and others
|
|
||||||
copied from it from time to time. Authorship of the contents of
|
|
||||||
the patches directory is retained by the Minecraft Forge project.
|
|
||||||
This is because the patches are partially machine generated
|
|
||||||
artifacts, and are changed heavily due to the way forge works.
|
|
||||||
Individual attribution within them is impossible.
|
|
||||||
|
|
||||||
Consent:
|
|
||||||
All contributions to Forge must consent to the release of any
|
|
||||||
patch content to the Forge project.
|
|
||||||
|
|
||||||
A note on infectivity:
|
|
||||||
The LGPL is chosen specifically so that projects may depend on Forge
|
|
||||||
features without being infected with its license. That is the
|
|
||||||
purpose of the LGPL. Mods and others using this code via ordinary
|
|
||||||
Java mechanics for referencing libraries are specifically not bound
|
|
||||||
by Forge's license for the Mod code.
|
|
||||||
|
|
||||||
|
|
||||||
=== MCP Data ===
|
|
||||||
This software includes data from the Minecraft Coder Pack (MCP), with kind permission
|
|
||||||
from them. The license to MCP data is not transitive - distribution of this data by
|
|
||||||
third parties requires independent licensing from the MCP team. This data is not
|
|
||||||
redistributable without permission from the MCP team.
|
|
||||||
|
|
||||||
=== Sharing ===
|
|
||||||
I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of
|
|
||||||
the minecraft modding community. All contributions to these parts should be licensed under the same additional grant.
|
|
||||||
|
|
||||||
-- Runtime patcher --
|
|
||||||
License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher
|
|
||||||
and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
|
|
||||||
|
|
||||||
-- ASM transformers --
|
|
||||||
License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories)
|
|
||||||
under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
This software includes portions from the Apache Maven project at
|
|
||||||
http://maven.apache.org/ specifically the ComparableVersion.java code. It is
|
|
||||||
included based on guidelines at
|
|
||||||
http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
|
|
||||||
with notices intact. The only change is a non-functional change of package name.
|
|
||||||
|
|
||||||
This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating
|
|
||||||
binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/
|
|
||||||
authored by genman, heikok, pivot.
|
|
||||||
The only changes are to replace some Trove collection types with standard Java collections, and repackaged.
|
|
||||||
=========================================================================
|
|
||||||
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 2.1, February 1999
|
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
[This is the first released version of the Lesser GPL. It also counts
|
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
|
||||||
the version number 2.1.]
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
Licenses are intended to guarantee your freedom to share and change
|
|
||||||
free software--to make sure the software is free for all its users.
|
|
||||||
|
|
||||||
This license, the Lesser General Public License, applies to some
|
|
||||||
specially designated software packages--typically libraries--of the
|
|
||||||
Free Software Foundation and other authors who decide to use it. You
|
|
||||||
can use it too, but we suggest you first think carefully about whether
|
|
||||||
this license or the ordinary General Public License is the better
|
|
||||||
strategy to use in any particular case, based on the explanations below.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom of use,
|
|
||||||
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 this service if you wish); that you receive source code or can get
|
|
||||||
it if you want it; that you can change the software and use pieces of
|
|
||||||
it in new free programs; and that you are informed that you can do
|
|
||||||
these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
distributors to deny you these rights or to ask you to surrender these
|
|
||||||
rights. These restrictions translate to certain responsibilities for
|
|
||||||
you if you distribute copies of the library or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
|
||||||
you. You must make sure that they, too, receive or can get the source
|
|
||||||
code. If you link other code with the library, you must provide
|
|
||||||
complete object files to the recipients, so that they can relink them
|
|
||||||
with the library after making changes to the library and recompiling
|
|
||||||
it. And you must show them these terms so they know their rights.
|
|
||||||
|
|
||||||
We protect your rights with a two-step method: (1) we copyright the
|
|
||||||
library, and (2) we offer you this license, which gives you legal
|
|
||||||
permission to copy, distribute and/or modify the library.
|
|
||||||
|
|
||||||
To protect each distributor, we want to make it very clear that
|
|
||||||
there is no warranty for the free library. Also, if the library is
|
|
||||||
modified by someone else and passed on, the recipients should know
|
|
||||||
that what they have is not the original version, so that the original
|
|
||||||
author's reputation will not be affected by problems that might be
|
|
||||||
introduced by others.
|
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
|
||||||
any free program. We wish to make sure that a company cannot
|
|
||||||
effectively restrict the users of a free program by obtaining a
|
|
||||||
restrictive license from a patent holder. Therefore, we insist that
|
|
||||||
any patent license obtained for a version of the library must be
|
|
||||||
consistent with the full freedom of use specified in this license.
|
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the
|
|
||||||
ordinary GNU General Public License. This license, the GNU Lesser
|
|
||||||
General Public License, applies to certain designated libraries, and
|
|
||||||
is quite different from the ordinary General Public License. We use
|
|
||||||
this license for certain libraries in order to permit linking those
|
|
||||||
libraries into non-free programs.
|
|
||||||
|
|
||||||
When a program is linked with a library, whether statically or using
|
|
||||||
a shared library, the combination of the two is legally speaking a
|
|
||||||
combined work, a derivative of the original library. The ordinary
|
|
||||||
General Public License therefore permits such linking only if the
|
|
||||||
entire combination fits its criteria of freedom. The Lesser General
|
|
||||||
Public License permits more lax criteria for linking other code with
|
|
||||||
the library.
|
|
||||||
|
|
||||||
We call this license the "Lesser" General Public License because it
|
|
||||||
does Less to protect the user's freedom than the ordinary General
|
|
||||||
Public License. It also provides other free software developers Less
|
|
||||||
of an advantage over competing non-free programs. These disadvantages
|
|
||||||
are the reason we use the ordinary General Public License for many
|
|
||||||
libraries. However, the Lesser license provides advantages in certain
|
|
||||||
special circumstances.
|
|
||||||
|
|
||||||
For example, on rare occasions, there may be a special need to
|
|
||||||
encourage the widest possible use of a certain library, so that it becomes
|
|
||||||
a de-facto standard. To achieve this, non-free programs must be
|
|
||||||
allowed to use the library. A more frequent case is that a free
|
|
||||||
library does the same job as widely used non-free libraries. In this
|
|
||||||
case, there is little to gain by limiting the free library to free
|
|
||||||
software only, so we use the Lesser General Public License.
|
|
||||||
|
|
||||||
In other cases, permission to use a particular library in non-free
|
|
||||||
programs enables a greater number of people to use a large body of
|
|
||||||
free software. For example, permission to use the GNU C Library in
|
|
||||||
non-free programs enables many more people to use the whole GNU
|
|
||||||
operating system, as well as its variant, the GNU/Linux operating
|
|
||||||
system.
|
|
||||||
|
|
||||||
Although the Lesser General Public License is Less protective of the
|
|
||||||
users' freedom, it does ensure that the user of a program that is
|
|
||||||
linked with the Library has the freedom and the wherewithal to run
|
|
||||||
that program using a modified version of the Library.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow. Pay close attention to the difference between a
|
|
||||||
"work based on the library" and a "work that uses the library". The
|
|
||||||
former contains code derived from the library, whereas the latter must
|
|
||||||
be combined with the library in order to run.
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
|
||||||
program which contains a notice placed by the copyright holder or
|
|
||||||
other authorized party saying it may be distributed under the terms of
|
|
||||||
this Lesser General Public License (also called "this License").
|
|
||||||
Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
|
||||||
prepared so as to be conveniently linked with application programs
|
|
||||||
(which use some of those functions and data) to form executables.
|
|
||||||
|
|
||||||
The "Library", below, refers to any such software library or work
|
|
||||||
which has been distributed under these terms. A "work based on the
|
|
||||||
Library" means either the Library or any derivative work under
|
|
||||||
copyright law: that is to say, a work containing the Library or a
|
|
||||||
portion of it, either verbatim or with modifications and/or translated
|
|
||||||
straightforwardly into another language. (Hereinafter, translation is
|
|
||||||
included without limitation in the term "modification".)
|
|
||||||
|
|
||||||
"Source code" for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For a library, complete source code means
|
|
||||||
all the source code for all modules it contains, plus any associated
|
|
||||||
interface definition files, plus the scripts used to control compilation
|
|
||||||
and installation of the library.
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running a program using the Library is not restricted, and output from
|
|
||||||
such a program is covered only if its contents constitute a work based
|
|
||||||
on the Library (independent of the use of the Library in a tool for
|
|
||||||
writing it). Whether that is true depends on what the Library does
|
|
||||||
and what the program that uses the Library does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
|
||||||
complete source code as you receive it, in any medium, provided that
|
|
||||||
you conspicuously and appropriately publish on each copy an
|
|
||||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
|
||||||
all the notices that refer to this License and to the absence of any
|
|
||||||
warranty; and distribute a copy of this License along with the
|
|
||||||
Library.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
|
||||||
and you may at your option offer warranty protection in exchange for a
|
|
||||||
fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
|
||||||
of it, thus forming a work based on the Library, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The modified work must itself be a software library.
|
|
||||||
|
|
||||||
b) You must cause the files modified to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
c) You must cause the whole of the work to be licensed at no
|
|
||||||
charge to all third parties under the terms of this License.
|
|
||||||
|
|
||||||
d) If a facility in the modified Library refers to a function or a
|
|
||||||
table of data to be supplied by an application program that uses
|
|
||||||
the facility, other than as an argument passed when the facility
|
|
||||||
is invoked, then you must make a good faith effort to ensure that,
|
|
||||||
in the event an application does not supply such function or
|
|
||||||
table, the facility still operates, and performs whatever part of
|
|
||||||
its purpose remains meaningful.
|
|
||||||
|
|
||||||
(For example, a function in a library to compute square roots has
|
|
||||||
a purpose that is entirely well-defined independent of the
|
|
||||||
application. Therefore, Subsection 2d requires that any
|
|
||||||
application-supplied function or table used by this function must
|
|
||||||
be optional: if the application does not supply it, the square
|
|
||||||
root function must still compute square roots.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Library,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Library, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote
|
|
||||||
it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Library.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Library
|
|
||||||
with the Library (or with a work based on the Library) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
|
||||||
License instead of this License to a given copy of the Library. To do
|
|
||||||
this, you must alter all the notices that refer to this License, so
|
|
||||||
that they refer to the ordinary GNU General Public License, version 2,
|
|
||||||
instead of to this License. (If a newer version than version 2 of the
|
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
|
||||||
that version instead if you wish.) Do not make any other change in
|
|
||||||
these notices.
|
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
|
||||||
subsequent copies and derivative works made from that copy.
|
|
||||||
|
|
||||||
This option is useful when you wish to copy part of the code of
|
|
||||||
the Library into a program that is not a library.
|
|
||||||
|
|
||||||
4. You may copy and distribute the Library (or a portion or
|
|
||||||
derivative of it, under Section 2) in object code or executable form
|
|
||||||
under the terms of Sections 1 and 2 above provided that you accompany
|
|
||||||
it with the complete corresponding machine-readable source code, which
|
|
||||||
must be distributed under the terms of Sections 1 and 2 above on a
|
|
||||||
medium customarily used for software interchange.
|
|
||||||
|
|
||||||
If distribution of object code is made by offering access to copy
|
|
||||||
from a designated place, then offering equivalent access to copy the
|
|
||||||
source code from the same place satisfies the requirement to
|
|
||||||
distribute the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
5. A program that contains no derivative of any portion of the
|
|
||||||
Library, but is designed to work with the Library by being compiled or
|
|
||||||
linked with it, is called a "work that uses the Library". Such a
|
|
||||||
work, in isolation, is not a derivative work of the Library, and
|
|
||||||
therefore falls outside the scope of this License.
|
|
||||||
|
|
||||||
However, linking a "work that uses the Library" with the Library
|
|
||||||
creates an executable that is a derivative of the Library (because it
|
|
||||||
contains portions of the Library), rather than a "work that uses the
|
|
||||||
library". The executable is therefore covered by this License.
|
|
||||||
Section 6 states terms for distribution of such executables.
|
|
||||||
|
|
||||||
When a "work that uses the Library" uses material from a header file
|
|
||||||
that is part of the Library, the object code for the work may be a
|
|
||||||
derivative work of the Library even though the source code is not.
|
|
||||||
Whether this is true is especially significant if the work can be
|
|
||||||
linked without the Library, or if the work is itself a library. The
|
|
||||||
threshold for this to be true is not precisely defined by law.
|
|
||||||
|
|
||||||
If such an object file uses only numerical parameters, data
|
|
||||||
structure layouts and accessors, and small macros and small inline
|
|
||||||
functions (ten lines or less in length), then the use of the object
|
|
||||||
file is unrestricted, regardless of whether it is legally a derivative
|
|
||||||
work. (Executables containing this object code plus portions of the
|
|
||||||
Library will still fall under Section 6.)
|
|
||||||
|
|
||||||
Otherwise, if the work is a derivative of the Library, you may
|
|
||||||
distribute the object code for the work under the terms of Section 6.
|
|
||||||
Any executables containing that work also fall under Section 6,
|
|
||||||
whether or not they are linked directly with the Library itself.
|
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
|
||||||
link a "work that uses the Library" with the Library to produce a
|
|
||||||
work containing portions of the Library, and distribute that work
|
|
||||||
under terms of your choice, provided that the terms permit
|
|
||||||
modification of the work for the customer's own use and reverse
|
|
||||||
engineering for debugging such modifications.
|
|
||||||
|
|
||||||
You must give prominent notice with each copy of the work that the
|
|
||||||
Library is used in it and that the Library and its use are covered by
|
|
||||||
this License. You must supply a copy of this License. If the work
|
|
||||||
during execution displays copyright notices, you must include the
|
|
||||||
copyright notice for the Library among them, as well as a reference
|
|
||||||
directing the user to the copy of this License. Also, you must do one
|
|
||||||
of these things:
|
|
||||||
|
|
||||||
a) Accompany the work with the complete corresponding
|
|
||||||
machine-readable source code for the Library including whatever
|
|
||||||
changes were used in the work (which must be distributed under
|
|
||||||
Sections 1 and 2 above); and, if the work is an executable linked
|
|
||||||
with the Library, with the complete machine-readable "work that
|
|
||||||
uses the Library", as object code and/or source code, so that the
|
|
||||||
user can modify the Library and then relink to produce a modified
|
|
||||||
executable containing the modified Library. (It is understood
|
|
||||||
that the user who changes the contents of definitions files in the
|
|
||||||
Library will not necessarily be able to recompile the application
|
|
||||||
to use the modified definitions.)
|
|
||||||
|
|
||||||
b) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (1) uses at run time a
|
|
||||||
copy of the library already present on the user's computer system,
|
|
||||||
rather than copying library functions into the executable, and (2)
|
|
||||||
will operate properly with a modified version of the library, if
|
|
||||||
the user installs one, as long as the modified version is
|
|
||||||
interface-compatible with the version that the work was made with.
|
|
||||||
|
|
||||||
c) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
|
||||||
specified in Subsection 6a, above, for a charge no more
|
|
||||||
than the cost of performing this distribution.
|
|
||||||
|
|
||||||
d) If distribution of the work is made by offering access to copy
|
|
||||||
from a designated place, offer equivalent access to copy the above
|
|
||||||
specified materials from the same place.
|
|
||||||
|
|
||||||
e) Verify that the user has already received a copy of these
|
|
||||||
materials or that you have already sent this user a copy.
|
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
|
||||||
Library" must include any data and utility programs needed for
|
|
||||||
reproducing the executable from it. However, as a special exception,
|
|
||||||
the materials to be distributed need not include anything that is
|
|
||||||
normally distributed (in either source or binary form) with the major
|
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
|
||||||
which the executable runs, unless that component itself accompanies
|
|
||||||
the executable.
|
|
||||||
|
|
||||||
It may happen that this requirement contradicts the license
|
|
||||||
restrictions of other proprietary libraries that do not normally
|
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
|
||||||
use both them and the Library together in an executable that you
|
|
||||||
distribute.
|
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
|
||||||
Library side-by-side in a single library together with other library
|
|
||||||
facilities not covered by this License, and distribute such a combined
|
|
||||||
library, provided that the separate distribution of the work based on
|
|
||||||
the Library and of the other library facilities is otherwise
|
|
||||||
permitted, and provided that you do these two things:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work
|
|
||||||
based on the Library, uncombined with any other library
|
|
||||||
facilities. This must be distributed under the terms of the
|
|
||||||
Sections above.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library of the fact
|
|
||||||
that part of it is a work based on the Library, and explaining
|
|
||||||
where to find the accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
8. You may not copy, modify, sublicense, link with, or distribute
|
|
||||||
the Library except as expressly provided under this License. Any
|
|
||||||
attempt otherwise to copy, modify, sublicense, link with, or
|
|
||||||
distribute the Library is void, and will automatically terminate your
|
|
||||||
rights under this License. However, parties who have received copies,
|
|
||||||
or rights, from you under this License will not have their licenses
|
|
||||||
terminated so long as such parties remain in full compliance.
|
|
||||||
|
|
||||||
9. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Library or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Library (or any work based on the
|
|
||||||
Library), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Library or works based on it.
|
|
||||||
|
|
||||||
10. Each time you redistribute the Library (or any work based on the
|
|
||||||
Library), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute, link with or modify the Library
|
|
||||||
subject to these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties with
|
|
||||||
this License.
|
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
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
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Library at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Library by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Library.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under any
|
|
||||||
particular circumstance, the balance of the section is intended to apply,
|
|
||||||
and the section as a whole is intended to apply in other circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
12. If the distribution and/or use of the Library is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Library under this License may add
|
|
||||||
an explicit geographical distribution limitation excluding those countries,
|
|
||||||
so that distribution is permitted only in or among countries not thus
|
|
||||||
excluded. In such case, this License incorporates the limitation as if
|
|
||||||
written in the body of this License.
|
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
|
||||||
versions of the Lesser General Public License from time to time.
|
|
||||||
Such new versions will be similar in spirit to the present version,
|
|
||||||
but may differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Library
|
|
||||||
specifies a version number of this License which applies to it and
|
|
||||||
"any later version", you have the option of following the terms and
|
|
||||||
conditions either of that version or of any later version published by
|
|
||||||
the Free Software Foundation. If the Library does not specify a
|
|
||||||
license version number, you may choose any version ever published by
|
|
||||||
the Free Software Foundation.
|
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
|
||||||
programs whose distribution conditions are incompatible with these,
|
|
||||||
write to the author to ask for permission. For software which is
|
|
||||||
copyrighted by the Free Software Foundation, write to the Free
|
|
||||||
Software Foundation; we sometimes make exceptions for this. Our
|
|
||||||
decision will be guided by the two goals of preserving the free status
|
|
||||||
of all derivatives of our free software and of promoting the sharing
|
|
||||||
and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
||||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
||||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
|
||||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
||||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
||||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
||||||
AND/OR REDISTRIBUTE THE LIBRARY 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
|
|
||||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
52
build.gradle
52
build.gradle
|
@ -2,9 +2,9 @@ plugins {
|
||||||
id 'eclipse'
|
id 'eclipse'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
id 'java-library'
|
id 'net.neoforged.gradle' version '[6.0.18,6.2)'
|
||||||
id 'net.minecraftforge.gradle' version '[6.0,6.2)'
|
id 'org.spongepowered.mixin' version '0.7.+'
|
||||||
id 'org.parchmentmc.librarian.forgegradle' version '1.+'
|
//id 'org.parchmentmc.librarian.forgegradle' version '1.+'
|
||||||
}
|
}
|
||||||
|
|
||||||
version = mod_version
|
version = mod_version
|
||||||
|
@ -23,6 +23,9 @@ java {
|
||||||
configurations {
|
configurations {
|
||||||
provided
|
provided
|
||||||
compile.extendsFrom(provided)
|
compile.extendsFrom(provided)
|
||||||
|
implementation.extendsFrom(provided)
|
||||||
|
runtime.extendsFrom(provided)
|
||||||
|
minecraftLibrary.extendsFrom(provided)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
|
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
|
||||||
|
@ -58,7 +61,7 @@ minecraft {
|
||||||
// When true, this property will add the folder name of all declared run configurations to generated IDE run configurations.
|
// When true, this property will add the folder name of all declared run configurations to generated IDE run configurations.
|
||||||
// The folder name can be set on a run configuration using the "folderName" property.
|
// The folder name can be set on a run configuration using the "folderName" property.
|
||||||
// By default, the folder name of a run configuration is the name of the Gradle project containing it.
|
// By default, the folder name of a run configuration is the name of the Gradle project containing it.
|
||||||
// generateRunFolders = true
|
generateRunFolders = true
|
||||||
|
|
||||||
// This property enables access transformers for use in development.
|
// This property enables access transformers for use in development.
|
||||||
// They will be applied to the Minecraft artifact.
|
// They will be applied to the Minecraft artifact.
|
||||||
|
@ -73,7 +76,7 @@ minecraft {
|
||||||
runs {
|
runs {
|
||||||
// applies to all the run configs below
|
// applies to all the run configs below
|
||||||
configureEach {
|
configureEach {
|
||||||
workingDirectory project.file('run')
|
workingDirectory project.file("run/${it.name}")
|
||||||
|
|
||||||
// Recommended logging data for a userdev environment
|
// Recommended logging data for a userdev environment
|
||||||
// The markers can be added/remove as needed separated by commas.
|
// The markers can be added/remove as needed separated by commas.
|
||||||
|
@ -112,8 +115,8 @@ minecraft {
|
||||||
}
|
}
|
||||||
|
|
||||||
data {
|
data {
|
||||||
// example of overriding the workingDirectory set in configureEach above
|
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
|
||||||
workingDirectory project.file('run-data')
|
// workingDirectory project.file('run-data')
|
||||||
|
|
||||||
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
|
||||||
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
||||||
|
@ -156,16 +159,9 @@ dependencies {
|
||||||
// The "userdev" classifier will be requested and setup by ForgeGradle.
|
// The "userdev" classifier will be requested and setup by ForgeGradle.
|
||||||
// If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"],
|
// If the group id is "net.minecraft" and the artifact id is one of ["client", "server", "joined"],
|
||||||
// then special handling is done to allow a setup of a vanilla dependency without the use of an external repository.
|
// then special handling is done to allow a setup of a vanilla dependency without the use of an external repository.
|
||||||
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
|
minecraft "net.neoforged:forge:${minecraft_version}-${neo_version}"
|
||||||
|
|
||||||
provided "dev.zontreck:LibAC:${libac}"
|
provided "dev.zontreck:LibAC:${libac}"
|
||||||
implementation "dev.zontreck:LibAC:${libac}"
|
|
||||||
minecraftLibrary "dev.zontreck:LibAC:${libac}"
|
|
||||||
|
|
||||||
|
|
||||||
provided "dev.zontreck:EventsBus:${eventsbus}"
|
|
||||||
implementation "dev.zontreck:EventsBus:${eventsbus}"
|
|
||||||
minecraftLibrary "dev.zontreck:EventsBus:${eventsbus}"
|
|
||||||
|
|
||||||
// Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings
|
// Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings
|
||||||
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
|
||||||
|
@ -187,13 +183,20 @@ dependencies {
|
||||||
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
|
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
|
||||||
// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments.
|
// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments.
|
||||||
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
|
||||||
tasks.named('processResources', ProcessResources).configure {
|
tasks.withType(ProcessResources).configureEach {
|
||||||
var replaceProperties = [
|
var replaceProperties = [
|
||||||
minecraft_version : minecraft_version, minecraft_version_range: minecraft_version_range,
|
minecraft_version : minecraft_version,
|
||||||
forge_version : forge_version, forge_version_range: forge_version_range,
|
minecraft_version_range: minecraft_version_range,
|
||||||
|
neo_version : neo_version,
|
||||||
|
neo_version_range: neo_version_range,
|
||||||
loader_version_range: loader_version_range,
|
loader_version_range: loader_version_range,
|
||||||
mod_id : mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version,
|
mod_id : mod_id,
|
||||||
mod_authors : mod_authors, mod_description: mod_description,
|
mod_name: mod_name,
|
||||||
|
mod_license: mod_license,
|
||||||
|
mod_version: mod_version,
|
||||||
|
mod_authors : mod_authors,
|
||||||
|
mod_description: mod_description,
|
||||||
|
pack_format_number: pack_format_number,
|
||||||
]
|
]
|
||||||
inputs.properties replaceProperties
|
inputs.properties replaceProperties
|
||||||
|
|
||||||
|
@ -204,10 +207,11 @@ tasks.named('processResources', ProcessResources).configure {
|
||||||
|
|
||||||
// Example for how to get properties into the manifest for reading at runtime.
|
// Example for how to get properties into the manifest for reading at runtime.
|
||||||
tasks.named('jar', Jar).configure {
|
tasks.named('jar', Jar).configure {
|
||||||
from {
|
|
||||||
configurations.provided.asFileTree.collect{zipTree(it)}
|
duplicatesStrategy = "exclude"
|
||||||
}
|
from { configurations.provided.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
|
//exclude 'META-INF/*'
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes([
|
attributes([
|
||||||
|
|
|
@ -4,11 +4,10 @@ org.gradle.jvmargs=-Xmx3G
|
||||||
org.gradle.daemon=false
|
org.gradle.daemon=false
|
||||||
|
|
||||||
|
|
||||||
parchment_version=2023.09.03
|
# parchment_version=2023.09.03
|
||||||
# luckperms_api_version=5.4
|
# luckperms_api_version=5.4
|
||||||
|
|
||||||
libac=1.4.46
|
libac=1.5.11
|
||||||
eventsbus=1.0.45
|
|
||||||
## Environment Properties
|
## Environment Properties
|
||||||
|
|
||||||
# The Minecraft version must agree with the Forge version to get a valid artifact
|
# The Minecraft version must agree with the Forge version to get a valid artifact
|
||||||
|
@ -16,12 +15,13 @@ minecraft_version=1.20.1
|
||||||
# The Minecraft version range can use any release version of Minecraft as bounds.
|
# The Minecraft version range can use any release version of Minecraft as bounds.
|
||||||
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
|
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
|
||||||
# as they do not follow standard versioning conventions.
|
# as they do not follow standard versioning conventions.
|
||||||
minecraft_version_range=[1.20.1,1.21)
|
|
||||||
# The Forge version must agree with the Minecraft version to get a valid artifact
|
minecraft_version_range=[1.20,1.21)
|
||||||
forge_version=47.2.0
|
# The Neo version must agree with the Minecraft version to get a valid artifact
|
||||||
# The Forge version range can use any version of Forge as bounds or match the loader version range
|
neo_version=47.1.65
|
||||||
forge_version_range=[47,)
|
# The Neo version range can use any version of Neo as bounds or match the loader version range
|
||||||
# The loader version range can only use the major version of Forge/FML as bounds
|
neo_version_range=[47.1,)
|
||||||
|
# The loader version range can only use the major version of Neo/FML as bounds
|
||||||
loader_version_range=[47,)
|
loader_version_range=[47,)
|
||||||
# The mapping channel to use for mappings.
|
# The mapping channel to use for mappings.
|
||||||
# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"].
|
# The default set of supported mapping channels are ["official", "snapshot", "snapshot_nodoc", "stable", "stable_nodoc"].
|
||||||
|
@ -37,10 +37,10 @@ loader_version_range=[47,)
|
||||||
#
|
#
|
||||||
# Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge.
|
# Parchment is an unofficial project maintained by ParchmentMC, separate from Minecraft Forge.
|
||||||
# Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started
|
# Additional setup is needed to use their mappings, see https://parchmentmc.org/docs/getting-started
|
||||||
mapping_channel=parchment
|
mapping_channel=official
|
||||||
# The mapping version to query from the mapping channel.
|
# The mapping version to query from the mapping channel.
|
||||||
# This must match the format required by the mapping channel.
|
# This must match the format required by the mapping channel.
|
||||||
mapping_version=2023.09.03-1.20.1
|
mapping_version=1.20.1
|
||||||
|
|
||||||
|
|
||||||
## Mod Properties
|
## Mod Properties
|
||||||
|
@ -49,11 +49,11 @@ mapping_version=2023.09.03-1.20.1
|
||||||
# Must match the String constant located in the main mod class annotated with @Mod.
|
# Must match the String constant located in the main mod class annotated with @Mod.
|
||||||
mod_id=libzontreck
|
mod_id=libzontreck
|
||||||
# The human-readable display name for the mod.
|
# The human-readable display name for the mod.
|
||||||
mod_name=Zontreck Library Mod
|
mod_name=Zontreck's Library Mod
|
||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=GPLv3
|
mod_license=GPLv3
|
||||||
# The mod version. See https://semver.org/
|
# The mod version. See https://semver.org/
|
||||||
mod_version=1.10.021324.2257
|
mod_version=1201.12.0.1
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
|
@ -62,3 +62,6 @@ mod_group_id=dev.zontreck
|
||||||
mod_authors=zontreck
|
mod_authors=zontreck
|
||||||
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
|
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
|
||||||
mod_description=LibZontreck\nLibrary Mod!
|
mod_description=LibZontreck\nLibrary Mod!
|
||||||
|
|
||||||
|
# Pack version - this changes each minecraft release, in general.
|
||||||
|
pack_format_number=15
|
1
gradle/wrapper/gradle-wrapper.properties
vendored
1
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,6 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -9,7 +9,6 @@ import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import dev.zontreck.ariaslib.util.DelayedExecutorService;
|
import dev.zontreck.ariaslib.util.DelayedExecutorService;
|
||||||
import dev.zontreck.eventsbus.Bus;
|
|
||||||
import dev.zontreck.libzontreck.chestgui.ChestGUIRegistry;
|
import dev.zontreck.libzontreck.chestgui.ChestGUIRegistry;
|
||||||
import dev.zontreck.libzontreck.currency.Bank;
|
import dev.zontreck.libzontreck.currency.Bank;
|
||||||
import dev.zontreck.libzontreck.currency.CurrencyHelper;
|
import dev.zontreck.libzontreck.currency.CurrencyHelper;
|
||||||
|
@ -89,14 +88,14 @@ public class LibZontreck {
|
||||||
MinecraftForge.EVENT_BUS.register(new NetworkEvents());
|
MinecraftForge.EVENT_BUS.register(new NetworkEvents());
|
||||||
MinecraftForge.EVENT_BUS.register(ChestGUIRegistry.class);
|
MinecraftForge.EVENT_BUS.register(ChestGUIRegistry.class);
|
||||||
|
|
||||||
Bus.Reset();
|
|
||||||
|
|
||||||
ModMenuTypes.REGISTRY.register(bus);
|
ModMenuTypes.REGISTRY.register(bus);
|
||||||
//CreativeModeTabs.register(bus);
|
//CreativeModeTabs.register(bus);
|
||||||
ModItems.register(bus);
|
ModItems.register(bus);
|
||||||
|
|
||||||
Bus.Register(CurrencyHelper.class, null);
|
MinecraftForge.EVENT_BUS.register(CurrencyHelper.class);
|
||||||
Bus.Register(Bank.class, null);
|
MinecraftForge.EVENT_BUS.register(Bank.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setup(final FMLCommonSetupEvent event)
|
private void setup(final FMLCommonSetupEvent event)
|
||||||
|
@ -117,7 +116,6 @@ public class LibZontreck {
|
||||||
public void onServerStopping(final ServerStoppingEvent ev)
|
public void onServerStopping(final ServerStoppingEvent ev)
|
||||||
{
|
{
|
||||||
ALIVE=false;
|
ALIVE=false;
|
||||||
DelayedExecutorService.stop();
|
|
||||||
|
|
||||||
Iterator<Profile> iProfile = PROFILES.values().iterator();
|
Iterator<Profile> iProfile = PROFILES.values().iterator();
|
||||||
while(iProfile.hasNext())
|
while(iProfile.hasNext())
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dev.zontreck.libzontreck.currency;
|
package dev.zontreck.libzontreck.currency;
|
||||||
|
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Bus;
|
|
||||||
import dev.zontreck.libzontreck.chat.ChatColor;
|
import dev.zontreck.libzontreck.chat.ChatColor;
|
||||||
import dev.zontreck.libzontreck.currency.events.TransactionHistoryFlushEvent;
|
import dev.zontreck.libzontreck.currency.events.TransactionHistoryFlushEvent;
|
||||||
import dev.zontreck.libzontreck.profiles.Profile;
|
import dev.zontreck.libzontreck.profiles.Profile;
|
||||||
|
@ -11,6 +10,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
import net.minecraft.nbt.NbtUtils;
|
import net.minecraft.nbt.NbtUtils;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -94,7 +94,7 @@ public class Account
|
||||||
LongTermTransactionHistoryRecord rec = LongTermTransactionHistoryRecord.of(player_id);
|
LongTermTransactionHistoryRecord rec = LongTermTransactionHistoryRecord.of(player_id);
|
||||||
rec.addHistory(history);
|
rec.addHistory(history);
|
||||||
rec.commit();
|
rec.commit();
|
||||||
Bus.Post(new TransactionHistoryFlushEvent(this, rec, history));
|
MinecraftForge.EVENT_BUS.post(new TransactionHistoryFlushEvent(this, rec, history));
|
||||||
rec = null;
|
rec = null;
|
||||||
history = new ArrayList<>();
|
history = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
package dev.zontreck.libzontreck.currency;
|
package dev.zontreck.libzontreck.currency;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import dev.zontreck.eventsbus.Bus;
|
|
||||||
import dev.zontreck.eventsbus.Subscribe;
|
|
||||||
import dev.zontreck.libzontreck.LibZontreck;
|
import dev.zontreck.libzontreck.LibZontreck;
|
||||||
import dev.zontreck.libzontreck.chat.ChatColor;
|
|
||||||
import dev.zontreck.libzontreck.chat.ChatColorFactory;
|
|
||||||
import dev.zontreck.libzontreck.currency.events.BankAccountCreatedEvent;
|
import dev.zontreck.libzontreck.currency.events.BankAccountCreatedEvent;
|
||||||
import dev.zontreck.libzontreck.currency.events.BankReadyEvent;
|
import dev.zontreck.libzontreck.currency.events.BankReadyEvent;
|
||||||
import dev.zontreck.libzontreck.currency.events.TransactionEvent;
|
import dev.zontreck.libzontreck.currency.events.TransactionEvent;
|
||||||
|
@ -22,6 +17,7 @@ import net.minecraft.nbt.ListTag;
|
||||||
import net.minecraft.nbt.NbtIo;
|
import net.minecraft.nbt.NbtIo;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -81,7 +77,7 @@ public class Bank
|
||||||
accounts.add(new Account((CompoundTag) t));
|
accounts.add(new Account((CompoundTag) t));
|
||||||
}
|
}
|
||||||
|
|
||||||
Bus.Post(new BankReadyEvent());
|
MinecraftForge.EVENT_BUS.post(new BankReadyEvent());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -128,7 +124,7 @@ public class Bank
|
||||||
instance.accounts.add(new Account(ID));
|
instance.accounts.add(new Account(ID));
|
||||||
|
|
||||||
instance.commit();
|
instance.commit();
|
||||||
Bus.Post(new BankAccountCreatedEvent(getAccount(ID)));
|
MinecraftForge.EVENT_BUS.post(new BankAccountCreatedEvent(getAccount(ID)));
|
||||||
}else {
|
}else {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +139,7 @@ public class Bank
|
||||||
protected static boolean postTx(Transaction tx) throws InvalidSideException, InvocationTargetException, IllegalAccessException {
|
protected static boolean postTx(Transaction tx) throws InvalidSideException, InvocationTargetException, IllegalAccessException {
|
||||||
if(ServerUtilities.isClient())return false;
|
if(ServerUtilities.isClient())return false;
|
||||||
TransactionEvent ev = new TransactionEvent(tx);
|
TransactionEvent ev = new TransactionEvent(tx);
|
||||||
if(Bus.Post(ev))
|
if(MinecraftForge.EVENT_BUS.post(ev))
|
||||||
{
|
{
|
||||||
// Send the list of reasons to the user
|
// Send the list of reasons to the user
|
||||||
String reasonStr = String.join("\n", ev.reasons);
|
String reasonStr = String.join("\n", ev.reasons);
|
||||||
|
@ -208,9 +204,9 @@ public class Bank
|
||||||
Profile.unload(fromProf);
|
Profile.unload(fromProf);
|
||||||
|
|
||||||
|
|
||||||
Bus.Post(new WalletUpdatedEvent(from.player_id, fromOld, from.balance, tx));
|
MinecraftForge.EVENT_BUS.post(new WalletUpdatedEvent(from.player_id, fromOld, from.balance, tx));
|
||||||
|
|
||||||
Bus.Post(new WalletUpdatedEvent(to.player_id, toOld, to.balance, tx));
|
MinecraftForge.EVENT_BUS.post(new WalletUpdatedEvent(to.player_id, toOld, to.balance, tx));
|
||||||
|
|
||||||
if(from.isValidPlayer() && !ServerUtilities.playerIsOffline(from.player_id))
|
if(from.isValidPlayer() && !ServerUtilities.playerIsOffline(from.player_id))
|
||||||
{
|
{
|
||||||
|
@ -232,7 +228,7 @@ public class Bank
|
||||||
* This event is fired when wallets get updated. It cannot be cancelled
|
* This event is fired when wallets get updated. It cannot be cancelled
|
||||||
* @param ev The event containing the player ID and new+old wallet data
|
* @param ev The event containing the player ID and new+old wallet data
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@SubscribeEvent
|
||||||
public static void onWalletUpdate(WalletUpdatedEvent ev)
|
public static void onWalletUpdate(WalletUpdatedEvent ev)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package dev.zontreck.libzontreck.currency.events;
|
package dev.zontreck.libzontreck.currency.events;
|
||||||
|
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Event;
|
|
||||||
import dev.zontreck.libzontreck.currency.Account;
|
import dev.zontreck.libzontreck.currency.Account;
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
public class BankAccountCreatedEvent extends Event
|
public class BankAccountCreatedEvent extends Event
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.zontreck.libzontreck.currency.events;
|
package dev.zontreck.libzontreck.currency.events;
|
||||||
|
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains no information by itself, it only signals that the Bank is open for business
|
* Contains no information by itself, it only signals that the Bank is open for business
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package dev.zontreck.libzontreck.currency.events;
|
package dev.zontreck.libzontreck.currency.events;
|
||||||
|
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Cancellable;
|
|
||||||
import dev.zontreck.eventsbus.Event;
|
|
||||||
import dev.zontreck.libzontreck.currency.Transaction;
|
import dev.zontreck.libzontreck.currency.Transaction;
|
||||||
|
import net.minecraftforge.eventbus.api.Cancelable;
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Cancellable
|
@Cancelable
|
||||||
public class TransactionEvent extends Event
|
public class TransactionEvent extends Event
|
||||||
{
|
{
|
||||||
public Transaction tx;
|
public Transaction tx;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package dev.zontreck.libzontreck.currency.events;
|
package dev.zontreck.libzontreck.currency.events;
|
||||||
|
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Event;
|
|
||||||
import dev.zontreck.libzontreck.currency.Account;
|
import dev.zontreck.libzontreck.currency.Account;
|
||||||
import dev.zontreck.libzontreck.currency.LongTermTransactionHistoryRecord;
|
import dev.zontreck.libzontreck.currency.LongTermTransactionHistoryRecord;
|
||||||
import dev.zontreck.libzontreck.currency.Transaction;
|
import dev.zontreck.libzontreck.currency.Transaction;
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package dev.zontreck.libzontreck.currency.events;
|
package dev.zontreck.libzontreck.currency.events;
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Event;
|
|
||||||
import dev.zontreck.libzontreck.currency.Transaction;
|
import dev.zontreck.libzontreck.currency.Transaction;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
590
src/main/java/dev/zontreck/libzontreck/edlibmc/Auxiliaries.java
Normal file
590
src/main/java/dev/zontreck/libzontreck/edlibmc/Auxiliaries.java
Normal file
|
@ -0,0 +1,590 @@
|
||||||
|
/*
|
||||||
|
* @file Auxiliaries.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* General commonly used functionality.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.InputConstants;
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.SharedConstants;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.ComponentUtils;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
|
||||||
|
public class Auxiliaries {
|
||||||
|
private static String modid;
|
||||||
|
private static Logger logger;
|
||||||
|
private static Supplier<CompoundTag> server_config_supplier = CompoundTag::new;
|
||||||
|
|
||||||
|
public static void init(String modid, Logger logger, Supplier<CompoundTag> server_config_supplier) {
|
||||||
|
Auxiliaries.modid = modid;
|
||||||
|
Auxiliaries.logger = logger;
|
||||||
|
Auxiliaries.server_config_supplier = server_config_supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Mod specific exports
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static String modid() {
|
||||||
|
return modid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Logger logger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Sidedness, system/environment, tagging interfaces
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public interface IExperimentalFeature {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isModLoaded(final String registry_name) {
|
||||||
|
return ModList.get().isLoaded(registry_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDevelopmentMode() {
|
||||||
|
return SharedConstants.IS_RUNNING_IN_IDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean isShiftDown() {
|
||||||
|
return (InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_SHIFT) ||
|
||||||
|
InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_RIGHT_SHIFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean isCtrlDown() {
|
||||||
|
return (InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_LEFT_CONTROL) ||
|
||||||
|
InputConstants.isKeyDown(SidedProxy.mc().getWindow().getWindow(), GLFW.GLFW_KEY_RIGHT_CONTROL));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Logging
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static void logInfo(final String msg) {
|
||||||
|
logger.info(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logWarn(final String msg) {
|
||||||
|
logger.warn(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logError(final String msg) {
|
||||||
|
logger.error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logDebug(final String msg) { /*logger.debug(msg);*/ }
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Localization, text formatting
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text localization wrapper, implicitly prepends `MODID` to the
|
||||||
|
* translation keys. Forces formatting argument, nullable if no special formatting shall be applied..
|
||||||
|
*/
|
||||||
|
public static MutableComponent localizable(String modtrkey, Object... args) {
|
||||||
|
return Component.translatable((modtrkey.startsWith("block.") || (modtrkey.startsWith("item."))) ? (modtrkey) : (modid + "." + modtrkey), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MutableComponent localizable(String modtrkey, @Nullable ChatFormatting color, Object... args) {
|
||||||
|
final MutableComponent tr = Component.translatable(modid + "." + modtrkey, args);
|
||||||
|
if (color != null) tr.getStyle().applyFormat(color);
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component localizable(String modtrkey) {
|
||||||
|
return localizable(modtrkey, new Object[]{});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Component localizable_block_key(String blocksubkey) {
|
||||||
|
return Component.translatable("block." + modid + "." + blocksubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static String localize(String translationKey, Object... args) {
|
||||||
|
Component tr = Component.translatable(translationKey, args);
|
||||||
|
tr.getStyle().applyFormat(ChatFormatting.RESET);
|
||||||
|
final String ft = tr.getString();
|
||||||
|
if (ft.contains("${")) {
|
||||||
|
// Non-recursive, non-argument lang file entry cross referencing.
|
||||||
|
Pattern pt = Pattern.compile("\\$\\{([^}]+)\\}");
|
||||||
|
Matcher mt = pt.matcher(ft);
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
while (mt.find()) {
|
||||||
|
String m = mt.group(1);
|
||||||
|
if (m.contains("?")) {
|
||||||
|
String[] kv = m.split("\\?", 2);
|
||||||
|
String key = kv[0].trim();
|
||||||
|
boolean not = key.startsWith("!");
|
||||||
|
if (not) key = key.replaceFirst("!", "");
|
||||||
|
m = kv[1].trim();
|
||||||
|
if (!server_config_supplier.get().contains(key)) {
|
||||||
|
m = "";
|
||||||
|
} else {
|
||||||
|
boolean r = server_config_supplier.get().getBoolean(key);
|
||||||
|
if (not) r = !r;
|
||||||
|
if (!r) m = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mt.appendReplacement(sb, Matcher.quoteReplacement((Component.translatable(m)).getString().trim()));
|
||||||
|
}
|
||||||
|
mt.appendTail(sb);
|
||||||
|
return sb.toString();
|
||||||
|
} else {
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a given key is translated for the current language.
|
||||||
|
*/
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean hasTranslation(String key) {
|
||||||
|
return net.minecraft.client.resources.language.I18n.exists(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MutableComponent join(Collection<? extends Component> components, String separator) {
|
||||||
|
return ComponentUtils.formatList(components, Component.literal(separator), Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MutableComponent join(Component... components) {
|
||||||
|
final MutableComponent tc = Component.empty();
|
||||||
|
for (Component c : components) {
|
||||||
|
tc.append(c);
|
||||||
|
}
|
||||||
|
return tc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(Component component) {
|
||||||
|
return component.getSiblings().isEmpty() && component.getString().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Tooltip {
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean extendedTipCondition() {
|
||||||
|
return isShiftDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean helpCondition() {
|
||||||
|
return isShiftDown() && isCtrlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an extended tooltip or help tooltip depending on the key states of CTRL and SHIFT.
|
||||||
|
* Returns true if the localisable help/tip was added, false if not (either not CTL/SHIFT or
|
||||||
|
* no translation found).
|
||||||
|
*/
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean addInformation(@Nullable String advancedTooltipTranslationKey, @Nullable String helpTranslationKey, List<Component> tooltip, TooltipFlag flag, boolean addAdvancedTooltipHints) {
|
||||||
|
// Note: intentionally not using keybinding here, this must be `control` or `shift`.
|
||||||
|
final boolean help_available = (helpTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".help");
|
||||||
|
final boolean tip_available = (advancedTooltipTranslationKey != null) && Auxiliaries.hasTranslation(helpTranslationKey + ".tip");
|
||||||
|
if ((!help_available) && (!tip_available)) return false;
|
||||||
|
String tip_text = "";
|
||||||
|
if (helpCondition()) {
|
||||||
|
if (help_available) tip_text = localize(helpTranslationKey + ".help");
|
||||||
|
} else if (extendedTipCondition()) {
|
||||||
|
if (tip_available) tip_text = localize(advancedTooltipTranslationKey + ".tip");
|
||||||
|
} else if (addAdvancedTooltipHints) {
|
||||||
|
if (tip_available) tip_text += localize(modid + ".tooltip.hint.extended") + (help_available ? " " : "");
|
||||||
|
if (help_available) tip_text += localize(modid + ".tooltip.hint.help");
|
||||||
|
}
|
||||||
|
if (tip_text.isEmpty()) return false;
|
||||||
|
String[] tip_list = tip_text.split("\\r?\\n");
|
||||||
|
for (String tip : tip_list) {
|
||||||
|
tooltip.add(Component.literal(tip.replaceAll("\\s+$", "").replaceAll("^\\s+", "")).withStyle(ChatFormatting.GRAY));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an extended tooltip or help tooltip for a given stack depending on the key states of CTRL and SHIFT.
|
||||||
|
* Format in the lang file is (e.g. for items): "item.MODID.REGISTRYNAME.tip" and "item.MODID.REGISTRYNAME.help".
|
||||||
|
* Return value see method pattern above.
|
||||||
|
*/
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean addInformation(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag, boolean addAdvancedTooltipHints) {
|
||||||
|
return addInformation(stack.getDescriptionId(), stack.getDescriptionId(), tooltip, flag, addAdvancedTooltipHints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static boolean addInformation(String translation_key, List<Component> tooltip) {
|
||||||
|
if (!Auxiliaries.hasTranslation(translation_key)) return false;
|
||||||
|
tooltip.add(Component.literal(localize(translation_key).replaceAll("\\s+$", "").replaceAll("^\\s+", "")).withStyle(ChatFormatting.GRAY));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static void playerChatMessage(final Player player, final String message) {
|
||||||
|
player.displayClientMessage(Component.translatable(message.trim()), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable Component unserializeTextComponent(String serialized) {
|
||||||
|
return Component.Serializer.fromJson(serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String serializeTextComponent(Component tc) {
|
||||||
|
return (tc == null) ? ("") : (Component.Serializer.toJson(tc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Tag Handling
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static boolean isInItemTag(Item item, ResourceLocation tag) {
|
||||||
|
return ForgeRegistries.ITEMS.tags().stream().filter(tg -> tg.getKey().location().equals(tag)).anyMatch(tk -> tk.contains(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static boolean isInBlockTag(Block block, ResourceLocation tag) {
|
||||||
|
return ForgeRegistries.BLOCKS.tags().stream().filter(tg -> tg.getKey().location().equals(tag)).anyMatch(tk -> tk.contains(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static ResourceLocation getResourceLocation(Item item) {
|
||||||
|
return ForgeRegistries.ITEMS.getKey(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static ResourceLocation getResourceLocation(Block block) {
|
||||||
|
return ForgeRegistries.BLOCKS.getKey(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static ResourceLocation getResourceLocation(net.minecraft.world.inventory.MenuType<?> menu) {
|
||||||
|
return ForgeRegistries.MENU_TYPES.getKey(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static ResourceLocation getResourceLocation(net.minecraft.world.level.material.Fluid fluid) {
|
||||||
|
return ForgeRegistries.FLUIDS.getKey(fluid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Item NBT data
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to getDisplayName(), returns null if no custom name is set.
|
||||||
|
*/
|
||||||
|
public static @Nullable Component getItemLabel(ItemStack stack) {
|
||||||
|
CompoundTag nbt = stack.getTagElement("display");
|
||||||
|
if (nbt != null && nbt.contains("Name", 8)) {
|
||||||
|
try {
|
||||||
|
Component tc = unserializeTextComponent(nbt.getString("Name"));
|
||||||
|
if (tc != null) return tc;
|
||||||
|
nbt.remove("Name");
|
||||||
|
} catch (Exception e) {
|
||||||
|
nbt.remove("Name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack setItemLabel(ItemStack stack, @Nullable Component name) {
|
||||||
|
if (name != null) {
|
||||||
|
CompoundTag nbt = stack.getOrCreateTagElement("display");
|
||||||
|
nbt.putString("Name", serializeTextComponent(name));
|
||||||
|
} else {
|
||||||
|
if (stack.hasTag()) stack.removeTagKey("display");
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Block handling
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static boolean isWaterLogged(BlockState state) {
|
||||||
|
return state.hasProperty(BlockStateProperties.WATERLOGGED) && state.getValue(BlockStateProperties.WATERLOGGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB getPixeledAABB(double x0, double y0, double z0, double x1, double y1, double z1) {
|
||||||
|
return new AABB(x0 / 16.0, y0 / 16.0, z0 / 16.0, x1 / 16.0, y1 / 16.0, z1 / 16.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB getRotatedAABB(AABB bb, Direction new_facing) {
|
||||||
|
return getRotatedAABB(bb, new_facing, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB[] getRotatedAABB(AABB[] bb, Direction new_facing) {
|
||||||
|
return getRotatedAABB(bb, new_facing, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB getRotatedAABB(AABB bb, Direction new_facing, boolean horizontal_rotation) {
|
||||||
|
if (!horizontal_rotation) {
|
||||||
|
switch (new_facing.get3DDataValue()) {
|
||||||
|
case 0:
|
||||||
|
return new AABB(1 - bb.maxX, bb.minZ, bb.minY, 1 - bb.minX, bb.maxZ, bb.maxY); // D
|
||||||
|
case 1:
|
||||||
|
return new AABB(1 - bb.maxX, 1 - bb.maxZ, 1 - bb.maxY, 1 - bb.minX, 1 - bb.minZ, 1 - bb.minY); // U
|
||||||
|
case 2:
|
||||||
|
return new AABB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // N --> bb
|
||||||
|
case 3:
|
||||||
|
return new AABB(1 - bb.maxX, bb.minY, 1 - bb.maxZ, 1 - bb.minX, bb.maxY, 1 - bb.minZ); // S
|
||||||
|
case 4:
|
||||||
|
return new AABB(bb.minZ, bb.minY, 1 - bb.maxX, bb.maxZ, bb.maxY, 1 - bb.minX); // W
|
||||||
|
case 5:
|
||||||
|
return new AABB(1 - bb.maxZ, bb.minY, bb.minX, 1 - bb.minZ, bb.maxY, bb.maxX); // E
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (new_facing.get3DDataValue()) {
|
||||||
|
case 0:
|
||||||
|
return new AABB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // D --> bb
|
||||||
|
case 1:
|
||||||
|
return new AABB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // U --> bb
|
||||||
|
case 2:
|
||||||
|
return new AABB(bb.minX, bb.minY, bb.minZ, bb.maxX, bb.maxY, bb.maxZ); // N --> bb
|
||||||
|
case 3:
|
||||||
|
return new AABB(1 - bb.maxX, bb.minY, 1 - bb.maxZ, 1 - bb.minX, bb.maxY, 1 - bb.minZ); // S
|
||||||
|
case 4:
|
||||||
|
return new AABB(bb.minZ, bb.minY, 1 - bb.maxX, bb.maxZ, bb.maxY, 1 - bb.minX); // W
|
||||||
|
case 5:
|
||||||
|
return new AABB(1 - bb.maxZ, bb.minY, bb.minX, 1 - bb.minZ, bb.maxY, bb.maxX); // E
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB[] getRotatedAABB(AABB[] bbs, Direction new_facing, boolean horizontal_rotation) {
|
||||||
|
final AABB[] transformed = new AABB[bbs.length];
|
||||||
|
for (int i = 0; i < bbs.length; ++i) transformed[i] = getRotatedAABB(bbs[i], new_facing, horizontal_rotation);
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB getYRotatedAABB(AABB bb, int clockwise_90deg_steps) {
|
||||||
|
final Direction[] direction_map = new Direction[]{Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||||
|
return getRotatedAABB(bb, direction_map[(clockwise_90deg_steps + 4096) & 0x03], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB[] getYRotatedAABB(AABB[] bbs, int clockwise_90deg_steps) {
|
||||||
|
final AABB[] transformed = new AABB[bbs.length];
|
||||||
|
for (int i = 0; i < bbs.length; ++i) transformed[i] = getYRotatedAABB(bbs[i], clockwise_90deg_steps);
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB getMirroredAABB(AABB bb, Direction.Axis axis) {
|
||||||
|
return switch (axis) {
|
||||||
|
case X -> new AABB(1 - bb.maxX, bb.minY, bb.minZ, 1 - bb.minX, bb.maxY, bb.maxZ);
|
||||||
|
case Y -> new AABB(bb.minX, 1 - bb.maxY, bb.minZ, bb.maxX, 1 - bb.minY, bb.maxZ);
|
||||||
|
case Z -> new AABB(bb.minX, bb.minY, 1 - bb.maxZ, bb.maxX, bb.maxY, 1 - bb.minZ);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB[] getMirroredAABB(AABB[] bbs, Direction.Axis axis) {
|
||||||
|
final AABB[] transformed = new AABB[bbs.length];
|
||||||
|
for (int i = 0; i < bbs.length; ++i) transformed[i] = getMirroredAABB(bbs[i], axis);
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VoxelShape getUnionShape(AABB... aabbs) {
|
||||||
|
VoxelShape shape = Shapes.empty();
|
||||||
|
for (AABB aabb : aabbs) shape = Shapes.joinUnoptimized(shape, Shapes.create(aabb), BooleanOp.OR);
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VoxelShape getUnionShape(AABB[]... aabb_list) {
|
||||||
|
VoxelShape shape = Shapes.empty();
|
||||||
|
for (AABB[] aabbs : aabb_list) {
|
||||||
|
for (AABB aabb : aabbs) shape = Shapes.joinUnoptimized(shape, Shapes.create(aabb), BooleanOp.OR);
|
||||||
|
}
|
||||||
|
return shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AABB[] getMappedAABB(AABB[] bbs, Function<AABB, AABB> mapper) {
|
||||||
|
final AABB[] transformed = new AABB[bbs.length];
|
||||||
|
for (int i = 0; i < bbs.length; ++i) transformed[i] = mapper.apply(bbs[i]);
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class BlockPosRange implements Iterable<BlockPos> {
|
||||||
|
private final int x0, x1, y0, y1, z0, z1;
|
||||||
|
|
||||||
|
public BlockPosRange(int x0, int y0, int z0, int x1, int y1, int z1) {
|
||||||
|
this.x0 = Math.min(x0, x1);
|
||||||
|
this.x1 = Math.max(x0, x1);
|
||||||
|
this.y0 = Math.min(y0, y1);
|
||||||
|
this.y1 = Math.max(y0, y1);
|
||||||
|
this.z0 = Math.min(z0, z1);
|
||||||
|
this.z1 = Math.max(z0, z1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockPosRange of(AABB range) {
|
||||||
|
return new BlockPosRange(
|
||||||
|
(int) Math.floor(range.minX),
|
||||||
|
(int) Math.floor(range.minY),
|
||||||
|
(int) Math.floor(range.minZ),
|
||||||
|
(int) Math.floor(range.maxX - .0625),
|
||||||
|
(int) Math.floor(range.maxY - .0625),
|
||||||
|
(int) Math.floor(range.maxZ - .0625)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXSize() {
|
||||||
|
return x1 - x0 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getYSize() {
|
||||||
|
return y1 - y0 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getZSize() {
|
||||||
|
return z1 - z0 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getArea() {
|
||||||
|
return getXSize() * getZSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return getYSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVolume() {
|
||||||
|
return getXSize() * getYSize() * getZSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos byXZYIndex(int xyz_index) {
|
||||||
|
final int xsz = getXSize(), ysz = getYSize(), zsz = getZSize();
|
||||||
|
xyz_index = xyz_index % (xsz * ysz * zsz);
|
||||||
|
final int y = xyz_index / (xsz * zsz);
|
||||||
|
xyz_index -= y * (xsz * zsz);
|
||||||
|
final int z = xyz_index / xsz;
|
||||||
|
xyz_index -= z * xsz;
|
||||||
|
final int x = xyz_index;
|
||||||
|
return new BlockPos(x0 + x, y0 + y, z0 + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos byXZIndex(int xz_index, int y_offset) {
|
||||||
|
final int xsz = getXSize(), zsz = getZSize();
|
||||||
|
xz_index = xz_index % (xsz * zsz);
|
||||||
|
final int z = xz_index / xsz;
|
||||||
|
xz_index -= z * xsz;
|
||||||
|
final int x = xz_index;
|
||||||
|
return new BlockPos(x0 + x, y0 + y_offset, z0 + z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class BlockRangeIterator implements Iterator<BlockPos> {
|
||||||
|
private final BlockPosRange range_;
|
||||||
|
private int x, y, z;
|
||||||
|
|
||||||
|
public BlockRangeIterator(BlockPosRange range) {
|
||||||
|
range_ = range;
|
||||||
|
x = range.x0;
|
||||||
|
y = range.y0;
|
||||||
|
z = range.z0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return (z <= range_.z1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockPos next() {
|
||||||
|
if (!hasNext()) throw new NoSuchElementException();
|
||||||
|
final BlockPos pos = new BlockPos(x, y, z);
|
||||||
|
++x;
|
||||||
|
if (x > range_.x1) {
|
||||||
|
x = range_.x0;
|
||||||
|
++y;
|
||||||
|
if (y > range_.y1) {
|
||||||
|
y = range_.y0;
|
||||||
|
++z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockRangeIterator iterator() {
|
||||||
|
return new BlockRangeIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<BlockPos> stream() {
|
||||||
|
return java.util.stream.StreamSupport.stream(spliterator(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// JAR resource related
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static String loadResourceText(InputStream is) {
|
||||||
|
try {
|
||||||
|
if (is == null) return "";
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||||
|
return br.lines().collect(Collectors.joining("\n"));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String loadResourceText(String path) {
|
||||||
|
return loadResourceText(Auxiliaries.class.getResourceAsStream(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logGitVersion(String mod_name) {
|
||||||
|
try {
|
||||||
|
// Done during construction to have an exact version in case of a crash while registering.
|
||||||
|
String version = Auxiliaries.loadResourceText("/.gitversion-" + modid).trim();
|
||||||
|
logInfo(mod_name + ((version.isEmpty()) ? (" (dev build)") : (" GIT id #" + version)) + ".");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// (void)e; well, then not. Priority is not to get unneeded crashes because of version logging.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
src/main/java/dev/zontreck/libzontreck/edlibmc/Containers.java
Normal file
138
src/main/java/dev/zontreck/libzontreck/edlibmc/Containers.java
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.Slot;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
public class Containers {
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Slots
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static class StorageSlot extends Slot {
|
||||||
|
protected BiConsumer<ItemStack, ItemStack> slot_change_action_ = (oldStack, newStack) -> {
|
||||||
|
};
|
||||||
|
protected int stack_limit_ = 64;
|
||||||
|
public boolean enabled = true;
|
||||||
|
|
||||||
|
public StorageSlot(Container inventory, int index, int x, int y) {
|
||||||
|
super(inventory, index, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StorageSlot setSlotStackLimit(int limit) {
|
||||||
|
stack_limit_ = Mth.clamp(limit, 1, 64);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxStackSize() {
|
||||||
|
return stack_limit_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StorageSlot setSlotChangeNotifier(BiConsumer<ItemStack, ItemStack> action) {
|
||||||
|
slot_change_action_ = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQuickCraft(ItemStack oldStack, ItemStack newStack) {
|
||||||
|
slot_change_action_.accept(oldStack, newStack);
|
||||||
|
} // no crafting trigger
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(ItemStack stack) {
|
||||||
|
if (stack.is(getItem().getItem())) {
|
||||||
|
super.set(stack);
|
||||||
|
} else {
|
||||||
|
final ItemStack before = getItem().copy();
|
||||||
|
super.set(stack); // whatever this does else next to setting inventory.
|
||||||
|
slot_change_action_.accept(before, getItem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPlace(ItemStack stack) {
|
||||||
|
return enabled && this.container.canPlaceItem(this.getSlotIndex(), stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxStackSize(ItemStack stack) {
|
||||||
|
return Math.min(getMaxStackSize(), stack_limit_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public boolean isActive() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LockedSlot extends Slot {
|
||||||
|
protected int stack_limit_ = 64;
|
||||||
|
public boolean enabled = true;
|
||||||
|
|
||||||
|
public LockedSlot(Container inventory, int index, int x, int y) {
|
||||||
|
super(inventory, index, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockedSlot setSlotStackLimit(int limit) {
|
||||||
|
stack_limit_ = Mth.clamp(limit, 1, 64);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxStackSize() {
|
||||||
|
return stack_limit_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxStackSize(ItemStack stack) {
|
||||||
|
return Math.min(getMaxStackSize(), stack_limit_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPlace(ItemStack stack) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPickup(Player player) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public boolean isActive() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HiddenSlot extends Slot {
|
||||||
|
public HiddenSlot(Container inventory, int index) {
|
||||||
|
super(inventory, index, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxStackSize(ItemStack stack) {
|
||||||
|
return getMaxStackSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPlace(ItemStack stack) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mayPickup(Player player) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public boolean isActive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
440
src/main/java/dev/zontreck/libzontreck/edlibmc/Crafting.java
Normal file
440
src/main/java/dev/zontreck/libzontreck/edlibmc/Crafting.java
Normal file
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* @file Recipes.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Recipe utility functionality.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.util.Tuple;
|
||||||
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.SimpleContainer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.entity.player.StackedContents;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.inventory.CraftingContainer;
|
||||||
|
import net.minecraft.world.item.EnchantedBookItem;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.crafting.*;
|
||||||
|
import net.minecraft.world.item.enchantment.Enchantment;
|
||||||
|
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||||
|
import net.minecraft.world.item.enchantment.EnchantmentInstance;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.ComposterBlock;
|
||||||
|
import net.minecraftforge.common.ForgeHooks;
|
||||||
|
import net.minecraftforge.common.brewing.BrewingRecipeRegistry;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
|
||||||
|
public class Crafting {
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Crafting recipe by registry name.
|
||||||
|
*/
|
||||||
|
public static Optional<CraftingRecipe> getCraftingRecipe(Level world, ResourceLocation recipe_id) {
|
||||||
|
Recipe<?> recipe = world.getRecipeManager().byKey(recipe_id).orElse(null);
|
||||||
|
return (recipe instanceof CraftingRecipe) ? Optional.of((CraftingRecipe) recipe) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of matching recipes by the first N slots (crafting grid slots) of the given inventory.
|
||||||
|
*/
|
||||||
|
public static List<CraftingRecipe> get3x3CraftingRecipes(Level world, Container crafting_grid_slots) {
|
||||||
|
return CraftingGrid.instance3x3.getRecipes(world, crafting_grid_slots);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a recipe by the first N slots (crafting grid slots).
|
||||||
|
*/
|
||||||
|
public static Optional<CraftingRecipe> get3x3CraftingRecipe(Level world, Container crafting_grid_slots) {
|
||||||
|
return get3x3CraftingRecipes(world, crafting_grid_slots).stream().findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the result item of the recipe with the given grid layout.
|
||||||
|
*/
|
||||||
|
public static ItemStack get3x3CraftingResult(Level world, Container grid, CraftingRecipe recipe) {
|
||||||
|
return CraftingGrid.instance3x3.getCraftingResult(world, grid, recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the items remaining in the grid after crafting 3x3.
|
||||||
|
*/
|
||||||
|
public static List<ItemStack> get3x3RemainingItems(Level world, Container grid, CraftingRecipe recipe) {
|
||||||
|
return CraftingGrid.instance3x3.getRemainingItems(world, grid, recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ItemStack> get3x3Placement(Level world, CraftingRecipe recipe, Container item_inventory, @Nullable Container crafting_grid) {
|
||||||
|
final int width = 3;
|
||||||
|
final int height = 3;
|
||||||
|
if (!recipe.canCraftInDimensions(width, height)) return Collections.emptyList();
|
||||||
|
List<ItemStack> used = new ArrayList<>(); //NonNullList.withSize(width*height);
|
||||||
|
for (int i = width * height; i > 0; --i) used.add(ItemStack.EMPTY);
|
||||||
|
Container check_inventory = Inventories.copyOf(item_inventory);
|
||||||
|
Inventories.InventoryRange source = new Inventories.InventoryRange(check_inventory);
|
||||||
|
final List<Ingredient> ingredients = recipe.getIngredients();
|
||||||
|
final List<ItemStack> preferred = new ArrayList<>(width * height);
|
||||||
|
if (crafting_grid != null) {
|
||||||
|
for (int i = 0; i < crafting_grid.getContainerSize(); ++i) {
|
||||||
|
ItemStack stack = crafting_grid.getItem(i);
|
||||||
|
if (stack.isEmpty()) continue;
|
||||||
|
stack = stack.copy();
|
||||||
|
stack.setCount(1);
|
||||||
|
if (!source.extract(stack).isEmpty()) preferred.add(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < ingredients.size(); ++i) {
|
||||||
|
final Ingredient ingredient = ingredients.get(i);
|
||||||
|
if (ingredient == Ingredient.EMPTY) continue;
|
||||||
|
ItemStack stack = preferred.stream().filter(ingredient).findFirst().orElse(ItemStack.EMPTY);
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
preferred.remove(stack);
|
||||||
|
} else {
|
||||||
|
stack = source.stream().filter(ingredient).findFirst().orElse(ItemStack.EMPTY);
|
||||||
|
if (stack.isEmpty()) return Collections.emptyList();
|
||||||
|
stack = stack.copy();
|
||||||
|
stack.setCount(1);
|
||||||
|
if (source.extract(stack).isEmpty()) return Collections.emptyList();
|
||||||
|
}
|
||||||
|
used.set(i, stack);
|
||||||
|
}
|
||||||
|
if (recipe instanceof ShapedRecipe shaped) {
|
||||||
|
List<ItemStack> placement = NonNullList.withSize(width * height, ItemStack.EMPTY);
|
||||||
|
for (int row = 0; row < shaped.getRecipeHeight(); ++row) {
|
||||||
|
for (int col = 0; col < shaped.getRecipeWidth(); ++col) {
|
||||||
|
placement.set(width * row + col, used.get(row * shaped.getRecipeWidth() + col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return placement;
|
||||||
|
} else {
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the recipe for a given input stack to smelt, null if there is no recipe
|
||||||
|
* for the given type (SMELTING,BLASTING,SMOKING, etc).
|
||||||
|
*/
|
||||||
|
public static <T extends Recipe<?>> Optional<AbstractCookingRecipe> getFurnaceRecipe(RecipeType<T> recipe_type, Level world, ItemStack input_stack) {
|
||||||
|
if (input_stack.isEmpty()) {
|
||||||
|
return Optional.empty();
|
||||||
|
} else if (recipe_type == RecipeType.SMELTING) {
|
||||||
|
SimpleContainer inventory = new SimpleContainer(3);
|
||||||
|
inventory.setItem(0, input_stack);
|
||||||
|
SmeltingRecipe recipe = world.getRecipeManager().getRecipeFor(RecipeType.SMELTING, inventory, world).orElse(null);
|
||||||
|
return (recipe == null) ? Optional.empty() : Optional.of(recipe);
|
||||||
|
} else if (recipe_type == RecipeType.BLASTING) {
|
||||||
|
SimpleContainer inventory = new SimpleContainer(3);
|
||||||
|
inventory.setItem(0, input_stack);
|
||||||
|
BlastingRecipe recipe = world.getRecipeManager().getRecipeFor(RecipeType.BLASTING, inventory, world).orElse(null);
|
||||||
|
return (recipe == null) ? Optional.empty() : Optional.of(recipe);
|
||||||
|
} else if (recipe_type == RecipeType.SMOKING) {
|
||||||
|
SimpleContainer inventory = new SimpleContainer(3);
|
||||||
|
inventory.setItem(0, input_stack);
|
||||||
|
SmokingRecipe recipe = world.getRecipeManager().getRecipeFor(RecipeType.SMOKING, inventory, world).orElse(null);
|
||||||
|
return (recipe == null) ? Optional.empty() : Optional.of(recipe);
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static <T extends Recipe<?>> int getSmeltingTimeNeeded(RecipeType<T> recipe_type, Level world, ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return 0;
|
||||||
|
final int t = getFurnaceRecipe(recipe_type, world, stack).map((AbstractCookingRecipe::getCookingTime)).orElse(0);
|
||||||
|
return (t <= 0) ? 200 : t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the burn time of an item when used as fuel, 0 if it is no fuel.
|
||||||
|
*/
|
||||||
|
public static int getFuelBurntime(Level world, ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return 0;
|
||||||
|
int t = ForgeHooks.getBurnTime(stack, null);
|
||||||
|
return Math.max(t, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if an item can be used as fuel.
|
||||||
|
*/
|
||||||
|
public static boolean isFuel(Level world, ItemStack stack) {
|
||||||
|
return (getFuelBurntime(world, stack) > 0) || (stack.getItem() == Items.LAVA_BUCKET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns burntime and remaining stack then the item shall be used as fuel.
|
||||||
|
*/
|
||||||
|
public static Tuple<Integer, ItemStack> consumeFuel(Level world, ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return new Tuple<>(0, stack);
|
||||||
|
int burnime = getFuelBurntime(world, stack);
|
||||||
|
if ((stack.getItem() == Items.LAVA_BUCKET)) {
|
||||||
|
if (burnime <= 0) burnime = 1000 * 20;
|
||||||
|
return new Tuple<>(burnime, new ItemStack(Items.BUCKET));
|
||||||
|
} else if (burnime <= 0) {
|
||||||
|
return new Tuple<>(0, stack);
|
||||||
|
} else {
|
||||||
|
ItemStack left_over = stack.copy();
|
||||||
|
left_over.shrink(1);
|
||||||
|
return new Tuple<>(burnime, left_over);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the item can be used as brewing fuel.
|
||||||
|
*/
|
||||||
|
public static boolean isBrewingFuel(Level world, ItemStack stack) {
|
||||||
|
return (stack.getItem() == Items.BLAZE_POWDER) || (stack.getItem() == Items.BLAZE_ROD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the item can be used as brewing ingredient.
|
||||||
|
*/
|
||||||
|
public static boolean isBrewingIngredient(Level world, ItemStack stack) {
|
||||||
|
return BrewingRecipeRegistry.isValidIngredient(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the item can be used as brewing bottle.
|
||||||
|
*/
|
||||||
|
public static boolean isBrewingInput(Level world, ItemStack stack) {
|
||||||
|
return BrewingRecipeRegistry.isValidInput(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the burn time for brewing of the given stack.
|
||||||
|
*/
|
||||||
|
public static int getBrewingFuelBurntime(Level world, ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return 0;
|
||||||
|
if (stack.getItem() == Items.BLAZE_POWDER) return (400 * 20);
|
||||||
|
if (stack.getItem() == Items.BLAZE_ROD) return (400 * 40);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns brewing burn time and remaining stack if the item shall be used as fuel.
|
||||||
|
*/
|
||||||
|
public static Tuple<Integer, ItemStack> consumeBrewingFuel(Level world, ItemStack stack) {
|
||||||
|
int burntime = getBrewingFuelBurntime(world, stack);
|
||||||
|
if (burntime <= 0) return new Tuple<>(0, stack.copy());
|
||||||
|
stack = stack.copy();
|
||||||
|
stack.shrink(1);
|
||||||
|
return new Tuple<>(burntime, stack.isEmpty() ? ItemStack.EMPTY : stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getCompostingChance(ItemStack stack) {
|
||||||
|
return ComposterBlock.COMPOSTABLES.getOrDefault(stack.getItem(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the enchtments bound to the given stack.
|
||||||
|
*/
|
||||||
|
public static Map<Enchantment, Integer> getEnchantmentsOnItem(Level world, ItemStack stack) {
|
||||||
|
return (stack.isEmpty() || (stack.getTag() == null)) ? Collections.emptyMap() : EnchantmentHelper.getEnchantments(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an enchanted book with the given enchantment, emtpy stack if not applicable.
|
||||||
|
*/
|
||||||
|
public static ItemStack getEnchantmentBook(Level world, Enchantment enchantment, int level) {
|
||||||
|
return ((!enchantment.isAllowedOnBooks()) || (level <= 0)) ? ItemStack.EMPTY : EnchantedBookItem.createForEnchantment(new EnchantmentInstance(enchantment, level));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the accumulated repair cost for the given enchantments.
|
||||||
|
*/
|
||||||
|
public static int getEnchantmentRepairCost(Level world, Map<Enchantment, Integer> enchantments) {
|
||||||
|
int repair_cost = 0;
|
||||||
|
for (Map.Entry<Enchantment, Integer> e : enchantments.entrySet())
|
||||||
|
repair_cost = repair_cost * 2 + 1; // @see: RepairContainer.getNewRepairCost()
|
||||||
|
return repair_cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trys to add an enchtment to the given stack, returns boolean success.
|
||||||
|
*/
|
||||||
|
public static boolean addEnchantmentOnItem(Level world, ItemStack stack, Enchantment enchantment, int level) {
|
||||||
|
if (stack.isEmpty() || (level <= 0) || (!stack.isEnchantable()) || (level >= enchantment.getMaxLevel()))
|
||||||
|
return false;
|
||||||
|
final Map<Enchantment, Integer> on_item = getEnchantmentsOnItem(world, stack);
|
||||||
|
if (on_item.keySet().stream().anyMatch(ench -> ench.isCompatibleWith(enchantment))) return false;
|
||||||
|
final ItemStack book = EnchantedBookItem.createForEnchantment(new EnchantmentInstance(enchantment, level));
|
||||||
|
if ((!(stack.isBookEnchantable(book) && enchantment.isAllowedOnBooks())) && (!stack.canApplyAtEnchantingTable(enchantment)) && (!enchantment.canEnchant(stack)))
|
||||||
|
return false;
|
||||||
|
final int existing_level = on_item.getOrDefault(enchantment, 0);
|
||||||
|
if (existing_level > 0) level = Mth.clamp(level + existing_level, 1, enchantment.getMaxLevel());
|
||||||
|
on_item.put(enchantment, level);
|
||||||
|
EnchantmentHelper.setEnchantments(on_item, stack);
|
||||||
|
stack.setRepairCost(getEnchantmentRepairCost(world, on_item));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes enchantments from a stack, returns the removed enchantments.
|
||||||
|
*/
|
||||||
|
public static Map<Enchantment, Integer> removeEnchantmentsOnItem(Level world, ItemStack stack, BiPredicate<Enchantment, Integer> filter) {
|
||||||
|
if (stack.isEmpty()) return Collections.emptyMap();
|
||||||
|
final Map<Enchantment, Integer> on_item = getEnchantmentsOnItem(world, stack);
|
||||||
|
final Map<Enchantment, Integer> removed = new HashMap<>();
|
||||||
|
for (Map.Entry<Enchantment, Integer> e : on_item.entrySet()) {
|
||||||
|
if (filter.test(e.getKey(), e.getValue())) {
|
||||||
|
removed.put(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Enchantment e : removed.keySet()) {
|
||||||
|
on_item.remove(e);
|
||||||
|
}
|
||||||
|
EnchantmentHelper.setEnchantments(on_item, stack);
|
||||||
|
stack.setRepairCost(getEnchantmentRepairCost(world, on_item));
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class CraftingGrid implements CraftingContainer {
|
||||||
|
private static final CraftingGrid instance3x3 = new CraftingGrid(3, 3);
|
||||||
|
|
||||||
|
final int _width;
|
||||||
|
|
||||||
|
private CraftingGrid(int width, int height) {
|
||||||
|
_width=width;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fill(Container grid) {
|
||||||
|
for (int i = 0; i < getContainerSize(); ++i)
|
||||||
|
setItem(i, i >= grid.getContainerSize() ? ItemStack.EMPTY : grid.getItem(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CraftingRecipe> getRecipes(Level world, Container grid) {
|
||||||
|
fill(grid);
|
||||||
|
return world.getRecipeManager().getRecipesFor(RecipeType.CRAFTING, this, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ItemStack> getRemainingItems(Level world, Container grid, CraftingRecipe recipe) {
|
||||||
|
fill(grid);
|
||||||
|
return recipe.getRemainingItems(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack getCraftingResult(Level world, Container grid, CraftingRecipe recipe) {
|
||||||
|
fill(grid);
|
||||||
|
return recipe.assemble(this, world.registryAccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return _width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ItemStack> getItems() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getContainerSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getItem(int i) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeItem(int i, int i1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeItemNoUpdate(int i) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(int i, ItemStack itemStack) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stillValid(Player player) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearContent() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillStackedContents(StackedContents stackedContents) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class BrewingOutput {
|
||||||
|
public static final int DEFAULT_BREWING_TIME = 400;
|
||||||
|
public static final BrewingOutput EMPTY = new BrewingOutput(ItemStack.EMPTY, new SimpleContainer(1), new SimpleContainer(1), 0, 0, DEFAULT_BREWING_TIME);
|
||||||
|
public final ItemStack item;
|
||||||
|
public final Container potionInventory;
|
||||||
|
public final Container ingredientInventory;
|
||||||
|
public final int potionSlot;
|
||||||
|
public final int ingredientSlot;
|
||||||
|
public final int brewTime;
|
||||||
|
|
||||||
|
public BrewingOutput(ItemStack output_potion, Container potion_inventory, Container ingredient_inventory, int potion_slot, int ingredient_slot, int time_needed) {
|
||||||
|
item = output_potion;
|
||||||
|
potionInventory = potion_inventory;
|
||||||
|
ingredientInventory = ingredient_inventory;
|
||||||
|
potionSlot = potion_slot;
|
||||||
|
ingredientSlot = ingredient_slot;
|
||||||
|
brewTime = time_needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BrewingOutput find(Level world, Container potion_inventory, Container ingredient_inventory) {
|
||||||
|
for (int potion_slot = 0; potion_slot < potion_inventory.getContainerSize(); ++potion_slot) {
|
||||||
|
final ItemStack pstack = potion_inventory.getItem(potion_slot);
|
||||||
|
if (!isBrewingInput(world, pstack)) continue;
|
||||||
|
for (int ingredient_slot = 0; ingredient_slot < ingredient_inventory.getContainerSize(); ++ingredient_slot) {
|
||||||
|
final ItemStack istack = ingredient_inventory.getItem(ingredient_slot);
|
||||||
|
if ((!isBrewingIngredient(world, istack)) || (ingredient_slot == potion_slot) || (isBrewingFuel(world, istack)))
|
||||||
|
continue;
|
||||||
|
final ItemStack result = BrewingRecipeRegistry.getOutput(pstack, istack);
|
||||||
|
if (result.isEmpty()) continue;
|
||||||
|
return new BrewingOutput(result, potion_inventory, ingredient_inventory, potion_slot, ingredient_slot, DEFAULT_BREWING_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BrewingOutput.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
485
src/main/java/dev/zontreck/libzontreck/edlibmc/Fluidics.java
Normal file
485
src/main/java/dev/zontreck/libzontreck/edlibmc/Fluidics.java
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
/*
|
||||||
|
* @file Fluidics.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* General fluid handling functionality.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.util.Tuple;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.material.Fluid;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||||
|
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import net.minecraftforge.fluids.FluidActionResult;
|
||||||
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
import net.minecraftforge.fluids.FluidUtil;
|
||||||
|
import net.minecraftforge.fluids.IFluidTank;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
|
||||||
|
import net.minecraftforge.items.IItemHandler;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
|
||||||
|
public class Fluidics {
|
||||||
|
public static class SingleTankFluidHandler implements IFluidHandler {
|
||||||
|
private final IFluidTank tank_;
|
||||||
|
|
||||||
|
public SingleTankFluidHandler(IFluidTank tank) {
|
||||||
|
tank_ = tank;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTanks() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack getFluidInTank(int tank) {
|
||||||
|
return tank_.getFluid();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTankCapacity(int tank) {
|
||||||
|
return tank_.getCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFluidValid(int tank, @Nonnull FluidStack stack) {
|
||||||
|
return tank_.isFluidValid(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fill(FluidStack resource, FluidAction action) {
|
||||||
|
return tank_.fill(resource, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(FluidStack resource, FluidAction action) {
|
||||||
|
return tank_.drain(resource, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(int maxDrain, FluidAction action) {
|
||||||
|
return tank_.drain(maxDrain, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingleTankOutputFluidHandler implements IFluidHandler {
|
||||||
|
private final IFluidTank tank_;
|
||||||
|
|
||||||
|
public SingleTankOutputFluidHandler(IFluidTank tank) {
|
||||||
|
tank_ = tank;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTanks() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack getFluidInTank(int tank) {
|
||||||
|
return tank_.getFluid().copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTankCapacity(int tank) {
|
||||||
|
return tank_.getCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFluidValid(int tank, @Nonnull FluidStack stack) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fill(FluidStack resource, FluidAction action) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(FluidStack resource, FluidAction action) {
|
||||||
|
return tank_.drain(resource, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(int maxDrain, FluidAction action) {
|
||||||
|
return tank_.drain(maxDrain, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Tank implements IFluidTank {
|
||||||
|
private Predicate<FluidStack> validator_ = ((e) -> true);
|
||||||
|
private BiConsumer<Tank, Integer> interaction_notifier_ = ((tank, diff) -> {
|
||||||
|
});
|
||||||
|
private FluidStack fluid_ = FluidStack.EMPTY;
|
||||||
|
private int capacity_;
|
||||||
|
private int fill_rate_;
|
||||||
|
private int drain_rate_;
|
||||||
|
|
||||||
|
public Tank(int capacity) {
|
||||||
|
this(capacity, capacity, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank(int capacity, int fill_rate, int drain_rate) {
|
||||||
|
this(capacity, fill_rate, drain_rate, e -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank(int capacity, int fill_rate, int drain_rate, Predicate<FluidStack> validator) {
|
||||||
|
capacity_ = capacity;
|
||||||
|
setMaxFillRate(fill_rate);
|
||||||
|
setMaxDrainRate(drain_rate);
|
||||||
|
setValidator(validator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank load(CompoundTag nbt) {
|
||||||
|
if (nbt.contains("tank", Tag.TAG_COMPOUND)) {
|
||||||
|
setFluid(FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
|
||||||
|
} else {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag save(CompoundTag nbt) {
|
||||||
|
if (!isEmpty()) {
|
||||||
|
nbt.put("tank", fluid_.writeToNBT(new CompoundTag()));
|
||||||
|
}
|
||||||
|
return nbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank clear() {
|
||||||
|
setFluid(null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCapacity() {
|
||||||
|
return capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank setCapacity(int capacity) {
|
||||||
|
capacity_ = capacity;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxDrainRate() {
|
||||||
|
return drain_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank setMaxDrainRate(int rate) {
|
||||||
|
drain_rate_ = Mth.clamp(rate, 0, capacity_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxFillRate() {
|
||||||
|
return fill_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank setMaxFillRate(int rate) {
|
||||||
|
fill_rate_ = Mth.clamp(rate, 0, capacity_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank setValidator(Predicate<FluidStack> validator) {
|
||||||
|
validator_ = (validator != null) ? validator : ((e) -> true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tank setInteractionNotifier(BiConsumer<Tank, Integer> notifier) {
|
||||||
|
interaction_notifier_ = (notifier != null) ? notifier : ((tank, diff) -> {
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LazyOptional<IFluidHandler> createFluidHandler() {
|
||||||
|
return LazyOptional.of(() -> new Fluidics.SingleTankFluidHandler(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LazyOptional<IFluidHandler> createOutputFluidHandler() {
|
||||||
|
return LazyOptional.of(() -> new Fluidics.SingleTankOutputFluidHandler(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// IFluidTank ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public FluidStack getFluid() {
|
||||||
|
return fluid_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFluid(@Nullable FluidStack stack) {
|
||||||
|
fluid_ = (stack == null) ? FluidStack.EMPTY : stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFluidAmount() {
|
||||||
|
return fluid_.getAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return fluid_.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFull() {
|
||||||
|
return getFluidAmount() >= getCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFluidValid(FluidStack stack) {
|
||||||
|
return validator_.test(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFluidEqual(FluidStack stack) {
|
||||||
|
return (stack == null) ? (fluid_.isEmpty()) : fluid_.isFluidEqual(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fill(FluidStack fs, FluidAction action) {
|
||||||
|
if ((fs == null) || fs.isEmpty() || (!isFluidValid(fs))) {
|
||||||
|
return 0;
|
||||||
|
} else if (action.simulate()) {
|
||||||
|
if (fluid_.isEmpty()) return Math.min(capacity_, fs.getAmount());
|
||||||
|
if (!fluid_.isFluidEqual(fs)) return 0;
|
||||||
|
return Math.min(capacity_ - fluid_.getAmount(), fs.getAmount());
|
||||||
|
} else if (fluid_.isEmpty()) {
|
||||||
|
fluid_ = new FluidStack(fs, Math.min(capacity_, fs.getAmount()));
|
||||||
|
return fluid_.getAmount();
|
||||||
|
} else if (!fluid_.isFluidEqual(fs)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
int amount = capacity_ - fluid_.getAmount();
|
||||||
|
if (fs.getAmount() < amount) {
|
||||||
|
fluid_.grow(fs.getAmount());
|
||||||
|
amount = fs.getAmount();
|
||||||
|
} else {
|
||||||
|
fluid_.setAmount(capacity_);
|
||||||
|
}
|
||||||
|
if (amount != 0) interaction_notifier_.accept(this, amount);
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public FluidStack drain(int maxDrain) {
|
||||||
|
return drain(maxDrain, FluidAction.EXECUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(FluidStack fs, FluidAction action) {
|
||||||
|
return ((fs.isEmpty()) || (!fs.isFluidEqual(fluid_))) ? FluidStack.EMPTY : drain(fs.getAmount(), action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(int maxDrain, FluidAction action) {
|
||||||
|
final int amount = Math.min(fluid_.getAmount(), maxDrain);
|
||||||
|
final FluidStack stack = new FluidStack(fluid_, amount);
|
||||||
|
if ((amount > 0) && action.execute()) {
|
||||||
|
fluid_.shrink(amount);
|
||||||
|
if (fluid_.isEmpty()) fluid_ = FluidStack.EMPTY;
|
||||||
|
if (amount != 0) interaction_notifier_.accept(this, -amount);
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static @Nullable IFluidHandler handler(Level world, BlockPos pos, @Nullable Direction side) {
|
||||||
|
return FluidUtil.getFluidHandler(world, pos, side).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills or drains items with fluid handlers from or into tile blocks with fluid handlers.
|
||||||
|
*/
|
||||||
|
public static boolean manualFluidHandlerInteraction(Level world, BlockPos pos, @Nullable Direction side, Player player, InteractionHand hand) {
|
||||||
|
return manualTrackedFluidHandlerInteraction(world, pos, side, player, hand) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean manualFluidHandlerInteraction(Player player, InteractionHand hand, IFluidHandler handler) {
|
||||||
|
return FluidUtil.interactWithFluidHandler(player, hand, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills or drains items with fluid handlers from or into tile blocks with fluid handlers.
|
||||||
|
* Returns the fluid and (possibly negative) amount that transferred from the item into the block.
|
||||||
|
*/
|
||||||
|
public static @Nullable Tuple<Fluid, Integer> manualTrackedFluidHandlerInteraction(Level world, BlockPos pos, @Nullable Direction side, Player player, InteractionHand hand) {
|
||||||
|
if (world.isClientSide()) return null;
|
||||||
|
final ItemStack held = player.getItemInHand(hand);
|
||||||
|
if (held.isEmpty()) return null;
|
||||||
|
final IFluidHandler fh = handler(world, pos, side);
|
||||||
|
if (fh == null) return null;
|
||||||
|
final IItemHandler ih = player.getCapability(ForgeCapabilities.ITEM_HANDLER).orElse(null);
|
||||||
|
if (ih == null) return null;
|
||||||
|
FluidActionResult far = FluidUtil.tryFillContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true);
|
||||||
|
if (!far.isSuccess()) far = FluidUtil.tryEmptyContainerAndStow(held, fh, ih, Integer.MAX_VALUE, player, true);
|
||||||
|
if (!far.isSuccess()) return null;
|
||||||
|
final ItemStack rstack = far.getResult().copy();
|
||||||
|
player.setItemInHand(hand, far.getResult());
|
||||||
|
final IFluidHandler fh_before = FluidUtil.getFluidHandler(held).orElse(null);
|
||||||
|
final IFluidHandler fh_after = FluidUtil.getFluidHandler(rstack).orElse(null);
|
||||||
|
if ((fh_before == null) || (fh_after == null) || (fh_after.getTanks() != fh_before.getTanks()))
|
||||||
|
return null; // should not be, but y'never know.
|
||||||
|
for (int i = 0; i < fh_before.getTanks(); ++i) {
|
||||||
|
final int vol_before = fh_before.getFluidInTank(i).getAmount();
|
||||||
|
final int vol_after = fh_after.getFluidInTank(i).getAmount();
|
||||||
|
if (vol_before != vol_after) {
|
||||||
|
return new Tuple<>(
|
||||||
|
(vol_before > 0) ? (fh_before.getFluidInTank(i).getFluid()) : (fh_after.getFluidInTank(i).getFluid()),
|
||||||
|
(vol_before - vol_after)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean manualFluidHandlerInteraction(Player player, InteractionHand hand, Level world, BlockPos pos, @Nullable Direction side) {
|
||||||
|
return FluidUtil.interactWithFluidHandler(player, hand, world, pos, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int fill(Level world, BlockPos pos, Direction side, FluidStack fs, FluidAction action) {
|
||||||
|
IFluidHandler fh = FluidUtil.getFluidHandler(world, pos, side).orElse(null);
|
||||||
|
return (fh == null) ? (0) : (fh.fill(fs, action));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int fill(Level world, BlockPos pos, Direction side, FluidStack fs) {
|
||||||
|
return fill(world, pos, side, fs, FluidAction.EXECUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fluid tank access when itemized.
|
||||||
|
*/
|
||||||
|
public static class FluidContainerItemCapabilityWrapper implements IFluidHandlerItem, ICapabilityProvider {
|
||||||
|
private final LazyOptional<IFluidHandlerItem> handler_ = LazyOptional.of(() -> this);
|
||||||
|
private final Function<ItemStack, CompoundTag> nbt_getter_;
|
||||||
|
private final BiConsumer<ItemStack, CompoundTag> nbt_setter_;
|
||||||
|
private final Predicate<FluidStack> validator_;
|
||||||
|
private final ItemStack container_;
|
||||||
|
private final int capacity_;
|
||||||
|
private final int transfer_rate_;
|
||||||
|
|
||||||
|
public FluidContainerItemCapabilityWrapper(ItemStack container, int capacity, int transfer_rate,
|
||||||
|
Function<ItemStack, CompoundTag> nbt_getter,
|
||||||
|
BiConsumer<ItemStack, CompoundTag> nbt_setter,
|
||||||
|
Predicate<FluidStack> validator) {
|
||||||
|
container_ = container;
|
||||||
|
capacity_ = capacity;
|
||||||
|
transfer_rate_ = transfer_rate;
|
||||||
|
nbt_getter_ = nbt_getter;
|
||||||
|
nbt_setter_ = nbt_setter;
|
||||||
|
validator_ = (validator != null) ? validator : (e -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(Capability<T> capability, @Nullable Direction side) {
|
||||||
|
return (capability == ForgeCapabilities.FLUID_HANDLER) ? handler_.cast() : LazyOptional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FluidStack readnbt() {
|
||||||
|
final CompoundTag nbt = nbt_getter_.apply(container_);
|
||||||
|
return ((nbt == null) || (nbt.isEmpty())) ? FluidStack.EMPTY : FluidStack.loadFluidStackFromNBT(nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writenbt(FluidStack fs) {
|
||||||
|
CompoundTag nbt = new CompoundTag();
|
||||||
|
if (!fs.isEmpty()) fs.writeToNBT(nbt);
|
||||||
|
nbt_setter_.accept(container_, nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getContainer() {
|
||||||
|
return container_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTanks() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack getFluidInTank(int tank) {
|
||||||
|
return readnbt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTankCapacity(int tank) {
|
||||||
|
return capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFluidValid(int tank, FluidStack fs) {
|
||||||
|
return isFluidValid(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFluidValid(FluidStack fs) {
|
||||||
|
return validator_.test(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int fill(FluidStack fs, FluidAction action) {
|
||||||
|
if ((fs.isEmpty()) || (!isFluidValid(fs) || (container_.getCount() != 1))) return 0;
|
||||||
|
FluidStack tank = readnbt();
|
||||||
|
final int amount = Math.min(Math.min(fs.getAmount(), transfer_rate_), capacity_ - tank.getAmount());
|
||||||
|
if (amount <= 0) return 0;
|
||||||
|
if (tank.isEmpty()) {
|
||||||
|
if (action.execute()) {
|
||||||
|
tank = new FluidStack(fs.getFluid(), amount, fs.getTag());
|
||||||
|
writenbt(tank);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!tank.isFluidEqual(fs)) {
|
||||||
|
return 0;
|
||||||
|
} else if (action.execute()) {
|
||||||
|
tank.grow(amount);
|
||||||
|
writenbt(tank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(FluidStack fs, FluidAction action) {
|
||||||
|
if ((fs.isEmpty()) || (container_.getCount() != 1)) return FluidStack.EMPTY;
|
||||||
|
final FluidStack tank = readnbt();
|
||||||
|
if ((!tank.isEmpty()) && (!tank.isFluidEqual(fs))) return FluidStack.EMPTY;
|
||||||
|
return drain(fs.getAmount(), action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FluidStack drain(int max, FluidAction action) {
|
||||||
|
if ((max <= 0) || (container_.getCount() != 1)) return FluidStack.EMPTY;
|
||||||
|
FluidStack tank = readnbt();
|
||||||
|
if (tank.isEmpty()) return FluidStack.EMPTY;
|
||||||
|
final int amount = Math.min(Math.min(tank.getAmount(), max), transfer_rate_);
|
||||||
|
final FluidStack fs = tank.copy();
|
||||||
|
fs.setAmount(amount);
|
||||||
|
if (action.execute()) {
|
||||||
|
tank.shrink(amount);
|
||||||
|
writenbt(tank);
|
||||||
|
}
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
466
src/main/java/dev/zontreck/libzontreck/edlibmc/Guis.java
Normal file
466
src/main/java/dev/zontreck/libzontreck/edlibmc/Guis.java
Normal file
|
@ -0,0 +1,466 @@
|
||||||
|
/*
|
||||||
|
* @file Guis.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Gui Wrappers and Widgets.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.Window;
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
|
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||||
|
import net.minecraft.client.sounds.SoundManager;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
|
||||||
|
public class Guis {
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Gui base
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static abstract class ContainerGui<T extends AbstractContainerMenu> extends AbstractContainerScreen<T> {
|
||||||
|
protected final ResourceLocation background_image_;
|
||||||
|
protected final Player player_;
|
||||||
|
protected final Guis.BackgroundImage gui_background_;
|
||||||
|
protected final TooltipDisplay tooltip_ = new TooltipDisplay();
|
||||||
|
|
||||||
|
public ContainerGui(T menu, Inventory player_inv, Component title, String background_image, int width, int height) {
|
||||||
|
super(menu, player_inv, title);
|
||||||
|
this.background_image_ = new ResourceLocation(Auxiliaries.modid(), background_image);
|
||||||
|
this.player_ = player_inv.player;
|
||||||
|
this.imageWidth = width;
|
||||||
|
this.imageHeight = height;
|
||||||
|
gui_background_ = new Guis.BackgroundImage(background_image_, width, height, Coord2d.ORIGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerGui(T menu, Inventory player_inv, Component title, String background_image) {
|
||||||
|
super(menu, player_inv, title);
|
||||||
|
this.background_image_ = new ResourceLocation(Auxiliaries.modid(), background_image);
|
||||||
|
this.player_ = player_inv.player;
|
||||||
|
gui_background_ = new Guis.BackgroundImage(background_image_, imageWidth, imageHeight, Coord2d.ORIGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
super.init();
|
||||||
|
gui_background_.init(this, Coord2d.ORIGIN).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics mx, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
renderBackground(mx);
|
||||||
|
super.render(mx, mouseX, mouseY, partialTicks);
|
||||||
|
if (!tooltip_.render(mx, this, mouseX, mouseY)) renderTooltip(mx, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderLabels(GuiGraphics mx, int x, int y) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected final void renderBg(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) {
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableDepthTest();
|
||||||
|
gui_background_.draw(mx, this);
|
||||||
|
renderBgWidgets(mx, partialTicks, mouseX, mouseY);
|
||||||
|
RenderSystem.disableBlend();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ResourceLocation getBackgroundImage() {
|
||||||
|
return background_image_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderBgWidgets(GuiGraphics mx, float partialTicks, int mouseX, int mouseY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void renderItemTemplate(GuiGraphics mx, ItemStack stack, int x, int y) {
|
||||||
|
final int x0 = getGuiLeft();
|
||||||
|
final int y0 = getGuiTop();
|
||||||
|
|
||||||
|
mx.renderFakeItem(stack, x0 + x, y0 + y);
|
||||||
|
RenderSystem.disableColorLogicOp(); //RenderSystem.disableColorMaterial();
|
||||||
|
RenderSystem.enableDepthTest(); //RenderSystem.enableAlphaTest();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.colorMask(true, true, true, true);
|
||||||
|
RenderSystem.setShaderColor(0.7f, 0.7f, 0.7f, 0.8f);
|
||||||
|
RenderSystem.setShaderTexture(0, background_image_);
|
||||||
|
mx.blit(background_image_,x0 + x, y0 + y, x, y, 16, 16);
|
||||||
|
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Gui elements
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class Coord2d {
|
||||||
|
public static final Coord2d ORIGIN = new Coord2d(0, 0);
|
||||||
|
public final int x, y;
|
||||||
|
|
||||||
|
public Coord2d(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coord2d of(int x, int y) {
|
||||||
|
return new Coord2d(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "[" + x + "," + y + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class UiWidget extends AbstractWidget {
|
||||||
|
protected static final Component EMPTY_TEXT = Component.literal("");
|
||||||
|
protected static final Function<UiWidget, Component> NO_TOOLTIP = (uiw) -> EMPTY_TEXT;
|
||||||
|
|
||||||
|
private final Minecraft mc_;
|
||||||
|
private Function<UiWidget, Component> tooltip_ = NO_TOOLTIP;
|
||||||
|
private Screen parent_;
|
||||||
|
|
||||||
|
public UiWidget(int x, int y, int width, int height, Component title) {
|
||||||
|
super(x, y, width, height, title);
|
||||||
|
mc_ = Minecraft.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiWidget init(Screen parent) {
|
||||||
|
this.parent_ = parent;
|
||||||
|
this.setX(((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>) parent).getGuiLeft() : 0));
|
||||||
|
this.setY(((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>) parent).getGuiTop() : 0));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiWidget init(Screen parent, Coord2d position) {
|
||||||
|
this.parent_ = parent;
|
||||||
|
this.setX(position.x + ((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>) parent).getGuiLeft() : 0));
|
||||||
|
this.setY(position.y + ((parent instanceof AbstractContainerScreen<?>) ? ((AbstractContainerScreen<?>) parent).getGuiTop() : 0));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final UiWidget tooltip(Function<UiWidget, Component> tip) {
|
||||||
|
tooltip_ = tip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final UiWidget tooltip(Component tip) {
|
||||||
|
tooltip_ = (o) -> tip;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getWidth() {
|
||||||
|
return this.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getHeight() {
|
||||||
|
return this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coord2d getMousePosition() {
|
||||||
|
final Window win = mc_.getWindow();
|
||||||
|
return Coord2d.of(
|
||||||
|
Mth.clamp(((int) (mc_.mouseHandler.xpos() * (double) win.getGuiScaledWidth() / (double) win.getScreenWidth())) - this.getX(), -1, this.width + 1),
|
||||||
|
Mth.clamp(((int) (mc_.mouseHandler.ypos() * (double) win.getGuiScaledHeight() / (double) win.getScreenHeight())) - this.getY(), -1, this.height + 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Coord2d screenCoordinates(Coord2d xy, boolean reverse) {
|
||||||
|
return (reverse) ? (Coord2d.of(xy.x + getX(), xy.y + getY())) : (Coord2d.of(xy.x - getX(), xy.y - getY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiWidget show() {
|
||||||
|
visible = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiWidget hide() {
|
||||||
|
visible = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderWidget(GuiGraphics mxs, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
//super.renderWidget(mxs, mouseX, mouseY, partialTicks);
|
||||||
|
if (isHovered) renderToolTip(mxs, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public void renderToolTip(GuiGraphics mx, int mouseX, int mouseY) {
|
||||||
|
if (!visible || (!active) || (tooltip_ == NO_TOOLTIP)) return;
|
||||||
|
final Component tip = tooltip_.apply(this);
|
||||||
|
if (tip.getString().trim().isEmpty()) return;
|
||||||
|
mx.renderTooltip(mc_.font, Arrays.asList(tip.getVisualOrderText()), mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class HorizontalProgressBar extends UiWidget {
|
||||||
|
private final Coord2d texture_position_base_;
|
||||||
|
private final Coord2d texture_position_filled_;
|
||||||
|
private final ResourceLocation atlas_;
|
||||||
|
private double progress_max_ = 100;
|
||||||
|
private double progress_ = 0;
|
||||||
|
|
||||||
|
public HorizontalProgressBar(ResourceLocation atlas, int width, int height, Coord2d base_texture_xy, Coord2d filled_texture_xy) {
|
||||||
|
super(0, 0, width, height, EMPTY_TEXT);
|
||||||
|
atlas_ = atlas;
|
||||||
|
texture_position_base_ = base_texture_xy;
|
||||||
|
texture_position_filled_ = filled_texture_xy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalProgressBar setProgress(double progress) {
|
||||||
|
progress_ = Mth.clamp(progress, 0, progress_max_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getProgress() {
|
||||||
|
return progress_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalProgressBar setMaxProgress(double progress) {
|
||||||
|
progress_max_ = Math.max(progress, 0);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMaxProgress() {
|
||||||
|
return progress_max_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalProgressBar show() {
|
||||||
|
visible = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalProgressBar hide() {
|
||||||
|
visible = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playDownSound(SoundManager handler) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderWidget(GuiGraphics mxs, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
RenderSystem.setShaderTexture(0, atlas_);
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableDepthTest();
|
||||||
|
mxs.blit(atlas_, getX(), getY(), texture_position_base_.x, texture_position_base_.y, width, height);
|
||||||
|
if ((progress_max_ > 0) && (progress_ > 0)) {
|
||||||
|
int w = Mth.clamp((int) Math.round((progress_ * width) / progress_max_), 0, width);
|
||||||
|
mxs.blit(atlas_, getX(), getY(), texture_position_filled_.x, texture_position_filled_.y, w, height);
|
||||||
|
}
|
||||||
|
if (isHovered) renderToolTip(mxs, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class BackgroundImage extends UiWidget {
|
||||||
|
private final ResourceLocation atlas_;
|
||||||
|
private final Coord2d atlas_position_;
|
||||||
|
public boolean visible;
|
||||||
|
|
||||||
|
public BackgroundImage(ResourceLocation atlas, int width, int height, Coord2d atlas_position) {
|
||||||
|
super(0, 0, width, height, EMPTY_TEXT);
|
||||||
|
atlas_ = atlas;
|
||||||
|
atlas_position_ = atlas_position;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(GuiGraphics mx, Screen parent) {
|
||||||
|
if (!visible) return;
|
||||||
|
RenderSystem.setShaderTexture(0, atlas_);
|
||||||
|
mx.blit(atlas_, getX(), getY(), atlas_position_.x, atlas_position_.y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class CheckBox extends UiWidget {
|
||||||
|
private final Coord2d texture_position_off_;
|
||||||
|
private final Coord2d texture_position_on_;
|
||||||
|
private final ResourceLocation atlas_;
|
||||||
|
private boolean checked_ = false;
|
||||||
|
private Consumer<CheckBox> on_click_ = (checkbox) -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
public CheckBox(ResourceLocation atlas, int width, int height, Coord2d atlas_texture_position_off, Coord2d atlas_texture_position_on) {
|
||||||
|
super(0, 0, width, height, EMPTY_TEXT);
|
||||||
|
texture_position_off_ = atlas_texture_position_off;
|
||||||
|
texture_position_on_ = atlas_texture_position_on;
|
||||||
|
atlas_ = atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checked() {
|
||||||
|
return checked_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckBox checked(boolean on) {
|
||||||
|
checked_ = on;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckBox onclick(Consumer<CheckBox> action) {
|
||||||
|
on_click_ = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(double mouseX, double mouseY) {
|
||||||
|
checked_ = !checked_;
|
||||||
|
on_click_.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderWidget(GuiGraphics mxs, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
|
RenderSystem.setShaderTexture(0, atlas_);
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableDepthTest();
|
||||||
|
Coord2d pos = checked_ ? texture_position_on_ : texture_position_off_;
|
||||||
|
mxs.blit(atlas_, getX(), getY(), pos.x, pos.y, width, height);
|
||||||
|
if (isHovered) renderToolTip(mxs, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class ImageButton extends UiWidget {
|
||||||
|
private final Coord2d texture_position_;
|
||||||
|
private final ResourceLocation atlas_;
|
||||||
|
private Consumer<ImageButton> on_click_ = (bt) -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public ImageButton(ResourceLocation atlas, int width, int height, Coord2d atlas_texture_position) {
|
||||||
|
super(0, 0, width, height, Component.empty());
|
||||||
|
texture_position_ = atlas_texture_position;
|
||||||
|
atlas_ = atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageButton onclick(Consumer<ImageButton> action) {
|
||||||
|
on_click_ = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(double mouseX, double mouseY) {
|
||||||
|
on_click_.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderWidget(GuiGraphics mxs, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
|
RenderSystem.setShaderTexture(0, atlas_);
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableDepthTest();
|
||||||
|
Coord2d pos = texture_position_;
|
||||||
|
mxs.blit(atlas_, getX(), getY(), pos.x, pos.y, width, height);
|
||||||
|
if (isHovered) renderToolTip(mxs, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class Image extends UiWidget {
|
||||||
|
private final Coord2d texture_position_;
|
||||||
|
private final ResourceLocation atlas_;
|
||||||
|
|
||||||
|
public Image(ResourceLocation atlas, int width, int height, Coord2d atlas_texture_position) {
|
||||||
|
super(0, 0, width, height, Component.empty());
|
||||||
|
texture_position_ = atlas_texture_position;
|
||||||
|
atlas_ = atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(double mouseX, double mouseY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderWidget(GuiGraphics mxs, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionTexShader);
|
||||||
|
RenderSystem.setShaderTexture(0, atlas_);
|
||||||
|
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
|
||||||
|
RenderSystem.enableBlend();
|
||||||
|
RenderSystem.defaultBlendFunc();
|
||||||
|
RenderSystem.enableDepthTest();
|
||||||
|
Coord2d pos = texture_position_;
|
||||||
|
mxs.blit(atlas_, getX(), getY(), pos.x, pos.y, width, height);
|
||||||
|
if (isHovered) renderToolTip(mxs, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class TextBox extends net.minecraft.client.gui.components.EditBox {
|
||||||
|
public TextBox(int x, int y, int width, int height, Component title, Font font) {
|
||||||
|
super(font, x, y, width, height, title);
|
||||||
|
setBordered(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextBox withMaxLength(int len) {
|
||||||
|
super.setMaxLength(len);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextBox withBordered(boolean b) {
|
||||||
|
super.setBordered(b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextBox withValue(String s) {
|
||||||
|
super.setValue(s);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextBox withEditable(boolean e) {
|
||||||
|
super.setEditable(e);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextBox withResponder(Consumer<String> r) {
|
||||||
|
super.setResponder(r);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1134
src/main/java/dev/zontreck/libzontreck/edlibmc/Inventories.java
Normal file
1134
src/main/java/dev/zontreck/libzontreck/edlibmc/Inventories.java
Normal file
File diff suppressed because it is too large
Load diff
409
src/main/java/dev/zontreck/libzontreck/edlibmc/Networking.java
Normal file
409
src/main/java/dev/zontreck/libzontreck/edlibmc/Networking.java
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
/*
|
||||||
|
* @file Networking.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Main client/server message handling.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraftforge.common.util.FakePlayer;
|
||||||
|
import net.minecraftforge.network.NetworkDirection;
|
||||||
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
|
import net.minecraftforge.network.NetworkRegistry;
|
||||||
|
import net.minecraftforge.network.simple.SimpleChannel;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
|
public class Networking {
|
||||||
|
private static final String PROTOCOL = "1";
|
||||||
|
private static SimpleChannel DEFAULT_CHANNEL;
|
||||||
|
|
||||||
|
public static void init(String modid) {
|
||||||
|
DEFAULT_CHANNEL = NetworkRegistry.ChannelBuilder
|
||||||
|
.named(new ResourceLocation(modid, "default_ch"))
|
||||||
|
.clientAcceptedVersions(PROTOCOL::equals).serverAcceptedVersions(PROTOCOL::equals).networkProtocolVersion(() -> PROTOCOL)
|
||||||
|
.simpleChannel();
|
||||||
|
int discr = -1;
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyClientToServer.class, PacketTileNotifyClientToServer::compose, PacketTileNotifyClientToServer::parse, PacketTileNotifyClientToServer.Handler::handle);
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, PacketTileNotifyServerToClient.class, PacketTileNotifyServerToClient::compose, PacketTileNotifyServerToClient::parse, PacketTileNotifyServerToClient.Handler::handle);
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncClientToServer.class, PacketContainerSyncClientToServer::compose, PacketContainerSyncClientToServer::parse, PacketContainerSyncClientToServer.Handler::handle);
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, PacketContainerSyncServerToClient.class, PacketContainerSyncServerToClient::compose, PacketContainerSyncServerToClient::parse, PacketContainerSyncServerToClient.Handler::handle);
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, PacketNbtNotifyClientToServer.class, PacketNbtNotifyClientToServer::compose, PacketNbtNotifyClientToServer::parse, PacketNbtNotifyClientToServer.Handler::handle);
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, PacketNbtNotifyServerToClient.class, PacketNbtNotifyServerToClient::compose, PacketNbtNotifyServerToClient::parse, PacketNbtNotifyServerToClient.Handler::handle);
|
||||||
|
DEFAULT_CHANNEL.registerMessage(++discr, OverlayTextMessage.class, OverlayTextMessage::compose, OverlayTextMessage::parse, OverlayTextMessage.Handler::handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Tile entity notifications
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public interface IPacketTileNotifyReceiver {
|
||||||
|
default void onServerPacketReceived(CompoundTag nbt) {
|
||||||
|
}
|
||||||
|
|
||||||
|
default void onClientPacketReceived(Player player, CompoundTag nbt) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketTileNotifyClientToServer {
|
||||||
|
CompoundTag nbt = null;
|
||||||
|
BlockPos pos = BlockPos.ZERO;
|
||||||
|
|
||||||
|
public static void sendToServer(BlockPos pos, CompoundTag nbt) {
|
||||||
|
if ((pos != null) && (nbt != null))
|
||||||
|
DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(pos, nbt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToServer(BlockEntity te, CompoundTag nbt) {
|
||||||
|
if ((te != null) && (nbt != null))
|
||||||
|
DEFAULT_CHANNEL.sendToServer(new PacketTileNotifyClientToServer(te, nbt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketTileNotifyClientToServer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketTileNotifyClientToServer(BlockPos pos, CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketTileNotifyClientToServer(BlockEntity te, CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
pos = te.getBlockPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketTileNotifyClientToServer parse(final FriendlyByteBuf buf) {
|
||||||
|
return new PacketTileNotifyClientToServer(buf.readBlockPos(), buf.readNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final PacketTileNotifyClientToServer pkt, final FriendlyByteBuf buf) {
|
||||||
|
buf.writeBlockPos(pkt.pos);
|
||||||
|
buf.writeNbt(pkt.nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final PacketTileNotifyClientToServer pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
Player player = ctx.get().getSender();
|
||||||
|
if (player == null) return;
|
||||||
|
Level world = player.level();
|
||||||
|
final BlockEntity te = world.getBlockEntity(pkt.pos);
|
||||||
|
if (!(te instanceof IPacketTileNotifyReceiver)) return;
|
||||||
|
((IPacketTileNotifyReceiver) te).onClientPacketReceived(ctx.get().getSender(), pkt.nbt);
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketTileNotifyServerToClient {
|
||||||
|
CompoundTag nbt = null;
|
||||||
|
BlockPos pos = BlockPos.ZERO;
|
||||||
|
|
||||||
|
public static void sendToPlayer(Player player, BlockEntity te, CompoundTag nbt) {
|
||||||
|
if ((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (te == null) || (nbt == null))
|
||||||
|
return;
|
||||||
|
DEFAULT_CHANNEL.sendTo(new PacketTileNotifyServerToClient(te, nbt), ((ServerPlayer) player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToPlayers(BlockEntity te, CompoundTag nbt) {
|
||||||
|
if (te == null || te.getLevel() == null) return;
|
||||||
|
for (Player player : te.getLevel().players()) sendToPlayer(player, te, nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketTileNotifyServerToClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketTileNotifyServerToClient(BlockPos pos, CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketTileNotifyServerToClient(BlockEntity te, CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
pos = te.getBlockPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketTileNotifyServerToClient parse(final FriendlyByteBuf buf) {
|
||||||
|
return new PacketTileNotifyServerToClient(buf.readBlockPos(), buf.readNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final PacketTileNotifyServerToClient pkt, final FriendlyByteBuf buf) {
|
||||||
|
buf.writeBlockPos(pkt.pos);
|
||||||
|
buf.writeNbt(pkt.nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final PacketTileNotifyServerToClient pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
Level world = SidedProxy.getWorldClientSide();
|
||||||
|
if (world == null) return;
|
||||||
|
final BlockEntity te = world.getBlockEntity(pkt.pos);
|
||||||
|
if (!(te instanceof IPacketTileNotifyReceiver)) return;
|
||||||
|
((IPacketTileNotifyReceiver) te).onServerPacketReceived(pkt.nbt);
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
// (GUI) Container synchronization
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public interface INetworkSynchronisableContainer {
|
||||||
|
void onServerPacketReceived(int windowId, CompoundTag nbt);
|
||||||
|
|
||||||
|
void onClientPacketReceived(int windowId, Player player, CompoundTag nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketContainerSyncClientToServer {
|
||||||
|
int id = -1;
|
||||||
|
CompoundTag nbt = null;
|
||||||
|
|
||||||
|
public static void sendToServer(int windowId, CompoundTag nbt) {
|
||||||
|
if (nbt != null) DEFAULT_CHANNEL.sendToServer(new PacketContainerSyncClientToServer(windowId, nbt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToServer(AbstractContainerMenu container, CompoundTag nbt) {
|
||||||
|
if (nbt != null)
|
||||||
|
DEFAULT_CHANNEL.sendToServer(new PacketContainerSyncClientToServer(container.containerId, nbt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketContainerSyncClientToServer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketContainerSyncClientToServer(int id, CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketContainerSyncClientToServer parse(final FriendlyByteBuf buf) {
|
||||||
|
return new PacketContainerSyncClientToServer(buf.readInt(), buf.readNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final PacketContainerSyncClientToServer pkt, final FriendlyByteBuf buf) {
|
||||||
|
buf.writeInt(pkt.id);
|
||||||
|
buf.writeNbt(pkt.nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final PacketContainerSyncClientToServer pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
Player player = ctx.get().getSender();
|
||||||
|
if ((player == null) || !(player.containerMenu instanceof INetworkSynchronisableContainer)) return;
|
||||||
|
if (player.containerMenu.containerId != pkt.id) return;
|
||||||
|
((INetworkSynchronisableContainer) player.containerMenu).onClientPacketReceived(pkt.id, player, pkt.nbt);
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketContainerSyncServerToClient {
|
||||||
|
int id = -1;
|
||||||
|
CompoundTag nbt = null;
|
||||||
|
|
||||||
|
public static void sendToPlayer(Player player, int windowId, CompoundTag nbt) {
|
||||||
|
if ((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (nbt == null)) return;
|
||||||
|
DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(windowId, nbt), ((ServerPlayer) player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToPlayer(Player player, AbstractContainerMenu container, CompoundTag nbt) {
|
||||||
|
if ((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (nbt == null)) return;
|
||||||
|
DEFAULT_CHANNEL.sendTo(new PacketContainerSyncServerToClient(container.containerId, nbt), ((ServerPlayer) player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <C extends AbstractContainerMenu & INetworkSynchronisableContainer>
|
||||||
|
void sendToListeners(Level world, C container, CompoundTag nbt) {
|
||||||
|
for (Player player : world.players()) {
|
||||||
|
if (player.containerMenu.containerId != container.containerId) continue;
|
||||||
|
sendToPlayer(player, container.containerId, nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketContainerSyncServerToClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketContainerSyncServerToClient(int id, CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketContainerSyncServerToClient parse(final FriendlyByteBuf buf) {
|
||||||
|
return new PacketContainerSyncServerToClient(buf.readInt(), buf.readNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final PacketContainerSyncServerToClient pkt, final FriendlyByteBuf buf) {
|
||||||
|
buf.writeInt(pkt.id);
|
||||||
|
buf.writeNbt(pkt.nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final PacketContainerSyncServerToClient pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
Player player = SidedProxy.getPlayerClientSide();
|
||||||
|
if ((player == null) || !(player.containerMenu instanceof INetworkSynchronisableContainer)) return;
|
||||||
|
if (player.containerMenu.containerId != pkt.id) return;
|
||||||
|
((INetworkSynchronisableContainer) player.containerMenu).onServerPacketReceived(pkt.id, pkt.nbt);
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
// World notifications
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static class PacketNbtNotifyClientToServer {
|
||||||
|
public static final Map<String, BiConsumer<Player, CompoundTag>> handlers = new HashMap<>();
|
||||||
|
final CompoundTag nbt;
|
||||||
|
|
||||||
|
public static void sendToServer(CompoundTag nbt) {
|
||||||
|
if (nbt != null) DEFAULT_CHANNEL.sendToServer(new PacketNbtNotifyClientToServer(nbt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketNbtNotifyClientToServer(CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketNbtNotifyClientToServer parse(final FriendlyByteBuf buf) {
|
||||||
|
return new PacketNbtNotifyClientToServer(buf.readNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final PacketNbtNotifyClientToServer pkt, final FriendlyByteBuf buf) {
|
||||||
|
buf.writeNbt(pkt.nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final PacketNbtNotifyClientToServer pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
final ServerPlayer player = ctx.get().getSender();
|
||||||
|
if (player == null) return;
|
||||||
|
final String hnd = pkt.nbt.getString("hnd");
|
||||||
|
if (hnd.isEmpty()) return;
|
||||||
|
if (handlers.containsKey(hnd)) handlers.get(hnd).accept(player, pkt.nbt);
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketNbtNotifyServerToClient {
|
||||||
|
public static final Map<String, Consumer<CompoundTag>> handlers = new HashMap<>();
|
||||||
|
final CompoundTag nbt;
|
||||||
|
|
||||||
|
public static void sendToPlayer(Player player, CompoundTag nbt) {
|
||||||
|
if ((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || (nbt == null)) return;
|
||||||
|
DEFAULT_CHANNEL.sendTo(new PacketNbtNotifyServerToClient(nbt), ((ServerPlayer) player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToPlayers(Level world, CompoundTag nbt) {
|
||||||
|
for (Player player : world.players()) sendToPlayer(player, nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketNbtNotifyServerToClient(CompoundTag nbt) {
|
||||||
|
this.nbt = nbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PacketNbtNotifyServerToClient parse(final FriendlyByteBuf buf) {
|
||||||
|
return new PacketNbtNotifyServerToClient(buf.readNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final PacketNbtNotifyServerToClient pkt, final FriendlyByteBuf buf) {
|
||||||
|
buf.writeNbt(pkt.nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final PacketNbtNotifyServerToClient pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
ctx.get().enqueueWork(() -> {
|
||||||
|
final String hnd = pkt.nbt.getString("hnd");
|
||||||
|
if (hnd.isEmpty()) return;
|
||||||
|
if (handlers.containsKey(hnd)) handlers.get(hnd).accept(pkt.nbt);
|
||||||
|
});
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Main window GUI text message
|
||||||
|
//--------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static class OverlayTextMessage {
|
||||||
|
public static final int DISPLAY_TIME_MS = 3000;
|
||||||
|
private static BiConsumer<Component, Integer> handler_ = null;
|
||||||
|
private final Component data_;
|
||||||
|
private int delay_ = DISPLAY_TIME_MS;
|
||||||
|
|
||||||
|
private Component data() {
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int delay() {
|
||||||
|
return delay_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setHandler(BiConsumer<Component, Integer> handler) {
|
||||||
|
if (handler_ == null) handler_ = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendToPlayer(Player player, Component message, int delay) {
|
||||||
|
if ((!(player instanceof ServerPlayer)) || (player instanceof FakePlayer) || Auxiliaries.isEmpty(message))
|
||||||
|
return;
|
||||||
|
DEFAULT_CHANNEL.sendTo(new OverlayTextMessage(message, delay), ((ServerPlayer) player).connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverlayTextMessage() {
|
||||||
|
data_ = Component.translatable("[unset]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public OverlayTextMessage(final Component tct, int delay) {
|
||||||
|
data_ = tct.copy();
|
||||||
|
delay_ = delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OverlayTextMessage parse(final FriendlyByteBuf buf) {
|
||||||
|
try {
|
||||||
|
return new OverlayTextMessage(buf.readComponent(), DISPLAY_TIME_MS);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return new OverlayTextMessage(Component.translatable("[incorrect translation]"), DISPLAY_TIME_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void compose(final OverlayTextMessage pkt, final FriendlyByteBuf buf) {
|
||||||
|
try {
|
||||||
|
buf.writeComponent(pkt.data());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Auxiliaries.logger().error("OverlayTextMessage.toBytes() failed: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler {
|
||||||
|
public static void handle(final OverlayTextMessage pkt, final Supplier<NetworkEvent.Context> ctx) {
|
||||||
|
if (handler_ != null) ctx.get().enqueueWork(() -> handler_.accept(pkt.data(), pkt.delay()));
|
||||||
|
ctx.get().setPacketHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* @file OptionalRecipeCondition.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Recipe condition to enable opt'ing out JSON based recipes.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.GsonHelper;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraftforge.common.crafting.conditions.ICondition;
|
||||||
|
import net.minecraftforge.common.crafting.conditions.IConditionSerializer;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.IForgeRegistry;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
|
||||||
|
public class OptionalRecipeCondition implements ICondition {
|
||||||
|
private static ResourceLocation NAME;
|
||||||
|
|
||||||
|
private final List<ResourceLocation> all_required;
|
||||||
|
private final List<ResourceLocation> any_missing;
|
||||||
|
private final List<ResourceLocation> all_required_tags;
|
||||||
|
private final List<ResourceLocation> any_missing_tags;
|
||||||
|
private final @Nullable ResourceLocation result;
|
||||||
|
private final boolean result_is_tag;
|
||||||
|
private final boolean experimental;
|
||||||
|
|
||||||
|
private static boolean with_experimental = false;
|
||||||
|
private static boolean without_recipes = false;
|
||||||
|
private static Predicate<Block> block_optouts = (block) -> false;
|
||||||
|
private static Predicate<Item> item_optouts = (item) -> false;
|
||||||
|
|
||||||
|
public static void init(String modid, Logger logger) {
|
||||||
|
NAME = new ResourceLocation(modid, "optional");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void on_config(boolean enable_experimental, boolean disable_all_recipes,
|
||||||
|
Predicate<Block> block_optout_provider,
|
||||||
|
Predicate<Item> item_optout_provider) {
|
||||||
|
with_experimental = enable_experimental;
|
||||||
|
without_recipes = disable_all_recipes;
|
||||||
|
block_optouts = block_optout_provider;
|
||||||
|
item_optouts = item_optout_provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OptionalRecipeCondition(ResourceLocation result, List<ResourceLocation> required, List<ResourceLocation> missing, List<ResourceLocation> required_tags, List<ResourceLocation> missing_tags, boolean isexperimental, boolean result_is_tag) {
|
||||||
|
all_required = required;
|
||||||
|
any_missing = missing;
|
||||||
|
all_required_tags = required_tags;
|
||||||
|
any_missing_tags = missing_tags;
|
||||||
|
this.result = result;
|
||||||
|
this.result_is_tag = result_is_tag;
|
||||||
|
experimental = isexperimental;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getID() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Optional recipe, all-required: [");
|
||||||
|
for (ResourceLocation e : all_required) sb.append(e.toString()).append(",");
|
||||||
|
for (ResourceLocation e : all_required_tags) sb.append("#").append(e.toString()).append(",");
|
||||||
|
sb.delete(sb.length() - 1, sb.length()).append("], any-missing: [");
|
||||||
|
for (ResourceLocation e : any_missing) sb.append(e.toString()).append(",");
|
||||||
|
for (ResourceLocation e : any_missing_tags) sb.append("#").append(e.toString()).append(",");
|
||||||
|
sb.delete(sb.length() - 1, sb.length()).append("]");
|
||||||
|
if (experimental) sb.append(" EXPERIMENTAL");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(IContext context) {
|
||||||
|
if (without_recipes) return false;
|
||||||
|
if ((experimental) && (!with_experimental)) return false;
|
||||||
|
final IForgeRegistry<Item> item_registry = ForgeRegistries.ITEMS;
|
||||||
|
//final Collection<ResourceLocation> item_tags = SerializationTags.getInstance().getOrEmpty(Registry.ITEM_REGISTRY).getAvailableTags();
|
||||||
|
if (result != null) {
|
||||||
|
boolean item_registered = item_registry.containsKey(result);
|
||||||
|
if (!item_registered) return false; // required result not registered
|
||||||
|
if (item_optouts.test(item_registry.getValue(result))) return false;
|
||||||
|
if (ForgeRegistries.BLOCKS.containsKey(result) && block_optouts.test(ForgeRegistries.BLOCKS.getValue(result)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!all_required.isEmpty()) {
|
||||||
|
for (ResourceLocation rl : all_required) {
|
||||||
|
if (!item_registry.containsKey(rl)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!all_required_tags.isEmpty()) {
|
||||||
|
for (ResourceLocation rl : all_required_tags) {
|
||||||
|
if (item_registry.tags().getTagNames().noneMatch(tk -> tk.location().equals(rl)))
|
||||||
|
return false; // if(!item_tags.contains(rl)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!any_missing.isEmpty()) {
|
||||||
|
for (ResourceLocation rl : any_missing) {
|
||||||
|
if (!item_registry.containsKey(rl)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!any_missing_tags.isEmpty()) {
|
||||||
|
for (ResourceLocation rl : any_missing_tags) {
|
||||||
|
if (item_registry.tags().getTagNames().noneMatch(tk -> tk.location().equals(rl)))
|
||||||
|
return true; // if(!item_tags.contains(rl)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Serializer implements IConditionSerializer<OptionalRecipeCondition> {
|
||||||
|
public static final Serializer INSTANCE = new Serializer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getID() {
|
||||||
|
return OptionalRecipeCondition.NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonObject json, OptionalRecipeCondition condition) {
|
||||||
|
JsonArray required = new JsonArray();
|
||||||
|
JsonArray missing = new JsonArray();
|
||||||
|
for (ResourceLocation e : condition.all_required) required.add(e.toString());
|
||||||
|
for (ResourceLocation e : condition.any_missing) missing.add(e.toString());
|
||||||
|
json.add("required", required);
|
||||||
|
json.add("missing", missing);
|
||||||
|
if (condition.result != null) {
|
||||||
|
json.addProperty("result", (condition.result_is_tag ? "#" : "") + condition.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OptionalRecipeCondition read(JsonObject json) {
|
||||||
|
List<ResourceLocation> required = new ArrayList<>();
|
||||||
|
List<ResourceLocation> missing = new ArrayList<>();
|
||||||
|
List<ResourceLocation> required_tags = new ArrayList<>();
|
||||||
|
List<ResourceLocation> missing_tags = new ArrayList<>();
|
||||||
|
ResourceLocation result = null;
|
||||||
|
boolean experimental = false;
|
||||||
|
boolean result_is_tag = false;
|
||||||
|
if (json.has("result")) {
|
||||||
|
String s = json.get("result").getAsString();
|
||||||
|
if (s.startsWith("#")) {
|
||||||
|
result = new ResourceLocation(s.substring(1));
|
||||||
|
result_is_tag = true;
|
||||||
|
} else {
|
||||||
|
result = new ResourceLocation(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json.has("required")) {
|
||||||
|
for (JsonElement e : GsonHelper.getAsJsonArray(json, "required")) {
|
||||||
|
String s = e.getAsString();
|
||||||
|
if (s.startsWith("#")) {
|
||||||
|
required_tags.add(new ResourceLocation(s.substring(1)));
|
||||||
|
} else {
|
||||||
|
required.add(new ResourceLocation(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json.has("missing")) {
|
||||||
|
for (JsonElement e : GsonHelper.getAsJsonArray(json, "missing")) {
|
||||||
|
String s = e.getAsString();
|
||||||
|
if (s.startsWith("#")) {
|
||||||
|
missing_tags.add(new ResourceLocation(s.substring(1)));
|
||||||
|
} else {
|
||||||
|
missing.add(new ResourceLocation(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json.has("experimental")) experimental = json.get("experimental").getAsBoolean();
|
||||||
|
return new OptionalRecipeCondition(result, required, missing, required_tags, missing_tags, experimental, result_is_tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
src/main/java/dev/zontreck/libzontreck/edlibmc/Overlay.java
Normal file
165
src/main/java/dev/zontreck/libzontreck/edlibmc/Overlay.java
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* @file Overlay.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Renders status messages in one line.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.Window;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.util.Tuple;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
|
public class Overlay {
|
||||||
|
public static void show(Player player, final Component message) {
|
||||||
|
Networking.OverlayTextMessage.sendToPlayer(player, message, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(Player player, final Component message, int delay) {
|
||||||
|
Networking.OverlayTextMessage.sendToPlayer(player, message, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(BlockState state, BlockPos pos) {
|
||||||
|
show(state, pos, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(BlockState state, BlockPos pos, int displayTimeoutMs) {
|
||||||
|
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> (() -> TextOverlayGui.show(state, pos, displayTimeoutMs)));
|
||||||
|
} // Only called when client side
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Client side handler
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public static class TextOverlayGui extends Screen {
|
||||||
|
public static final TextOverlayGui INSTANCE = new TextOverlayGui();
|
||||||
|
private static final Component EMPTY_TEXT = Component.literal("");
|
||||||
|
private static final BlockState EMPTY_STATE = null;
|
||||||
|
private static double overlay_y_ = 0.75;
|
||||||
|
private static int text_color_ = 0x00ffaa00;
|
||||||
|
private static int border_color_ = 0xaa333333;
|
||||||
|
private static int background_color1_ = 0xaa333333;
|
||||||
|
private static int background_color2_ = 0xaa444444;
|
||||||
|
private static long text_deadline_ = 0;
|
||||||
|
private static Component text_ = EMPTY_TEXT;
|
||||||
|
private static long state_deadline_ = 0;
|
||||||
|
private static @Nullable BlockState state_ = EMPTY_STATE;
|
||||||
|
private static BlockPos pos_ = BlockPos.ZERO;
|
||||||
|
|
||||||
|
public static void on_config(double overlay_y) {
|
||||||
|
on_config(overlay_y, 0x00ffaa00, 0xaa333333, 0xaa333333, 0xaa444444);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void on_config(double overlay_y, int text_color, int border_color, int background_color1, int background_color2) {
|
||||||
|
overlay_y_ = overlay_y;
|
||||||
|
text_color_ = text_color;
|
||||||
|
border_color_ = border_color;
|
||||||
|
background_color1_ = background_color1;
|
||||||
|
background_color2_ = background_color2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized Component text() {
|
||||||
|
return text_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized long deadline() {
|
||||||
|
return text_deadline_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void hide() {
|
||||||
|
text_deadline_ = 0;
|
||||||
|
text_ = EMPTY_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void show(Component s, int displayTimeoutMs) {
|
||||||
|
text_ = (s == null) ? (EMPTY_TEXT) : (s.copy());
|
||||||
|
text_deadline_ = System.currentTimeMillis() + displayTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void show(String s, int displayTimeoutMs) {
|
||||||
|
text_ = ((s == null) || (s.isEmpty())) ? (EMPTY_TEXT) : (Component.literal(s));
|
||||||
|
text_deadline_ = System.currentTimeMillis() + displayTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void show(BlockState state, BlockPos pos, int displayTimeoutMs) {
|
||||||
|
pos_ = new BlockPos(pos);
|
||||||
|
state_ = state;
|
||||||
|
state_deadline_ = System.currentTimeMillis() + displayTimeoutMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Optional<Tuple<BlockState, BlockPos>> state_pos() {
|
||||||
|
return ((state_deadline_ < System.currentTimeMillis()) || (state_ == EMPTY_STATE)) ? Optional.empty() : Optional.of(new Tuple<>(state_, pos_));
|
||||||
|
}
|
||||||
|
|
||||||
|
TextOverlayGui() {
|
||||||
|
super(Component.literal(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRenderGui(final GuiGraphics mxs) {
|
||||||
|
if (deadline() < System.currentTimeMillis()) return;
|
||||||
|
if (text() == EMPTY_TEXT) return;
|
||||||
|
String txt = text().getString();
|
||||||
|
if (txt.isEmpty()) return;
|
||||||
|
final net.minecraft.client.Minecraft mc = net.minecraft.client.Minecraft.getInstance();
|
||||||
|
final Window win = mc.getWindow();
|
||||||
|
final Font fr = mc.font;
|
||||||
|
final boolean was_unicode = fr.isBidirectional();
|
||||||
|
final int cx = win.getGuiScaledWidth() / 2;
|
||||||
|
final int cy = (int) (win.getGuiScaledHeight() * overlay_y_);
|
||||||
|
final int w = fr.width(txt);
|
||||||
|
final int h = fr.lineHeight;
|
||||||
|
mxs.fillGradient(cx - (w / 2) - 3, cy - 2, cx + (w / 2) + 2, cy + h + 2, 0xaa333333, 0xaa444444);
|
||||||
|
mxs.hLine(cx - (w / 2) - 3, cx + (w / 2) + 2, cy - 2, 0xaa333333);
|
||||||
|
mxs.hLine(cx - (w / 2) - 3, cx + (w / 2) + 2, cy + h + 2, 0xaa333333);
|
||||||
|
mxs.vLine(cx - (w / 2) - 3, cy - 2, cy + h + 2, 0xaa333333);
|
||||||
|
mxs.vLine(cx + (w / 2) + 2, cy - 2, cy + h + 2, 0xaa333333);
|
||||||
|
mxs.drawCenteredString(fr, text(), cx, cy + 1, 0x00ffaa00);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void onRenderWorldOverlay(final com.mojang.blaze3d.vertex.PoseStack mxs, final double partialTick) {
|
||||||
|
final Optional<Tuple<BlockState, BlockPos>> sp = state_pos();
|
||||||
|
if (sp.isEmpty()) return;
|
||||||
|
final ClientLevel world = Minecraft.getInstance().level;
|
||||||
|
final LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
if ((player == null) || (world == null)) return;
|
||||||
|
final BlockState state = sp.get().getA();
|
||||||
|
final BlockPos pos = sp.get().getB();
|
||||||
|
final int light = (world.hasChunkAt(pos)) ? LightTexture.pack(world.getBrightness(LightLayer.BLOCK, pos), world.getBrightness(LightLayer.SKY, pos)) : LightTexture.pack(15, 15);
|
||||||
|
final MultiBufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();
|
||||||
|
final double px = Mth.lerp(partialTick, player.xo, player.getX());
|
||||||
|
final double py = Mth.lerp(partialTick, player.yo, player.getY());
|
||||||
|
final double pz = Mth.lerp(partialTick, player.zo, player.getZ());
|
||||||
|
mxs.pushPose();
|
||||||
|
mxs.translate((pos.getX() - px), (pos.getY() - py - player.getEyeHeight()), (pos.getZ() - pz));
|
||||||
|
Minecraft.getInstance().getBlockRenderer().renderSingleBlock(state, mxs, buffer, light, OverlayTexture.NO_OVERLAY);
|
||||||
|
mxs.popPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
src/main/java/dev/zontreck/libzontreck/edlibmc/README.md
Normal file
4
src/main/java/dev/zontreck/libzontreck/edlibmc/README.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Engineer's Decor LibMC
|
||||||
|
____________
|
||||||
|
|
||||||
|
As Engineer's Decor has been abandoned and discontinued, i've adopted the LibMC, and split the mod up among my various mods. Engineer's Decor blocks, and items, will now reside in Thresholds.
|
263
src/main/java/dev/zontreck/libzontreck/edlibmc/Registries.java
Normal file
263
src/main/java/dev/zontreck/libzontreck/edlibmc/Registries.java
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* @file Registries.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Common game registry handling.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.flag.FeatureFlagSet;
|
||||||
|
import net.minecraft.world.flag.FeatureFlags;
|
||||||
|
import net.minecraft.world.inventory.MenuType;
|
||||||
|
import net.minecraft.world.item.BlockItem;
|
||||||
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraftforge.eventbus.api.IEventBus;
|
||||||
|
import net.minecraftforge.registries.DeferredRegister;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Registries {
|
||||||
|
private static String modid = null;
|
||||||
|
private static String creative_tab_icon = "";
|
||||||
|
private static CreativeModeTab creative_tab = null;
|
||||||
|
private static final Map<String, TagKey<Block>> registered_block_tag_keys = new HashMap<>();
|
||||||
|
private static final Map<String, TagKey<Item>> registered_item_tag_keys = new HashMap<>();
|
||||||
|
|
||||||
|
private static final Map<String, RegistryObject<Block>> registered_blocks = new HashMap<>();
|
||||||
|
private static final Map<String, RegistryObject<Item>> registered_items = new HashMap<>();
|
||||||
|
private static final Map<String, RegistryObject<BlockEntityType<?>>> registered_block_entity_types = new HashMap<>();
|
||||||
|
private static final Map<String, RegistryObject<EntityType<?>>> registered_entity_types = new HashMap<>();
|
||||||
|
private static final Map<String, RegistryObject<MenuType<?>>> registered_menu_types = new HashMap<>();
|
||||||
|
private static final Map<String, RegistryObject<RecipeSerializer<?>>> recipe_serializers = new HashMap<>();
|
||||||
|
private static final Map<String, RegistryObject<CreativeModeTab>> registered_tabs = new HashMap<>();
|
||||||
|
|
||||||
|
public static final List<Supplier<? extends ItemLike>> tabItems = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static DeferredRegister<Block> BLOCKS;
|
||||||
|
private static DeferredRegister<Item> ITEMS;
|
||||||
|
private static DeferredRegister<CreativeModeTab> TABS;
|
||||||
|
private static DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES;
|
||||||
|
private static DeferredRegister<MenuType<?>> MENUS;
|
||||||
|
private static DeferredRegister<EntityType<?>> ENTITIES;
|
||||||
|
private static DeferredRegister<RecipeSerializer<?>> RECIPE_SERIALIZERS;
|
||||||
|
private static List<DeferredRegister<?>> MOD_REGISTRIES;
|
||||||
|
|
||||||
|
public static void init(String mod_id, String creative_tab_icon_item_name, IEventBus bus) {
|
||||||
|
modid = mod_id;
|
||||||
|
creative_tab_icon = creative_tab_icon_item_name;
|
||||||
|
BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, modid);
|
||||||
|
ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, modid);
|
||||||
|
BLOCK_ENTITIES = DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, modid);
|
||||||
|
MENUS = DeferredRegister.create(ForgeRegistries.MENU_TYPES, modid);
|
||||||
|
ENTITIES = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, modid);
|
||||||
|
RECIPE_SERIALIZERS = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, modid);
|
||||||
|
TABS = DeferredRegister.create(net.minecraft.core.registries.Registries.CREATIVE_MODE_TAB, modid);
|
||||||
|
|
||||||
|
Consumer<DeferredRegister<?>> consumer = (X)->{
|
||||||
|
X.register(bus);
|
||||||
|
};
|
||||||
|
|
||||||
|
List.of(BLOCKS, ITEMS, BLOCK_ENTITIES, MENUS, ENTITIES, RECIPE_SERIALIZERS, TABS).forEach(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static Block getBlock(String block_name) {
|
||||||
|
return registered_blocks.get(block_name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Item> RegistryObject<T> withTab(RegistryObject<T> item)
|
||||||
|
{
|
||||||
|
tabItems.add(item);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item getItem(String name) {
|
||||||
|
return registered_items.get(name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntityType<?> getEntityType(String name) {
|
||||||
|
return registered_entity_types.get(name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockEntityType<?> getBlockEntityType(String block_name) {
|
||||||
|
return registered_block_entity_types.get(block_name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MenuType<?> getMenuType(String name) {
|
||||||
|
return registered_menu_types.get(name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RecipeSerializer<?> getRecipeSerializer(String name) {
|
||||||
|
return recipe_serializers.get(name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockEntityType<?> getBlockEntityTypeOfBlock(String block_name) {
|
||||||
|
return getBlockEntityType("tet_" + block_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockEntityType<?> getBlockEntityTypeOfBlock(Block block) {
|
||||||
|
return getBlockEntityTypeOfBlock(ForgeRegistries.BLOCKS.getKey(block).getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MenuType<?> getMenuTypeOfBlock(String name) {
|
||||||
|
return getMenuType("ct_" + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MenuType<?> getMenuTypeOfBlock(Block block) {
|
||||||
|
return getMenuTypeOfBlock(ForgeRegistries.BLOCKS.getKey(block).getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TagKey<Block> getBlockTagKey(String name) {
|
||||||
|
return registered_block_tag_keys.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TagKey<Item> getItemTagKey(String name) {
|
||||||
|
return registered_item_tag_keys.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static List<Block> getRegisteredBlocks() {
|
||||||
|
return Collections.unmodifiableList(registered_blocks.values().stream().map(RegistryObject::get).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static List<Item> getRegisteredItems() {
|
||||||
|
return Collections.unmodifiableList(registered_items.values().stream().map(RegistryObject::get).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static List<BlockEntityType<?>> getRegisteredBlockEntityTypes() {
|
||||||
|
return Collections.unmodifiableList(registered_block_entity_types.values().stream().map(RegistryObject::get).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static List<EntityType<?>> getRegisteredEntityTypes() {
|
||||||
|
return Collections.unmodifiableList(registered_entity_types.values().stream().map(RegistryObject::get).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static <T extends Item> void addItem(String registry_name, Supplier<T> supplier) {
|
||||||
|
registered_items.put(registry_name, withTab(ITEMS.register(registry_name, supplier)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Block> void addBlock(String registry_name, Supplier<T> block_supplier) {
|
||||||
|
registered_blocks.put(registry_name, BLOCKS.register(registry_name, block_supplier));
|
||||||
|
registered_items.put(registry_name, withTab(ITEMS.register(registry_name, () -> new BlockItem(registered_blocks.get(registry_name).get(), (new Item.Properties())))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends CreativeModeTab> void addCreativeModeTab(String id, Component title, ItemStack icon)
|
||||||
|
{
|
||||||
|
registered_tabs.put(id, TABS.register(id, ()-> CreativeModeTab
|
||||||
|
.builder()
|
||||||
|
.icon(()->icon)
|
||||||
|
.title(title)
|
||||||
|
.withSearchBar()
|
||||||
|
|
||||||
|
.displayItems((display, output) -> tabItems.forEach(it->output.accept(it.get())))
|
||||||
|
|
||||||
|
.build()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <TB extends Block, TI extends Item> void addBlock(String registry_name, Supplier<TB> block_supplier, Supplier<TI> item_supplier) {
|
||||||
|
registered_blocks.put(registry_name, BLOCKS.register(registry_name, block_supplier));
|
||||||
|
registered_items.put(registry_name, ITEMS.register(registry_name, item_supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends BlockEntity> void addBlockEntityType(String registry_name, BlockEntityType.BlockEntitySupplier<T> ctor, String... block_names) {
|
||||||
|
registered_block_entity_types.put(registry_name, BLOCK_ENTITIES.register(registry_name, () -> {
|
||||||
|
final Block[] blocks = Arrays.stream(block_names).map(s -> {
|
||||||
|
Block b = BLOCKS.getEntries().stream().filter((ro) -> ro.getId().getPath().equals(s)).findFirst().map(RegistryObject::get).orElse(null);
|
||||||
|
if (b == null) Auxiliaries.logError("registered_blocks does not encompass '" + s + "'");
|
||||||
|
return b;
|
||||||
|
}).filter(Objects::nonNull).collect(Collectors.toList()).toArray(new Block[]{});
|
||||||
|
return BlockEntityType.Builder.of(ctor, blocks).build(null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends EntityType<?>> void addEntityType(String registry_name, Supplier<EntityType<?>> supplier) {
|
||||||
|
registered_entity_types.put(registry_name, ENTITIES.register(registry_name, supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends MenuType<?>> void addMenuType(String registry_name, MenuType.MenuSupplier<?> supplier, FeatureFlagSet flags) {
|
||||||
|
registered_menu_types.put(registry_name, MENUS.register(registry_name, () -> new MenuType<>(supplier, flags)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addRecipeSerializer(String registry_name, Supplier<? extends RecipeSerializer<?>> serializer_supplier) {
|
||||||
|
recipe_serializers.put(registry_name, RECIPE_SERIALIZERS.register(registry_name, serializer_supplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addOptionalBlockTag(String tag_name, ResourceLocation... default_blocks) {
|
||||||
|
final Set<Supplier<Block>> default_suppliers = new HashSet<>();
|
||||||
|
for (ResourceLocation rl : default_blocks) default_suppliers.add(() -> ForgeRegistries.BLOCKS.getValue(rl));
|
||||||
|
final TagKey<Block> key = ForgeRegistries.BLOCKS.tags().createOptionalTagKey(new ResourceLocation(modid, tag_name), default_suppliers);
|
||||||
|
registered_block_tag_keys.put(tag_name, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addOptionaItemTag(String tag_name, ResourceLocation... default_items) {
|
||||||
|
final Set<Supplier<Item>> default_suppliers = new HashSet<>();
|
||||||
|
for (ResourceLocation rl : default_items) default_suppliers.add(() -> ForgeRegistries.ITEMS.getValue(rl));
|
||||||
|
final TagKey<Item> key = ForgeRegistries.ITEMS.tags().createOptionalTagKey(new ResourceLocation(modid, tag_name), default_suppliers);
|
||||||
|
registered_item_tag_keys.put(tag_name, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
/**
|
||||||
|
* This function is to be removed as it cannot add items to a tab anymore
|
||||||
|
*/
|
||||||
|
public static <TB extends Block, TI extends Item> void addBlock(String registry_name, Supplier<TB> block_supplier, BiFunction<Block, Item.Properties, Item> item_builder) {
|
||||||
|
addBlock(registry_name, block_supplier, () -> item_builder.apply(registered_blocks.get(registry_name).get(), (new Item.Properties())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addBlock(String registry_name, Supplier<? extends Block> block_supplier, BlockEntityType.BlockEntitySupplier<?> block_entity_ctor) {
|
||||||
|
addBlock(registry_name, block_supplier);
|
||||||
|
addBlockEntityType("tet_" + registry_name, block_entity_ctor, registry_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addBlock(String registry_name, Supplier<? extends Block> block_supplier, BiFunction<Block, Item.Properties, Item> item_builder, BlockEntityType.BlockEntitySupplier<?> block_entity_ctor) {
|
||||||
|
addBlock(registry_name, block_supplier, item_builder);
|
||||||
|
addBlockEntityType("tet_" + registry_name, block_entity_ctor, registry_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addBlock(String registry_name, Supplier<? extends Block> block_supplier, BiFunction<Block, Item.Properties, Item> item_builder, BlockEntityType.BlockEntitySupplier<?> block_entity_ctor, MenuType.MenuSupplier<?> menu_type_supplier, FeatureFlagSet menuFlags) {
|
||||||
|
addBlock(registry_name, block_supplier, item_builder);
|
||||||
|
addBlockEntityType("tet_" + registry_name, block_entity_ctor, registry_name);
|
||||||
|
addMenuType("ct_" + registry_name, menu_type_supplier, menuFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addBlock(String registry_name, Supplier<? extends Block> block_supplier, BlockEntityType.BlockEntitySupplier<?> block_entity_ctor, MenuType.MenuSupplier<?> menu_type_supplier, FeatureFlagSet menuFlags) {
|
||||||
|
addBlock(registry_name, block_supplier, block_entity_ctor);
|
||||||
|
addMenuType("ct_" + registry_name, menu_type_supplier, menuFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
180
src/main/java/dev/zontreck/libzontreck/edlibmc/RfEnergy.java
Normal file
180
src/main/java/dev/zontreck/libzontreck/edlibmc/RfEnergy.java
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* @file RfEnergy.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* General RF/FE energy handling functionality.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import net.minecraftforge.energy.IEnergyStorage;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class RfEnergy {
|
||||||
|
public static int feed(Level world, BlockPos pos, @Nullable Direction side, int rf_energy) {
|
||||||
|
final BlockEntity te = world.getBlockEntity(pos);
|
||||||
|
if (te == null) return 0;
|
||||||
|
final IEnergyStorage es = te.getCapability(ForgeCapabilities.ENERGY, side).orElse(null);
|
||||||
|
if (es == null) return 0;
|
||||||
|
return es.receiveEnergy(rf_energy, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Battery implements IEnergyStorage {
|
||||||
|
protected int capacity_;
|
||||||
|
protected int charge_rate_;
|
||||||
|
protected int discharge_rate_;
|
||||||
|
protected int energy_;
|
||||||
|
|
||||||
|
public Battery(int capacity) {
|
||||||
|
this(capacity, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery(int capacity, int transfer_rate) {
|
||||||
|
this(capacity, transfer_rate, transfer_rate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery(int capacity, int charge_rate, int discharge_rate) {
|
||||||
|
this(capacity, charge_rate, discharge_rate, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery(int capacity, int charge_rate, int discharge_rate, int energy) {
|
||||||
|
capacity_ = Math.max(capacity, 1);
|
||||||
|
charge_rate_ = Mth.clamp(charge_rate, 0, capacity_);
|
||||||
|
discharge_rate_ = Mth.clamp(discharge_rate, 0, capacity_);
|
||||||
|
energy_ = Mth.clamp(energy, 0, capacity_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public Battery setMaxEnergyStored(int capacity) {
|
||||||
|
capacity_ = Math.max(capacity, 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery setEnergyStored(int energy) {
|
||||||
|
energy_ = Mth.clamp(energy, 0, capacity_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery setChargeRate(int in_rate) {
|
||||||
|
charge_rate_ = Mth.clamp(in_rate, 0, capacity_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery setDischargeRate(int out_rate) {
|
||||||
|
discharge_rate_ = Mth.clamp(out_rate, 0, capacity_);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChargeRate() {
|
||||||
|
return charge_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDischargeRate() {
|
||||||
|
return discharge_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return energy_ <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFull() {
|
||||||
|
return energy_ >= capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSOC() {
|
||||||
|
return (int) Mth.clamp((100.0 * energy_ / capacity_ + .5), 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getComparatorOutput() {
|
||||||
|
return (int) Mth.clamp((15.0 * energy_ / capacity_ + .2), 0, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean draw(int energy) {
|
||||||
|
if (energy_ < energy) return false;
|
||||||
|
energy_ -= energy;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean feed(int energy) {
|
||||||
|
energy_ = Math.min(energy_ + energy, capacity_);
|
||||||
|
return energy_ >= capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery clear() {
|
||||||
|
energy_ = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery load(CompoundTag nbt, String key) {
|
||||||
|
setEnergyStored(nbt.getInt(key));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Battery load(CompoundTag nbt) {
|
||||||
|
return load(nbt, "Energy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag save(CompoundTag nbt, String key) {
|
||||||
|
nbt.putInt(key, energy_);
|
||||||
|
return nbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundTag save(CompoundTag nbt) {
|
||||||
|
return save(nbt, "Energy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public LazyOptional<IEnergyStorage> createEnergyHandler() {
|
||||||
|
return LazyOptional.of(() -> this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IEnergyStorage ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int receiveEnergy(int feed_energy, boolean simulate) {
|
||||||
|
if (!canReceive()) return 0;
|
||||||
|
int e = Math.min(Math.min(charge_rate_, feed_energy), capacity_ - energy_);
|
||||||
|
if (!simulate) energy_ += e;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int extractEnergy(int draw_energy, boolean simulate) {
|
||||||
|
if (!canExtract()) return 0;
|
||||||
|
int e = Math.min(Math.min(discharge_rate_, draw_energy), energy_);
|
||||||
|
if (!simulate) energy_ -= e;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEnergyStored() {
|
||||||
|
return energy_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxEnergyStored() {
|
||||||
|
return capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canExtract() {
|
||||||
|
return discharge_rate_ > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canReceive() {
|
||||||
|
return charge_rate_ > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* @file RsSignals.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* General redstone signal related functionality.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class RsSignals {
|
||||||
|
|
||||||
|
public static boolean hasSignalConnector(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction realSide) {
|
||||||
|
return state.isSignalSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int fromContainer(@Nullable Container container) {
|
||||||
|
if (container == null) return 0;
|
||||||
|
final double max = container.getMaxStackSize();
|
||||||
|
if (max <= 0) return 0;
|
||||||
|
boolean nonempty = false;
|
||||||
|
double fill_level = 0;
|
||||||
|
for (int i = 0; i < container.getContainerSize(); ++i) {
|
||||||
|
ItemStack stack = container.getItem(i);
|
||||||
|
if (stack.isEmpty() || (stack.getMaxStackSize() <= 0)) continue;
|
||||||
|
fill_level += ((double) stack.getCount()) / Math.min(max, stack.getMaxStackSize());
|
||||||
|
nonempty = true;
|
||||||
|
}
|
||||||
|
fill_level /= container.getContainerSize();
|
||||||
|
return (int) (Math.floor(fill_level * 14) + (nonempty ? 1 : 0)); // vanilla compliant calculation.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
122
src/main/java/dev/zontreck/libzontreck/edlibmc/SidedProxy.java
Normal file
122
src/main/java/dev/zontreck/libzontreck/edlibmc/SidedProxy.java
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* @file SidedProxy.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* General client/server sidedness selection proxy.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class SidedProxy {
|
||||||
|
@Nullable
|
||||||
|
public static Player getPlayerClientSide() {
|
||||||
|
return proxy.getPlayerClientSide();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Level getWorldClientSide() {
|
||||||
|
return proxy.getWorldClientSide();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Minecraft mc() {
|
||||||
|
return proxy.mc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Boolean> isCtrlDown() {
|
||||||
|
return proxy.isCtrlDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Boolean> isShiftDown() {
|
||||||
|
return proxy.isShiftDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<String> getClipboard() {
|
||||||
|
return proxy.getClipboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setClipboard(String text) {
|
||||||
|
return proxy.setClipboard(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final ISidedProxy proxy = DistExecutor.unsafeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new);
|
||||||
|
|
||||||
|
private interface ISidedProxy {
|
||||||
|
default @Nullable Player getPlayerClientSide() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Level getWorldClientSide() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Minecraft mc() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
default Optional<Boolean> isCtrlDown() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
default Optional<Boolean> isShiftDown() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
default Optional<String> getClipboard() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean setClipboard(String text) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClientProxy implements ISidedProxy {
|
||||||
|
public @Nullable Player getPlayerClientSide() {
|
||||||
|
return Minecraft.getInstance().player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Level getWorldClientSide() {
|
||||||
|
return Minecraft.getInstance().level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Minecraft mc() {
|
||||||
|
return Minecraft.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Boolean> isCtrlDown() {
|
||||||
|
return Optional.of(Auxiliaries.isCtrlDown());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Boolean> isShiftDown() {
|
||||||
|
return Optional.of(Auxiliaries.isShiftDown());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getClipboard() {
|
||||||
|
return (mc() == null) ? Optional.empty() : Optional.of(net.minecraft.client.gui.font.TextFieldHelper.getClipboardContents(mc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setClipboard(String text) {
|
||||||
|
if (mc() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
net.minecraft.client.gui.font.TextFieldHelper.setClipboardContents(Minecraft.getInstance(), text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ServerProxy implements ISidedProxy {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* @file SlabSliceBlock.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Half slab ("slab slices") characteristics class. Actually
|
||||||
|
* it's now a quarter slab, but who cares.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||||
|
import net.minecraft.world.level.material.Fluid;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SlabSliceBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock {
|
||||||
|
public static final IntegerProperty PARTS = IntegerProperty.create("parts", 0, 14);
|
||||||
|
|
||||||
|
protected static final VoxelShape[] AABBs = {
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 2. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 4. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 6. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 8. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 10. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 12. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 14. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 2. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 4. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 6. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 8. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 10. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 12. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 14. / 16, 0, 1, 16. / 16, 1)),
|
||||||
|
Shapes.create(new AABB(0, 0, 0, 1, 1, 1)) // <- with 4bit fill
|
||||||
|
};
|
||||||
|
|
||||||
|
protected static final int[] num_slabs_contained_in_parts_ = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 0x1}; // <- with 4bit fill
|
||||||
|
private static boolean with_pickup = false;
|
||||||
|
|
||||||
|
public static void on_config(boolean direct_slab_pickup) {
|
||||||
|
with_pickup = direct_slab_pickup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SlabSliceBlock(long config, BlockBehaviour.Properties builder) {
|
||||||
|
super(config, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean is_cube(BlockState state) {
|
||||||
|
return state.getValue(PARTS) == 0x07;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
if (!Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true)) return;
|
||||||
|
if (with_pickup) Auxiliaries.Tooltip.addInformation("engineersdecor.tooltip.slabpickup", tooltip);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderTypeHint getRenderTypeHint() {
|
||||||
|
return (((config & StandardBlocks.CFG_TRANSLUCENT) != 0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return AABBs[state.getValue(PARTS) & 0xf];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return getShape(state, world, pos, selectionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(PARTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
final BlockPos pos = context.getClickedPos();
|
||||||
|
BlockState state = context.getLevel().getBlockState(pos);
|
||||||
|
if (state.getBlock() == this) {
|
||||||
|
int parts = state.getValue(PARTS);
|
||||||
|
if (parts == 7) return null; // -> is already a full block.
|
||||||
|
parts += (parts < 7) ? 1 : -1;
|
||||||
|
if (parts == 7) state = state.setValue(WATERLOGGED, false);
|
||||||
|
return state.setValue(PARTS, parts);
|
||||||
|
} else {
|
||||||
|
final Direction face = context.getClickedFace();
|
||||||
|
final BlockState placement_state = super.getStateForPlacement(context); // fluid state
|
||||||
|
if (face == Direction.UP) return placement_state.setValue(PARTS, 0);
|
||||||
|
if (face == Direction.DOWN) return placement_state.setValue(PARTS, 14);
|
||||||
|
if (!face.getAxis().isHorizontal()) return placement_state;
|
||||||
|
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
|
||||||
|
return placement_state.setValue(PARTS, isupper ? 14 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
|
||||||
|
if (context.getItemInHand().getItem() != this.asItem()) return false;
|
||||||
|
if (!context.replacingClickedOnBlock()) return true;
|
||||||
|
final Direction face = context.getClickedFace();
|
||||||
|
final int parts = state.getValue(PARTS);
|
||||||
|
if (parts == 7) return false;
|
||||||
|
if ((face == Direction.UP) && (parts < 7)) return true;
|
||||||
|
if ((face == Direction.DOWN) && (parts > 7)) return true;
|
||||||
|
if (!face.getAxis().isHorizontal()) return false;
|
||||||
|
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
|
||||||
|
return isupper ? (parts == 0) : (parts == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState rotate(BlockState state, Rotation rot) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState mirror(BlockState state, Mirror mirrorIn) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDynamicDropList() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion) {
|
||||||
|
return new ArrayList<>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.getValue(PARTS) & 0xf])));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void attack(BlockState state, Level world, BlockPos pos, Player player) {
|
||||||
|
if ((world.isClientSide) || (!with_pickup)) return;
|
||||||
|
final ItemStack stack = player.getMainHandItem();
|
||||||
|
if (stack.isEmpty() || (Block.byItem(stack.getItem()) != this)) return;
|
||||||
|
if (stack.getCount() >= stack.getMaxStackSize()) return;
|
||||||
|
Vec3 lv = player.getLookAngle();
|
||||||
|
Direction facing = Direction.getNearest((float) lv.x, (float) lv.y, (float) lv.z);
|
||||||
|
if ((facing != Direction.UP) && (facing != Direction.DOWN)) return;
|
||||||
|
if (state.getBlock() != this) return;
|
||||||
|
int parts = state.getValue(PARTS);
|
||||||
|
if ((facing == Direction.DOWN) && (parts <= 7)) {
|
||||||
|
if (parts > 0) {
|
||||||
|
world.setBlock(pos, state.setValue(PARTS, parts - 1), 3);
|
||||||
|
} else {
|
||||||
|
world.removeBlock(pos, false);
|
||||||
|
}
|
||||||
|
} else if ((facing == Direction.UP) && (parts >= 7)) {
|
||||||
|
if (parts < 14) {
|
||||||
|
world.setBlock(pos, state.setValue(PARTS, parts + 1), 3);
|
||||||
|
} else {
|
||||||
|
world.removeBlock(pos, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!player.isCreative()) {
|
||||||
|
stack.grow(1);
|
||||||
|
if (player.getInventory() != null) player.getInventory().setChanged();
|
||||||
|
}
|
||||||
|
SoundType st = this.getSoundType(state, world, pos, null);
|
||||||
|
world.playSound(player, pos, st.getPlaceSound(), SoundSource.BLOCKS, (st.getVolume() + 1f) / 2.5f, 0.9f * st.getPitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) {
|
||||||
|
return (state.getValue(PARTS) != 14) && (super.placeLiquid(world, pos, state, fluidState));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) {
|
||||||
|
return (state.getValue(PARTS) != 14) && (super.canPlaceLiquid(world, pos, state, fluid));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,698 @@
|
||||||
|
/*
|
||||||
|
* @file StandardBlocks.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Common functionality class for decor blocks.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||||
|
import net.minecraft.world.level.material.Fluid;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
|
import net.minecraft.world.level.pathfinder.PathComputationType;
|
||||||
|
import net.minecraft.world.level.storage.loot.LootContext;
|
||||||
|
import net.minecraft.world.level.storage.loot.LootParams;
|
||||||
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public class StandardBlocks {
|
||||||
|
public static final long CFG_DEFAULT = 0x0000000000000000L; // no special config
|
||||||
|
public static final long CFG_CUTOUT = 0x0000000000000001L; // cutout rendering
|
||||||
|
public static final long CFG_MIPPED = 0x0000000000000002L; // cutout mipped rendering
|
||||||
|
public static final long CFG_TRANSLUCENT = 0x0000000000000004L; // indicates a block/pane is glass like (transparent, etc)
|
||||||
|
public static final long CFG_WATERLOGGABLE = 0x0000000000000008L; // The derived block extends IWaterLoggable
|
||||||
|
public static final long CFG_HORIZIONTAL = 0x0000000000000010L; // horizontal block, affects bounding box calculation at construction time and placement
|
||||||
|
public static final long CFG_LOOK_PLACEMENT = 0x0000000000000020L; // placed in direction the player is looking when placing.
|
||||||
|
public static final long CFG_FACING_PLACEMENT = 0x0000000000000040L; // placed on the facing the player has clicked.
|
||||||
|
public static final long CFG_OPPOSITE_PLACEMENT = 0x0000000000000080L; // placed placed in the opposite direction of the face the player clicked.
|
||||||
|
public static final long CFG_FLIP_PLACEMENT_IF_SAME = 0x0000000000000100L; // placement direction flipped if an instance of the same class was clicked
|
||||||
|
public static final long CFG_FLIP_PLACEMENT_SHIFTCLICK = 0x0000000000000200L; // placement direction flipped if player is sneaking
|
||||||
|
public static final long CFG_STRICT_CONNECTIONS = 0x0000000000000400L; // blocks do not connect to similar blocks around (implementation details may vary a bit)
|
||||||
|
public static final long CFG_AI_PASSABLE = 0x0000000000000800L; // does not block movement path for AI, needed for non-opaque blocks with collision shapes not thin at the bottom or one side.
|
||||||
|
|
||||||
|
public interface IStandardBlock {
|
||||||
|
default long config() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean hasDynamicDropList() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<ItemStack> dropList(BlockState state, Level world, @Nullable BlockEntity te, boolean explosion) {
|
||||||
|
return Collections.singletonList((!world.isClientSide()) ? (new ItemStack(state.getBlock().asItem())) : (ItemStack.EMPTY));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RenderTypeHint {SOLID, CUTOUT, CUTOUT_MIPPED, TRANSLUCENT, TRANSLUCENT_NO_CRUMBLING}
|
||||||
|
|
||||||
|
default RenderTypeHint getRenderTypeHint() {
|
||||||
|
return getRenderTypeHint(config());
|
||||||
|
}
|
||||||
|
|
||||||
|
default RenderTypeHint getRenderTypeHint(long config) {
|
||||||
|
if ((config & CFG_CUTOUT) != 0) return RenderTypeHint.CUTOUT;
|
||||||
|
if ((config & CFG_MIPPED) != 0) return RenderTypeHint.CUTOUT_MIPPED;
|
||||||
|
if ((config & CFG_TRANSLUCENT) != 0) return RenderTypeHint.TRANSLUCENT;
|
||||||
|
return RenderTypeHint.SOLID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BaseBlock extends Block implements IStandardBlock, SimpleWaterloggedBlock {
|
||||||
|
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||||||
|
public final long config;
|
||||||
|
|
||||||
|
public BaseBlock(long conf, BlockBehaviour.Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
config = conf;
|
||||||
|
BlockState state = getStateDefinition().any();
|
||||||
|
if ((conf & CFG_WATERLOGGABLE) != 0) state = state.setValue(WATERLOGGED, false);
|
||||||
|
registerDefaultState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long config() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderTypeHint getRenderTypeHint() {
|
||||||
|
return getRenderTypeHint(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public boolean isPathfindable(BlockState state, BlockGetter world, BlockPos pos, PathComputationType type) {
|
||||||
|
return ((config & CFG_AI_PASSABLE) != 0) && (super.isPathfindable(state, world, pos, type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSignalConnector(BlockState state, BlockGetter world, BlockPos pos, @Nullable Direction side) {
|
||||||
|
return state.isSignalSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) {
|
||||||
|
final boolean rsup = (state.hasBlockEntity() && (state.getBlock() != newState.getBlock()));
|
||||||
|
super.onRemove(state, world, pos, newState, isMoving);
|
||||||
|
if (rsup) world.updateNeighbourForOutputSignal(pos, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) {
|
||||||
|
return (((config & CFG_WATERLOGGABLE) == 0) || (!state.getValue(WATERLOGGED))) && super.propagatesSkylightDown(state, reader, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public FluidState getFluidState(BlockState state) {
|
||||||
|
return (((config & CFG_WATERLOGGABLE) != 0) && state.getValue(WATERLOGGED)) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) {
|
||||||
|
if (((config & CFG_WATERLOGGABLE) != 0) && (state.getValue(WATERLOGGED)))
|
||||||
|
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // SimpleWaterloggedBlock
|
||||||
|
public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) {
|
||||||
|
return ((config & CFG_WATERLOGGABLE) != 0) && SimpleWaterloggedBlock.super.canPlaceLiquid(world, pos, state, fluid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // SimpleWaterloggedBlock
|
||||||
|
public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) {
|
||||||
|
return ((config & CFG_WATERLOGGABLE) != 0) && SimpleWaterloggedBlock.super.placeLiquid(world, pos, state, fluidState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // SimpleWaterloggedBlock
|
||||||
|
public ItemStack pickupBlock(LevelAccessor world, BlockPos pos, BlockState state) {
|
||||||
|
return ((config & CFG_WATERLOGGABLE) != 0) ? (SimpleWaterloggedBlock.super.pickupBlock(world, pos, state)) : (ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // SimpleWaterloggedBlock
|
||||||
|
public Optional<SoundEvent> getPickupSound() {
|
||||||
|
return ((config & CFG_WATERLOGGABLE) != 0) ? (SimpleWaterloggedBlock.super.getPickupSound()) : Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Cutout extends BaseBlock implements IStandardBlock {
|
||||||
|
private final VoxelShape vshape;
|
||||||
|
|
||||||
|
public Cutout(long conf, BlockBehaviour.Properties properties) {
|
||||||
|
this(conf, properties, Auxiliaries.getPixeledAABB(0, 0, 0, 16, 16, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cutout(long conf, BlockBehaviour.Properties properties, AABB aabb) {
|
||||||
|
this(conf, properties, Shapes.create(aabb));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cutout(long conf, BlockBehaviour.Properties properties, AABB[] aabbs) {
|
||||||
|
this(conf, properties, Arrays.stream(aabbs).map(Shapes::create).reduce(Shapes.empty(), (shape, aabb) -> Shapes.joinUnoptimized(shape, aabb, BooleanOp.OR)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cutout(long conf, BlockBehaviour.Properties properties, VoxelShape voxel_shape) {
|
||||||
|
super(conf, properties);
|
||||||
|
vshape = voxel_shape;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return vshape;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return vshape;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
BlockState state = super.getStateForPlacement(context);
|
||||||
|
if ((config & CFG_WATERLOGGABLE) != 0) {
|
||||||
|
FluidState fs = context.getLevel().getFluidState(context.getClickedPos());
|
||||||
|
state = state.setValue(WATERLOGGED, fs.getType() == Fluids.WATER);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PushReaction getPistonPushReaction(BlockState state) {
|
||||||
|
return PushReaction.NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean propagatesSkylightDown(BlockState state, BlockGetter reader, BlockPos pos) {
|
||||||
|
if ((config & CFG_WATERLOGGABLE) != 0) {
|
||||||
|
if (state.getValue(WATERLOGGED)) return false;
|
||||||
|
}
|
||||||
|
return super.propagatesSkylightDown(state, reader, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public FluidState getFluidState(BlockState state) {
|
||||||
|
if ((config & CFG_WATERLOGGABLE) != 0) {
|
||||||
|
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
|
||||||
|
}
|
||||||
|
return super.getFluidState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState updateShape(BlockState state, Direction facing, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) {
|
||||||
|
if ((config & CFG_WATERLOGGABLE) != 0) {
|
||||||
|
if (state.getValue(WATERLOGGED))
|
||||||
|
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WaterLoggable extends Cutout implements IStandardBlock {
|
||||||
|
public WaterLoggable(long config, BlockBehaviour.Properties properties) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaterLoggable(long config, BlockBehaviour.Properties properties, AABB aabb) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, aabb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaterLoggable(long config, BlockBehaviour.Properties properties, VoxelShape voxel_shape) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, voxel_shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WaterLoggable(long config, BlockBehaviour.Properties properties, AABB[] aabbs) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, aabbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(WATERLOGGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Directed extends Cutout implements IStandardBlock {
|
||||||
|
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||||
|
protected final Map<BlockState, VoxelShape> vshapes;
|
||||||
|
|
||||||
|
public Directed(long config, BlockBehaviour.Properties properties, final Function<List<BlockState>, Map<BlockState, VoxelShape>> shape_supplier) {
|
||||||
|
super(config, properties);
|
||||||
|
registerDefaultState(super.defaultBlockState().setValue(FACING, Direction.UP));
|
||||||
|
vshapes = shape_supplier.apply(getStateDefinition().getPossibleStates());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Directed(long config, BlockBehaviour.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier) {
|
||||||
|
this(config, properties, (states) -> {
|
||||||
|
final Map<BlockState, VoxelShape> vshapes = new HashMap<>();
|
||||||
|
final ArrayList<VoxelShape> indexed_shapes = shape_supplier.get();
|
||||||
|
for (BlockState state : states)
|
||||||
|
vshapes.put(state, indexed_shapes.get(state.getValue(FACING).get3DDataValue()));
|
||||||
|
return vshapes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Directed(long config, BlockBehaviour.Properties properties, final AABB[] unrotatedAABBs) {
|
||||||
|
this(config, properties, (states) -> {
|
||||||
|
final boolean is_horizontal = ((config & CFG_HORIZIONTAL) != 0);
|
||||||
|
Map<BlockState, VoxelShape> vshapes = new HashMap<>();
|
||||||
|
for (BlockState state : states) {
|
||||||
|
vshapes.put(state, Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, state.getValue(FACING), is_horizontal)));
|
||||||
|
}
|
||||||
|
return vshapes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Directed(long config, BlockBehaviour.Properties properties, final AABB unrotatedAABB) {
|
||||||
|
this(config, properties, new AABB[]{unrotatedAABB});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return vshapes.get(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return getShape(state, world, pos, selectionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(FACING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
BlockState state = super.getStateForPlacement(context);
|
||||||
|
if (state == null) return null;
|
||||||
|
Direction facing = context.getClickedFace();
|
||||||
|
if ((config & (CFG_HORIZIONTAL | CFG_LOOK_PLACEMENT)) == (CFG_HORIZIONTAL | CFG_LOOK_PLACEMENT)) {
|
||||||
|
// horizontal placement in direction the player is looking
|
||||||
|
facing = context.getHorizontalDirection();
|
||||||
|
} else if ((config & (CFG_HORIZIONTAL | CFG_LOOK_PLACEMENT)) == (CFG_HORIZIONTAL)) {
|
||||||
|
// horizontal placement on a face
|
||||||
|
if (((facing == Direction.UP) || (facing == Direction.DOWN))) return null;
|
||||||
|
} else if ((config & CFG_LOOK_PLACEMENT) != 0) {
|
||||||
|
// placement in direction the player is looking, with up and down
|
||||||
|
facing = context.getNearestLookingDirection();
|
||||||
|
}
|
||||||
|
if ((config & CFG_OPPOSITE_PLACEMENT) != 0) facing = facing.getOpposite();
|
||||||
|
if (((config & CFG_FLIP_PLACEMENT_SHIFTCLICK) != 0) && (context.getPlayer() != null) && (context.getPlayer().isShiftKeyDown()))
|
||||||
|
facing = facing.getOpposite();
|
||||||
|
return state.setValue(FACING, facing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AxisAligned extends Cutout implements IStandardBlock {
|
||||||
|
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.AXIS;
|
||||||
|
protected final ArrayList<VoxelShape> vshapes;
|
||||||
|
|
||||||
|
public AxisAligned(long config, BlockBehaviour.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier) {
|
||||||
|
super(config, properties);
|
||||||
|
registerDefaultState(super.defaultBlockState().setValue(AXIS, Direction.Axis.X));
|
||||||
|
vshapes = shape_supplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AxisAligned(long config, BlockBehaviour.Properties properties, final AABB[] unrotatedAABBs) {
|
||||||
|
this(config, properties, () -> new ArrayList<>(Arrays.asList(
|
||||||
|
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.EAST, false)),
|
||||||
|
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.UP, false)),
|
||||||
|
Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, Direction.SOUTH, false)),
|
||||||
|
Shapes.block()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AxisAligned(long config, BlockBehaviour.Properties properties, final AABB unrotatedAABB) {
|
||||||
|
this(config, properties, new AABB[]{unrotatedAABB});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return vshapes.get((state.getValue(AXIS)).ordinal() & 0x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return getShape(state, world, pos, selectionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(AXIS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
Direction facing;
|
||||||
|
if ((config & CFG_LOOK_PLACEMENT) != 0) {
|
||||||
|
facing = context.getNearestLookingDirection();
|
||||||
|
} else {
|
||||||
|
facing = context.getClickedFace();
|
||||||
|
}
|
||||||
|
return super.getStateForPlacement(context).setValue(AXIS, facing.getAxis());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState rotate(BlockState state, Rotation rotation) {
|
||||||
|
switch (rotation) {
|
||||||
|
case CLOCKWISE_90:
|
||||||
|
case COUNTERCLOCKWISE_90:
|
||||||
|
switch (state.getValue(AXIS)) {
|
||||||
|
case X:
|
||||||
|
return state.setValue(AXIS, Direction.Axis.Z);
|
||||||
|
case Z:
|
||||||
|
return state.setValue(AXIS, Direction.Axis.X);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Horizontal extends Cutout implements IStandardBlock {
|
||||||
|
public static final DirectionProperty HORIZONTAL_FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||||
|
protected final Map<BlockState, VoxelShape> vshapes;
|
||||||
|
protected final Map<BlockState, VoxelShape> cshapes;
|
||||||
|
|
||||||
|
public Horizontal(long config, BlockBehaviour.Properties properties, final Function<List<BlockState>, Map<BlockState, VoxelShape>> shape_supplier) {
|
||||||
|
super(config | CFG_HORIZIONTAL, properties);
|
||||||
|
registerDefaultState(super.defaultBlockState().setValue(HORIZONTAL_FACING, Direction.NORTH));
|
||||||
|
vshapes = shape_supplier.apply(getStateDefinition().getPossibleStates());
|
||||||
|
cshapes = shape_supplier.apply(getStateDefinition().getPossibleStates());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Horizontal(long config, BlockBehaviour.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier) {
|
||||||
|
this(config, properties, (states) -> {
|
||||||
|
final Map<BlockState, VoxelShape> vshapes = new HashMap<>();
|
||||||
|
final ArrayList<VoxelShape> indexed_shapes = shape_supplier.get();
|
||||||
|
for (BlockState state : states)
|
||||||
|
vshapes.put(state, indexed_shapes.get(state.getValue(HORIZONTAL_FACING).get3DDataValue()));
|
||||||
|
return vshapes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Horizontal(long config, BlockBehaviour.Properties properties, final AABB unrotatedAABB) {
|
||||||
|
this(config, properties, new AABB[]{unrotatedAABB});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Horizontal(long config, BlockBehaviour.Properties properties, final AABB[] unrotatedAABBs) {
|
||||||
|
this(config, properties, (states) -> {
|
||||||
|
Map<BlockState, VoxelShape> vshapes = new HashMap<>();
|
||||||
|
for (BlockState state : states) {
|
||||||
|
vshapes.put(state, Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(unrotatedAABBs, state.getValue(HORIZONTAL_FACING), true)));
|
||||||
|
}
|
||||||
|
return vshapes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(HORIZONTAL_FACING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return vshapes.get(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return cshapes.get(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
BlockState state = super.getStateForPlacement(context);
|
||||||
|
if (state == null) return null;
|
||||||
|
Direction facing = context.getClickedFace();
|
||||||
|
if ((config & CFG_LOOK_PLACEMENT) != 0) {
|
||||||
|
// horizontal placement in direction the player is looking
|
||||||
|
facing = context.getHorizontalDirection();
|
||||||
|
} else {
|
||||||
|
// horizontal placement on a face
|
||||||
|
facing = ((facing == Direction.UP) || (facing == Direction.DOWN)) ? (context.getHorizontalDirection()) : facing;
|
||||||
|
}
|
||||||
|
if ((config & CFG_OPPOSITE_PLACEMENT) != 0) facing = facing.getOpposite();
|
||||||
|
if (((config & CFG_FLIP_PLACEMENT_SHIFTCLICK) != 0) && (context.getPlayer() != null) && (context.getPlayer().isShiftKeyDown()))
|
||||||
|
facing = facing.getOpposite();
|
||||||
|
return state.setValue(HORIZONTAL_FACING, facing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState rotate(BlockState state, Rotation rot) {
|
||||||
|
return state.setValue(HORIZONTAL_FACING, rot.rotate(state.getValue(HORIZONTAL_FACING)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState mirror(BlockState state, Mirror mirrorIn) {
|
||||||
|
return state.rotate(mirrorIn.getRotation(state.getValue(HORIZONTAL_FACING)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DirectedWaterLoggable extends Directed implements IStandardBlock {
|
||||||
|
public DirectedWaterLoggable(long config, BlockBehaviour.Properties properties, AABB aabb) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, aabb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectedWaterLoggable(long config, BlockBehaviour.Properties properties, AABB[] aabbs) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, aabbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectedWaterLoggable(long config, BlockBehaviour.Properties properties, final Function<List<BlockState>, Map<BlockState, VoxelShape>> shape_supplier) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, shape_supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectedWaterLoggable(long config, BlockBehaviour.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, shape_supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(WATERLOGGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AxisAlignedWaterLoggable extends AxisAligned implements IStandardBlock {
|
||||||
|
public AxisAlignedWaterLoggable(long config, BlockBehaviour.Properties properties, AABB aabb) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, aabb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AxisAlignedWaterLoggable(long config, BlockBehaviour.Properties properties, AABB[] aabbs) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, aabbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AxisAlignedWaterLoggable(long config, BlockBehaviour.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier) {
|
||||||
|
super(config | CFG_WATERLOGGABLE, properties, shape_supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(WATERLOGGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HorizontalWaterLoggable extends Horizontal implements IStandardBlock {
|
||||||
|
public HorizontalWaterLoggable(long config, BlockBehaviour.Properties properties, AABB aabb) {
|
||||||
|
super(config | CFG_WATERLOGGABLE | CFG_HORIZIONTAL, properties, aabb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalWaterLoggable(long config, BlockBehaviour.Properties properties, AABB[] aabbs) {
|
||||||
|
super(config | CFG_WATERLOGGABLE | CFG_HORIZIONTAL, properties, aabbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalWaterLoggable(long config, BlockBehaviour.Properties properties, final Supplier<ArrayList<VoxelShape>> shape_supplier) {
|
||||||
|
super(config | CFG_WATERLOGGABLE | CFG_HORIZIONTAL, properties, shape_supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalWaterLoggable(long config, BlockBehaviour.Properties properties, final Function<List<BlockState>, Map<BlockState, VoxelShape>> shape_supplier) {
|
||||||
|
super(config | CFG_WATERLOGGABLE | CFG_HORIZIONTAL, properties, shape_supplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(WATERLOGGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class HorizontalFourWayWaterLoggable extends WaterLoggable implements IStandardBlock {
|
||||||
|
public static final BooleanProperty NORTH = BlockStateProperties.NORTH;
|
||||||
|
public static final BooleanProperty EAST = BlockStateProperties.EAST;
|
||||||
|
public static final BooleanProperty SOUTH = BlockStateProperties.SOUTH;
|
||||||
|
public static final BooleanProperty WEST = BlockStateProperties.WEST;
|
||||||
|
protected final Map<BlockState, VoxelShape> shapes;
|
||||||
|
protected final Map<BlockState, VoxelShape> collision_shapes;
|
||||||
|
|
||||||
|
public HorizontalFourWayWaterLoggable(long config, BlockBehaviour.Properties properties, AABB base_aabb, final AABB[] side_aabb, int railing_height_extension) {
|
||||||
|
super(config, properties, base_aabb);
|
||||||
|
Map<BlockState, VoxelShape> build_shapes = new HashMap<>();
|
||||||
|
Map<BlockState, VoxelShape> build_collision_shapes = new HashMap<>();
|
||||||
|
for (BlockState state : getStateDefinition().getPossibleStates()) {
|
||||||
|
{
|
||||||
|
VoxelShape shape = ((base_aabb.getXsize() == 0) || (base_aabb.getYsize() == 0) || (base_aabb.getZsize() == 0)) ? Shapes.empty() : Shapes.create(base_aabb);
|
||||||
|
if (state.getValue(NORTH))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.NORTH, true)), BooleanOp.OR);
|
||||||
|
if (state.getValue(EAST))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.EAST, true)), BooleanOp.OR);
|
||||||
|
if (state.getValue(SOUTH))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.SOUTH, true)), BooleanOp.OR);
|
||||||
|
if (state.getValue(WEST))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getRotatedAABB(side_aabb, Direction.WEST, true)), BooleanOp.OR);
|
||||||
|
if (shape.isEmpty()) shape = Shapes.block();
|
||||||
|
build_shapes.put(state.setValue(WATERLOGGED, false), shape);
|
||||||
|
build_shapes.put(state.setValue(WATERLOGGED, true), shape);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// how the hack to extend a shape, these are the above with y+4px.
|
||||||
|
VoxelShape shape = ((base_aabb.getXsize() == 0) || (base_aabb.getYsize() == 0) || (base_aabb.getZsize() == 0)) ? Shapes.empty() : Shapes.create(base_aabb);
|
||||||
|
if (state.getValue(NORTH))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
|
||||||
|
Direction.NORTH, true), bb -> bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
|
||||||
|
if (state.getValue(EAST))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
|
||||||
|
Direction.EAST, true), bb -> bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
|
||||||
|
if (state.getValue(SOUTH))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
|
||||||
|
Direction.SOUTH, true), bb -> bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
|
||||||
|
if (state.getValue(WEST))
|
||||||
|
shape = Shapes.joinUnoptimized(shape, Auxiliaries.getUnionShape(Auxiliaries.getMappedAABB(Auxiliaries.getRotatedAABB(side_aabb,
|
||||||
|
Direction.WEST, true), bb -> bb.expandTowards(0, railing_height_extension, 0))), BooleanOp.OR);
|
||||||
|
if (shape.isEmpty()) shape = Shapes.block();
|
||||||
|
build_collision_shapes.put(state.setValue(WATERLOGGED, false), shape);
|
||||||
|
build_collision_shapes.put(state.setValue(WATERLOGGED, true), shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shapes = build_shapes;
|
||||||
|
collision_shapes = build_collision_shapes;
|
||||||
|
registerDefaultState(super.defaultBlockState().setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false).setValue(WATERLOGGED, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HorizontalFourWayWaterLoggable(long config, BlockBehaviour.Properties properties, AABB base_aabb, final AABB side_aabb, int railing_height_extension) {
|
||||||
|
this(config, properties, base_aabb, new AABB[]{side_aabb}, railing_height_extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(NORTH, EAST, SOUTH, WEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
return super.getStateForPlacement(context).setValue(NORTH, false).setValue(EAST, false).setValue(SOUTH, false).setValue(WEST, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) {
|
||||||
|
return shapes.getOrDefault(state, Shapes.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context) {
|
||||||
|
return collision_shapes.getOrDefault(state, Shapes.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BooleanProperty getDirectionProperty(Direction face) {
|
||||||
|
return switch (face) {
|
||||||
|
case EAST -> HorizontalFourWayWaterLoggable.EAST;
|
||||||
|
case SOUTH -> HorizontalFourWayWaterLoggable.SOUTH;
|
||||||
|
case WEST -> HorizontalFourWayWaterLoggable.WEST;
|
||||||
|
default -> HorizontalFourWayWaterLoggable.NORTH;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* @file StandardDoorBlock.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Door blocks, almost entirely based on vanilla.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.DoorBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockSetType;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DoorHingeSide;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.minecraft.world.phys.shapes.BooleanOp;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class StandardDoorBlock extends DoorBlock implements StandardBlocks.IStandardBlock {
|
||||||
|
private final long config_;
|
||||||
|
protected final VoxelShape[][][][] shapes_;
|
||||||
|
protected final SoundEvent open_sound_;
|
||||||
|
protected final SoundEvent close_sound_;
|
||||||
|
|
||||||
|
public StandardDoorBlock(long config, BlockBehaviour.Properties properties, AABB[] open_aabbs_top, AABB[] open_aabbs_bottom, AABB[] closed_aabbs_top, AABB[] closed_aabbs_bottom, SoundEvent open_sound, SoundEvent close_sound, BlockSetType blockSetType) {
|
||||||
|
super(properties, blockSetType);
|
||||||
|
VoxelShape[][][][] shapes = new VoxelShape[Direction.values().length][2][2][2];
|
||||||
|
for (Direction facing : Direction.values()) {
|
||||||
|
for (boolean open : new boolean[]{false, true}) {
|
||||||
|
for (DoubleBlockHalf half : new DoubleBlockHalf[]{DoubleBlockHalf.UPPER, DoubleBlockHalf.LOWER}) {
|
||||||
|
for (boolean hinge_right : new boolean[]{false, true}) {
|
||||||
|
VoxelShape shape = Shapes.empty();
|
||||||
|
if (facing.getAxis() == Direction.Axis.Y) {
|
||||||
|
shape = Shapes.block();
|
||||||
|
} else {
|
||||||
|
final AABB[] aabbs = (open) ? ((half == DoubleBlockHalf.UPPER) ? open_aabbs_top : open_aabbs_bottom) : ((half == DoubleBlockHalf.UPPER) ? closed_aabbs_top : closed_aabbs_bottom);
|
||||||
|
for (AABB e : aabbs) {
|
||||||
|
AABB aabb = Auxiliaries.getRotatedAABB(e, facing, true);
|
||||||
|
if (!hinge_right)
|
||||||
|
aabb = Auxiliaries.getMirroredAABB(aabb, facing.getClockWise().getAxis());
|
||||||
|
shape = Shapes.join(shape, Shapes.create(aabb), BooleanOp.OR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shapes[facing.ordinal()][open ? 1 : 0][hinge_right ? 1 : 0][half == DoubleBlockHalf.UPPER ? 0 : 1] = shape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config_ = config;
|
||||||
|
shapes_ = shapes;
|
||||||
|
open_sound_ = open_sound;
|
||||||
|
close_sound_ = close_sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardDoorBlock(long config, BlockBehaviour.Properties properties, AABB open_aabb, AABB closed_aabb, SoundEvent open_sound, SoundEvent close_sound, BlockSetType blockSetType) {
|
||||||
|
this(config, properties, new AABB[]{open_aabb}, new AABB[]{open_aabb}, new AABB[]{closed_aabb}, new AABB[]{closed_aabb}, open_sound, close_sound, blockSetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardDoorBlock(long config, BlockBehaviour.Properties properties, SoundEvent open_sound, SoundEvent close_sound, BlockSetType blockSetType) {
|
||||||
|
this(
|
||||||
|
config, properties,
|
||||||
|
Auxiliaries.getPixeledAABB(13, 0, 0, 16, 16, 16),
|
||||||
|
Auxiliaries.getPixeledAABB(0, 0, 13, 16, 16, 16),
|
||||||
|
open_sound,
|
||||||
|
close_sound,
|
||||||
|
blockSetType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardDoorBlock(long config, BlockBehaviour.Properties properties) {
|
||||||
|
this(
|
||||||
|
config, properties,
|
||||||
|
Auxiliaries.getPixeledAABB(13, 0, 0, 16, 16, 16),
|
||||||
|
Auxiliaries.getPixeledAABB(0, 0, 13, 16, 16, 16),
|
||||||
|
SoundEvents.WOODEN_DOOR_OPEN,
|
||||||
|
SoundEvents.WOODEN_DOOR_CLOSE,
|
||||||
|
BlockSetType.OAK
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long config() {
|
||||||
|
return config_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void sound(BlockGetter world, BlockPos pos, boolean open) {
|
||||||
|
if (world instanceof Level)
|
||||||
|
((Level) world).playSound(null, pos, open ? open_sound_ : close_sound_, SoundSource.BLOCKS, 0.7f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void actuate_adjacent_wing(BlockState state, BlockGetter world_ro, BlockPos pos, boolean open) {
|
||||||
|
if (!(world_ro instanceof final Level world)) return;
|
||||||
|
final BlockPos adjecent_pos = pos.relative((state.getValue(HINGE) == DoorHingeSide.LEFT) ? (state.getValue(FACING).getClockWise()) : (state.getValue(FACING).getCounterClockWise()));
|
||||||
|
if (!world.isLoaded(adjecent_pos)) return;
|
||||||
|
BlockState adjacent_state = world.getBlockState(adjecent_pos);
|
||||||
|
if (adjacent_state.getBlock() != this) return;
|
||||||
|
if (adjacent_state.getValue(OPEN) == open) return;
|
||||||
|
world.setBlock(adjecent_pos, adjacent_state.setValue(OPEN, open), 2 | 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
|
||||||
|
return shapes_[state.getValue(FACING).ordinal()][state.getValue(OPEN) ? 1 : 0][state.getValue(HINGE) == DoorHingeSide.RIGHT ? 1 : 0][state.getValue(HALF) == DoubleBlockHalf.UPPER ? 0 : 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||||
|
setOpen(player, world, state, pos, !state.getValue(OPEN));
|
||||||
|
return InteractionResult.sidedSuccess(world.isClientSide());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void neighborChanged(BlockState state, Level world, BlockPos pos, Block block, BlockPos fromPos, boolean isMoving) {
|
||||||
|
boolean powered = world.hasNeighborSignal(pos) || world.hasNeighborSignal(pos.relative(state.getValue(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
|
||||||
|
if ((block == this) || (powered == state.getValue(POWERED))) return;
|
||||||
|
world.setBlock(pos, state.setValue(POWERED, powered).setValue(OPEN, powered), 2);
|
||||||
|
actuate_adjacent_wing(state, world, pos, powered);
|
||||||
|
if (powered != state.getValue(OPEN)) sound(world, pos, powered);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOpen(@Nullable Entity entity, Level world, BlockState state, BlockPos pos, boolean open) {
|
||||||
|
if (!state.is(this) || (state.getValue(OPEN) == open)) return;
|
||||||
|
state = state.setValue(OPEN, open);
|
||||||
|
world.setBlock(pos, state, 2 | 8);
|
||||||
|
sound(world, pos, open);
|
||||||
|
actuate_adjacent_wing(state, world, pos, open);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* @file StandardEntityBlocks.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Common functionality class for blocks with block entities.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.MenuProvider;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.EntityBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||||
|
import net.minecraftforge.common.util.FakePlayer;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
public class StandardEntityBlocks {
|
||||||
|
public interface IStandardEntityBlock<ET extends StandardBlockEntity> extends EntityBlock {
|
||||||
|
|
||||||
|
default boolean isBlockEntityTicking(Level world, BlockState state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default InteractionResult useOpenGui(BlockState state, Level world, BlockPos pos, Player player) {
|
||||||
|
if (world.isClientSide()) return InteractionResult.SUCCESS;
|
||||||
|
final BlockEntity te = world.getBlockEntity(pos);
|
||||||
|
if (!(te instanceof MenuProvider) || ((player instanceof FakePlayer))) return InteractionResult.FAIL;
|
||||||
|
player.openMenu((MenuProvider) te);
|
||||||
|
return InteractionResult.CONSUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
default BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
|
||||||
|
BlockEntityType<?> tet = Registries.getBlockEntityTypeOfBlock(state.getBlock());
|
||||||
|
return (tet == null) ? null : tet.create(pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
default <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level world, BlockState state, BlockEntityType<T> te_type) {
|
||||||
|
return (world.isClientSide || (!isBlockEntityTicking(world, state))) ? (null) : ((Level w, BlockPos p, BlockState s, T te) -> ((StandardBlockEntity) te).tick());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
default <T extends BlockEntity> GameEventListener getListener(ServerLevel world, T te) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class StandardBlockEntity extends BlockEntity {
|
||||||
|
public StandardBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||||
|
super(type, pos, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* @file StandardFenceBlock.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Wall blocks.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.FenceGateBlock;
|
||||||
|
import net.minecraft.world.level.block.WallBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.WallSide;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class StandardFenceBlock extends WallBlock implements StandardBlocks.IStandardBlock {
|
||||||
|
public static final BooleanProperty UP = BlockStateProperties.UP;
|
||||||
|
public static final EnumProperty<WallSide> WALL_EAST = BlockStateProperties.EAST_WALL;
|
||||||
|
public static final EnumProperty<WallSide> WALL_NORTH = BlockStateProperties.NORTH_WALL;
|
||||||
|
public static final EnumProperty<WallSide> WALL_SOUTH = BlockStateProperties.SOUTH_WALL;
|
||||||
|
public static final EnumProperty<WallSide> WALL_WEST = BlockStateProperties.WEST_WALL;
|
||||||
|
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||||||
|
private final Map<BlockState, VoxelShape> shape_voxels;
|
||||||
|
private final Map<BlockState, VoxelShape> collision_shape_voxels;
|
||||||
|
private final long config;
|
||||||
|
|
||||||
|
public StandardFenceBlock(long config, BlockBehaviour.Properties properties) {
|
||||||
|
this(config, properties, 1.5, 16, 1.5, 0, 14, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardFenceBlock(long config, BlockBehaviour.Properties properties, double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) {
|
||||||
|
super(properties);
|
||||||
|
shape_voxels = buildShapes(pole_width, pole_height, side_width, side_min_y, side_max_low_y, side_max_tall_y);
|
||||||
|
collision_shape_voxels = buildShapes(pole_width, 24, pole_width, 0, 24, 24);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long config() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VoxelShape combinedShape(VoxelShape pole, WallSide height, VoxelShape low, VoxelShape high) {
|
||||||
|
if (height == WallSide.TALL) return Shapes.or(pole, high);
|
||||||
|
if (height == WallSide.LOW) return Shapes.or(pole, low);
|
||||||
|
return pole;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<BlockState, VoxelShape> buildShapes(double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) {
|
||||||
|
final double px0 = 8.0 - pole_width, px1 = 8.0 + pole_width, sx0 = 8.0 - side_width, sx1 = 8.0 + side_width;
|
||||||
|
VoxelShape vp = Block.box(px0, 0, px0, px1, pole_height, px1);
|
||||||
|
VoxelShape vs1 = Block.box(sx0, side_min_y, 0, sx1, side_max_low_y, sx1);
|
||||||
|
VoxelShape vs2 = Block.box(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
|
||||||
|
VoxelShape vs3 = Block.box(0, side_min_y, sx0, sx1, side_max_low_y, sx1);
|
||||||
|
VoxelShape vs4 = Block.box(sx0, side_min_y, sx0, 16, side_max_low_y, sx1);
|
||||||
|
VoxelShape vs5 = Block.box(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1);
|
||||||
|
VoxelShape vs6 = Block.box(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16);
|
||||||
|
VoxelShape vs7 = Block.box(0, side_min_y, sx0, sx1, side_max_tall_y, sx1);
|
||||||
|
VoxelShape vs8 = Block.box(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1);
|
||||||
|
ImmutableMap.Builder<BlockState, VoxelShape> builder = ImmutableMap.builder();
|
||||||
|
for (Boolean up : UP.getPossibleValues()) {
|
||||||
|
for (WallSide wh_east : WALL_EAST.getPossibleValues()) {
|
||||||
|
for (WallSide wh_north : WALL_NORTH.getPossibleValues()) {
|
||||||
|
for (WallSide wh_west : WALL_WEST.getPossibleValues()) {
|
||||||
|
for (WallSide wh_south : WALL_SOUTH.getPossibleValues()) {
|
||||||
|
VoxelShape shape = Shapes.empty();
|
||||||
|
shape = combinedShape(shape, wh_east, vs4, vs8);
|
||||||
|
shape = combinedShape(shape, wh_west, vs3, vs7);
|
||||||
|
shape = combinedShape(shape, wh_north, vs1, vs5);
|
||||||
|
shape = combinedShape(shape, wh_south, vs2, vs6);
|
||||||
|
if (up) shape = Shapes.or(shape, vp);
|
||||||
|
BlockState bs = defaultBlockState().setValue(UP, up)
|
||||||
|
.setValue(WALL_EAST, wh_east)
|
||||||
|
.setValue(WALL_NORTH, wh_north)
|
||||||
|
.setValue(WALL_WEST, wh_west)
|
||||||
|
.setValue(WALL_SOUTH, wh_south);
|
||||||
|
builder.put(bs.setValue(WATERLOGGED, false), shape);
|
||||||
|
builder.put(bs.setValue(WATERLOGGED, true), shape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return shape_voxels.getOrDefault(state, Shapes.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return collision_shape_voxels.getOrDefault(state, Shapes.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side) {
|
||||||
|
final Block block = facingState.getBlock();
|
||||||
|
if ((block instanceof FenceGateBlock) || (block instanceof StandardFenceBlock) || (block instanceof VariantWallBlock))
|
||||||
|
return true;
|
||||||
|
final BlockState oppositeState = world.getBlockState(facingPos.relative(side, 2));
|
||||||
|
if (!(oppositeState.getBlock() instanceof StandardFenceBlock)) return false;
|
||||||
|
return facingState.isRedstoneConductor(world, facingPos) && canSupportCenter(world, facingPos, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WallSide selectWallHeight(LevelReader world, BlockPos pos, Direction direction) {
|
||||||
|
return WallSide.LOW; // @todo: implement
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
LevelReader world = context.getLevel();
|
||||||
|
BlockPos pos = context.getClickedPos();
|
||||||
|
FluidState fs = context.getLevel().getFluidState(context.getClickedPos());
|
||||||
|
boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
|
||||||
|
boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST);
|
||||||
|
boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH);
|
||||||
|
boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST);
|
||||||
|
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
|
||||||
|
return defaultBlockState()
|
||||||
|
.setValue(UP, not_straight)
|
||||||
|
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_EAST, e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
|
||||||
|
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_WEST, w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE)
|
||||||
|
.setValue(WATERLOGGED, fs.getType() == Fluids.WATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState updateShape(BlockState state, Direction side, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) {
|
||||||
|
if (state.getValue(BlockStateProperties.WATERLOGGED))
|
||||||
|
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
|
||||||
|
if (side == Direction.DOWN) return super.updateShape(state, side, facingState, world, pos, facingPos);
|
||||||
|
boolean n = (side == Direction.NORTH) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_NORTH) != WallSide.NONE);
|
||||||
|
boolean e = (side == Direction.EAST) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_EAST) != WallSide.NONE);
|
||||||
|
boolean s = (side == Direction.SOUTH) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_SOUTH) != WallSide.NONE);
|
||||||
|
boolean w = (side == Direction.WEST) ? attachesTo(facingState, world, facingPos, side) : (state.getValue(WALL_WEST) != WallSide.NONE);
|
||||||
|
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
|
||||||
|
return state.setValue(UP, not_straight)
|
||||||
|
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_EAST, e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
|
||||||
|
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_WEST, w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PushReaction getPistonPushReaction(BlockState state) {
|
||||||
|
return PushReaction.NORMAL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* @file StandardStairsBlock.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Stairs and roof blocks, almost entirely based on vanilla stairs.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.block.StairBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class StandardStairsBlock extends StairBlock implements StandardBlocks.IStandardBlock {
|
||||||
|
private final long config;
|
||||||
|
|
||||||
|
public StandardStairsBlock(long config, java.util.function.Supplier<BlockState> state, BlockBehaviour.Properties properties) {
|
||||||
|
super(state, properties);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState p_279289_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PushReaction getPistonPushReaction(BlockState state) {
|
||||||
|
return PushReaction.NORMAL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* @file Tooltip.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Delayed tooltip for a selected area. Constructed with a
|
||||||
|
* GUI, invoked in `render()`.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
|
import net.minecraft.network.chat.*;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public class TooltipDisplay {
|
||||||
|
private static long default_delay = 800;
|
||||||
|
private static int default_max_deviation = 1;
|
||||||
|
|
||||||
|
public static void config(long delay, int max_deviation) {
|
||||||
|
default_delay = clamp(delay, 500, 5000);
|
||||||
|
default_max_deviation = Mth.clamp(max_deviation, 1, 5);
|
||||||
|
}
|
||||||
|
private static long clamp(long p1, long a, long b) {
|
||||||
|
return p1 < a ? a : Math.min(p1, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static class TipRange {
|
||||||
|
public final int x0, y0, x1, y1;
|
||||||
|
public final Supplier<Component> text;
|
||||||
|
|
||||||
|
public TipRange(int x, int y, int w, int h, Component text) {
|
||||||
|
this(x, y, w, h, () -> text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TipRange(int x, int y, int w, int h, Supplier<Component> text) {
|
||||||
|
this.text = text;
|
||||||
|
this.x0 = x;
|
||||||
|
this.y0 = y;
|
||||||
|
this.x1 = x0 + w - 1;
|
||||||
|
this.y1 = y0 + h - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private List<TipRange> ranges = new ArrayList<>();
|
||||||
|
private long delay = default_delay;
|
||||||
|
private int max_deviation = default_max_deviation;
|
||||||
|
private int x_last, y_last;
|
||||||
|
private long t;
|
||||||
|
private static boolean had_render_exception = false;
|
||||||
|
|
||||||
|
public TooltipDisplay() {
|
||||||
|
t = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TooltipDisplay init(List<TipRange> ranges, long delay_ms, int max_deviation_xy) {
|
||||||
|
this.ranges = ranges;
|
||||||
|
this.delay = delay_ms;
|
||||||
|
this.max_deviation = max_deviation_xy;
|
||||||
|
t = System.currentTimeMillis();
|
||||||
|
x_last = y_last = 0;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TooltipDisplay init(List<TipRange> ranges) {
|
||||||
|
return init(ranges, default_delay, default_max_deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TooltipDisplay init(TipRange... ranges) {
|
||||||
|
return init(Arrays.asList(ranges), default_delay, default_max_deviation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TooltipDisplay delay(int ms) {
|
||||||
|
delay = (ms <= 0) ? default_delay : ms;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetTimer() {
|
||||||
|
t = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends AbstractContainerMenu> boolean render(GuiGraphics mx, final AbstractContainerScreen<T> gui, int x, int y) {
|
||||||
|
if (had_render_exception) return false;
|
||||||
|
if ((Math.abs(x - x_last) > max_deviation) || (Math.abs(y - y_last) > max_deviation)) {
|
||||||
|
x_last = x;
|
||||||
|
y_last = y;
|
||||||
|
resetTimer();
|
||||||
|
return false;
|
||||||
|
} else if (Math.abs(System.currentTimeMillis() - t) < delay) {
|
||||||
|
return false;
|
||||||
|
} else if (ranges.stream().noneMatch(
|
||||||
|
(tip) -> {
|
||||||
|
if ((x < tip.x0) || (x > tip.x1) || (y < tip.y0) || (y > tip.y1)) return false;
|
||||||
|
String text = tip.text.get().toString();
|
||||||
|
if (text.isEmpty()) return false;
|
||||||
|
try {
|
||||||
|
mx.renderComponentTooltip(Minecraft.getInstance().font, tip.text.get().toFlatList(Style.EMPTY), x, y);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
had_render_exception = true;
|
||||||
|
Auxiliaries.logError("Tooltip rendering disabled due to exception: '" + ex.getMessage() + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
resetTimer();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* @file VariantSlabBlock.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Standard half block horizontal slab characteristics class.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||||
|
import net.minecraft.world.level.block.state.properties.SlabType;
|
||||||
|
import net.minecraft.world.level.material.Fluid;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public class VariantSlabBlock extends StandardBlocks.WaterLoggable implements StandardBlocks.IStandardBlock {
|
||||||
|
public static final EnumProperty<SlabType> TYPE = BlockStateProperties.SLAB_TYPE;
|
||||||
|
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 3);
|
||||||
|
|
||||||
|
protected static final VoxelShape[] AABBs = {
|
||||||
|
Shapes.create(new AABB(0, 8. / 16, 0, 1, 16. / 16, 1)), // top slab
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 8. / 16, 1)), // bottom slab
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 16. / 16, 1)), // both slabs
|
||||||
|
Shapes.create(new AABB(0, 0. / 16, 0, 1, 16. / 16, 1)) // << 2bit fill
|
||||||
|
};
|
||||||
|
protected static final int[] num_slabs_contained_in_parts_ = {1, 1, 2, 2};
|
||||||
|
private static boolean with_pickup = false;
|
||||||
|
|
||||||
|
public static void on_config(boolean direct_slab_pickup) {
|
||||||
|
with_pickup = direct_slab_pickup;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean is_cube(BlockState state) {
|
||||||
|
return state.getValue(TYPE) == SlabType.DOUBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariantSlabBlock(long config, BlockBehaviour.Properties builder) {
|
||||||
|
super(config, builder);
|
||||||
|
registerDefaultState(defaultBlockState().setValue(TYPE, SlabType.BOTTOM));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderTypeHint getRenderTypeHint() {
|
||||||
|
return (((config & StandardBlocks.CFG_TRANSLUCENT) != 0) ? (RenderTypeHint.TRANSLUCENT) : (RenderTypeHint.CUTOUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
if (!Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true)) return;
|
||||||
|
if (with_pickup && Auxiliaries.Tooltip.helpCondition())
|
||||||
|
Auxiliaries.Tooltip.addInformation("engineersdecor.tooltip.slabpickup", "engineersdecor.tooltip.slabpickup", tooltip, flag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public boolean skipRendering(BlockState state, BlockState adjacentBlockState, Direction side) {
|
||||||
|
return (adjacentBlockState == state) || (super.skipRendering(state, adjacentBlockState, side));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter source, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return AABBs[state.getValue(TYPE).ordinal() & 0x3];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return getShape(state, world, pos, selectionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(TYPE, TEXTURE_VARIANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
BlockPos pos = context.getClickedPos();
|
||||||
|
if (context.getLevel().getBlockState(pos).getBlock() == this)
|
||||||
|
return context.getLevel().getBlockState(pos).setValue(TYPE, SlabType.DOUBLE).setValue(WATERLOGGED, false);
|
||||||
|
final int rnd = Mth.clamp((int) (Mth.getSeed(context.getClickedPos()) & 0x3), 0, 3);
|
||||||
|
final Direction face = context.getClickedFace();
|
||||||
|
final BlockState placement_state = super.getStateForPlacement(context).setValue(TEXTURE_VARIANT, rnd); // fluid state
|
||||||
|
if (face == Direction.UP) return placement_state.setValue(TYPE, SlabType.BOTTOM);
|
||||||
|
if (face == Direction.DOWN) return placement_state.setValue(TYPE, SlabType.TOP);
|
||||||
|
if (!face.getAxis().isHorizontal()) return placement_state;
|
||||||
|
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
|
||||||
|
return placement_state.setValue(TYPE, isupper ? SlabType.TOP : SlabType.BOTTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
|
||||||
|
if (context.getItemInHand().getItem() != this.asItem()) return false;
|
||||||
|
if (!context.replacingClickedOnBlock()) return true;
|
||||||
|
final Direction face = context.getClickedFace();
|
||||||
|
final SlabType type = state.getValue(TYPE);
|
||||||
|
if ((face == Direction.UP) && (type == SlabType.BOTTOM)) return true;
|
||||||
|
if ((face == Direction.DOWN) && (type == SlabType.TOP)) return true;
|
||||||
|
if (!face.getAxis().isHorizontal()) return false;
|
||||||
|
final boolean isupper = ((context.getClickLocation().y() - context.getClickedPos().getY()) > 0.5);
|
||||||
|
return isupper ? (type == SlabType.BOTTOM) : (type == SlabType.TOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState rotate(BlockState state, Rotation rot) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public BlockState mirror(BlockState state, Mirror mirrorIn) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDynamicDropList() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ItemStack> dropList(BlockState state, Level world, BlockEntity te, boolean explosion) {
|
||||||
|
return new ArrayList<>(Collections.singletonList(new ItemStack(this.asItem(), num_slabs_contained_in_parts_[state.getValue(TYPE).ordinal() & 0x3])));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void attack(BlockState state, Level world, BlockPos pos, Player player) {
|
||||||
|
if ((world.isClientSide) || (!with_pickup)) return;
|
||||||
|
final ItemStack stack = player.getMainHandItem();
|
||||||
|
if (stack.isEmpty() || (Block.byItem(stack.getItem()) != this)) return;
|
||||||
|
if (stack.getCount() >= stack.getMaxStackSize()) return;
|
||||||
|
Vec3 lv = player.getLookAngle();
|
||||||
|
Direction facing = Direction.getNearest((float) lv.x, (float) lv.y, (float) lv.z);
|
||||||
|
if ((facing != Direction.UP) && (facing != Direction.DOWN)) return;
|
||||||
|
if (state.getBlock() != this) return;
|
||||||
|
SlabType type = state.getValue(TYPE);
|
||||||
|
if (facing == Direction.DOWN) {
|
||||||
|
if (type == SlabType.DOUBLE) {
|
||||||
|
world.setBlock(pos, state.setValue(TYPE, SlabType.BOTTOM), 3);
|
||||||
|
} else {
|
||||||
|
world.removeBlock(pos, false);
|
||||||
|
}
|
||||||
|
} else if (facing == Direction.UP) {
|
||||||
|
if (type == SlabType.DOUBLE) {
|
||||||
|
world.setBlock(pos, state.setValue(TYPE, SlabType.TOP), 3);
|
||||||
|
} else {
|
||||||
|
world.removeBlock(pos, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!player.isCreative()) {
|
||||||
|
stack.grow(1);
|
||||||
|
if (player.getInventory() != null) player.getInventory().setChanged();
|
||||||
|
}
|
||||||
|
SoundType st = this.getSoundType(state, world, pos, null);
|
||||||
|
world.playSound(player, pos, st.getPlaceSound(), SoundSource.BLOCKS, (st.getVolume() + 1f) / 2.5f, 0.9f * st.getPitch());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) {
|
||||||
|
return (state.getValue(TYPE) != SlabType.DOUBLE) && super.placeLiquid(world, pos, state, fluidState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) {
|
||||||
|
return (state.getValue(TYPE) != SlabType.DOUBLE) && super.canPlaceLiquid(world, pos, state, fluid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* @file VariantWallBlock.java
|
||||||
|
* @author Stefan Wilhelm (wile)
|
||||||
|
* @copyright (C) 2020 Stefan Wilhelm
|
||||||
|
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||||
|
*
|
||||||
|
* Wall blocks.
|
||||||
|
*/
|
||||||
|
package dev.zontreck.libzontreck.edlibmc;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableMap.Builder;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.SpawnPlacements;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.TooltipFlag;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.BlockGetter;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.LevelReader;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.FenceGateBlock;
|
||||||
|
import net.minecraft.world.level.block.WallBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.*;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
import net.minecraft.world.level.material.PushReaction;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class VariantWallBlock extends WallBlock implements StandardBlocks.IStandardBlock {
|
||||||
|
public static final BooleanProperty UP = BlockStateProperties.UP;
|
||||||
|
public static final EnumProperty<WallSide> WALL_EAST = BlockStateProperties.EAST_WALL;
|
||||||
|
public static final EnumProperty<WallSide> WALL_NORTH = BlockStateProperties.NORTH_WALL;
|
||||||
|
public static final EnumProperty<WallSide> WALL_SOUTH = BlockStateProperties.SOUTH_WALL;
|
||||||
|
public static final EnumProperty<WallSide> WALL_WEST = BlockStateProperties.WEST_WALL;
|
||||||
|
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||||||
|
public static final IntegerProperty TEXTURE_VARIANT = IntegerProperty.create("tvariant", 0, 7);
|
||||||
|
private final Map<BlockState, VoxelShape> shape_voxels;
|
||||||
|
private final Map<BlockState, VoxelShape> collision_shape_voxels;
|
||||||
|
private final long config;
|
||||||
|
|
||||||
|
public VariantWallBlock(long config, BlockBehaviour.Properties builder) {
|
||||||
|
super(builder);
|
||||||
|
shape_voxels = buildWallShapes(4, 16, 4, 0, 16, 16);
|
||||||
|
collision_shape_voxels = buildWallShapes(6, 16, 5, 0, 24, 24);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long config() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
public void appendHoverText(ItemStack stack, @Nullable BlockGetter world, List<Component> tooltip, TooltipFlag flag) {
|
||||||
|
Auxiliaries.Tooltip.addInformation(stack, world, tooltip, flag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VoxelShape combinedShape(VoxelShape pole, WallSide height, VoxelShape low, VoxelShape high) {
|
||||||
|
if (height == WallSide.TALL) return Shapes.or(pole, high);
|
||||||
|
if (height == WallSide.LOW) return Shapes.or(pole, low);
|
||||||
|
return pole;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<BlockState, VoxelShape> buildWallShapes(double pole_width, double pole_height, double side_width, double side_min_y, double side_max_low_y, double side_max_tall_y) {
|
||||||
|
final double px0 = 8.0 - pole_width, px1 = 8.0 + pole_width, sx0 = 8.0 - side_width, sx1 = 8.0 + side_width;
|
||||||
|
VoxelShape vp = Block.box(px0, 0, px0, px1, pole_height, px1);
|
||||||
|
VoxelShape vs1 = Block.box(sx0, side_min_y, 0, sx1, side_max_low_y, sx1);
|
||||||
|
VoxelShape vs2 = Block.box(sx0, side_min_y, sx0, sx1, side_max_low_y, 16);
|
||||||
|
VoxelShape vs3 = Block.box(0, side_min_y, sx0, sx1, side_max_low_y, sx1);
|
||||||
|
VoxelShape vs4 = Block.box(sx0, side_min_y, sx0, 16, side_max_low_y, sx1);
|
||||||
|
VoxelShape vs5 = Block.box(sx0, side_min_y, 0, sx1, side_max_tall_y, sx1);
|
||||||
|
VoxelShape vs6 = Block.box(sx0, side_min_y, sx0, sx1, side_max_tall_y, 16);
|
||||||
|
VoxelShape vs7 = Block.box(0, side_min_y, sx0, sx1, side_max_tall_y, sx1);
|
||||||
|
VoxelShape vs8 = Block.box(sx0, side_min_y, sx0, 16, side_max_tall_y, sx1);
|
||||||
|
Builder<BlockState, VoxelShape> builder = ImmutableMap.builder();
|
||||||
|
for (Boolean up : UP.getPossibleValues()) {
|
||||||
|
for (WallSide wh_east : WALL_EAST.getPossibleValues()) {
|
||||||
|
for (WallSide wh_north : WALL_NORTH.getPossibleValues()) {
|
||||||
|
for (WallSide wh_west : WALL_WEST.getPossibleValues()) {
|
||||||
|
for (WallSide wh_south : WALL_SOUTH.getPossibleValues()) {
|
||||||
|
VoxelShape shape = Shapes.empty();
|
||||||
|
shape = combinedShape(shape, wh_east, vs4, vs8);
|
||||||
|
shape = combinedShape(shape, wh_west, vs3, vs7);
|
||||||
|
shape = combinedShape(shape, wh_north, vs1, vs5);
|
||||||
|
shape = combinedShape(shape, wh_south, vs2, vs6);
|
||||||
|
if (up) shape = Shapes.or(shape, vp);
|
||||||
|
BlockState bs = defaultBlockState().setValue(UP, up)
|
||||||
|
.setValue(WALL_EAST, wh_east)
|
||||||
|
.setValue(WALL_NORTH, wh_north)
|
||||||
|
.setValue(WALL_WEST, wh_west)
|
||||||
|
.setValue(WALL_SOUTH, wh_south);
|
||||||
|
final VoxelShape tvs = shape;
|
||||||
|
TEXTURE_VARIANT.getPossibleValues().forEach((tv) -> {
|
||||||
|
builder.put(bs.setValue(TEXTURE_VARIANT, tv).setValue(WATERLOGGED, false), tvs);
|
||||||
|
builder.put(bs.setValue(TEXTURE_VARIANT, tv).setValue(WATERLOGGED, true), tvs);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return shape_voxels.getOrDefault(state, Shapes.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext selectionContext) {
|
||||||
|
return collision_shape_voxels.getOrDefault(state, Shapes.block());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
builder.add(TEXTURE_VARIANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean attachesTo(BlockState facingState, LevelReader world, BlockPos facingPos, Direction side) {
|
||||||
|
final Block block = facingState.getBlock();
|
||||||
|
if ((block instanceof FenceGateBlock) || (block instanceof WallBlock)) return true;
|
||||||
|
final BlockState oppositeState = world.getBlockState(facingPos.relative(side, 2));
|
||||||
|
if (!(oppositeState.getBlock() instanceof VariantWallBlock)) return false;
|
||||||
|
return facingState.isRedstoneConductor(world, facingPos) && Block.canSupportCenter(world, facingPos, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WallSide selectWallHeight(LevelReader world, BlockPos pos, Direction direction) {
|
||||||
|
return WallSide.LOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
LevelReader world = context.getLevel();
|
||||||
|
BlockPos pos = context.getClickedPos();
|
||||||
|
FluidState fs = context.getLevel().getFluidState(context.getClickedPos());
|
||||||
|
boolean n = attachesTo(world.getBlockState(pos.north()), world, pos.north(), Direction.SOUTH);
|
||||||
|
boolean e = attachesTo(world.getBlockState(pos.east()), world, pos.east(), Direction.WEST);
|
||||||
|
boolean s = attachesTo(world.getBlockState(pos.south()), world, pos.south(), Direction.NORTH);
|
||||||
|
boolean w = attachesTo(world.getBlockState(pos.west()), world, pos.west(), Direction.EAST);
|
||||||
|
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
|
||||||
|
return defaultBlockState().setValue(UP, not_straight)
|
||||||
|
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_EAST, e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
|
||||||
|
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_WEST, w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE)
|
||||||
|
.setValue(WATERLOGGED, fs.getType() == Fluids.WATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState updateShape(BlockState state, Direction side, BlockState facingState, LevelAccessor world, BlockPos pos, BlockPos facingPos) {
|
||||||
|
if (state.getValue(WATERLOGGED)) world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world));
|
||||||
|
if (side == Direction.DOWN) return super.updateShape(state, side, facingState, world, pos, facingPos);
|
||||||
|
boolean n = (side == Direction.NORTH) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_NORTH) != WallSide.NONE;
|
||||||
|
boolean e = (side == Direction.EAST) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_EAST) != WallSide.NONE;
|
||||||
|
boolean s = (side == Direction.SOUTH) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_SOUTH) != WallSide.NONE;
|
||||||
|
boolean w = (side == Direction.WEST) ? this.attachesTo(facingState, world, facingPos, side) : state.getValue(WALL_WEST) != WallSide.NONE;
|
||||||
|
boolean not_straight = (!n || !s || e || w) && (n || s || !e || !w);
|
||||||
|
return state.setValue(UP, not_straight)
|
||||||
|
.setValue(WALL_NORTH, n ? selectWallHeight(world, pos, Direction.NORTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_EAST, e ? selectWallHeight(world, pos, Direction.EAST) : WallSide.NONE)
|
||||||
|
.setValue(WALL_SOUTH, s ? selectWallHeight(world, pos, Direction.SOUTH) : WallSide.NONE)
|
||||||
|
.setValue(WALL_WEST, w ? selectWallHeight(world, pos, Direction.WEST) : WallSide.NONE)
|
||||||
|
.setValue(TEXTURE_VARIANT, ((int) Mth.getSeed(pos)) & 0x7);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValidSpawn(BlockState state, BlockGetter world, BlockPos pos, SpawnPlacements.Type type, @Nullable EntityType<?> entityType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPossibleToRespawnInThis(BlockState state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public PushReaction getPistonPushReaction(BlockState state) {
|
||||||
|
return PushReaction.NORMAL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ public class ForgeEventHandlers {
|
||||||
|
|
||||||
MinecraftForge.EVENT_BUS.post(new ProfileLoadedEvent(prof, player, level));
|
MinecraftForge.EVENT_BUS.post(new ProfileLoadedEvent(prof, player, level));
|
||||||
|
|
||||||
DelayedExecutorService.getInstance().schedule(new Task("send-msg", true) {
|
Thread tx = new Thread(new Task("send-msg", true) {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Check player wallet, then send wallet to client
|
// Check player wallet, then send wallet to client
|
||||||
|
@ -61,7 +61,8 @@ public class ForgeEventHandlers {
|
||||||
S2CServerAvailable avail = new S2CServerAvailable();
|
S2CServerAvailable avail = new S2CServerAvailable();
|
||||||
avail.send(player);
|
avail.send(player);
|
||||||
}
|
}
|
||||||
}, 10);
|
});
|
||||||
|
tx.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package dev.zontreck.libzontreck.events;
|
||||||
|
|
||||||
|
import dev.zontreck.libzontreck.vectors.WorldPosition;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraftforge.eventbus.api.Cancelable;
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event should be cancelled if a Teleport Implementation is provided and handles the teleport
|
||||||
|
* <br/>
|
||||||
|
* The event not being cancelled should indicate that the sender should handle teleport themselves.
|
||||||
|
*/
|
||||||
|
@Cancelable
|
||||||
|
public class TeleportEvent extends Event
|
||||||
|
{
|
||||||
|
WorldPosition position;
|
||||||
|
ServerPlayer player;
|
||||||
|
|
||||||
|
public TeleportEvent(WorldPosition position, ServerPlayer player)
|
||||||
|
{
|
||||||
|
this.position=position;
|
||||||
|
this.player=player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerPlayer getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldPosition getPosition() {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package dev.zontreck.libzontreck.items;
|
||||||
|
|
||||||
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.items.ItemStackHandler;
|
||||||
|
|
||||||
|
public class InputItemStackHandler extends ItemStackHandler {
|
||||||
|
private final ItemStackHandler internalSlot;
|
||||||
|
|
||||||
|
public InputItemStackHandler(ItemStackHandler hidden) {
|
||||||
|
super();
|
||||||
|
internalSlot = hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSize(int size) {
|
||||||
|
stacks = NonNullList.<ItemStack>withSize(size, ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStackInSlot(int slot, ItemStack stack) {
|
||||||
|
internalSlot.setStackInSlot(slot, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSlots() {
|
||||||
|
return internalSlot.getSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getStackInSlot(int slot) {
|
||||||
|
return internalSlot.getStackInSlot(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
|
||||||
|
setStackInSlot(slot, stack);
|
||||||
|
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack extractItem(int slot, int amount, boolean simulate) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package dev.zontreck.libzontreck.items;
|
||||||
|
|
||||||
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.items.ItemStackHandler;
|
||||||
|
|
||||||
|
public class OutputItemStackHandler extends ItemStackHandler {
|
||||||
|
private final ItemStackHandler internalSlot;
|
||||||
|
|
||||||
|
public OutputItemStackHandler(ItemStackHandler hidden) {
|
||||||
|
super();
|
||||||
|
internalSlot = hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSize(int size) {
|
||||||
|
stacks = NonNullList.<ItemStack>withSize(size, ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStackInSlot(int slot, ItemStack stack) {
|
||||||
|
internalSlot.setStackInSlot(slot, stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSlots() {
|
||||||
|
return internalSlot.getSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getStackInSlot(int slot) {
|
||||||
|
return internalSlot.getStackInSlot(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack extractItem(int slot, int amount, boolean simulate) {
|
||||||
|
return internalSlot.extractItem(slot, amount, simulate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.zontreck.libzontreck.mazegen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All the basic functions a maze algorithm must implement
|
||||||
|
*/
|
||||||
|
public interface IMazeAlgorithm
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.zontreck.libzontreck.mazegen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a 16x16 chunk for a maze
|
||||||
|
*/
|
||||||
|
public class MazeChunk
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package dev.zontreck.libzontreck.mazegen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The starting point for initializing, and generating, a maze.
|
||||||
|
*/
|
||||||
|
public class MazeFactory
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
package dev.zontreck.libzontreck.networking.packets;
|
package dev.zontreck.libzontreck.networking.packets;
|
||||||
|
|
||||||
import dev.zontreck.eventsbus.Bus;
|
|
||||||
import dev.zontreck.libzontreck.currency.Transaction;
|
import dev.zontreck.libzontreck.currency.Transaction;
|
||||||
import dev.zontreck.libzontreck.currency.events.WalletUpdatedEvent;
|
import dev.zontreck.libzontreck.currency.events.WalletUpdatedEvent;
|
||||||
import dev.zontreck.libzontreck.util.ServerUtilities;
|
import dev.zontreck.libzontreck.util.ServerUtilities;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.network.NetworkDirection;
|
import net.minecraftforge.network.NetworkDirection;
|
||||||
import net.minecraftforge.network.NetworkEvent;
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
import net.minecraftforge.network.simple.SimpleChannel;
|
import net.minecraftforge.network.simple.SimpleChannel;
|
||||||
|
@ -63,7 +64,7 @@ public class S2CWalletUpdatedPacket implements IPacket
|
||||||
return ServerUtilities.handlePacket(supplier, new Runnable() {
|
return ServerUtilities.handlePacket(supplier, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Bus.Post(new WalletUpdatedEvent(ID, oldBal, balance, tx));
|
MinecraftForge.EVENT_BUS.post(new WalletUpdatedEvent(ID, oldBal, balance, tx));
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,10 +109,8 @@ public class HeadCache
|
||||||
|
|
||||||
creds.add(
|
creds.add(
|
||||||
new CreditsEntry(HeadUtilities.cachedLookup("zontreck"), "Aria (zontreck)", "Developer, Designer, Artist", "Aria is the primary developer and project maintainer"));
|
new CreditsEntry(HeadUtilities.cachedLookup("zontreck"), "Aria (zontreck)", "Developer, Designer, Artist", "Aria is the primary developer and project maintainer"));
|
||||||
creds.add(
|
creds.add(new CreditsEntry(HeadUtilities.cachedLookup("firesyde424"), "firesyde424", "Tester", "Firesyde has helped to test my mods and given feedback."));
|
||||||
new CreditsEntry(HeadUtilities.cachedLookup("PossumTheWarrior"), "PossumTheWarrior", "Tester, Adviser, Designer, Artist", "Poss has helped to test the mods from very early on. Poss has also contributed the artwork and mob model for the Possum"));
|
creds.add(new CreditsEntry(HeadUtilities.cachedLookup("EmberCat42"), "EmberCat42", "Tester", "EmberCat42 has helped to test and reported on a major bug in Night Vision"));
|
||||||
creds.add(
|
|
||||||
new CreditsEntry(HeadUtilities.cachedLookup("GemMD"), "GemMD", "Tester, Adviser, Designer", "GemMD has provided advice on marketing and development decisions for various mods"));
|
|
||||||
|
|
||||||
CREDITS = creds;
|
CREDITS = creds;
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
# Find more information on toml format here: https://github.com/toml-lang/toml
|
# Find more information on toml format here: https://github.com/toml-lang/toml
|
||||||
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
|
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
|
||||||
modLoader="javafml" #mandatory
|
modLoader="javafml" #mandatory
|
||||||
# A version range to match for said mod loader - for regular FML @Mod it will be the forge version
|
# A version range to match for said mod loader - for regular FML @Mod it will be the the FML version. This is currently 47.
|
||||||
loaderVersion="${loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
|
loaderVersion="${loader_version_range}" #mandatory
|
||||||
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
|
||||||
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
|
||||||
license="${mod_license}"
|
license="${mod_license}"
|
||||||
# A URL to refer people to when problems occur with this mod
|
# A URL to refer people to when problems occur with this mod
|
||||||
issueTrackerURL="https://github.com/zontreck/LibZontreckMod/issues" #optional
|
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
|
||||||
# A list of mods - how many allowed here is determined by the individual mod loader
|
# A list of mods - how many allowed here is determined by the individual mod loader
|
||||||
[[mods]] #mandatory
|
[[mods]] #mandatory
|
||||||
# The modid of the mod
|
# The modid of the mod
|
||||||
|
@ -20,7 +20,7 @@ modId="${mod_id}" #mandatory
|
||||||
version="${mod_version}" #mandatory
|
version="${mod_version}" #mandatory
|
||||||
# A display name for the mod
|
# A display name for the mod
|
||||||
displayName="${mod_name}" #mandatory
|
displayName="${mod_name}" #mandatory
|
||||||
# A URL to query for updates for this mod. See the JSON update specification https://docs.minecraftforge.net/en/latest/misc/updatechecker/
|
# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/
|
||||||
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
|
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
|
||||||
# A URL for the "homepage" for this mod, displayed in the mod UI
|
# A URL for the "homepage" for this mod, displayed in the mod UI
|
||||||
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
|
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
|
||||||
|
@ -36,7 +36,7 @@ authors="${mod_authors}" #optional
|
||||||
# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component.
|
# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component.
|
||||||
# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value.
|
# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value.
|
||||||
# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself.
|
# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself.
|
||||||
#displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional)
|
displayTest="IGNORE_ALL_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional)
|
||||||
|
|
||||||
# The description text for the mod (multi line!) (#mandatory)
|
# The description text for the mod (multi line!) (#mandatory)
|
||||||
description='''${mod_description}'''
|
description='''${mod_description}'''
|
||||||
|
@ -47,7 +47,7 @@ description='''${mod_description}'''
|
||||||
# Does this dependency have to exist - if not, ordering below must be specified
|
# Does this dependency have to exist - if not, ordering below must be specified
|
||||||
mandatory=true #mandatory
|
mandatory=true #mandatory
|
||||||
# The version range of the dependency
|
# The version range of the dependency
|
||||||
versionRange="${forge_version_range}" #mandatory
|
versionRange="${neo_version_range}" #mandatory
|
||||||
# An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory
|
# An ordering relationship for the dependency - BEFORE or AFTER required if the dependency is not mandatory
|
||||||
# BEFORE - This mod is loaded BEFORE the dependency
|
# BEFORE - This mod is loaded BEFORE the dependency
|
||||||
# AFTER - This mod is loaded AFTER the dependency
|
# AFTER - This mod is loaded AFTER the dependency
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
"description": {
|
"description": {
|
||||||
"text": "${mod_id} resources"
|
"text": "${mod_id} resources"
|
||||||
},
|
},
|
||||||
"pack_format": 15
|
"pack_format": ${pack_format_number}
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in a new issue