mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-08 20:44:59 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b5f4aa433 | ||
|
|
9672d40b34 | ||
|
|
4fdea65537 | ||
|
|
89057ebb2d | ||
|
|
d829ae466b | ||
|
|
137f651486 | ||
|
|
ff88cbf2d7 | ||
|
|
4922760b46 | ||
|
|
45bd25401f | ||
|
|
050f0b5a5c | ||
|
|
2c5fa02a9e | ||
|
|
287e8317c9 | ||
|
|
95d1533342 | ||
|
|
19179ec894 | ||
|
|
d8b301457f | ||
|
|
40edf05109 | ||
|
|
4e5a92aff7 | ||
|
|
a09e6058a9 | ||
|
|
e774dd5e10 | ||
|
|
0866dbee98 | ||
|
|
208f7ffc41 | ||
|
|
88a6021546 | ||
|
|
a05267a566 | ||
|
|
dd3413dcaa | ||
|
|
34394ef8a5 | ||
|
|
7c0d3f4bd6 | ||
|
|
cd4f3617be | ||
|
|
256bc60d35 | ||
|
|
70426ab7b3 | ||
|
|
0030c02e9a | ||
|
|
7382db1bbd | ||
|
|
b669cbecf1 | ||
|
|
6283931667 | ||
|
|
3b3bc4b9ad | ||
|
|
bef4fdd949 | ||
|
|
4a06c8f95b | ||
|
|
241bed1989 | ||
|
|
f5594c25db | ||
|
|
7cda901ae5 | ||
|
|
538459eab5 | ||
|
|
3a9ec8b6ac | ||
|
|
330a13271b | ||
|
|
bf5dd5566b | ||
|
|
b9d0143245 | ||
|
|
43fc39c99b | ||
|
|
6337536dc3 | ||
|
|
9b96560119 | ||
|
|
3ce1c877e3 | ||
|
|
3d150d95cb | ||
|
|
03e84e3d37 | ||
|
|
547833bedb | ||
|
|
61e0d4b7e7 | ||
|
|
6867ed50e6 | ||
|
|
e7a47c199b | ||
|
|
b2bc474382 | ||
|
|
eef6baea4b | ||
|
|
efd49701e1 | ||
|
|
a7f0d6be72 | ||
|
|
0ff38c2da9 | ||
|
|
307bb1714c | ||
|
|
7d8e6a0ea5 | ||
|
|
b6d2af2170 | ||
|
|
8309567076 | ||
|
|
23312198a6 | ||
|
|
fa5e2ae4ca | ||
|
|
a8a1547845 | ||
|
|
113e05b7e6 | ||
|
|
080785ad05 | ||
|
|
6c14ca0a1d | ||
|
|
ea38c4e1d6 | ||
|
|
50e8c97aa3 | ||
|
|
9a82239f2c | ||
|
|
8ed2be38eb | ||
|
|
582f59e73d | ||
|
|
464e44de85 | ||
|
|
392d2ace8a | ||
|
|
978938d450 | ||
|
|
7a2328c294 | ||
|
|
d2cd45e07b | ||
|
|
8a24900623 | ||
|
|
15a0034e03 | ||
|
|
7aae6dc31c | ||
|
|
875bc1990e | ||
|
|
74aefd22be | ||
|
|
72ff51d093 | ||
|
|
7d74a65ec0 | ||
|
|
0ac8908b37 | ||
|
|
2dd4349e54 | ||
|
|
f813104ec4 | ||
|
|
4fe9eb6ea5 | ||
|
|
c0814274ec | ||
|
|
23605fc22d | ||
|
|
8ee90a7338 | ||
|
|
9bd19b9254 | ||
|
|
df4c54ea8d | ||
|
|
e71edd50b0 | ||
|
|
1b7cee7c4e | ||
|
|
03f2c31191 | ||
|
|
9b318f68fb | ||
|
|
adc91bb1dd | ||
|
|
21198c4d39 | ||
|
|
e70ea765f8 | ||
|
|
56a083ce6f | ||
|
|
7389f349db | ||
|
|
5b6b51f80c | ||
|
|
1481b68133 | ||
|
|
ebf2d6f0e1 | ||
|
|
7e7064e006 | ||
|
|
ebcf62fd97 | ||
|
|
74dde96905 | ||
|
|
d73d2bffc2 | ||
|
|
94055bbd6a | ||
|
|
dc1b1fc4db | ||
|
|
fd38dfc31f | ||
|
|
cb82b73e78 | ||
|
|
91d4a8022e | ||
|
|
a12722c4b0 | ||
|
|
b2624a3d36 | ||
|
|
b34e901a93 | ||
|
|
e155c69b20 | ||
|
|
82622d1e15 | ||
|
|
a168702151 | ||
|
|
73f113ed9b | ||
|
|
c19ac860d4 | ||
|
|
d33cdd79c4 | ||
|
|
ed24f15033 | ||
|
|
15c34061bc | ||
|
|
728f25114b | ||
|
|
4a8c154855 | ||
|
|
f65010a547 | ||
|
|
609e50428e | ||
|
|
e8f5cd6489 | ||
|
|
8de7026a00 | ||
|
|
33d377cea7 | ||
|
|
da98b476b0 | ||
|
|
9dd145bc67 | ||
|
|
83b7c35ed1 | ||
|
|
bb0bebf3a2 | ||
|
|
a49ed1996b | ||
|
|
6d5d31f5ab | ||
|
|
3eafe426ba | ||
|
|
eea5227fe3 | ||
|
|
1f520cfd73 | ||
|
|
8357fdc826 | ||
|
|
27bb7b2ad9 | ||
|
|
b9af0ef491 | ||
|
|
7f19399ba3 | ||
|
|
4982bb780d | ||
|
|
a069612e04 | ||
|
|
ab09b791bd | ||
|
|
b086a65828 | ||
|
|
f1f6dae00f |
No files matched your search
@@ -0,0 +1,19 @@
|
||||
# This is the universal Text Editor Configuration
|
||||
# for all GTNewHorizons projects
|
||||
# See: https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{bat,ini}]
|
||||
end_of_line = crlf
|
||||
|
||||
[*.{dtd,json,info,mcmeta,md,sh,svg,xml,xsd,xsl,yaml,yml}]
|
||||
indent_size = 2
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore spotlessApply reformat
|
||||
464e44de8541a46833f50c3bd363d50e1677a685
|
||||
+42
-20
@@ -1,22 +1,44 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
* text eol=lf
|
||||
|
||||
# Custom for Visual Studio
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
*.vbproj merge=union
|
||||
*.fsproj merge=union
|
||||
*.dbproj merge=union
|
||||
*.[jJ][aA][rR] binary
|
||||
|
||||
# Standard to msysgit
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
*.[pP][nN][gG] binary
|
||||
*.[jJ][pP][gG] binary
|
||||
*.[jJ][pP][eE][gG] binary
|
||||
*.[gG][iI][fF] binary
|
||||
*.[tT][iI][fF] binary
|
||||
*.[tT][iI][fF][fF] binary
|
||||
*.[iI][cC][oO] binary
|
||||
*.[sS][vV][gG] text
|
||||
*.[eE][pP][sS] binary
|
||||
*.[xX][cC][fF] binary
|
||||
|
||||
*.[kK][aA][rR] binary
|
||||
*.[mM]4[aA] binary
|
||||
*.[mM][iI][dD] binary
|
||||
*.[mM][iI][dD][iI] binary
|
||||
*.[mM][pP]3 binary
|
||||
*.[oO][gG][gG] binary
|
||||
*.[rR][aA] binary
|
||||
|
||||
*.7[zZ] binary
|
||||
*.[gG][zZ] binary
|
||||
*.[tT][aA][rR] binary
|
||||
*.[tT][gG][zZ] binary
|
||||
*.[zZ][iI][pP] binary
|
||||
|
||||
*.[tT][cC][nN] binary
|
||||
*.[sS][oO] binary
|
||||
*.[dD][lL][lL] binary
|
||||
*.[dD][yY][lL][iI][bB] binary
|
||||
*.[pP][sS][dD] binary
|
||||
*.[tT][tT][fF] binary
|
||||
*.[oO][tT][fF] binary
|
||||
|
||||
*.[pP][aA][tT][cC][hH] -text
|
||||
|
||||
*.[bB][aA][tT] text eol=crlf
|
||||
*.[cC][mM][dD] text eol=crlf
|
||||
*.[pP][sS]1 text eol=crlf
|
||||
|
||||
*[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# bashsupport disable=BP5006 # Global environment variables
|
||||
RUNDIR="run" \
|
||||
CRASH="crash-reports" \
|
||||
SERVERLOG="server.log"
|
||||
|
||||
# enable nullglob to get 0 results when no match rather than the pattern
|
||||
shopt -s nullglob
|
||||
|
||||
# store matches in array
|
||||
crash_reports=("$RUNDIR/$CRASH/crash"*.txt)
|
||||
|
||||
# if array not empty there are crash_reports
|
||||
if [ "${#crash_reports[@]}" -gt 0 ]; then
|
||||
# get the latest crash_report from array
|
||||
latest_crash_report="${crash_reports[-1]}"
|
||||
{
|
||||
printf 'Latest crash report detected %s:\n' "${latest_crash_report##*/}"
|
||||
cat "$latest_crash_report"
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep --quiet --fixed-strings 'Fatal errors were detected' "$SERVERLOG"; then
|
||||
{
|
||||
printf 'Fatal errors detected:\n'
|
||||
cat server.log
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep --quiet --fixed-strings 'The state engine was in incorrect state ERRORED and forced into state SERVER_STOPPED' \
|
||||
"$SERVERLOG"; then
|
||||
{
|
||||
printf 'Server force stopped:'
|
||||
cat server.log
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep --quiet --perl-regexp --only-matching '.+Done \(.+\)\! For help, type "help" or "\?"' "$SERVERLOG"; then
|
||||
{
|
||||
printf 'Server did not finish startup:'
|
||||
cat server.log
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'No crash reports detected'
|
||||
exit 0
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
name: Build and test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main ]
|
||||
push:
|
||||
branches: [ master, main ]
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
|
||||
secrets: inherit
|
||||
@@ -1,27 +0,0 @@
|
||||
# This workflow will build a Java project with Gradle
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Java CI with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Optimize images
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master, main, release/** ]
|
||||
paths:
|
||||
- "**/*.png"
|
||||
|
||||
push:
|
||||
branches: [ master, main, release/** ]
|
||||
paths:
|
||||
- "**/*.png"
|
||||
|
||||
jobs:
|
||||
optimize-images:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,98 @@
|
||||
name: Deploy Javadoc
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- feature/doc
|
||||
- '**/*-javadoc'
|
||||
tags:
|
||||
- '*'
|
||||
delete:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
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:
|
||||
result_hash: ${{ steps.deploy.outputs.hash }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: |
|
||||
8
|
||||
11
|
||||
17
|
||||
distribution: 'temurin'
|
||||
cache: gradle
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Generate Javadoc
|
||||
run: ./gradlew --no-daemon spotlessCheck javadoc
|
||||
|
||||
- name: Deploy 🚀
|
||||
id: deploy
|
||||
shell: bash
|
||||
run: |
|
||||
mv build/docs $RUNNER_TEMP/docs_output
|
||||
rm build -rf
|
||||
git checkout gh-pages
|
||||
mkdir -p $TARGET_FOLDER
|
||||
rm -rf $TARGET_FOLDER
|
||||
mv $RUNNER_TEMP/docs_output $TARGET_FOLDER -v
|
||||
git add $TARGET_FOLDER
|
||||
if git diff --cached --exit-code > /dev/null ; then
|
||||
echo Nothing to deploy
|
||||
exit 0
|
||||
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 "Deploy javadoc from ref $TARGET_FOLDER"
|
||||
git push
|
||||
echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
TARGET_FOLDER: ${{ github.ref_name }}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
name: Release tagged build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ '*' ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release-tags:
|
||||
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master
|
||||
secrets: inherit
|
||||
+29
-19
@@ -1,20 +1,30 @@
|
||||
*.class
|
||||
*.log
|
||||
*.ctxt
|
||||
.mtj.tmp/
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
hs_err_pid*
|
||||
.gradle
|
||||
**/build/
|
||||
!src/**/build/
|
||||
gradle-app.setting
|
||||
!gradle-wrapper.jar
|
||||
.gradletasknamecache
|
||||
.idea/
|
||||
run/
|
||||
.settings
|
||||
/.idea/
|
||||
/run/
|
||||
/build/
|
||||
/eclipse/
|
||||
.classpath
|
||||
.project
|
||||
/bin/
|
||||
/config/
|
||||
/crash-reports/
|
||||
/logs/
|
||||
options.txt
|
||||
/saves/
|
||||
usernamecache.json
|
||||
banned-ips.json
|
||||
banned-players.json
|
||||
eula.txt
|
||||
ops.json
|
||||
server.properties
|
||||
servers.dat
|
||||
usercache.json
|
||||
whitelist.json
|
||||
/out/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
src/main/resources/mixins.*.json
|
||||
*.bat
|
||||
layout.json
|
||||
@@ -0,0 +1,3 @@
|
||||
# Any Github changes require admin approval
|
||||
/.github/** @GTNewHorizons/admin
|
||||
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU 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.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
+165
@@ -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.
|
||||
@@ -21,9 +21,16 @@ Originally created by TecTech authors, taken with permission. It's under MIT lic
|
||||
}
|
||||
```
|
||||
Replace `master-SNAPSHOT` with a commit hash or a tag name (if any) of your choice to prevent unexpected upstream changes.
|
||||
2. Add `required-after:structurelib;` to your `@Mod(dependencies = "..")` tring.
|
||||
2. Add `required-after:structurelib;` to your `@Mod(dependencies = "..")` tring.
|
||||
3. Add `structurelib` to your `mcmod.info`.
|
||||
|
||||
## Contribution
|
||||
|
||||
The project is developed using IDEA. Please manually configure the build to not delegate to gradle for textures to show up in dev.
|
||||
|
||||
## Current license
|
||||
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.
|
||||
@@ -0,0 +1,41 @@
|
||||
javadoc {
|
||||
javadocTool = javaToolchains.javadocToolFor {
|
||||
// Last version with frames, but it also has search
|
||||
languageVersion = JavaLanguageVersion.of(25)
|
||||
vendor = JvmVendorSpec.ADOPTIUM
|
||||
}
|
||||
|
||||
inputs.file "src/main/javadoc/overview.html"
|
||||
|
||||
include "com/gtnewhorizon/structurelib/alignment/**"
|
||||
include "com/gtnewhorizon/structurelib/constructable/**"
|
||||
include "com/gtnewhorizon/structurelib/structure/**"
|
||||
include "com/gtnewhorizon/structurelib/util/**"
|
||||
include "com/gtnewhorizon/structurelib/StructureLibAPI.java"
|
||||
include "com/gtnewhorizon/structurelib/StructureEvent.java"
|
||||
|
||||
classpath += sourceSets.patchedMc.output
|
||||
|
||||
options {
|
||||
overview "src/main/javadoc/overview.html"
|
||||
links 'https://docs.oracle.com/en/java/javase/25/docs/api/'
|
||||
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('javadocJar', Jar) {
|
||||
dependsOn javadoc
|
||||
archiveClassifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events "passed", "skipped", "failed"
|
||||
}
|
||||
}
|
||||
+4
-71
@@ -1,72 +1,5 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "forge"
|
||||
url = "https://files.minecraftforge.net/maven"
|
||||
}
|
||||
maven {
|
||||
name = "sonatype"
|
||||
url = "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'forge'
|
||||
|
||||
version = modVersion
|
||||
group= modGroup
|
||||
archivesBaseName = modBaseName
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
minecraft {
|
||||
version = project.forgeVersion
|
||||
replaceIn "StructureLib.java"
|
||||
replace '${version}', project.version
|
||||
runDir = "run"
|
||||
}
|
||||
|
||||
processResources
|
||||
{
|
||||
inputs.property "version", project.version
|
||||
inputs.property "mcversion", project.minecraft.version
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include 'mcmod.info'
|
||||
expand 'version': project.version, 'mcversion': project.minecraft.version
|
||||
}
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude 'mcmod.info'
|
||||
}
|
||||
}
|
||||
|
||||
task deobfJar(type: Jar) {
|
||||
from sourceSets.main.output
|
||||
classifier = 'deobf'
|
||||
}
|
||||
|
||||
task sourceJar(type: Jar) {
|
||||
from sourceSets.main.allSource
|
||||
classifier = 'sources'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives deobfJar
|
||||
archives sourceJar
|
||||
}
|
||||
|
||||
install.dependsOn reobf
|
||||
|
||||
idea {
|
||||
module {
|
||||
inheritOutputDirs = false
|
||||
outputDir = new File(compileJava.destinationDir.parentFile.parentFile, "idea")
|
||||
testOutputDir = new File(compileTestJava.destinationDir.parentFile.parentFile, "ideaTest")
|
||||
}
|
||||
//version: 1707058017
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhconvention'
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Add your dependencies here
|
||||
|
||||
dependencies {
|
||||
api("com.github.GTNewHorizons:GTNHLib:0.11.21:dev")
|
||||
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.110-GTNH:dev")
|
||||
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
|
||||
}
|
||||
+237
-4
@@ -1,5 +1,238 @@
|
||||
modGroup = com.gtnewhorizon
|
||||
modVersion = 1.0.7
|
||||
modBaseName = StructureLib
|
||||
# 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
|
||||
|
||||
forgeVersion = 1.7.10-10.13.4.1614-1.7.10
|
||||
# Human-readable mod name, available for mcmod.info population.
|
||||
modName = StructureLib
|
||||
|
||||
# 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
|
||||
|
||||
# Whether to use modGroup as the maven publishing group.
|
||||
# When false, com.github.GTNewHorizons is used.
|
||||
useModGroupForPublishing = false
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# Enables modern Java syntax support. Valid values:
|
||||
# - false: No modern syntax, Java 8 only
|
||||
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
|
||||
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
|
||||
# - modern: Native modern Java bytecode, no downgrading
|
||||
enableModernJavaSyntax = jabel
|
||||
|
||||
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
|
||||
# and should *not* be used in most situations. -1 disables this.
|
||||
# forceToolchainVersion = -1
|
||||
|
||||
# Target JVM version for JVM Downgrader bytecode downgrading.
|
||||
# Only used when enableModernJavaSyntax = jvmDowngrader
|
||||
# downgradeTargetVersion = 8
|
||||
|
||||
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
|
||||
# Classes will be available in META-INF/versions/N/ for each version N in this list.
|
||||
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
|
||||
# jvmDowngraderMultiReleaseVersions = 21,25
|
||||
|
||||
# Specifies how JVM Downgrader API stubs are provided. Options:
|
||||
# - shade: Shade minimized stubs into the jar
|
||||
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
|
||||
# - external: Another dependency provides stubs (no constraint, no warning)
|
||||
# - (empty): Warning reminding you to configure stubs
|
||||
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
|
||||
# jvmDowngraderStubsProvider =
|
||||
|
||||
# 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 =
|
||||
|
||||
# [DEPRECATED] Mod Group replacement token.
|
||||
gradleTokenGroupName =
|
||||
|
||||
# [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
|
||||
apiPackage =
|
||||
|
||||
# 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 = 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. The package must exist or
|
||||
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
|
||||
# it must match with this or the build will fail.
|
||||
mixinsPackage = mixins
|
||||
|
||||
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
||||
# 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
|
||||
|
||||
# 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: UniMixins 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 customize the default working directory used by the runClient* tasks. Relative to the project directory.
|
||||
# runClientWorkingDirectory = run/client
|
||||
|
||||
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
|
||||
# runServerWorkingDirectory = run/server
|
||||
|
||||
# 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
|
||||
|
||||
# Non-GTNH properties
|
||||
org.gradle.configuration-cache = true
|
||||
org.gradle.parallel = true
|
||||
@@ -0,0 +1,12 @@
|
||||
#This file is generated by updateDaemonJvm
|
||||
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect
|
||||
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect
|
||||
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect
|
||||
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d76df094a9cbbabd3b08251f9e61444a/redirect
|
||||
toolchainVersion=25
|
||||
Vendored
BIN
Binary file not shown.
+3
-2
@@ -1,6 +1,7 @@
|
||||
#Sat Jan 04 01:52:14 CET 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
|
||||
@@ -1,79 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/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/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# 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\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn ( ) {
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die ( ) {
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -82,83 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
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.
|
||||
JAVACMD=java
|
||||
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.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
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=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# 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, 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" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+46
-43
@@ -1,4 +1,22 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@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 ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -8,26 +26,30 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
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
|
||||
|
||||
@@ -35,54 +57,35 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
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
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
before_install:
|
||||
- ./gradlew setupCIWorkspace
|
||||
@@ -0,0 +1,4 @@
|
||||
// Add any additional repositiroes for your dependencies here
|
||||
|
||||
repositories {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
// RetroFuturaGradle
|
||||
name "GTNH Maven"
|
||||
url "https://nexus.gtnewhorizons.com/repository/public/"
|
||||
mavenContent {
|
||||
includeGroup("com.gtnewhorizons")
|
||||
includeGroupByRegex("com\\.gtnewhorizons\\..+")
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
mavenLocal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26'
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
// this is not an API! go check StructureLibAPI instead!
|
||||
public class ChannelDescription {
|
||||
|
||||
private static final ItemStackMap<Set<ChannelDescription>> itemToChannels = new ItemStackMap<>(true);
|
||||
private static final Map<String, ChannelDescription> registry = new HashMap<>();
|
||||
private final String channel;
|
||||
private final Map<String, String> descriptions = new HashMap<>();
|
||||
private final ItemStackMap<Integer> items = new ItemStackMap<>(true);
|
||||
|
||||
public static ChannelDescription get(final String channel) {
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
return registry.computeIfAbsent(channel, ChannelDescription::new);
|
||||
}
|
||||
|
||||
public static boolean has(final String channel) {
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
return registry.containsKey(channel);
|
||||
}
|
||||
|
||||
public static Collection<Map.Entry<String, Integer>> iterate(final ItemStack block) {
|
||||
Set<ChannelDescription> channels = itemToChannels.get(block);
|
||||
if (channels == null) return Collections.emptySet();
|
||||
return channels.stream().map(d -> new AbstractMap.SimpleImmutableEntry<>(d.channel, d.items.get(block)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static void set(final String channel, final String modid, final String description) {
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
registry.computeIfAbsent(channel, ChannelDescription::new).add(modid, description);
|
||||
}
|
||||
|
||||
public static void item(final String channel, int channelValue, ItemStack stack) {
|
||||
if (StructureLibAPI.isDebugEnabled()) {
|
||||
if (!channel.toLowerCase(Locale.ROOT).equals(channel)) {
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
}
|
||||
if (channelValue <= 0) {
|
||||
throw new IllegalArgumentException("Channel value must be greater than 0");
|
||||
}
|
||||
}
|
||||
ChannelDescription inst = registry.computeIfAbsent(channel, ChannelDescription::new);
|
||||
inst.items.put(stack, channelValue);
|
||||
itemToChannels.computeIfAbsent(stack, s -> new HashSet<>()).add(inst);
|
||||
}
|
||||
|
||||
public static Map<String, ChannelDescription> getAll() {
|
||||
return Collections.unmodifiableMap(registry);
|
||||
}
|
||||
|
||||
private ChannelDescription(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public ChannelDescription add(String modid, String description) {
|
||||
if (Loader.instance().getIndexedModList().get(modid) == null) {
|
||||
throw new IllegalArgumentException("Mod not found: " + modid);
|
||||
}
|
||||
descriptions.put(modid, description);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, String> getDescriptions() {
|
||||
return Collections.unmodifiableMap(descriptions);
|
||||
}
|
||||
|
||||
public Map<ItemStack, Integer> getItems() {
|
||||
return Collections.unmodifiableMap(items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||
|
||||
/**
|
||||
* Handler for middle-click channel picking. When the player holds a hologram projector on the cursor and middle-clicks
|
||||
* a registered indicator item, the corresponding channel is set on the projector. The same applies in world when the
|
||||
* projector is held in hand and a registered indicator block is middle-clicked. Shift+middle-click wipes all channels.
|
||||
* Was made side neutral to support server-side validation at some point if necessary.
|
||||
*/
|
||||
public final class ChannelPickHandler {
|
||||
|
||||
private ChannelPickHandler() {}
|
||||
|
||||
/**
|
||||
* Handle a middle-click event while holding a hologram projector on the cursor.
|
||||
*/
|
||||
public static boolean handleMiddleClick(EntityPlayer player, ItemStack holoStack, @Nullable ItemStack targetStack,
|
||||
final boolean isSneaking) {
|
||||
return handlePick(player, holoStack, targetStack, isSneaking, true);
|
||||
}
|
||||
|
||||
public static boolean handleWorldPick(World world, EntityPlayer player, ItemStack holoStack,
|
||||
@Nullable MovingObjectPosition hit, final boolean isSneaking) {
|
||||
ItemStack targetStack = null;
|
||||
|
||||
if (hit != null) {
|
||||
Block block = world.getBlock(hit.blockX, hit.blockY, hit.blockZ);
|
||||
if (!block.isAir(world, hit.blockX, hit.blockY, hit.blockZ)) {
|
||||
targetStack = block.getPickBlock(hit, world, hit.blockX, hit.blockY, hit.blockZ, player);
|
||||
}
|
||||
}
|
||||
|
||||
// A null target still goes through so that shift+middle-click wipes even when aiming at nothing
|
||||
return handlePick(player, holoStack, targetStack, isSneaking, false);
|
||||
}
|
||||
|
||||
private static boolean handlePick(EntityPlayer player, ItemStack holoStack, @Nullable ItemStack targetStack,
|
||||
final boolean isSneaking, final boolean onCursor) {
|
||||
// Clear channels on shift+middle-click. The "press again to confirm" gesture is gated client-side
|
||||
// (see ChannelWipeConfirmation) before this method is ever called.
|
||||
if (isSneaking) {
|
||||
ChannelDataAccessor.wipeChannelData(holoStack);
|
||||
sendChannelData(player, holoStack, onCursor);
|
||||
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.cleared"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (targetStack == null) return false; // nothing hovered, cancel silently
|
||||
|
||||
Collection<Map.Entry<String, Integer>> channels = ChannelDescription.iterate(targetStack);
|
||||
if (channels.isEmpty()) {
|
||||
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.nomapping"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assuming there's only 1 channel per item for now (same behavior as drag and drop)
|
||||
Map.Entry<String, Integer> first = channels.iterator().next();
|
||||
ChannelDataAccessor.setChannelData(holoStack, first.getKey(), first.getValue());
|
||||
sendChannelData(player, holoStack, onCursor);
|
||||
player.addChatMessage(
|
||||
new ChatComponentTranslation("structurelib.pickchannel.success", first.getKey(), first.getValue()));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Player is the one triggering this update, so should already be in-sync.
|
||||
*/
|
||||
private static void sendChannelData(EntityPlayer player, ItemStack holoStack, boolean onCursor) {
|
||||
if (!(player instanceof EntityPlayerMP)) {
|
||||
StructureLib.net.sendToServer(new SetChannelDataMessage(holoStack, onCursor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
|
||||
/**
|
||||
* Client-side-only confirmation gate for "wipe all channels" shift+middle-click gesture.
|
||||
*/
|
||||
public final class ChannelWipeConfirmation {
|
||||
|
||||
private static final long CONFIRM_WINDOW_MS = 5000;
|
||||
private static long armedAtMs = -1;
|
||||
|
||||
private ChannelWipeConfirmation() {}
|
||||
|
||||
/**
|
||||
* Call this before invoking {@link ChannelPickHandler} whenever the wipe gesture (shift+middle-click) is detected.
|
||||
*
|
||||
* @return true if this is a confirming second press within the window and the wipe should proceed now; false if
|
||||
* this press only armed the confirmation.
|
||||
*/
|
||||
public static boolean confirm(EntityPlayer player) {
|
||||
long now = Minecraft.getSystemTime();
|
||||
if (now - armedAtMs > CONFIRM_WINDOW_MS) {
|
||||
armedAtMs = now;
|
||||
player.addChatMessage(new ChatComponentTranslation("structurelib.pickchannel.confirmwipe"));
|
||||
return false;
|
||||
}
|
||||
armedAtMs = -1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,686 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
|
||||
|
||||
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;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.profiler.Profiler;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.MouseEvent;
|
||||
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.item.ItemConstructableTrigger;
|
||||
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;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
|
||||
private static final short[] RGBA_NO_TINT = { 255, 255, 255, 255 };
|
||||
private static final short[] RGBA_RED_TINT = { 255, 128, 128, 0 };
|
||||
private static final Map<HintParticleInfo, HintGroup> allHints = new HashMap<>();
|
||||
/**
|
||||
* All batches of hints.
|
||||
*/
|
||||
private static final List<HintGroup> allGroups = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Current batch of hints. Belongs to the same logical multiblock.
|
||||
*/
|
||||
private static HintGroup currentHints;
|
||||
/**
|
||||
* Collection of all hints. Sorted from farthest (index 0) to nearest (index size - 1)
|
||||
* <p>
|
||||
* 10k should be more than enough, if not a bit wasteful
|
||||
*/
|
||||
private static final List<HintParticleInfo> allHintsForRender = new ArrayList<>(10000);
|
||||
/**
|
||||
* If the diff to current player position is too great sort the allHints. Initial value is very far off below y=0
|
||||
* but not huge enough to make a NaN.
|
||||
* <p>
|
||||
* We are using this supposedly immutable object as a mutable object here. So be aware.
|
||||
*/
|
||||
private static final Vec3 lastPlayerPos = Vec3.createVectorHelper(0, -1e30, 0);
|
||||
/**
|
||||
* if true rebuild the allHints list from all hintOwners (and sort it)
|
||||
*/
|
||||
private static boolean allHintsDirty = false;
|
||||
/**
|
||||
* counter keeping track of how many renderThrough hint particle we have. enable optimization if no renderThrough
|
||||
*/
|
||||
private static int renderThrough;
|
||||
/**
|
||||
* A throttle map for local player. Integrated server will use the throttle map in super class instead.
|
||||
*/
|
||||
private static final Map<Object, Long> localThrottleMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
|
||||
ensureHinting();
|
||||
HintParticleInfo info = new HintParticleInfo(w, x, y, z, icons, RGBa);
|
||||
|
||||
// check and remove colliding holograms
|
||||
if (ConfigurationHandler.INSTANCE.isRemoveCollidingHologram()) {
|
||||
HintGroup dupe = allHints.get(info);
|
||||
if (dupe != null && dupe != currentHints) {
|
||||
allGroups.remove(dupe);
|
||||
removeGroup(dupe);
|
||||
}
|
||||
}
|
||||
allHints.put(info, currentHints);
|
||||
currentHints.getHints().add(info);
|
||||
allHintsDirty = true;
|
||||
|
||||
EntityFX particle = new WeightlessParticleFX(
|
||||
w,
|
||||
x + RANDOM.nextFloat() * 0.5F,
|
||||
y + RANDOM.nextFloat() * 0.5F,
|
||||
z + RANDOM.nextFloat() * 0.5F,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
particle.setRBGColorF(0, 0.6F * RANDOM.nextFloat(), 0.8f);
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(particle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {
|
||||
hintParticleTinted(w, x, y, z, createIIconFromBlock(block, meta), RGBa);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
|
||||
hintParticleTinted(w, x, y, z, icons, RGBA_NO_TINT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hintParticle(World w, int x, int y, int z, Block block, int meta) {
|
||||
hintParticleTinted(w, x, y, z, createIIconFromBlock(block, meta), RGBA_NO_TINT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateHintParticleTint(EntityPlayer player, World w, int x, int y, int z, short[] rgBa) {
|
||||
if (player instanceof EntityPlayerMP) return super.updateHintParticleTint(player, w, x, y, z, rgBa);
|
||||
if (player != getCurrentPlayer()) return false; // how?
|
||||
HintParticleInfo hint = getHintParticleInfo(w, x, y, z);
|
||||
if (hint == null) {
|
||||
return false;
|
||||
}
|
||||
hint.setTint(rgBa);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static HintParticleInfo getHintParticleInfo(World w, int x, int y, int z) {
|
||||
HintParticleInfo info = new HintParticleInfo(w, x, y, z, null, null);
|
||||
HintGroup existing = allHints.get(info);
|
||||
if (existing != null) {
|
||||
for (HintParticleInfo hint : existing.getHints()) {
|
||||
if (hint.equals(info)) {
|
||||
return hint;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markHintParticleError(EntityPlayer player, World w, int x, int y, int z) {
|
||||
if (player instanceof EntityPlayerMP) return super.markHintParticleError(player, w, x, y, z);
|
||||
if (player != getCurrentPlayer()) return false; // how?
|
||||
HintParticleInfo hint = getHintParticleInfo(w, x, y, z);
|
||||
if (hint == null) {
|
||||
return false;
|
||||
}
|
||||
hint.setTint(RGBA_RED_TINT);
|
||||
hint.setRenderThrough();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
|
||||
boolean forceUpdateLastSend) {
|
||||
if (player instanceof EntityPlayerMP)
|
||||
super.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend);
|
||||
else if (player != null)
|
||||
addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend, localThrottleMap);
|
||||
}
|
||||
|
||||
static void removeGroup(HintGroup group) {
|
||||
for (HintParticleInfo hintParticleInfo : group.getHints()) {
|
||||
allHints.remove(hintParticleInfo);
|
||||
if (hintParticleInfo.renderThrough) renderThrough--;
|
||||
}
|
||||
allHintsDirty = true;
|
||||
}
|
||||
|
||||
private static IIcon[] createIIconFromBlock(Block block, int meta) {
|
||||
IIcon[] ret = new IIcon[6];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
ret[i] = block.getIcon(i, meta);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientSideChatMessages(String... messages) {
|
||||
GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI();
|
||||
for (String s : messages) {
|
||||
chat.printChatMessage(new ChatComponentText(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPlayer getCurrentPlayer() {
|
||||
return Minecraft.getMinecraft().thePlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCurrentPlayer(EntityPlayer player) {
|
||||
return player == Minecraft.getMinecraft().thePlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startHinting(World w) {
|
||||
if (!w.isRemote) return;
|
||||
if (currentHints != null) endHinting(w);
|
||||
currentHints = new HintGroup();
|
||||
}
|
||||
|
||||
private void ensureHinting() {
|
||||
if (currentHints == null) currentHints = new HintGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endHinting(World w) {
|
||||
if (!w.isRemote || currentHints == null) return;
|
||||
while (!allGroups.isEmpty() && allGroups.size() >= ConfigurationHandler.INSTANCE.getMaxCoexistingHologram()) {
|
||||
allGroups.remove(0);
|
||||
}
|
||||
if (!currentHints.getHints().isEmpty()) allGroups.add(currentHints);
|
||||
// we use the player existence time here as some worlds don't really advance the time ticker
|
||||
currentHints.setCreationTime(getCurrentPlayer().ticksExisted);
|
||||
currentHints = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOverworldTime() {
|
||||
// there is no overworld, better just hope current world time is ok...
|
||||
return Minecraft.getMinecraft().theWorld.getTotalWorldTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadChannels(ItemStack trigger) {
|
||||
StructureLib.net.sendToServer(new SetChannelDataMessage(trigger));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preInit(FMLPreInitializationEvent e) {
|
||||
FMLCommonHandler.instance().bus().register(new FMLEventHandler());
|
||||
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||
}
|
||||
|
||||
static void markTextureUsed(IIcon icon) {
|
||||
if (StructureLib.COMPAT instanceof IStructureCompat)
|
||||
((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<>();
|
||||
private int creationTime = -1;
|
||||
|
||||
public List<HintParticleInfo> getHints() {
|
||||
return hints;
|
||||
}
|
||||
|
||||
public void setCreationTime(int creationTime) {
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
public int getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HintParticleInfo {
|
||||
|
||||
private final World w;
|
||||
// these are the block coordinate for e.g. w.getBlock()
|
||||
private final int x, y, z;
|
||||
private final IIcon[] icons;
|
||||
private short[] tint;
|
||||
private boolean renderThrough;
|
||||
|
||||
private final long creationTime = System.currentTimeMillis(); // use tick time instead maybe
|
||||
|
||||
public HintParticleInfo(World w, int x, int y, int z, IIcon[] icons, short[] tint) {
|
||||
this.w = w;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.icons = icons;
|
||||
this.tint = tint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof HintParticleInfo that)) return false;
|
||||
|
||||
return x == that.x && y == that.y && z == that.z;
|
||||
}
|
||||
|
||||
public void setTint(short[] tint) {
|
||||
this.tint = tint;
|
||||
}
|
||||
|
||||
public void setRenderThrough() {
|
||||
if (!this.renderThrough) {
|
||||
ClientProxy.renderThrough += 1;
|
||||
allHintsDirty = true;
|
||||
}
|
||||
this.renderThrough = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = x;
|
||||
result = 31 * result + y;
|
||||
result = 31 * result + z;
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isInFrustrum(Frustrum frustrum) {
|
||||
return frustrum.isBoxInFrustum(x + 0.25, y + 0.25, z + 0.25, x + 0.75, y + 0.75, z + 0.75);
|
||||
}
|
||||
|
||||
public void draw(Tessellator tes, double eyeX, double eyeY, double eyeZ, int eyeXint, int eyeYint,
|
||||
int eyeZint) {
|
||||
double size = 0.5;
|
||||
|
||||
int brightness = w.blockExists(x, 0, z) ? w.getLightBrightnessForSkyBlocks(x, y, z, 0) : 0;
|
||||
tes.setBrightness(brightness);
|
||||
|
||||
tes.setColorRGBA(
|
||||
(int) (tint[0] * .9F),
|
||||
(int) (tint[1] * .95F),
|
||||
(int) (tint[2] * 1F),
|
||||
ConfigurationHandler.INSTANCE.getHintTransparency());
|
||||
|
||||
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;
|
||||
|
||||
// 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]);
|
||||
|
||||
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:
|
||||
// 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);
|
||||
tes.addVertexWithUV(X + size, Y, Z, U, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z, U, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||
break;
|
||||
case 1:
|
||||
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);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, U, V);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, U, V);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z, U, v);
|
||||
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||
break;
|
||||
case 2:
|
||||
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);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z, u, V);
|
||||
tes.addVertexWithUV(X, Y, Z, U, V);
|
||||
break;
|
||||
case 3:
|
||||
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);
|
||||
tes.addVertexWithUV(X, Y + size, Z + size, u, v);
|
||||
tes.addVertexWithUV(X, Y + size, Z + size, u, v);
|
||||
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||
break;
|
||||
case 4:
|
||||
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);
|
||||
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X, Y, Z, u, V);
|
||||
tes.addVertexWithUV(X, Y, Z + size, U, V);
|
||||
break;
|
||||
case 5:
|
||||
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);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, u, v);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, u, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z + size, u, V);
|
||||
tes.addVertexWithUV(X + size, Y, Z, U, V);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
public double getSquareDistanceTo(Vec3 point) {
|
||||
return point.squareDistanceTo(x + 0.5, y + 0.5, z + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Vec3 playerPos = Minecraft.getMinecraft().thePlayer.getPosition(1);
|
||||
boolean sortRequired = false;
|
||||
// The allGroups is implicitly sorted by creationTime
|
||||
// here we exploit this ordering to reduce iteration size
|
||||
int deadline = Minecraft.getMinecraft().thePlayer.ticksExisted
|
||||
- ConfigurationHandler.INSTANCE.getHintLifespan();
|
||||
int i;
|
||||
for (i = 0; i < allGroups.size(); i++) {
|
||||
if (allGroups.get(i).getCreationTime() > deadline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != 0) {
|
||||
sortRequired = true;
|
||||
// clear this block and go no further
|
||||
List<HintGroup> toRemove = allGroups.subList(0, i);
|
||||
toRemove.forEach(ClientProxy::removeGroup);
|
||||
toRemove.clear();
|
||||
}
|
||||
if (allHintsDirty) {
|
||||
allHintsForRender.clear();
|
||||
// we need greatly efficient sequential access and mildly efficient sorting
|
||||
// rebuild is sort of rare, and it's expected to lag anyway
|
||||
// so no min-heap here
|
||||
for (HintGroup c : allGroups) allHintsForRender.addAll(c.getHints());
|
||||
sortRequired = true;
|
||||
}
|
||||
if (sortRequired || playerPos.squareDistanceTo(lastPlayerPos) > 1e-2) {
|
||||
// only redo sort if player moved some distance
|
||||
// default is 0.1 block
|
||||
// if there was a full rebuild, go sort it as well
|
||||
allHintsForRender.sort(Comparator.comparingDouble(info -> -info.getSquareDistanceTo(playerPos)));
|
||||
resetPlayerLocation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ForgeEventHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void onMouseEvent(MouseEvent event) {
|
||||
final EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
|
||||
if (player == null || player.isDead) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ItemStack heldItem = player.getHeldItem();
|
||||
|
||||
if (heldItem == null || !(heldItem.getItem() instanceof ItemConstructableTrigger)) return;
|
||||
|
||||
if (event.button != 2 || !event.buttonstate) return;
|
||||
|
||||
// Sink the middle click as soon as a projector is held, so vanilla pick block does not run
|
||||
event.setCanceled(true);
|
||||
|
||||
final boolean isSneaking = player.isSneaking();
|
||||
if (isSneaking && !ChannelWipeConfirmation.confirm(player)) return;
|
||||
|
||||
ChannelPickHandler.handleWorldPick(
|
||||
player.worldObj,
|
||||
player,
|
||||
heldItem,
|
||||
Minecraft.getMinecraft().objectMouseOver,
|
||||
isSneaking);
|
||||
}
|
||||
|
||||
@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) {
|
||||
// flush hints. we are in a different world now.
|
||||
allHintsForRender.clear();
|
||||
allGroups.clear();
|
||||
lastPlayerPos.yCoord = -1e30;
|
||||
renderThrough = 0;
|
||||
// clear throttles. hopefully a world switch is enough long as a cool down.
|
||||
localThrottleMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderWorldLast(RenderWorldLastEvent e) {
|
||||
if (allHintsForRender.isEmpty()) return;
|
||||
|
||||
// seriously, I'm not a OpenGL expert, so I'm probably doing a lot of very stupid stuff here.
|
||||
// Please consider contributing patches if you find something to optimize.
|
||||
|
||||
Profiler p = Minecraft.getMinecraft().mcProfiler;
|
||||
|
||||
p.startSection("HintParticle");
|
||||
p.startSection("Prepare");
|
||||
Frustrum frustrum = new Frustrum();
|
||||
Entity entitylivingbase = Minecraft.getMinecraft().renderViewEntity;
|
||||
double d0 = entitylivingbase.lastTickPosX
|
||||
+ (entitylivingbase.posX - entitylivingbase.lastTickPosX) * e.partialTicks;
|
||||
double d1 = entitylivingbase.lastTickPosY
|
||||
+ (entitylivingbase.posY - entitylivingbase.lastTickPosY) * e.partialTicks;
|
||||
double d2 = entitylivingbase.lastTickPosZ
|
||||
+ (entitylivingbase.posZ - entitylivingbase.lastTickPosZ) * e.partialTicks;
|
||||
int i0 = (int) d0, i1 = (int) d1, i2 = (int) d2;
|
||||
frustrum.setPosition(d0, d1, d2);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT); // TODO figure out original states
|
||||
// we need the back facing rendered because the thing is transparent
|
||||
GL11.glDisable(GL11.GL_CULL_FACE);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glEnable(GL11.GL_BLEND); // enable blend so it is transparent
|
||||
GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA);
|
||||
// depth test begin as enabled
|
||||
boolean renderThrough = false;
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(TextureMap.locationBlocksTexture);
|
||||
GL11.glTranslated(-d0 + i0, -d1 + i1, -d2 + i2);
|
||||
Tessellator tes = Tessellator.instance;
|
||||
tes.startDrawing(GL11.GL_TRIANGLES);
|
||||
for (int i = 0, allHintsForRenderSize = allHintsForRender.size(), cubeDrawn = 0; i
|
||||
< allHintsForRenderSize; i++, cubeDrawn++) {
|
||||
HintParticleInfo hint = allHintsForRender.get(i);
|
||||
if (!hint.isInFrustrum(frustrum)) continue;
|
||||
if (renderThrough != hint.renderThrough) {
|
||||
if (i > 0) {
|
||||
p.endStartSection("Draw");
|
||||
tes.draw();
|
||||
tes.startDrawing(GL11.GL_TRIANGLES);
|
||||
cubeDrawn = 0;
|
||||
p.endStartSection("Prepare");
|
||||
}
|
||||
if (hint.renderThrough) GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
else GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
renderThrough = hint.renderThrough;
|
||||
}
|
||||
// Minecraft Tessellator draw in batch of 4096. This plays nicely with quads, but for trigs
|
||||
// it might occasionally end up with one half in first batch and the other half in later batch.
|
||||
// so we manually flush the buffer here if that would happen.
|
||||
// 6 faces per particle, 2 trig per face, 3 vertex per trig
|
||||
if ((cubeDrawn + 1) >= 4096 / (6 * 2 * 3)) {
|
||||
tes.draw();
|
||||
tes.startDrawing(GL11.GL_TRIANGLES);
|
||||
}
|
||||
hint.draw(tes, d0, d1, d2, i0, i1, i2);
|
||||
}
|
||||
p.endStartSection("Draw");
|
||||
tes.draw();
|
||||
p.endSection();
|
||||
|
||||
GL11.glPopAttrib();
|
||||
GL11.glPopMatrix();
|
||||
p.endSection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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.FMLInitializationEvent;
|
||||
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) {}
|
||||
|
||||
public void hintParticle(World w, int x, int y, int z, IIcon[] icons) {}
|
||||
|
||||
public void hintParticle(World w, int x, int y, int z, Block block, int meta) {}
|
||||
|
||||
public boolean updateHintParticleTint(EntityPlayer player, World w, int x, int y, int z, short[] rgBa) {
|
||||
if (player instanceof EntityPlayerMP) { // just in case
|
||||
StructureLib.net.sendTo(
|
||||
new UpdateHintParticleMessage(x, (short) y, z, rgBa[0], rgBa[1], rgBa[2], rgBa[3]),
|
||||
(EntityPlayerMP) player);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public EntityPlayer getCurrentPlayer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isCurrentPlayer(EntityPlayer player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addClientSideChatMessages(String... messages) {}
|
||||
|
||||
public void startHinting(World w) {}
|
||||
|
||||
public void endHinting(World w) {}
|
||||
|
||||
public void preInit(FMLPreInitializationEvent e) {}
|
||||
|
||||
public void init(FMLInitializationEvent e) {}
|
||||
|
||||
public long getOverworldTime() {
|
||||
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
||||
}
|
||||
|
||||
public void uploadChannels(ItemStack trigger) {}
|
||||
|
||||
public boolean markHintParticleError(EntityPlayer player, World w, int x, int y, int z) {
|
||||
if (player instanceof EntityPlayerMP) { // just in case
|
||||
StructureLib.net.sendTo(new ErrorHintParticleMessage(x, (short) y, z), (EntityPlayerMP) player);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
boolean forceUpdateLastSend) {
|
||||
if (player instanceof EntityPlayerMP) {
|
||||
Map<Object, Long> submap = throttleMap.computeIfAbsent((EntityPlayerMP) player, p -> new HashMap<>());
|
||||
addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend, submap);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
|
||||
short intervalRequired, boolean forceUpdateLastSend, Map<Object, Long> submap) {
|
||||
long now = System.currentTimeMillis();
|
||||
Long old;
|
||||
if (forceUpdateLastSend) {
|
||||
old = submap.put(throttleKey, now);
|
||||
} else {
|
||||
old = submap.get(throttleKey);
|
||||
}
|
||||
if (old == null || now - old >= intervalRequired) {
|
||||
player.addChatComponentMessage(text);
|
||||
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,21 +1,39 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
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 java.io.File;
|
||||
import java.util.Map;
|
||||
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 {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private Configuration config;
|
||||
private int maxCoexistingHologram;
|
||||
private boolean removeCollidingHologram;
|
||||
private int hintLifespan;
|
||||
private int hintTransparency;
|
||||
private int autoPlaceBudget;
|
||||
private int autoPlaceInterval;
|
||||
private Map<String, Pair<List<String>, List<String>>> registryOrders;
|
||||
|
||||
ConfigurationHandler() {
|
||||
FMLCommonHandler.instance().bus().register(this);
|
||||
@@ -39,11 +57,91 @@ public enum ConfigurationHandler {
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
maxCoexistingHologram = config.getInt("maxCoexisting", "client.hologram", 1, 1, 100, "An attempt will be made to prune old holograms when a new hologram is about to be projected");
|
||||
removeCollidingHologram = config.getBoolean("removeColliding", "client.hologram", true, "An attempt will be made to remove an existing hologram if it collides with a new hologram.");
|
||||
maxCoexistingHologram = config.getInt(
|
||||
"maxCoexisting",
|
||||
"client.hologram",
|
||||
1,
|
||||
1,
|
||||
100,
|
||||
"An attempt will be made to prune old holograms when a new hologram is about to be projected");
|
||||
removeCollidingHologram = config.getBoolean(
|
||||
"removeColliding",
|
||||
"client.hologram",
|
||||
true,
|
||||
"An attempt will be made to remove an existing hologram if it collides with a new hologram.");
|
||||
hintLifespan = config
|
||||
.getInt("hintLifespan", "client.hologram", 400, 1, 20000, "Ticks before a hologram disappears.");
|
||||
hintTransparency = config.getInt(
|
||||
"hintTransparency",
|
||||
"client.hologram",
|
||||
192,
|
||||
1,
|
||||
255,
|
||||
"Alpha value of hologram particles. Higher the value, the more \"ghostly\" the hologram will appear to be.");
|
||||
autoPlaceBudget = config.getInt(
|
||||
"autoPlaceBudget",
|
||||
"common.hologram",
|
||||
25,
|
||||
1,
|
||||
200,
|
||||
"Max number of elements can be placed in one round of auto place.\n"
|
||||
+ "As expected, server side settings will overrides client settings.\n"
|
||||
+ "Certain larger multi might increase these values beyond this configured value.");
|
||||
autoPlaceInterval = config.getInt(
|
||||
"autoPlaceInterval",
|
||||
"common.hologram",
|
||||
300,
|
||||
0,
|
||||
20000,
|
||||
"Unit: millisecond. Minimal interval between two auto place round.\n"
|
||||
+ "As expected, server side settings will overrides client settings.\n"
|
||||
+ "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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +160,28 @@ public enum ConfigurationHandler {
|
||||
return removeCollidingHologram;
|
||||
}
|
||||
|
||||
public int getHintLifespan() {
|
||||
return hintLifespan;
|
||||
}
|
||||
|
||||
public int getHintTransparency() {
|
||||
return hintTransparency;
|
||||
}
|
||||
|
||||
public int getAutoPlaceBudget() {
|
||||
return autoPlaceBudget;
|
||||
}
|
||||
|
||||
public int getAutoPlaceInterval() {
|
||||
return autoPlaceInterval;
|
||||
}
|
||||
|
||||
public Pair<List<String>, List<String>> getRegistryOrder(String name) {
|
||||
return registryOrders.get(name);
|
||||
}
|
||||
|
||||
Configuration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
|
||||
enum ConfigurationVersion {
|
||||
|
||||
V1 {
|
||||
|
||||
@Override
|
||||
protected void step(Configuration c) {
|
||||
}
|
||||
protected void step(Configuration c) {}
|
||||
};
|
||||
|
||||
private static final ConfigurationVersion[] VALUES = values();
|
||||
@@ -32,7 +34,8 @@ enum ConfigurationVersion {
|
||||
}
|
||||
|
||||
public static ConfigurationVersion identify(Configuration c) {
|
||||
return Arrays.stream(VALUES).filter(v -> Objects.equals(c.getLoadedConfigVersion(), v.getVersionMarker())).findFirst().orElse(V1);
|
||||
return Arrays.stream(VALUES).filter(v -> Objects.equals(c.getLoadedConfigVersion(), v.getVersionMarker()))
|
||||
.findFirst().orElse(V1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
|
||||
public class DevelopHelper {
|
||||
|
||||
private static final boolean DEBUG = Boolean.getBoolean("structurelib.develop_helper");
|
||||
|
||||
public static void onPreInit() {
|
||||
if (!DEBUG) return;
|
||||
StructureLib.LOGGER.info("Running develop helper");
|
||||
StructureLibAPI.registerChannelDescription("sandstone", StructureLibAPI.MOD_ID, "variants of Sandstone");
|
||||
StructureLibAPI.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 1, new ItemStack(Blocks.sandstone));
|
||||
StructureLibAPI
|
||||
.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 2, new ItemStack(Blocks.sandstone, 1, 1));
|
||||
StructureLibAPI
|
||||
.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 3, new ItemStack(Blocks.sandstone, 1, 2));
|
||||
StructureLibAPI.registerChannelDescription("sandbricks", StructureLibAPI.MOD_ID, "variants of Sandbricks");
|
||||
StructureLibAPI.registerChannelDescription("apple", StructureLibAPI.MOD_ID, "apple");
|
||||
StructureLibAPI.registerChannelDescription("bright", StructureLibAPI.MOD_ID, "bright");
|
||||
StructureLibAPI.registerChannelDescription("apple_blight", StructureLibAPI.MOD_ID, "apple_blight");
|
||||
StructureLibAPI.registerChannelDescription("app_eagle", StructureLibAPI.MOD_ID, "app_eagle");
|
||||
}
|
||||
|
||||
public void x() {
|
||||
NBTTagCompound command = new NBTTagCompound();
|
||||
NBTTagList items = new NBTTagList();
|
||||
int[] metas = { 0, 1, 2 };
|
||||
for (int meta : metas) {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
tag.setTag("Item", new ItemStack(Blocks.sandstone, 1, meta).writeToNBT(new NBTTagCompound()));
|
||||
NBTTagList channels = new NBTTagList();
|
||||
NBTTagCompound channel = new NBTTagCompound();
|
||||
channel.setString("Channel", "sandstone");
|
||||
channel.setInteger("Value", meta + 1);
|
||||
channels.appendTag(channel);
|
||||
tag.setTag("Channels", channels);
|
||||
items.appendTag(tag);
|
||||
}
|
||||
command.setTag("Items", items);
|
||||
FMLInterModComms.sendMessage("structurelib", "register_channel_item", command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
@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 parentGuiConfig) {
|
||||
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.func_146283_a(
|
||||
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.undoAll"), 300),
|
||||
mouseX,
|
||||
mouseY);
|
||||
if (this.resetHoverChecker.checkHover(mouseX, mouseY)) this.func_146283_a(
|
||||
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.resetAll"), 300),
|
||||
mouseX,
|
||||
mouseY);
|
||||
}
|
||||
|
||||
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.func_146283_a(
|
||||
mc.fontRenderer.listFormattedStringToWidth(tooltip, entriesList.getListWidth()),
|
||||
mouseX,
|
||||
mouseY);
|
||||
}
|
||||
if (hoverCheckerBtnDrag.checkHover(mouseX, mouseY) && !isDragging) {
|
||||
GuiEditRegistryConfig.this.func_146283_a(
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import cpw.mods.fml.client.IModGuiFactory;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
|
||||
import cpw.mods.fml.client.IModGuiFactory;
|
||||
|
||||
public class GuiFactory implements IModGuiFactory {
|
||||
|
||||
@Override
|
||||
public void initialize(Minecraft minecraftInstance) {
|
||||
}
|
||||
public void initialize(Minecraft minecraftInstance) {}
|
||||
|
||||
@Override
|
||||
public Class<? extends GuiScreen> mainConfigGuiClass() {
|
||||
@@ -25,4 +26,4 @@ public class GuiFactory implements IModGuiFactory {
|
||||
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.gui.EmptyContainer;
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
|
||||
|
||||
import cpw.mods.fml.common.network.IGuiHandler;
|
||||
|
||||
public class GuiHandler implements IGuiHandler {
|
||||
|
||||
@Override
|
||||
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||
switch (ID) {
|
||||
case 0:
|
||||
return new EmptyContainer(player);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||
switch (ID) {
|
||||
case 0:
|
||||
return new GuiScreenConfigureChannels(new EmptyContainer(player), player.inventory.getStackInSlot(x));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,31 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.config.IConfigElement;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraftforge.common.config.ConfigElement;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraftforge.common.config.ConfigElement;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
|
||||
import cpw.mods.fml.client.config.GuiConfig;
|
||||
import cpw.mods.fml.client.config.IConfigElement;
|
||||
|
||||
public class GuiModConfig extends GuiConfig {
|
||||
|
||||
public GuiModConfig(GuiScreen guiScreen) {
|
||||
super(guiScreen, getConfigElements(), StructureLibAPI.MOD_ID, false, false, GuiConfig.getAbridgedConfigPath(ConfigurationHandler.INSTANCE.getConfig().toString()));
|
||||
super(
|
||||
guiScreen,
|
||||
getConfigElements(),
|
||||
StructureLibAPI.MOD_ID,
|
||||
false,
|
||||
false,
|
||||
GuiConfig.getAbridgedConfigPath(ConfigurationHandler.INSTANCE.getConfig().toString()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static List<IConfigElement> getConfigElements() {
|
||||
final Configuration config = ConfigurationHandler.INSTANCE.getConfig();
|
||||
return config.getCategoryNames().stream()
|
||||
.filter(name -> name.indexOf('.') == -1)
|
||||
.map(name -> new ConfigElement(config.getCategory(name)))
|
||||
.collect(Collectors.toList());
|
||||
return config.getCategoryNames().stream().filter(name -> name.indexOf('.') == -1)
|
||||
.map(name -> new ConfigElement(config.getCategory(name))).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
public interface IStructureCompat {
|
||||
|
||||
void markTextureUsed(IIcon o);
|
||||
}
|
||||
@@ -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,197 @@
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureElement;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.Event;
|
||||
|
||||
/**
|
||||
* Subscribers are <b>required</b> to not keep reference to these events, nor to depend on values of these events after
|
||||
* the method call returns.
|
||||
* <p>
|
||||
* All of these events will be fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}, if they are
|
||||
* enabled by {@link StructureLibAPI#enableInstrument(Object)}.
|
||||
* <p>
|
||||
* All public stuff in this class and its enclosed classes are considered part of the public API. Backwards
|
||||
* compatibility is maintained to the maximum extend possible.
|
||||
*/
|
||||
public abstract class StructureEvent extends Event {
|
||||
|
||||
/*
|
||||
* In case the event is passed to another thread for processing.
|
||||
*/
|
||||
private final Object identifier = StructureLibAPI.instrument.get();
|
||||
|
||||
StructureEvent() {}
|
||||
|
||||
/**
|
||||
* The instrument identifier. Subscribers should filter by these identifiers to prevent listening to a structure
|
||||
* event for other people.
|
||||
*/
|
||||
public Object getInstrumentIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired <b>just before</b> any structure element is visited.
|
||||
*/
|
||||
public static final class StructureElementVisitedEvent extends StructureEvent {
|
||||
|
||||
private final World world;
|
||||
private final int x, y, z;
|
||||
private final int a, b, c;
|
||||
private final IStructureElement<?> element;
|
||||
|
||||
/**
|
||||
* Fire this event on {@link MinecraftForge#EVENT_BUS} by populating it with given arguments. Do nothing if
|
||||
* instrumenting isn't enabled for current thread.
|
||||
* <p>
|
||||
* Any {@link Exception} thrown by event listeners will be logged and ignored. {@link Error} will be propagated
|
||||
* as is.
|
||||
*/
|
||||
public static void fireEvent(World world, int x, int y, int z, int a, int b, int c,
|
||||
IStructureElement<?> element) {
|
||||
if (StructureLibAPI.isInstrumentEnabled()) {
|
||||
try {
|
||||
MinecraftForge.EVENT_BUS.post(new StructureElementVisitedEvent(world, x, y, z, a, b, c, element));
|
||||
} catch (Exception e) {
|
||||
// logging is done by event bus, we just ignore the consequence here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StructureElementVisitedEvent(World world, int x, int y, int z, int a, int b, int c,
|
||||
IStructureElement<?> element) {
|
||||
this.world = world;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* A position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
|
||||
* <p>
|
||||
* Submit a feature request if you need the baseOffset.
|
||||
*/
|
||||
public int getA() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* B position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
|
||||
* <p>
|
||||
* Submit a feature request if you need the baseOffset.
|
||||
*/
|
||||
public int getB() {
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* C position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
|
||||
* <p>
|
||||
* Submit a feature request if you need the baseOffset.
|
||||
*/
|
||||
public int getC() {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Element being visited.
|
||||
*/
|
||||
public IStructureElement<?> getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StructureElementVisitedEvent{" + "a="
|
||||
+ a
|
||||
+ ", b="
|
||||
+ b
|
||||
+ ", c="
|
||||
+ c
|
||||
+ ", element="
|
||||
+ element
|
||||
+ ", world="
|
||||
+ getWorld()
|
||||
+ ", x="
|
||||
+ getX()
|
||||
+ ", y="
|
||||
+ getY()
|
||||
+ ", z="
|
||||
+ getZ()
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Location of this event.
|
||||
*/
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Location of this event. Absolute world coordinate.
|
||||
*/
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Location of this event. Absolute world coordinate.
|
||||
*/
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Location of this event. Absolute world coordinate.
|
||||
*/
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +1,218 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||
import com.gtnewhorizon.structurelib.block.BlockHint;
|
||||
import com.gtnewhorizon.structurelib.item.ItemBlockHint;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
|
||||
import com.gtnewhorizon.structurelib.proxy.CommonProxy;
|
||||
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.CHANNEL_SHOW_ERROR;
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
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.ItemDebugConstructableTrigger;
|
||||
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.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||
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;
|
||||
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* This class does not contain a stable API. Refrain from using this class.
|
||||
*/
|
||||
@Mod(modid = StructureLibAPI.MOD_ID, name = "StructureLib", version = "${version}", acceptableRemoteVersions = "*", guiFactory = "com.gtnewhorizon.structurelib.GuiFactory")
|
||||
@Mod(
|
||||
modid = MOD_ID,
|
||||
name = "StructureLib",
|
||||
version = Tags.VERSION,
|
||||
acceptableRemoteVersions = "*",
|
||||
guiFactory = "com.gtnewhorizon.structurelib.GuiFactory")
|
||||
public class StructureLib {
|
||||
public static boolean DEBUG_MODE = Boolean.getBoolean("structurelib.debug");
|
||||
public static Logger LOGGER = LogManager.getLogger("StructureLib");
|
||||
|
||||
@SidedProxy(serverSide = "com.gtnewhorizon.structurelib.proxy.CommonProxy", clientSide = "com.gtnewhorizon.structurelib.proxy.ClientProxy")
|
||||
static CommonProxy proxy;
|
||||
static SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(StructureLibAPI.MOD_ID);
|
||||
private static final String STRUCTURECOMPAT_MODID = "structurecompat";
|
||||
public static boolean DEBUG_MODE;
|
||||
public static boolean PANIC_MODE = Boolean.getBoolean("structurelib.panic");
|
||||
public static final Logger LOGGER = LogManager.getLogger("StructureLib");
|
||||
|
||||
static {
|
||||
net.registerMessage(AlignmentMessage.ServerHandler.class, AlignmentMessage.AlignmentQuery.class, 0, Side.SERVER);
|
||||
net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT);
|
||||
}
|
||||
@SidedProxy(
|
||||
serverSide = "com.gtnewhorizon.structurelib.CommonProxy",
|
||||
clientSide = "com.gtnewhorizon.structurelib.ClientProxy")
|
||||
static CommonProxy proxy;
|
||||
|
||||
public static final XSTR RANDOM = new XSTR();
|
||||
static final SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(MOD_ID);
|
||||
|
||||
static Block blockHint;
|
||||
static Item itemBlockHint;
|
||||
static Item itemFrontRotationTool;
|
||||
static Item itemConstructableTrigger;
|
||||
public static final CreativeTabs creativeTab = new CreativeTabs("structurelib") {
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public Item getTabIconItem() {
|
||||
return StructureLibAPI.getItemBlockHint();
|
||||
}
|
||||
};
|
||||
static {
|
||||
net.registerMessage(
|
||||
AlignmentMessage.ServerHandler.class,
|
||||
AlignmentMessage.AlignmentQuery.class,
|
||||
0,
|
||||
Side.SERVER);
|
||||
net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT);
|
||||
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);
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent e) {
|
||||
ConfigurationHandler.INSTANCE.init(e.getSuggestedConfigurationFile());
|
||||
GameRegistry.registerBlock(blockHint = new BlockHint(), ItemBlockHint.class, "blockhint");
|
||||
itemBlockHint = ItemBlock.getItemFromBlock(StructureLibAPI.getBlockHint());
|
||||
GameRegistry.registerItem(itemFrontRotationTool = new ItemFrontRotationTool(), itemFrontRotationTool.getUnlocalizedName());
|
||||
GameRegistry.registerItem(itemConstructableTrigger = new ItemConstructableTrigger(), itemConstructableTrigger.getUnlocalizedName());
|
||||
proxy.preInit(e);
|
||||
}
|
||||
try {
|
||||
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
// turn on debug by default in dev mode
|
||||
// this will be overridden if above property is present and set to false
|
||||
DEBUG_MODE = (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
|
||||
}
|
||||
}
|
||||
|
||||
public static void addClientSideChatMessages(String... messages) {
|
||||
proxy.addClientSideChatMessages(messages);
|
||||
}
|
||||
public static final XSTR RANDOM = new XSTR();
|
||||
|
||||
public static EntityPlayer getCurrentPlayer() {
|
||||
return proxy.getCurrentPlayer();
|
||||
}
|
||||
@Mod.Instance
|
||||
static StructureLib INSTANCE;
|
||||
@Mod.Instance(STRUCTURECOMPAT_MODID)
|
||||
static Object COMPAT;
|
||||
|
||||
public static boolean isCurrentPlayer(EntityPlayer player) {
|
||||
return proxy.isCurrentPlayer(player);
|
||||
}
|
||||
static Block blockHint;
|
||||
static Item itemBlockHint;
|
||||
static Item itemFrontRotationTool;
|
||||
static Item itemConstructableTrigger;
|
||||
static Item itemDebugConstructableTrigger;
|
||||
public static final CreativeTabs creativeTab = new CreativeTabs("structurelib") {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public Item getTabIconItem() {
|
||||
return StructureLibAPI.getItemBlockHint();
|
||||
}
|
||||
};
|
||||
|
||||
@Mod.EventHandler
|
||||
public void preInit(FMLPreInitializationEvent e) {
|
||||
ConfigurationHandler.INSTANCE.init(e.getSuggestedConfigurationFile());
|
||||
GameRegistry.registerBlock(blockHint = new BlockHint(), ItemBlockHint.class, "blockhint");
|
||||
itemBlockHint = ItemBlock.getItemFromBlock(StructureLibAPI.getBlockHint());
|
||||
GameRegistry.registerItem(
|
||||
itemFrontRotationTool = new ItemFrontRotationTool(),
|
||||
itemFrontRotationTool.getUnlocalizedName());
|
||||
GameRegistry.registerItem(
|
||||
itemConstructableTrigger = new ItemConstructableTrigger(),
|
||||
itemConstructableTrigger.getUnlocalizedName());
|
||||
GameRegistry.registerItem(
|
||||
itemDebugConstructableTrigger = new ItemDebugConstructableTrigger(),
|
||||
itemDebugConstructableTrigger.getUnlocalizedName());
|
||||
proxy.preInit(e);
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
|
||||
|
||||
InventoryUtility.init();
|
||||
|
||||
ChannelDescription.set(CHANNEL_SHOW_ERROR, MOD_ID, "channels.structurelib.show_errors");
|
||||
|
||||
DevelopHelper.onPreInit();
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void init(FMLInitializationEvent e) {
|
||||
proxy.init(e);
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
public void handleIMC(FMLInterModComms.IMCEvent event) {
|
||||
for (FMLInterModComms.IMCMessage message : event.getMessages()) {
|
||||
switch (message.key) {
|
||||
case "register_channel":
|
||||
processRegisterChannel(message);
|
||||
break;
|
||||
case "register_channel_item":
|
||||
processRegisterChannelItem(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processRegisterChannel(FMLInterModComms.IMCMessage message) {
|
||||
NBTTagList tags = message.getNBTValue().getTagList("Channels", Constants.NBT.TAG_COMPOUND);
|
||||
for (int i = 0; i < tags.tagCount(); i++) {
|
||||
NBTTagCompound tag = tags.getCompoundTagAt(i);
|
||||
StructureLibAPI.registerChannelDescription(
|
||||
tag.getString("Channel"),
|
||||
message.getSender(),
|
||||
tag.getString("Description"));
|
||||
}
|
||||
}
|
||||
|
||||
private void processRegisterChannelItem(FMLInterModComms.IMCMessage message) {
|
||||
NBTTagList items = message.getNBTValue().getTagList("Items", Constants.NBT.TAG_COMPOUND);
|
||||
for (int i = 0; i < items.tagCount(); i++) {
|
||||
NBTTagCompound itemTag = items.getCompoundTagAt(i);
|
||||
ItemStack item = ItemStack.loadItemStackFromNBT(itemTag.getCompoundTag("Item"));
|
||||
NBTTagList tags = itemTag.getTagList("Channels", Constants.NBT.TAG_COMPOUND);
|
||||
for (int j = 0; j < tags.tagCount(); j++) {
|
||||
NBTTagCompound tag = tags.getCompoundTagAt(j);
|
||||
StructureLibAPI.registerChannelItem(
|
||||
tag.getString("Channel"),
|
||||
message.getSender(),
|
||||
tag.getInteger("Value"),
|
||||
item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void addClientSideChatMessages(String... messages) {
|
||||
proxy.addClientSideChatMessages(messages);
|
||||
}
|
||||
|
||||
public static EntityPlayer getCurrentPlayer() {
|
||||
return proxy.getCurrentPlayer();
|
||||
}
|
||||
|
||||
public static boolean isCurrentPlayer(EntityPlayer player) {
|
||||
return proxy.isCurrentPlayer(player);
|
||||
}
|
||||
|
||||
public static long getOverworldTime() {
|
||||
return proxy.getOverworldTime();
|
||||
}
|
||||
|
||||
public static StructureLib instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public CommonProxy proxy() {
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
@@ -1,123 +1,388 @@
|
||||
package com.gtnewhorizon.structurelib;
|
||||
|
||||
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.proxy;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.proxy;
|
||||
import com.gtnewhorizon.gtnhlib.util.AnimatedTooltipHandler;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||
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;
|
||||
|
||||
/**
|
||||
* A stable interface into the structure lib's internals. Backwards compatibility is maintained to the maximum extend possible.
|
||||
* A stable interface into the structure lib's internals. Backwards compatibility is maintained to the maximum extend
|
||||
* possible.
|
||||
*/
|
||||
public class StructureLibAPI {
|
||||
public static final String MOD_ID = "structurelib";
|
||||
public static final int HINT_BLOCK_META_GENERIC_0 = 0;
|
||||
public static final int HINT_BLOCK_META_GENERIC_1 = 1;
|
||||
public static final int HINT_BLOCK_META_GENERIC_2 = 2;
|
||||
public static final int HINT_BLOCK_META_GENERIC_3 = 3;
|
||||
public static final int HINT_BLOCK_META_GENERIC_4 = 4;
|
||||
public static final int HINT_BLOCK_META_GENERIC_5 = 5;
|
||||
public static final int HINT_BLOCK_META_GENERIC_6 = 6;
|
||||
public static final int HINT_BLOCK_META_GENERIC_7 = 7;
|
||||
public static final int HINT_BLOCK_META_GENERIC_8 = 8;
|
||||
public static final int HINT_BLOCK_META_GENERIC_9 = 9;
|
||||
public static final int HINT_BLOCK_META_GENERIC_10 = 10;
|
||||
public static final int HINT_BLOCK_META_GENERIC_11 = 11;
|
||||
public static final int HINT_BLOCK_META_DEFAULT = 12;
|
||||
public static final int HINT_BLOCK_META_AIR = 13;
|
||||
public static final int HINT_BLOCK_META_NOT_AIR = 14;
|
||||
public static final int HINT_BLOCK_META_ERROR = 15;
|
||||
|
||||
/**
|
||||
* Start a batch of hinting. All hints particles generated during one batch will be considered to belong to one hologram.
|
||||
*
|
||||
* You don't need to call this unless your constructable tool didn't call {@link com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility#handle(ItemStack, EntityPlayer, World, int, int, int, int)}
|
||||
*/
|
||||
public static void startHinting(World w) {
|
||||
proxy.startHinting(w);
|
||||
}
|
||||
public static final String MOD_ID = "structurelib";
|
||||
public static final int HINT_BLOCK_META_GENERIC_0 = 0;
|
||||
public static final int HINT_BLOCK_META_GENERIC_1 = 1;
|
||||
public static final int HINT_BLOCK_META_GENERIC_2 = 2;
|
||||
public static final int HINT_BLOCK_META_GENERIC_3 = 3;
|
||||
public static final int HINT_BLOCK_META_GENERIC_4 = 4;
|
||||
public static final int HINT_BLOCK_META_GENERIC_5 = 5;
|
||||
public static final int HINT_BLOCK_META_GENERIC_6 = 6;
|
||||
public static final int HINT_BLOCK_META_GENERIC_7 = 7;
|
||||
public static final int HINT_BLOCK_META_GENERIC_8 = 8;
|
||||
public static final int HINT_BLOCK_META_GENERIC_9 = 9;
|
||||
public static final int HINT_BLOCK_META_GENERIC_10 = 10;
|
||||
public static final int HINT_BLOCK_META_GENERIC_11 = 11;
|
||||
public static final int HINT_BLOCK_META_DEFAULT = 12;
|
||||
public static final int HINT_BLOCK_META_AIR = 13;
|
||||
public static final int HINT_BLOCK_META_NOT_AIR = 14;
|
||||
public static final int HINT_BLOCK_META_ERROR = 15;
|
||||
public static final String CHANNEL_SHOW_ERROR = "show_error";
|
||||
static final ThreadLocal<Object> instrument = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* Start current batch of hinting. All hints particles generated during one batch will be considered to belong to one hologram.
|
||||
*
|
||||
* You don't need to call this unless your constructable tool didn't call {@link com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility#handle(ItemStack, EntityPlayer, World, int, int, int, int)}
|
||||
*/
|
||||
public static void endHinting(World w) {
|
||||
proxy.endHinting(w);
|
||||
}
|
||||
/**
|
||||
* Start instrumenting <b>for the current thread only</b>. When instrumentation is enabled,
|
||||
* {@link IStructureDefinition}'s builtin iteration methods will send {@link StructureEvent} and its subclasses when
|
||||
* appropriate. Otherwise, these events will not be sent at all, regardless of whether a listener for these events
|
||||
* are registered.
|
||||
* <p>
|
||||
* Identifiers are required to be value-comparable, i.e. overrides {@link Object#equals(Object)}. Suggested
|
||||
* identifier includes
|
||||
* <ul>
|
||||
* <li>Mod Instance or {@link cpw.mods.fml.common.ModContainer}</li>
|
||||
* <li>{@link com.gtnewhorizon.structurelib.alignment.constructable.IConstructable} and its friends, if you are
|
||||
* triggering structure check via this interface.</li>
|
||||
* <li>{@link net.minecraft.util.ResourceLocation}</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* It's an API abuse for normal {@link IStructureDefinition} users to depend on this feature. They have full control
|
||||
* and full knowledge of the {@link IStructureDefinition} they are using. Using events to instrument the structure
|
||||
* check/autoplace is completely unnecessary and does nothing but add overhead.
|
||||
* <p>
|
||||
* Instruments are required to call {@link #disableInstrument()} whenever it is done with its business, as there can
|
||||
* be only one instrument for any given thread.
|
||||
*
|
||||
* @param identifier an identifier sent along the actual event.
|
||||
* @throws IllegalStateException if instrument has already been enabled.
|
||||
*/
|
||||
public static void enableInstrument(Object identifier) {
|
||||
if (isInstrumentEnabled()) throw new IllegalStateException();
|
||||
instrument.set(identifier);
|
||||
}
|
||||
|
||||
public static void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
|
||||
proxy.hintParticleTinted(w, x, y, z, icons, RGBa);
|
||||
}
|
||||
/**
|
||||
* Stop instrumenting.
|
||||
*
|
||||
* @throws IllegalStateException if instrumenting hasn't been enabled.
|
||||
*/
|
||||
public static void disableInstrument() {
|
||||
if (!isInstrumentEnabled()) throw new IllegalStateException();
|
||||
instrument.set(null);
|
||||
}
|
||||
|
||||
public static void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {
|
||||
proxy.hintParticleTinted(w, x, y, z, block, meta, RGBa);
|
||||
}
|
||||
/**
|
||||
* Check if instrumenting is enabled.
|
||||
*
|
||||
* @return true if enabled, false otherwise
|
||||
*/
|
||||
public static boolean isInstrumentEnabled() {
|
||||
return instrument.get() != null;
|
||||
}
|
||||
|
||||
public static void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
|
||||
proxy.hintParticle(w, x, y, z, icons);
|
||||
}
|
||||
/**
|
||||
* Start a batch of hinting. All hints particles generated during one batch will be considered to belong to one
|
||||
* hologram.
|
||||
* <p>
|
||||
* You don't need to call this unless your constructable tool didn't call
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility#handle(ItemStack, EntityPlayer, World, int, int, int, int)}
|
||||
*/
|
||||
public static void startHinting(World w) {
|
||||
proxy.startHinting(w);
|
||||
}
|
||||
|
||||
public static void hintParticle(World w, int x, int y, int z, Block block, int meta) {
|
||||
proxy.hintParticle(w, x, y, z, block, meta);
|
||||
}
|
||||
/**
|
||||
* End current batch of hinting. All hints particles generated during one batch will be considered to belong to one
|
||||
* hologram.
|
||||
* <p>
|
||||
* You don't need to call this unless your constructable tool didn't call
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility#handle(ItemStack, EntityPlayer, World, int, int, int, int)}
|
||||
*/
|
||||
public static void endHinting(World w) {
|
||||
proxy.endHinting(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the ExtendedFacing of this tile entity.
|
||||
* The ExtendedFacing will later be set onto given tile entity via {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)}
|
||||
* upon arrival of server reply.
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void queryAlignment(IAlignmentProvider provider) {
|
||||
StructureLib.net.sendToServer(new AlignmentMessage.AlignmentQuery(provider));
|
||||
}
|
||||
/**
|
||||
* Generate a new hint particle on client side at given location using given textures with a tint.
|
||||
*
|
||||
* @param w World to spawn. Usually the client world.
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @param icons 6 texture. in forge direction order.
|
||||
* @param RGBa a 4 short array. tint in rgba form. currently alpha channel is ignored, but we might change this
|
||||
* later on.
|
||||
*/
|
||||
public static void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
|
||||
proxy.hintParticleTinted(w, x, y, z, icons, RGBa);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to all players. Can be called on server side only.
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider) {
|
||||
StructureLib.net.sendToAll(new AlignmentMessage.AlignmentData(provider));
|
||||
}
|
||||
/**
|
||||
* Generate a new hint particle on client side at given location using textures from given block with a tint.
|
||||
*
|
||||
* @param w World to spawn. Usually the client world.
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @param block block to take texture from
|
||||
* @param meta the meta of block to take texture from
|
||||
* @param RGBa a 4 short array. tint in rgba form. currently alpha channel is ignored, but we might change this
|
||||
* later on.
|
||||
*/
|
||||
public static void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {
|
||||
proxy.hintParticleTinted(w, x, y, z, block, meta, RGBa);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to given player. Can be called on server side only.
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider, EntityPlayerMP player) {
|
||||
StructureLib.net.sendTo(new AlignmentMessage.AlignmentData(provider), player);
|
||||
}
|
||||
/**
|
||||
* Generate a new hint particle on client side at given location using given textures.
|
||||
*
|
||||
* @param w World to spawn. Usually the client world.
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @param icons 6 texture. in forge direction order.
|
||||
*/
|
||||
public static void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
|
||||
proxy.hintParticle(w, x, y, z, icons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to all players around target point. Can be called on server side only.
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider, NetworkRegistry.TargetPoint targetPoint) {
|
||||
StructureLib.net.sendToAllAround(new AlignmentMessage.AlignmentData(provider), targetPoint);
|
||||
}
|
||||
/**
|
||||
* Generate a new hint particle on client side at given location using textures from given block.
|
||||
*
|
||||
* @param w World to spawn. Usually the client world.
|
||||
* @param x x coord
|
||||
* @param y y coord
|
||||
* @param z z coord
|
||||
* @param block block to take texture from
|
||||
* @param meta the meta of block to take texture from
|
||||
*/
|
||||
public static void hintParticle(World w, int x, int y, int z, Block block, int meta) {
|
||||
proxy.hintParticle(w, x, y, z, block, meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the tint of given hint particle. Do nothing if particle not found.
|
||||
* <p>
|
||||
* Can be called on either client side or server side. Server side will schedule a network message to update it for
|
||||
* this player only. Will do nothing if no hint particle is found at given location.
|
||||
*
|
||||
* @return false if nothing updated. true if updated or update instruction sent.
|
||||
*/
|
||||
public static boolean markHintParticleError(EntityPlayer player, World w, int x, int y, int z) {
|
||||
return proxy.markHintParticleError(player, w, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to all players in that dimension. Can be called on server side only.
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider, World dimension) {
|
||||
StructureLib.net.sendToDimension(new AlignmentMessage.AlignmentData(provider), dimension.provider.dimensionId);
|
||||
}
|
||||
/**
|
||||
* Update the tint of given hint particle. Do nothing if particle not found.
|
||||
*
|
||||
* @return false if nothing updated. true if updated.
|
||||
*/
|
||||
public static boolean updateHintParticleTint(EntityPlayer player, World w, int x, int y, int z, short[] RGBa) {
|
||||
return proxy.updateHintParticleTint(player, w, x, y, z, RGBa);
|
||||
}
|
||||
|
||||
public static Block getBlockHint() {
|
||||
return StructureLib.blockHint;
|
||||
}
|
||||
/**
|
||||
* Query the ExtendedFacing of this tile entity from client side. Can be sent only on client side. The
|
||||
* ExtendedFacing will later be set onto given tile entity via
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} upon arrival of
|
||||
* server reply.
|
||||
* <p>
|
||||
* The server side will query the {@link ExtendedFacing} of this provider using
|
||||
* {@link IAlignment#getExtendedFacing()}
|
||||
*
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void queryAlignment(IAlignmentProvider provider) {
|
||||
StructureLib.net.sendToServer(new AlignmentMessage.AlignmentQuery(provider));
|
||||
}
|
||||
|
||||
public static Item getItemBlockHint() {
|
||||
return StructureLib.itemBlockHint;
|
||||
}
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to all players. Can be called on server side only.
|
||||
* <p>
|
||||
* The receiving tile entity will receive the {@link ExtendedFacing} via its
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method.
|
||||
*
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider) {
|
||||
StructureLib.net.sendToAll(new AlignmentMessage.AlignmentData(provider));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to given player. Can be called on server side only.
|
||||
* <p>
|
||||
* The receiving tile entity will receive the {@link ExtendedFacing} via its
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method.
|
||||
*
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider, EntityPlayerMP player) {
|
||||
StructureLib.net.sendTo(new AlignmentMessage.AlignmentData(provider), player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to all players around target point. Can be called on server side
|
||||
* only.
|
||||
* <p>
|
||||
* The receiving tile entity will receive the {@link ExtendedFacing} via its
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method.
|
||||
*
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider, NetworkRegistry.TargetPoint targetPoint) {
|
||||
StructureLib.net.sendToAllAround(new AlignmentMessage.AlignmentData(provider), targetPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the ExtendedFacing of this Tile Entity to all players in that dimension. Can be called on server side only.
|
||||
* <p>
|
||||
* The receiving tile entity will receive the {@link ExtendedFacing} via its
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method.
|
||||
*
|
||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
||||
*/
|
||||
public static void sendAlignment(IAlignmentProvider provider, World dimension) {
|
||||
StructureLib.net.sendToDimension(new AlignmentMessage.AlignmentData(provider), dimension.provider.dimensionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Block for StructureLib supplied hint block.
|
||||
*/
|
||||
public static Block getBlockHint() {
|
||||
return StructureLib.blockHint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ItemBlock for StructureLib supplied hint block.
|
||||
*/
|
||||
public static Item getItemBlockHint() {
|
||||
return StructureLib.itemBlockHint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hologram item.
|
||||
*/
|
||||
public static Item getDefaultHologramItem() {
|
||||
return StructureLib.itemConstructableTrigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if structure debug mode is on.
|
||||
*
|
||||
* @return true if debug mode is on
|
||||
*/
|
||||
public static boolean isDebugEnabled() {
|
||||
return StructureLib.DEBUG_MODE;
|
||||
}
|
||||
|
||||
public static void setDebugEnabled(boolean enabled) {
|
||||
StructureLib.DEBUG_MODE = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if given block can be replaced without much effort. The exact predicate clauses is not stable and will
|
||||
* be changed, but the general idea will always stay the same.
|
||||
* <p>
|
||||
* Use this in your
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean isBlockTriviallyReplaceable(World w, int x, int y, int z, EntityPlayerMP actor) {
|
||||
return isBlockTriviallyReplaceable(w, x, y, z, (EntityPlayer) actor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if given block can be replaced without much effort. The exact predicate clauses is not stable and will
|
||||
* be changed, but the general idea will always stay the same.
|
||||
* <p>
|
||||
* Use this in your
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
|
||||
*/
|
||||
public static boolean isBlockTriviallyReplaceable(World w, int x, int y, int z, EntityPlayer actor) {
|
||||
// TODO extend this function a bit
|
||||
Block block = w.getBlock(x, y, z);
|
||||
return block.isAir(w, x, y, z) || block.isReplaceable(w, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send chat to player, but throttled.
|
||||
*
|
||||
* @param throttleKey throttle key. Must properly implement {@link Object#hashCode()} and
|
||||
* {@link Object#equals(Object)} to handle value equality.
|
||||
* @param player player to send chat to
|
||||
* @param text chat to send
|
||||
* @param intervalRequired interval required before last recorded time to actually send the message. unit in
|
||||
* millisecond. we purposefully chose to not use a bigger data type to limit how long this
|
||||
* interval can be
|
||||
*/
|
||||
public static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
|
||||
short intervalRequired) {
|
||||
addThrottledChat(throttleKey, player, text, intervalRequired, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send chat to player, but throttled.
|
||||
*
|
||||
* @param throttleKey throttle key. Must properly implement {@link Object#hashCode()} and
|
||||
* {@link Object#equals(Object)} to handle value equality.
|
||||
* @param player player to send chat to
|
||||
* @param text chat to send
|
||||
* @param intervalRequired interval required before last recorded time to actually send the message. unit in
|
||||
* millisecond. we purposefully chose to not use a bigger data type to limit how long
|
||||
* this interval can be
|
||||
* @param forceUpdateLastSend if true, always update the last send time, even if the message isn't actually sent.
|
||||
*/
|
||||
public static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
|
||||
short intervalRequired, boolean forceUpdateLastSend) {
|
||||
proxy.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a channel description here. The description should describe how a channel will be used inside your mod.
|
||||
*
|
||||
* @param channel channel key
|
||||
* @param modid your modid
|
||||
* @param description localization key for your description
|
||||
*/
|
||||
public static void registerChannelDescription(final String channel, final String modid, final String description) {
|
||||
ChannelDescription.set(channel, modid, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an item stack as an indicator for a (channel, value) pair.
|
||||
*
|
||||
* @param channel channel key
|
||||
* @param modid your modid
|
||||
* @param channelValue value
|
||||
* @param stack item stack. having a metadata of
|
||||
* {@link net.minecraftforge.oredict.OreDictionary#WILDCARD_VALUE} will match without comparing
|
||||
* NBT or metadata value. having a tag with {@code tag.getBoolean("*") == true} will match
|
||||
* without comparing NBT.
|
||||
*/
|
||||
public static void registerChannelItem(final String channel, final String modid, final int channelValue,
|
||||
final ItemStack stack) {
|
||||
ChannelDescription.item(channel, channelValue, stack);
|
||||
AnimatedTooltipHandler.addItemTooltip(
|
||||
stack,
|
||||
AnimatedTooltipHandler.translatedText("structurelib.tooltip.channelvalue", channelValue, channel));
|
||||
AnimatedTooltipHandler.addItemTooltip(
|
||||
stack,
|
||||
AnimatedTooltipHandler.translatedText("structurelib.tooltip.indicator_dnd", channelValue, channel));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.gtnewhorizon.structurelib.alignment;
|
||||
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
class AlignmentLimits implements IAlignmentLimits {
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
|
||||
public class AlignmentUtility {
|
||||
private AlignmentUtility(){
|
||||
}
|
||||
|
||||
public static boolean handle(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ){
|
||||
private AlignmentUtility() {}
|
||||
|
||||
public static boolean handle(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ) {
|
||||
TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ);
|
||||
if (tTileEntity == null || aPlayer instanceof FakePlayer) {
|
||||
return aPlayer instanceof EntityPlayerMP;
|
||||
|
||||
@@ -1,66 +1,73 @@
|
||||
package com.gtnewhorizon.structurelib.alignment;
|
||||
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
||||
int DIRECTIONS_COUNT= Direction.VALUES.length;
|
||||
int ROTATIONS_COUNT= Rotation.VALUES.length;
|
||||
int FLIPS_COUNT= Flip.VALUES.length;
|
||||
public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
|
||||
|
||||
int DIRECTIONS_COUNT = Direction.VALUES.length;
|
||||
int ROTATIONS_COUNT = Rotation.VALUES.length;
|
||||
int FLIPS_COUNT = Flip.VALUES.length;
|
||||
int STATES_COUNT = ExtendedFacing.STATES_COUNT;
|
||||
|
||||
ExtendedFacing getExtendedFacing();
|
||||
|
||||
/**
|
||||
* Set the facing without additional checks. Tools altering facing should use
|
||||
* {@link #toolSetExtendedFacing(ExtendedFacing)} instead.
|
||||
*/
|
||||
void setExtendedFacing(ExtendedFacing alignment);
|
||||
|
||||
IAlignmentLimits getAlignmentLimits();
|
||||
|
||||
@Override
|
||||
default IAlignment getAlignment(){
|
||||
default IAlignment getAlignment() {
|
||||
return this;
|
||||
}
|
||||
|
||||
static int getAlignmentIndex(ForgeDirection direction, Rotation rotation, Flip flip){
|
||||
return (direction.ordinal()*ROTATIONS_COUNT+rotation.getIndex())*FLIPS_COUNT+flip.getIndex();
|
||||
static int getAlignmentIndex(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||
return (direction.ordinal() * ROTATIONS_COUNT + rotation.getIndex()) * FLIPS_COUNT + flip.getIndex();
|
||||
}
|
||||
|
||||
default ForgeDirection getDirection(){
|
||||
default ForgeDirection getDirection() {
|
||||
return getExtendedFacing().getDirection();
|
||||
}
|
||||
|
||||
default void setDirection(ForgeDirection direction){
|
||||
default void setDirection(ForgeDirection direction) {
|
||||
setExtendedFacing(getExtendedFacing().with(direction));
|
||||
}
|
||||
|
||||
default Rotation getRotation(){
|
||||
default Rotation getRotation() {
|
||||
return getExtendedFacing().getRotation();
|
||||
}
|
||||
|
||||
default void setRotation(Rotation rotation){
|
||||
default void setRotation(Rotation rotation) {
|
||||
setExtendedFacing(getExtendedFacing().with(rotation));
|
||||
}
|
||||
|
||||
default Flip getFlip(){
|
||||
default Flip getFlip() {
|
||||
return getExtendedFacing().getFlip();
|
||||
}
|
||||
|
||||
default void setFlip(Flip flip){
|
||||
default void setFlip(Flip flip) {
|
||||
setExtendedFacing(getExtendedFacing().with(flip));
|
||||
}
|
||||
|
||||
default boolean toolSetDirection(ForgeDirection direction){
|
||||
if(direction==null || direction==ForgeDirection.UNKNOWN){
|
||||
for (int i = 0,j=getDirection().ordinal()+1, valuesLength = Direction.VALUES.length; i < valuesLength; i++) {
|
||||
default boolean toolSetDirection(ForgeDirection direction) {
|
||||
if (direction == null || direction == ForgeDirection.UNKNOWN) {
|
||||
for (int i = 0, j = getDirection().ordinal() + 1, valuesLength = Direction.VALUES.length; i
|
||||
< valuesLength; i++) {
|
||||
if (toolSetDirection(Direction.VALUES[(j + i) % valuesLength].getForgeDirection())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) {
|
||||
if(checkedSetExtendedFacing(extendedFacing)){
|
||||
if (checkedSetExtendedFacing(extendedFacing)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -68,17 +75,17 @@ public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean checkedSetDirection(ForgeDirection direction){
|
||||
if (isNewDirectionValid(direction)){
|
||||
default boolean checkedSetDirection(ForgeDirection direction) {
|
||||
if (isNewDirectionValid(direction)) {
|
||||
setDirection(direction);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean canSetToDirectionAny(ForgeDirection direction){
|
||||
default boolean canSetToDirectionAny(ForgeDirection direction) {
|
||||
for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) {
|
||||
if(isNewExtendedFacingValid(extendedFacing)){
|
||||
if (isNewExtendedFacingValid(extendedFacing)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -86,66 +93,71 @@ public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
||||
}
|
||||
|
||||
default boolean toolSetRotation(Rotation rotation) {
|
||||
if(rotation==null){
|
||||
if (rotation == null) {
|
||||
int flips = Flip.VALUES.length;
|
||||
int rotations = Rotation.VALUES.length;
|
||||
for (int ii = 0,jj=getFlip().ordinal(); ii < flips; ii++) {
|
||||
for (int ii = 0, jj = getFlip().ordinal(); ii < flips; ii++) {
|
||||
for (int i = 1, j = getRotation().ordinal(); i < rotations; i++) {
|
||||
if (checkedSetExtendedFacing(ExtendedFacing.of(getDirection(), Rotation.VALUES[(j + i) % rotations], Flip.VALUES[(jj + ii) % flips]))) {
|
||||
if (checkedSetExtendedFacing(
|
||||
ExtendedFacing.of(
|
||||
getDirection(),
|
||||
Rotation.VALUES[(j + i) % rotations],
|
||||
Flip.VALUES[(jj + ii) % flips]))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}else {
|
||||
} else {
|
||||
return checkedSetRotation(rotation);
|
||||
}
|
||||
}
|
||||
|
||||
default boolean checkedSetRotation(Rotation rotation){
|
||||
if (isNewRotationValid(rotation)){
|
||||
default boolean checkedSetRotation(Rotation rotation) {
|
||||
if (isNewRotationValid(rotation)) {
|
||||
setRotation(rotation);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean toolSetFlip(Flip flip){
|
||||
if(flip==null){
|
||||
default boolean toolSetFlip(Flip flip) {
|
||||
if (flip == null) {
|
||||
for (int i = 1, j = getFlip().ordinal(), valuesLength = Flip.VALUES.length; i < valuesLength; i++) {
|
||||
if (checkedSetFlip(Flip.VALUES[(j + i) % valuesLength])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}else {
|
||||
} else {
|
||||
return checkedSetFlip(flip);
|
||||
}
|
||||
}
|
||||
|
||||
default boolean checkedSetFlip(Flip flip){
|
||||
if (isNewFlipValid(flip)){
|
||||
default boolean checkedSetFlip(Flip flip) {
|
||||
if (isNewFlipValid(flip)) {
|
||||
setFlip(flip);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean toolSetExtendedFacing(ExtendedFacing extendedFacing){
|
||||
if(extendedFacing==null){
|
||||
for (int i = 0,j=getExtendedFacing().ordinal()+1, valuesLength = ExtendedFacing.VALUES.length; i < valuesLength; i++) {
|
||||
if (checkedSetExtendedFacing(ExtendedFacing.VALUES[(j + i)%valuesLength])){
|
||||
default boolean toolSetExtendedFacing(ExtendedFacing extendedFacing) {
|
||||
if (extendedFacing == null) {
|
||||
for (int i = 0, j = getExtendedFacing().ordinal() + 1, valuesLength = ExtendedFacing.VALUES.length; i
|
||||
< valuesLength; i++) {
|
||||
if (checkedSetExtendedFacing(ExtendedFacing.VALUES[(j + i) % valuesLength])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}else {
|
||||
} else {
|
||||
return checkedSetExtendedFacing(extendedFacing);
|
||||
}
|
||||
}
|
||||
|
||||
default boolean checkedSetExtendedFacing(ExtendedFacing alignment){
|
||||
if (isNewExtendedFacingValid(alignment)){
|
||||
default boolean checkedSetExtendedFacing(ExtendedFacing alignment) {
|
||||
if (isNewExtendedFacingValid(alignment)) {
|
||||
setExtendedFacing(alignment);
|
||||
return true;
|
||||
}
|
||||
@@ -153,31 +165,53 @@ public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
||||
}
|
||||
|
||||
default boolean isNewDirectionValid(ForgeDirection direction) {
|
||||
return isNewExtendedFacingValid(direction,getRotation(),getFlip());
|
||||
return isNewExtendedFacingValid(direction, getRotation(), getFlip());
|
||||
}
|
||||
|
||||
default boolean isNewRotationValid(Rotation rotation){
|
||||
return isNewExtendedFacingValid(getDirection(),rotation,getFlip());
|
||||
default boolean isNewRotationValid(Rotation rotation) {
|
||||
return isNewExtendedFacingValid(getDirection(), rotation, getFlip());
|
||||
}
|
||||
|
||||
default boolean isNewFlipValid(Flip flip){
|
||||
return isNewExtendedFacingValid(getDirection(),getRotation(),flip);
|
||||
default boolean isNewFlipValid(Flip flip) {
|
||||
return isNewExtendedFacingValid(getDirection(), getRotation(), flip);
|
||||
}
|
||||
|
||||
default boolean isExtendedFacingValid() {
|
||||
return isNewExtendedFacingValid(getDirection(),getRotation(),getFlip());
|
||||
return isNewExtendedFacingValid(getDirection(), getRotation(), getFlip());
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip){
|
||||
default boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||
return getAlignmentLimits().isNewExtendedFacingValid(direction, rotation, flip);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isNewExtendedFacingValid(ExtendedFacing alignment){
|
||||
return getAlignmentLimits().isNewExtendedFacingValid(
|
||||
alignment.getDirection(),
|
||||
alignment.getRotation(),
|
||||
alignment.getFlip());
|
||||
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,45 @@
|
||||
package com.gtnewhorizon.structurelib.alignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||
|
||||
public interface IAlignmentLimits {
|
||||
|
||||
/**
|
||||
* Can be placed in any orientation.
|
||||
*/
|
||||
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
|
||||
|
||||
/**
|
||||
* Must be standing upright; i.e., top side of the block facing up.
|
||||
*/
|
||||
IAlignmentLimits UPRIGHT = (direction, rotation, flip) -> direction.offsetY == 0
|
||||
&& (flip.isVerticallyFliped() ? rotation.isUpsideDown() : rotation.isNotRotated());
|
||||
|
||||
/**
|
||||
* Can be in any orientation except upside down; i.e., top side of the block facing down.
|
||||
*/
|
||||
IAlignmentLimits NOT_UPSIDE_DOWN = (direction, rotation, flip) -> direction.offsetY != 0
|
||||
|| (flip.isVerticallyFliped() ? !rotation.isNotRotated() : !rotation.isUpsideDown());
|
||||
|
||||
/**
|
||||
* Front side of the block must be facing up.
|
||||
*/
|
||||
IAlignmentLimits FACING_UP = (direction, rotation, flip) -> direction == ForgeDirection.UP;
|
||||
|
||||
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
|
||||
|
||||
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
|
||||
return isNewExtendedFacingValid(
|
||||
alignment.getDirection(),
|
||||
alignment.getRotation(),
|
||||
alignment.getFlip());
|
||||
return isNewExtendedFacingValid(alignment.getDirection(), alignment.getRotation(), alignment.getFlip());
|
||||
}
|
||||
|
||||
static IAlignmentLimits allowOnly(ExtendedFacing... allowedFacings) {
|
||||
@@ -40,6 +59,7 @@ public interface IAlignmentLimits {
|
||||
}
|
||||
|
||||
class Builder {
|
||||
|
||||
protected final boolean[] validStates = new boolean[STATES_COUNT];
|
||||
|
||||
private Builder() {}
|
||||
@@ -57,12 +77,14 @@ public interface IAlignmentLimits {
|
||||
}
|
||||
|
||||
public Builder deny(ForgeDirection fd) {
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex).forEach(v -> validStates[v] = false);
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
|
||||
.forEach(v -> validStates[v] = false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder allow(ForgeDirection fd) {
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex).forEach(v -> validStates[v] = true);
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
|
||||
.forEach(v -> validStates[v] = true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -77,22 +99,26 @@ public interface IAlignmentLimits {
|
||||
}
|
||||
|
||||
public Builder deny(Rotation fd) {
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex).forEach(v -> validStates[v] = false);
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
|
||||
.forEach(v -> validStates[v] = false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder allow(Rotation fd) {
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex).forEach(v -> validStates[v] = true);
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
|
||||
.forEach(v -> validStates[v] = true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder deny(Flip fd) {
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex).forEach(v -> validStates[v] = false);
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
|
||||
.forEach(v -> validStates[v] = false);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder allow(Flip fd) {
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex).forEach(v -> validStates[v] = true);
|
||||
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
|
||||
.forEach(v -> validStates[v] = true);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -113,7 +139,7 @@ public interface IAlignmentLimits {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefers rotation over flip, so both flip will get translated to opposite rotation and no flip
|
||||
* Prefers rotation to flip, so both flip will get translated to opposite rotation and no flip
|
||||
*
|
||||
* @param flip the preferred flip to be used Horizontal or vertical
|
||||
* @return this
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.alignment;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IAlignmentProvider {
|
||||
|
||||
@Nullable
|
||||
IAlignment getAlignment();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package com.gtnewhorizon.structurelib.alignment;
|
||||
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import static java.lang.Math.abs;
|
||||
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public class IntegerAxisSwap {
|
||||
|
||||
private final Vec3Impl forFirstAxis;
|
||||
private final Vec3Impl forSecondAxis;
|
||||
private final Vec3Impl forThirdAxis;
|
||||
@@ -16,69 +18,77 @@ public class IntegerAxisSwap {
|
||||
this.forFirstAxis = Direction.getAxisVector(forFirstAxis);
|
||||
this.forSecondAxis = Direction.getAxisVector(forSecondAxis);
|
||||
this.forThirdAxis = Direction.getAxisVector(forThirdAxis);
|
||||
if(abs(this.forFirstAxis.get0())+abs(this.forSecondAxis.get0())+abs(this.forThirdAxis.get0())!=1 ||
|
||||
abs(this.forFirstAxis.get1())+abs(this.forSecondAxis.get1())+abs(this.forThirdAxis.get1())!=1 ||
|
||||
abs(this.forFirstAxis.get2())+abs(this.forSecondAxis.get2())+abs(this.forThirdAxis.get2())!=1){
|
||||
throw new IllegalArgumentException("Axis are overlapping/missing! "+
|
||||
forFirstAxis.name()+" "+
|
||||
forSecondAxis.name()+" "+
|
||||
forThirdAxis.name());
|
||||
if (abs(this.forFirstAxis.get0()) + abs(this.forSecondAxis.get0()) + abs(this.forThirdAxis.get0()) != 1
|
||||
|| abs(this.forFirstAxis.get1()) + abs(this.forSecondAxis.get1()) + abs(this.forThirdAxis.get1()) != 1
|
||||
|| abs(this.forFirstAxis.get2()) + abs(this.forSecondAxis.get2()) + abs(this.forThirdAxis.get2())
|
||||
!= 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Axis are overlapping/missing! " + forFirstAxis
|
||||
.name() + " " + forSecondAxis.name() + " " + forThirdAxis.name());
|
||||
}
|
||||
}
|
||||
|
||||
public Vec3Impl translate(Vec3Impl point){
|
||||
public Vec3Impl translate(Vec3Impl point) {
|
||||
return new Vec3Impl(
|
||||
forFirstAxis.get0()*point.get0() +forFirstAxis.get1()*point.get1() +forFirstAxis.get2()*point.get2(),
|
||||
forSecondAxis.get0()*point.get0()+forSecondAxis.get1()*point.get1()+forSecondAxis.get2()*point.get2(),
|
||||
forThirdAxis.get0()*point.get0() +forThirdAxis.get1()*point.get1() +forThirdAxis.get2()*point.get2()
|
||||
);
|
||||
forFirstAxis.get0() * point.get0() + forFirstAxis.get1() * point.get1()
|
||||
+ forFirstAxis.get2() * point.get2(),
|
||||
forSecondAxis.get0() * point.get0() + forSecondAxis.get1() * point.get1()
|
||||
+ forSecondAxis.get2() * point.get2(),
|
||||
forThirdAxis.get0() * point.get0() + forThirdAxis.get1() * point.get1()
|
||||
+ forThirdAxis.get2() * point.get2());
|
||||
}
|
||||
|
||||
public Vec3Impl inverseTranslate(Vec3Impl point){
|
||||
public Vec3Impl inverseTranslate(Vec3Impl point) {
|
||||
return new Vec3Impl(
|
||||
forFirstAxis.get0()*point.get0()+forSecondAxis.get0()*point.get1()+forThirdAxis.get0()*point.get2(),
|
||||
forFirstAxis.get1()*point.get0()+forSecondAxis.get1()*point.get1()+forThirdAxis.get1()*point.get2(),
|
||||
forFirstAxis.get2()*point.get0()+forSecondAxis.get2()*point.get1()+forThirdAxis.get2()*point.get2()
|
||||
);
|
||||
forFirstAxis.get0() * point.get0() + forSecondAxis.get0() * point.get1()
|
||||
+ forThirdAxis.get0() * point.get2(),
|
||||
forFirstAxis.get1() * point.get0() + forSecondAxis.get1() * point.get1()
|
||||
+ forThirdAxis.get1() * point.get2(),
|
||||
forFirstAxis.get2() * point.get0() + forSecondAxis.get2() * point.get1()
|
||||
+ forThirdAxis.get2() * point.get2());
|
||||
}
|
||||
|
||||
public Vec3 translate(Vec3 point){
|
||||
public Vec3 translate(Vec3 point) {
|
||||
return Vec3.createVectorHelper(
|
||||
forFirstAxis.get0()*point.xCoord +forFirstAxis.get1()*point.yCoord +forFirstAxis.get2()*point.zCoord,
|
||||
forSecondAxis.get0()*point.xCoord+forSecondAxis.get1()*point.yCoord+forSecondAxis.get2()*point.zCoord,
|
||||
forThirdAxis.get0()*point.xCoord +forThirdAxis.get1()*point.yCoord +forThirdAxis.get2()*point.zCoord
|
||||
);
|
||||
forFirstAxis.get0() * point.xCoord + forFirstAxis.get1() * point.yCoord
|
||||
+ forFirstAxis.get2() * point.zCoord,
|
||||
forSecondAxis.get0() * point.xCoord + forSecondAxis.get1() * point.yCoord
|
||||
+ forSecondAxis.get2() * point.zCoord,
|
||||
forThirdAxis.get0() * point.xCoord + forThirdAxis.get1() * point.yCoord
|
||||
+ forThirdAxis.get2() * point.zCoord);
|
||||
}
|
||||
|
||||
public Vec3 inverseTranslate(Vec3 point){
|
||||
public Vec3 inverseTranslate(Vec3 point) {
|
||||
return Vec3.createVectorHelper(
|
||||
forFirstAxis.get0()*point.xCoord+forSecondAxis.get0()*point.yCoord+forThirdAxis.get0()*point.zCoord,
|
||||
forFirstAxis.get1()*point.xCoord+forSecondAxis.get1()*point.yCoord+forThirdAxis.get1()*point.zCoord,
|
||||
forFirstAxis.get2()*point.xCoord+forSecondAxis.get2()*point.yCoord+forThirdAxis.get2()*point.zCoord
|
||||
);
|
||||
}
|
||||
|
||||
public void translate(int[] point,int[] out){
|
||||
out[0]=forFirstAxis.get0()*point[0] +forFirstAxis.get1()*point[1] +forFirstAxis.get2()*point[2];
|
||||
out[1]=forSecondAxis.get0()*point[0]+forSecondAxis.get1()*point[1]+forSecondAxis.get2()*point[2];
|
||||
out[2]=forThirdAxis.get0()*point[0] +forThirdAxis.get1()*point[1] +forThirdAxis.get2()*point[2];
|
||||
forFirstAxis.get0() * point.xCoord + forSecondAxis.get0() * point.yCoord
|
||||
+ forThirdAxis.get0() * point.zCoord,
|
||||
forFirstAxis.get1() * point.xCoord + forSecondAxis.get1() * point.yCoord
|
||||
+ forThirdAxis.get1() * point.zCoord,
|
||||
forFirstAxis.get2() * point.xCoord + forSecondAxis.get2() * point.yCoord
|
||||
+ forThirdAxis.get2() * point.zCoord);
|
||||
}
|
||||
|
||||
public void inverseTranslate(int[] point,int[] out){
|
||||
out[0]=forFirstAxis.get0()*point[0]+forSecondAxis.get0()*point[1]+forThirdAxis.get0()*point[2];
|
||||
out[1]=forFirstAxis.get1()*point[0]+forSecondAxis.get1()*point[1]+forThirdAxis.get1()*point[2];
|
||||
out[2]=forFirstAxis.get2()*point[0]+forSecondAxis.get2()*point[1]+forThirdAxis.get2()*point[2];
|
||||
public void translate(int[] point, int[] out) {
|
||||
out[0] = forFirstAxis.get0() * point[0] + forFirstAxis.get1() * point[1] + forFirstAxis.get2() * point[2];
|
||||
out[1] = forSecondAxis.get0() * point[0] + forSecondAxis.get1() * point[1] + forSecondAxis.get2() * point[2];
|
||||
out[2] = forThirdAxis.get0() * point[0] + forThirdAxis.get1() * point[1] + forThirdAxis.get2() * point[2];
|
||||
}
|
||||
|
||||
public void translate(double[] point,double[] out){
|
||||
out[0]=forFirstAxis.get0()*point[0] +forFirstAxis.get1()*point[1] +forFirstAxis.get2()*point[2];
|
||||
out[1]=forSecondAxis.get0()*point[0]+forSecondAxis.get1()*point[1]+forSecondAxis.get2()*point[2];
|
||||
out[2]=forThirdAxis.get0()*point[0] +forThirdAxis.get1()*point[1] +forThirdAxis.get2()*point[2];
|
||||
public void inverseTranslate(int[] point, int[] out) {
|
||||
out[0] = forFirstAxis.get0() * point[0] + forSecondAxis.get0() * point[1] + forThirdAxis.get0() * point[2];
|
||||
out[1] = forFirstAxis.get1() * point[0] + forSecondAxis.get1() * point[1] + forThirdAxis.get1() * point[2];
|
||||
out[2] = forFirstAxis.get2() * point[0] + forSecondAxis.get2() * point[1] + forThirdAxis.get2() * point[2];
|
||||
}
|
||||
|
||||
public void inverseTranslate(double[] point,double[] out){
|
||||
out[0]=forFirstAxis.get0()*point[0]+forSecondAxis.get0()*point[1]+forThirdAxis.get0()*point[2];
|
||||
out[1]=forFirstAxis.get1()*point[0]+forSecondAxis.get1()*point[1]+forThirdAxis.get1()*point[2];
|
||||
out[2]=forFirstAxis.get2()*point[0]+forSecondAxis.get2()*point[1]+forThirdAxis.get2()*point[2];
|
||||
public void translate(double[] point, double[] out) {
|
||||
out[0] = forFirstAxis.get0() * point[0] + forFirstAxis.get1() * point[1] + forFirstAxis.get2() * point[2];
|
||||
out[1] = forSecondAxis.get0() * point[0] + forSecondAxis.get1() * point[1] + forSecondAxis.get2() * point[2];
|
||||
out[2] = forThirdAxis.get0() * point[0] + forThirdAxis.get1() * point[1] + forThirdAxis.get2() * point[2];
|
||||
}
|
||||
|
||||
public void inverseTranslate(double[] point, double[] out) {
|
||||
out[0] = forFirstAxis.get0() * point[0] + forSecondAxis.get0() * point[1] + forThirdAxis.get0() * point[2];
|
||||
out[1] = forFirstAxis.get1() * point[0] + forSecondAxis.get1() * point[1] + forThirdAxis.get1() * point[2];
|
||||
out[2] = forFirstAxis.get2() * point[0] + forSecondAxis.get2() * point[1] + forThirdAxis.get2() * point[2];
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.util.MiscUtils.getTagKeys;
|
||||
import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
|
||||
/**
|
||||
* Provide accessor methods for channel data from trigger item.
|
||||
*
|
||||
* <a href="{@docRoot}/overview-summary.html#channels">Channels section on overview</a>
|
||||
*/
|
||||
public class ChannelDataAccessor {
|
||||
|
||||
private static final String SECONDARY_HINT_TAG = "channels";
|
||||
|
||||
private ChannelDataAccessor() {}
|
||||
|
||||
/**
|
||||
* Return a trigger item that
|
||||
* <ul>
|
||||
* <li>Still carry all the subchannel in some unspecified ways</li>
|
||||
* <li>Have its master channel data replaced with specified subchannel's data</li>
|
||||
* </ul>
|
||||
*
|
||||
* If given subchannel does not exist, masterStack may or may not be returned to save performance. Code within
|
||||
* StructureLib can depend on this behavior, you cannot.
|
||||
*
|
||||
* @param masterStack trigger item. can be the return value of another
|
||||
* {@link com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor#withChannel(net.minecraft.item.ItemStack, String)}
|
||||
* call
|
||||
* @param channel subchannel to use. will use current master channel data instead if this channel does not exist
|
||||
* Note: all channel identifiers are supposed to be lower case and not empty.
|
||||
* @return a trigger item with new master channel
|
||||
*/
|
||||
public static ItemStack withChannel(ItemStack masterStack, String channel) {
|
||||
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (!masterStack.hasTagCompound() || !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|
||||
|| !masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).hasKey(channel, NBT.TAG_INT))
|
||||
return masterStack;
|
||||
ItemStack ret = new ItemStack(
|
||||
masterStack.getItem(),
|
||||
masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel),
|
||||
Items.feather.getDamage(masterStack));
|
||||
ret.setTagCompound(masterStack.stackTagCompound);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given trigger item contains any subchannel
|
||||
*
|
||||
* @param masterStack trigger stack to check
|
||||
* @return true if contains any subchannel
|
||||
*/
|
||||
public static boolean hasSubChannel(ItemStack masterStack) {
|
||||
if (masterStack == null) throw new IllegalArgumentException();
|
||||
return masterStack.hasTagCompound() && masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given trigger item contains specified subchannel
|
||||
*
|
||||
* @param masterStack trigger stack to check
|
||||
* @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
|
||||
* @return true if contains specified subchannel
|
||||
*/
|
||||
public static boolean hasSubChannel(ItemStack masterStack, String channel) {
|
||||
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
return !channel.isEmpty() && masterStack.hasTagCompound()
|
||||
&& masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|
||||
&& masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).hasKey(channel, NBT.TAG_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subchannel data from given trigger item. Will use master channel instead if not present.
|
||||
*
|
||||
* @param masterStack trigger stack to query from
|
||||
* @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
|
||||
* @return channel data
|
||||
*/
|
||||
public static int getChannelData(ItemStack masterStack, String channel) {
|
||||
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (!masterStack.hasTagCompound() || !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|
||||
|| !masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).hasKey(channel, NBT.TAG_INT))
|
||||
return masterStack.stackSize;
|
||||
return masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the subchannel data on given trigger item
|
||||
*
|
||||
* @param masterStack trigger stack to check
|
||||
* @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
|
||||
* @param data positive subchannel data
|
||||
*/
|
||||
public static void setChannelData(ItemStack masterStack, String channel, int data) {
|
||||
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
|
||||
if (data < 1) throw new IllegalArgumentException("Channel data must be positive");
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound();
|
||||
NBTTagCompound main = masterStack.stackTagCompound;
|
||||
if (!main.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)) main.setTag(SECONDARY_HINT_TAG, new NBTTagCompound());
|
||||
main.getCompoundTag(SECONDARY_HINT_TAG).setInteger(channel, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the given subchannel from given trigger item, if it exists
|
||||
*
|
||||
* @param masterStack trigger stack to unset
|
||||
* @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
|
||||
*/
|
||||
public static void unsetChannelData(ItemStack masterStack, String channel) {
|
||||
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
|
||||
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||
if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound();
|
||||
NBTTagCompound main = masterStack.stackTagCompound;
|
||||
if (!main.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)) main.setTag(SECONDARY_HINT_TAG, new NBTTagCompound());
|
||||
NBTTagCompound tag = main.getCompoundTag(SECONDARY_HINT_TAG);
|
||||
tag.removeTag(channel);
|
||||
if (tag.hasNoTags()) main.removeTag(SECONDARY_HINT_TAG);
|
||||
if (main.hasNoTags()) masterStack.stackTagCompound = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wipe all subchannel data on given trigger item
|
||||
*
|
||||
* @param masterStack trigger stack to wipe
|
||||
*/
|
||||
public static void wipeChannelData(ItemStack masterStack) {
|
||||
if (masterStack == null) throw new IllegalArgumentException();
|
||||
if (masterStack.stackTagCompound != null) {
|
||||
masterStack.stackTagCompound.removeTag(SECONDARY_HINT_TAG);
|
||||
if (masterStack.stackTagCompound.hasNoTags()) {
|
||||
masterStack.stackTagCompound = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all subchannel data on trigger stack. Does not include master channel!!
|
||||
*
|
||||
* @param masterStack trigger stack to check
|
||||
* @return A java8 stream of pairs. Key is channel identifier and value is channel data. Pairs do not support
|
||||
* mutation nor removal. Can still cause {@link java.util.ConcurrentModificationException} if underlying
|
||||
* channel data is modified while stream is working.
|
||||
*/
|
||||
public static Stream<Entry<String, Integer>> iterateChannelData(ItemStack masterStack) {
|
||||
if (!hasSubChannel(masterStack)) return Stream.empty();
|
||||
NBTTagCompound tag = masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG);
|
||||
return getTagKeys(tag).stream().map(s -> new ImmutablePair<>(s, tag.getInteger(s)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of subchannels present on given trigger item. Does not include master channel.
|
||||
*
|
||||
* @param masterStack trigger stack to query from
|
||||
* @return subchannel count
|
||||
*/
|
||||
public static int countChannelData(ItemStack masterStack) {
|
||||
if (!hasSubChannel(masterStack)) return 0;
|
||||
NBTTagCompound tag = masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG);
|
||||
return tag.func_150296_c().size();
|
||||
}
|
||||
}
|
||||
+86
-51
@@ -1,82 +1,117 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.structurelib.ConfigurationHandler;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.item.ItemDebugConstructableTrigger;
|
||||
import com.gtnewhorizon.structurelib.structure.IItemSource;
|
||||
import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
|
||||
|
||||
public class ConstructableUtility {
|
||||
private ConstructableUtility() {
|
||||
|
||||
}
|
||||
private static final WeakHashMap<EntityPlayerMP, Long> lastUse = new WeakHashMap<>();
|
||||
private static long clientSideLastUse = 0;
|
||||
|
||||
public static boolean handle(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, int aSide) {
|
||||
private ConstructableUtility() {}
|
||||
|
||||
public static boolean handle(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ,
|
||||
int aSide) {
|
||||
StructureLibAPI.startHinting(aWorld);
|
||||
boolean ret = handle0(aStack, aPlayer, aWorld, aX, aY, aZ, aSide);
|
||||
StructureLibAPI.endHinting(aWorld);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean handle0(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, int aSide) {
|
||||
private static boolean handle0(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ,
|
||||
int aSide) {
|
||||
TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ);
|
||||
if (tTileEntity == null || aPlayer instanceof FakePlayer) {
|
||||
return aPlayer instanceof EntityPlayerMP;
|
||||
}
|
||||
if (aPlayer instanceof EntityPlayerMP) {
|
||||
//struct gen
|
||||
if (aPlayer.isSneaking() && aPlayer.capabilities.isCreativeMode) {
|
||||
if (tTileEntity instanceof IConstructableProvider) {
|
||||
IConstructable constructable = ((IConstructableProvider) tTileEntity).getConstructable();
|
||||
if (constructable != null) {
|
||||
constructable.construct(aStack, false);
|
||||
}
|
||||
} else if (tTileEntity instanceof IConstructable) {
|
||||
((IConstructable) tTileEntity).construct(aStack, false);
|
||||
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
|
||||
IMultiblockInfoContainer<TileEntity> iMultiblockInfoContainer = IMultiblockInfoContainer.get(tTileEntity.getClass());
|
||||
if (tTileEntity instanceof IAlignment) {
|
||||
iMultiblockInfoContainer.construct(aStack, false, tTileEntity,
|
||||
((IAlignment) tTileEntity).getExtendedFacing());
|
||||
} else {
|
||||
iMultiblockInfoContainer.construct(aStack, false, tTileEntity,
|
||||
ExtendedFacing.of(ForgeDirection.getOrientation(aSide)));
|
||||
}
|
||||
// not sneaking - client side will generate hologram. nothing to do on server side
|
||||
if (!aPlayer.isSneaking()) return true;
|
||||
|
||||
long timePast = System.currentTimeMillis() - getLastUseMilis(aPlayer);
|
||||
if (timePast < ConfigurationHandler.INSTANCE.getAutoPlaceInterval()) {
|
||||
aPlayer.addChatComponentMessage(
|
||||
new ChatComponentTranslation(
|
||||
"item.structurelib.constructableTrigger.too_fast",
|
||||
ConfigurationHandler.INSTANCE.getAutoPlaceInterval() - timePast));
|
||||
return true;
|
||||
}
|
||||
} else if (!StructureLib.isCurrentPlayer(aPlayer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IConstructable constructable = null;
|
||||
if (tTileEntity instanceof IConstructableProvider)
|
||||
constructable = ((IConstructableProvider) tTileEntity).getConstructable();
|
||||
else if (tTileEntity instanceof IConstructable) {
|
||||
constructable = (IConstructable) tTileEntity;
|
||||
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
|
||||
ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing()
|
||||
: ExtendedFacing.of(ForgeDirection.getOrientation(aSide));
|
||||
constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass())
|
||||
.toConstructable(tTileEntity, facing);
|
||||
}
|
||||
|
||||
if (constructable == null) return false;
|
||||
|
||||
if (aPlayer instanceof EntityPlayerMP playerMP) {
|
||||
// server side and sneaking (already checked above)
|
||||
// do construct
|
||||
if (aPlayer.capabilities.isCreativeMode || aStack.getItem() instanceof ItemDebugConstructableTrigger) {
|
||||
constructable.construct(aStack, false);
|
||||
} else if (constructable instanceof ISurvivalConstructable) {
|
||||
int built = ((ISurvivalConstructable) constructable).survivalConstruct(
|
||||
aStack,
|
||||
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
|
||||
ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
|
||||
if (built > 0) {
|
||||
playerMP.addChatMessage(new ChatComponentTranslation("structurelib.autoplace.built_stat", built));
|
||||
} else if (built == -1) {
|
||||
playerMP.addChatMessage(new ChatComponentTranslation("structurelib.autoplace.complete"));
|
||||
}
|
||||
setLastUseMilis(aPlayer);
|
||||
} else {
|
||||
playerMP.addChatMessage(new ChatComponentTranslation("structurelib.autoplace.error.not_enabled"));
|
||||
}
|
||||
return true;
|
||||
} else if (StructureLib.isCurrentPlayer(aPlayer)) {//particles and text client side
|
||||
//if ((!aPlayer.isSneaking() || !aPlayer.capabilities.isCreativeMode)) {
|
||||
if (tTileEntity instanceof IConstructableProvider) {
|
||||
IConstructable constructable = ((IConstructableProvider) tTileEntity).getConstructable();
|
||||
if (constructable != null) {
|
||||
constructable.construct(aStack, true);
|
||||
StructureLib.addClientSideChatMessages(constructable.getStructureDescription(aStack));
|
||||
}
|
||||
} else if (tTileEntity instanceof IConstructable) {
|
||||
IConstructable constructable = (IConstructable) tTileEntity;
|
||||
constructable.construct(aStack, true);
|
||||
StructureLib.addClientSideChatMessages(constructable.getStructureDescription(aStack));
|
||||
return false;
|
||||
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
|
||||
IMultiblockInfoContainer<TileEntity> iMultiblockInfoContainer = IMultiblockInfoContainer.get(tTileEntity.getClass());
|
||||
if (tTileEntity instanceof IAlignment) {
|
||||
iMultiblockInfoContainer.construct(aStack, true, tTileEntity,
|
||||
((IAlignment) tTileEntity).getExtendedFacing());
|
||||
} else {
|
||||
iMultiblockInfoContainer.construct(aStack, true, tTileEntity,
|
||||
ExtendedFacing.of(ForgeDirection.getOrientation(aSide)));
|
||||
}
|
||||
StructureLib.addClientSideChatMessages(IMultiblockInfoContainer.get(tTileEntity.getClass()).getDescription(aStack));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// client side
|
||||
// particles and text
|
||||
constructable.construct(aStack, true);
|
||||
if (System.currentTimeMillis() - getLastUseMilis(aPlayer) >= 300)
|
||||
StructureLib.addClientSideChatMessages(constructable.getStructureDescription(aStack));
|
||||
setLastUseMilis(aPlayer);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void setLastUseMilis(EntityPlayer aPlayer) {
|
||||
if (!(aPlayer instanceof EntityPlayerMP))
|
||||
// assume client side
|
||||
clientSideLastUse = System.currentTimeMillis();
|
||||
else lastUse.put((EntityPlayerMP) aPlayer, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private static long getLastUseMilis(EntityPlayer aPlayer) {
|
||||
if (!(aPlayer instanceof EntityPlayerMP))
|
||||
// assume client side
|
||||
return clientSideLastUse;
|
||||
return lastUse.getOrDefault(aPlayer, 0L);
|
||||
}
|
||||
}
|
||||
+12
-2
@@ -1,16 +1,26 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Created by Tec on 24.03.2017.
|
||||
*/
|
||||
public interface IConstructable {
|
||||
|
||||
void construct(ItemStack stackSize, boolean hintsOnly);
|
||||
|
||||
/**
|
||||
* Get the structure definition used for this constructable. Can be null if this constructable is not backed by one.
|
||||
*/
|
||||
default IStructureDefinition<?> getStructureDefinition() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
String[] getStructureDescription(ItemStack stackSize);
|
||||
}
|
||||
|
||||
+6
-5
@@ -6,9 +6,10 @@ import javax.annotation.Nullable;
|
||||
* Implement this interface if this tile entity MIGHT be constructable
|
||||
*/
|
||||
public interface IConstructableProvider {
|
||||
/**
|
||||
* @return null if not constructable, an instance otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
IConstructable getConstructable();
|
||||
|
||||
/**
|
||||
* @return null if not constructable, an instance otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
IConstructable getConstructable();
|
||||
}
|
||||
+53
-4
@@ -1,17 +1,25 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import java.util.HashMap;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
|
||||
import com.gtnewhorizon.structurelib.structure.IItemSource;
|
||||
import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* To implement IConstructable on not own TileEntities
|
||||
*/
|
||||
public interface IMultiblockInfoContainer<T> {
|
||||
|
||||
HashMap<String, IMultiblockInfoContainer<?>> MULTIBLOCK_MAP = new HashMap<>();
|
||||
|
||||
/**
|
||||
@@ -33,6 +41,47 @@ public interface IMultiblockInfoContainer<T> {
|
||||
|
||||
void construct(ItemStack stackSize, boolean hintsOnly, T tileEntity, ExtendedFacing aSide);
|
||||
|
||||
/**
|
||||
* Construct the structure using
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
|
||||
*
|
||||
* @return -1 if done, a helping pointer
|
||||
*/
|
||||
int survivalConstruct(ItemStack stackSize, int elementBudge, ISurvivalBuildEnvironment env, T tileEntity,
|
||||
ExtendedFacing aSide);
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
String[] getDescription(ItemStack stackSize);
|
||||
|
||||
default ISurvivalConstructable toConstructable(T tileEntity, ExtendedFacing aSide) {
|
||||
return new ISurvivalConstructable() {
|
||||
|
||||
@Override
|
||||
public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
|
||||
return IMultiblockInfoContainer.this
|
||||
.survivalConstruct(stackSize, elementBudget, env, tileEntity, aSide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source,
|
||||
EntityPlayerMP actor) {
|
||||
return IMultiblockInfoContainer.this.survivalConstruct(
|
||||
stackSize,
|
||||
elementBudget,
|
||||
ISurvivalBuildEnvironment.create(source, actor),
|
||||
tileEntity,
|
||||
aSide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void construct(ItemStack stackSize, boolean hintsOnly) {
|
||||
IMultiblockInfoContainer.this.construct(stackSize, hintsOnly, tileEntity, aSide);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getStructureDescription(ItemStack stackSize) {
|
||||
return IMultiblockInfoContainer.this.getDescription(stackSize);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
|
||||
import com.gtnewhorizon.structurelib.structure.IItemSource;
|
||||
import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
|
||||
|
||||
/**
|
||||
* An extension to {@link IConstructable}. Tile Entities (or delegates returned by
|
||||
* {@link IConstructableProvider#getConstructable()} should implement this interface to allow third party tools and
|
||||
* StructureLib builtin constructable tools to trigger autoplace in survival mode.
|
||||
* <p>
|
||||
* While not originally planned, a creative player can also use this interface to trigger autoplace by using an
|
||||
* {@link IItemSource} with every possible items in the game.
|
||||
*/
|
||||
public interface ISurvivalConstructable extends IConstructable {
|
||||
|
||||
/**
|
||||
* End player might want to disable this hack if it turns out to be too crashy
|
||||
*/
|
||||
boolean DISABLE_HACKY_MIGRATION_CODE = Boolean.getBoolean("structurelib.disable_isc_migration_hack");
|
||||
|
||||
/**
|
||||
* Construct the structure using
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, java.util.function.Consumer)}
|
||||
* or
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, IItemSource, EntityPlayerMP, boolean)}
|
||||
*
|
||||
* @param elementBudget The server configured element budget. The implementor can choose to tune this up a bit if
|
||||
* the structure is too big, but generally should not be a 4 digits number to not overwhelm the
|
||||
* server
|
||||
* @return -1 if done, otherwise number of elements placed this round
|
||||
*/
|
||||
@Deprecated
|
||||
default int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source, EntityPlayerMP actor) {
|
||||
return survivalConstruct(stackSize, elementBudget, ISurvivalBuildEnvironment.create(source, actor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the structure using
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
|
||||
* or
|
||||
* {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, ISurvivalBuildEnvironment, boolean)}
|
||||
*
|
||||
* @param elementBudget The server configured element budget. The implementor can choose to tune this up a bit if
|
||||
* the structure is too big, but generally should not be a 4 digits number to not overwhelm the
|
||||
* server
|
||||
* @return -1 if done, -2 if not supported (e.g. due to an incompatible change in the API of env, otherwise number
|
||||
* of elements placed this round
|
||||
*/
|
||||
default int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
|
||||
EntityPlayer actor = env.getActor();
|
||||
if (actor instanceof EntityPlayerMP)
|
||||
return survivalConstruct(stackSize, elementBudget, env.getSource(), (EntityPlayerMP) actor);
|
||||
else if (!DISABLE_HACKY_MIGRATION_CODE && __get_player() == null) {
|
||||
// as far as I know, no implementor would actually do anything to actor beyond passing it down to
|
||||
// IStructureDefinition, so it's probably fine to proceed like this
|
||||
GlobalStates.entityPlayer.set(env.getActor());
|
||||
try {
|
||||
return survivalConstruct(stackSize, elementBudget, env.getSource(), null);
|
||||
} finally {
|
||||
GlobalStates.entityPlayer.remove();
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal hack. Do not use.
|
||||
*/
|
||||
@Deprecated
|
||||
static EntityPlayer __get_player() {
|
||||
return GlobalStates.entityPlayer.get();
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalStates {
|
||||
|
||||
public static final ThreadLocal<EntityPlayer> entityPlayer = new ThreadLocal<>();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package contains interfaces and utility methods for autobuilding a structure.
|
||||
*/
|
||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||
@@ -1,9 +1,16 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
||||
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public enum Direction {
|
||||
|
||||
DOWN(ForgeDirection.DOWN),
|
||||
UP(ForgeDirection.UP),
|
||||
NORTH(ForgeDirection.NORTH),
|
||||
@@ -13,11 +20,13 @@ public enum Direction {
|
||||
|
||||
private final ForgeDirection forgeDirection;
|
||||
private final Vec3Impl axisVector;
|
||||
public static final Direction[] VALUES=values();
|
||||
public static final Direction[] VALUES = values();
|
||||
private static final Map<Vec3Impl, Direction> reverseMap = Arrays.stream(VALUES)
|
||||
.collect(Collectors.toMap(d -> d.axisVector, Function.identity()));
|
||||
|
||||
Direction(ForgeDirection forgeDirection) {
|
||||
this.forgeDirection = forgeDirection;
|
||||
axisVector=new Vec3Impl(forgeDirection.offsetX,forgeDirection.offsetY,forgeDirection.offsetZ);
|
||||
axisVector = Vec3Impl.getFromPool(forgeDirection.offsetX, forgeDirection.offsetY, forgeDirection.offsetZ);
|
||||
}
|
||||
|
||||
public ForgeDirection getForgeDirection() {
|
||||
@@ -28,7 +37,11 @@ public enum Direction {
|
||||
return axisVector;
|
||||
}
|
||||
|
||||
public static Vec3Impl getAxisVector(ForgeDirection forgeDirection){
|
||||
public static Vec3Impl getAxisVector(ForgeDirection forgeDirection) {
|
||||
return VALUES[forgeDirection.ordinal()].axisVector;
|
||||
}
|
||||
|
||||
public static Direction getByAxisVector(Vec3Impl vec3) {
|
||||
return reverseMap.get(vec3);
|
||||
}
|
||||
}
|
||||
+171
-98
@@ -1,21 +1,33 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
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.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;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||
|
||||
public enum ExtendedFacing {
|
||||
|
||||
DOWN_NORMAL_NONE("down normal none"),
|
||||
DOWN_NORMAL_HORIZONTAL("down normal horizontal"),
|
||||
DOWN_NORMAL_VERTICAL("down normal vertical"),
|
||||
@@ -113,13 +125,14 @@ public enum ExtendedFacing {
|
||||
EAST_COUNTER_CLOCKWISE_VERTICAL("east counter clockwise vertical"),
|
||||
EAST_COUNTER_CLOCKWISE_BOTH("east counter clockwise both");
|
||||
|
||||
public static final ExtendedFacing DEFAULT=NORTH_NORMAL_NONE;
|
||||
public static final ExtendedFacing DEFAULT = NORTH_NORMAL_NONE;
|
||||
public static final ExtendedFacing[] VALUES = values();
|
||||
public static final Map<ForgeDirection, List<ExtendedFacing>> FOR_FACING=new HashMap<>();
|
||||
public static final Map<ForgeDirection, List<ExtendedFacing>> FOR_FACING = new HashMap<>();
|
||||
public static final int STATES_COUNT = VALUES.length;
|
||||
|
||||
static {
|
||||
stream(values()).forEach(extendedFacing ->
|
||||
FOR_FACING.compute(extendedFacing.direction, ((forgeDirection, extendedFacings) -> {
|
||||
stream(values()).forEach(
|
||||
extendedFacing -> FOR_FACING.compute(extendedFacing.direction, ((forgeDirection, extendedFacings) -> {
|
||||
if (extendedFacings == null) {
|
||||
extendedFacings = new ArrayList<>();
|
||||
}
|
||||
@@ -128,10 +141,24 @@ public enum ExtendedFacing {
|
||||
})));
|
||||
}
|
||||
|
||||
private static final Map<String, ExtendedFacing> NAME_LOOKUP = stream(VALUES).collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing));
|
||||
private static final EnumMap<ForgeDirection, ImmutableSet<ExtendedFacing>> LOOKUP_BY_DIRECTION = stream(VALUES).collect(groupingBy(ExtendedFacing::getDirection, () -> new EnumMap<>(ForgeDirection.class), collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
private static final EnumMap<Rotation, ImmutableSet<ExtendedFacing>> LOOKUP_BY_ROTATION = stream(VALUES).collect(groupingBy(ExtendedFacing::getRotation, () -> new EnumMap<>(Rotation.class), collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
private static final EnumMap<Flip, ImmutableSet<ExtendedFacing>> LOOKUP_BY_FLIP = stream(VALUES).collect(groupingBy(ExtendedFacing::getFlip, () -> new EnumMap<>(Flip.class), collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
private static final Map<String, ExtendedFacing> NAME_LOOKUP = stream(VALUES)
|
||||
.collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing));
|
||||
private static final EnumMap<ForgeDirection, ImmutableSet<ExtendedFacing>> LOOKUP_BY_DIRECTION = stream(VALUES)
|
||||
.collect(
|
||||
groupingBy(
|
||||
ExtendedFacing::getDirection,
|
||||
() -> new EnumMap<>(ForgeDirection.class),
|
||||
collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
private static final EnumMap<Rotation, ImmutableSet<ExtendedFacing>> LOOKUP_BY_ROTATION = stream(VALUES).collect(
|
||||
groupingBy(
|
||||
ExtendedFacing::getRotation,
|
||||
() -> new EnumMap<>(Rotation.class),
|
||||
collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
private static final EnumMap<Flip, ImmutableSet<ExtendedFacing>> LOOKUP_BY_FLIP = stream(VALUES).collect(
|
||||
groupingBy(
|
||||
ExtendedFacing::getFlip,
|
||||
() -> new EnumMap<>(Flip.class),
|
||||
collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
||||
private final ForgeDirection direction;
|
||||
private final ForgeDirection a, b, c;
|
||||
private final Rotation rotation;
|
||||
@@ -142,133 +169,146 @@ public enum ExtendedFacing {
|
||||
|
||||
ExtendedFacing(String name) {
|
||||
this.name = name;
|
||||
direction= Direction.VALUES[ordinal()/(ROTATIONS_COUNT*FLIPS_COUNT)].getForgeDirection();
|
||||
rotation=Rotation.VALUES[ordinal()/FLIPS_COUNT-direction.ordinal()*ROTATIONS_COUNT];
|
||||
flip=Flip.VALUES[ordinal()%FLIPS_COUNT];
|
||||
ForgeDirection a,b,c;
|
||||
switch (direction){
|
||||
direction = Direction.VALUES[ordinal() / (ROTATIONS_COUNT * FLIPS_COUNT)].getForgeDirection();
|
||||
rotation = Rotation.VALUES[ordinal() / FLIPS_COUNT - direction.ordinal() * ROTATIONS_COUNT];
|
||||
flip = Flip.VALUES[ordinal() % FLIPS_COUNT];
|
||||
ForgeDirection a, b, c;
|
||||
switch (direction) {
|
||||
case DOWN:
|
||||
a= ForgeDirection.WEST;
|
||||
b= ForgeDirection.SOUTH;
|
||||
c= ForgeDirection.UP;
|
||||
a = ForgeDirection.WEST;
|
||||
b = ForgeDirection.SOUTH;
|
||||
c = ForgeDirection.UP;
|
||||
break;
|
||||
case UP:
|
||||
a= ForgeDirection.EAST;
|
||||
b= ForgeDirection.SOUTH;
|
||||
c= ForgeDirection.DOWN;
|
||||
a = ForgeDirection.EAST;
|
||||
b = ForgeDirection.SOUTH;
|
||||
c = ForgeDirection.DOWN;
|
||||
break;
|
||||
case NORTH:
|
||||
a= ForgeDirection.WEST;
|
||||
b= ForgeDirection.DOWN;
|
||||
c= ForgeDirection.SOUTH;
|
||||
a = ForgeDirection.WEST;
|
||||
b = ForgeDirection.DOWN;
|
||||
c = ForgeDirection.SOUTH;
|
||||
break;
|
||||
case SOUTH:
|
||||
a= ForgeDirection.EAST;
|
||||
b= ForgeDirection.DOWN;
|
||||
c= ForgeDirection.NORTH;
|
||||
a = ForgeDirection.EAST;
|
||||
b = ForgeDirection.DOWN;
|
||||
c = ForgeDirection.NORTH;
|
||||
break;
|
||||
case WEST:
|
||||
a= ForgeDirection.SOUTH;
|
||||
b= ForgeDirection.DOWN;
|
||||
c= ForgeDirection.EAST;
|
||||
a = ForgeDirection.SOUTH;
|
||||
b = ForgeDirection.DOWN;
|
||||
c = ForgeDirection.EAST;
|
||||
break;
|
||||
case EAST:
|
||||
a= ForgeDirection.NORTH;
|
||||
b= ForgeDirection.DOWN;
|
||||
c= ForgeDirection.WEST;
|
||||
a = ForgeDirection.NORTH;
|
||||
b = ForgeDirection.DOWN;
|
||||
c = ForgeDirection.WEST;
|
||||
break;
|
||||
default:throw new RuntimeException("Is impossible...");
|
||||
default:
|
||||
throw new RuntimeException("Is impossible...");
|
||||
}
|
||||
switch (flip){//This duplicates some axis swaps since flip boolean would do, but seems more convenient to use
|
||||
switch (flip) { // This duplicates some axis swaps since flip boolean would do, but seems more convenient to use
|
||||
case HORIZONTAL:
|
||||
a=a.getOpposite();
|
||||
a = a.getOpposite();
|
||||
break;
|
||||
case BOTH:
|
||||
a=a.getOpposite();
|
||||
a = a.getOpposite();
|
||||
case VERTICAL:
|
||||
b=b.getOpposite();
|
||||
b = b.getOpposite();
|
||||
break;
|
||||
case NONE: break;
|
||||
default:throw new RuntimeException("Even more impossible...");
|
||||
case NONE:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Even more impossible...");
|
||||
}
|
||||
switch (rotation) {
|
||||
case CLOCKWISE: {
|
||||
ForgeDirection _a=a;
|
||||
a =b;
|
||||
b =_a.getOpposite();
|
||||
ForgeDirection _a = a;
|
||||
a = b;
|
||||
b = _a.getOpposite();
|
||||
break;
|
||||
}
|
||||
case UPSIDE_DOWN:
|
||||
a=a.getOpposite();
|
||||
b=b.getOpposite();
|
||||
a = a.getOpposite();
|
||||
b = b.getOpposite();
|
||||
break;
|
||||
case COUNTER_CLOCKWISE: {
|
||||
ForgeDirection _a=a;
|
||||
a =b.getOpposite();
|
||||
b =_a;
|
||||
ForgeDirection _a = a;
|
||||
a = b.getOpposite();
|
||||
b = _a;
|
||||
break;
|
||||
}
|
||||
case NORMAL: break;
|
||||
case NORMAL:
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("More impossible...");
|
||||
}
|
||||
this.a=a;
|
||||
this.b=b;
|
||||
this.c=c;
|
||||
integerAxisSwap =new IntegerAxisSwap(a,b,c);
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
integerAxisSwap = new IntegerAxisSwap(a, b, c);
|
||||
}
|
||||
|
||||
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip){
|
||||
if(direction==ForgeDirection.UNKNOWN){
|
||||
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||
if (direction == ForgeDirection.UNKNOWN) {
|
||||
return VALUES[IAlignment.getAlignmentIndex(ForgeDirection.NORTH, rotation, flip)];
|
||||
}
|
||||
return VALUES[IAlignment.getAlignmentIndex(direction, rotation, flip)];
|
||||
}
|
||||
|
||||
public static ExtendedFacing of(ForgeDirection direction){
|
||||
if(direction==ForgeDirection.UNKNOWN){
|
||||
public static ExtendedFacing of(ForgeDirection direction) {
|
||||
if (direction == ForgeDirection.UNKNOWN) {
|
||||
return DEFAULT;
|
||||
}
|
||||
return VALUES[IAlignment.getAlignmentIndex(direction, Rotation.NORMAL, Flip.NONE)];
|
||||
}
|
||||
|
||||
public static ImmutableSet<ExtendedFacing> getAllWith(ForgeDirection direction) { return LOOKUP_BY_DIRECTION.get(direction);}
|
||||
public static ImmutableSet<ExtendedFacing> getAllWith(Rotation rotation) { return LOOKUP_BY_ROTATION.get(rotation);}
|
||||
public static ImmutableSet<ExtendedFacing> getAllWith(Flip flip) { return LOOKUP_BY_FLIP.get(flip);}
|
||||
|
||||
public ExtendedFacing with(ForgeDirection direction){
|
||||
return of(direction,rotation,flip);
|
||||
public static ImmutableSet<ExtendedFacing> getAllWith(ForgeDirection direction) {
|
||||
return LOOKUP_BY_DIRECTION.get(direction);
|
||||
}
|
||||
|
||||
public ExtendedFacing with(Rotation rotation){
|
||||
return of(direction,rotation,flip);
|
||||
public static ImmutableSet<ExtendedFacing> getAllWith(Rotation rotation) {
|
||||
return LOOKUP_BY_ROTATION.get(rotation);
|
||||
}
|
||||
|
||||
public ExtendedFacing with(Flip flip){
|
||||
return of(direction,rotation,flip);
|
||||
public static ImmutableSet<ExtendedFacing> getAllWith(Flip flip) {
|
||||
return LOOKUP_BY_FLIP.get(flip);
|
||||
}
|
||||
|
||||
public ExtendedFacing with(ForgeDirection direction) {
|
||||
return of(direction, rotation, flip);
|
||||
}
|
||||
|
||||
public ExtendedFacing with(Rotation rotation) {
|
||||
return of(direction, rotation, flip);
|
||||
}
|
||||
|
||||
public ExtendedFacing with(Flip flip) {
|
||||
return of(direction, rotation, flip);
|
||||
}
|
||||
|
||||
public ExtendedFacing getOppositeDirection() {
|
||||
return of(direction.getOpposite(),rotation,flip);
|
||||
return of(direction.getOpposite(), rotation, flip);
|
||||
}
|
||||
|
||||
public ExtendedFacing getOppositeRotation() {
|
||||
return of(direction,rotation.getOpposite(),flip);
|
||||
return of(direction, rotation.getOpposite(), flip);
|
||||
}
|
||||
|
||||
public ExtendedFacing getOppositeFlip() {
|
||||
return of(direction,rotation,flip.getOpposite());
|
||||
return of(direction, rotation, flip.getOpposite());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the same effective facing achieved by different rot/flip combo
|
||||
*
|
||||
* @return same effective facing, but different enum value
|
||||
*/
|
||||
public ExtendedFacing getDuplicate(){
|
||||
return of(direction,rotation.getOpposite(),flip.getOpposite());
|
||||
public ExtendedFacing getDuplicate() {
|
||||
return of(direction, rotation.getOpposite(), flip.getOpposite());
|
||||
}
|
||||
|
||||
public int getIndex(){
|
||||
public int getIndex() {
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
@@ -276,6 +316,10 @@ public enum ExtendedFacing {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getLocalizedName() {
|
||||
return StatCollector.translateToLocal("structurelib.facing." + getIndex());
|
||||
}
|
||||
|
||||
public static ExtendedFacing byName(String name) {
|
||||
return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
@@ -301,40 +345,69 @@ public enum ExtendedFacing {
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates relative to front facing offset to world offset
|
||||
* @param abcOffset A,B,C offset (facing relative L-->R,U-->D,F-->B)
|
||||
* @return X,Y,Z offset in world
|
||||
* Translates relative to front facing offset to world offset
|
||||
*
|
||||
* @param abcOffset A,B,C offset (facing relative {@code L-->R,U-->D,F-->B})
|
||||
* @return X, Y, Z offset in world
|
||||
*/
|
||||
public Vec3 getWorldOffset(Vec3 abcOffset) {
|
||||
return integerAxisSwap.inverseTranslate(abcOffset);
|
||||
}
|
||||
|
||||
public Vec3Impl getWorldOffset(Vec3Impl abcOffset) {
|
||||
return integerAxisSwap.inverseTranslate(abcOffset);
|
||||
}
|
||||
public void getWorldOffset(int[] point,int[] out){
|
||||
integerAxisSwap.inverseTranslate(point,out);
|
||||
}
|
||||
public void getWorldOffset(double[] point,double[] out){
|
||||
integerAxisSwap.inverseTranslate(point,out);
|
||||
|
||||
public void getWorldOffset(int[] point, int[] out) {
|
||||
integerAxisSwap.inverseTranslate(point, out);
|
||||
}
|
||||
|
||||
public void getWorldOffset(double[] point, double[] out) {
|
||||
integerAxisSwap.inverseTranslate(point, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates world offset to relative front facing offset
|
||||
* @param xyzOffset X,Y,Z offset in world
|
||||
* @return A,B,C offset (facing relative L-->R,U-->D,F-->B)
|
||||
* do note the ABC coordinate system is (X, -Y, Z).
|
||||
*/
|
||||
public Vec3 getOffsetABC(Vec3 xyzOffset){
|
||||
public ForgeDirection getWorldDirection(ForgeDirection abcDirection) {
|
||||
switch (abcDirection) {
|
||||
case NORTH:
|
||||
return getRelativeForwardInWorld();
|
||||
case SOUTH:
|
||||
return getRelativeBackInWorld();
|
||||
case UP:
|
||||
return getRelativeDownInWorld();
|
||||
case DOWN:
|
||||
return getRelativeUpInWorld();
|
||||
case EAST:
|
||||
return getRelativeLeftInWorld();
|
||||
case WEST:
|
||||
return getRelativeRightInWorld();
|
||||
default:
|
||||
return ForgeDirection.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates world offset to relative front facing offset
|
||||
*
|
||||
* @param xyzOffset X,Y,Z offset in world
|
||||
* @return A, B, C offset (facing relative {@code L-->R,U-->D,F-->B})
|
||||
*/
|
||||
public Vec3 getOffsetABC(Vec3 xyzOffset) {
|
||||
return integerAxisSwap.translate(xyzOffset);
|
||||
}
|
||||
public Vec3Impl getOffsetABC(Vec3Impl xyzOffset){
|
||||
|
||||
public Vec3Impl getOffsetABC(Vec3Impl xyzOffset) {
|
||||
return integerAxisSwap.translate(xyzOffset);
|
||||
}
|
||||
public void getOffsetABC(int[] point,int[] out){
|
||||
integerAxisSwap.translate(point,out);
|
||||
|
||||
public void getOffsetABC(int[] point, int[] out) {
|
||||
integerAxisSwap.translate(point, out);
|
||||
}
|
||||
public void getOffsetABC(double[] point,double[] out){
|
||||
integerAxisSwap.translate(point,out);
|
||||
|
||||
public void getOffsetABC(double[] point, double[] out) {
|
||||
integerAxisSwap.translate(point, out);
|
||||
}
|
||||
|
||||
public IntegerAxisSwap getIntegerAxisSwap() {
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.lang.Math.abs;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public enum Flip {
|
||||
|
||||
NONE(3, "none"),
|
||||
HORIZONTAL(2, "horizontal"),
|
||||
VERTICAL(1, "vertical"),
|
||||
@@ -26,7 +28,7 @@ public enum Flip {
|
||||
this.name = nameIn;
|
||||
}
|
||||
|
||||
public int getIndex(){
|
||||
public int getIndex() {
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
@@ -58,19 +60,19 @@ public enum Flip {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean isNotFlipped(){
|
||||
return this==NONE;
|
||||
public boolean isNotFlipped() {
|
||||
return this == NONE;
|
||||
}
|
||||
|
||||
public boolean isBothFlipped(){
|
||||
return this==BOTH;
|
||||
public boolean isBothFlipped() {
|
||||
return this == BOTH;
|
||||
}
|
||||
|
||||
public boolean isHorizontallyFlipped() {
|
||||
return this==HORIZONTAL || isBothFlipped();
|
||||
return this == HORIZONTAL || isBothFlipped();
|
||||
}
|
||||
|
||||
public boolean isVerticallyFliped() {
|
||||
return this==VERTICAL || isBothFlipped();
|
||||
return this == VERTICAL || isBothFlipped();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.lang.Math.abs;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public enum Rotation {
|
||||
|
||||
NORMAL(2, "normal"),
|
||||
CLOCKWISE(3, "clockwise"),
|
||||
UPSIDE_DOWN(0, "upside down"),
|
||||
@@ -19,14 +21,15 @@ public enum Rotation {
|
||||
private final String name;
|
||||
|
||||
public static final Rotation[] VALUES = values();
|
||||
private static final Map<String, Rotation> NAME_LOOKUP = stream(VALUES).collect(toMap(Rotation::getName2, (rotation) -> rotation));
|
||||
private static final Map<String, Rotation> NAME_LOOKUP = stream(VALUES)
|
||||
.collect(toMap(Rotation::getName2, (rotation) -> rotation));
|
||||
|
||||
Rotation(int oppositeIn, String nameIn) {
|
||||
this.opposite = oppositeIn;
|
||||
this.name = nameIn;
|
||||
}
|
||||
|
||||
public int getIndex(){
|
||||
public int getIndex() {
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
@@ -58,19 +61,19 @@ public enum Rotation {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean isNotRotated(){
|
||||
return this==NORMAL;
|
||||
public boolean isNotRotated() {
|
||||
return this == NORMAL;
|
||||
}
|
||||
|
||||
public boolean isClockwise(){
|
||||
return this==CLOCKWISE;
|
||||
public boolean isClockwise() {
|
||||
return this == CLOCKWISE;
|
||||
}
|
||||
|
||||
public boolean isCounterClockwise(){
|
||||
return this==COUNTER_CLOCKWISE;
|
||||
public boolean isCounterClockwise() {
|
||||
return this == COUNTER_CLOCKWISE;
|
||||
}
|
||||
|
||||
public boolean isUpsideDown(){
|
||||
return this==UPSIDE_DOWN;
|
||||
public boolean isUpsideDown() {
|
||||
return this == UPSIDE_DOWN;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
/**
|
||||
* All public stuff in this package and its subpackages are considered part of the public API.
|
||||
* Backwards compatibility is maintained to the maximum extend possible.
|
||||
* This package provide a framework for rotating a given block, or a multiblock structure.
|
||||
* <p>
|
||||
* All public stuff in this package and its subpackages are considered part of the public API. Backwards compatibility
|
||||
* is maintained to the maximum extend possible.
|
||||
*/
|
||||
package com.gtnewhorizon.structurelib.alignment;
|
||||
package com.gtnewhorizon.structurelib.alignment;
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.gtnewhorizon.structurelib.block;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
@@ -13,55 +11,63 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import java.util.List;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class BlockHint extends Block {
|
||||
private static final IIcon[] hint = new IIcon[16];
|
||||
|
||||
public BlockHint() {
|
||||
super(Material.iron);
|
||||
setBlockName("structurelib.blockhint");
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
private static final IIcon[] hint = new IIcon[16];
|
||||
|
||||
@Override
|
||||
public void registerBlockIcons(IIconRegister aIconRegister) {
|
||||
//super.registerBlockIcons(aIconRegister);
|
||||
hint[0] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_0");
|
||||
hint[1] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_1");
|
||||
hint[2] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_2");
|
||||
hint[3] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_3");
|
||||
hint[4] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_4");
|
||||
hint[5] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_5");
|
||||
hint[6] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_6");
|
||||
hint[7] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_7");
|
||||
hint[8] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_8");
|
||||
hint[9] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_9");
|
||||
hint[10] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_10");
|
||||
hint[11] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_11");
|
||||
hint[12] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_DEFAULT");
|
||||
hint[13] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_AIR");
|
||||
hint[14] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_NOAIR");
|
||||
hint[15] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_ERROR");
|
||||
}
|
||||
public BlockHint() {
|
||||
super(Material.iron);
|
||||
setBlockName("structurelib.blockhint");
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(int aSide, int aMeta) {
|
||||
return hint[aMeta];
|
||||
}
|
||||
@Override
|
||||
public void registerBlockIcons(IIconRegister aIconRegister) {
|
||||
// super.registerBlockIcons(aIconRegister);
|
||||
hint[0] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_0");
|
||||
hint[1] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_1");
|
||||
hint[2] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_2");
|
||||
hint[3] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_3");
|
||||
hint[4] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_4");
|
||||
hint[5] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_5");
|
||||
hint[6] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_6");
|
||||
hint[7] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_7");
|
||||
hint[8] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_8");
|
||||
hint[9] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_9");
|
||||
hint[10] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_10");
|
||||
hint[11] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_11");
|
||||
hint[12] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_DEFAULT");
|
||||
hint[13] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_AIR");
|
||||
hint[14] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_NOAIR");
|
||||
hint[15] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_ERROR");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public IIcon getIcon(IBlockAccess aWorld, int xCoord, int yCoord, int zCoord, int aSide) {
|
||||
int tMeta = aWorld.getBlockMetadata(xCoord, yCoord, zCoord);
|
||||
return getIcon(aSide, tMeta);
|
||||
}
|
||||
@Override
|
||||
public IIcon getIcon(int aSide, int aMeta) {
|
||||
// For numbered hints, top and bottom are blank
|
||||
if (aSide <= 1 && aMeta <= 11) {
|
||||
return hint[13];
|
||||
}
|
||||
return hint[aMeta];
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void getSubBlocks(Item aItem, CreativeTabs par2CreativeTabs, List aList) {
|
||||
for (int i = 0; i <= 15; i++) {
|
||||
aList.add(new ItemStack(aItem, 1, i));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public IIcon getIcon(IBlockAccess aWorld, int xCoord, int yCoord, int zCoord, int aSide) {
|
||||
int tMeta = aWorld.getBlockMetadata(xCoord, yCoord, zCoord);
|
||||
return getIcon(aSide, tMeta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubBlocks(Item item, CreativeTabs tab, List<ItemStack> list) {
|
||||
for (int i = 0; i <= 15; i++) {
|
||||
list.add(new ItemStack(item, 1, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.gtnewhorizon.structurelib.command;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.command.CommandBase;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.command.WrongUsageException;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.ChatComponentTranslation;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
|
||||
public class CommandConfigureChannels extends CommandBase {
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "sl_channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandUsage(ICommandSender p_71518_1_) {
|
||||
return "/sl_channel <wipe/getall>\n" + "/sl_channel <get/unset> <channel name>\n"
|
||||
+ "/sl_channel <set> <channel name> <value>\n"
|
||||
+ "<channel name> can have space in it. Values below 1 clear the channel";
|
||||
}
|
||||
|
||||
@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_, "get", "set", "unset", "wipe", "getall");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processCommand(ICommandSender sender, String[] args) {
|
||||
if (args.length == 0) throw new WrongUsageException(getCommandUsage(sender));
|
||||
|
||||
if (!(sender instanceof EntityPlayerMP player)) throw new WrongUsageException("must be a player");
|
||||
|
||||
ItemStack heldItem = player.getHeldItem();
|
||||
if (heldItem == null || !(heldItem.getItem() instanceof ItemConstructableTrigger)) {
|
||||
throw new WrongUsageException("must hold a hologram");
|
||||
}
|
||||
|
||||
String channel;
|
||||
|
||||
switch (args[0]) {
|
||||
case "getall":
|
||||
if (!ChannelDataAccessor.hasSubChannel(heldItem)) {
|
||||
sender.addChatMessage(new ChatComponentText("No subchannel"));
|
||||
} else {
|
||||
sender.addChatMessage(
|
||||
new ChatComponentTranslation(
|
||||
"item.structurelib.constructableTrigger.desc.lshift.0",
|
||||
ChannelDataAccessor.countChannelData(heldItem)));
|
||||
ChannelDataAccessor.iterateChannelData(heldItem).map(e -> e.getKey() + ": " + e.getValue())
|
||||
.map(ChatComponentText::new).forEach(player::addChatMessage);
|
||||
}
|
||||
return;
|
||||
case "wipe":
|
||||
ChannelDataAccessor.wipeChannelData(heldItem);
|
||||
return;
|
||||
case "get":
|
||||
case "unset":
|
||||
if (args.length < 2) throw new WrongUsageException(getCommandUsage(sender));
|
||||
channel = args.length == 2 ? args[1] : Arrays.stream(args).skip(1).collect(Collectors.joining());
|
||||
break;
|
||||
case "set":
|
||||
if (args.length < 3) throw new WrongUsageException(getCommandUsage(sender));
|
||||
channel = args.length == 3 ? args[1]
|
||||
: Arrays.stream(args).skip(1).limit(args.length - 2).collect(Collectors.joining());
|
||||
break;
|
||||
default:
|
||||
throw new WrongUsageException(getCommandUsage(sender));
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
case "get":
|
||||
if (ChannelDataAccessor.hasSubChannel(heldItem, channel)) sender.addChatMessage(
|
||||
new ChatComponentText(
|
||||
channel + " value: " + ChannelDataAccessor.getChannelData(heldItem, channel)));
|
||||
else sender.addChatMessage(new ChatComponentText(channel + " value: N/A"));
|
||||
break;
|
||||
case "clear":
|
||||
if (ChannelDataAccessor.hasSubChannel(heldItem, channel))
|
||||
sender.addChatMessage(new ChatComponentText(channel + " no value"));
|
||||
else {
|
||||
ChannelDataAccessor.unsetChannelData(heldItem, channel);
|
||||
sender.addChatMessage(new ChatComponentText(channel + " cleared"));
|
||||
player.inventoryContainer.detectAndSendChanges();
|
||||
}
|
||||
break;
|
||||
case "set":
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(args[args.length - 1]);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new WrongUsageException("%s not a valid value", args[args.length - 1]);
|
||||
}
|
||||
|
||||
if (value < 1) {
|
||||
ChannelDataAccessor.unsetChannelData(heldItem, channel);
|
||||
} else {
|
||||
ChannelDataAccessor.setChannelData(heldItem, channel, value);
|
||||
}
|
||||
player.inventoryContainer.detectAndSendChanges();
|
||||
sender.addChatMessage(
|
||||
new ChatComponentText(value < 1 ? channel + " no value" : channel + " value: " + value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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 player)) throw new WrongUsageException("must be a player");
|
||||
|
||||
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,57 @@
|
||||
package com.gtnewhorizon.structurelib.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||
|
||||
@Override
|
||||
public String[] getASMTransformerClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMixinConfig() {
|
||||
return "mixins.structurelib.early.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||
List<String> mixins = new ArrayList<>(
|
||||
Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld"));
|
||||
if (FMLLaunchHandler.side() == Side.CLIENT) {
|
||||
mixins.add("MixinGuiContainer_PickChannel");
|
||||
}
|
||||
return mixins;
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.entity.fx;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.proxy.ClientProxy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class EntityFXBlockHint extends EntityFX {
|
||||
private IIcon[] icons;
|
||||
private short[] mRGBa = {255,255,255,0};
|
||||
|
||||
public EntityFXBlockHint(World world){
|
||||
this(world,0,0,0, Blocks.stone,0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param world
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param icons DOWN, UP, NORTH, SOUTH, WEST, EAST
|
||||
*/
|
||||
public EntityFXBlockHint(World world, int x, int y, int z, IIcon[] icons) {
|
||||
super(world, x + .25, y + .5, z + .25);
|
||||
particleGravity = 0;
|
||||
prevPosX = posX;
|
||||
prevPosY = posY;
|
||||
prevPosZ = posZ;
|
||||
noClip = true;
|
||||
particleMaxAge = 2000 + StructureLib.RANDOM.nextInt(200);
|
||||
this.icons=icons;
|
||||
}
|
||||
|
||||
public EntityFXBlockHint(World world, int x, int y, int z, Block block, int meta) {
|
||||
super(world, x+.25, y+.5, z+.25);
|
||||
particleGravity = 0;
|
||||
prevPosX = posX;
|
||||
prevPosY = posY;
|
||||
prevPosZ = posZ;
|
||||
noClip = true;
|
||||
particleMaxAge = 2000 + StructureLib.RANDOM.nextInt(200);
|
||||
icons = new IIcon[6];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
icons[i] = block.getIcon(i, meta);
|
||||
}
|
||||
}
|
||||
|
||||
public EntityFXBlockHint withColorTint(short[] coloure){
|
||||
this.mRGBa =coloure;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(Tessellator tes, float subTickTime, float p_70539_3_, float p_70539_4_, float p_70539_5_, float p_70539_6_, float p_70539_7_) {
|
||||
float size = .5f;
|
||||
float X = (float) (prevPosX + (posX - prevPosX) * (double) subTickTime - EntityFX.interpPosX);
|
||||
float Y = (float) (prevPosY + (posY - prevPosY) * (double) subTickTime - EntityFX.interpPosY) - size / 2;
|
||||
float Z = (float) (prevPosZ + (posZ - prevPosZ) * (double) subTickTime - EntityFX.interpPosZ);
|
||||
GL11.glDisable(GL11.GL_CULL_FACE);
|
||||
GL11.glDepthMask(false);
|
||||
tes.setColorRGBA((int) (mRGBa[0] * .9F), (int) (mRGBa[1] * .95F), (int) (mRGBa[2] * 1F), 192);
|
||||
|
||||
//var8, var9 - X U
|
||||
//var 10, var 11 - Y V
|
||||
for(int i=0;i<6;i++){
|
||||
if(icons[i]==null) {
|
||||
continue;
|
||||
}
|
||||
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:
|
||||
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||
tes.addVertexWithUV(X, Y, Z, u, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z, U, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||
break;
|
||||
case 1:
|
||||
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X, Y + size, Z + size, u, V);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, U, V);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z, U, v);
|
||||
break;
|
||||
case 2:
|
||||
tes.addVertexWithUV(X, Y, Z, U, V);
|
||||
tes.addVertexWithUV(X, Y + size, Z, U, v);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z, u, V);
|
||||
break;
|
||||
case 3:
|
||||
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, U, v);
|
||||
tes.addVertexWithUV(X, Y + size, Z + size, u, v);
|
||||
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||
break;
|
||||
case 4:
|
||||
tes.addVertexWithUV(X, Y, Z + size, U, V);
|
||||
tes.addVertexWithUV(X, Y + size, Z + size, U, v);
|
||||
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||
tes.addVertexWithUV(X, Y, Z, u, V);
|
||||
break;
|
||||
case 5:
|
||||
tes.addVertexWithUV(X + size, Y, Z, U, V);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z, U, v);
|
||||
tes.addVertexWithUV(X + size, Y + size, Z + size, u, v);
|
||||
tes.addVertexWithUV(X + size, Y, Z + size, u, V);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GL11.glEnable(GL11.GL_CULL_FACE);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderInPass(int pass) {
|
||||
return pass==2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDead() {
|
||||
super.setDead();
|
||||
ClientProxy.onHintDead(this);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,40 @@
|
||||
package com.gtnewhorizon.structurelib.entity.fx;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class WeightlessParticleFX extends EntityFX
|
||||
{
|
||||
public WeightlessParticleFX(World p_i1205_1_, double p_i1205_2_, double p_i1205_4_, double p_i1205_6_, double p_i1205_8_, double p_i1205_10_, double p_i1205_12_)
|
||||
{
|
||||
public class WeightlessParticleFX extends EntityFX {
|
||||
|
||||
public WeightlessParticleFX(World p_i1205_1_, double p_i1205_2_, double p_i1205_4_, double p_i1205_6_,
|
||||
double p_i1205_8_, double p_i1205_10_, double p_i1205_12_) {
|
||||
super(p_i1205_1_, p_i1205_2_, p_i1205_4_, p_i1205_6_, p_i1205_8_, p_i1205_10_, p_i1205_12_);
|
||||
this.motionX = p_i1205_8_ + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.05F);
|
||||
this.motionY = p_i1205_10_ + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.05F);
|
||||
this.motionZ = p_i1205_12_ + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.05F);
|
||||
this.motionX = p_i1205_8_ + (double) ((float) (Math.random() * 2.0D - 1.0D) * 0.05F);
|
||||
this.motionY = p_i1205_10_ + (double) ((float) (Math.random() * 2.0D - 1.0D) * 0.05F);
|
||||
this.motionZ = p_i1205_12_ + (double) ((float) (Math.random() * 2.0D - 1.0D) * 0.05F);
|
||||
this.particleRed = this.particleGreen = this.particleBlue = this.rand.nextFloat() * 0.3F + 0.7F;
|
||||
this.particleScale = this.rand.nextFloat() * this.rand.nextFloat() * 6.0F + 1.0F;
|
||||
this.particleMaxAge = (int)(16.0D / ((double)this.rand.nextFloat() * 0.8D + 0.2D)) + 2;
|
||||
this.noClip=true;
|
||||
this.particleMaxAge = (int) (16.0D / ((double) this.rand.nextFloat() * 0.8D + 0.2D)) + 2;
|
||||
this.noClip = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate()
|
||||
{
|
||||
public void onUpdate() {
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
|
||||
if (this.particleAge++ >= this.particleMaxAge)
|
||||
{
|
||||
if (this.particleAge++ >= this.particleMaxAge) {
|
||||
this.setDead();
|
||||
}
|
||||
|
||||
this.setParticleTextureIndex(7 - this.particleAge * 8 / this.particleMaxAge);
|
||||
//this.motionY += 0.004D;
|
||||
// this.motionY += 0.004D;
|
||||
this.moveEntity(this.motionX, this.motionY, this.motionZ);
|
||||
this.motionX *= 0.8999999761581421D;
|
||||
this.motionY *= 0.8999999761581421D;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Container;
|
||||
|
||||
public class EmptyContainer extends Container {
|
||||
|
||||
private final EntityPlayer player;
|
||||
|
||||
public EmptyContainer(EntityPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInteractWith(EntityPlayer player) {
|
||||
return player == this.player;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,686 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.inventory.Container;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.gtnewhorizon.structurelib.ChannelDescription;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
|
||||
|
||||
import codechicken.nei.VisiblityData;
|
||||
import codechicken.nei.api.INEIGuiHandler;
|
||||
import codechicken.nei.api.TaggedInventoryArea;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.Optional;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
|
||||
@Optional.Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = "NotEnoughItems")
|
||||
public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScreen, INEIGuiHandler {
|
||||
|
||||
private static final String I18N_PREFIX = "item.structurelib.constructableTrigger.gui.";
|
||||
|
||||
private static final int ADD_BTN = 0;
|
||||
private static final int UNSET_BTN = 1;
|
||||
private static final int WIPE_BTN = 2;
|
||||
|
||||
private static final int KEY_MAX_WIDTH = 50;
|
||||
private static final int INFO_BOX_SIZE = 11;
|
||||
private final ItemStack trigger;
|
||||
private final GuiChannelsList list;
|
||||
private final GuiAutoCompleteList autoCompleteList;
|
||||
private final Map<String, List<String>> tooltipSplitCache = new HashMap<>();
|
||||
private final TIntObjectMap<Map<String, String>> trimCache = new TIntObjectHashMap<>();
|
||||
private GuiTextField key, value;
|
||||
protected int guiTop, guiLeft;
|
||||
private List<String> info;
|
||||
private boolean wipePending = false;
|
||||
private long wipePendingTime = 0;
|
||||
private static final long WIPE_CONFIRM_TIMEOUT_MS = 3000;
|
||||
|
||||
public GuiScreenConfigureChannels(Container container, ItemStack trigger) {
|
||||
super(container);
|
||||
|
||||
this.trigger = trigger;
|
||||
list = new GuiChannelsList(152, 91, 12, 21, 14);
|
||||
list.addSelectionListener((list, selectedIndex) -> {
|
||||
Entry<String, Integer> e = list.getElementAt(selectedIndex);
|
||||
key.setText(e.getKey());
|
||||
value.setText(e.getValue().toString());
|
||||
updateButtons();
|
||||
});
|
||||
autoCompleteList = new GuiAutoCompleteList(151 + 12 - 45 - 2, 50, 45, 119 + 12, 14);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui() {
|
||||
super.initGui();
|
||||
// so you can keep holding backspace to delete a long sequence of chars
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
guiLeft = (this.width - this.getXSize()) / 2;
|
||||
guiTop = (this.height - this.getYSize()) / 2;
|
||||
|
||||
key = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 119, 151 + 12 - 45, 12) {
|
||||
|
||||
@Override
|
||||
public void writeText(String text) {
|
||||
// force lower case
|
||||
super.writeText(text.toLowerCase(Locale.ROOT));
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) {
|
||||
super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
|
||||
boolean flag = p_146192_1_ >= this.xPosition && p_146192_1_ < this.xPosition + this.width
|
||||
&& p_146192_2_ >= this.yPosition
|
||||
&& p_146192_2_ < this.yPosition + this.height;
|
||||
if (flag && p_146192_3_ == 1) {
|
||||
key.setText("");
|
||||
value.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String p_146180_1_) {
|
||||
super.setText(p_146180_1_);
|
||||
updateButtons();
|
||||
}
|
||||
};
|
||||
value = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 139, 151 + 12 - 45, 12) {
|
||||
|
||||
@Override
|
||||
public void writeText(String text) {
|
||||
// ignore write requests containing non digit characters.
|
||||
// we don't accept decimals or negative numbers, so we can safely use isDigit instead of something more
|
||||
// advanced
|
||||
// use codePoints() in case someone pasted an emoji by mistake
|
||||
if (text != null && text.codePoints().allMatch(Character::isDigit)) {
|
||||
super.writeText(text);
|
||||
updateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocused(boolean p_146195_1_) {
|
||||
if (!p_146195_1_ && isFocused() && !StringUtils.isBlank(getText())) {
|
||||
int result;
|
||||
try {
|
||||
result = Math.max(Integer.parseInt(getText()), 1);
|
||||
} catch (NumberFormatException e) {
|
||||
result = 1;
|
||||
}
|
||||
setText(String.valueOf(result));
|
||||
}
|
||||
super.setFocused(p_146195_1_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) {
|
||||
super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
|
||||
boolean flag = p_146192_1_ >= this.xPosition && p_146192_1_ < this.xPosition + this.width
|
||||
&& p_146192_2_ >= this.yPosition
|
||||
&& p_146192_2_ < this.yPosition + this.height;
|
||||
if (flag && p_146192_3_ == 1) {
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String p_146180_1_) {
|
||||
super.setText(p_146180_1_);
|
||||
updateButtons();
|
||||
}
|
||||
};
|
||||
|
||||
list.onGuiInit(this);
|
||||
autoCompleteList.onGuiInit(this);
|
||||
|
||||
addButton(new GuiButton(ADD_BTN, guiLeft + 12, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "add")));
|
||||
addButton(new GuiButton(UNSET_BTN, guiLeft + 65, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "unset")));
|
||||
addButton(new GuiButton(WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "wipe")));
|
||||
|
||||
updateButtons();
|
||||
tooltipSplitCache.clear();
|
||||
info = StreamSupport
|
||||
.stream(
|
||||
Splitter.on("\\n").split(StatCollector.translateToLocal(I18N_PREFIX + "info")).spliterator(),
|
||||
false)
|
||||
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 1 / 3).stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGuiLeft() {
|
||||
return guiLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGuiTop() {
|
||||
return guiTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getXSize() {
|
||||
return 176;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getYSize() {
|
||||
return 188;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addButton(GuiButton button) {
|
||||
getButtonList().add(button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeButton(GuiButton button) {
|
||||
getButtonList().remove(button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOverlayOffsetX() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doActionPerformed(GuiButton but) {
|
||||
actionPerformed(but);
|
||||
}
|
||||
|
||||
public List<GuiButton> getButtonList() {
|
||||
return buttonList;
|
||||
}
|
||||
|
||||
private boolean isMouseOverValue() {
|
||||
int mx = Mouse.getEventX() * this.width / this.mc.displayWidth;
|
||||
int my = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
|
||||
return mx >= value.xPosition && mx < value.xPosition + value.width
|
||||
&& my >= value.yPosition
|
||||
&& my < value.yPosition + value.height;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMouseInput() {
|
||||
int delta = Mouse.getEventDWheel();
|
||||
if (delta != 0) {
|
||||
if (isMouseOverValue()) {
|
||||
if (delta > 0) {
|
||||
value.setText(String.valueOf(getValue() + 1));
|
||||
} else {
|
||||
value.setText(String.valueOf(Math.max(getValue() - 1, 1)));
|
||||
}
|
||||
} else {
|
||||
list.handleDWheel(delta);
|
||||
}
|
||||
}
|
||||
super.handleMouseInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mX, int mY, int button) {
|
||||
super.mouseClicked(mX, mY, button);
|
||||
key.mouseClicked(mX, mY, button);
|
||||
value.mouseClicked(mX, mY, button);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void keyTyped(char aChar, int aKey) {
|
||||
GuiScrollableList<?> l = key.isFocused() ? autoCompleteList : list;
|
||||
switch (aKey) {
|
||||
case Keyboard.KEY_TAB:
|
||||
if (key.isFocused()) {
|
||||
if (autoCompleteList.getSelectedElement() != null) {
|
||||
key.setText(autoCompleteList.getSelectedElement());
|
||||
return;
|
||||
}
|
||||
|
||||
key.setFocused(false);
|
||||
value.setFocused(true);
|
||||
} else {
|
||||
key.setFocused(true);
|
||||
value.setFocused(false);
|
||||
}
|
||||
return;
|
||||
case Keyboard.KEY_RETURN:
|
||||
case Keyboard.KEY_NUMPADENTER:
|
||||
if (key.isFocused() && autoCompleteList.getSelectedElement() != null) {
|
||||
key.setText(autoCompleteList.getSelectedElement());
|
||||
key.setFocused(false);
|
||||
value.setFocused(true);
|
||||
value.setText("1");
|
||||
value.setSelectionPos(0);
|
||||
return;
|
||||
}
|
||||
GuiButton add = getButtonList().get(ADD_BTN);
|
||||
if (add.enabled) doActionPerformed(add);
|
||||
return;
|
||||
case Keyboard.KEY_UP:
|
||||
if (l.selectedIndex > 0) l.setSelection(l.selectedIndex - 1);
|
||||
return;
|
||||
case Keyboard.KEY_DOWN:
|
||||
if (l.selectedIndex < l.getNumElements() - 1) l.setSelection(l.selectedIndex + 1);
|
||||
return;
|
||||
case Keyboard.KEY_ESCAPE:
|
||||
if (key.isFocused()) {
|
||||
if (autoCompleteList.getSelectedElement() != null) {
|
||||
autoCompleteList.reset();
|
||||
} else {
|
||||
key.setFocused(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (key.textboxKeyTyped(aChar, aKey) || value.textboxKeyTyped(aChar, aKey)) {
|
||||
updateButtons();
|
||||
return;
|
||||
}
|
||||
super.keyTyped(aChar, aKey);
|
||||
}
|
||||
|
||||
private void updateButtons() {
|
||||
// this will be called from setText of key and value. NEVER UPDATE THE VALUE OF THESE HERE OR GET A
|
||||
// STACKOVERFLOW!
|
||||
String keyText = key.getText();
|
||||
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
|
||||
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
|
||||
: I18n.format(I18N_PREFIX + "add");
|
||||
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(key.getText())
|
||||
&& !StringUtils.isBlank(value.getText())
|
||||
&& Integer.parseInt(value.getText()) > 0;
|
||||
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||
|
||||
autoCompleteList.reset();
|
||||
}
|
||||
|
||||
private int getValue() {
|
||||
try {
|
||||
return Integer.parseInt(value.getText());
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton btn) {
|
||||
if (btn == null) return;
|
||||
switch (btn.id) {
|
||||
case ADD_BTN:
|
||||
int value = getValue();
|
||||
if (value <= 0) return;
|
||||
ChannelDataAccessor.setChannelData(trigger, key.getText(), value);
|
||||
break;
|
||||
case UNSET_BTN:
|
||||
ChannelDataAccessor.unsetChannelData(trigger, key.getText());
|
||||
break;
|
||||
case WIPE_BTN:
|
||||
if (!wipePending) {
|
||||
wipePending = true;
|
||||
wipePendingTime = System.currentTimeMillis();
|
||||
btn.displayString = I18n.format(I18N_PREFIX + "wipe.confirm");
|
||||
} else {
|
||||
wipePending = false;
|
||||
btn.displayString = I18n.format(I18N_PREFIX + "wipe");
|
||||
ChannelDataAccessor.wipeChannelData(trigger);
|
||||
key.setText("");
|
||||
this.value.setText("");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
|
||||
super.actionPerformed(btn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed() {
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
StructureLib.instance().proxy().uploadChannels(trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScreen() {
|
||||
super.updateScreen();
|
||||
key.updateCursorCounter();
|
||||
value.updateCursorCounter();
|
||||
if (wipePending && System.currentTimeMillis() - wipePendingTime > WIPE_CONFIRM_TIMEOUT_MS) {
|
||||
wipePending = false;
|
||||
getButtonList().get(WIPE_BTN).displayString = I18n.format(I18N_PREFIX + "wipe");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer(float partialTick, int mX, int mY) {
|
||||
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/channels.png"));
|
||||
int topLeftX = (this.width - this.getXSize()) / 2;
|
||||
int topLeftY = (this.height - this.getYSize()) / 2;
|
||||
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
|
||||
list.drawScreen(mX, mY, partialTick);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
|
||||
key.drawTextBox();
|
||||
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
|
||||
value.drawTextBox();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
|
||||
if (key.isFocused() && autoCompleteList.getNumElements() > 0) {
|
||||
autoCompleteList.drawScreen(mouseX, mouseY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
String highlight = null;
|
||||
if (key.isFocused() && autoCompleteList.getNumElements() > 0) {
|
||||
highlight = autoCompleteList.getSelectedElement();
|
||||
}
|
||||
if (highlight == null && ChannelDescription.has(key.getText())) {
|
||||
highlight = key.getText();
|
||||
}
|
||||
if (highlight != null) {
|
||||
drawChannelDescriptionTooltip(highlight);
|
||||
}
|
||||
if (mouseX > xSize + guiLeft - INFO_BOX_SIZE - 12 && mouseX < xSize + guiLeft - 12
|
||||
&& mouseY > guiTop + 7
|
||||
&& mouseY < guiTop + INFO_BOX_SIZE + 7) {
|
||||
drawHoveringText(info, mouseX, mouseY, fontRendererObj);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawChannelDescriptionTooltip(String candidate) {
|
||||
List<String> tooltip = tooltipSplitCache.computeIfAbsent(candidate, this::getChannelDescriptionTooltip);
|
||||
drawHoveringText(tooltip, guiLeft + 151 + 12, guiTop + 122, fontRendererObj);
|
||||
}
|
||||
|
||||
private List<String> getChannelDescriptionTooltip(String candidate) {
|
||||
List<String> tooltip = new ArrayList<>();
|
||||
int maxLine = width - (guiLeft + 151 + 12) - 12;
|
||||
for (Entry<String, String> e : ChannelDescription.get(candidate).getDescriptions().entrySet()) {
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(
|
||||
StatCollector.translateToLocalFormatted(
|
||||
I18N_PREFIX + "channels.from",
|
||||
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
|
||||
maxLine));
|
||||
tooltip.addAll(
|
||||
fontRendererObj.listFormattedStringToWidth(StatCollector.translateToLocal(e.getValue()), maxLine));
|
||||
}
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
public String trim(String str, int keyMaxWidth) {
|
||||
Map<String, String> cache = trimCache.get(keyMaxWidth);
|
||||
if (cache == null) {
|
||||
trimCache.put(keyMaxWidth, cache = new HashMap<>());
|
||||
}
|
||||
return cache.computeIfAbsent(str, e -> {
|
||||
String s = fontRendererObj.trimStringToWidth(e, keyMaxWidth);
|
||||
if (s.length() != e.length()) {
|
||||
StringBuilder buffer = new StringBuilder(s).deleteCharAt(s.length() - 1).append("...");
|
||||
while ((s = fontRendererObj.trimStringToWidth(buffer.toString(), keyMaxWidth)).length()
|
||||
!= buffer.length())
|
||||
// drop last original char
|
||||
buffer.deleteCharAt(s.length() - 4);
|
||||
do {
|
||||
buffer.append('.');
|
||||
} while (fontRendererObj.getStringWidth(buffer.toString()) <= keyMaxWidth);
|
||||
s = buffer.deleteCharAt(s.length() - 1).insert(s.length() - 3, EnumChatFormatting.GRAY).toString();
|
||||
}
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Optional.Method(modid = "NotEnoughItems")
|
||||
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
|
||||
if (button != 0 || mousex <= guiLeft
|
||||
|| mousey <= guiTop
|
||||
|| mousex >= guiLeft + getXSize()
|
||||
|| mousey >= guiTop + getYSize()) {
|
||||
return false;
|
||||
}
|
||||
Collection<Entry<String, Integer>> channels = ChannelDescription.iterate(draggedStack);
|
||||
switch (channels.size()) {
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
default: // TODO implement a GUI to select which pair to use, instead of blindly using the first
|
||||
Entry<String, Integer> e = channels.iterator().next();
|
||||
ChannelDataAccessor.setChannelData(trigger, e.getKey(), e.getValue());
|
||||
key.setText(e.getKey());
|
||||
value.setText(e.getValue().toString());
|
||||
draggedStack.stackSize = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private class GuiAutoCompleteList extends GuiScrollableList<String> {
|
||||
|
||||
private List<Entry<String, String>> cache; // <key, display string>
|
||||
|
||||
public GuiAutoCompleteList(int width, int height, int originX, int originY, int slotHeight) {
|
||||
super(width, height, originX, originY, slotHeight);
|
||||
margin = 2;
|
||||
}
|
||||
|
||||
private List<Entry<String, String>> getOptions() {
|
||||
if (cache == null) {
|
||||
cache = search();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private List<Entry<String, String>> search() {
|
||||
List<Entry<String, String>> cache = new ArrayList<>();
|
||||
Set<String> added = new HashSet<>();
|
||||
String term = key.getText();
|
||||
String[] subjects = ChannelDescription.getAll().keySet().toArray(new String[0]);
|
||||
Arrays.sort(subjects);
|
||||
if (StringUtils.isBlank(term)) {
|
||||
// nothing. just gives out everything
|
||||
for (String subject : subjects) {
|
||||
cache.add(new AbstractMap.SimpleImmutableEntry<>(subject, EnumChatFormatting.GRAY + subject));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
// prefix match
|
||||
for (String s : subjects) {
|
||||
if (s.startsWith(term)) {
|
||||
cache.add(
|
||||
new AbstractMap.SimpleImmutableEntry<>(
|
||||
s,
|
||||
term + EnumChatFormatting.GRAY + s.substring(term.length())));
|
||||
added.add(s);
|
||||
}
|
||||
}
|
||||
// subsequence match
|
||||
for (String subject : subjects) {
|
||||
if (!added.contains(subject) && subject.length() > term.length()) {
|
||||
int i = 0, j = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean mode = true;
|
||||
sb.append(EnumChatFormatting.WHITE);
|
||||
while (i < term.length() && j < subject.length()) {
|
||||
if (term.codePointAt(i) == subject.codePointAt(j)) {
|
||||
if (!mode) {
|
||||
sb.append(EnumChatFormatting.WHITE);
|
||||
mode = true;
|
||||
}
|
||||
sb.appendCodePoint(term.codePointAt(i));
|
||||
i++;
|
||||
j++;
|
||||
} else {
|
||||
if (mode) {
|
||||
sb.append(EnumChatFormatting.GRAY);
|
||||
mode = false;
|
||||
}
|
||||
sb.appendCodePoint(subject.codePointAt(j));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (i == term.length()) {
|
||||
if (j < subject.length()) {
|
||||
sb.append(EnumChatFormatting.GRAY);
|
||||
sb.append(subject, j, subject.length());
|
||||
}
|
||||
cache.add(new AbstractMap.SimpleImmutableEntry<>(subject, sb.toString()));
|
||||
added.add(subject);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
cache = search();
|
||||
selectedIndex = cache.isEmpty() ? -1 : 0;
|
||||
resetScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementAt(int index) {
|
||||
return index >= 0 && index < cache.size() ? getOptions().get(index).getKey() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumElements() {
|
||||
return getOptions().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||
boolean isHovering) {
|
||||
int color = elementIndex == selectedIndex ? 0xffffff : 0xcccccc;
|
||||
fontRendererObj.drawString(getOptions().get(elementIndex).getValue(), x + margin, y + margin / 2, color);
|
||||
}
|
||||
}
|
||||
|
||||
private class GuiChannelsList extends GuiScrollableList<Entry<String, Integer>> {
|
||||
|
||||
private List<Entry<String, Integer>> cache;
|
||||
|
||||
public GuiChannelsList(int width, int height, int originX, int originY, int slotHeight) {
|
||||
super(width, height, originX, originY, slotHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry<String, Integer> getElementAt(int index) {
|
||||
return ChannelDataAccessor.iterateChannelData(trigger).sorted().skip(index).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumElements() {
|
||||
return ChannelDataAccessor.countChannelData(trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
|
||||
if (mXRelative >= margin + 1 && mXRelative <= margin + 5
|
||||
&& mYRelative >= margin / 2 + 1
|
||||
&& mYRelative <= margin / 2 + 5) {
|
||||
Entry<String, Integer> e = getElementAt(elementIndex);
|
||||
if (e != null) {
|
||||
ChannelDataAccessor.unsetChannelData(trigger, e.getKey());
|
||||
updateButtons();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.elementClicked(elementIndex, doubleClick, mXRelative, mYRelative);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareDrawElements() {
|
||||
cache = ChannelDataAccessor.iterateChannelData(trigger).sorted().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||
boolean isHovering) {
|
||||
if (elementIndex < 0 || elementIndex >= cache.size()) {
|
||||
return;
|
||||
}
|
||||
Entry<String, Integer> e = cache.get(elementIndex);
|
||||
if (e == null) return;
|
||||
if (elementIndex > 0) {
|
||||
// args x1, x2, y1, color
|
||||
drawHorizontalLine(minX + 1, maxX - 1, y - 2, 0xffaaaaaa);
|
||||
}
|
||||
int keyMaxWidth = KEY_MAX_WIDTH * 2;
|
||||
if (isHovering) {
|
||||
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/channels.png"));
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
drawTexturedModalRect(x + margin + 1, y + margin / 2 + 1, 251, 251, 5, 5);
|
||||
}
|
||||
fontRendererObj.drawString(trim(e.getKey(), keyMaxWidth - 9), x + margin + 8, y + margin / 2, 0xffffff);
|
||||
int valueX = x + margin + keyMaxWidth + margin;
|
||||
fontRendererObj.drawString(trim(e.getValue().toString(), maxX - valueX), valueX, y + margin / 2, 0xffffff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mX, int mY, float partialTick) {
|
||||
super.drawScreen(mX, mY, partialTick);
|
||||
// args x1, y1, y2, color
|
||||
drawVerticalLine(minX + margin + margin / 2 + KEY_MAX_WIDTH * 2, minY + 1, maxY - 1, 0xffaaaaaa);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
package com.gtnewhorizon.structurelib.gui;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Shamelessly stolen from EnderCore, with some amount of edit to fit in one file. Original code from CrazyPants.
|
||||
* <p>
|
||||
* As the code has been released into public domain, it is now LGPL as part of this library as a whole. You can go to
|
||||
* EnderCore's repository to obtain a public domain copy though
|
||||
* <p>
|
||||
* Major edits
|
||||
* <ul>
|
||||
* <li>Separate dwheel handling to handleDWheel. The next event calls often cause the mouse click event to not fire on
|
||||
* other widgets</li>
|
||||
* <li>scrollbar are now rendered within width</li>
|
||||
* <li>added prepareDrawElements</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author CrazyPants
|
||||
* @author glee8e
|
||||
*/
|
||||
public abstract class GuiScrollableList<T> {
|
||||
|
||||
protected final int slotHeight;
|
||||
private final Minecraft mc = Minecraft.getMinecraft();
|
||||
protected final int originX;
|
||||
protected final int originY;
|
||||
protected final int width;
|
||||
protected final int height;
|
||||
protected int minY;
|
||||
protected int maxY;
|
||||
protected int minX;
|
||||
protected int maxX;
|
||||
protected int mouseX;
|
||||
protected int mouseY;
|
||||
protected int selectedIndex = -1;
|
||||
protected int margin = 4;
|
||||
protected final List<ListSelectionListener<T>> listeners = new CopyOnWriteArrayList<>();
|
||||
private int scrollUpButtonID;
|
||||
private int scrollDownButtonID;
|
||||
private float initialClickY = -2.0F;
|
||||
private float scrollMultiplier;
|
||||
private float amountScrolled;
|
||||
private long lastClickedTime;
|
||||
|
||||
private boolean showSelectionBox = true;
|
||||
|
||||
public GuiScrollableList(int width, int height, int originX, int originY, int slotHeight) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.originX = originX;
|
||||
this.originY = originY;
|
||||
this.slotHeight = slotHeight;
|
||||
|
||||
minY = originY;
|
||||
maxY = minY + height;
|
||||
minX = originX;
|
||||
maxX = minX + width;
|
||||
}
|
||||
|
||||
public void onGuiInit(IGuiScreen gui) {
|
||||
minY = originY + gui.getGuiTop();
|
||||
maxY = minY + height;
|
||||
minX = originX + gui.getGuiLeft();
|
||||
maxX = minX + width;
|
||||
}
|
||||
|
||||
public void addSelectionListener(ListSelectionListener<T> listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeSelectionListener(ListSelectionListener<T> listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public T getSelectedElement() {
|
||||
return getElementAt(selectedIndex);
|
||||
}
|
||||
|
||||
public void setSelection(T selection) {
|
||||
setSelection(getIndexOf(selection));
|
||||
}
|
||||
|
||||
public void setSelection(int index) {
|
||||
if (index == selectedIndex) {
|
||||
return;
|
||||
}
|
||||
selectedIndex = index;
|
||||
for (ListSelectionListener<T> listener : listeners) {
|
||||
listener.selectionChanged(this, selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndexOf(T element) {
|
||||
if (element == null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < getNumElements(); i++) {
|
||||
if (element.equals(getElementAt(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public abstract T getElementAt(int index);
|
||||
|
||||
public abstract int getNumElements();
|
||||
|
||||
protected abstract void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||
boolean isHovering);
|
||||
|
||||
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setShowSelectionBox(boolean val) {
|
||||
showSelectionBox = val;
|
||||
}
|
||||
|
||||
protected int getContentHeight() {
|
||||
return getNumElements() * slotHeight;
|
||||
}
|
||||
|
||||
public void setScrollButtonIds(int scrollUpButtonID, int scrollDownButtonID) {
|
||||
this.scrollUpButtonID = scrollUpButtonID;
|
||||
this.scrollDownButtonID = scrollDownButtonID;
|
||||
}
|
||||
|
||||
private void clampScrollToBounds() {
|
||||
int i = getContentOverhang();
|
||||
if (i < 0) {
|
||||
i *= -1;
|
||||
}
|
||||
if (amountScrolled < 0.0F) {
|
||||
amountScrolled = 0.0F;
|
||||
}
|
||||
if (amountScrolled > i) {
|
||||
amountScrolled = i;
|
||||
}
|
||||
}
|
||||
|
||||
public int getContentOverhang() {
|
||||
return getContentHeight() - (height - margin);
|
||||
}
|
||||
|
||||
public void actionPerformed(GuiButton b) {
|
||||
if (b.enabled) {
|
||||
if (b.id == scrollUpButtonID) {
|
||||
amountScrolled -= slotHeight * 2f / 3;
|
||||
initialClickY = -2.0F;
|
||||
clampScrollToBounds();
|
||||
} else if (b.id == scrollDownButtonID) {
|
||||
amountScrolled += slotHeight * 2f / 3;
|
||||
initialClickY = -2.0F;
|
||||
clampScrollToBounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void resetScroll() {
|
||||
amountScrolled = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* draws the slot to the screen, pass in mouse's current x and y and partial ticks
|
||||
*/
|
||||
public void drawScreen(int mX, int mY, float partialTick) {
|
||||
GL11.glPushMatrix();
|
||||
GL11.glLoadIdentity();
|
||||
GL11.glTranslatef(0.0F, 0.0F, -2000.0F);
|
||||
this.mouseX = mX;
|
||||
this.mouseY = mY;
|
||||
|
||||
processMouseEvents();
|
||||
|
||||
if (getContentOverhang() > 0) maxX = minX + width - 6;
|
||||
else maxX = minX + width;
|
||||
|
||||
clampScrollToBounds();
|
||||
|
||||
GL11.glDisable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_FOG);
|
||||
|
||||
ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||
int sx = minX * sr.getScaleFactor();
|
||||
int sw = width * sr.getScaleFactor();
|
||||
int sy = mc.displayHeight - (maxY * sr.getScaleFactor());
|
||||
int sh = height * sr.getScaleFactor();
|
||||
GL11.glEnable(GL11.GL_SCISSOR_TEST);
|
||||
GL11.glScissor(sx, sy, sw, sh);
|
||||
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
drawContainerBackground(tessellator);
|
||||
|
||||
int amountScrolled = (int) this.amountScrolled;
|
||||
int contentYOffset = this.minY + margin - amountScrolled;
|
||||
|
||||
boolean hoveringPrecondition = mX >= minX && mX <= maxX && mY >= minY && mY <= maxY;
|
||||
int slotHeight = this.slotHeight - margin;
|
||||
|
||||
if (selectedIndex != -1) {
|
||||
int elementY = contentYOffset + selectedIndex * this.slotHeight;
|
||||
if (elementY + slotHeight > maxY) {
|
||||
this.amountScrolled = (selectedIndex + 1) * this.slotHeight - this.height + margin;
|
||||
amountScrolled = (int) this.amountScrolled;
|
||||
contentYOffset = this.minY + margin - amountScrolled;
|
||||
} else if (elementY < minY) {
|
||||
this.amountScrolled = selectedIndex * this.slotHeight;
|
||||
amountScrolled = (int) this.amountScrolled;
|
||||
contentYOffset = this.minY + margin - amountScrolled;
|
||||
}
|
||||
}
|
||||
|
||||
prepareDrawElements();
|
||||
|
||||
for (int i = 0; i < getNumElements(); ++i) {
|
||||
|
||||
int elementY = contentYOffset + i * this.slotHeight;
|
||||
|
||||
if (elementY <= maxY && elementY + slotHeight >= minY) {
|
||||
|
||||
if (showSelectionBox && i == selectedIndex) {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorOpaque_I(8421504);
|
||||
tessellator.addVertexWithUV(minX, elementY + slotHeight + 2, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX, elementY + slotHeight + 2, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX, elementY - 2, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(minX, elementY - 2, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.setColorOpaque_I(0);
|
||||
tessellator.addVertexWithUV(minX + 1, elementY + slotHeight + 1, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX - 1, elementY + slotHeight + 1, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(maxX - 1, elementY - 1, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(minX + 1, elementY - 1, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
boolean isHovering = hoveringPrecondition && elementY - 2 <= mY && mY < elementY + slotHeight + 2;
|
||||
drawElement(i, minX, elementY, slotHeight, tessellator, isHovering);
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glShadeModel(GL11.GL_SMOOTH);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorRGBA_I(0, 0);
|
||||
tessellator.addVertexWithUV(this.minX, this.minY + margin, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(this.maxX, this.minY + margin, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.setColorRGBA_I(0, 255);
|
||||
tessellator.addVertexWithUV(this.maxX, this.minY, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(this.minX, this.minY, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorRGBA_I(0, 255);
|
||||
tessellator.addVertexWithUV(this.minX, this.maxY, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(this.maxX, this.maxY, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.setColorRGBA_I(0, 0);
|
||||
tessellator.addVertexWithUV(this.maxX, this.maxY - margin, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(this.minX, this.maxY - margin, 0.0D, 0.0D, 0.0D);
|
||||
tessellator.draw();
|
||||
|
||||
renderScrollBar(tessellator);
|
||||
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glShadeModel(GL11.GL_FLAT);
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
protected void prepareDrawElements() {}
|
||||
|
||||
protected void renderScrollBar(Tessellator tessellator) {
|
||||
|
||||
int contentHeightOverBounds = getContentOverhang();
|
||||
if (contentHeightOverBounds > 0) {
|
||||
|
||||
int clear = (maxY - minY) * (maxY - minY) / getContentHeight();
|
||||
|
||||
if (clear < 32) {
|
||||
clear = 32;
|
||||
}
|
||||
|
||||
if (clear > maxY - minY - 8) {
|
||||
clear = maxY - minY - 8;
|
||||
}
|
||||
|
||||
int y = (int) this.amountScrolled * (maxY - minY - clear) / contentHeightOverBounds + minY;
|
||||
if (y < minY) {
|
||||
y = minY;
|
||||
}
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
int scrollBarMinX = getScrollBarX();
|
||||
int scrollBarMaxX = scrollBarMinX + 6;
|
||||
tessellator.startDrawingQuads();
|
||||
|
||||
tessellator.setColorRGBA_I(0, 255);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, maxY, 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, maxY, 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, minY, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, minY, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
tessellator.setColorRGBA_F(0.3f, 0.3f, 0.3f, 1);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, (y + clear), 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, (y + clear), 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMaxX, y, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, y, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
tessellator.setColorRGBA_F(0.7f, 0.7f, 0.7f, 1);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, (y + clear - 1), 0.0D, 0.0D, 1.0D);
|
||||
tessellator.addVertexWithUV((scrollBarMaxX - 1), (y + clear - 1), 0.0D, 1.0D, 1.0D);
|
||||
tessellator.addVertexWithUV((scrollBarMaxX - 1), y, 0.0D, 1.0D, 0.0D);
|
||||
tessellator.addVertexWithUV(scrollBarMinX, y, 0.0D, 0.0D, 0.0D);
|
||||
|
||||
tessellator.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
private void processMouseEvents() {
|
||||
if (Mouse.isButtonDown(0)) {
|
||||
processMouseBown();
|
||||
} else {
|
||||
initialClickY = -1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDWheel(int mouseWheelDelta) {
|
||||
if (!Mouse.isButtonDown(0)) {
|
||||
if (mouseWheelDelta > 0) {
|
||||
amountScrolled -= slotHeight / 2f;
|
||||
} else /* if (mouseWheelDelta < 0) */ {
|
||||
amountScrolled += slotHeight / 2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processMouseBown() {
|
||||
int contentHeightOverBounds;
|
||||
if (initialClickY == -1.0F) {
|
||||
|
||||
if (mouseY >= minY && mouseY <= maxY && mouseX >= minX && mouseX <= maxX + 6) {
|
||||
|
||||
boolean clickInBounds = true;
|
||||
|
||||
int y = mouseY - minY + (int) amountScrolled - margin;
|
||||
int mouseOverElement = y / slotHeight;
|
||||
|
||||
if (mouseX >= minX && mouseX <= maxX
|
||||
&& mouseOverElement >= 0
|
||||
&& y >= 0
|
||||
&& mouseOverElement < getNumElements()) {
|
||||
boolean doubleClick = mouseOverElement == selectedIndex
|
||||
&& Minecraft.getSystemTime() - lastClickedTime < 250L;
|
||||
if (elementClicked(
|
||||
mouseOverElement,
|
||||
doubleClick,
|
||||
mouseX - minX,
|
||||
y - slotHeight * mouseOverElement)) {
|
||||
setSelection(mouseOverElement);
|
||||
lastClickedTime = Minecraft.getSystemTime();
|
||||
}
|
||||
} else if (mouseX >= minX && mouseX <= maxX && y < 0) {
|
||||
clickInBounds = false;
|
||||
}
|
||||
|
||||
int scrollBarMinX = getScrollBarX();
|
||||
int scrollBarMaxX = scrollBarMinX + 6;
|
||||
if (mouseX >= scrollBarMinX && mouseX <= scrollBarMaxX) {
|
||||
|
||||
scrollMultiplier = -1.0F;
|
||||
contentHeightOverBounds = getContentOverhang();
|
||||
|
||||
if (contentHeightOverBounds < 1) {
|
||||
contentHeightOverBounds = 1;
|
||||
}
|
||||
|
||||
int empty = (int) ((float) ((maxY - minY) * (maxY - minY)) / (float) getContentHeight());
|
||||
if (empty < 32) {
|
||||
empty = 32;
|
||||
}
|
||||
if (empty > maxY - minY - 8) {
|
||||
empty = maxY - minY - 8;
|
||||
}
|
||||
scrollMultiplier /= (float) (maxY - minY - empty) / (float) contentHeightOverBounds;
|
||||
|
||||
} else {
|
||||
scrollMultiplier = 1.0F;
|
||||
}
|
||||
|
||||
if (clickInBounds) {
|
||||
initialClickY = mouseY;
|
||||
} else {
|
||||
initialClickY = -2.0F;
|
||||
}
|
||||
|
||||
} else {
|
||||
initialClickY = -2.0F;
|
||||
}
|
||||
|
||||
} else if (initialClickY >= 0.0F) {
|
||||
// Scrolling
|
||||
amountScrolled -= (mouseY - initialClickY) * scrollMultiplier;
|
||||
initialClickY = mouseY;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getScrollBarX() {
|
||||
return maxX;
|
||||
}
|
||||
|
||||
protected void drawContainerBackground(Tessellator tess) {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
tess.startDrawingQuads();
|
||||
tess.setColorOpaque_I(0x202020);
|
||||
tess.addVertex(minX, maxY, 0.0D);
|
||||
tess.addVertex(maxX, maxY, 0.0D);
|
||||
tess.addVertex(maxX, minY, 0.0D);
|
||||
tess.addVertex(minX, minY, 0.0D);
|
||||
tess.draw();
|
||||
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
public interface IGuiScreen {
|
||||
|
||||
int getGuiLeft();
|
||||
|
||||
int getGuiTop();
|
||||
|
||||
int getXSize();
|
||||
|
||||
int getYSize();
|
||||
|
||||
void addButton(GuiButton button);
|
||||
|
||||
void removeButton(GuiButton button);
|
||||
|
||||
int getOverlayOffsetX();
|
||||
|
||||
void doActionPerformed(GuiButton but);
|
||||
}
|
||||
|
||||
public interface ListSelectionListener<T> {
|
||||
|
||||
void selectionChanged(GuiScrollableList<T> list, int selectedIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.gtnewhorizon.structurelib.item;
|
||||
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -7,60 +11,69 @@ import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
|
||||
public class ItemBlockHint extends ItemBlock {
|
||||
public ItemBlockHint(Block p_i45328_1_) {
|
||||
super(p_i45328_1_);
|
||||
setMaxDamage(0);
|
||||
setHasSubtypes(true);
|
||||
setCreativeTab(CreativeTabs.tabBlock);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addInformation(ItemStack aStack, EntityPlayer aPlayer, List aList, boolean aF3_H) {
|
||||
aList.add(translateToLocal("structurelib.blockhint.desc.0"));//Helps while building
|
||||
switch (aStack.getItemDamage()) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
aList.add(EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD + translateToLocal("structurelib.blockhint.desc.1"));//Placeholder for a certain group.
|
||||
break;
|
||||
case 12:
|
||||
aList.add(EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD + translateToLocal("structurelib.blockhint.desc.2"));//General placeholder.
|
||||
break;
|
||||
case 13:
|
||||
aList.add(EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD + translateToLocal("structurelib.blockhint.desc.3"));//Make sure it contains Air material.
|
||||
break;
|
||||
case 14:
|
||||
aList.add(EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD + translateToLocal("structurelib.blockhint.desc.4"));//Make sure it does not contain Air material.
|
||||
break;
|
||||
case 15:
|
||||
aList.add(EnumChatFormatting.BLUE.toString() + translateToLocal("structurelib.blockhint.desc.5"));//ERROR, what did u expect?
|
||||
break;
|
||||
default://WTF?
|
||||
aList.add("Damn son where did you get that!?");
|
||||
aList.add(EnumChatFormatting.BLUE.toString() + "From outer space... I guess...");
|
||||
}
|
||||
}
|
||||
public ItemBlockHint(Block p_i45328_1_) {
|
||||
super(p_i45328_1_);
|
||||
setMaxDamage(0);
|
||||
setHasSubtypes(true);
|
||||
setCreativeTab(CreativeTabs.tabBlock);
|
||||
}
|
||||
|
||||
public int getMetadata(int aMeta) {
|
||||
return aMeta;
|
||||
}
|
||||
@Override
|
||||
public void addInformation(ItemStack stack, EntityPlayer player, List<String> tooltip, boolean adv) {
|
||||
tooltip.add(translateToLocal("structurelib.blockhint.desc.0")); // Helps while building
|
||||
switch (stack.getItemDamage()) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
// Placeholder for a certain group.
|
||||
tooltip.add(
|
||||
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||
+ translateToLocal("structurelib.blockhint.desc.1"));
|
||||
break;
|
||||
case 12:
|
||||
// General placeholder.
|
||||
tooltip.add(
|
||||
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||
+ translateToLocal("structurelib.blockhint.desc.2"));
|
||||
break;
|
||||
case 13:
|
||||
// Make sure it contains Air material.
|
||||
tooltip.add(
|
||||
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||
+ translateToLocal("structurelib.blockhint.desc.3"));
|
||||
break;
|
||||
case 14:
|
||||
// Make sure it does not contain Air material.
|
||||
tooltip.add(
|
||||
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||
+ translateToLocal("structurelib.blockhint.desc.4"));
|
||||
break;
|
||||
case 15:
|
||||
// ERROR, what did u expect?
|
||||
tooltip.add(EnumChatFormatting.BLUE + translateToLocal("structurelib.blockhint.desc.5"));
|
||||
break;
|
||||
default: // WTF?
|
||||
tooltip.add("Damn son where did you get that!?");
|
||||
tooltip.add(EnumChatFormatting.BLUE + "From outer space... I guess...");
|
||||
}
|
||||
}
|
||||
|
||||
public String getUnlocalizedName(ItemStack aStack) {
|
||||
return this.field_150939_a.getUnlocalizedName() + "." + getDamage(aStack);
|
||||
}
|
||||
public int getMetadata(int aMeta) {
|
||||
return aMeta;
|
||||
}
|
||||
|
||||
public String getUnlocalizedName(ItemStack aStack) {
|
||||
return this.field_150939_a.getUnlocalizedName() + "." + getDamage(aStack);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,25 @@
|
||||
package com.gtnewhorizon.structurelib.item;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
import static net.minecraft.util.EnumChatFormatting.BLUE;
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
import static net.minecraft.util.StatCollector.translateToLocalFormatted;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
import static net.minecraft.util.EnumChatFormatting.BLUE;
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
|
||||
|
||||
public class ItemConstructableTrigger extends Item {
|
||||
|
||||
public ItemConstructableTrigger() {
|
||||
setUnlocalizedName("structurelib.constructableTrigger");
|
||||
setTextureName(MOD_ID + ":itemConstructableTrigger");
|
||||
@@ -21,17 +27,48 @@ public class ItemConstructableTrigger extends Item {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ) {
|
||||
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
|
||||
if (getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||
// channel gui is opened from server and config gui from client
|
||||
if (!world.isRemote && !player.isSneaking()) {
|
||||
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||
} else if (world.isRemote && player.isSneaking()) {
|
||||
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||
}
|
||||
}
|
||||
return super.onItemRightClick(stack, world, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
|
||||
float hitX, float hitY, float hitZ) {
|
||||
return ConstructableUtility.handle(stack, player, world, x, y, z, side);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) {
|
||||
aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.0"));//Triggers Constructable Interface
|
||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.1"));//Shows multiblock construction details,
|
||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.2"));//just Use on a multiblock controller.
|
||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.3"));//(Sneak Use in creative to build)
|
||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.4"));//Quantity affects tier/mode/type
|
||||
public void addInformation(ItemStack stack, EntityPlayer player, List<String> tooltip, boolean adv) {
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
|
||||
tooltip.add(
|
||||
translateToLocalFormatted(
|
||||
"item.structurelib.constructableTrigger.desc.lshift.0",
|
||||
ChannelDataAccessor.countChannelData(stack)));
|
||||
ChannelDataAccessor.iterateChannelData(stack).map(e -> e.getKey() + ": " + e.getValue())
|
||||
.forEach(tooltip::add);
|
||||
} else {
|
||||
// Tooltip text:
|
||||
// Triggers Constructable Interface
|
||||
// Shows multiblock construction details,
|
||||
// just Use on a multiblock controller.
|
||||
// (Sneak Use in creative to build)
|
||||
// Quantity affects tier/mode/type
|
||||
|
||||
tooltip.add(translateToLocal("item.structurelib.constructableTrigger.desc.0"));
|
||||
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.1"));
|
||||
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.2"));
|
||||
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.3"));
|
||||
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.4"));
|
||||
if (ChannelDataAccessor.hasSubChannel(stack))
|
||||
tooltip.add(translateToLocal("item.structurelib.constructableTrigger.desc.5"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.gtnewhorizon.structurelib.item;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
|
||||
/**
|
||||
* Hologram projector that always performs a creative build, whatever the player's game mode.
|
||||
*/
|
||||
public class ItemDebugConstructableTrigger extends ItemConstructableTrigger {
|
||||
|
||||
public ItemDebugConstructableTrigger() {
|
||||
setUnlocalizedName("structurelib.debugConstructableTrigger");
|
||||
setTextureName(MOD_ID + ":itemDebugConstructableTrigger");
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,47 @@
|
||||
package com.gtnewhorizon.structurelib.item;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.AlignmentUtility;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.AlignmentUtility;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class ItemFrontRotationTool extends Item {
|
||||
public ItemFrontRotationTool() {
|
||||
setMaxStackSize(1);
|
||||
setUnlocalizedName("structurelib.frontRotationTool");
|
||||
setTextureName(MOD_ID + ":itemFrontRotationTool");
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ) {
|
||||
return AlignmentUtility.handle(player, world, x, y, z);
|
||||
}
|
||||
public ItemFrontRotationTool() {
|
||||
setMaxStackSize(1);
|
||||
setUnlocalizedName("structurelib.frontRotationTool");
|
||||
setTextureName(MOD_ID + ":itemFrontRotationTool");
|
||||
setCreativeTab(StructureLib.creativeTab);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack p_77624_1_, EntityPlayer p_77624_2_, List 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 front panel,
|
||||
aList.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.2"));//which allows structure rotation.
|
||||
}
|
||||
@Override
|
||||
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
|
||||
float hitX, float hitY, float hitZ) {
|
||||
return AlignmentUtility.handle(player, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, EntityPlayer player, List<String> tooltip, boolean adv) {
|
||||
// Tooltip Text:
|
||||
// Triggers Front Rotation Interface
|
||||
// Rotates only the front panel,
|
||||
// which allows structure rotation.
|
||||
|
||||
tooltip.add(translateToLocal("item.structurelib.frontRotationTool.desc.0"));
|
||||
tooltip.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.1"));
|
||||
tooltip.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.2"));
|
||||
}
|
||||
}
|
||||
@@ -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 + "'}";
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.gtnewhorizon.structurelib.mixins.early;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.gtnewhorizon.structurelib.ChannelPickHandler;
|
||||
import com.gtnewhorizon.structurelib.ChannelWipeConfirmation;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import cpw.mods.fml.common.Loader;
|
||||
|
||||
@Mixin(value = GuiContainer.class, priority = 100)
|
||||
public abstract class MixinGuiContainer_PickChannel {
|
||||
|
||||
@Inject(method = "mouseClicked(III)V", at = @At("HEAD"), cancellable = true)
|
||||
private void slib$onMouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) {
|
||||
if (mouseButton != 2) return;
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().thePlayer;
|
||||
if (player == null) return;
|
||||
|
||||
ItemStack cursorStack = player.inventory.getItemStack();
|
||||
if (cursorStack == null || !(cursorStack.getItem() instanceof ItemConstructableTrigger)) return;
|
||||
|
||||
// Sink middle click as soon as projector is picked up
|
||||
ci.cancel();
|
||||
|
||||
ItemStack hoveredStack = null;
|
||||
try {
|
||||
Slot slot = slib$invokeGetSlotAtPosition(mouseX, mouseY);
|
||||
if (slot != null) {
|
||||
hoveredStack = slot.getStack();
|
||||
}
|
||||
|
||||
// try NEI
|
||||
if (hoveredStack == null && Loader.isModLoaded("NotEnoughItems") && !NEIClientConfig.isHidden()) {
|
||||
hoveredStack = GuiContainerManager.getStackMouseOver((GuiContainer) (Object) this);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
StructureLib.LOGGER.error("Error while resolving hovered slot for channel pick", e);
|
||||
}
|
||||
|
||||
boolean isSneaking = GuiScreen.isShiftKeyDown();
|
||||
if (isSneaking && !ChannelWipeConfirmation.confirm(player)) return;
|
||||
|
||||
ChannelPickHandler.handleMiddleClick(player, cursorStack, hoveredStack, isSneaking);
|
||||
}
|
||||
|
||||
@Invoker("getSlotAtPosition")
|
||||
protected abstract Slot slib$invokeGetSlotAtPosition(int x, int y);
|
||||
}
|
||||
+44
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,30 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
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;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.DimensionManager;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
|
||||
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 abstract class AlignmentMessage implements IMessage {
|
||||
|
||||
int mPosX;
|
||||
int mPosY;
|
||||
int mPosZ;
|
||||
int mPosD;
|
||||
int mAlign;
|
||||
|
||||
public AlignmentMessage() {
|
||||
}
|
||||
public AlignmentMessage() {}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf pBuffer) {
|
||||
@@ -47,11 +49,9 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
}
|
||||
|
||||
private AlignmentMessage(IAlignmentProvider provider) {
|
||||
if (!(provider instanceof TileEntity))
|
||||
throw new IllegalArgumentException("Provider must be a TileEntity");
|
||||
if (!(provider instanceof TileEntity)) throw new IllegalArgumentException("Provider must be a TileEntity");
|
||||
IAlignment alignment = provider.getAlignment();
|
||||
if (alignment == null)
|
||||
throw new IllegalArgumentException("Passed in provider does not provide an alignment!");
|
||||
if (alignment == null) throw new IllegalArgumentException("Passed in provider does not provide an alignment!");
|
||||
TileEntity base = (TileEntity) provider;
|
||||
mPosX = base.xCoord;
|
||||
mPosY = base.yCoord;
|
||||
@@ -69,8 +69,8 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
}
|
||||
|
||||
public static class AlignmentQuery extends AlignmentMessage {
|
||||
public AlignmentQuery() {
|
||||
}
|
||||
|
||||
public AlignmentQuery() {}
|
||||
|
||||
public AlignmentQuery(IAlignmentProvider provider) {
|
||||
super(provider);
|
||||
@@ -82,8 +82,8 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
}
|
||||
|
||||
public static class AlignmentData extends AlignmentMessage {
|
||||
public AlignmentData() {
|
||||
}
|
||||
|
||||
public AlignmentData() {}
|
||||
|
||||
private AlignmentData(AlignmentQuery query) {
|
||||
mPosX = query.mPosX;
|
||||
@@ -103,10 +103,12 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
}
|
||||
|
||||
public static class ClientHandler implements IMessageHandler<AlignmentData, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(AlignmentData pMessage, MessageContext pCtx) {
|
||||
if (StructureLib.getCurrentPlayer().worldObj.provider.dimensionId == pMessage.mPosD) {
|
||||
TileEntity te = StructureLib.getCurrentPlayer().worldObj.getTileEntity(pMessage.mPosX, pMessage.mPosY, pMessage.mPosZ);
|
||||
TileEntity te = StructureLib.getCurrentPlayer().worldObj
|
||||
.getTileEntity(pMessage.mPosX, pMessage.mPosY, pMessage.mPosZ);
|
||||
if (te instanceof IAlignmentProvider) {
|
||||
IAlignment alignment = ((IAlignmentProvider) te).getAlignment();
|
||||
if (alignment != null) {
|
||||
@@ -119,6 +121,7 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
}
|
||||
|
||||
public static class ServerHandler implements IMessageHandler<AlignmentQuery, AlignmentData> {
|
||||
|
||||
@Override
|
||||
public AlignmentData onMessage(AlignmentQuery pMessage, MessageContext pCtx) {
|
||||
World world = DimensionManager.getWorld(pMessage.mPosD);
|
||||
@@ -126,8 +129,7 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
TileEntity te = world.getTileEntity(pMessage.mPosX, pMessage.mPosY, pMessage.mPosZ);
|
||||
if (te instanceof IAlignmentProvider) {
|
||||
IAlignment alignment = ((IAlignmentProvider) te).getAlignment();
|
||||
if (alignment == null)
|
||||
return null;
|
||||
if (alignment == null) return null;
|
||||
pMessage.mAlign = alignment.getExtendedFacing().getIndex();
|
||||
return new AlignmentData(pMessage);
|
||||
}
|
||||
@@ -135,4 +137,4 @@ public abstract class AlignmentMessage implements IMessage {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
|
||||
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 ErrorHintParticleMessage implements IMessage {
|
||||
|
||||
private int x;
|
||||
private short y;
|
||||
private int z;
|
||||
|
||||
public ErrorHintParticleMessage() {}
|
||||
|
||||
public ErrorHintParticleMessage(int x, short y, int z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
x = buf.readInt();
|
||||
y = buf.readShort();
|
||||
z = buf.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
buf.writeInt(x);
|
||||
buf.writeShort(y);
|
||||
buf.writeInt(z);
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<ErrorHintParticleMessage, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(ErrorHintParticleMessage msg, MessageContext ctx) {
|
||||
boolean updateResult = StructureLibAPI.markHintParticleError(
|
||||
StructureLib.getCurrentPlayer(),
|
||||
StructureLib.getCurrentPlayer().worldObj,
|
||||
msg.x,
|
||||
msg.y,
|
||||
msg.z);
|
||||
if (StructureLibAPI.isDebugEnabled()) LOGGER.debug(
|
||||
"Server instructed to mark hint particle at ({}, {}, {}) error, result {}!",
|
||||
msg.x,
|
||||
msg.y,
|
||||
msg.z,
|
||||
updateResult);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||
|
||||
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 SetChannelDataMessage implements IMessage {
|
||||
|
||||
private final List<Map.Entry<String, Integer>> data = new ArrayList<>();
|
||||
private boolean onCursor;
|
||||
|
||||
public SetChannelDataMessage() {}
|
||||
|
||||
public SetChannelDataMessage(ItemStack trigger) {
|
||||
this(trigger, false);
|
||||
}
|
||||
|
||||
public SetChannelDataMessage(ItemStack trigger, final boolean onCursor) {
|
||||
this.onCursor = onCursor;
|
||||
ChannelDataAccessor.iterateChannelData(trigger).forEach(data::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
onCursor = buf.readBoolean();
|
||||
data.clear();
|
||||
int size = ByteBufUtils.readVarShort(buf);
|
||||
for (int i = 0; i < size; i++) {
|
||||
data.add(Pair.of(ByteBufUtils.readUTF8String(buf), ByteBufUtils.readVarInt(buf, 4)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
buf.writeBoolean(onCursor);
|
||||
ByteBufUtils.writeVarShort(buf, data.size());
|
||||
for (Entry<String, Integer> e : data) {
|
||||
ByteBufUtils.writeUTF8String(buf, e.getKey());
|
||||
ByteBufUtils.writeVarInt(buf, e.getValue(), 4);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<SetChannelDataMessage, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) {
|
||||
EntityPlayerMP player = ctx.getServerHandler().playerEntity;
|
||||
ItemStack target = message.onCursor ? player.inventory.getItemStack() : player.getHeldItem();
|
||||
if (target != null && target.getItem() instanceof ItemConstructableTrigger) {
|
||||
ChannelDataAccessor.wipeChannelData(target);
|
||||
for (Entry<String, Integer> e : message.data) {
|
||||
ChannelDataAccessor.setChannelData(target, e.getKey(), e.getValue());
|
||||
}
|
||||
// since this is a set all channel request from the client, we would assume client already know
|
||||
// what this would look like on the client, so no sync
|
||||
} else {
|
||||
StructureLib.LOGGER.warn(
|
||||
"{} trying to set channel data on {}, which is not a hologram projector!",
|
||||
player.getUniqueID(),
|
||||
target);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.gtnewhorizon.structurelib.net;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
|
||||
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 UpdateHintParticleMessage implements IMessage {
|
||||
|
||||
private int x;
|
||||
private short y;
|
||||
private int z;
|
||||
private short r;
|
||||
private short g;
|
||||
private short b;
|
||||
private short a;
|
||||
|
||||
public UpdateHintParticleMessage() {}
|
||||
|
||||
public UpdateHintParticleMessage(int x, short y, int z, short r, short g, short b, short a) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromBytes(ByteBuf buf) {
|
||||
x = buf.readInt();
|
||||
y = buf.readShort();
|
||||
z = buf.readInt();
|
||||
r = buf.readUnsignedByte();
|
||||
g = buf.readUnsignedByte();
|
||||
b = buf.readUnsignedByte();
|
||||
a = buf.readUnsignedByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBytes(ByteBuf buf) {
|
||||
buf.writeInt(x);
|
||||
buf.writeShort(y);
|
||||
buf.writeInt(z);
|
||||
buf.writeByte(r);
|
||||
buf.writeByte(g);
|
||||
buf.writeByte(b);
|
||||
buf.writeByte(a);
|
||||
}
|
||||
|
||||
public static class Handler implements IMessageHandler<UpdateHintParticleMessage, IMessage> {
|
||||
|
||||
@Override
|
||||
public IMessage onMessage(UpdateHintParticleMessage msg, MessageContext ctx) {
|
||||
boolean updateResult = StructureLibAPI.updateHintParticleTint(
|
||||
StructureLib.getCurrentPlayer(),
|
||||
StructureLib.getCurrentPlayer().worldObj,
|
||||
msg.x,
|
||||
msg.y,
|
||||
msg.z,
|
||||
new short[] { msg.r, msg.g, msg.b, msg.a, });
|
||||
if (StructureLibAPI.isDebugEnabled()) LOGGER.debug(
|
||||
"Server instructed to update hint particle at ({}, {}, {}), result {}!",
|
||||
msg.x,
|
||||
msg.y,
|
||||
msg.z,
|
||||
updateResult);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.proxy;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
import com.gtnewhorizon.structurelib.ConfigurationHandler;
|
||||
import com.gtnewhorizon.structurelib.entity.fx.EntityFXBlockHint;
|
||||
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiNewChat;
|
||||
import net.minecraft.client.particle.EntityFX;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
@Override
|
||||
public void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
|
||||
EntityFXBlockHint hint = new EntityFXBlockHint(w, x, y, z, icons).withColorTint(RGBa);
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(hint);
|
||||
ensureHinting();
|
||||
if (ConfigurationHandler.INSTANCE.isRemoveCollidingHologram()) {
|
||||
HintParticleInfo info = new HintParticleInfo(x, y, z, currentHints);
|
||||
EntityFXBlockHint dupe = allHints.inverse().get(info);
|
||||
if (dupe != null) {
|
||||
List<EntityFXBlockHint> owner = allHints.get(dupe).owner;
|
||||
// in case someone forget to call endHinting
|
||||
if (owner != currentHints) {
|
||||
hintOwners.remove(owner);
|
||||
owner.forEach(EntityFXBlockHint::setDead);
|
||||
owner.clear();
|
||||
}
|
||||
}
|
||||
allHints.forcePut(hint, info);
|
||||
}
|
||||
currentHints.add(hint);
|
||||
|
||||
EntityFX particle = new WeightlessParticleFX(w, x + RANDOM.nextFloat() * 0.5F, y + RANDOM.nextFloat() * 0.5F, z + RANDOM.nextFloat() * 0.5F, 0, 0, 0);
|
||||
particle.setRBGColorF(0, 0.6F * RANDOM.nextFloat(), 0.8f);
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(particle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {
|
||||
hintParticleTinted(w, x, y, z, createIIconFromBlock(block, meta), RGBa);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
|
||||
hintParticleTinted(w, x, y, z, icons, new short[]{255, 255, 255, 0});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hintParticle(World w, int x, int y, int z, Block block, int meta) {
|
||||
hintParticleTinted(w, x, y, z, createIIconFromBlock(block, meta), new short[]{255, 255, 255, 0});
|
||||
}
|
||||
|
||||
private static IIcon[] createIIconFromBlock(Block block, int meta) {
|
||||
IIcon[] ret = new IIcon[6];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
ret[i] = block.getIcon(i, meta);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientSideChatMessages(String... messages) {
|
||||
GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI();
|
||||
for (String s : messages) {
|
||||
chat.printChatMessage(new ChatComponentText(s));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPlayer getCurrentPlayer() {
|
||||
return Minecraft.getMinecraft().thePlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCurrentPlayer(EntityPlayer player) {
|
||||
return player == Minecraft.getMinecraft().thePlayer;
|
||||
}
|
||||
|
||||
private static final BiMap<EntityFXBlockHint, HintParticleInfo> allHints = HashBiMap.create();
|
||||
private static final List<List<EntityFXBlockHint>> hintOwners = new ArrayList<>();
|
||||
private static List<EntityFXBlockHint> currentHints;
|
||||
|
||||
public static void onHintDead(EntityFXBlockHint fx) {
|
||||
if (ConfigurationHandler.INSTANCE.isRemoveCollidingHologram())
|
||||
allHints.remove(fx);
|
||||
for (Iterator<List<EntityFXBlockHint>> iterator = hintOwners.iterator(); iterator.hasNext(); ) {
|
||||
List<EntityFXBlockHint> list = iterator.next();
|
||||
if (list.remove(fx)) {
|
||||
if (list.isEmpty())
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startHinting(World w) {
|
||||
if (!w.isRemote)
|
||||
return;
|
||||
if (currentHints != null)
|
||||
hintOwners.add(currentHints);
|
||||
currentHints = new LinkedList<>();
|
||||
}
|
||||
|
||||
private void ensureHinting() {
|
||||
if (currentHints == null)
|
||||
currentHints = new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endHinting(World w) {
|
||||
if (!w.isRemote)
|
||||
return;
|
||||
while (!hintOwners.isEmpty() && hintOwners.size() >= ConfigurationHandler.INSTANCE.getMaxCoexistingHologram()) {
|
||||
List<EntityFXBlockHint> list = hintOwners.remove(0);
|
||||
list.forEach(EntityFXBlockHint::setDead);
|
||||
list.clear();
|
||||
}
|
||||
if (!currentHints.isEmpty())
|
||||
hintOwners.add(currentHints);
|
||||
currentHints = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preInit(FMLPreInitializationEvent e) {
|
||||
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||
}
|
||||
|
||||
private static class HintParticleInfo {
|
||||
private final int x, y, z;
|
||||
private final List<EntityFXBlockHint> owner;
|
||||
|
||||
public HintParticleInfo(int x, int y, int z, List<EntityFXBlockHint> owner) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
HintParticleInfo that = (HintParticleInfo) o;
|
||||
|
||||
if (x != that.x) return false;
|
||||
if (y != that.y) return false;
|
||||
return z == that.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = x;
|
||||
result = 31 * result + y;
|
||||
result = 31 * result + z;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ForgeEventHandler {
|
||||
@SubscribeEvent
|
||||
public void onWorldLoad(WorldEvent.Load e) {
|
||||
if (e.world.isRemote) {
|
||||
allHints.clear();
|
||||
hintOwners.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.gtnewhorizon.structurelib.proxy;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CommonProxy {
|
||||
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){}
|
||||
public void hintParticle(World w, int x, int y, int z, IIcon[] icons){}
|
||||
public void hintParticle(World w, int x, int y, int z, Block block, int meta){}
|
||||
public EntityPlayer getCurrentPlayer() {
|
||||
return null;
|
||||
}
|
||||
public boolean isCurrentPlayer(EntityPlayer player) {
|
||||
return false;
|
||||
}
|
||||
public void addClientSideChatMessages(String... messages){
|
||||
}
|
||||
public void startHinting(World w) {}
|
||||
public void endHinting(World w) {}
|
||||
|
||||
public void preInit(FMLPreInitializationEvent e) {}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
|
||||
/**
|
||||
* Represent the environment in which autoplace of a single element took place.
|
||||
*/
|
||||
public class AutoPlaceEnvironment {
|
||||
|
||||
private IItemSource source;
|
||||
private final EntityPlayer actor;
|
||||
private final Consumer<IChatComponent> chatter;
|
||||
private final IStructureDefinition<?> definition;
|
||||
private final String piece;
|
||||
private final ExtendedFacing facing;
|
||||
final int[] offsetABC;
|
||||
private final int[] baseOffsetABC;
|
||||
|
||||
public static AutoPlaceEnvironment fromLegacy(IItemSource source, EntityPlayer actor,
|
||||
Consumer<IChatComponent> chatter) {
|
||||
if (source instanceof WrappedIItemSource) {
|
||||
AutoPlaceEnvironment original = ((WrappedIItemSource) source).container;
|
||||
// feels like this is extremely likely to cause issues, but this does allow us to recover lost info.
|
||||
// TODO probably should remove this once all addons migrate over
|
||||
if (actor != original.actor || chatter != original.chatter) {
|
||||
AutoPlaceEnvironment newEnv = original;
|
||||
if (actor != original.actor) newEnv = newEnv.withActor(actor);
|
||||
if (chatter != original.chatter) newEnv = newEnv.withChatter(chatter);
|
||||
return newEnv;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
return new AutoPlaceEnvironment(source, actor, chatter, null, null, null, null, null);
|
||||
}
|
||||
|
||||
AutoPlaceEnvironment(EntityPlayer actor, Consumer<IChatComponent> chatter, IStructureDefinition<?> definition,
|
||||
String piece, ExtendedFacing facing, int[] baseOffsetABC) {
|
||||
this.source = null;
|
||||
this.actor = actor;
|
||||
this.chatter = chatter;
|
||||
this.definition = definition;
|
||||
this.piece = piece;
|
||||
this.facing = facing;
|
||||
this.offsetABC = new int[3];
|
||||
this.baseOffsetABC = baseOffsetABC;
|
||||
}
|
||||
|
||||
AutoPlaceEnvironment(IItemSource source, EntityPlayer actor, Consumer<IChatComponent> chatter,
|
||||
IStructureDefinition<?> definition, String piece, ExtendedFacing facing, int[] offsetABC,
|
||||
int[] baseOffsetABC) {
|
||||
this.source = definition != null && !(source instanceof WrappedIItemSource)
|
||||
? new WrappedIItemSource(this, source)
|
||||
: source;
|
||||
this.actor = actor;
|
||||
this.chatter = chatter;
|
||||
this.definition = definition;
|
||||
this.piece = piece;
|
||||
this.facing = facing;
|
||||
this.offsetABC = offsetABC;
|
||||
this.baseOffsetABC = baseOffsetABC;
|
||||
}
|
||||
|
||||
protected AutoPlaceEnvironment(AutoPlaceEnvironment parent) {
|
||||
this(
|
||||
parent.getSource(),
|
||||
parent.getActor(),
|
||||
parent.getChatter(),
|
||||
parent.definition,
|
||||
parent.piece,
|
||||
parent.facing,
|
||||
parent.offsetABC,
|
||||
parent.baseOffsetABC);
|
||||
}
|
||||
|
||||
void setSource(IItemSource source) {
|
||||
this.source = definition != null && !(source instanceof WrappedIItemSource)
|
||||
? new WrappedIItemSource(this, source)
|
||||
: source;
|
||||
}
|
||||
|
||||
public APILevel getAPILevel() {
|
||||
return definition == null ? actor instanceof EntityPlayerMP ? APILevel.Legacy : APILevel.LegacyRelaxed
|
||||
: APILevel.V2;
|
||||
}
|
||||
|
||||
/**
|
||||
* From where survival autoplace will drain resources.
|
||||
*/
|
||||
public IItemSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* The initiator of actions. for very critical errors you can also just send the error messages here, bypassing any
|
||||
* filter that {@link #getChatter()} might have. You might want to use
|
||||
* {@link com.gtnewhorizon.structurelib.StructureLibAPI#addThrottledChat(Object, EntityPlayer, IChatComponent, short)}
|
||||
* to help reduce spam.
|
||||
*/
|
||||
public EntityPlayer getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* send error messages here. Caller will choose an appropriate way to forward it to player if the other fallbacks
|
||||
* also fails.
|
||||
*/
|
||||
public Consumer<IChatComponent> getChatter() {
|
||||
return chatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if given location is contained within the current piece.
|
||||
*
|
||||
* @param offsetA offset in A direction <b>relative to current element</b>
|
||||
* @param offsetB offset in B direction <b>relative to current element</b>
|
||||
* @param offsetC offset in C direction <b>relative to current element</b>
|
||||
* @return true if contained, false otherwise.
|
||||
*/
|
||||
public boolean isContainedInPiece(int offsetA, int offsetB, int offsetC) {
|
||||
if (definition == null) return false;
|
||||
return definition.isContainedInStructure(
|
||||
piece,
|
||||
offsetABC[0] + offsetA + baseOffsetABC[0],
|
||||
offsetABC[1] + offsetB + baseOffsetABC[1],
|
||||
offsetABC[2] + offsetC + baseOffsetABC[2]);
|
||||
}
|
||||
|
||||
public ExtendedFacing getFacing() {
|
||||
return facing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance with source modified to given value.
|
||||
*
|
||||
* @param source new source
|
||||
* @return new instance
|
||||
*/
|
||||
public AutoPlaceEnvironment withSource(IItemSource source) {
|
||||
return new AutoPlaceEnvironment(source, actor, chatter, definition, piece, facing, offsetABC, baseOffsetABC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance with actor modified to given value.
|
||||
*
|
||||
* @param actor new actor
|
||||
* @return new instance
|
||||
*/
|
||||
public AutoPlaceEnvironment withActor(EntityPlayer actor) {
|
||||
return new AutoPlaceEnvironment(source, actor, chatter, definition, piece, facing, offsetABC, baseOffsetABC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance with chatter modified to given value.
|
||||
*
|
||||
* @param chatter new chatter
|
||||
* @return new instance
|
||||
*/
|
||||
public AutoPlaceEnvironment withChatter(Consumer<IChatComponent> chatter) {
|
||||
return new AutoPlaceEnvironment(source, actor, chatter, definition, piece, facing, offsetABC, baseOffsetABC);
|
||||
}
|
||||
|
||||
private static class WrappedIItemSource implements IItemSource {
|
||||
|
||||
final AutoPlaceEnvironment container;
|
||||
final IItemSource delegate;
|
||||
|
||||
public WrappedIItemSource(AutoPlaceEnvironment container, IItemSource delegate) {
|
||||
this.container = container;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Map<ItemStack, Integer> take(Predicate<ItemStack> predicate, boolean simulate, int count) {
|
||||
return delegate.take(predicate, simulate, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack takeOne(Predicate<ItemStack> predicate, boolean simulate) {
|
||||
return delegate.takeOne(predicate, simulate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeAll(Predicate<ItemStack> predicate, boolean simulate, int count) {
|
||||
return delegate.takeAll(predicate, simulate, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeOne(ItemStack stack, boolean simulate) {
|
||||
return delegate.takeOne(stack, simulate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeAll(ItemStack stack, boolean simulate) {
|
||||
return delegate.takeAll(stack, simulate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the various API level an {@link AutoPlaceEnvironment} has implemented.
|
||||
* <p>
|
||||
* Enum constants are defined in chronological order.
|
||||
*/
|
||||
public enum APILevel {
|
||||
/**
|
||||
* Implements {@link #getChatter()}, {@link #getActor()} and {@link #getSource()}. {@link #getActor()} is
|
||||
* guaranteed to be an {@link net.minecraft.entity.player.EntityPlayerMP}
|
||||
*/
|
||||
Legacy,
|
||||
/**
|
||||
* Implements {@link #getChatter()}, {@link #getActor()} and {@link #getSource()}. {@link #getActor()} is
|
||||
* <b>NOT</b> guaranteed to be an {@link net.minecraft.entity.player.EntityPlayerMP}
|
||||
*/
|
||||
LegacyRelaxed,
|
||||
/**
|
||||
* Implements everything so far we have defined.
|
||||
*/
|
||||
V2,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
class ChatThrottleKey {
|
||||
|
||||
static class NoExplicitChannel {
|
||||
|
||||
private static final int hashOffset = NoExplicitChannel.class.hashCode();
|
||||
private final String subchannel;
|
||||
|
||||
public NoExplicitChannel(String subchannel) {
|
||||
this.subchannel = subchannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof NoExplicitChannel that)) return false;
|
||||
|
||||
return subchannel.equals(that.subchannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// as this one will be placed within a map along with instances of different classes, it'd be best to add
|
||||
// an offset based off class type to avoid collision
|
||||
return subchannel.hashCode() + hashOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
class DefaultSurvivalBuildEnvironment implements ISurvivalBuildEnvironment {
|
||||
|
||||
private final IItemSource source;
|
||||
private final EntityPlayer actor;
|
||||
|
||||
public DefaultSurvivalBuildEnvironment(IItemSource source, EntityPlayer actor) {
|
||||
this.source = source;
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IItemSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityPlayer getActor() {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@ package com.gtnewhorizon.structurelib.structure;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface IBlockPosConsumer {
|
||||
void consume(World world, int x, int y, int z);
|
||||
|
||||
void consume(World world, int x, int y, int z);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
/**
|
||||
* Describes a channel used by one structure definition.
|
||||
*
|
||||
* <p>
|
||||
* Channel values are integers stored on the constructable trigger item. Implementations may override the default
|
||||
* validation and normalization policy when a simple bounded range is not sufficient.
|
||||
* </p>
|
||||
*/
|
||||
public interface IChannelDefinition {
|
||||
|
||||
/**
|
||||
* Creates a channel with a bounded inclusive range and clamping normalization.
|
||||
*
|
||||
* @param channel channel identifier
|
||||
* @param minimumValue inclusive minimum value
|
||||
* @param maximumValue inclusive maximum value
|
||||
* @return bounded channel definition
|
||||
*/
|
||||
static IChannelDefinition bounded(String channel, int minimumValue, int maximumValue) {
|
||||
if (minimumValue < 1 || maximumValue < minimumValue) {
|
||||
throw new IllegalArgumentException("Invalid channel bounds");
|
||||
}
|
||||
return new IChannelDefinition() {
|
||||
|
||||
@Override
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumValue() {
|
||||
return minimumValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumValue() {
|
||||
return maximumValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a channel accepting every positive integer.
|
||||
*
|
||||
* @param channel channel identifier
|
||||
* @return unbounded channel definition
|
||||
*/
|
||||
static IChannelDefinition unbounded(String channel) {
|
||||
return () -> channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lower-case channel identifier.
|
||||
*
|
||||
* @return channel identifier
|
||||
*/
|
||||
String getChannel();
|
||||
|
||||
/**
|
||||
* Returns the smallest value advertised by this definition. The default is {@code 1}.
|
||||
*
|
||||
* @return minimum value
|
||||
*/
|
||||
default int getMinimumValue() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largest value advertised by this definition. The default is {@link java.lang.Integer#MAX_VALUE}.
|
||||
*
|
||||
* @return maximum value
|
||||
*/
|
||||
default int getMaximumValue() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a value is already valid without normalization.
|
||||
*
|
||||
* @param value value to check
|
||||
* @return true when the value is valid
|
||||
*/
|
||||
default boolean isValueValid(int value) {
|
||||
return value >= getMinimumValue() && value <= getMaximumValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a stored value to the value used by the structure definition.
|
||||
*
|
||||
* @param value stored value
|
||||
* @return normalized value
|
||||
*/
|
||||
default int normalizeValue(int value) {
|
||||
return Math.max(getMinimumValue(), Math.min(getMaximumValue(), value));
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,16 @@ import net.minecraft.block.Block;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface ICustomBlockSetting {
|
||||
/**
|
||||
* Default block setting calls {@link World#setBlock(int x, int y, int z, Block block, int meta, int updateType)} like:
|
||||
* {@code world.setBlock(x,y,z,this/block,meta,2)} where updateType 2 means to update lighting and stuff
|
||||
*
|
||||
* @param world world that should be affected
|
||||
* @param x x position to set
|
||||
* @param y y position to set
|
||||
* @param z z position to set
|
||||
* @param meta required meta
|
||||
*/
|
||||
void setBlock(World world, int x, int y, int z, int meta);
|
||||
|
||||
/**
|
||||
* Default block setting calls {@link World#setBlock(int x, int y, int z, Block block, int meta, int updateType)}
|
||||
* like: {@code world.setBlock(x,y,z,this/block,meta,2)} where updateType 2 means to update lighting and stuff
|
||||
*
|
||||
* @param world world that should be affected
|
||||
* @param x x position to set
|
||||
* @param y y position to set
|
||||
* @param z z position to set
|
||||
* @param meta required meta
|
||||
*/
|
||||
void setBlock(World world, int x, int y, int z, int meta);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import com.gtnewhorizon.structurelib.util.InventoryIterable;
|
||||
import com.gtnewhorizon.structurelib.util.InventoryUtility;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
|
||||
/**
|
||||
* Represent an item source. Take only, cannot be placed back.
|
||||
*/
|
||||
public interface IItemSource {
|
||||
|
||||
/**
|
||||
* Retrieve some amount of items from this item source matching given filter.
|
||||
* <p>
|
||||
* We probably don't need to get a lot of items from this method, so I don't care about the overhead of storing all
|
||||
* fetched items even if downstream code doesn't need it
|
||||
*
|
||||
* @param predicate filtering predicate. should return true for ItemStacks that should be added to result collection
|
||||
* @param simulate whether to actually commit the modification. true for dry run, false otherwise.
|
||||
* @param count how many items to extract.
|
||||
* @return A nonnull map reflecting the result of extraction. Note this map is NBT and metadata aware.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<ItemStack, Integer> take(Predicate<ItemStack> predicate, boolean simulate, int count);
|
||||
|
||||
/**
|
||||
* Take exactly one item that matches the predicate.
|
||||
*
|
||||
* @param predicate filtering predicate. should return true for ItemStacks that should be added to result collection
|
||||
* @param simulate whether to actually commit the modification. true for dry run, false otherwise.
|
||||
* @return item stack, or null if none matches
|
||||
* @throws IllegalArgumentException if given predicate is null.
|
||||
*/
|
||||
default ItemStack takeOne(Predicate<ItemStack> predicate, boolean simulate) {
|
||||
if (predicate == null) throw new IllegalArgumentException();
|
||||
Map<ItemStack, Integer> take = take(predicate, simulate, 1);
|
||||
return take.isEmpty() ? null : take.keySet().iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take exactly count amount of items matching predicate.
|
||||
*
|
||||
* @param predicate filtering predicate. should return true for ItemStacks that should be added to result collection
|
||||
* @param simulate whether to actually commit the modification. true for dry run, false otherwise.
|
||||
* @param count how many items to extract.
|
||||
* @return true if at least count amount of items can be/is taken.
|
||||
* @throws IllegalArgumentException if given predicate is null
|
||||
*/
|
||||
default boolean takeAll(Predicate<ItemStack> predicate, boolean simulate, int count) {
|
||||
// fast path for 1 item take requests
|
||||
if (count == 1) return takeOne(predicate, simulate) != null;
|
||||
Map<ItemStack, Integer> have = take(predicate, true, count);
|
||||
if (have.values().stream().mapToInt(Integer::intValue).sum() < count) return false;
|
||||
take(predicate, simulate, count);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take exactly one item. ItemStack given must have a stack size of 1.
|
||||
* <p>
|
||||
* This is meant to be an optimized version of {@link #takeOne(Predicate, boolean)}. Implementations that support
|
||||
* item storage indexed by ItemStack can use this to speed up calculation.
|
||||
*
|
||||
* @param stack what item to extract. The NBT tag of this ItemStack is siginifcant and will be considered during
|
||||
* matching. must have a stack size of 1.
|
||||
* @param simulate whether to actually commit the modification. true for dry run, false otherwise.
|
||||
* @return true if found. false otherwise
|
||||
* @throws IllegalArgumentException if given stack is invalid, or has a stack size other than 1.
|
||||
*/
|
||||
default boolean takeOne(ItemStack stack, boolean simulate) {
|
||||
if (stack == null || stack.getItem() == null || stack.stackSize != 1) throw new IllegalArgumentException();
|
||||
ItemStack took = takeOne(ItemStackPredicate.from(stack, NBTMode.EXACT), simulate);
|
||||
return took != null && took.stackSize > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take some item. Will not take item if it cannot find enough items to take.
|
||||
* <p>
|
||||
* This is meant to be an optimized version of {@link #takeOne(Predicate, boolean)}. Implementations that support
|
||||
* indexing off ItemStack can use this to speed up calculation.
|
||||
*
|
||||
* @param stack what item and how many to extract. The NBT tag of this ItemStack is siginifcant and will be
|
||||
* considered during matching
|
||||
* @param simulate whether to actually commit the modification. true for dry run, false otherwise.
|
||||
* @return true if enough item can be/has been extracted. false otherwise
|
||||
* @throws IllegalArgumentException if given stack is invalid
|
||||
*/
|
||||
default boolean takeAll(ItemStack stack, boolean simulate) {
|
||||
if (stack == null || stack.getItem() == null) throw new IllegalArgumentException();
|
||||
// fast path for 1 item take requests
|
||||
if (stack.stackSize == 1) return takeOne(stack, simulate);
|
||||
ItemStackPredicate predicate = ItemStackPredicate.from(stack, NBTMode.EXACT);
|
||||
Map<ItemStack, Integer> have = take(predicate, true, stack.stackSize);
|
||||
if (have.values().stream().mapToInt(Integer::intValue).sum() < stack.stackSize) return false;
|
||||
if (simulate) return true;
|
||||
take(predicate, false, stack.stackSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an IItemSource from inventories associated with given player.
|
||||
* <p>
|
||||
* This will be backed by {@link InventoryUtility}
|
||||
*/
|
||||
static IItemSource fromPlayer(EntityPlayerMP player) {
|
||||
return new IItemSource() {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<ItemStack, Integer> take(Predicate<ItemStack> p, boolean s, int c) {
|
||||
return InventoryUtility.takeFromInventory(player, p, s, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeOne(ItemStack stack, boolean simulate) {
|
||||
if (stack == null || stack.getItem() == null || stack.stackSize != 1)
|
||||
throw new IllegalArgumentException();
|
||||
return InventoryUtility.takeFromInventory(player, stack, simulate) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeAll(ItemStack stack, boolean simulate) {
|
||||
return InventoryUtility.takeFromInventory(player, stack, simulate) == stack.stackSize;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an IItemSource from given inventory
|
||||
* <p>
|
||||
* This will be backed by {@link InventoryUtility}
|
||||
*/
|
||||
static IItemSource fromInventory(IInventory inv) {
|
||||
return (p, s, c) -> InventoryUtility.takeFromInventory(new InventoryIterable<>(inv), p, s, c, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
public interface ISpecialItemBlock {
|
||||
|
||||
int getItemMetaFromBlockMeta(Block block, int blockMeta);
|
||||
}
|
||||
@@ -1,172 +1,494 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded;
|
||||
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.DEBUG_MODE;
|
||||
import com.gtnewhorizon.structurelib.StructureLib;
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
|
||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||
|
||||
/**
|
||||
* This is the structure definition of your multi. You will have one of these for each multi.
|
||||
*
|
||||
* <h2>Cache the instances of this class</h2>
|
||||
* <p>
|
||||
* You will construct this definition once per multi, either at class load or at first call to your variant of
|
||||
* {@code getStructureDefinition()}. You can use a {@link ClassValue ClassValue<IStructureDefinition<?>>} if you
|
||||
* have a template for subclass. You can also use {@link StructureUtility#defer(Function) defer()} or its overloads for
|
||||
* simpler cases, but it is slightly less performant as it will allocate new objects on each structure check, whereas
|
||||
* {@link ClassValue} will allocate once and cache it onwards.
|
||||
* <p>
|
||||
* <h2>Constructing</h2>
|
||||
* <p>
|
||||
* {@linkplain IStructureDefinition} is constructed using {@link IStructureDefinition#builder()}
|
||||
* <p>
|
||||
* Builder has two main methods:
|
||||
* <ul>
|
||||
* <li>{@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addShape(String, String[][])
|
||||
* addShape()} is a 2d string array describing the layout of structure. You can optionally use
|
||||
* `StructureUtility#transpose()` to transpose the array to make the structure look nicer in source code.
|
||||
* <li>{@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addElement(Character, IStructureElement)
|
||||
* addElement()}. It takes a char identifier (mapped to a char in any of
|
||||
* {@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addShape(String, String[][]) addShape()}
|
||||
* calls) and a {@link IStructureElement}.
|
||||
* </ul>
|
||||
* <h2>Position A, B and C</h2>
|
||||
*
|
||||
* Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped.
|
||||
* <ul>
|
||||
* <li>The "A" position would be the number of blocks on the left side of the controller, not counting controller
|
||||
* itself.</li>
|
||||
* <li>The "B" position would be the number of blocks on the top side of the controller, not counting controller
|
||||
* itself.</li>
|
||||
* <li>The "C" position would be the number of blocks between you and controller, not counting controller itself.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <T> Type of the context object. Usually this will be the multiblock controller.
|
||||
*/
|
||||
public interface IStructureDefinition<T> {
|
||||
/**
|
||||
* Used internally
|
||||
*
|
||||
* @param name same name as for other methods here
|
||||
* @return the array of elements to process
|
||||
*/
|
||||
IStructureElement<T>[] getStructureFor(String name);
|
||||
|
||||
default boolean check(T object, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ,
|
||||
int basePositionA, int basePositionB, int basePositionC,
|
||||
boolean forceCheckAllBlocks) {
|
||||
return iterate(object, null, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
||||
basePositionA, basePositionB, basePositionC, false, forceCheckAllBlocks);
|
||||
}
|
||||
/**
|
||||
* Returns channel definitions declared by this structure.
|
||||
*
|
||||
* @return an immutable or otherwise read-only channel collection
|
||||
*/
|
||||
default Collection<IChannelDefinition> getChannelDefinitions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
default boolean hints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ,
|
||||
int basePositionA, int basePositionB, int basePositionC) {
|
||||
return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
||||
basePositionA, basePositionB, basePositionC, true, null);
|
||||
}
|
||||
/**
|
||||
* Returns channel definitions explicitly added by the structure builder.
|
||||
*
|
||||
* @return explicitly declared channel definitions
|
||||
*/
|
||||
default Collection<IChannelDefinition> getExplicitChannelDefinitions() {
|
||||
return getChannelDefinitions();
|
||||
}
|
||||
|
||||
default boolean build(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ,
|
||||
int basePositionA, int basePositionB, int basePositionC) {
|
||||
return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
||||
basePositionA, basePositionB, basePositionC, false, null);
|
||||
}
|
||||
/**
|
||||
* Finds a channel definition by identifier.
|
||||
*
|
||||
* @param channel channel identifier
|
||||
* @return channel definition, or {@code null} when it is not declared
|
||||
*/
|
||||
default IChannelDefinition getChannelDefinition(String channel) {
|
||||
for (IChannelDefinition definition : getChannelDefinitions()) {
|
||||
if (definition.getChannel().equals(channel)) return definition;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
default boolean buildOrHints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ,
|
||||
int basePositionA, int basePositionB, int basePositionC,
|
||||
boolean hintsOnly) {
|
||||
return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
||||
basePositionA, basePositionB, basePositionC, hintsOnly, null);
|
||||
}
|
||||
/**
|
||||
* Reads one channel using the standard trigger-item channel semantics.
|
||||
*
|
||||
* @param trigger trigger item containing channel data
|
||||
* @param channel channel identifier
|
||||
* @return stored channel data, or the trigger stack size when absent
|
||||
*/
|
||||
default int getChannelData(ItemStack trigger, String channel) {
|
||||
return ChannelDataAccessor.getChannelData(trigger, channel);
|
||||
}
|
||||
|
||||
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<T>[] elements, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ,
|
||||
int basePositionA, int basePositionB, int basePositionC,
|
||||
boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||
if (world.isRemote ^ hintsOnly) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Used internally
|
||||
* <p>
|
||||
* Might, but is not required, to throw {@link java.util.NoSuchElementException} if the given structure piece is not
|
||||
* found.
|
||||
*
|
||||
* @param name same name as for other methods here
|
||||
* @return the array of elements to process
|
||||
* @throws java.util.NoSuchElementException if the given structure piece is not found and the moon phase perfectly
|
||||
* matches
|
||||
*/
|
||||
IStructureElement<T>[] getStructureFor(String name);
|
||||
|
||||
//change base position to base offset
|
||||
basePositionA = -basePositionA;
|
||||
basePositionB = -basePositionB;
|
||||
basePositionC = -basePositionC;
|
||||
boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC);
|
||||
|
||||
int[] abc = new int[]{basePositionA, basePositionB, basePositionC};
|
||||
int[] xyz = new int[3];
|
||||
/**
|
||||
* Run a structure check.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @param forceCheckAllBlocks check all location even if chunk not currently loaded.
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
default boolean check(T object, String piece, World world, ExtendedFacing extendedFacing, int basePositionX,
|
||||
int basePositionY, int basePositionZ, int basePositionA, int basePositionB, int basePositionC,
|
||||
boolean forceCheckAllBlocks) {
|
||||
return iterate(
|
||||
object,
|
||||
null,
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
false,
|
||||
forceCheckAllBlocks);
|
||||
}
|
||||
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||
for (IStructureElement<T> element : elements) {
|
||||
if (element.isNavigating()) {
|
||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
||||
} else {
|
||||
extendedFacing.getWorldOffset(abc, xyz);
|
||||
xyz[0] += basePositionX;
|
||||
xyz[1] += basePositionY;
|
||||
xyz[2] += basePositionZ;
|
||||
/**
|
||||
* Spawn hint particles. Should not be called on server side.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
default boolean hints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
|
||||
int basePositionC) {
|
||||
return iterate(
|
||||
object,
|
||||
trigger,
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
true,
|
||||
null);
|
||||
}
|
||||
|
||||
if (world.blockExists(xyz[0], xyz[1], xyz[2])) {
|
||||
if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) {
|
||||
if (DEBUG_MODE) {
|
||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " +
|
||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (DEBUG_MODE) {
|
||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " +
|
||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
abc[0] += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (IStructureElement<T> element : elements) {
|
||||
if (element.isNavigating()) {
|
||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
||||
} else {
|
||||
extendedFacing.getWorldOffset(abc, xyz);
|
||||
xyz[0] += basePositionX;
|
||||
xyz[1] += basePositionY;
|
||||
xyz[2] += basePositionZ;
|
||||
/**
|
||||
* Build the multi in creative mode on a best-effort basis. Does not guarantee the structure is correct after a
|
||||
* successful call.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param trigger The trigger item that contains channel data.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @return true if sucessful, false otherwise
|
||||
*/
|
||||
default boolean build(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
|
||||
int basePositionC) {
|
||||
return iterate(
|
||||
object,
|
||||
trigger,
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
false,
|
||||
null);
|
||||
}
|
||||
|
||||
if (world.blockExists(xyz[0], xyz[1], xyz[2])) {
|
||||
if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) {
|
||||
if (DEBUG_MODE) {
|
||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " +
|
||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (DEBUG_MODE) {
|
||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " +
|
||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
||||
}
|
||||
}
|
||||
abc[0] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (hintsOnly) {
|
||||
for (IStructureElement<T> element : elements) {
|
||||
if (element.isNavigating()) {
|
||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
||||
} else {
|
||||
extendedFacing.getWorldOffset(abc, xyz);
|
||||
xyz[0] += basePositionX;
|
||||
xyz[1] += basePositionY;
|
||||
xyz[2] += basePositionZ;
|
||||
/**
|
||||
* Call {@link #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} if
|
||||
* {@code hintsOnly}, call
|
||||
* {@link #hints(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} otherwise.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param trigger The trigger item that contains channel data.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @param hintsOnly whether to spawn hints or do creative build.
|
||||
* @return true if sucessful, false otherwise
|
||||
* @see #hints(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
|
||||
* @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
|
||||
*/
|
||||
default boolean buildOrHints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
|
||||
int basePositionC, boolean hintsOnly) {
|
||||
return iterate(
|
||||
object,
|
||||
trigger,
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
hintsOnly,
|
||||
null);
|
||||
}
|
||||
|
||||
element.spawnHint(object, world, xyz[0], xyz[1], xyz[2], trigger);
|
||||
/**
|
||||
* Cause a survival build.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param trigger The trigger item that contains channel data.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @param elementBudget build up to this many elements
|
||||
* @param source from where to drain resource
|
||||
* @param actor source of action
|
||||
* @param check whether {@link IStructureElement#check(Object, World, int, int, int)} should be called if
|
||||
* anything would be placed. use with caution. if in doubt, call
|
||||
* {@link #check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}
|
||||
* after this call and set this parameter to false
|
||||
* @return number of elements built, or -1 if structure done
|
||||
* @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
|
||||
*/
|
||||
@Deprecated
|
||||
default int survivalBuild(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
|
||||
int basePositionC, int elementBudget, IItemSource source, EntityPlayerMP actor, boolean check) {
|
||||
EntityPlayer realActor;
|
||||
if (actor == null) {
|
||||
realActor = ISurvivalConstructable.__get_player();
|
||||
if (realActor == null) throw new IllegalArgumentException();
|
||||
} else {
|
||||
realActor = actor;
|
||||
}
|
||||
return survivalBuild(
|
||||
object,
|
||||
trigger,
|
||||
piece,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
elementBudget,
|
||||
ISurvivalBuildEnvironment.create(source, realActor),
|
||||
check);
|
||||
}
|
||||
|
||||
abc[0] += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (IStructureElement<T> element : elements) {
|
||||
if (element.isNavigating()) {
|
||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
||||
} else {
|
||||
extendedFacing.getWorldOffset(abc, xyz);
|
||||
xyz[0] += basePositionX;
|
||||
xyz[1] += basePositionY;
|
||||
xyz[2] += basePositionZ;
|
||||
/**
|
||||
* Cause a survival build.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param trigger The trigger item that contains channel data.
|
||||
* @param piece the structure piece's string identifier.
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @param elementBudget build up to this many elements
|
||||
* @param env build environment.
|
||||
* @param check whether {@link IStructureElement#check(Object, World, int, int, int)} should be called if
|
||||
* anything would be placed. use with caution. if in doubt, call
|
||||
* {@link IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}
|
||||
* after this call and set this parameter to false
|
||||
* @return number of elements built, or -1 if structure done
|
||||
* @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
|
||||
*/
|
||||
default int survivalBuild(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
||||
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
|
||||
int basePositionC, int elementBudget, ISurvivalBuildEnvironment env, boolean check) {
|
||||
SurvivalBuildStructureWalker<T> walker = new SurvivalBuildStructureWalker<>(
|
||||
object,
|
||||
trigger,
|
||||
elementBudget,
|
||||
env,
|
||||
this,
|
||||
piece,
|
||||
extendedFacing,
|
||||
new int[] { basePositionA, basePositionB, basePositionC },
|
||||
check);
|
||||
StructureUtility.iterateV2(
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
walker,
|
||||
"survivalBuild");
|
||||
return walker.getBuilt();
|
||||
}
|
||||
|
||||
if (world.blockExists(xyz[0], xyz[1], xyz[2])) {
|
||||
element.placeBlock(object, world, xyz[0], xyz[1], xyz[2], trigger);
|
||||
}
|
||||
abc[0] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default void iterate(String piece, World world, ExtendedFacing extendedFacing, int basePositionX, int basePositionY,
|
||||
int basePositionZ, int basePositionA, int basePositionB, int basePositionC, IStructureWalker<T> predicate) {
|
||||
StructureUtility.iterateV2(
|
||||
getStructureFor(piece),
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
predicate,
|
||||
"customIterate");
|
||||
}
|
||||
|
||||
static <T> StructureDefinition.Builder<T> builder() {
|
||||
return StructureDefinition.builder();
|
||||
}
|
||||
/**
|
||||
* Low level utility.
|
||||
*
|
||||
* @param object context object. usually multiblock controller.
|
||||
* @param trigger The trigger item that contains channel data.
|
||||
* @param elements the structure piece
|
||||
* @param world the world object this check takes place in.
|
||||
* @param extendedFacing the current structure's orientation.
|
||||
* @param basePositionX X location of the structure
|
||||
* @param basePositionY Y location of the structure
|
||||
* @param basePositionZ Z location of the structure
|
||||
* @param basePositionA see class javadoc
|
||||
* @param basePositionB see class javadoc
|
||||
* @param basePositionC see class javadoc
|
||||
* @param hintsOnly whether to spawn hints or do creative build. no effect if
|
||||
* {@code checkBlocksIfNotNullForceCheckAllIfTrue} is not null.
|
||||
* @param checkBlocksIfNotNullForceCheckAllIfTrue creative build or spawn hints if null. Force check not loaded
|
||||
* chunk if true. Check only loaded chunk is false.
|
||||
* @return true if iteration completed successfully.
|
||||
* @param <T> type of context object.
|
||||
*/
|
||||
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<T>[] elements, World world,
|
||||
ExtendedFacing extendedFacing, int basePositionX, int basePositionY, int basePositionZ, int basePositionA,
|
||||
int basePositionB, int basePositionC, boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||
if (!world.isRemote && hintsOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
||||
boolean success;
|
||||
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||
success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
|
||||
"check");
|
||||
} else {
|
||||
success = StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||
"check force");
|
||||
}
|
||||
if (StructureLibAPI.isDebugEnabled() && success) {
|
||||
StructureLib.LOGGER
|
||||
.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
|
||||
}
|
||||
return success;
|
||||
} else {
|
||||
if (hintsOnly) {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.spawnHint(object, world, x, y, z, trigger);
|
||||
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
|
||||
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
|
||||
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
"spawnHint");
|
||||
} else {
|
||||
StructureUtility.iterateV2(
|
||||
elements,
|
||||
world,
|
||||
extendedFacing,
|
||||
basePositionX,
|
||||
basePositionY,
|
||||
basePositionZ,
|
||||
basePositionA,
|
||||
basePositionB,
|
||||
basePositionC,
|
||||
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||
e.placeBlock(object, world, x, y, z, trigger);
|
||||
return true;
|
||||
}),
|
||||
"placeBlock");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of builder.
|
||||
*
|
||||
* @param <T> type of context object
|
||||
*/
|
||||
static <T> StructureDefinition.Builder<T> builder() {
|
||||
return StructureDefinition.builder();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,391 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
|
||||
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
|
||||
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||
|
||||
/**
|
||||
* Use StructureUtility to instantiate
|
||||
* Use StructureUtility to instantiate. These are the building blocks for your {@link IStructureDefinition}. It
|
||||
* represents what a particular element can be and how this particular element should be autoplaced.
|
||||
*/
|
||||
public interface IStructureElement<T> {
|
||||
boolean check(T t, World world, int x, int y, int z);
|
||||
|
||||
boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||
boolean check(T t, World world, int x, int y, int z);
|
||||
|
||||
boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||
/**
|
||||
* Returns the channels used by this element.
|
||||
*
|
||||
* @return lower-case channel identifiers
|
||||
*/
|
||||
default Collection<String> getChannels() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
default int getStepA() {
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* Pure (stateless and side effect free) function to test if current block could be valid. Used to give user hints
|
||||
* about incorrectly placed blocks given a controller and trigger item. If couldBeValid(...) == false for a set of
|
||||
* channels, with those same tiers, check(...) == false. Not required, defaults to true which is always safe but
|
||||
* won't give the user error hints.
|
||||
*/
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default int getStepB() {
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Returns a human-readable description of what block(s) this element accepts. Used for diagnostic messages when
|
||||
* structure checks fail (e.g. "expected Input Bus at (1, 2, 3)").
|
||||
* <p>
|
||||
* Unlike {@link #getBlocksToPlace}, which returns the full set of concrete block variants for autoplacing (e.g.
|
||||
* every tier of Input Bus), this method returns <b>category-level</b> descriptions suitable for display to the
|
||||
* player (e.g. just "Input Bus"). It also requires no parameters, no world, trigger item, or environment, making it
|
||||
* usable in contexts where those are unavailable.
|
||||
* <p>
|
||||
* Implementations should return <b>lang keys</b> (e.g. {@code "tile.blockGlass.name"}) rather than pre-translated
|
||||
* display names. The caller is responsible for translating these keys on the client side via
|
||||
* {@code StatCollector.translateToLocal()}. If a key has no matching translation, {@code translateToLocal} returns
|
||||
* the input string unchanged, so pre-translated fallback strings also work.
|
||||
*
|
||||
* @param context the context object (e.g. the multiblock controller), used to determine state-dependent
|
||||
* descriptions
|
||||
* @return a list of accepted block descriptions as lang keys (or display names as fallback), or null if no
|
||||
* description is available
|
||||
*/
|
||||
@Nullable
|
||||
default List<String> getDescription(T context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default int getStepC() {
|
||||
return 0;
|
||||
}
|
||||
boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||
|
||||
default boolean resetA() {
|
||||
return false;
|
||||
}
|
||||
boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||
|
||||
default boolean resetB() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Try place the block by taking resource from given IItemSource.
|
||||
* <p>
|
||||
* You might want to use
|
||||
* {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
|
||||
* or its overloads to implement this.
|
||||
*
|
||||
* @param trigger trigger item
|
||||
* @param s drain resources from this place
|
||||
* @param actor source of action. for very critical errors you can also just send the error messages here,
|
||||
* bypassing any filter that chatter might have.
|
||||
* @param chatter send error messages here. Caller will choose an appropriate way to forward it to player if the
|
||||
* other fallbacks also fails.
|
||||
* @deprecated caller should call the non deprecated overload. implementor for reusable {@link IStructureElement}
|
||||
* should still implement this. implementor for private implementations can freely ignore this as long
|
||||
* as you know the caller will not call this overload.
|
||||
*/
|
||||
@Deprecated
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
if (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
|
||||
if (StructureLibAPI.isDebugEnabled()) LOGGER.error(
|
||||
"Default implementation of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
|
||||
getClass().getName());
|
||||
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
|
||||
default boolean resetC() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* A simplified version of survivalPlaceBlock. Return null if not implemented.
|
||||
* <p>
|
||||
* <b>BY OVERRIDING THIS TO NULL, YOU ACKNOWLEDGE THAT THE {@link #check(Object, World, int, int, int)} OF THIS
|
||||
* CLASS IS SIDE EFFECT FREE AND CAN HAVE ITS CHECK CALLED WITHOUT BREAKING ANYTHING. OTHERWISE OVERRIDE THE
|
||||
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)} TO PROVIDE A
|
||||
* SIDEEFFECT FREE {@link PlaceResult#SKIP}.</b>
|
||||
* <p>
|
||||
* It should be noticed that this filter is only advisory and the actual
|
||||
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)} or
|
||||
* {@link #check(Object, World, int, int, int)} are free to reject/accept/place blocks not contained in this list,
|
||||
* e.g. when the element can be placed anywhere in the structure, but no more than 3 overall; when the structure
|
||||
* element cannot know all blocks it will accept at the time of calling; when this is backed by a legacy
|
||||
* {@link IStructureElement} that just didn't implement this API.
|
||||
*
|
||||
* @param trigger trigger item
|
||||
* @param env autoplacing environment
|
||||
* @return null if not implemented, otherwise an instance describing what kind of blocks will be placed by
|
||||
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
|
||||
*/
|
||||
@Nullable
|
||||
default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default boolean isNavigating() {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Try place the block by taking resource from given {@link IItemSource}.
|
||||
* <p>
|
||||
* You might want to use
|
||||
* {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
|
||||
* or its overloads to implement this.
|
||||
* <p>
|
||||
* The default implementation provided will
|
||||
* <ol>
|
||||
* <li>Get {@link BlocksToPlace} via
|
||||
* {@link #getBlocksToPlace(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}. If not null
|
||||
* <ol>
|
||||
* <li>call {@link #check(Object, World, int, int, int)}. If this returns {@code true}, {@link PlaceResult#SKIP}
|
||||
* will be returned without further action.</li>
|
||||
* <li>Use the {@link BlocksToPlace} retrieved earlier and passed in {@link IItemSource} to determine an item to
|
||||
* place</li>
|
||||
* <li>Hand over control to
|
||||
* {@link StructureUtility#survivalPlaceBlock(ItemStack, NBTMode, NBTTagCompound, boolean, World, int, int, int, IItemSource, EntityPlayer, Consumer)}</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>Call legacy
|
||||
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)} if
|
||||
* {@code env.getActor()} contains an {@link EntityPlayerMP}</li>
|
||||
* <li>Emit warning under debug mode (or throw exception under panic mode), then return
|
||||
* {@link PlaceResult#SKIP}</li>
|
||||
* </ol>
|
||||
* It should be noticed that the default implementation is likely to cause unexpected issues if the
|
||||
* {@link #check(Object, World, int, int, int)} is not side effect free, or is not idempotent. E.g. the
|
||||
* {@link #check(Object, World, int, int, int)} will add current coord to a {@link java.util.List}. As this method
|
||||
* might be invoked at a location that is already accepted, this will cause the machine to have multiple of this
|
||||
* coord in its list.
|
||||
*
|
||||
* @param trigger trigger item
|
||||
* @param env autoplacing environment
|
||||
*/
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||
IItemSource source = env.getSource();
|
||||
EntityPlayer actor = env.getActor();
|
||||
Consumer<IChatComponent> chatter = env.getChatter();
|
||||
if (e != null) {
|
||||
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||
if (e.getStacks() == null) {
|
||||
ItemStack taken = source.takeOne(e.getPredicate(), true);
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
taken,
|
||||
NBTMode.EXACT,
|
||||
taken.stackTagCompound,
|
||||
true,
|
||||
world,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
source,
|
||||
actor,
|
||||
chatter);
|
||||
}
|
||||
for (ItemStack stack : e.getStacks()) {
|
||||
if (!source.takeOne(stack, true)) continue;
|
||||
return StructureUtility.survivalPlaceBlock(
|
||||
stack,
|
||||
NBTMode.EXACT,
|
||||
stack.stackTagCompound,
|
||||
true,
|
||||
world,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
source,
|
||||
actor,
|
||||
chatter);
|
||||
}
|
||||
return PlaceResult.REJECT;
|
||||
}
|
||||
if (actor instanceof EntityPlayerMP)
|
||||
return survivalPlaceBlock(t, world, x, y, z, trigger, source, (EntityPlayerMP) actor, chatter);
|
||||
if (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
|
||||
if (StructureLibAPI.isDebugEnabled()) LOGGER.info(
|
||||
"Fallback shim code of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
|
||||
getClass().getName());
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget the messed up class dependency graph for now. this is just so convenient.
|
||||
*/
|
||||
default IStructureElementNoPlacement<T> noPlacement() {
|
||||
return new IStructureElementNoPlacement<T>() {
|
||||
|
||||
@Override
|
||||
public boolean check(T t, World world, int x, int y, int z) {
|
||||
return IStructureElement.this.check(t, world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return IStructureElement.this.couldBeValid(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return IStructureElement.this.spawnHint(t, world, x, y, z, trigger);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> getDescription(T context) {
|
||||
return IStructureElement.this.getDescription(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
default int getStepA() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
default int getStepB() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default int getStepC() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
default boolean resetA() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean resetB() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean resetC() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean isNavigating() {
|
||||
return false;
|
||||
}
|
||||
|
||||
enum PlaceResult {
|
||||
/**
|
||||
* The element's check passed, the position is already valid. No placement is needed. In the walker: continue
|
||||
* iteration (no error, not counted as placed). In ofChain: short-circuits, remaining alternatives are skipped.
|
||||
*/
|
||||
SKIP,
|
||||
/**
|
||||
* The element cannot handle this position (occupied, missing resources, etc.). In the walker: stop iteration
|
||||
* with error particles. In ofChain: try next alternative.
|
||||
*/
|
||||
REJECT,
|
||||
/**
|
||||
* This element has no survivalPlaceBlock implementation (default fallback), or otherwise cannot place but is
|
||||
* not an error condition. In the walker: continue iteration silently (like SKIP). In ofChain: try next
|
||||
* alternative (like REJECT), so a better-suited element can handle it.
|
||||
*/
|
||||
REJECT_CONTINUE,
|
||||
/**
|
||||
* Autoplace cannot proceed within this tick. To proceed further would require stopping the placement and wait
|
||||
* for next round.
|
||||
*/
|
||||
STOP,
|
||||
/**
|
||||
* Element placed successfully.
|
||||
*/
|
||||
ACCEPT,
|
||||
/**
|
||||
* Combination of ACCEPT and STOP.
|
||||
* <p>
|
||||
* Element placed successfully. To proceed further would require stopping the placement and wait for next round.
|
||||
*/
|
||||
ACCEPT_STOP;
|
||||
}
|
||||
|
||||
final class BlocksToPlace {
|
||||
|
||||
public static final BlocksToPlace errored = createEmpty();
|
||||
private final Predicate<ItemStack> predicate;
|
||||
private final Iterable<ItemStack> stacks;
|
||||
|
||||
public static BlocksToPlace createEmpty() {
|
||||
return new BlocksToPlace(s -> false, Collections.emptyList());
|
||||
}
|
||||
|
||||
public static BlocksToPlace create(ItemStack... stacks) {
|
||||
return create(Arrays.asList(stacks));
|
||||
}
|
||||
|
||||
public static BlocksToPlace create(Iterable<ItemStack> stacks) {
|
||||
Predicate<ItemStack> predicate = null;
|
||||
|
||||
for (ItemStack stack : stacks) {
|
||||
ItemStackPredicate p = ItemStackPredicate.from(stack, NBTMode.EXACT);
|
||||
if (predicate == null) predicate = p;
|
||||
else predicate = predicate.or(p);
|
||||
}
|
||||
|
||||
return new BlocksToPlace(predicate, stacks);
|
||||
}
|
||||
|
||||
public static BlocksToPlace create(Block block, int meta) {
|
||||
Item itemBlock = Item.getItemFromBlock(block);
|
||||
if (itemBlock instanceof ISpecialItemBlock) {
|
||||
meta = ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta);
|
||||
}
|
||||
return create(itemBlock, meta);
|
||||
}
|
||||
|
||||
public static BlocksToPlace create(Item item, int meta) {
|
||||
return new BlocksToPlace(
|
||||
ItemStackPredicate.from(item).setMeta(meta),
|
||||
Collections.singletonList(new ItemStack(item, 1, meta)));
|
||||
}
|
||||
|
||||
public static BlocksToPlace create(ItemStack itemStack) {
|
||||
return new BlocksToPlace(ItemStackPredicate.from(itemStack), Collections.singletonList(itemStack));
|
||||
}
|
||||
|
||||
public static BlocksToPlace create(Predicate<ItemStack> predicate) {
|
||||
return new BlocksToPlace(predicate, null);
|
||||
}
|
||||
|
||||
BlocksToPlace(Predicate<ItemStack> predicate, Iterable<ItemStack> stacks) {
|
||||
this.predicate = predicate;
|
||||
this.stacks = stacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the blocks to place filter. This is usually slower as it requires walking through inventories one by one.
|
||||
* <p>
|
||||
* Suitable for use with {@link IItemSource#takeOne(Predicate, boolean)}
|
||||
*
|
||||
* @return a predicate. never null.
|
||||
*/
|
||||
@Nonnull
|
||||
public Predicate<ItemStack> getPredicate() {
|
||||
return predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ItemStacks that this one is known to accept. Can be null if this info is not available at the time
|
||||
* this instance is constructed.
|
||||
* <p>
|
||||
* Suitable for use with {@link IItemSource#takeOne(ItemStack, boolean)}.
|
||||
* <p>
|
||||
* This should be equivalent to the predicate returned from {@link #getPredicate()}
|
||||
*/
|
||||
@Nullable
|
||||
public Iterable<ItemStack> getStacks() {
|
||||
return stacks;
|
||||
}
|
||||
}
|
||||
}
|
||||
+134
-28
@@ -1,41 +1,147 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Use StructureUtility to instantiate
|
||||
*/
|
||||
public interface IStructureElementChain<T> extends IStructureElement<T> {
|
||||
IStructureElement<T>[] fallbacks();
|
||||
|
||||
@Override
|
||||
default boolean check(T t, World world, int x, int y, int z) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.check(t, world, x, y, z)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
IStructureElement<T>[] fallbacks();
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
default boolean check(T t, World world, int x, int y, int z) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.check(t, world, x, y, z)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.couldBeValid(t, world, x, y, z, trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
default List<String> getDescription(T context) {
|
||||
Set<String> descriptions = new LinkedHashSet<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
List<String> desc = fallback.getDescription(context);
|
||||
if (desc != null) {
|
||||
descriptions.addAll(desc);
|
||||
}
|
||||
}
|
||||
return descriptions.isEmpty() ? null : new ArrayList<>(descriptions);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
Predicate<ItemStack> predicate = null;
|
||||
List<Iterable<ItemStack>> is = new ArrayList<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
BlocksToPlace e = fallback.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||
if (e == null) continue;
|
||||
if (predicate == null) predicate = e.getPredicate();
|
||||
else predicate = predicate.or(e.getPredicate());
|
||||
Iterable<ItemStack> stacks = e.getStacks();
|
||||
if (stacks != null) is.add(stacks);
|
||||
}
|
||||
if (predicate == null) return null;
|
||||
return new BlocksToPlace(predicate, Iterables.concat(is));
|
||||
}
|
||||
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
boolean allContinue = true;
|
||||
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
PlaceResult result = fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, bufferedNoise::add);
|
||||
switch (result) {
|
||||
case REJECT_CONTINUE:
|
||||
break;
|
||||
case REJECT:
|
||||
allContinue = false;
|
||||
break;
|
||||
case SKIP:
|
||||
bufferedNoise.forEach(chatter);
|
||||
return PlaceResult.SKIP;
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// dump all that noise back into upstream
|
||||
bufferedNoise.forEach(chatter);
|
||||
return allContinue ? PlaceResult.REJECT_CONTINUE : PlaceResult.REJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
boolean allContinue = true;
|
||||
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||
for (IStructureElement<T> fallback : fallbacks()) {
|
||||
PlaceResult result = fallback
|
||||
.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
|
||||
switch (result) {
|
||||
case REJECT_CONTINUE:
|
||||
break;
|
||||
case REJECT:
|
||||
allContinue = false;
|
||||
break;
|
||||
case SKIP:
|
||||
bufferedNoise.forEach(env.getChatter());
|
||||
return PlaceResult.SKIP;
|
||||
default:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// dump all that noise back into upstream
|
||||
bufferedNoise.forEach(env.getChatter());
|
||||
return allContinue ? PlaceResult.REJECT_CONTINUE : PlaceResult.REJECT;
|
||||
}
|
||||
}
|
||||
+30
-8
@@ -1,16 +1,38 @@
|
||||
package com.gtnewhorizon.structurelib.structure;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IChatComponent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface IStructureElementCheckOnly<T> extends IStructureElement<T> {
|
||||
@Override
|
||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||
AutoPlaceEnvironment env) {
|
||||
return PlaceResult.REJECT_CONTINUE;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user