Compare commits

...
17 Commits
Author SHA1 Message Date
Lyft 9b96560119 Fix IStructureElementChain returning an invalid Iterable (#33) 2024-11-09 15:46:49 +01:00
Dream Master 3ce1c877e3 update 2024-08-07 15:42:01 +07:00
GleaseandDream Master 3d150d95cb customizable registries (#25)
* add a bit javadoc

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* customizable registries

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* add gui for customizing registries

* add a bit javadoc

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* spotless

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* remove debug statements

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* add notes on localization

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* update

* fix

* fix

---------

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
Co-authored-by: Dream Master <dream-master@gmx.net>
2024-08-07 15:37:30 +07:00
Dream Master 03e84e3d37 update 2024-07-10 11:12:56 +07:00
Jason Mitchell 547833bedb Bump NEI dep
Bump GTNHLib dep for CoordinatePacker

Bump deps
2024-07-09 09:39:45 -07:00
Jason Mitchell 61e0d4b7e7 Add basic BlockChangeNotifier & BlockChangeListener 2024-07-09 09:39:45 -07:00
Jason Mitchell 6867ed50e6 Add mixins 2024-07-09 09:39:45 -07:00
Jason Mitchell e7a47c199b Enable checkstyle 2024-07-09 09:39:45 -07:00
Jason Mitchell b2bc474382 Update BS, enable Jabel & Generic injection 2024-07-09 09:39:45 -07:00
Martin Robertz eef6baea4b update 2024-04-16 22:13:22 +02:00
Glease efd49701e1 add a new api to stack extractors
Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
2024-04-16 17:32:31 +08:00
Raven Szewczyk a7f0d6be72 [ci skip] upgraded build system 2024-02-04 20:01:59 +00:00
Glease 0ff38c2da9 revise javadoc publish (#27)
* fix deprecation

* add cleanup
2024-01-09 22:52:41 +01:00
chochem 307bb1714c Fix broken mod (#26)
* clear out repositories

* update bs

* update deps

* spotless
2024-01-09 11:42:10 +08:00
Raven Szewczyk 7d8e6a0ea5 [ci skip] upgraded build system 2024-01-08 21:29:25 +00:00
Glease b6d2af2170 add new api to check for rotation/flip changes (#24)
* add new api to check for rotation/flip changes

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* add a bit javadoc

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

---------

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
2023-09-13 14:29:39 +02:00
Glease 8309567076 fix cube faces rendering order (#22)
* fix cube faces rendering order

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* spotless

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

* address review

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>

---------

Signed-off-by: Glease <4586901+Glease@users.noreply.github.com>
2023-07-13 22:05:35 +02:00
49 changed files with 2364 additions and 1376 deletions

No files matched your search

+34 -4
View File
@@ -7,6 +7,7 @@ on:
- feature/doc
tags:
- '*'
delete:
permissions:
contents: write
@@ -14,7 +15,38 @@ permissions:
id-token: write
jobs:
prune:
if: github.event_name == 'delete'
concurrency: javadoc-publish
runs-on: ubuntu-latest
outputs:
result_hash: ${{ steps.deploy.outputs.hash }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
ref: gh-pages
- name: Deploy 🚀
id: deploy
shell: bash
run: |
if git rm -r $TARGET_FOLDER ; then
echo Nothing to clean up
fi
find . -name javadoc -type d -print | sed 's#^\./\(.*\)/javadoc$#\1#' | sort --version-sort --reverse > index.txt
git add index.txt
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR_ID}-${GITHUB_ACTOR}@users.noreply.github.com"
git commit -m "Clean up javadoc after ref $TARGET_FOLDER is deleted"
git push
echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
env:
TARGET_FOLDER: ${{ github.ref_name }}
javadoc:
if: github.event_name == 'push'
concurrency: javadoc-publish
runs-on: ubuntu-latest
outputs:
@@ -57,11 +89,9 @@ jobs:
find . -name javadoc -type d -print | sed 's#^\./\(.*\)/javadoc$#\1#' | sort --version-sort --reverse > index.txt
git add index.txt
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
git config user.email "${GITHUB_ACTOR_ID}-${GITHUB_ACTOR}@users.noreply.github.com"
git commit -m "Deploy javadoc from ref $TARGET_FOLDER"
git push
echo "::set-output name=hash::$(git rev-parse HEAD)"
echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
env:
TARGET_FOLDER: ${{ github.ref_name }}
COMMIT_AUTHOR: ${{ github.pusher.name }}
COMMIT_EMAIL: ${{ github.pusher.email }}
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+2
View File
@@ -32,3 +32,5 @@ The project is developed using IDEA. Please manually configure the build to not
Due to me adding in ItemStackMap, which was derived from code from CodeChicken's LGPL mod NEI. This mod is effectively considered
licensed under LGPL in its entirety since commit ebef584b8c518f8202cfe0984fb28a6d23e52b3d or any squash commit containing it, e.g.
609e50428ee4e293f65f67f554a70b9fcfae97c4.
Note while the LICENSE.txt is added by a much later commit, all code on previous commits are covered by this as well.
+2 -1259
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,8 +1,8 @@
// Add your dependencies here
dependencies {
api("com.github.GTNewHorizons:GTNHLib:0.0.10:dev")
runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.3.46-GTNH:dev")
api("com.github.GTNewHorizons:GTNHLib:0.5.19:dev")
runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.6.44-GTNH:dev")
testImplementation(platform('org.junit:junit-bom:5.9.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
+170 -27
View File
@@ -1,60 +1,203 @@
# ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empty to disable.
# LOCAL to test local config updates.
gtnh.settings.blowdryerTag = 0.2.0
# Human-readable mod name, available for mcmod.info population.
modName = StructureLib
# This is a case-sensitive string to identify your mod. Convention is to use lower case.
# Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation.
# Conventionally lowercase.
modId = structurelib
# Root package of the mod, used to find various classes in other properties,
# mcmod.info substitution, enabling assertions in run tasks, etc.
modGroup = com.gtnewhorizon.structurelib
# WHY is there no version field?
# The build script relies on git to provide a version via tags. It is super easy and will enable you to always know the
# code base or your binary. Check out this tutorial: https://blog.mattclemente.com/2017/10/13/versioning-with-git-tags/
# Whether to use modGroup as the maven publishing group.
# Due to a history of using JitPack, the default is com.github.GTNewHorizons for all mods.
useModGroupForPublishing = false
# Will update your build.gradle automatically whenever an update is available
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
autoUpdateBuildScript = false
# Version of Minecraft to target
minecraftVersion = 1.7.10
# Version of Minecraft Forge to target
forgeVersion = 10.13.4.1614
# Select a username for testing your mod with breakpoints. You may leave this empty for a random user name each time you
# restart Minecraft in development. Choose this dependent on your mod:
# Do you need consistent player progressing (for example Thaumcraft)? -> Select a name
# Do you need to test how your custom blocks interacts with a player that is not the owner? -> leave name empty
# Specify an MCP channel for dependency deobfuscation and the deobfParams task.
channel = stable
# Specify an MCP mappings version for dependency deobfuscation and the deobfParams task.
mappingsVersion = 12
# Defines other MCP mappings for dependency deobfuscation.
remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/
# Select a default username for testing your mod. You can always override this per-run by running
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = "Developer"
# Define a source file of your project with:
# public static final String VERSION = "GRADLETOKEN_VERSION";
# The string's content will be replaced with your mods version when compiled. You should use this to specify your mod's
# version in @Mod([...], version = VERSION, [...])
# Leave these properties empty to skip individual token replacements
replaceGradleTokenInFile = StructureLib.java
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = true
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
enableGenericInjection = true
# Generate a class with a String field for the mod version named as defined below.
# If generateGradleTokenClass is empty or not missing, no such class will be generated.
# If gradleTokenVersion is empty or missing, the field will not be present in the class.
generateGradleTokenClass = com.gtnewhorizon.structurelib.Tags
# Name of the token containing the project's current version to generate/replace.
gradleTokenVersion = VERSION
# [DEPRECATED] Mod ID replacement token.
gradleTokenModId =
# [DEPRECATED] Mod name replacement token.
gradleTokenModName =
gradleTokenVersion = GRADLETOKEN_VERSION
# [DEPRECATED] Mod Group replacement token.
gradleTokenGroupName =
# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise you can
# [DEPRECATED]
# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
# public static final String VERSION = "GRADLETOKEN_VERSION";
# The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's
# version in @Mod([...], version = VERSION, [...]).
# Leave these properties empty to skip individual token replacements.
replaceGradleTokenInFile =
# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can
# leave this property empty.
# Example value: apiPackage = api + modGroup = com.myname.mymodid -> com.myname.mymodid.api
# Example value: (apiPackage = api) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.api
apiPackage =
# Specify the configuration file for Forge's access transformers here. I must be placed into /src/main/resources/META-INF/
# Example value: mymodid_at.cfg
# Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/
# There can be multiple files in a space-separated list.
# Example value: mymodid_at.cfg nei_at.cfg
accessTransformersFile =
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
usesMixins = false
usesMixins = true
# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
# This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
# Mixin classes will have access to "main" classes, but not the other way around.
separateMixinSourceSet =
# Adds some debug arguments like verbose output and class export.
usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
mixinsPackage =
mixinsPackage = mixins
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
# This parameter is for legacy compatability only
# Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin
coreModClass =
# This parameter is for legacy compatibility only
# Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin
coreModClass = core.StructureLibCore
# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
containsMixinsAndOrCoreModOnly = false
# If enabled, you may use 'shadowImplementation' for dependencies. They will be integrated in your jar. It is your
# responsibility check the licence and request permission for distribution, if required.
# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins.
forceEnableMixins = false
# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated into your jar. It is your
# responsibility to check the license and request permission for distribution if required.
usesShadowedDependencies = false
# If disabled, won't remove unused classes from shadowed dependencies. Some libraries use reflection to access
# their own classes, making the minimization unreliable.
minimizeShadowedDependencies = true
# If disabled, won't rename the shadowed classes.
relocateShadowedDependencies = true
# Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
includeWellKnownRepositories = true
# A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated
# list of strings, with the acceptable keys being(case does not matter):
# cursemaven
# modrinth
excludeWellKnownRepositories =
# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
usesMavenPublishing = true
# Maven repository to publish the mod to.
# mavenPublishUrl = https\://nexus.gtnewhorizons.com/repository/releases/
# Publishing to Modrinth requires you to set the MODRINTH_TOKEN environment variable to your current Modrinth API token.
#
# The project's ID on Modrinth. Can be either the slug or the ID.
# Leave this empty if you don't want to publish to Modrinth.
modrinthProjectId =
# The project's relations on Modrinth. You can use this to refer to other projects on Modrinth.
# Syntax: scope1-type1:name1;scope2-type2:name2;...
# Where scope can be one of [required, optional, incompatible, embedded],
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
modrinthRelations =
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
#
# The project's numeric ID on CurseForge. You can find this in the About Project box.
# Leave this empty if you don't want to publish on CurseForge.
curseForgeProjectId =
# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
# Syntax: type1:name1;type2:name2;...
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
# and the name is the CurseForge project slug of the other mod.
# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
curseForgeRelations =
# Optional parameter to customize the produced artifacts. Use this to preserve artifact naming when migrating older
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
# Note: the specified string must be escaped, so e.g. 1\\.1\\.\\d+ instead of 1\.1\.\d+
# versionPattern =
# Uncomment to prevent the source code from being published.
# noPublishedSources = true
# Uncomment this to disable Spotless checks.
# This should only be uncommented to keep it easier to sync with upstream/other forks.
# That is, if there is no other active fork/upstream, NEVER change this.
# disableSpotless = true
# Uncomment this to disable Checkstyle checks (currently wildcard import check).
# disableCheckstyle = true
# Override the IDEA build type. Valid values are: "" (leave blank, do not override), "idea" (force use native IDEA build), "gradle"
# (force use delegated build).
# This is meant to be set in $HOME/.gradle/gradle.properties.
# e.g. add "systemProp.org.gradle.project.ideaOverrideBuildType=idea" will override the build type to be native build.
# WARNING: If you do use this option, it will overwrite whatever you have in your existing projects. This might not be what you want!
# Usually there is no need to uncomment this here as other developers do not necessarily use the same build type as you.
# ideaOverrideBuildType = idea
# Whether IDEA should run spotless checks when pressing the Build button.
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
Binary file not shown.
+2 -1
View File
@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+21 -13
View File
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -83,10 +85,9 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,10 +134,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
Vendored
+12 -10
View File
@@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
-4
View File
@@ -1,8 +1,4 @@
// Add any additional repositiroes for your dependencies here
repositories {
maven {
name = "GTNH"
url = "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
}
}
+5 -10
View File
@@ -4,10 +4,10 @@ pluginManagement {
maven {
// RetroFuturaGradle
name "GTNH Maven"
url "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
allowInsecureProtocol = true
url "https://nexus.gtnewhorizons.com/repository/public/"
mavenContent {
includeGroup("com.gtnewhorizons.retrofuturagradle")
includeGroup("com.gtnewhorizons")
includeGroupByRegex("com\\.gtnewhorizons\\..+")
}
}
gradlePluginPortal()
@@ -17,12 +17,7 @@ pluginManagement {
}
plugins {
id 'com.diffplug.blowdryerSetup' version '1.6.0'
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' // Provides java toolchains
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.29'
}
blowdryerSetup {
repoSubfolder 'gtnhShared'
github('GTNewHorizons/ExampleMod1.7.10', 'tag', '0.2.0')
//devLocal '.' // Use this when testing config updates locally
}
@@ -2,15 +2,24 @@ package com.gtnewhorizon.structurelib;
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
import java.util.*;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraft.client.gui.GuiNewChat;
import net.minecraft.client.particle.EntityFX;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.culling.Frustrum;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
@@ -23,18 +32,27 @@ import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.world.WorldEvent;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventPriority;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.common.network.FMLNetworkEvent;
public class ClientProxy extends CommonProxy {
@@ -46,7 +64,6 @@ public class ClientProxy extends CommonProxy {
*/
private static final List<HintGroup> allGroups = new ArrayList<>();
public static int HOLOGRAM_LIFETIME = 200;
/**
* Current batch of hints. Belongs to the same logical multiblock.
*/
@@ -247,6 +264,47 @@ public class ClientProxy extends CommonProxy {
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
}
static void sendRegistryOrderToServer() {
if (FMLEventHandler.configSent) return;
FMLEventHandler.configSent = true;
for (String s : SortedRegistry.ALL_REGISTRIES.keySet()) {
Pair<List<String>, List<String>> registryOrder = ConfigurationHandler.INSTANCE.getRegistryOrder(s);
if (registryOrder == null || registryOrder.getKey().isEmpty() && registryOrder.getValue().isEmpty())
continue;
StructureLib.net
.sendToServer(new RegistryOrderSyncMessage(s, registryOrder.getKey(), registryOrder.getValue()));
}
}
@Override
public void loadComplete(FMLLoadCompleteEvent evt) {
super.loadComplete(evt);
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
SortedRegistry<?> r = e.getValue().get();
if (r == null) continue;
Pair<List<String>, List<String>> p = ConfigurationHandler.INSTANCE.getRegistryOrder(e.getKey());
r.registerOrdering(getSPPlayerUUID(), p.getKey(), p.getValue());
}
}
public UUID getSPPlayerUUID() {
return EntityPlayer.func_146094_a(Minecraft.getMinecraft().getSession().func_148256_e());
}
public void displayConfigGUI(String category) {
ConfigElement<Object> element = new ConfigElement<>(
ConfigurationHandler.INSTANCE.getConfig().getCategory(category));
GuiConfig guiConfig = new GuiConfig(
null,
element.getChildElements(),
StructureLibAPI.MOD_ID,
null,
false,
false,
I18n.format(element.getLanguageKey()));
Minecraft.getMinecraft().displayGuiScreen(guiConfig);
}
private static class HintGroup {
private final List<HintParticleInfo> hints = new LinkedList<>();
@@ -335,26 +393,32 @@ public class ClientProxy extends CommonProxy {
double X = (x - eyeXint) + 0.25;
double Y = (y - eyeYint) + 0.25;
double Z = (z - eyeZint) + 0.25;
double worldX = x + 0.25;
double worldY = y + 0.25;
double worldZ = z + 0.25;
for (int i = 0; i < 6; i++) {
if (icons[i] == null) continue;
// this rendering code is independently written by glee8e on July 10th, 2023
// and is released as part of StructureLib under LGPL terms, just like everything else in this project
// cube is a very special model. its facings can be rendered correctly by viewer distance without using
// surface normals and view vector
// here we do a 2 pass render.
// first pass we draw obstructed faces (i.e. faces that are further away from player)
// second pass we draw unobstructed faces
for (int j = 0; j < 2; j++) {
boolean unobstructedPass = j == 1;
for (int i = 0; i < 6; i++) {
if (icons[i] == null) continue;
markTextureUsed(icons[i]);
markTextureUsed(icons[i]);
double u = icons[i].getMinU();
double U = icons[i].getMaxU();
double v = icons[i].getMinV();
double V = icons[i].getMaxV();
// cube is a very special model. its facings can be rendered correctly by viewer distance without using
// surface normals and view vector
// here we do a 2 pass render.
// first pass we draw obstructed faces (i.e. faces that are further away from player)
// second pass we draw unobstructed faces
for (int j = 0; j < 2; j++) {
double u = icons[i].getMinU();
double U = icons[i].getMaxU();
double v = icons[i].getMinV();
double V = icons[i].getMaxV();
switch (i) { // {DOWN, UP, NORTH, SOUTH, WEST, EAST}
case 0:
if ((Y >= eyeY) != (j == 1)) continue;
// all these ifs is in form if ((is face unobstructed) != (is in unobstructred pass))
if ((worldY >= eyeY) != unobstructedPass) continue;
tes.setNormal(0, -1, 0);
tes.addVertexWithUV(X, Y, Z + size, u, V);
tes.addVertexWithUV(X, Y, Z, u, v);
@@ -364,7 +428,7 @@ public class ClientProxy extends CommonProxy {
tes.addVertexWithUV(X, Y, Z + size, u, V);
break;
case 1:
if ((Y + size <= eyeY) != (j == 1)) continue;
if ((worldY + size <= eyeY) != unobstructedPass) continue;
tes.setNormal(0, 1, 0);
tes.addVertexWithUV(X, Y + size, Z, u, v);
tes.addVertexWithUV(X, Y + size, Z + size, u, V);
@@ -374,7 +438,7 @@ public class ClientProxy extends CommonProxy {
tes.addVertexWithUV(X, Y + size, Z, u, v);
break;
case 2:
if ((Z >= eyeZ) != (j == 1)) continue;
if ((worldZ >= eyeZ) != unobstructedPass) continue;
tes.setNormal(0, 0, -1);
tes.addVertexWithUV(X, Y, Z, U, V);
tes.addVertexWithUV(X, Y + size, Z, U, v);
@@ -384,7 +448,7 @@ public class ClientProxy extends CommonProxy {
tes.addVertexWithUV(X, Y, Z, U, V);
break;
case 3:
if ((Z <= eyeZ) != (j == 1)) continue;
if ((worldZ + size <= eyeZ) != unobstructedPass) continue;
tes.setNormal(0, 0, 1);
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
tes.addVertexWithUV(X + size, Y + size, Z + size, U, v);
@@ -394,7 +458,7 @@ public class ClientProxy extends CommonProxy {
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
break;
case 4:
if ((X >= eyeX) != (j == 1)) continue;
if ((worldX >= eyeX) != unobstructedPass) continue;
tes.setNormal(-1, 0, 0);
tes.addVertexWithUV(X, Y, Z + size, U, V);
tes.addVertexWithUV(X, Y + size, Z + size, U, v);
@@ -404,7 +468,7 @@ public class ClientProxy extends CommonProxy {
tes.addVertexWithUV(X, Y, Z + size, U, V);
break;
case 5:
if ((X + size <= eyeX) != (j == 1)) continue;
if ((worldX + size <= eyeX) != unobstructedPass) continue;
tes.setNormal(1, 0, 0);
tes.addVertexWithUV(X + size, Y, Z, U, V);
tes.addVertexWithUV(X + size, Y + size, Z, U, v);
@@ -429,12 +493,34 @@ public class ClientProxy extends CommonProxy {
public static class FMLEventHandler {
static boolean connected = false;
static boolean configSent = false;
private void resetPlayerLocation() {
lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX;
lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY;
lastPlayerPos.zCoord = Minecraft.getMinecraft().thePlayer.posZ;
}
@SubscribeEvent
public void onPlayerLogIn(FMLNetworkEvent.ClientConnectedToServerEvent e) {
connected = true;
configSent = false;
}
@SubscribeEvent
public void onPlayerLogOut(FMLNetworkEvent.ClientDisconnectionFromServerEvent e) {
connected = false;
}
@SubscribeEvent(priority = EventPriority.LOWEST)
public void onConfigChange(ConfigChangedEvent.PostConfigChangedEvent e) {
if (connected && e.modID.equals(StructureLibAPI.MOD_ID)) {
configSent = false;
sendRegistryOrderToServer();
}
}
@SubscribeEvent
public void onClientTick(ClientTickEvent e) {
if (e.phase == Phase.END && Minecraft.getMinecraft().thePlayer != null) {
@@ -478,6 +564,13 @@ public class ClientProxy extends CommonProxy {
public static class ForgeEventHandler {
@SubscribeEvent
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
sendRegistryOrderToServer();
}
}
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load e) {
if (e.world.isRemote) {
@@ -550,7 +643,6 @@ public class ClientProxy extends CommonProxy {
tes.draw();
tes.startDrawing(GL11.GL_TRIANGLES);
}
// TODO verify if we need to add eyeHeight
hint.draw(tes, d0, d1, d2, i0, i1, i2);
}
p.endStartSection("Draw");
@@ -12,13 +12,23 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
public class CommonProxy {
public CommonProxy() {
super();
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
}
public void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {}
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {}
@@ -69,6 +79,10 @@ public class CommonProxy {
}
}
public void loadComplete(FMLLoadCompleteEvent e) {
ConfigurationHandler.INSTANCE.loadRegistryOrder();
}
private final Map<EntityPlayerMP, Map<Object, Long>> throttleMap = new WeakHashMap<>();
public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
@@ -93,4 +107,22 @@ public class CommonProxy {
if (!forceUpdateLastSend) submap.put(throttleKey, now);
}
}
public void displayConfigGUI(String category) {}
public static class ForgeEventHandler {
private int ticksSinceLastPurge;
@SubscribeEvent
public void onServerTickEnd(TickEvent.ServerTickEvent e) {
if (e.phase != TickEvent.Phase.END) return;
if (++ticksSinceLastPurge < 20 * 60 * 10) return;
ticksSinceLastPurge = 0;
MinecraftServer server = MinecraftServer.getServer();
// it's hard to tell why server could be null in a server tick handler, but who knows?
if (server == null) return;
SortedRegistry.cleanup(server);
}
}
}
@@ -1,15 +1,26 @@
package com.gtnewhorizon.structurelib;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
public enum ConfigurationHandler {
@@ -22,6 +33,7 @@ public enum ConfigurationHandler {
private int hintTransparency;
private int autoPlaceBudget;
private int autoPlaceInterval;
private Map<String, Pair<List<String>, List<String>>> registryOrders;
ConfigurationHandler() {
FMLCommonHandler.instance().bus().register(this);
@@ -86,8 +98,50 @@ public enum ConfigurationHandler {
+ "Note this relates to the wall clock, not in game ticks.\n"
+ "Value smaller than default is likely to be perceived as no minimal interval whatsoever.");
loadRegistryOrder();
saveConfig();
}
void loadRegistryOrder() {
loadRegistryOrderImpl();
setLanguageKeys();
saveConfig();
}
private void loadRegistryOrderImpl() {
registryOrders = new HashMap<>();
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
SortedRegistry<?> r = e.getValue().get();
if (r == null) continue;
String category = "registries." + e.getKey();
if (FMLLaunchHandler.side().isClient()) {
// only meaningful on client, and would crash on server
config.setCategoryConfigEntryClass(category, RegistryOrderEntry.class);
}
Property pOrder = config.get(
category,
"ordering",
Iterables.toArray(r.getCurrentOrdering(), String.class),
"stuff not in this list will be automatically available after all entries listed here in their natural order, unless explicitly disabled in disabled config below.");
Property pDisable = config.get(category, "disabled", new String[0], "stuff in this list will be disabled");
List<String> all = Lists.newArrayList(r.getCurrentOrdering());
List<String> curVal = new ArrayList<>(Arrays.asList(pOrder.getStringList()));
List<String> disabled = new ArrayList<>(Arrays.asList(pDisable.getStringList()));
curVal.removeAll(disabled);
all.removeAll(disabled);
curVal.removeIf(s -> !all.remove(s));
curVal.addAll(all);
pOrder.set(curVal.toArray(new String[0]));
registryOrders.put(e.getKey(), Pair.of(curVal, disabled));
}
saveConfig();
}
private void saveConfig() {
if (config.hasChanged()) {
config.save();
config.load();
}
}
@@ -122,7 +176,12 @@ public enum ConfigurationHandler {
return autoPlaceInterval;
}
public Pair<List<String>, List<String>> getRegistryOrder(String name) {
return registryOrders.get(name);
}
Configuration getConfig() {
return config;
}
}
@@ -0,0 +1,702 @@
/*
* Forge Mod Loader Copyright (c) 2012-2014 cpw. All rights reserved. This program and the accompanying materials are
* made available under the terms of the GNU Lesser Public License v2.1 which accompanies this distribution, and is
* available at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html Contributors (this class): bspkrs - implementation
*/
package com.gtnewhorizon.structurelib;
import static cpw.mods.fml.client.config.GuiUtils.RESET_CHAR;
import static cpw.mods.fml.client.config.GuiUtils.UNDO_CHAR;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.WeakHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiListExtended;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import cpw.mods.fml.client.config.GuiButtonExt;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiUnicodeGlyphButton;
import cpw.mods.fml.client.config.GuiUtils;
import cpw.mods.fml.client.config.HoverChecker;
import cpw.mods.fml.client.config.IConfigElement;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
import cpw.mods.fml.client.event.ConfigChangedEvent.PostConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.eventhandler.Event.Result;
/**
* This class is the base GuiScreen for all config GUI screens. It can be extended by mods to provide the top-level
* config screen that will be called when the Config button is clicked from the Main Menu Mods list.
*
* @author bspkrs
*/
public class GuiEditRegistryConfig extends GuiScreen {
public static final String OMITTED = "..";
public static final int LIST_WINDOW_PADDING = 20;
public static final int MID_SECTION_WIDTH = 10;
/**
* A reference to the screen object that created this. Used for navigating between screens.
*/
public final GuiScreen parentScreen;
private final IConfigElement<String> disabledElement;
private final String[] beforeDisabledValue;
private final IConfigElement<String> orderElement;
private final String[] beforeOrderValue;
final Collection<EntriesList> lists;
private final String registryName;
EntriesList disabledEntries;
EntriesList orderEntries;
public String title = "Config GUI";
public String titleLine2;
private GuiButtonExt btnDefault;
private GuiButtonExt btnUndoAll;
public final String modID;
/**
* When set to a non-null value the OnConfigChanged and PostConfigChanged events will be posted when the Done button
* is pressed if any configElements were changed (includes child screens). If not defined, the events will be posted
* if the parent gui is null or if the parent gui is not an instance of GuiConfig.
*/
public final String configID;
public final boolean isWorldRunning;
public final boolean allRequireWorldRestart;
public final boolean allRequireMcRestart;
public boolean needsRefresh = true;
private HoverChecker undoHoverChecker;
private HoverChecker resetHoverChecker;
@SuppressWarnings({ "rawtypes", "unchecked" })
public GuiEditRegistryConfig(GuiScreen parentScreen, IConfigElement disabled, IConfigElement order,
String registryName, String modID, String configID, boolean allRequireWorldRestart,
boolean allRequireMcRestart, String title, String titleLine2) {
this.mc = parentScreen.mc;
this.lists = Collections.newSetFromMap(new WeakHashMap<>());
this.disabledElement = disabled;
this.beforeDisabledValue = (String[]) this.disabledElement.getList();
this.orderElement = order;
this.beforeOrderValue = (String[]) this.orderElement.getList();
this.parentScreen = parentScreen;
this.allRequireWorldRestart = allRequireWorldRestart;
this.allRequireMcRestart = allRequireMcRestart;
this.registryName = registryName;
this.modID = modID;
this.configID = configID;
this.isWorldRunning = mc.theWorld != null;
if (title != null) this.title = title;
this.titleLine2 = titleLine2;
if (this.titleLine2 != null && this.titleLine2.startsWith(" > "))
this.titleLine2 = this.titleLine2.replaceFirst(" > ", "");
}
/**
* Adds the buttons (and other controls) to the screen in question.
*/
@SuppressWarnings("unchecked")
@Override
public void initGui() {
Keyboard.enableRepeatEvents(true);
if (this.disabledEntries == null || this.needsRefresh) {
this.disabledEntries = new EntriesList(
(width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING,
LIST_WINDOW_PADDING,
I18n.format("structurelib.configgui.disabled"));
this.disabledEntries.initEntries(Arrays.asList(disabledElement.getList()));
} else {
this.disabledEntries.reinit((width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING, LIST_WINDOW_PADDING);
}
if (this.orderEntries == null || this.needsRefresh) {
this.orderEntries = new EntriesList(
(width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING,
(width + MID_SECTION_WIDTH) / 2,
I18n.format("structurelib.configgui.enabled"));
this.orderEntries.initEntries(Arrays.asList(orderElement.getList()));
} else {
this.orderEntries
.reinit((width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING, (width + MID_SECTION_WIDTH) / 2);
}
this.needsRefresh = false;
int undoGlyphWidth = mc.fontRenderer.getStringWidth(UNDO_CHAR) * 2;
int resetGlyphWidth = mc.fontRenderer.getStringWidth(RESET_CHAR) * 2;
int doneWidth = Math.max(mc.fontRenderer.getStringWidth(I18n.format("gui.done")) + 20, 100);
int undoWidth = mc.fontRenderer.getStringWidth(" " + I18n.format("fml.configgui.tooltip.undoChanges"))
+ undoGlyphWidth
+ 20;
int resetWidth = mc.fontRenderer.getStringWidth(" " + I18n.format("fml.configgui.tooltip.resetToDefault"))
+ resetGlyphWidth
+ 20;
int buttonWidthHalf = (doneWidth + 5 + undoWidth + 5 + resetWidth + 5) / 2;
this.buttonList.add(
new GuiButtonExt(
2000,
this.width / 2 - buttonWidthHalf,
this.height - 29,
doneWidth,
20,
I18n.format("gui.done")));
this.buttonList.add(
this.btnDefault = new GuiUnicodeGlyphButton(
2001,
this.width / 2 - buttonWidthHalf + doneWidth + 5 + undoWidth + 5,
this.height - 29,
resetWidth,
20,
" " + I18n.format("fml.configgui.tooltip.resetToDefault"),
RESET_CHAR,
2.0F));
this.buttonList.add(
btnUndoAll = new GuiUnicodeGlyphButton(
2002,
this.width / 2 - buttonWidthHalf + doneWidth + 5,
this.height - 29,
undoWidth,
20,
" " + I18n.format("fml.configgui.tooltip.undoChanges"),
UNDO_CHAR,
2.0F));
this.undoHoverChecker = new HoverChecker(this.btnUndoAll, 800);
this.resetHoverChecker = new HoverChecker(this.btnDefault, 800);
}
/**
* Called when the screen is unloaded. Used to disable keyboard repeat events
*/
@Override
public void onGuiClosed() {
if (this.configID != null && this.parentScreen instanceof GuiConfig) {
GuiConfig parentGuiConfig = (GuiConfig) this.parentScreen;
parentGuiConfig.needsRefresh = true;
parentGuiConfig.initGui();
}
if (!(this.parentScreen instanceof GuiConfig)) Keyboard.enableRepeatEvents(false);
}
public boolean saveConfigElements() {
if (isChanged()) {
disabledElement.set(disabledEntries.entries.stream().map(e -> e.name).toArray(String[]::new));
orderElement.set(orderEntries.entries.stream().map(e -> e.name).toArray(String[]::new));
}
return false;
}
@Override
protected void actionPerformed(GuiButton button) {
if (button.id == 2000) {
try {
if ((configID != null || this.parentScreen == null || !(this.parentScreen instanceof GuiConfig))
&& isChanged()) {
saveConfigElements();
if (Loader.isModLoaded(modID)) {
ConfigChangedEvent event = new OnConfigChangedEvent(modID, configID, isWorldRunning, false);
FMLCommonHandler.instance().bus().post(event);
if (!event.getResult().equals(Result.DENY)) FMLCommonHandler.instance().bus()
.post(new PostConfigChangedEvent(modID, configID, isWorldRunning, false));
if (this.parentScreen instanceof GuiConfig) ((GuiConfig) this.parentScreen).needsRefresh = true;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
this.mc.displayGuiScreen(this.parentScreen);
} else if (button.id == 2001) {
setToDefault();
} else if (button.id == 2002) {
undoChanges();
}
}
public boolean isInitialized() {
return disabledEntries != null && orderEntries != null;
}
public void undoChanges() {
disabledEntries.initEntries(Arrays.asList(beforeDisabledValue));
orderEntries.initEntries(Arrays.asList(beforeOrderValue));
}
public void setToDefault() {
disabledEntries.initEntries(Arrays.asList(disabledElement.getList()));
orderEntries.initEntries(Arrays.asList(orderElement.getList()));
}
public boolean isDefault() {
return !orderEntries.areContentEqual(orderElement.getDefaults())
|| !disabledEntries.areContentEqual(disabledElement.getDefaults());
}
public boolean isChanged() {
return !orderEntries.areContentEqual(beforeOrderValue) || !disabledEntries.areContentEqual(beforeDisabledValue);
}
/**
* Called when the mouse is clicked.
*/
@Override
protected void mouseClicked(int x, int y, int mouseEvent) {
if (mouseEvent != 0 || !this.disabledEntries.func_148179_a(x, y, mouseEvent)
&& !this.orderEntries.func_148179_a(x, y, mouseEvent)) {
super.mouseClicked(x, y, mouseEvent);
}
}
/**
* Called when the mouse is moved or a mouse button is released. Signature: (mouseX, mouseY, which) which==-1 is
* mouseMove, which==0 or which==1 is mouseUp
*/
@Override
protected void mouseMovedOrUp(int x, int y, int mouseEvent) {
if (mouseEvent != 0 || !this.disabledEntries.func_148181_b(x, y, mouseEvent)
&& !this.orderEntries.func_148181_b(x, y, mouseEvent)) {
super.mouseMovedOrUp(x, y, mouseEvent);
}
}
/**
* Fired when a key is typed. This is the equivalent of KeyListener.keyTyped(KeyEvent e).
*/
@Override
protected void keyTyped(char eventChar, int eventKey) {
if (eventKey == Keyboard.KEY_ESCAPE) this.mc.displayGuiScreen(parentScreen);
}
String trimStringToWidth(String str, int width) {
int strwidth = mc.fontRenderer.getStringWidth(str);
int elipsisWidth = mc.fontRenderer.getStringWidth(OMITTED);
if (strwidth > width - elipsisWidth && elipsisWidth > strwidth) {
return mc.fontRenderer.trimStringToWidth(str, width - elipsisWidth).trim() + OMITTED;
}
return str;
}
/**
* Draws the screen and all the components in it.
*/
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
for (EntriesList list : lists) {
list.handleDrag(mouseX, mouseY, partialTicks);
}
this.drawDefaultBackground();
for (EntriesList list : lists) {
list.drawScreen(mouseX, mouseY, partialTicks);
}
this.drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 16777215);
String title2 = this.titleLine2;
if (title2 != null) {
title2 = trimStringToWidth(title2, width - 6);
this.drawCenteredString(this.fontRendererObj, title2, this.width / 2, 18, 16777215);
}
this.btnUndoAll.enabled = isChanged();
this.btnDefault.enabled = isDefault();
super.drawScreen(mouseX, mouseY, partialTicks);
for (EntriesList list : lists) {
list.drawScreenPost(mouseX, mouseY, partialTicks);
}
if (this.undoHoverChecker.checkHover(mouseX, mouseY)) this.drawToolTip(
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.undoAll"), 300),
mouseX,
mouseY);
if (this.resetHoverChecker.checkHover(mouseX, mouseY)) this.drawToolTip(
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.resetAll"), 300),
mouseX,
mouseY);
}
@SuppressWarnings("rawtypes")
public void drawToolTip(List stringList, int x, int y) {
this.func_146283_a(stringList, x, y);
}
private class EntriesList extends GuiListExtended {
final String heading;
final List<ListEntry> entries = new ArrayList<>();
int controlX;
public EntriesList(int width, int left, String heading) {
super(
mc,
width,
GuiEditRegistryConfig.this.height,
titleLine2 != null ? 33 : 23,
GuiEditRegistryConfig.this.height - 32,
20);
this.heading = heading;
if (heading != null) {
setHasListHeader(true, mc.fontRenderer.FONT_HEIGHT + 4);
}
setSlotXBoundsFromLeft(left);
this.controlX = this.getScrollBarX() - 20;
lists.add(this);
}
@Override
public void drawScreen(int p_148128_1_, int p_148128_2_, float p_148128_3_) {
GL11.glEnable(GL11.GL_SCISSOR_TEST);
ScaledResolution scaledresolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
GL11.glScissor(
(left - 4) * scaledresolution.getScaleFactor(),
(int) ((scaledresolution.getScaledHeight_double() - (bottom + 4))
* scaledresolution.getScaleFactor()),
(width + 8) * scaledresolution.getScaleFactor(),
(height + 8) * scaledresolution.getScaleFactor());
super.drawScreen(p_148128_1_, p_148128_2_, p_148128_3_);
GL11.glDisable(GL11.GL_SCISSOR_TEST);
}
@Override
public void overlayBackground(int p_148136_1_, int p_148136_2_, int p_148136_3_, int p_148136_4_) {}
@Override
protected void drawContainerBackground(Tessellator tessellator) {
GL11.glEnable(GL11.GL_BLEND);
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 0, 1);
GL11.glDisable(GL11.GL_ALPHA_TEST);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_TEXTURE_2D);
tessellator.startDrawingQuads();
tessellator.setColorRGBA_I(0, 64);
tessellator.addVertex(left, bottom, 0);
tessellator.addVertex(right, bottom, 0);
tessellator.addVertex(right, top, 0);
tessellator.addVertex(left, top, 0);
tessellator.draw();
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glShadeModel(GL11.GL_FLAT);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glDisable(GL11.GL_BLEND);
}
@Override
protected void drawListHeader(int p_148129_1_, int p_148129_2_, Tessellator p_148129_3_) {
if (heading == null) {
return;
}
FontRenderer fr = mc.fontRenderer;
fr.drawString(
this.heading,
p_148129_1_ + this.width / 2 - fr.getStringWidth(this.heading) / 2,
Math.min(this.top + 3, p_148129_2_),
0xffffff);
}
void reinit(int width, int left) {
this.width = width;
this.bottom = GuiEditRegistryConfig.this.height - 32;
this.setSlotXBoundsFromLeft(left);
this.controlX = this.getScrollBarX() - 20;
}
void initEntries(Iterable<?> entries) {
this.controlX = this.getScrollBarX() - 20;
this.entries.clear();
for (Object entry : entries) {
addEntry(new ListEntry(entry.toString()));
}
}
void addEntry(ListEntry entry) {
entry.entriesList = this;
this.entries.add(entry);
this.controlX = Math.min(this.controlX, this.getScrollBarX() - entry.getControlSetWidth() - 2);
}
@Override
public IGuiListEntry getListEntry(int i) {
return entries.get(i);
}
@Override
protected int getSize() {
return entries.size();
}
int getLabelX() {
return left + 2;
}
int getControlX() {
return controlX;
}
@Override
public int getListWidth() {
return width;
}
@Override
protected int getScrollBarX() {
return left + getListWidth() - 6;
}
public void handleDrag(int mouseX, int mouseY, float partialTicks) {
for (int i = 0, entriesSize = entries.size(); i < entriesSize; i++) {
ListEntry entry = entries.get(i);
if (entry.handleDrag(i, mouseX, mouseY, partialTicks)) {
return;
}
}
}
public void drawScreenPost(int mouseX, int mouseY, float partialTicks) {
for (ListEntry entry : entries) {
entry.drawToolTip(mouseX, mouseY);
}
}
public boolean areContentEqual(Object[] vals) {
if (vals.length != entries.size()) return false;
for (int i = 0; i < entries.size(); i++) {
ListEntry entry = entries.get(i);
if (!entry.name.equals(vals[i])) return false;
}
return true;
}
}
class ListEntry implements GuiListExtended.IGuiListEntry {
private EntriesList entriesList;
private final HoverChecker hoverCheckerText = new HoverChecker(0, 0, 0, 0, 0);
final String name;
final String localizedName;
private final String tooltip;
public boolean modified = false;
private final GuiButtonExt btnDrag = new GuiButtonDrag(1, 0, 0, 20, 20);
private final HoverChecker hoverCheckerBtnDrag = new HoverChecker(btnDrag, 800);
private boolean isDragging = false;
private long lastScroll = 0;
ListEntry(String name) {
this.name = name;
String nameKey = "structurelib.sortedregistry." + registryName + "." + name;
this.localizedName = trans(nameKey, name);
String tooltipKey = nameKey + ".tooltip";
String tooltip = trans(tooltipKey, null);
StringBuilder tooltipBuilder = new StringBuilder();
tooltipBuilder.append(EnumChatFormatting.GREEN);
tooltipBuilder.append(this.localizedName);
if (tooltip != null) {
tooltipBuilder.append('\n').append(EnumChatFormatting.YELLOW).append(tooltip);
}
tooltipBuilder.append('\n');
tooltipBuilder.append(EnumChatFormatting.DARK_GRAY);
tooltipBuilder.append(this.name);
this.tooltip = tooltipBuilder.toString();
}
@Override
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, Tessellator tessellator,
int mouseX, int mouseY, boolean isSelected) {
drawOutlineBox(x, y, listWidth, slotHeight, tessellator);
drawLabel(y, slotHeight);
drawControlSet(y, slotHeight, mouseX, mouseY);
}
private void drawOutlineBox(int x, int y, int listWidth, int slotHeight, Tessellator tessellator) {
if (isDragging) {
// left x, right x, top y, bottom y
int lx = x, rx = entriesList.getScrollBarX() - 4, ty = y, by = y + slotHeight;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_TEXTURE_2D);
tessellator.startDrawingQuads();
tessellator.setColorOpaque_I(0x808080);
tessellator.addVertexWithUV(lx - 4, by + 4, 0.0D, 0.0D, 1.0D);
tessellator.addVertexWithUV(rx + 4, by + 4, 0.0D, 1.0D, 1.0D);
tessellator.addVertexWithUV(rx + 4, ty - 4, 0.0D, 1.0D, 0.0D);
tessellator.addVertexWithUV(lx - 4, ty - 4, 0.0D, 0.0D, 0.0D);
tessellator.setColorOpaque_I(0x202020);
tessellator.addVertexWithUV(lx - 2, by + 2, 0.0D, 0.0D, 1.0D);
tessellator.addVertexWithUV(rx + 2, by + 2, 0.0D, 1.0D, 1.0D);
tessellator.addVertexWithUV(rx + 2, ty - 2, 0.0D, 1.0D, 0.0D);
tessellator.addVertexWithUV(lx - 2, ty - 2, 0.0D, 0.0D, 0.0D);
tessellator.draw();
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
}
private void drawControlSet(int y, int slotHeight, int mouseX, int mouseY) {
btnDrag.xPosition = entriesList.getControlX();
btnDrag.yPosition = y + (slotHeight - btnDrag.height) / 2;
btnDrag.drawButton(mc, mouseX, mouseY);
}
private void drawLabel(int y, int slotHeight) {
String label = modified ? EnumChatFormatting.ITALIC + localizedName : localizedName;
int allowedWidth = entriesList.getControlX() - entriesList.getLabelX() - 4;
// probably not very wise to recompute this every render tick, but it doesn't matter for now
String toDraw = trimStringToWidth(label, allowedWidth);
hoverCheckerText.updateBounds(
y + slotHeight / 2 - mc.fontRenderer.FONT_HEIGHT / 2,
y + slotHeight / 2 + mc.fontRenderer.FONT_HEIGHT / 2,
entriesList.getLabelX(),
entriesList.getLabelX() + mc.fontRenderer.getStringWidth(toDraw));
mc.fontRenderer.drawString(
toDraw,
entriesList.getLabelX(),
y + slotHeight / 2 - mc.fontRenderer.FONT_HEIGHT / 2,
getLabelColor());
}
void drawToolTip(int mouseX, int mouseY) {
// ensure checkHover() is always called, or the hover threshold might fail under certain corner cases
if (hoverCheckerText.checkHover(mouseX, mouseY)) {
GuiEditRegistryConfig.this.drawToolTip(
mc.fontRenderer.listFormattedStringToWidth(tooltip, entriesList.getListWidth()),
mouseX,
mouseY);
}
if (hoverCheckerBtnDrag.checkHover(mouseX, mouseY) && !isDragging) {
GuiEditRegistryConfig.this.drawToolTip(
mc.fontRenderer.listFormattedStringToWidth(
I18n.format("structurelib.configgui.drag.tooltip"),
entriesList.getListWidth()),
mouseX,
mouseY);
}
}
protected int getLabelColor() {
return isDragging ? 0x888888 : 0xffffff;
}
int getControlSetWidth() {
return this.btnDrag.getButtonWidth();
}
public boolean handleDrag(int index, int mouseX, int mouseY, float partialTicks) {
if (!isDragging) {
return false;
}
for (EntriesList list : lists) {
if (list == entriesList) continue;
if (mouseX > list.left && mouseX < list.right && mouseY > list.top && mouseY < list.bottom) {
int l;
if (list.entries.isEmpty()) l = 0;
else l = list.func_148124_c(mouseX, mouseY);
if (l < 0) {
if (mouseY < (list.bottom + list.top) / 2) {
l = 0;
} else {
l = list.entries.size();
}
}
entriesList.func_148143_b(true);
entriesList.entries.remove(index);
list.func_148143_b(false);
list.entries.add(l, this);
this.entriesList = list;
return true;
}
}
long now = System.currentTimeMillis();
if (now - lastScroll > 20) {
if (mouseY < entriesList.top) {
entriesList.scrollBy(-3);
lastScroll = now;
} else if (mouseY > entriesList.bottom) {
entriesList.scrollBy(3);
lastScroll = now;
}
}
int l = entriesList.func_148124_c(
MathHelper.clamp_int(mouseX, entriesList.left, entriesList.right),
MathHelper.clamp_int(mouseY, entriesList.top, entriesList.bottom));
if (l < 0 || l == index) return true;
entriesList.entries.remove(index);
entriesList.entries.add(l, this);
return true;
}
@Override
public void mouseReleased(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
isDragging = false;
}
@Override
public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
if (btnDrag.mousePressed(mc, x, y)) {
isDragging = true;
return true;
}
return false;
}
}
private static String trans(String transKey, String fallback) {
String translated = I18n.format(transKey);
return translated.equals(transKey) ? fallback : translated;
}
private static class GuiButtonDrag extends GuiButtonExt {
public GuiButtonDrag(int id, int xPos, int yPos, int width, int height) {
super(id, xPos, yPos, width, height, null);
}
@Override
public void drawButton(Minecraft mc, int mouseX, int mouseY) {
if (this.visible) {
this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition
&& mouseX < this.xPosition + this.width
&& mouseY < this.yPosition + this.height;
int k = this.getHoverState(this.field_146123_n);
GuiUtils.drawContinuousTexturedBox(
buttonTextures,
this.xPosition,
this.yPosition,
0,
46 + k * 20,
this.width,
this.height,
200,
20,
2,
3,
2,
2,
this.zLevel);
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/fourwayarrow2.png"));
int x = this.xPosition + (this.width - 12) / 2;
int y = this.yPosition + (this.height - 12) / 2;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(x, y + 12, zLevel, 0, 1);
tessellator.addVertexWithUV(x + 12, y + 12, zLevel, 1, 1);
tessellator.addVertexWithUV(x + 12, y, zLevel, 1, 0);
tessellator.addVertexWithUV(x, y, zLevel, 0, 0);
tessellator.draw();
}
}
}
}
@@ -4,6 +4,7 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
import cpw.mods.fml.common.network.IGuiHandler;
public class GuiHandler implements IGuiHandler {
@@ -0,0 +1,77 @@
package com.gtnewhorizon.structurelib;
import java.util.List;
import net.minecraft.client.gui.GuiScreen;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.IConfigElement;
@SuppressWarnings({ "unchecked" })
public final class RegistryOrderEntry extends GuiConfigEntries.CategoryEntry {
public RegistryOrderEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList,
IConfigElement<?> configElement) {
super(owningScreen, owningEntryList, configElement);
}
@Override
protected GuiScreen buildChildScreen() {
List<IConfigElement<?>> childElements = this.configElement.getChildElements();
IConfigElement<?> disabled = null, order = null;
for (IConfigElement<?> e : childElements) {
if (e.getName().equals("disabled")) disabled = e;
else if (e.getName().equals("ordering")) order = e;
}
if (disabled == null || order == null) {
throw new IllegalArgumentException("Did not find expected config entires");
}
return new GuiEditRegistryConfig(
this.owningScreen,
disabled,
order,
configElement.getName(),
this.owningScreen.modID,
null,
owningScreen.allRequireWorldRestart || this.configElement.requiresWorldRestart(),
owningScreen.allRequireMcRestart || this.configElement.requiresMcRestart(),
this.owningScreen.title,
((this.owningScreen.titleLine2 == null ? "" : this.owningScreen.titleLine2) + " > " + this.name));
}
private GuiEditRegistryConfig getChildScreen() {
return (GuiEditRegistryConfig) childScreen;
}
@Override
public boolean saveConfigElement() {
GuiEditRegistryConfig c = getChildScreen();
if (c != null && c.isInitialized()) return c.saveConfigElements();
return false;
}
@Override
public boolean isChanged() {
GuiEditRegistryConfig c = getChildScreen();
return c != null && c.isInitialized() && c.isChanged();
}
@Override
public boolean isDefault() {
GuiEditRegistryConfig c = getChildScreen();
return c != null && c.isInitialized() && c.isDefault();
}
@Override
public void setToDefault() {
GuiEditRegistryConfig c = getChildScreen();
if (c != null && c.isInitialized()) c.setToDefault();
}
@Override
public void undoChanges() {
GuiEditRegistryConfig c = getChildScreen();
if (c != null && c.isInitialized()) c.undoChanges();
}
}
@@ -0,0 +1,198 @@
package com.gtnewhorizon.structurelib;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.Spliterator;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
import org.apache.commons.lang3.tuple.Pair;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.PlayerEvent;
public class SortedRegistry<V> implements Iterable<V> {
static final ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES = new ConcurrentHashMap<>();
static {
RegistryOrderSyncMessage.Handler.setAllRegistries(ALL_REGISTRIES);
}
private final NavigableMap<String, V> store = new TreeMap<>();
/**
* Store stuff in an array for faster iteration access than a red black tree
*/
private List<V> baked = Collections.emptyList();
private final Map<UUID, Pair<List<String>, List<String>>> playerOrdering = new HashMap<>();
private final Map<UUID, List<V>> playerBaked = new HashMap<>();
/**
* Construct an unsynchronized registry
*/
public SortedRegistry() {}
/**
* Construct a synchronized registry whose ordering will be synced to the server
*
* @param name network name
*/
public SortedRegistry(String name) {
WeakReference<SortedRegistry<?>> oldValue = ALL_REGISTRIES.put(name, new WeakReference<>(this));
if (oldValue != null && oldValue.get() != null) {
throw new IllegalArgumentException("duplicate name");
}
FMLCommonHandler.instance().bus().register(this);
}
public static Set<String> getRegistryNames() {
return ALL_REGISTRIES.keySet();
}
public static SortedRegistry<?> getRegistry(String name) {
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(name);
return ref != null ? ref.get() : null;
}
static void cleanup(MinecraftServer server) {
@SuppressWarnings("unchecked")
List<EntityPlayerMP> players = server.getConfigurationManager().playerEntityList;
Set<UUID> onlinePlayerIDs = players.stream().map(EntityPlayerMP::getUniqueID).collect(Collectors.toSet());
int removed = 0;
for (WeakReference<SortedRegistry<?>> ref : ALL_REGISTRIES.values()) {
SortedRegistry<?> registry = ref.get();
if (registry == null) continue;
for (Iterator<UUID> iterator = registry.playerOrdering.keySet().iterator(); iterator.hasNext();) {
UUID uuid = iterator.next();
if (!onlinePlayerIDs.contains(uuid)) {
iterator.remove();
registry.playerBaked.remove(uuid);
removed++;
}
}
}
if (removed > 0) {
StructureLib.LOGGER.debug("Removed {} registry record for logged out players", removed);
}
}
@SubscribeEvent
public void onPlayerLogOut(PlayerEvent.PlayerLoggedOutEvent e) {
playerOrdering.remove(e.player.getUniqueID());
playerBaked.remove(e.player.getUniqueID());
}
public void register(String key, V val) {
if (key == null || val == null) throw new NullPointerException();
V old = store.putIfAbsent(key, val);
if (old != null) {
throw new IllegalArgumentException("Duplicate key: " + key);
}
baked = new ArrayList<>(store.values());
for (Map.Entry<UUID, Pair<List<String>, List<String>>> e : playerOrdering.entrySet()) {
makePlayerBaked(e.getKey(), e.getValue().getKey(), e.getValue().getValue());
}
}
public void registerOrdering(EntityPlayerMP player, List<String> ordering, List<String> disabled) {
registerOrdering(player.getUniqueID(), ordering, disabled);
}
public void registerOrdering(UUID playerID, List<String> ordering, List<String> disabled) {
playerOrdering.put(playerID, Pair.of(ordering, disabled));
makePlayerBaked(playerID, ordering, disabled);
}
private void makePlayerBaked(UUID player, List<String> ordering, List<String> disabled) {
TreeMap<String, V> copy = new TreeMap<>(store);
for (String s : disabled) {
copy.remove(s);
}
List<V> b = new ArrayList<>();
for (String k : ordering) {
V v = copy.remove(k);
if (v != null) {
b.add(v);
}
}
b.addAll(copy.values());
playerBaked.put(player, b);
}
public int size() {
return store.size();
}
public boolean isEmpty() {
return store.isEmpty();
}
public boolean containsKey(String key) {
return store.containsKey(key);
}
public boolean containsValue(V value) {
return store.containsValue(value);
}
public V get(String key) {
return store.get(key);
}
public Iterable<V> getPlayerOrdering(@Nullable EntityPlayerMP player) {
if (player == null) return baked;
List<V> playerPreference = playerBaked.get(player.getUniqueID());
if (playerPreference == null) return baked;
return playerPreference;
}
public Stream<String> getPlayerOrderingKeys(@Nullable EntityPlayerMP player) {
if (player == null) return store.keySet().stream();
List<V> playerOrder = playerBaked.get(player.getUniqueID());
if (playerOrder == null) return store.keySet().stream();
// rebuild the order from the baked map
Map<V, String> reverseMap = new IdentityHashMap<>();
store.forEach((k, v) -> reverseMap.put(v, k));
return playerOrder.stream().map(reverseMap::get);
}
Iterable<String> getCurrentOrdering() {
return store.keySet();
}
@Override
public Iterator<V> iterator() {
return baked.iterator();
}
@Override
public void forEach(Consumer<? super V> action) {
baked.forEach(action);
}
@Override
public Spliterator<V> spliterator() {
return baked.spliterator();
}
}
@@ -4,6 +4,7 @@ import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.gtnewhorizon.structurelib.structure.IStructureElement;
import cpw.mods.fml.common.eventhandler.Event;
/**
@@ -12,18 +12,22 @@ import org.apache.logging.log4j.Logger;
import com.gtnewhorizon.structurelib.block.BlockHint;
import com.gtnewhorizon.structurelib.command.CommandConfigureChannels;
import com.gtnewhorizon.structurelib.command.CommandRegistryDebug;
import com.gtnewhorizon.structurelib.item.ItemBlockHint;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import com.gtnewhorizon.structurelib.util.InventoryUtility;
import com.gtnewhorizon.structurelib.util.XSTR;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
@@ -38,7 +42,7 @@ import cpw.mods.fml.relauncher.SideOnly;
@Mod(
modid = StructureLibAPI.MOD_ID,
name = "StructureLib",
version = "GRADLETOKEN_VERSION",
version = Tags.VERSION,
acceptableRemoteVersions = "*",
guiFactory = "com.gtnewhorizon.structurelib.GuiFactory")
public class StructureLib {
@@ -65,6 +69,7 @@ public class StructureLib {
net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT);
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
try {
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
@@ -109,14 +114,21 @@ public class StructureLib {
proxy.preInit(e);
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
InventoryUtility.init();
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
}
}
@Mod.EventHandler
public void loadComplete(FMLLoadCompleteEvent e) {
proxy.loadComplete(e);
}
@Mod.EventHandler
public void serverStarting(FMLServerStartingEvent e) {
e.registerServerCommand(new CommandConfigureChannels());
e.registerServerCommand(new CommandRegistryDebug());
}
public static void addClientSideChatMessages(String... messages) {
@@ -17,6 +17,7 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
import cpw.mods.fml.common.network.NetworkRegistry;
/**
@@ -190,4 +190,28 @@ public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
return getAlignmentLimits()
.isNewExtendedFacingValid(alignment.getDirection(), alignment.getRotation(), alignment.getFlip());
}
/**
* Check if this object support a flip change, assuming both direction and rotation is not changed.
*/
default boolean isFlipChangeAllowed() {
ExtendedFacing facing = getExtendedFacing();
for (Flip flip : Flip.VALUES) {
if (flip == getFlip()) continue;
if (isNewExtendedFacingValid(facing.with(flip))) return true;
}
return false;
}
/**
* Check if this object support a rotation change, assuming both direction and flip is not changed.
*/
default boolean isRotationChangeAllowed() {
ExtendedFacing facing = getExtendedFacing();
for (Rotation rotation : Rotation.VALUES) {
if (rotation == getRotation()) continue;
if (isNewExtendedFacingValid(facing.with(rotation))) return true;
}
return false;
}
}
@@ -4,9 +4,18 @@ import static com.gtnewhorizon.structurelib.alignment.IAlignment.FLIPS_COUNT;
import static com.gtnewhorizon.structurelib.alignment.IAlignment.ROTATIONS_COUNT;
import static java.lang.Math.abs;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.*;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
import java.util.*;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import net.minecraft.util.StatCollector;
import net.minecraft.util.Vec3;
@@ -35,7 +35,7 @@ public class CommandConfigureChannels extends CommandBase {
}
@Override
public List<?> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
public List<String> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
if (p_71516_2_.length == 1)
return getListOfStringsMatchingLastWord(p_71516_2_, "get", "set", "unset", "wipe", "getall");
return null;
@@ -0,0 +1,52 @@
package com.gtnewhorizon.structurelib.command;
import java.util.List;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.ChatComponentText;
import com.gtnewhorizon.structurelib.SortedRegistry;
public class CommandRegistryDebug extends CommandBase {
@Override
public String getCommandName() {
return "sl_registry_debug";
}
@Override
public String getCommandUsage(ICommandSender p_71518_1_) {
return "/sl_registry_debug <registry name>";
}
@Override
public int getRequiredPermissionLevel() {
return 0;
}
@Override
public List<String> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
if (p_71516_2_.length == 1) return getListOfStringsMatchingLastWord(
p_71516_2_,
SortedRegistry.getRegistryNames().toArray(new String[0]));
return null;
}
@Override
public void processCommand(ICommandSender sender, String[] args) {
if (args.length != 1) throw new WrongUsageException(getCommandUsage(sender));
if (!(sender instanceof EntityPlayerMP)) throw new WrongUsageException("must be a player");
EntityPlayerMP player = (EntityPlayerMP) sender;
SortedRegistry<?> registry = SortedRegistry.getRegistry(args[0]);
if (registry == null) throw new WrongUsageException("registry not found");
player.addChatMessage(new ChatComponentText("Current order:"));
registry.getPlayerOrderingKeys(player).map(ChatComponentText::new).forEach(player::addChatMessage);
}
}
@@ -0,0 +1,53 @@
package com.gtnewhorizon.structurelib.core;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.structurelib.mixins.Mixins;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
public static final Logger log = LogManager.getLogger("StructureLibCore");
@Override
public String getMixinConfig() {
return "mixins.structurelib.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return Mixins.getEarlyMixins(loadedCoreMods);
}
@Override
public String[] getASMTransformerClass() {
return new String[0];
}
@Override
public String getModContainerClass() {
return null;
}
@Override
public String getSetupClass() {
return null;
}
@Override
public void injectData(Map<String, Object> data) {
}
@Override
public String getAccessTransformerClass() {
return null;
}
}
@@ -0,0 +1,11 @@
package com.gtnewhorizon.structurelib.event;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
public interface BlockChangeListener {
void onBlockChange(World world, Chunk chunk, int x, int y, int z, Block oldBlock, Block newBlock, int oldMeta,
int newMeta);
}
@@ -0,0 +1,33 @@
package com.gtnewhorizon.structurelib.event;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
@SuppressWarnings("ForLoopReplaceableByForEach")
public class BlockChangeNotifier {
private static final List<BlockChangeListener> listeners = new ArrayList<>();
public static synchronized void addListener(BlockChangeListener listener) {
listeners.add(listener);
}
public static void onBlockChange(World world, Chunk chunk, int x, int y, int z, Block oldBlock, Block newBlock,
int oldMeta, int newMeta) {
if (oldBlock == newBlock && oldMeta == newMeta) return;
if (chunk == null) {
chunk = world.getChunkFromBlockCoords(x, z);
}
final int numListeners = listeners.size();
for (int i = 0; i < numListeners; i++) {
final BlockChangeListener listener = listeners.get(i);
listener.onBlockChange(world, chunk, x, y, z, oldBlock, newBlock, oldMeta, newMeta);
}
}
}
@@ -28,8 +28,13 @@ public class ItemConstructableTrigger extends Item {
@Override
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null)
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
if (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
if (player.isSneaking()) {
StructureLib.instance().proxy().displayConfigGUI("registries");
} else {
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
}
}
return super.onItemRightClick(stack, world, player);
}
@@ -34,7 +34,7 @@ public class ItemFrontRotationTool extends Item {
@Override
@SideOnly(Side.CLIENT)
public void addInformation(ItemStack p_77624_1_, EntityPlayer p_77624_2_, List aList, boolean p_77624_4_) {
public void addInformation(ItemStack p_77624_1_, EntityPlayer p_77624_2_, List<String> aList, boolean p_77624_4_) {
aList.add(translateToLocal("item.structurelib.frontRotationTool.desc.0")); // Triggers Front Rotation Interface
aList.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.1")); // Rotates
// only the
@@ -0,0 +1,188 @@
package com.gtnewhorizon.structurelib.mixins;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import com.gtnewhorizon.structurelib.core.StructureLibCore;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
public enum Mixins {
BLOCK_CHANGE_NOTIFIER(new Builder("Block Change Notifier").setPhase(Phase.EARLY)
.addMixinClasses("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld")
.addTargetedMod(TargetedMod.VANILLA).setSide(Side.BOTH).setApplyIf(() -> true));
private final List<String> mixinClasses;
private final List<TargetedMod> targetedMods;
private final List<TargetedMod> excludedMods;
private final Supplier<Boolean> applyIf;
private final Phase phase;
private final Side side;
Mixins(Builder builder) {
this.mixinClasses = builder.mixinClasses;
this.targetedMods = builder.targetedMods;
this.excludedMods = builder.excludedMods;
this.applyIf = builder.applyIf;
this.phase = builder.phase;
this.side = builder.side;
if (this.mixinClasses.isEmpty()) {
throw new RuntimeException("No mixin class specified for Mixin : " + this.name());
}
if (this.targetedMods.isEmpty()) {
throw new RuntimeException("No targeted mods specified for Mixin : " + this.name());
}
if (this.applyIf == null) {
throw new RuntimeException("No ApplyIf function specified for Mixin : " + this.name());
}
if (this.phase == null) {
throw new RuntimeException("No Phase specified for Mixin : " + this.name());
}
if (this.side == null) {
throw new RuntimeException("No Side function specified for Mixin : " + this.name());
}
}
public static List<String> getEarlyMixins(Set<String> loadedCoreMods) {
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Phase.EARLY) {
if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
StructureLibCore.log.info("Not loading the following EARLY mixins: {}", notLoading.toString());
return mixins;
}
public static List<String> getLateMixins(Set<String> loadedMods) {
// NOTE: Any targetmod here needs a modid, not a coremod id
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Phase.LATE) {
if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
StructureLibCore.log.info("Not loading the following LATE mixins: {}", notLoading.toString());
return mixins;
}
private boolean shouldLoadSide() {
return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer())
|| (side == Side.CLIENT && FMLLaunchHandler.side().isClient());
}
private boolean allModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return false;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& !loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean noModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return true;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean shouldLoad(Set<String> loadedCoreMods, Set<String> loadedMods) {
return (shouldLoadSide() && applyIf.get()
&& allModsLoaded(targetedMods, loadedCoreMods, loadedMods)
&& noModsLoaded(excludedMods, loadedCoreMods, loadedMods));
}
private static class Builder {
private final String name;
private final List<String> mixinClasses = new ArrayList<>();
private final List<TargetedMod> targetedMods = new ArrayList<>();
private final List<TargetedMod> excludedMods = new ArrayList<>();
private Supplier<Boolean> applyIf = null;
private Phase phase = null;
private Side side = null;
public Builder(String name) {
this.name = name;
}
public Builder addMixinClasses(String... mixinClasses) {
this.mixinClasses.addAll(Arrays.asList(mixinClasses));
return this;
}
public Builder setPhase(Phase phase) {
if (this.phase != null) {
throw new RuntimeException("Trying to define Phase twice for " + this.name);
}
this.phase = phase;
return this;
}
public Builder setSide(Side side) {
if (this.side != null) {
throw new RuntimeException("Trying to define Side twice for " + this.name);
}
this.side = side;
return this;
}
public Builder setApplyIf(Supplier<Boolean> applyIf) {
this.applyIf = applyIf;
return this;
}
public Builder addTargetedMod(TargetedMod mod) {
this.targetedMods.add(mod);
return this;
}
public Builder addExcludedMod(TargetedMod mod) {
this.excludedMods.add(mod);
return this;
}
}
private enum Side {
BOTH,
CLIENT,
SERVER
}
private enum Phase {
EARLY,
LATE,
}
}
@@ -0,0 +1,30 @@
package com.gtnewhorizon.structurelib.mixins;
import cpw.mods.fml.common.Mod;
public enum TargetedMod {
VANILLA("Minecraft", null);
/** The "name" in the {@link Mod @Mod} annotation */
public final String modName;
/** Class that implements the IFMLLoadingPlugin interface */
public final String coreModClass;
/** The "modid" in the {@link Mod @Mod} annotation */
public final String modId;
TargetedMod(String modName, String coreModClass) {
this(modName, coreModClass, null);
}
TargetedMod(String modName, String coreModClass, String modId) {
this.modName = modName;
this.coreModClass = coreModClass;
this.modId = modId;
}
@Override
public String toString() {
return "TargetedMod{modName='" + modName + "', coreModClass='" + coreModClass + "', modId='" + modId + "'}";
}
}
@@ -0,0 +1,44 @@
package com.gtnewhorizon.structurelib.mixins.early.blockChangeNotifier;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.util.BlockSnapshot;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.gtnewhorizon.structurelib.event.BlockChangeNotifier;
import com.llamalad7.mixinextras.sugar.Local;
@Mixin(ForgeHooks.class)
public class MixinForgeHooks {
@Inject(
method = "onPlaceItemIntoWorld",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/World;markAndNotifyBlock(IIILnet/minecraft/world/chunk/Chunk;Lnet/minecraft/block/Block;Lnet/minecraft/block/Block;I)V",
shift = At.Shift.AFTER),
remap = false)
private static void onBlockChange(ItemStack itemstack, EntityPlayer player, World world, int x, int y, int z,
int side, float hitX, float hitY, float hitZ, CallbackInfoReturnable<Boolean> cir,
@Local(name = "blocksnapshot") BlockSnapshot blocksnapshot, @Local(name = "newBlock") Block newBlock,
@Local(name = "metadata") int newMetadata) {
BlockChangeNotifier.onBlockChange(
world,
null,
blocksnapshot.x,
blocksnapshot.y,
blocksnapshot.z,
blocksnapshot.replacedBlock,
newBlock,
blocksnapshot.meta,
newMetadata);
}
}
@@ -0,0 +1,55 @@
package com.gtnewhorizon.structurelib.mixins.early.blockChangeNotifier;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.gtnewhorizon.structurelib.event.BlockChangeNotifier;
import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.Share;
import com.llamalad7.mixinextras.sugar.ref.LocalIntRef;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
@Mixin(World.class)
public class MixinWorld {
@Inject(
method = "Lnet/minecraft/world/World;setBlock(IIILnet/minecraft/block/Block;II)Z",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/World;markAndNotifyBlock(IIILnet/minecraft/world/chunk/Chunk;Lnet/minecraft/block/Block;Lnet/minecraft/block/Block;I)V",
shift = At.Shift.AFTER,
remap = false))
public void setBlockObserve(int x, int y, int z, Block newBlock, int newMeta, int flags,
CallbackInfoReturnable<Boolean> cir, @Local(name = "block1") Block originalBlock,
@Share("originalMeta") LocalIntRef originalMeta, @Share("chunk") LocalRef<Chunk> chunkLocalRef) {
BlockChangeNotifier.onBlockChange(
(World) ((Object) (this)),
chunkLocalRef.get(),
x,
y,
z,
originalBlock,
newBlock,
originalMeta.get(),
newMeta);
}
@Inject(
method = "Lnet/minecraft/world/World;setBlock(IIILnet/minecraft/block/Block;II)Z",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/chunk/Chunk;func_150807_a(IIILnet/minecraft/block/Block;I)Z",
shift = At.Shift.BEFORE))
public void captureChunkAndOriginalMetadata(int x, int y, int z, Block blockIn, int metadataIn, int flags,
CallbackInfoReturnable<Boolean> cir, @Local(name = "chunk") Chunk chunk,
@Share("originalMeta") LocalIntRef originalMeta, @Share("chunk") LocalRef<Chunk> chunkLocalRef) {
chunkLocalRef.set(chunk);
originalMeta.set(chunk.getBlockMetadata(x & 15, y, z & 15));
}
}
@@ -0,0 +1,77 @@
package com.gtnewhorizon.structurelib.net;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import com.gtnewhorizon.structurelib.SortedRegistry;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf;
public class RegistryOrderSyncMessage implements IMessage {
private String name;
private List<String> order;
private List<String> disabled;
public RegistryOrderSyncMessage() {}
public RegistryOrderSyncMessage(String name, List<String> order, List<String> disabled) {
this.name = name;
this.order = order;
this.disabled = disabled;
}
@Override
public void fromBytes(ByteBuf buf) {
name = ByteBufUtils.readUTF8String(buf);
int size = ByteBufUtils.readVarInt(buf, 2);
order = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
order.add(ByteBufUtils.readUTF8String(buf));
}
size = ByteBufUtils.readVarInt(buf, 2);
disabled = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
disabled.add(ByteBufUtils.readUTF8String(buf));
}
}
@Override
public void toBytes(ByteBuf buf) {
ByteBufUtils.writeUTF8String(buf, name);
ByteBufUtils.writeVarInt(buf, order.size(), 2);
for (String s : order) {
ByteBufUtils.writeUTF8String(buf, s);
}
ByteBufUtils.writeVarInt(buf, disabled.size(), 2);
for (String s : disabled) {
ByteBufUtils.writeUTF8String(buf, s);
}
}
public static class Handler implements IMessageHandler<RegistryOrderSyncMessage, IMessage> {
private static ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES;
public static void setAllRegistries(ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> r) {
if (ALL_REGISTRIES == null) ALL_REGISTRIES = r;
}
@Override
public IMessage onMessage(RegistryOrderSyncMessage message, MessageContext ctx) {
if (ALL_REGISTRIES == null) return null;
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(message.name);
if (ref == null) return null;
SortedRegistry<?> r = ref.get();
if (r == null) return null;
r.registerOrdering(ctx.getServerHandler().playerEntity, message.order, message.disabled);
return null;
}
}
}
@@ -62,7 +62,8 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
if (e == null) continue;
if (predicate == null) predicate = e.getPredicate();
else predicate = predicate.or(e.getPredicate());
is.add(e.getStacks());
Iterable<ItemStack> stacks = e.getStacks();
if (stacks != null) is.add(stacks);
}
if (predicate == null) return null;
return new BlocksToPlace(predicate, Iterables.concat(is));
@@ -1,8 +1,15 @@
package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.notAir;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.step;
import java.util.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Collectors;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
@@ -2,9 +2,26 @@ package com.gtnewhorizon.structurelib.structure;
import static java.lang.Integer.MIN_VALUE;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.*;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
@@ -38,6 +55,7 @@ import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import cpw.mods.fml.common.registry.GameRegistry;
/**
@@ -576,6 +594,9 @@ public class StructureUtility {
* Implementation wise, this allows a block only when we don't yet have a tier, or the block at that location has
* the same tier as we already have.
* <p>
* Notice that if your tier extractor function returns null on any (Block, meta) pair, then it means this block is
* rejected, since null can never be a valid tier.
* <p>
* There is yet no TileEntity counterpart of this utility. Feel free to submit a PR to add it.
* <p>
* For most typical use cases, you will NOT want to return notSet from your tierExtractor. If you do so, we will
@@ -619,7 +640,8 @@ public class StructureUtility {
* </pre>
*
* @param tierExtractor a function to extract tier info from a block. This function can return null and will never
* be passed a null block or an invalid block meta.
* be passed a null block or an invalid block meta. If this function returns null, then the
* block would be considered invalid.
* @param allKnownTiers A list of all known tiers as of calling. Can be empty or null. No hint will be spawned if
* empty or null. Cannot have null elements. First element denotes the most primitive tier.
* Last element denotes the most advanced tier. If not all tiers are available at definition
@@ -14,7 +14,10 @@
*/
package com.gtnewhorizon.structurelib.util;
import java.util.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -30,6 +33,7 @@ import net.minecraft.inventory.InventoryEnderChest;
import net.minecraft.item.ItemStack;
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
import com.gtnewhorizon.structurelib.SortedRegistry;
import com.gtnewhorizon.structurelib.util.InventoryUtility.ItemStackExtractor.APIType;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
@@ -38,12 +42,13 @@ import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
*/
public class InventoryUtility {
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>();
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>("stackExtractors");
private static final List<Predicate<? super EntityPlayerMP>> enableEnder = new CopyOnWriteArrayList<>();
/**
* The remove() of the Iterable returned must be implemented!
*/
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>();
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>(
"inventoryProviders");
static {
inventoryProviders.register("5000-main-inventory", new InventoryProvider<InventoryIterable<InventoryPlayer>>() {
@@ -102,8 +107,8 @@ public class InventoryUtility {
registerInventoryProvider(key, newInventoryProvider(extractor));
}
public static Iterator<? extends ItemStackExtractor> getStackExtractors() {
return stackExtractors.iterator();
public static Iterator<? extends ItemStackExtractor> getStackExtractors(EntityPlayerMP player) {
return stackExtractors.getPlayerOrdering(player).iterator();
}
public static <Inv extends IInventory> InventoryProvider<InventoryIterable<Inv>> newInventoryProvider(
@@ -165,7 +170,7 @@ public class InventoryUtility {
boolean simulate, int count) {
ItemStackCounterImpl store = new ItemStackCounterImpl();
int sum = 0;
for (InventoryProvider<?> provider : inventoryProviders) {
for (InventoryProvider<?> provider : inventoryProviders.getPlayerOrdering(player)) {
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, null);
if (sum >= count) return store.getStore();
}
@@ -186,7 +191,7 @@ public class InventoryUtility {
int count = filter.stackSize;
ItemStackPredicate predicate = ItemStackPredicate.from(filter, NBTMode.EXACT);
ItemStackCounterImpl store = new ItemStackCounterImpl();
for (InventoryProvider<?> provider : inventoryProviders) {
for (InventoryProvider<?> provider : inventoryProviders.getPlayerOrdering(player)) {
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, filter);
if (sum >= count) return sum;
}
@@ -212,7 +217,7 @@ public class InventoryUtility {
* @param predicate item stack filter
* @param simulate whether to do removal
* @param count let's hope int size is enough...
* @param recursive do recursive lookup using {@link #getStackExtractors() stack extractors}
* @param recursive do recursive lookup using {@link #getStackExtractors(EntityPlayerMP) stack extractors}
* @return amount taken. never negative nor bigger than count...
*/
public static Map<ItemStack, Integer> takeFromInventory(Iterable<ItemStack> inv, Predicate<ItemStack> predicate,
@@ -252,7 +257,9 @@ public class InventoryUtility {
if (found == count) return count;
}
if (!recursive) continue;
for (ItemStackExtractor f : stackExtractors) {
for (ItemStackExtractor f : stackExtractors.getPlayerOrdering(player)) {
boolean end = f.isAPIImplemented(APIType.IS_VALID_SOURCE);
if (!f.isValidSource(stack, player)) continue;
if (filter != null && f.isAPIImplemented(APIType.EXTRACT_ONE_STACK)) {
copiedFilter.stackSize = count - found;
found += f.getItem(stack, copiedFilter, simulate, player);
@@ -260,11 +267,16 @@ public class InventoryUtility {
found += f.takeFromStack(predicate, simulate, count - found, store, stack, filter, player);
}
if (found >= count) return found;
if (end) break;
}
}
return found;
}
public static void init() {
// dummy method to init static final fields
}
public interface OptimizedExtractor {
/**
@@ -294,10 +306,15 @@ public class InventoryUtility {
enum APIType {
MAIN,
EXTRACT_ONE_STACK,
IS_VALID_SOURCE,
}
boolean isAPIImplemented(APIType type);
default boolean isValidSource(ItemStack is, EntityPlayerMP player) {
return true;
}
/**
* Extract a particular type of item. The extractor can choose to not return all items contained within this
* item as long as it makes sense, but the author should inform the player of this.
@@ -1,6 +1,12 @@
package com.gtnewhorizon.structurelib.util;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableMap;
import java.util.Spliterator;
import java.util.TreeMap;
import java.util.function.Consumer;
public class SortedRegistry<V> implements Iterable<V> {
+37
View File
@@ -251,5 +251,42 @@ The code for placeBlock() and spawnHint() will be left as an exercise for the re
<p>
Please refer to {@link com.gtnewhorizon.structurelib.StructureEvent} to see what events are available.
</p>
<h1>Extending Inventory Utility</h1>
{@link com.gtnewhorizon.structurelib.util.InventoryUtility} is how structurelib survival autoplace pulls item for autoplacing.
It has two key concepts: inventory providers and stack extractors.
<p>
Inventory Providers are classes that retrieve an Iterable of ItemStacks for examination, given an EntityPlayerMP as identifier.
They are also responsible for marking the relevant item container as dirty, so changes can be commited to disk properly when world save happens.
Examples include: main player inventory, vanilla ender chest, baubles inventory. StructureLib comes with main player inventory and vanilla ender chest built in.
</p>
<p>
Stack Extractors are classes that retrieve items from another item.
Examples include: Forestry backpacks, ME network over ME Wireless Terminals.
</p>
<p>
Both of these are managed internally by {@link com.gtnewhorizon.structurelib.SortedRegistry}.
By default, elements registered will be sorted in lexicographical order in ascending order.
However, facilities in StructureLib will allow an end user to tweak these registries as their wish.
They can disable, reorder, reenable any element within the registry, either in game, at main menu or with the game stopped,
Their changes will be synced to server as well, should they be playing on a dedicated server.
</p>
<p>
This has the following important implications
</p>
<ul>
<li>Not all inventory providers or stack extractors may be called during a survival autoplace session</li>
<li>One player could have stack extractor 001 ordered before 002, while the other having a completely reversed order</li>
</ul>
Therefore, if your stack extractor can somehow correlate with aonther one, make sure it still works if the calling order is inverted.
At the very least, inform the player about this ordering constraint.
A future StructrueLib refactor to specify this dependency info is possible, but the chance is low.
<h2>Localization</h2>
Both of these would show up in config GUI of StructureLib. Structurelib uses the following localization keys in that GUI.
<ul>
<li><code>structurelib.sortedregistry.inventoryproviders.&lt;registry_key&gt;</code>: This is the name shown on the button for inventory providers. If this is not provided, then registry_key will be used instead.</li>
<li><code>structurelib.sortedregistry.inventoryproviders.&lt;registry_key&gt;.tooltip</code>: This is the tooltip shown when user hover over the button for inventory providers. If this is not provided, then no explicit tooltip will be shown.</li>
<li><code>structurelib.sortedregistry.stackextractors.&lt;registry_key&gt;</code>: This is the name shown on the button for stack extractors. If this is not provided, then registry_key will be used instead.</li>
<li><code>structurelib.sortedregistry.stackextractors.&lt;registry_key&gt;.tooltip</code>: This is the tooltip shown when user hover over the button for stack extractors. If this is not provided, then no explicit tooltip will be shown.</li>
</ul>
</BODY>
</HTML>
@@ -172,3 +172,19 @@ structurelib.config.client.hologram.maxCoexisting=Max Coexisting Holograms
structurelib.config.client.hologram.maxCoexisting.tooltip=Number of holograms that can coexist. Usually all hints generated by one right click belongs to one hologram. An attempt will be made to prune old holograms when a new hologram is about to be projected and this limit would be exceeded.
structurelib.config.client.hologram.removeColliding=Remove Colliding Holograms
structurelib.config.client.hologram.removeColliding.tooltip=Whether an attempt will be made to remove any existing hologram that collides with a new hologram.
structurelib.config.registries=Sorted Registries
structurelib.config.registries.tooltip=Sorted Registry is how StructureLib internally keep track of certain features. Each individual elements may be reordered, disabled and enabled at user's command. Changes will be synced to server if needed.\nThis gui can also be accessed in game by shift right clicking air with Multiblock Structure Hologram Projector.
structurelib.config.registries.stackextractors=Stack Extractors
structurelib.config.registries.stackextractors.tooltip=Controls which inventory-from-stack functions are enabled during survival autoplace
structurelib.config.registries.inventoryproviders=Inventory Providers
structurelib.config.registries.inventoryproviders.tooltip=Controls which inventories functions are enabled during survival autoplace
structurelib.configgui.enabled=Enabled Entry
structurelib.configgui.disabled=Disabled Entry
structurelib.configgui.drag.tooltip=Drag this button to reorder this element.
structurelib.sortedregistry.inventoryproviders.5000-main-inventory=Main Player Inventory
structurelib.sortedregistry.inventoryproviders.5000-main-inventory.tooltip=36 slots of item in your main inventory. Armor slots excluded.
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory=Ender Chest Inventory
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory.tooltip=Links to your vanilla ender chest inventory. Require at least one "ender backpack like" item in main inventory to work.
@@ -172,3 +172,19 @@ structurelib.config.client.hologram.maxCoexisting=最多可以同时存在的全
structurelib.config.client.hologram.maxCoexisting.tooltip=这决定最多可以同时存在多少台机器的全息投影。一般一次使用全息投影仪右键生成的所有机器结构细节被视为一个全息投影。当同时存在多个全息投影,并超过此数量时,再次使用全息投影仪会移除旧有的全息投影。
structurelib.config.client.hologram.removeColliding=移除冲突的全息投影
structurelib.config.client.hologram.removeColliding.tooltip=当新的全息投影与已存在的全息投影冲突时,开启此选项时会移除已存在的全息投影。
structurelib.config.registries=注册表
structurelib.config.registries.tooltip=StructureLib 使用注册表来管理其部分功能。每个注册表中的每一个元素都可以被单独排序、禁用。需要时,注册表的内容将被同步至服务端。
structurelib.config.registries.stackextractors=物品提取器
structurelib.config.registries.stackextractors.tooltip=该注册表控制在生存模式自动放置时,如何从物品(如林业背包)提取库存。
structurelib.config.registries.inventoryproviders=物品栏定位
structurelib.config.registries.inventoryproviders.tooltip=该注册表控制在生存模式自动放置时,可用的物品栏来源。
structurelib.configgui.enabled=已启用
structurelib.configgui.disabled=已禁用
structurelib.configgui.drag.tooltip=拖动该按钮以重排。
structurelib.sortedregistry.inventoryproviders.5000-main-inventory=玩家物品栏
structurelib.sortedregistry.inventoryproviders.5000-main-inventory.tooltip=不包括护甲栏以外的36格。
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory=末影箱
structurelib.sortedregistry.inventoryproviders.7000-ender-inventory.tooltip=当前玩家的末影箱。需要在主物品栏中存在类似于末影背包的东西才会实际生效。
Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

@@ -0,0 +1,8 @@
{
"required": true,
"minVersion": "0.8.5-GTNH",
"package": "com.gtnewhorizon.structurelib.mixins.early",
"refmap": "mixins.structurelib.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}
@@ -0,0 +1,8 @@
{
"required": true,
"minVersion": "0.8.5-GTNH",
"package": "com.gtnewhorizon.structurelib.mixins",
"refmap": "mixins.structurelib.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}
@@ -0,0 +1,8 @@
{
"required": true,
"minVersion": "0.8.5-GTNH",
"package": "com.gtnewhorizon.structurelib.mixins.late",
"refmap": "mixins.structurelib.refmap.json",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8"
}
+1
View File
@@ -0,0 +1 @@
public net.minecraft.client.gui.GuiSlot func_148136_c(IIII)V # overlayBackground