mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-12 16:27:36 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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 eol=lf
|
||||||
* text=auto
|
|
||||||
|
|
||||||
# Custom for Visual Studio
|
*.[jJ][aA][rR] binary
|
||||||
*.cs diff=csharp
|
|
||||||
*.sln merge=union
|
|
||||||
*.csproj merge=union
|
|
||||||
*.vbproj merge=union
|
|
||||||
*.fsproj merge=union
|
|
||||||
*.dbproj merge=union
|
|
||||||
|
|
||||||
# Standard to msysgit
|
*.[pP][nN][gG] binary
|
||||||
*.doc diff=astextplain
|
*.[jJ][pP][gG] binary
|
||||||
*.DOC diff=astextplain
|
*.[jJ][pP][eE][gG] binary
|
||||||
*.docx diff=astextplain
|
*.[gG][iI][fF] binary
|
||||||
*.DOCX diff=astextplain
|
*.[tT][iI][fF] binary
|
||||||
*.dot diff=astextplain
|
*.[tT][iI][fF][fF] binary
|
||||||
*.DOT diff=astextplain
|
*.[iI][cC][oO] binary
|
||||||
*.pdf diff=astextplain
|
*.[sS][vV][gG] text
|
||||||
*.PDF diff=astextplain
|
*.[eE][pP][sS] binary
|
||||||
*.rtf diff=astextplain
|
*.[xX][cC][fF] binary
|
||||||
*.RTF diff=astextplain
|
|
||||||
|
*.[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
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
directory="run/crash-reports"
|
|
||||||
if [ -d $directory ]; then
|
|
||||||
echo "Crash reports detected:"
|
|
||||||
cat $directory/*
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "No crash reports detected"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
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
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
|
||||||
|
|
||||||
name: Build and test
|
name: Build and test
|
||||||
|
|
||||||
@@ -11,35 +9,5 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
build-and-test:
|
||||||
runs-on: ubuntu-latest
|
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master
|
||||||
steps:
|
secrets: inherit
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up JDK 8
|
|
||||||
uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
java-version: '8'
|
|
||||||
distribution: 'adopt'
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
|
||||||
run: chmod +x gradlew
|
|
||||||
|
|
||||||
- name: Setup the workspace
|
|
||||||
run: ./gradlew setupCIWorkspace
|
|
||||||
|
|
||||||
- name: Build the mod
|
|
||||||
run: ./gradlew build
|
|
||||||
|
|
||||||
- name: Run server for 1 minute
|
|
||||||
run: |
|
|
||||||
mkdir run
|
|
||||||
echo "eula=true" > run/eula.txt
|
|
||||||
timeout 10 ./gradlew runServer || true
|
|
||||||
|
|
||||||
- name: Test no crashes happend
|
|
||||||
run: |
|
|
||||||
chmod +x .github/scripts/test-no-crash-reports.sh
|
|
||||||
.github/scripts/test-no-crash-reports.sh
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
name: Deploy Javadoc
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- feature/doc
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
javadoc:
|
||||||
|
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}@users.noreply.github.com"
|
||||||
|
git commit -m "Deploy javadoc from ref $TARGET_FOLDER"
|
||||||
|
git push
|
||||||
|
echo "::set-output name=hash::$(git rev-parse HEAD)"
|
||||||
|
env:
|
||||||
|
TARGET_FOLDER: ${{ github.ref_name }}
|
||||||
|
COMMIT_AUTHOR: ${{ github.pusher.name }}
|
||||||
|
COMMIT_EMAIL: ${{ github.pusher.email }}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
|
||||||
|
|
||||||
name: Release latest build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master, main ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set release version
|
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set up JDK 8
|
|
||||||
uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
java-version: '8'
|
|
||||||
distribution: 'adopt'
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
|
||||||
run: chmod +x gradlew
|
|
||||||
|
|
||||||
- name: Setup the workspace
|
|
||||||
run: ./gradlew setupCIWorkspace
|
|
||||||
|
|
||||||
- name: Build the mod
|
|
||||||
run: ./gradlew build
|
|
||||||
|
|
||||||
- name: Update latest tag
|
|
||||||
uses: "marvinpinto/action-automatic-releases@latest"
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
automatic_release_tag: "latest"
|
|
||||||
prerelease: false
|
|
||||||
title: "Latest build"
|
|
||||||
files: build/libs/*.jar
|
|
||||||
@@ -1,45 +1,14 @@
|
|||||||
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
|
||||||
|
|
||||||
name: Release tagged build
|
name: Release tagged build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags: [ '*' ]
|
||||||
- '*'
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
release-tags:
|
||||||
runs-on: ubuntu-latest
|
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master
|
||||||
steps:
|
secrets: inherit
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set release version
|
|
||||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set up JDK 8
|
|
||||||
uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
java-version: '8'
|
|
||||||
distribution: 'adopt'
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Grant execute permission for gradlew
|
|
||||||
run: chmod +x gradlew
|
|
||||||
|
|
||||||
- name: Setup the workspace
|
|
||||||
run: ./gradlew setupCIWorkspace
|
|
||||||
|
|
||||||
- name: Build the mod
|
|
||||||
run: ./gradlew build
|
|
||||||
|
|
||||||
- name: Release under current tag
|
|
||||||
uses: "marvinpinto/action-automatic-releases@latest"
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
automatic_release_tag: "${{ env.RELEASE_VERSION }}"
|
|
||||||
prerelease: false
|
|
||||||
title: "${{ env.RELEASE_VERSION }}"
|
|
||||||
files: build/libs/*.jar
|
|
||||||
@@ -26,3 +26,4 @@ whitelist.json
|
|||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
src/main/resources/mixins.*.json
|
src/main/resources/mixins.*.json
|
||||||
|
*.bat
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Any Github changes require admin approval
|
||||||
|
/.github/** @GTNewHorizons/admin
|
||||||
|
|
||||||
+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.
|
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`.
|
3. Add `structurelib` to your `mcmod.info`.
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
The project is developed using IDEA. Please manually configure the build to not delegate to gradle for textures to show up in dev.
|
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,42 @@
|
|||||||
|
javadoc {
|
||||||
|
javadocTool = javaToolchains.javadocToolFor {
|
||||||
|
// Last version with frames, but it also has search
|
||||||
|
languageVersion = JavaLanguageVersion.of(11)
|
||||||
|
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/javase/8/docs/api/'
|
||||||
|
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
|
||||||
|
addBooleanOption("-frames", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('javadocJar', Jar) {
|
||||||
|
dependsOn javadoc
|
||||||
|
archiveClassifier = 'javadoc'
|
||||||
|
from javadoc.destinationDir
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
archives javadocJar
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1045
-304
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,9 @@
|
|||||||
// Add your dependencies here
|
// Add your dependencies here
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api("com.github.GTNewHorizons:GTNHLib:0.0.10:dev")
|
||||||
|
runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.3.46-GTNH:dev")
|
||||||
|
|
||||||
|
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||||
|
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||||
}
|
}
|
||||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,78 +17,113 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## 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/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# 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\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# Resolve links: $0 may be a link
|
||||||
APP_BASE_NAME=`basename "$0"`
|
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##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# 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"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
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.
|
which java >/dev/null 2>&1 || 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
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@@ -105,84 +140,105 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# 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 or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# 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" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=$(save "$@")
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
|
||||||
cd "$(dirname "$0")"
|
# 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
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# 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" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
Vendored
+15
-23
@@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,10 +25,14 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
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.
|
@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"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@@ -37,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -51,7 +55,7 @@ goto fail
|
|||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
@@ -61,38 +65,26 @@ echo location of your Java installation.
|
|||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_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=%*
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@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%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
+4
-1
@@ -1,5 +1,8 @@
|
|||||||
// Add any additional repositiroes for your dependencies here
|
// Add any additional repositiroes for your dependencies here
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "GTNH"
|
||||||
|
url = "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
// RetroFuturaGradle
|
||||||
|
name "GTNH Maven"
|
||||||
|
url "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
|
||||||
|
allowInsecureProtocol = true
|
||||||
|
mavenContent {
|
||||||
|
includeGroup("com.gtnewhorizons.retrofuturagradle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'com.diffplug.blowdryerSetup' version '1.6.0'
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' // Provides java toolchains
|
||||||
|
}
|
||||||
|
|
||||||
|
blowdryerSetup {
|
||||||
|
repoSubfolder 'gtnhShared'
|
||||||
|
github('GTNewHorizons/ExampleMod1.7.10', 'tag', '0.2.0')
|
||||||
|
//devLocal '.' // Use this when testing config updates locally
|
||||||
|
}
|
||||||
@@ -0,0 +1,569 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
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.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.renderer.culling.Frustrum;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap;
|
||||||
|
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.RenderWorldLastEvent;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||||
|
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) return false;
|
||||||
|
|
||||||
|
HintParticleInfo that = (HintParticleInfo) o;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// this rendering code is independently written by glee8e on July 10th, 2023
|
||||||
|
// and is released as part of StructureLib under LGPL terms, just like everything else in this project
|
||||||
|
// cube is a very special model. its facings can be rendered correctly by viewer distance without using
|
||||||
|
// surface normals and view vector
|
||||||
|
// here we do a 2 pass render.
|
||||||
|
// first pass we draw obstructed faces (i.e. faces that are further away from player)
|
||||||
|
// second pass we draw unobstructed faces
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
boolean unobstructedPass = j == 1;
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (icons[i] == null) continue;
|
||||||
|
|
||||||
|
markTextureUsed(icons[i]);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
||||||
|
private void resetPlayerLocation() {
|
||||||
|
lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX;
|
||||||
|
lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY;
|
||||||
|
lastPlayerPos.zCoord = Minecraft.getMinecraft().thePlayer.posZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
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,96 @@
|
|||||||
|
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 com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
|
||||||
|
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 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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,27 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
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 net.minecraftforge.common.config.ConfigCategory;
|
|
||||||
import net.minecraftforge.common.config.Configuration;
|
|
||||||
import net.minecraftforge.common.config.Property;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.config.ConfigCategory;
|
||||||
|
import net.minecraftforge.common.config.Configuration;
|
||||||
|
import net.minecraftforge.common.config.Property;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
|
||||||
public enum ConfigurationHandler {
|
public enum ConfigurationHandler {
|
||||||
|
|
||||||
INSTANCE;
|
INSTANCE;
|
||||||
|
|
||||||
private Configuration config;
|
private Configuration config;
|
||||||
private int maxCoexistingHologram;
|
private int maxCoexistingHologram;
|
||||||
private boolean removeCollidingHologram;
|
private boolean removeCollidingHologram;
|
||||||
|
private int hintLifespan;
|
||||||
|
private int hintTransparency;
|
||||||
|
private int autoPlaceBudget;
|
||||||
|
private int autoPlaceInterval;
|
||||||
|
|
||||||
ConfigurationHandler() {
|
ConfigurationHandler() {
|
||||||
FMLCommonHandler.instance().bus().register(this);
|
FMLCommonHandler.instance().bus().register(this);
|
||||||
@@ -39,8 +45,46 @@ public enum ConfigurationHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadConfig() {
|
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");
|
maxCoexistingHologram = config.getInt(
|
||||||
removeCollidingHologram = config.getBoolean("removeColliding", "client.hologram", true, "An attempt will be made to remove an existing hologram if it collides with a new hologram.");
|
"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.");
|
||||||
|
|
||||||
if (config.hasChanged()) {
|
if (config.hasChanged()) {
|
||||||
config.save();
|
config.save();
|
||||||
@@ -62,6 +106,22 @@ public enum ConfigurationHandler {
|
|||||||
return removeCollidingHologram;
|
return removeCollidingHologram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getHintLifespan() {
|
||||||
|
return hintLifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHintTransparency() {
|
||||||
|
return hintTransparency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAutoPlaceBudget() {
|
||||||
|
return autoPlaceBudget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAutoPlaceInterval() {
|
||||||
|
return autoPlaceInterval;
|
||||||
|
}
|
||||||
|
|
||||||
Configuration getConfig() {
|
Configuration getConfig() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
import net.minecraftforge.common.config.Configuration;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.config.Configuration;
|
||||||
|
|
||||||
enum ConfigurationVersion {
|
enum ConfigurationVersion {
|
||||||
|
|
||||||
V1 {
|
V1 {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void step(Configuration c) {
|
protected void step(Configuration c) {}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final ConfigurationVersion[] VALUES = values();
|
private static final ConfigurationVersion[] VALUES = values();
|
||||||
@@ -32,7 +34,8 @@ enum ConfigurationVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigurationVersion identify(Configuration c) {
|
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
|
@Nullable
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
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 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 {
|
public class GuiFactory implements IModGuiFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(Minecraft minecraftInstance) {
|
public void initialize(Minecraft minecraftInstance) {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends GuiScreen> mainConfigGuiClass() {
|
public Class<? extends GuiScreen> mainConfigGuiClass() {
|
||||||
@@ -25,4 +26,4 @@ public class GuiFactory implements IModGuiFactory {
|
|||||||
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
|
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
|
||||||
|
import cpw.mods.fml.common.network.IGuiHandler;
|
||||||
|
|
||||||
|
public class GuiHandler implements IGuiHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||||
|
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(player.inventory.getStackInSlot(x));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,31 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
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.List;
|
||||||
import java.util.stream.Collectors;
|
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 class GuiModConfig extends GuiConfig {
|
||||||
|
|
||||||
public GuiModConfig(GuiScreen guiScreen) {
|
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")
|
@SuppressWarnings("rawtypes")
|
||||||
private static List<IConfigElement> getConfigElements() {
|
private static List<IConfigElement> getConfigElements() {
|
||||||
final Configuration config = ConfigurationHandler.INSTANCE.getConfig();
|
final Configuration config = ConfigurationHandler.INSTANCE.getConfig();
|
||||||
return config.getCategoryNames().stream()
|
return config.getCategoryNames().stream().filter(name -> name.indexOf('.') == -1)
|
||||||
.filter(name -> name.indexOf('.') == -1)
|
.map(name -> new ConfigElement(config.getCategory(name))).collect(Collectors.toList());
|
||||||
.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,159 @@
|
|||||||
|
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,145 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
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 net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.creativetab.CreativeTabs;
|
import net.minecraft.creativetab.CreativeTabs;
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemBlock;
|
import net.minecraft.item.ItemBlock;
|
||||||
|
import net.minecraft.launchwrapper.Launch;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.block.BlockHint;
|
||||||
|
import com.gtnewhorizon.structurelib.command.CommandConfigureChannels;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemBlockHint;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
|
||||||
|
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Loader;
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
import cpw.mods.fml.common.SidedProxy;
|
||||||
|
import cpw.mods.fml.common.event.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.
|
* This class does not contain a stable API. Refrain from using this class.
|
||||||
*/
|
*/
|
||||||
@Mod(modid = StructureLibAPI.MOD_ID, name = "StructureLib", version = "GRADLETOKEN_VERSION", acceptableRemoteVersions = "*", guiFactory = "com.gtnewhorizon.structurelib.GuiFactory")
|
@Mod(
|
||||||
|
modid = StructureLibAPI.MOD_ID,
|
||||||
|
name = "StructureLib",
|
||||||
|
version = "GRADLETOKEN_VERSION",
|
||||||
|
acceptableRemoteVersions = "*",
|
||||||
|
guiFactory = "com.gtnewhorizon.structurelib.GuiFactory")
|
||||||
public class StructureLib {
|
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")
|
private static final String STRUCTURECOMPAT_MODID = "structurecompat";
|
||||||
static CommonProxy proxy;
|
public static boolean DEBUG_MODE;
|
||||||
static SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(StructureLibAPI.MOD_ID);
|
public static boolean PANIC_MODE = Boolean.getBoolean("structurelib.panic");
|
||||||
|
public static final Logger LOGGER = LogManager.getLogger("StructureLib");
|
||||||
|
|
||||||
static {
|
@SidedProxy(
|
||||||
net.registerMessage(AlignmentMessage.ServerHandler.class, AlignmentMessage.AlignmentQuery.class, 0, Side.SERVER);
|
serverSide = "com.gtnewhorizon.structurelib.CommonProxy",
|
||||||
net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT);
|
clientSide = "com.gtnewhorizon.structurelib.ClientProxy")
|
||||||
}
|
static CommonProxy proxy;
|
||||||
|
|
||||||
public static final XSTR RANDOM = new XSTR();
|
static final SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(StructureLibAPI.MOD_ID);
|
||||||
|
|
||||||
static Block blockHint;
|
static {
|
||||||
static Item itemBlockHint;
|
net.registerMessage(
|
||||||
static Item itemFrontRotationTool;
|
AlignmentMessage.ServerHandler.class,
|
||||||
static Item itemConstructableTrigger;
|
AlignmentMessage.AlignmentQuery.class,
|
||||||
public static final CreativeTabs creativeTab = new CreativeTabs("structurelib") {
|
0,
|
||||||
@Override
|
Side.SERVER);
|
||||||
@SideOnly(Side.CLIENT)
|
net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT);
|
||||||
public Item getTabIconItem() {
|
net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT);
|
||||||
return StructureLibAPI.getItemBlockHint();
|
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
|
||||||
}
|
net.registerMessage(ErrorHintParticleMessage.Handler.class, ErrorHintParticleMessage.class, 4, Side.CLIENT);
|
||||||
};
|
|
||||||
|
|
||||||
@Mod.EventHandler
|
try {
|
||||||
public void preInit(FMLPreInitializationEvent e) {
|
DEBUG_MODE = Boolean.parseBoolean(System.getProperty("structurelib.debug"));
|
||||||
ConfigurationHandler.INSTANCE.init(e.getSuggestedConfigurationFile());
|
} catch (IllegalArgumentException | NullPointerException e) {
|
||||||
GameRegistry.registerBlock(blockHint = new BlockHint(), ItemBlockHint.class, "blockhint");
|
// turn on debug by default in dev mode
|
||||||
itemBlockHint = ItemBlock.getItemFromBlock(StructureLibAPI.getBlockHint());
|
// this will be overridden if above property is present and set to false
|
||||||
GameRegistry.registerItem(itemFrontRotationTool = new ItemFrontRotationTool(), itemFrontRotationTool.getUnlocalizedName());
|
DEBUG_MODE = (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
|
||||||
GameRegistry.registerItem(itemConstructableTrigger = new ItemConstructableTrigger(), itemConstructableTrigger.getUnlocalizedName());
|
}
|
||||||
proxy.preInit(e);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void addClientSideChatMessages(String... messages) {
|
public static final XSTR RANDOM = new XSTR();
|
||||||
proxy.addClientSideChatMessages(messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EntityPlayer getCurrentPlayer() {
|
@Mod.Instance
|
||||||
return proxy.getCurrentPlayer();
|
static StructureLib INSTANCE;
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isCurrentPlayer(EntityPlayer player) {
|
static Object COMPAT;
|
||||||
return proxy.isCurrentPlayer(player);
|
|
||||||
}
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@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);
|
||||||
|
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
|
||||||
|
|
||||||
|
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
|
||||||
|
COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void serverStarting(FMLServerStartingEvent e) {
|
||||||
|
e.registerServerCommand(new CommandConfigureChannels());
|
||||||
|
}
|
||||||
|
|
||||||
|
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,131 +1,352 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
import static com.gtnewhorizon.structurelib.StructureLib.proxy;
|
||||||
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
|
||||||
import cpw.mods.fml.common.network.NetworkRegistry;
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
import net.minecraft.entity.player.EntityPlayerMP;
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
import net.minecraft.util.IIcon;
|
import net.minecraft.util.IIcon;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import static com.gtnewhorizon.structurelib.StructureLib.proxy;
|
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 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;
|
|
||||||
|
|
||||||
/**
|
public static final String MOD_ID = "structurelib";
|
||||||
* Start a batch of hinting. All hints particles generated during one batch will be considered to belong to one hologram.
|
public static final int HINT_BLOCK_META_GENERIC_0 = 0;
|
||||||
*
|
public static final int HINT_BLOCK_META_GENERIC_1 = 1;
|
||||||
* 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 final int HINT_BLOCK_META_GENERIC_2 = 2;
|
||||||
*/
|
public static final int HINT_BLOCK_META_GENERIC_3 = 3;
|
||||||
public static void startHinting(World w) {
|
public static final int HINT_BLOCK_META_GENERIC_4 = 4;
|
||||||
proxy.startHinting(w);
|
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;
|
||||||
|
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.
|
* 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
|
||||||
* 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)}
|
* appropriate. Otherwise, these events will not be sent at all, regardless of whether a listener for these events
|
||||||
*/
|
* are registered.
|
||||||
public static void endHinting(World w) {
|
* <p>
|
||||||
proxy.endHinting(w);
|
* 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(instrument);
|
||||||
|
}
|
||||||
|
|
||||||
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.
|
* Generate a new hint particle on client side at given location using given textures with a tint.
|
||||||
* The ExtendedFacing will later be set onto given tile entity via {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)}
|
*
|
||||||
* upon arrival of server reply.
|
* @param w World to spawn. Usually the client world.
|
||||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
* @param x x coord
|
||||||
*/
|
* @param y y coord
|
||||||
public static void queryAlignment(IAlignmentProvider provider) {
|
* @param z z coord
|
||||||
StructureLib.net.sendToServer(new AlignmentMessage.AlignmentQuery(provider));
|
* @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.
|
* Generate a new hint particle on client side at given location using textures from given block with a tint.
|
||||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
*
|
||||||
*/
|
* @param w World to spawn. Usually the client world.
|
||||||
public static void sendAlignment(IAlignmentProvider provider) {
|
* @param x x coord
|
||||||
StructureLib.net.sendToAll(new AlignmentMessage.AlignmentData(provider));
|
* @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.
|
* Generate a new hint particle on client side at given location using given textures.
|
||||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
*
|
||||||
*/
|
* @param w World to spawn. Usually the client world.
|
||||||
public static void sendAlignment(IAlignmentProvider provider, EntityPlayerMP player) {
|
* @param x x coord
|
||||||
StructureLib.net.sendTo(new AlignmentMessage.AlignmentData(provider), player);
|
* @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.
|
* Generate a new hint particle on client side at given location using textures from given block.
|
||||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
*
|
||||||
*/
|
* @param w World to spawn. Usually the client world.
|
||||||
public static void sendAlignment(IAlignmentProvider provider, NetworkRegistry.TargetPoint targetPoint) {
|
* @param x x coord
|
||||||
StructureLib.net.sendToAllAround(new AlignmentMessage.AlignmentData(provider), targetPoint);
|
* @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.
|
* Update the tint of given hint particle. Do nothing if particle not found.
|
||||||
* @throws IllegalArgumentException if is not tile entity or provided a null alignment
|
*
|
||||||
*/
|
* @return false if nothing updated. true if updated.
|
||||||
public static void sendAlignment(IAlignmentProvider provider, World dimension) {
|
*/
|
||||||
StructureLib.net.sendToDimension(new AlignmentMessage.AlignmentData(provider), dimension.provider.dimensionId);
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isDebugEnabled() {
|
/**
|
||||||
return StructureLib.DEBUG_MODE;
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
public static void setDebugEnabled(boolean enabled) {
|
/**
|
||||||
StructureLib.DEBUG_MODE = enabled;
|
* 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment;
|
package com.gtnewhorizon.structurelib.alignment;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||||
import net.minecraftforge.common.util.ForgeDirection;
|
|
||||||
|
|
||||||
class AlignmentLimits implements IAlignmentLimits {
|
class AlignmentLimits implements IAlignmentLimits {
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import net.minecraft.world.World;
|
|||||||
import net.minecraftforge.common.util.FakePlayer;
|
import net.minecraftforge.common.util.FakePlayer;
|
||||||
|
|
||||||
public class AlignmentUtility {
|
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);
|
TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ);
|
||||||
if (tTileEntity == null || aPlayer instanceof FakePlayer) {
|
if (tTileEntity == null || aPlayer instanceof FakePlayer) {
|
||||||
return aPlayer instanceof EntityPlayerMP;
|
return aPlayer instanceof EntityPlayerMP;
|
||||||
|
|||||||
@@ -1,66 +1,73 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment;
|
package com.gtnewhorizon.structurelib.alignment;
|
||||||
|
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
||||||
import net.minecraftforge.common.util.ForgeDirection;
|
|
||||||
|
|
||||||
public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
|
||||||
int DIRECTIONS_COUNT= Direction.VALUES.length;
|
|
||||||
int ROTATIONS_COUNT= Rotation.VALUES.length;
|
int DIRECTIONS_COUNT = Direction.VALUES.length;
|
||||||
int FLIPS_COUNT= Flip.VALUES.length;
|
int ROTATIONS_COUNT = Rotation.VALUES.length;
|
||||||
|
int FLIPS_COUNT = Flip.VALUES.length;
|
||||||
int STATES_COUNT = ExtendedFacing.STATES_COUNT;
|
int STATES_COUNT = ExtendedFacing.STATES_COUNT;
|
||||||
|
|
||||||
ExtendedFacing getExtendedFacing();
|
ExtendedFacing getExtendedFacing();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the facing without additional checks. Tools altering facing should use
|
||||||
|
* {@link #toolSetExtendedFacing(ExtendedFacing)} instead.
|
||||||
|
*/
|
||||||
void setExtendedFacing(ExtendedFacing alignment);
|
void setExtendedFacing(ExtendedFacing alignment);
|
||||||
|
|
||||||
IAlignmentLimits getAlignmentLimits();
|
IAlignmentLimits getAlignmentLimits();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default IAlignment getAlignment(){
|
default IAlignment getAlignment() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getAlignmentIndex(ForgeDirection direction, Rotation rotation, Flip flip){
|
static int getAlignmentIndex(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||||
return (direction.ordinal()*ROTATIONS_COUNT+rotation.getIndex())*FLIPS_COUNT+flip.getIndex();
|
return (direction.ordinal() * ROTATIONS_COUNT + rotation.getIndex()) * FLIPS_COUNT + flip.getIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
default ForgeDirection getDirection(){
|
default ForgeDirection getDirection() {
|
||||||
return getExtendedFacing().getDirection();
|
return getExtendedFacing().getDirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
default void setDirection(ForgeDirection direction){
|
default void setDirection(ForgeDirection direction) {
|
||||||
setExtendedFacing(getExtendedFacing().with(direction));
|
setExtendedFacing(getExtendedFacing().with(direction));
|
||||||
}
|
}
|
||||||
|
|
||||||
default Rotation getRotation(){
|
default Rotation getRotation() {
|
||||||
return getExtendedFacing().getRotation();
|
return getExtendedFacing().getRotation();
|
||||||
}
|
}
|
||||||
|
|
||||||
default void setRotation(Rotation rotation){
|
default void setRotation(Rotation rotation) {
|
||||||
setExtendedFacing(getExtendedFacing().with(rotation));
|
setExtendedFacing(getExtendedFacing().with(rotation));
|
||||||
}
|
}
|
||||||
|
|
||||||
default Flip getFlip(){
|
default Flip getFlip() {
|
||||||
return getExtendedFacing().getFlip();
|
return getExtendedFacing().getFlip();
|
||||||
}
|
}
|
||||||
|
|
||||||
default void setFlip(Flip flip){
|
default void setFlip(Flip flip) {
|
||||||
setExtendedFacing(getExtendedFacing().with(flip));
|
setExtendedFacing(getExtendedFacing().with(flip));
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean toolSetDirection(ForgeDirection direction){
|
default boolean toolSetDirection(ForgeDirection direction) {
|
||||||
if(direction==null || direction==ForgeDirection.UNKNOWN){
|
if (direction == null || direction == ForgeDirection.UNKNOWN) {
|
||||||
for (int i = 0,j=getDirection().ordinal()+1, valuesLength = Direction.VALUES.length; i < valuesLength; i++) {
|
for (int i = 0, j = getDirection().ordinal() + 1, valuesLength = Direction.VALUES.length; i
|
||||||
|
< valuesLength; i++) {
|
||||||
if (toolSetDirection(Direction.VALUES[(j + i) % valuesLength].getForgeDirection())) {
|
if (toolSetDirection(Direction.VALUES[(j + i) % valuesLength].getForgeDirection())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) {
|
for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) {
|
||||||
if(checkedSetExtendedFacing(extendedFacing)){
|
if (checkedSetExtendedFacing(extendedFacing)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,17 +75,17 @@ public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean checkedSetDirection(ForgeDirection direction){
|
default boolean checkedSetDirection(ForgeDirection direction) {
|
||||||
if (isNewDirectionValid(direction)){
|
if (isNewDirectionValid(direction)) {
|
||||||
setDirection(direction);
|
setDirection(direction);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean canSetToDirectionAny(ForgeDirection direction){
|
default boolean canSetToDirectionAny(ForgeDirection direction) {
|
||||||
for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) {
|
for (ExtendedFacing extendedFacing : ExtendedFacing.FOR_FACING.get(direction)) {
|
||||||
if(isNewExtendedFacingValid(extendedFacing)){
|
if (isNewExtendedFacingValid(extendedFacing)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,66 +93,71 @@ public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default boolean toolSetRotation(Rotation rotation) {
|
default boolean toolSetRotation(Rotation rotation) {
|
||||||
if(rotation==null){
|
if (rotation == null) {
|
||||||
int flips = Flip.VALUES.length;
|
int flips = Flip.VALUES.length;
|
||||||
int rotations = Rotation.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++) {
|
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 true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}else {
|
} else {
|
||||||
return checkedSetRotation(rotation);
|
return checkedSetRotation(rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean checkedSetRotation(Rotation rotation){
|
default boolean checkedSetRotation(Rotation rotation) {
|
||||||
if (isNewRotationValid(rotation)){
|
if (isNewRotationValid(rotation)) {
|
||||||
setRotation(rotation);
|
setRotation(rotation);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean toolSetFlip(Flip flip){
|
default boolean toolSetFlip(Flip flip) {
|
||||||
if(flip==null){
|
if (flip == null) {
|
||||||
for (int i = 1, j = getFlip().ordinal(), valuesLength = Flip.VALUES.length; i < valuesLength; i++) {
|
for (int i = 1, j = getFlip().ordinal(), valuesLength = Flip.VALUES.length; i < valuesLength; i++) {
|
||||||
if (checkedSetFlip(Flip.VALUES[(j + i) % valuesLength])) {
|
if (checkedSetFlip(Flip.VALUES[(j + i) % valuesLength])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}else {
|
} else {
|
||||||
return checkedSetFlip(flip);
|
return checkedSetFlip(flip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean checkedSetFlip(Flip flip){
|
default boolean checkedSetFlip(Flip flip) {
|
||||||
if (isNewFlipValid(flip)){
|
if (isNewFlipValid(flip)) {
|
||||||
setFlip(flip);
|
setFlip(flip);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean toolSetExtendedFacing(ExtendedFacing extendedFacing){
|
default boolean toolSetExtendedFacing(ExtendedFacing extendedFacing) {
|
||||||
if(extendedFacing==null){
|
if (extendedFacing == null) {
|
||||||
for (int i = 0,j=getExtendedFacing().ordinal()+1, valuesLength = ExtendedFacing.VALUES.length; i < valuesLength; i++) {
|
for (int i = 0, j = getExtendedFacing().ordinal() + 1, valuesLength = ExtendedFacing.VALUES.length; i
|
||||||
if (checkedSetExtendedFacing(ExtendedFacing.VALUES[(j + i)%valuesLength])){
|
< valuesLength; i++) {
|
||||||
|
if (checkedSetExtendedFacing(ExtendedFacing.VALUES[(j + i) % valuesLength])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}else {
|
} else {
|
||||||
return checkedSetExtendedFacing(extendedFacing);
|
return checkedSetExtendedFacing(extendedFacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean checkedSetExtendedFacing(ExtendedFacing alignment){
|
default boolean checkedSetExtendedFacing(ExtendedFacing alignment) {
|
||||||
if (isNewExtendedFacingValid(alignment)){
|
if (isNewExtendedFacingValid(alignment)) {
|
||||||
setExtendedFacing(alignment);
|
setExtendedFacing(alignment);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -153,31 +165,29 @@ public interface IAlignment extends IAlignmentLimits,IAlignmentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default boolean isNewDirectionValid(ForgeDirection direction) {
|
default boolean isNewDirectionValid(ForgeDirection direction) {
|
||||||
return isNewExtendedFacingValid(direction,getRotation(),getFlip());
|
return isNewExtendedFacingValid(direction, getRotation(), getFlip());
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isNewRotationValid(Rotation rotation){
|
default boolean isNewRotationValid(Rotation rotation) {
|
||||||
return isNewExtendedFacingValid(getDirection(),rotation,getFlip());
|
return isNewExtendedFacingValid(getDirection(), rotation, getFlip());
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isNewFlipValid(Flip flip){
|
default boolean isNewFlipValid(Flip flip) {
|
||||||
return isNewExtendedFacingValid(getDirection(),getRotation(),flip);
|
return isNewExtendedFacingValid(getDirection(), getRotation(), flip);
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isExtendedFacingValid() {
|
default boolean isExtendedFacingValid() {
|
||||||
return isNewExtendedFacingValid(getDirection(),getRotation(),getFlip());
|
return isNewExtendedFacingValid(getDirection(), getRotation(), getFlip());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
return getAlignmentLimits().isNewExtendedFacingValid(direction, rotation, flip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean isNewExtendedFacingValid(ExtendedFacing alignment){
|
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
|
||||||
return getAlignmentLimits().isNewExtendedFacingValid(
|
return getAlignmentLimits()
|
||||||
alignment.getDirection(),
|
.isNewExtendedFacingValid(alignment.getDirection(), alignment.getRotation(), alignment.getFlip());
|
||||||
alignment.getRotation(),
|
|
||||||
alignment.getFlip());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment;
|
package com.gtnewhorizon.structurelib.alignment;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
|
import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
|
|
||||||
import net.minecraftforge.common.util.ForgeDirection;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
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 {
|
public interface IAlignmentLimits {
|
||||||
|
|
||||||
@@ -17,10 +19,7 @@ public interface IAlignmentLimits {
|
|||||||
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
|
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
|
||||||
|
|
||||||
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
|
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
|
||||||
return isNewExtendedFacingValid(
|
return isNewExtendedFacingValid(alignment.getDirection(), alignment.getRotation(), alignment.getFlip());
|
||||||
alignment.getDirection(),
|
|
||||||
alignment.getRotation(),
|
|
||||||
alignment.getFlip());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static IAlignmentLimits allowOnly(ExtendedFacing... allowedFacings) {
|
static IAlignmentLimits allowOnly(ExtendedFacing... allowedFacings) {
|
||||||
@@ -40,6 +39,7 @@ public interface IAlignmentLimits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
|
|
||||||
protected final boolean[] validStates = new boolean[STATES_COUNT];
|
protected final boolean[] validStates = new boolean[STATES_COUNT];
|
||||||
|
|
||||||
private Builder() {}
|
private Builder() {}
|
||||||
@@ -57,12 +57,14 @@ public interface IAlignmentLimits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder deny(ForgeDirection fd) {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder allow(ForgeDirection fd) {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,22 +79,26 @@ public interface IAlignmentLimits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder deny(Rotation fd) {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder allow(Rotation fd) {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder deny(Flip fd) {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder allow(Flip fd) {
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +119,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
|
* @param flip the preferred flip to be used Horizontal or vertical
|
||||||
* @return this
|
* @return this
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.alignment;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public interface IAlignmentProvider {
|
public interface IAlignmentProvider {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
IAlignment getAlignment();
|
IAlignment getAlignment();
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment;
|
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 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 {
|
public class IntegerAxisSwap {
|
||||||
|
|
||||||
private final Vec3Impl forFirstAxis;
|
private final Vec3Impl forFirstAxis;
|
||||||
private final Vec3Impl forSecondAxis;
|
private final Vec3Impl forSecondAxis;
|
||||||
private final Vec3Impl forThirdAxis;
|
private final Vec3Impl forThirdAxis;
|
||||||
@@ -16,69 +18,77 @@ public class IntegerAxisSwap {
|
|||||||
this.forFirstAxis = Direction.getAxisVector(forFirstAxis);
|
this.forFirstAxis = Direction.getAxisVector(forFirstAxis);
|
||||||
this.forSecondAxis = Direction.getAxisVector(forSecondAxis);
|
this.forSecondAxis = Direction.getAxisVector(forSecondAxis);
|
||||||
this.forThirdAxis = Direction.getAxisVector(forThirdAxis);
|
this.forThirdAxis = Direction.getAxisVector(forThirdAxis);
|
||||||
if(abs(this.forFirstAxis.get0())+abs(this.forSecondAxis.get0())+abs(this.forThirdAxis.get0())!=1 ||
|
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.get1()) + abs(this.forSecondAxis.get1()) + abs(this.forThirdAxis.get1()) != 1
|
||||||
abs(this.forFirstAxis.get2())+abs(this.forSecondAxis.get2())+abs(this.forThirdAxis.get2())!=1){
|
|| abs(this.forFirstAxis.get2()) + abs(this.forSecondAxis.get2()) + abs(this.forThirdAxis.get2())
|
||||||
throw new IllegalArgumentException("Axis are overlapping/missing! "+
|
!= 1) {
|
||||||
forFirstAxis.name()+" "+
|
throw new IllegalArgumentException(
|
||||||
forSecondAxis.name()+" "+
|
"Axis are overlapping/missing! " + forFirstAxis
|
||||||
forThirdAxis.name());
|
.name() + " " + forSecondAxis.name() + " " + forThirdAxis.name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl translate(Vec3Impl point){
|
public Vec3Impl translate(Vec3Impl point) {
|
||||||
return new Vec3Impl(
|
return new Vec3Impl(
|
||||||
forFirstAxis.get0()*point.get0() +forFirstAxis.get1()*point.get1() +forFirstAxis.get2()*point.get2(),
|
forFirstAxis.get0() * point.get0() + forFirstAxis.get1() * point.get1()
|
||||||
forSecondAxis.get0()*point.get0()+forSecondAxis.get1()*point.get1()+forSecondAxis.get2()*point.get2(),
|
+ forFirstAxis.get2() * point.get2(),
|
||||||
forThirdAxis.get0()*point.get0() +forThirdAxis.get1()*point.get1() +forThirdAxis.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(
|
return new Vec3Impl(
|
||||||
forFirstAxis.get0()*point.get0()+forSecondAxis.get0()*point.get1()+forThirdAxis.get0()*point.get2(),
|
forFirstAxis.get0() * point.get0() + forSecondAxis.get0() * point.get1()
|
||||||
forFirstAxis.get1()*point.get0()+forSecondAxis.get1()*point.get1()+forThirdAxis.get1()*point.get2(),
|
+ forThirdAxis.get0() * point.get2(),
|
||||||
forFirstAxis.get2()*point.get0()+forSecondAxis.get2()*point.get1()+forThirdAxis.get2()*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(
|
return Vec3.createVectorHelper(
|
||||||
forFirstAxis.get0()*point.xCoord +forFirstAxis.get1()*point.yCoord +forFirstAxis.get2()*point.zCoord,
|
forFirstAxis.get0() * point.xCoord + forFirstAxis.get1() * point.yCoord
|
||||||
forSecondAxis.get0()*point.xCoord+forSecondAxis.get1()*point.yCoord+forSecondAxis.get2()*point.zCoord,
|
+ forFirstAxis.get2() * point.zCoord,
|
||||||
forThirdAxis.get0()*point.xCoord +forThirdAxis.get1()*point.yCoord +forThirdAxis.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(
|
return Vec3.createVectorHelper(
|
||||||
forFirstAxis.get0()*point.xCoord+forSecondAxis.get0()*point.yCoord+forThirdAxis.get0()*point.zCoord,
|
forFirstAxis.get0() * point.xCoord + forSecondAxis.get0() * point.yCoord
|
||||||
forFirstAxis.get1()*point.xCoord+forSecondAxis.get1()*point.yCoord+forThirdAxis.get1()*point.zCoord,
|
+ forThirdAxis.get0() * point.zCoord,
|
||||||
forFirstAxis.get2()*point.xCoord+forSecondAxis.get2()*point.yCoord+forThirdAxis.get2()*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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inverseTranslate(int[] point,int[] out){
|
public void translate(int[] point, int[] out) {
|
||||||
out[0]=forFirstAxis.get0()*point[0]+forSecondAxis.get0()*point[1]+forThirdAxis.get0()*point[2];
|
out[0] = forFirstAxis.get0() * point[0] + forFirstAxis.get1() * point[1] + forFirstAxis.get2() * point[2];
|
||||||
out[1]=forFirstAxis.get1()*point[0]+forSecondAxis.get1()*point[1]+forThirdAxis.get1()*point[2];
|
out[1] = forSecondAxis.get0() * point[0] + forSecondAxis.get1() * point[1] + forSecondAxis.get2() * point[2];
|
||||||
out[2]=forFirstAxis.get2()*point[0]+forSecondAxis.get2()*point[1]+forThirdAxis.get2()*point[2];
|
out[2] = forThirdAxis.get0() * point[0] + forThirdAxis.get1() * point[1] + forThirdAxis.get2() * point[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void translate(double[] point,double[] out){
|
public void inverseTranslate(int[] point, int[] out) {
|
||||||
out[0]=forFirstAxis.get0()*point[0] +forFirstAxis.get1()*point[1] +forFirstAxis.get2()*point[2];
|
out[0] = forFirstAxis.get0() * point[0] + forSecondAxis.get0() * point[1] + forThirdAxis.get0() * point[2];
|
||||||
out[1]=forSecondAxis.get0()*point[0]+forSecondAxis.get1()*point[1]+forSecondAxis.get2()*point[2];
|
out[1] = forFirstAxis.get1() * point[0] + forSecondAxis.get1() * point[1] + forThirdAxis.get1() * point[2];
|
||||||
out[2]=forThirdAxis.get0()*point[0] +forThirdAxis.get1()*point[1] +forThirdAxis.get2()*point[2];
|
out[2] = forFirstAxis.get2() * point[0] + forSecondAxis.get2() * point[1] + forThirdAxis.get2() * point[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void inverseTranslate(double[] point,double[] out){
|
public void translate(double[] point, double[] out) {
|
||||||
out[0]=forFirstAxis.get0()*point[0]+forSecondAxis.get0()*point[1]+forThirdAxis.get0()*point[2];
|
out[0] = forFirstAxis.get0() * point[0] + forFirstAxis.get1() * point[1] + forFirstAxis.get2() * point[2];
|
||||||
out[1]=forFirstAxis.get1()*point[0]+forSecondAxis.get1()*point[1]+forThirdAxis.get1()*point[2];
|
out[1] = forSecondAxis.get0() * point[0] + forSecondAxis.get1() * point[1] + forSecondAxis.get2() * point[2];
|
||||||
out[2]=forFirstAxis.get2()*point[0]+forSecondAxis.get2()*point[1]+forThirdAxis.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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+177
@@ -0,0 +1,177 @@
|
|||||||
|
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 #withChannel(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 subchannel data. should always be a positive value
|
||||||
|
*/
|
||||||
|
public static void setChannelData(ItemStack masterStack, String channel, int data) {
|
||||||
|
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 (data <= 0) throw new IllegalArgumentException();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.StructureLib;
|
import java.util.WeakHashMap;
|
||||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
import net.minecraft.entity.player.EntityPlayerMP;
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
import net.minecraft.util.ChatComponentTranslation;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraftforge.common.util.FakePlayer;
|
import net.minecraftforge.common.util.FakePlayer;
|
||||||
import net.minecraftforge.common.util.ForgeDirection;
|
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.structure.IItemSource;
|
||||||
|
import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
|
||||||
|
|
||||||
public class ConstructableUtility {
|
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);
|
StructureLibAPI.startHinting(aWorld);
|
||||||
boolean ret = handle0(aStack, aPlayer, aWorld, aX, aY, aZ, aSide);
|
boolean ret = handle0(aStack, aPlayer, aWorld, aX, aY, aZ, aSide);
|
||||||
StructureLibAPI.endHinting(aWorld);
|
StructureLibAPI.endHinting(aWorld);
|
||||||
return ret;
|
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);
|
TileEntity tTileEntity = aWorld.getTileEntity(aX, aY, aZ);
|
||||||
if (tTileEntity == null || aPlayer instanceof FakePlayer) {
|
if (tTileEntity == null || aPlayer instanceof FakePlayer) {
|
||||||
return aPlayer instanceof EntityPlayerMP;
|
return aPlayer instanceof EntityPlayerMP;
|
||||||
}
|
}
|
||||||
if (aPlayer instanceof EntityPlayerMP) {
|
if (aPlayer instanceof EntityPlayerMP) {
|
||||||
//struct gen
|
// not sneaking - client side will generate hologram. nothing to do on server side
|
||||||
if (aPlayer.isSneaking() && aPlayer.capabilities.isCreativeMode) {
|
if (!aPlayer.isSneaking()) return true;
|
||||||
if (tTileEntity instanceof IConstructableProvider) {
|
|
||||||
IConstructable constructable = ((IConstructableProvider) tTileEntity).getConstructable();
|
long timePast = System.currentTimeMillis() - getLastUseMilis(aPlayer);
|
||||||
if (constructable != null) {
|
if (timePast < ConfigurationHandler.INSTANCE.getAutoPlaceInterval()) {
|
||||||
constructable.construct(aStack, false);
|
aPlayer.addChatComponentMessage(
|
||||||
}
|
new ChatComponentTranslation(
|
||||||
} else if (tTileEntity instanceof IConstructable) {
|
"item.structurelib.constructableTrigger.too_fast",
|
||||||
((IConstructable) tTileEntity).construct(aStack, false);
|
ConfigurationHandler.INSTANCE.getAutoPlaceInterval() - timePast));
|
||||||
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
|
return true;
|
||||||
IMultiblockInfoContainer<TileEntity> iMultiblockInfoContainer = IMultiblockInfoContainer.get(tTileEntity.getClass());
|
}
|
||||||
if (tTileEntity instanceof IAlignment) {
|
} else if (!StructureLib.isCurrentPlayer(aPlayer)) {
|
||||||
iMultiblockInfoContainer.construct(aStack, false, tTileEntity,
|
return false;
|
||||||
((IAlignment) tTileEntity).getExtendedFacing());
|
}
|
||||||
} else {
|
|
||||||
iMultiblockInfoContainer.construct(aStack, false, tTileEntity,
|
IConstructable constructable = null;
|
||||||
ExtendedFacing.of(ForgeDirection.getOrientation(aSide)));
|
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) {
|
||||||
|
EntityPlayerMP playerMP = (EntityPlayerMP) aPlayer;
|
||||||
|
// server side and sneaking (already checked above)
|
||||||
|
// do construct
|
||||||
|
if (aPlayer.capabilities.isCreativeMode) {
|
||||||
|
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;
|
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;
|
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;
|
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.Side;
|
||||||
import cpw.mods.fml.relauncher.SideOnly;
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
import net.minecraft.item.ItemStack;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Tec on 24.03.2017.
|
* Created by Tec on 24.03.2017.
|
||||||
*/
|
*/
|
||||||
public interface IConstructable {
|
public interface IConstructable {
|
||||||
|
|
||||||
void construct(ItemStack stackSize, boolean hintsOnly);
|
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)
|
@SideOnly(Side.CLIENT)
|
||||||
String[] getStructureDescription(ItemStack stackSize);
|
String[] getStructureDescription(ItemStack stackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
+6
-5
@@ -6,9 +6,10 @@ import javax.annotation.Nullable;
|
|||||||
* Implement this interface if this tile entity MIGHT be constructable
|
* Implement this interface if this tile entity MIGHT be constructable
|
||||||
*/
|
*/
|
||||||
public interface IConstructableProvider {
|
public interface IConstructableProvider {
|
||||||
/**
|
|
||||||
* @return null if not constructable, an instance otherwise.
|
/**
|
||||||
*/
|
* @return null if not constructable, an instance otherwise.
|
||||||
@Nullable
|
*/
|
||||||
IConstructable getConstructable();
|
@Nullable
|
||||||
|
IConstructable getConstructable();
|
||||||
}
|
}
|
||||||
+53
-4
@@ -1,17 +1,25 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment.constructable;
|
package com.gtnewhorizon.structurelib.alignment.constructable;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
import java.util.HashMap;
|
||||||
import cpw.mods.fml.relauncher.Side;
|
|
||||||
import cpw.mods.fml.relauncher.SideOnly;
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
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
|
* To implement IConstructable on not own TileEntities
|
||||||
*/
|
*/
|
||||||
public interface IMultiblockInfoContainer<T> {
|
public interface IMultiblockInfoContainer<T> {
|
||||||
|
|
||||||
HashMap<String, IMultiblockInfoContainer<?>> MULTIBLOCK_MAP = new HashMap<>();
|
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);
|
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)
|
@SideOnly(Side.CLIENT)
|
||||||
String[] getDescription(ItemStack stackSize);
|
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;
|
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 net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||||
|
|
||||||
public enum Direction {
|
public enum Direction {
|
||||||
|
|
||||||
DOWN(ForgeDirection.DOWN),
|
DOWN(ForgeDirection.DOWN),
|
||||||
UP(ForgeDirection.UP),
|
UP(ForgeDirection.UP),
|
||||||
NORTH(ForgeDirection.NORTH),
|
NORTH(ForgeDirection.NORTH),
|
||||||
@@ -13,11 +20,13 @@ public enum Direction {
|
|||||||
|
|
||||||
private final ForgeDirection forgeDirection;
|
private final ForgeDirection forgeDirection;
|
||||||
private final Vec3Impl axisVector;
|
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) {
|
Direction(ForgeDirection forgeDirection) {
|
||||||
this.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() {
|
public ForgeDirection getForgeDirection() {
|
||||||
@@ -28,7 +37,11 @@ public enum Direction {
|
|||||||
return axisVector;
|
return axisVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vec3Impl getAxisVector(ForgeDirection forgeDirection){
|
public static Vec3Impl getAxisVector(ForgeDirection forgeDirection) {
|
||||||
return VALUES[forgeDirection.ordinal()].axisVector;
|
return VALUES[forgeDirection.ordinal()].axisVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Direction getByAxisVector(Vec3Impl vec3) {
|
||||||
|
return reverseMap.get(vec3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+161
-97
@@ -1,21 +1,24 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
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.FLIPS_COUNT;
|
||||||
import static com.gtnewhorizon.structurelib.alignment.IAlignment.ROTATIONS_COUNT;
|
import static com.gtnewhorizon.structurelib.alignment.IAlignment.ROTATIONS_COUNT;
|
||||||
import static java.lang.Math.abs;
|
import static java.lang.Math.abs;
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.stream.Collectors.*;
|
import static java.util.stream.Collectors.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
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 {
|
public enum ExtendedFacing {
|
||||||
|
|
||||||
DOWN_NORMAL_NONE("down normal none"),
|
DOWN_NORMAL_NONE("down normal none"),
|
||||||
DOWN_NORMAL_HORIZONTAL("down normal horizontal"),
|
DOWN_NORMAL_HORIZONTAL("down normal horizontal"),
|
||||||
DOWN_NORMAL_VERTICAL("down normal vertical"),
|
DOWN_NORMAL_VERTICAL("down normal vertical"),
|
||||||
@@ -113,13 +116,14 @@ public enum ExtendedFacing {
|
|||||||
EAST_COUNTER_CLOCKWISE_VERTICAL("east counter clockwise vertical"),
|
EAST_COUNTER_CLOCKWISE_VERTICAL("east counter clockwise vertical"),
|
||||||
EAST_COUNTER_CLOCKWISE_BOTH("east counter clockwise both");
|
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 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;
|
public static final int STATES_COUNT = VALUES.length;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
stream(values()).forEach(extendedFacing ->
|
stream(values()).forEach(
|
||||||
FOR_FACING.compute(extendedFacing.direction, ((forgeDirection, extendedFacings) -> {
|
extendedFacing -> FOR_FACING.compute(extendedFacing.direction, ((forgeDirection, extendedFacings) -> {
|
||||||
if (extendedFacings == null) {
|
if (extendedFacings == null) {
|
||||||
extendedFacings = new ArrayList<>();
|
extendedFacings = new ArrayList<>();
|
||||||
}
|
}
|
||||||
@@ -128,10 +132,24 @@ public enum ExtendedFacing {
|
|||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, ExtendedFacing> NAME_LOOKUP = stream(VALUES).collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing));
|
private static final Map<String, ExtendedFacing> NAME_LOOKUP = stream(VALUES)
|
||||||
private static final EnumMap<ForgeDirection, ImmutableSet<ExtendedFacing>> LOOKUP_BY_DIRECTION = stream(VALUES).collect(groupingBy(ExtendedFacing::getDirection, () -> new EnumMap<>(ForgeDirection.class), collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
.collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing));
|
||||||
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<ForgeDirection, ImmutableSet<ExtendedFacing>> LOOKUP_BY_DIRECTION = stream(VALUES)
|
||||||
private static final EnumMap<Flip, ImmutableSet<ExtendedFacing>> LOOKUP_BY_FLIP = stream(VALUES).collect(groupingBy(ExtendedFacing::getFlip, () -> new EnumMap<>(Flip.class), collectingAndThen(toSet(), ImmutableSet::copyOf)));
|
.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 direction;
|
||||||
private final ForgeDirection a, b, c;
|
private final ForgeDirection a, b, c;
|
||||||
private final Rotation rotation;
|
private final Rotation rotation;
|
||||||
@@ -142,133 +160,146 @@ public enum ExtendedFacing {
|
|||||||
|
|
||||||
ExtendedFacing(String name) {
|
ExtendedFacing(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
direction= Direction.VALUES[ordinal()/(ROTATIONS_COUNT*FLIPS_COUNT)].getForgeDirection();
|
direction = Direction.VALUES[ordinal() / (ROTATIONS_COUNT * FLIPS_COUNT)].getForgeDirection();
|
||||||
rotation=Rotation.VALUES[ordinal()/FLIPS_COUNT-direction.ordinal()*ROTATIONS_COUNT];
|
rotation = Rotation.VALUES[ordinal() / FLIPS_COUNT - direction.ordinal() * ROTATIONS_COUNT];
|
||||||
flip=Flip.VALUES[ordinal()%FLIPS_COUNT];
|
flip = Flip.VALUES[ordinal() % FLIPS_COUNT];
|
||||||
ForgeDirection a,b,c;
|
ForgeDirection a, b, c;
|
||||||
switch (direction){
|
switch (direction) {
|
||||||
case DOWN:
|
case DOWN:
|
||||||
a= ForgeDirection.WEST;
|
a = ForgeDirection.WEST;
|
||||||
b= ForgeDirection.SOUTH;
|
b = ForgeDirection.SOUTH;
|
||||||
c= ForgeDirection.UP;
|
c = ForgeDirection.UP;
|
||||||
break;
|
break;
|
||||||
case UP:
|
case UP:
|
||||||
a= ForgeDirection.EAST;
|
a = ForgeDirection.EAST;
|
||||||
b= ForgeDirection.SOUTH;
|
b = ForgeDirection.SOUTH;
|
||||||
c= ForgeDirection.DOWN;
|
c = ForgeDirection.DOWN;
|
||||||
break;
|
break;
|
||||||
case NORTH:
|
case NORTH:
|
||||||
a= ForgeDirection.WEST;
|
a = ForgeDirection.WEST;
|
||||||
b= ForgeDirection.DOWN;
|
b = ForgeDirection.DOWN;
|
||||||
c= ForgeDirection.SOUTH;
|
c = ForgeDirection.SOUTH;
|
||||||
break;
|
break;
|
||||||
case SOUTH:
|
case SOUTH:
|
||||||
a= ForgeDirection.EAST;
|
a = ForgeDirection.EAST;
|
||||||
b= ForgeDirection.DOWN;
|
b = ForgeDirection.DOWN;
|
||||||
c= ForgeDirection.NORTH;
|
c = ForgeDirection.NORTH;
|
||||||
break;
|
break;
|
||||||
case WEST:
|
case WEST:
|
||||||
a= ForgeDirection.SOUTH;
|
a = ForgeDirection.SOUTH;
|
||||||
b= ForgeDirection.DOWN;
|
b = ForgeDirection.DOWN;
|
||||||
c= ForgeDirection.EAST;
|
c = ForgeDirection.EAST;
|
||||||
break;
|
break;
|
||||||
case EAST:
|
case EAST:
|
||||||
a= ForgeDirection.NORTH;
|
a = ForgeDirection.NORTH;
|
||||||
b= ForgeDirection.DOWN;
|
b = ForgeDirection.DOWN;
|
||||||
c= ForgeDirection.WEST;
|
c = ForgeDirection.WEST;
|
||||||
break;
|
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:
|
case HORIZONTAL:
|
||||||
a=a.getOpposite();
|
a = a.getOpposite();
|
||||||
break;
|
break;
|
||||||
case BOTH:
|
case BOTH:
|
||||||
a=a.getOpposite();
|
a = a.getOpposite();
|
||||||
case VERTICAL:
|
case VERTICAL:
|
||||||
b=b.getOpposite();
|
b = b.getOpposite();
|
||||||
break;
|
break;
|
||||||
case NONE: break;
|
case NONE:
|
||||||
default:throw new RuntimeException("Even more impossible...");
|
break;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Even more impossible...");
|
||||||
}
|
}
|
||||||
switch (rotation) {
|
switch (rotation) {
|
||||||
case CLOCKWISE: {
|
case CLOCKWISE: {
|
||||||
ForgeDirection _a=a;
|
ForgeDirection _a = a;
|
||||||
a =b;
|
a = b;
|
||||||
b =_a.getOpposite();
|
b = _a.getOpposite();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UPSIDE_DOWN:
|
case UPSIDE_DOWN:
|
||||||
a=a.getOpposite();
|
a = a.getOpposite();
|
||||||
b=b.getOpposite();
|
b = b.getOpposite();
|
||||||
break;
|
break;
|
||||||
case COUNTER_CLOCKWISE: {
|
case COUNTER_CLOCKWISE: {
|
||||||
ForgeDirection _a=a;
|
ForgeDirection _a = a;
|
||||||
a =b.getOpposite();
|
a = b.getOpposite();
|
||||||
b =_a;
|
b = _a;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NORMAL: break;
|
case NORMAL:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("More impossible...");
|
throw new RuntimeException("More impossible...");
|
||||||
}
|
}
|
||||||
this.a=a;
|
this.a = a;
|
||||||
this.b=b;
|
this.b = b;
|
||||||
this.c=c;
|
this.c = c;
|
||||||
integerAxisSwap =new IntegerAxisSwap(a,b,c);
|
integerAxisSwap = new IntegerAxisSwap(a, b, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip){
|
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip) {
|
||||||
if(direction==ForgeDirection.UNKNOWN){
|
if (direction == ForgeDirection.UNKNOWN) {
|
||||||
return VALUES[IAlignment.getAlignmentIndex(ForgeDirection.NORTH, rotation, flip)];
|
return VALUES[IAlignment.getAlignmentIndex(ForgeDirection.NORTH, rotation, flip)];
|
||||||
}
|
}
|
||||||
return VALUES[IAlignment.getAlignmentIndex(direction, rotation, flip)];
|
return VALUES[IAlignment.getAlignmentIndex(direction, rotation, flip)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExtendedFacing of(ForgeDirection direction){
|
public static ExtendedFacing of(ForgeDirection direction) {
|
||||||
if(direction==ForgeDirection.UNKNOWN){
|
if (direction == ForgeDirection.UNKNOWN) {
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
}
|
}
|
||||||
return VALUES[IAlignment.getAlignmentIndex(direction, Rotation.NORMAL, Flip.NONE)];
|
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(ForgeDirection direction) {
|
||||||
public static ImmutableSet<ExtendedFacing> getAllWith(Rotation rotation) { return LOOKUP_BY_ROTATION.get(rotation);}
|
return LOOKUP_BY_DIRECTION.get(direction);
|
||||||
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){
|
public static ImmutableSet<ExtendedFacing> getAllWith(Rotation rotation) {
|
||||||
return of(direction,rotation,flip);
|
return LOOKUP_BY_ROTATION.get(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedFacing with(Flip flip){
|
public static ImmutableSet<ExtendedFacing> getAllWith(Flip flip) {
|
||||||
return of(direction,rotation,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() {
|
public ExtendedFacing getOppositeDirection() {
|
||||||
return of(direction.getOpposite(),rotation,flip);
|
return of(direction.getOpposite(), rotation, flip);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedFacing getOppositeRotation() {
|
public ExtendedFacing getOppositeRotation() {
|
||||||
return of(direction,rotation.getOpposite(),flip);
|
return of(direction, rotation.getOpposite(), flip);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedFacing getOppositeFlip() {
|
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
|
* Gets the same effective facing achieved by different rot/flip combo
|
||||||
|
*
|
||||||
* @return same effective facing, but different enum value
|
* @return same effective facing, but different enum value
|
||||||
*/
|
*/
|
||||||
public ExtendedFacing getDuplicate(){
|
public ExtendedFacing getDuplicate() {
|
||||||
return of(direction,rotation.getOpposite(),flip.getOpposite());
|
return of(direction, rotation.getOpposite(), flip.getOpposite());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIndex(){
|
public int getIndex() {
|
||||||
return ordinal();
|
return ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,6 +307,10 @@ public enum ExtendedFacing {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLocalizedName() {
|
||||||
|
return StatCollector.translateToLocal("structurelib.facing." + getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
public static ExtendedFacing byName(String name) {
|
public static ExtendedFacing byName(String name) {
|
||||||
return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT));
|
return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT));
|
||||||
}
|
}
|
||||||
@@ -301,40 +336,69 @@ public enum ExtendedFacing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates relative to front facing offset to world offset
|
* 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
|
* @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) {
|
public Vec3 getWorldOffset(Vec3 abcOffset) {
|
||||||
return integerAxisSwap.inverseTranslate(abcOffset);
|
return integerAxisSwap.inverseTranslate(abcOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl getWorldOffset(Vec3Impl abcOffset) {
|
public Vec3Impl getWorldOffset(Vec3Impl abcOffset) {
|
||||||
return integerAxisSwap.inverseTranslate(abcOffset);
|
return integerAxisSwap.inverseTranslate(abcOffset);
|
||||||
}
|
}
|
||||||
public void getWorldOffset(int[] point,int[] 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void getWorldOffset(double[] point, double[] out) {
|
||||||
|
integerAxisSwap.inverseTranslate(point, out);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates world offset to relative front facing offset
|
* do note the ABC coordinate system is (X, -Y, Z).
|
||||||
* @param xyzOffset X,Y,Z offset in world
|
|
||||||
* @return A,B,C offset (facing relative L-->R,U-->D,F-->B)
|
|
||||||
*/
|
*/
|
||||||
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);
|
return integerAxisSwap.translate(xyzOffset);
|
||||||
}
|
}
|
||||||
public Vec3Impl getOffsetABC(Vec3Impl xyzOffset){
|
|
||||||
|
public Vec3Impl getOffsetABC(Vec3Impl xyzOffset) {
|
||||||
return integerAxisSwap.translate(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() {
|
public IntegerAxisSwap getIntegerAxisSwap() {
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
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.lang.Math.abs;
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.stream.Collectors.toMap;
|
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 {
|
public enum Flip {
|
||||||
|
|
||||||
NONE(3, "none"),
|
NONE(3, "none"),
|
||||||
HORIZONTAL(2, "horizontal"),
|
HORIZONTAL(2, "horizontal"),
|
||||||
VERTICAL(1, "vertical"),
|
VERTICAL(1, "vertical"),
|
||||||
@@ -26,7 +28,7 @@ public enum Flip {
|
|||||||
this.name = nameIn;
|
this.name = nameIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIndex(){
|
public int getIndex() {
|
||||||
return ordinal();
|
return ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,19 +60,19 @@ public enum Flip {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotFlipped(){
|
public boolean isNotFlipped() {
|
||||||
return this==NONE;
|
return this == NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBothFlipped(){
|
public boolean isBothFlipped() {
|
||||||
return this==BOTH;
|
return this == BOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHorizontallyFlipped() {
|
public boolean isHorizontallyFlipped() {
|
||||||
return this==HORIZONTAL || isBothFlipped();
|
return this == HORIZONTAL || isBothFlipped();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVerticallyFliped() {
|
public boolean isVerticallyFliped() {
|
||||||
return this==VERTICAL || isBothFlipped();
|
return this == VERTICAL || isBothFlipped();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment.enumerable;
|
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.lang.Math.abs;
|
||||||
import static java.util.Arrays.stream;
|
import static java.util.Arrays.stream;
|
||||||
import static java.util.stream.Collectors.toMap;
|
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 {
|
public enum Rotation {
|
||||||
|
|
||||||
NORMAL(2, "normal"),
|
NORMAL(2, "normal"),
|
||||||
CLOCKWISE(3, "clockwise"),
|
CLOCKWISE(3, "clockwise"),
|
||||||
UPSIDE_DOWN(0, "upside down"),
|
UPSIDE_DOWN(0, "upside down"),
|
||||||
@@ -19,14 +21,15 @@ public enum Rotation {
|
|||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
public static final Rotation[] VALUES = values();
|
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) {
|
Rotation(int oppositeIn, String nameIn) {
|
||||||
this.opposite = oppositeIn;
|
this.opposite = oppositeIn;
|
||||||
this.name = nameIn;
|
this.name = nameIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIndex(){
|
public int getIndex() {
|
||||||
return ordinal();
|
return ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,19 +61,19 @@ public enum Rotation {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotRotated(){
|
public boolean isNotRotated() {
|
||||||
return this==NORMAL;
|
return this == NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isClockwise(){
|
public boolean isClockwise() {
|
||||||
return this==CLOCKWISE;
|
return this == CLOCKWISE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCounterClockwise(){
|
public boolean isCounterClockwise() {
|
||||||
return this==COUNTER_CLOCKWISE;
|
return this == COUNTER_CLOCKWISE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUpsideDown(){
|
public boolean isUpsideDown() {
|
||||||
return this==UPSIDE_DOWN;
|
return this == UPSIDE_DOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* All public stuff in this package and its subpackages are considered part of the public API.
|
* This package provide a framework for rotating a given block, or a multiblock structure.
|
||||||
* Backwards compatibility is maintained to the maximum extend possible.
|
* <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;
|
package com.gtnewhorizon.structurelib.block;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.StructureLib;
|
import java.util.List;
|
||||||
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
|
||||||
import cpw.mods.fml.relauncher.Side;
|
|
||||||
import cpw.mods.fml.relauncher.SideOnly;
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.material.Material;
|
import net.minecraft.block.material.Material;
|
||||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||||
@@ -13,55 +11,60 @@ import net.minecraft.item.ItemStack;
|
|||||||
import net.minecraft.util.IIcon;
|
import net.minecraft.util.IIcon;
|
||||||
import net.minecraft.world.IBlockAccess;
|
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 {
|
public class BlockHint extends Block {
|
||||||
private static final IIcon[] hint = new IIcon[16];
|
|
||||||
|
|
||||||
public BlockHint() {
|
private static final IIcon[] hint = new IIcon[16];
|
||||||
super(Material.iron);
|
|
||||||
setBlockName("structurelib.blockhint");
|
|
||||||
setCreativeTab(StructureLib.creativeTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public BlockHint() {
|
||||||
public void registerBlockIcons(IIconRegister aIconRegister) {
|
super(Material.iron);
|
||||||
//super.registerBlockIcons(aIconRegister);
|
setBlockName("structurelib.blockhint");
|
||||||
hint[0] = aIconRegister.registerIcon(StructureLibAPI.MOD_ID + ":iconsets/HINT_0");
|
setCreativeTab(StructureLib.creativeTab);
|
||||||
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
|
@Override
|
||||||
public IIcon getIcon(int aSide, int aMeta) {
|
public void registerBlockIcons(IIconRegister aIconRegister) {
|
||||||
return hint[aMeta];
|
// 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
|
@Override
|
||||||
@SideOnly(Side.CLIENT)
|
public IIcon getIcon(int aSide, int aMeta) {
|
||||||
public IIcon getIcon(IBlockAccess aWorld, int xCoord, int yCoord, int zCoord, int aSide) {
|
return hint[aMeta];
|
||||||
int tMeta = aWorld.getBlockMetadata(xCoord, yCoord, zCoord);
|
}
|
||||||
return getIcon(aSide, tMeta);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SideOnly(Side.CLIENT)
|
||||||
public void getSubBlocks(Item aItem, CreativeTabs par2CreativeTabs, List aList) {
|
public IIcon getIcon(IBlockAccess aWorld, int xCoord, int yCoord, int zCoord, int aSide) {
|
||||||
for (int i = 0; i <= 15; i++) {
|
int tMeta = aWorld.getBlockMetadata(xCoord, yCoord, zCoord);
|
||||||
aList.add(new ItemStack(aItem, 1, i));
|
return getIcon(aSide, tMeta);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
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. <value> must be positive integer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRequiredPermissionLevel() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<?> 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)) throw new WrongUsageException("must be a player");
|
||||||
|
|
||||||
|
EntityPlayerMP player = (EntityPlayerMP) sender;
|
||||||
|
|
||||||
|
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 <= 0) throw new WrongUsageException("%s not a valid value", args[args.length - 1]);
|
||||||
|
|
||||||
|
ChannelDataAccessor.setChannelData(heldItem, channel, value);
|
||||||
|
player.inventoryContainer.detectAndSendChanges();
|
||||||
|
sender.addChatMessage(new ChatComponentText(channel + " value: " + value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
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.client.particle.EntityFX;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
@SideOnly(Side.CLIENT)
|
@SideOnly(Side.CLIENT)
|
||||||
public class WeightlessParticleFX extends EntityFX
|
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 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_);
|
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.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.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.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.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.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.particleMaxAge = (int) (16.0D / ((double) this.rand.nextFloat() * 0.8D + 0.2D)) + 2;
|
||||||
this.noClip=true;
|
this.noClip = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to update the entity's position/logic.
|
* Called to update the entity's position/logic.
|
||||||
*/
|
*/
|
||||||
public void onUpdate()
|
public void onUpdate() {
|
||||||
{
|
|
||||||
this.prevPosX = this.posX;
|
this.prevPosX = this.posX;
|
||||||
this.prevPosY = this.posY;
|
this.prevPosY = this.posY;
|
||||||
this.prevPosZ = this.posZ;
|
this.prevPosZ = this.posZ;
|
||||||
|
|
||||||
if (this.particleAge++ >= this.particleMaxAge)
|
if (this.particleAge++ >= this.particleMaxAge) {
|
||||||
{
|
|
||||||
this.setDead();
|
this.setDead();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setParticleTextureIndex(7 - this.particleAge * 8 / this.particleMaxAge);
|
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.moveEntity(this.motionX, this.motionY, this.motionZ);
|
||||||
this.motionX *= 0.8999999761581421D;
|
this.motionX *= 0.8999999761581421D;
|
||||||
this.motionY *= 0.8999999761581421D;
|
this.motionY *= 0.8999999761581421D;
|
||||||
|
|||||||
@@ -0,0 +1,411 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.gui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.gui.GuiTextField;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
|
import org.lwjgl.input.Mouse;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.StructureLib;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||||
|
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
|
||||||
|
|
||||||
|
public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen {
|
||||||
|
|
||||||
|
private static final int KEY_MAX_WIDTH = 50;
|
||||||
|
private final ItemStack trigger;
|
||||||
|
private final GuiChannelsList list;
|
||||||
|
private GuiTextField key, value;
|
||||||
|
protected int guiTop, guiLeft;
|
||||||
|
|
||||||
|
public GuiScreenConfigureChannels(ItemStack trigger) {
|
||||||
|
this.trigger = trigger;
|
||||||
|
list = new GuiChannelsList(152, 100, 12, 12, 14);
|
||||||
|
list.addSelectionListener((list, selectedIndex) -> {
|
||||||
|
Entry<String, Integer> e = list.getElementAt(selectedIndex);
|
||||||
|
key.setText(e.getKey());
|
||||||
|
value.setText(e.getValue().toString());
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
addButton(
|
||||||
|
new GuiButton(
|
||||||
|
0,
|
||||||
|
guiLeft + 12,
|
||||||
|
guiTop + 157,
|
||||||
|
47,
|
||||||
|
20,
|
||||||
|
I18n.format("item.structurelib.constructableTrigger.gui.add")));
|
||||||
|
addButton(
|
||||||
|
new GuiButton(
|
||||||
|
1,
|
||||||
|
guiLeft + 65,
|
||||||
|
guiTop + 157,
|
||||||
|
47,
|
||||||
|
20,
|
||||||
|
I18n.format("item.structurelib.constructableTrigger.gui.unset")));
|
||||||
|
addButton(
|
||||||
|
new GuiButton(
|
||||||
|
2,
|
||||||
|
guiLeft + 118,
|
||||||
|
guiTop + 157,
|
||||||
|
47,
|
||||||
|
20,
|
||||||
|
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<GuiButton> getButtonList() {
|
||||||
|
return buttonList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMouseInput() {
|
||||||
|
int delta = Mouse.getEventDWheel();
|
||||||
|
if (delta != 0) list.handleDWheel(delta);
|
||||||
|
super.handleMouseInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mouseClicked(int mX, int mY, int button) {
|
||||||
|
key.mouseClicked(mX, mY, button);
|
||||||
|
value.mouseClicked(mX, mY, button);
|
||||||
|
super.mouseClicked(mX, mY, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void keyTyped(char aChar, int aKey) {
|
||||||
|
switch (aKey) {
|
||||||
|
case Keyboard.KEY_TAB:
|
||||||
|
if (key.isFocused()) {
|
||||||
|
key.setFocused(false);
|
||||||
|
value.setFocused(true);
|
||||||
|
} else {
|
||||||
|
key.setFocused(true);
|
||||||
|
value.setFocused(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_RETURN:
|
||||||
|
case Keyboard.KEY_NUMPADENTER:
|
||||||
|
GuiButton add = getButtonList().get(0);
|
||||||
|
if (add.enabled) doActionPerformed(add);
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_UP:
|
||||||
|
if (list.selectedIndex > 0) list.setSelection(list.selectedIndex - 1);
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_DOWN:
|
||||||
|
if (list.selectedIndex < list.getNumElements() - 1) list.setSelection(list.selectedIndex + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key.textboxKeyTyped(aChar, aKey)) {
|
||||||
|
updateButtons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value.textboxKeyTyped(aChar, aKey)) 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(0).displayString = existing ? I18n.format("item.structurelib.constructableTrigger.gui.set")
|
||||||
|
: I18n.format("item.structurelib.constructableTrigger.gui.add");
|
||||||
|
getButtonList().get(0).enabled = !StringUtils.isBlank(value.getText());
|
||||||
|
getButtonList().get(1).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 0:
|
||||||
|
int value = getValue();
|
||||||
|
if (value <= 0) return;
|
||||||
|
ChannelDataAccessor.setChannelData(trigger, key.getText(), value);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ChannelDataAccessor.unsetChannelData(trigger, key.getText());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ChannelDataAccessor.wipeChannelData(trigger);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doesGuiPauseGame() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawScreen(int mX, int mY, float partialTick) {
|
||||||
|
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());
|
||||||
|
super.drawScreen(mX, mY, partialTick);
|
||||||
|
list.drawScreen(mX, mY, partialTick);
|
||||||
|
fontRendererObj.drawString(
|
||||||
|
I18n.format("item.structurelib.constructableTrigger.gui.key"),
|
||||||
|
guiLeft + 12,
|
||||||
|
guiTop + 122,
|
||||||
|
0);
|
||||||
|
key.drawTextBox();
|
||||||
|
fontRendererObj.drawString(
|
||||||
|
I18n.format("item.structurelib.constructableTrigger.gui.value"),
|
||||||
|
guiLeft + 12,
|
||||||
|
guiTop + 142,
|
||||||
|
0);
|
||||||
|
value.drawTextBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String trim(String e, int keyMaxWidth) {
|
||||||
|
// TODO optimize this to not create a billion string, or maybe cache this
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
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
|
||||||
|
*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
prepareDrawElements();
|
||||||
|
|
||||||
|
for (int i = 0; i < getNumElements(); ++i) {
|
||||||
|
|
||||||
|
int elementY = contentYOffset + i * this.slotHeight;
|
||||||
|
int slotHeight = this.slotHeight - margin;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(2105376);
|
||||||
|
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;
|
package com.gtnewhorizon.structurelib.item;
|
||||||
|
|
||||||
|
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.creativetab.CreativeTabs;
|
import net.minecraft.creativetab.CreativeTabs;
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
@@ -7,60 +11,69 @@ import net.minecraft.item.ItemBlock;
|
|||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.EnumChatFormatting;
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
|
||||||
|
|
||||||
public class ItemBlockHint extends ItemBlock {
|
public class ItemBlockHint extends ItemBlock {
|
||||||
public ItemBlockHint(Block p_i45328_1_) {
|
|
||||||
super(p_i45328_1_);
|
|
||||||
setMaxDamage(0);
|
|
||||||
setHasSubtypes(true);
|
|
||||||
setCreativeTab(CreativeTabs.tabBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ItemBlockHint(Block p_i45328_1_) {
|
||||||
@SuppressWarnings("unchecked")
|
super(p_i45328_1_);
|
||||||
public void addInformation(ItemStack aStack, EntityPlayer aPlayer, List aList, boolean aF3_H) {
|
setMaxDamage(0);
|
||||||
aList.add(translateToLocal("structurelib.blockhint.desc.0"));//Helps while building
|
setHasSubtypes(true);
|
||||||
switch (aStack.getItemDamage()) {
|
setCreativeTab(CreativeTabs.tabBlock);
|
||||||
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 int getMetadata(int aMeta) {
|
@Override
|
||||||
return aMeta;
|
@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 + 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 + "From outer space... I guess...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getUnlocalizedName(ItemStack aStack) {
|
public int getMetadata(int aMeta) {
|
||||||
return this.field_150939_a.getUnlocalizedName() + "." + getDamage(aStack);
|
return aMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUnlocalizedName(ItemStack aStack) {
|
||||||
|
return this.field_150939_a.getUnlocalizedName() + "." + getDamage(aStack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,25 @@
|
|||||||
package com.gtnewhorizon.structurelib.item;
|
package com.gtnewhorizon.structurelib.item;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.StructureLib;
|
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||||
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
|
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.entity.player.EntityPlayer;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import java.util.List;
|
import org.lwjgl.input.Keyboard;
|
||||||
|
|
||||||
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
import com.gtnewhorizon.structurelib.StructureLib;
|
||||||
import static net.minecraft.util.EnumChatFormatting.BLUE;
|
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
|
||||||
|
|
||||||
public class ItemConstructableTrigger extends Item {
|
public class ItemConstructableTrigger extends Item {
|
||||||
|
|
||||||
public ItemConstructableTrigger() {
|
public ItemConstructableTrigger() {
|
||||||
setUnlocalizedName("structurelib.constructableTrigger");
|
setUnlocalizedName("structurelib.constructableTrigger");
|
||||||
setTextureName(MOD_ID + ":itemConstructableTrigger");
|
setTextureName(MOD_ID + ":itemConstructableTrigger");
|
||||||
@@ -21,17 +27,43 @@ public class ItemConstructableTrigger extends Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 (world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null)
|
||||||
|
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||||
|
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);
|
return ConstructableUtility.handle(stack, player, world, x, y, z, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) {
|
public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) {
|
||||||
aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.0"));//Triggers Constructable Interface
|
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
|
||||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.1"));//Shows multiblock construction details,
|
aList.add(
|
||||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.2"));//just Use on a multiblock controller.
|
translateToLocalFormatted(
|
||||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.3"));//(Sneak Use in creative to build)
|
"item.structurelib.constructableTrigger.desc.lshift.0",
|
||||||
aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.4"));//Quantity affects tier/mode/type
|
ChannelDataAccessor.countChannelData(aStack)));
|
||||||
|
ChannelDataAccessor.iterateChannelData(aStack).map(e -> e.getKey() + ": " + e.getValue())
|
||||||
|
.forEach(aList::add);
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
if (ChannelDataAccessor.hasSubChannel(aStack))
|
||||||
|
aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.5"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,48 @@
|
|||||||
package com.gtnewhorizon.structurelib.item;
|
package com.gtnewhorizon.structurelib.item;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.StructureLib;
|
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||||
import com.gtnewhorizon.structurelib.alignment.AlignmentUtility;
|
import static net.minecraft.util.StatCollector.translateToLocal;
|
||||||
import cpw.mods.fml.relauncher.Side;
|
|
||||||
import cpw.mods.fml.relauncher.SideOnly;
|
import java.util.List;
|
||||||
|
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.EnumChatFormatting;
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
import net.minecraft.world.World;
|
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 cpw.mods.fml.relauncher.Side;
|
||||||
import static net.minecraft.util.StatCollector.translateToLocal;
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
public class ItemFrontRotationTool extends Item {
|
public class ItemFrontRotationTool extends Item {
|
||||||
public ItemFrontRotationTool() {
|
|
||||||
setMaxStackSize(1);
|
|
||||||
setUnlocalizedName("structurelib.frontRotationTool");
|
|
||||||
setTextureName(MOD_ID + ":itemFrontRotationTool");
|
|
||||||
setCreativeTab(StructureLib.creativeTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ItemFrontRotationTool() {
|
||||||
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ) {
|
setMaxStackSize(1);
|
||||||
return AlignmentUtility.handle(player, world, x, y, z);
|
setUnlocalizedName("structurelib.frontRotationTool");
|
||||||
}
|
setTextureName(MOD_ID + ":itemFrontRotationTool");
|
||||||
|
setCreativeTab(StructureLib.creativeTab);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SideOnly(Side.CLIENT)
|
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
|
||||||
public void addInformation(ItemStack p_77624_1_, EntityPlayer p_77624_2_, List aList, boolean p_77624_4_) {
|
float hitX, float hitY, float hitZ) {
|
||||||
aList.add(translateToLocal("item.structurelib.frontRotationTool.desc.0"));//Triggers Front Rotation Interface
|
return AlignmentUtility.handle(player, world, x, y, z);
|
||||||
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
|
||||||
|
@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.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
package com.gtnewhorizon.structurelib.net;
|
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.nbt.NBTTagCompound;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraftforge.common.DimensionManager;
|
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 {
|
public abstract class AlignmentMessage implements IMessage {
|
||||||
|
|
||||||
int mPosX;
|
int mPosX;
|
||||||
int mPosY;
|
int mPosY;
|
||||||
int mPosZ;
|
int mPosZ;
|
||||||
int mPosD;
|
int mPosD;
|
||||||
int mAlign;
|
int mAlign;
|
||||||
|
|
||||||
public AlignmentMessage() {
|
public AlignmentMessage() {}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromBytes(ByteBuf pBuffer) {
|
public void fromBytes(ByteBuf pBuffer) {
|
||||||
@@ -47,11 +49,9 @@ public abstract class AlignmentMessage implements IMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AlignmentMessage(IAlignmentProvider provider) {
|
private AlignmentMessage(IAlignmentProvider provider) {
|
||||||
if (!(provider instanceof TileEntity))
|
if (!(provider instanceof TileEntity)) throw new IllegalArgumentException("Provider must be a TileEntity");
|
||||||
throw new IllegalArgumentException("Provider must be a TileEntity");
|
|
||||||
IAlignment alignment = provider.getAlignment();
|
IAlignment alignment = provider.getAlignment();
|
||||||
if (alignment == null)
|
if (alignment == null) throw new IllegalArgumentException("Passed in provider does not provide an alignment!");
|
||||||
throw new IllegalArgumentException("Passed in provider does not provide an alignment!");
|
|
||||||
TileEntity base = (TileEntity) provider;
|
TileEntity base = (TileEntity) provider;
|
||||||
mPosX = base.xCoord;
|
mPosX = base.xCoord;
|
||||||
mPosY = base.yCoord;
|
mPosY = base.yCoord;
|
||||||
@@ -69,8 +69,8 @@ public abstract class AlignmentMessage implements IMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class AlignmentQuery extends AlignmentMessage {
|
public static class AlignmentQuery extends AlignmentMessage {
|
||||||
public AlignmentQuery() {
|
|
||||||
}
|
public AlignmentQuery() {}
|
||||||
|
|
||||||
public AlignmentQuery(IAlignmentProvider provider) {
|
public AlignmentQuery(IAlignmentProvider provider) {
|
||||||
super(provider);
|
super(provider);
|
||||||
@@ -82,8 +82,8 @@ public abstract class AlignmentMessage implements IMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class AlignmentData extends AlignmentMessage {
|
public static class AlignmentData extends AlignmentMessage {
|
||||||
public AlignmentData() {
|
|
||||||
}
|
public AlignmentData() {}
|
||||||
|
|
||||||
private AlignmentData(AlignmentQuery query) {
|
private AlignmentData(AlignmentQuery query) {
|
||||||
mPosX = query.mPosX;
|
mPosX = query.mPosX;
|
||||||
@@ -103,10 +103,12 @@ public abstract class AlignmentMessage implements IMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ClientHandler implements IMessageHandler<AlignmentData, IMessage> {
|
public static class ClientHandler implements IMessageHandler<AlignmentData, IMessage> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IMessage onMessage(AlignmentData pMessage, MessageContext pCtx) {
|
public IMessage onMessage(AlignmentData pMessage, MessageContext pCtx) {
|
||||||
if (StructureLib.getCurrentPlayer().worldObj.provider.dimensionId == pMessage.mPosD) {
|
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) {
|
if (te instanceof IAlignmentProvider) {
|
||||||
IAlignment alignment = ((IAlignmentProvider) te).getAlignment();
|
IAlignment alignment = ((IAlignmentProvider) te).getAlignment();
|
||||||
if (alignment != null) {
|
if (alignment != null) {
|
||||||
@@ -119,6 +121,7 @@ public abstract class AlignmentMessage implements IMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ServerHandler implements IMessageHandler<AlignmentQuery, AlignmentData> {
|
public static class ServerHandler implements IMessageHandler<AlignmentQuery, AlignmentData> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AlignmentData onMessage(AlignmentQuery pMessage, MessageContext pCtx) {
|
public AlignmentData onMessage(AlignmentQuery pMessage, MessageContext pCtx) {
|
||||||
World world = DimensionManager.getWorld(pMessage.mPosD);
|
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);
|
TileEntity te = world.getTileEntity(pMessage.mPosX, pMessage.mPosY, pMessage.mPosZ);
|
||||||
if (te instanceof IAlignmentProvider) {
|
if (te instanceof IAlignmentProvider) {
|
||||||
IAlignment alignment = ((IAlignmentProvider) te).getAlignment();
|
IAlignment alignment = ((IAlignmentProvider) te).getAlignment();
|
||||||
if (alignment == null)
|
if (alignment == null) return null;
|
||||||
return null;
|
|
||||||
pMessage.mAlign = alignment.getExtendedFacing().getIndex();
|
pMessage.mAlign = alignment.getExtendedFacing().getIndex();
|
||||||
return new AlignmentData(pMessage);
|
return new AlignmentData(pMessage);
|
||||||
}
|
}
|
||||||
@@ -135,4 +137,4 @@ public abstract class AlignmentMessage implements IMessage {
|
|||||||
return null;
|
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,73 @@
|
|||||||
|
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<>();
|
||||||
|
|
||||||
|
public SetChannelDataMessage() {}
|
||||||
|
|
||||||
|
public SetChannelDataMessage(ItemStack trigger) {
|
||||||
|
ChannelDataAccessor.iterateChannelData(trigger).forEach(data::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fromBytes(ByteBuf buf) {
|
||||||
|
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) {
|
||||||
|
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 heldItem = player.getHeldItem();
|
||||||
|
if (heldItem != null && heldItem.getItem() instanceof ItemConstructableTrigger) {
|
||||||
|
ChannelDataAccessor.wipeChannelData(heldItem);
|
||||||
|
for (Entry<String, Integer> e : message.data) {
|
||||||
|
ChannelDataAccessor.setChannelData(heldItem, 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(),
|
||||||
|
heldItem);
|
||||||
|
}
|
||||||
|
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,31 @@
|
|||||||
|
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)) return false;
|
||||||
|
|
||||||
|
NoExplicitChannel that = (NoExplicitChannel) o;
|
||||||
|
|
||||||
|
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;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public interface IBlockPosConsumer {
|
public interface IBlockPosConsumer {
|
||||||
void consume(World world, int x, int y, int z);
|
|
||||||
|
void consume(World world, int x, int y, int z);
|
||||||
}
|
}
|
||||||
@@ -4,15 +4,16 @@ import net.minecraft.block.Block;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public interface ICustomBlockSetting {
|
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
|
* 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 world world that should be affected
|
||||||
* @param y y position to set
|
* @param x x position to set
|
||||||
* @param z z position to set
|
* @param y y position to set
|
||||||
* @param meta required meta
|
* @param z z position to set
|
||||||
*/
|
* @param meta required meta
|
||||||
void setBlock(World world, int x, int y, int z, int 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,175 +1,429 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.StructureLib;
|
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded;
|
||||||
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded;
|
||||||
|
|
||||||
|
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.item.ItemStack;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import com.gtnewhorizon.structurelib.StructureLib;
|
||||||
|
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||||
import static com.gtnewhorizon.structurelib.StructureLib.DEBUG_MODE;
|
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> {
|
public interface IStructureDefinition<T> {
|
||||||
/**
|
|
||||||
* Used internally
|
|
||||||
*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
default boolean check(T object, String piece, World world, ExtendedFacing extendedFacing,
|
/**
|
||||||
int basePositionX, int basePositionY, int basePositionZ,
|
* Used internally
|
||||||
int basePositionA, int basePositionB, int basePositionC,
|
* <p>
|
||||||
boolean forceCheckAllBlocks) {
|
* Might, but is not required, to throw {@link java.util.NoSuchElementException} if the given structure piece is not
|
||||||
return iterate(object, null, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
* found.
|
||||||
basePositionA, basePositionB, basePositionC, false, forceCheckAllBlocks);
|
*
|
||||||
}
|
* @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);
|
||||||
|
|
||||||
default boolean hints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean build(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
/**
|
||||||
int basePositionX, int basePositionY, int basePositionZ,
|
* Run a structure check.
|
||||||
int basePositionA, int basePositionB, int basePositionC) {
|
*
|
||||||
return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
* @param object context object. usually multiblock controller.
|
||||||
basePositionA, basePositionB, basePositionC, false, null);
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
default boolean buildOrHints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
|
/**
|
||||||
int basePositionX, int basePositionY, int basePositionZ,
|
* Spawn hint particles. Should not be called on server side.
|
||||||
int basePositionA, int basePositionB, int basePositionC,
|
*
|
||||||
boolean hintsOnly) {
|
* @param object context object. usually multiblock controller.
|
||||||
return iterate(object, trigger, getStructureFor(piece), world, extendedFacing, basePositionX, basePositionY, basePositionZ,
|
* @param piece the structure piece's string identifier.
|
||||||
basePositionA, basePositionB, basePositionC, hintsOnly, null);
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<T>[] elements, World world, ExtendedFacing extendedFacing,
|
/**
|
||||||
int basePositionX, int basePositionY, int basePositionZ,
|
* Build the multi in creative mode on a best-effort basis. Does not guarantee the structure is correct after a
|
||||||
int basePositionA, int basePositionB, int basePositionC,
|
* successful call.
|
||||||
boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
*
|
||||||
if (world.isRemote ^ hintsOnly) {
|
* @param object context object. usually multiblock controller.
|
||||||
return false;
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
//change base position to base offset
|
/**
|
||||||
basePositionA = -basePositionA;
|
* Call {@link #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} if
|
||||||
basePositionB = -basePositionB;
|
* {@code hintsOnly}, call
|
||||||
basePositionC = -basePositionC;
|
* {@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);
|
||||||
|
}
|
||||||
|
|
||||||
int[] abc = new int[]{basePositionA, basePositionB, basePositionC};
|
/**
|
||||||
int[] xyz = new int[3];
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
/**
|
||||||
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
* Cause a survival build.
|
||||||
for (IStructureElement<T> element : elements) {
|
*
|
||||||
if (element.isNavigating()) {
|
* @param object context object. usually multiblock controller.
|
||||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
* @param trigger The trigger item that contains channel data.
|
||||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
* @param piece the structure piece's string identifier.
|
||||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
* @param world the world object this check takes place in.
|
||||||
} else {
|
* @param extendedFacing the current structure's orientation.
|
||||||
extendedFacing.getWorldOffset(abc, xyz);
|
* @param basePositionX X location of the structure
|
||||||
xyz[0] += basePositionX;
|
* @param basePositionY Y location of the structure
|
||||||
xyz[1] += basePositionY;
|
* @param basePositionZ Z location of the structure
|
||||||
xyz[2] += basePositionZ;
|
* @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])) {
|
/**
|
||||||
if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) {
|
* Low level utility.
|
||||||
if (DEBUG_MODE) {
|
*
|
||||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " +
|
* @param object context object. usually multiblock controller.
|
||||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
* @param trigger The trigger item that contains channel data.
|
||||||
}
|
* @param elements the structure piece
|
||||||
return false;
|
* @param world the world object this check takes place in.
|
||||||
}
|
* @param extendedFacing the current structure's orientation.
|
||||||
} else {
|
* @param basePositionX X location of the structure
|
||||||
if (DEBUG_MODE) {
|
* @param basePositionY Y location of the structure
|
||||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " +
|
* @param basePositionZ Z location of the structure
|
||||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
* @param basePositionA see class javadoc
|
||||||
}
|
* @param basePositionB see class javadoc
|
||||||
return false;
|
* @param basePositionC see class javadoc
|
||||||
}
|
* @param hintsOnly whether to spawn hints or do creative build. no effect if
|
||||||
abc[0] += 1;
|
* {@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.
|
||||||
} else {
|
* @return true if iteration completed successfully.
|
||||||
for (IStructureElement<T> element : elements) {
|
* @param <T> type of context object.
|
||||||
if (element.isNavigating()) {
|
*/
|
||||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<T>[] elements, World world,
|
||||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
ExtendedFacing extendedFacing, int basePositionX, int basePositionY, int basePositionZ, int basePositionA,
|
||||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
int basePositionB, int basePositionC, boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||||
} else {
|
if (!world.isRemote && hintsOnly) {
|
||||||
extendedFacing.getWorldOffset(abc, xyz);
|
return false;
|
||||||
xyz[0] += basePositionX;
|
}
|
||||||
xyz[1] += basePositionY;
|
|
||||||
xyz[2] += basePositionZ;
|
|
||||||
|
|
||||||
if (world.blockExists(xyz[0], xyz[1], xyz[2])) {
|
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
||||||
if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) {
|
boolean success;
|
||||||
if (DEBUG_MODE) {
|
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " +
|
success = StructureUtility.iterateV2(
|
||||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
elements,
|
||||||
}
|
world,
|
||||||
return false;
|
extendedFacing,
|
||||||
}
|
basePositionX,
|
||||||
} else {
|
basePositionY,
|
||||||
if (DEBUG_MODE) {
|
basePositionZ,
|
||||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " +
|
basePositionA,
|
||||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
basePositionB,
|
||||||
}
|
basePositionC,
|
||||||
}
|
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
|
||||||
abc[0] += 1;
|
"check");
|
||||||
}
|
} else {
|
||||||
}
|
success = StructureUtility.iterateV2(
|
||||||
}
|
elements,
|
||||||
} else {
|
world,
|
||||||
if (hintsOnly) {
|
extendedFacing,
|
||||||
for (IStructureElement<T> element : elements) {
|
basePositionX,
|
||||||
if (element.isNavigating()) {
|
basePositionY,
|
||||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
basePositionZ,
|
||||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
basePositionA,
|
||||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
basePositionB,
|
||||||
} else {
|
basePositionC,
|
||||||
extendedFacing.getWorldOffset(abc, xyz);
|
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||||
xyz[0] += basePositionX;
|
"check force");
|
||||||
xyz[1] += basePositionY;
|
}
|
||||||
xyz[2] += basePositionZ;
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
element.spawnHint(object, world, xyz[0], xyz[1], xyz[2], trigger);
|
/**
|
||||||
|
* Create a new instance of builder.
|
||||||
abc[0] += 1;
|
*
|
||||||
}
|
* @param <T> type of context object
|
||||||
}
|
*/
|
||||||
} else {
|
static <T> StructureDefinition.Builder<T> builder() {
|
||||||
for (IStructureElement<T> element : elements) {
|
return StructureDefinition.builder();
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static <T> StructureDefinition.Builder<T> builder() {
|
|
||||||
return StructureDefinition.builder();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,43 +1,330 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
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.Collections;
|
||||||
|
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.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
import net.minecraft.world.World;
|
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> {
|
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);
|
boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||||
|
|
||||||
default int getStepA() {
|
boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
default int getStepB() {
|
/**
|
||||||
return 0;
|
* 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.SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
default int getStepC() {
|
/**
|
||||||
return 0;
|
* 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 resetA() {
|
/**
|
||||||
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.SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
default boolean resetB() {
|
/**
|
||||||
return false;
|
* Forget the messed up class dependency graph for now. this is just so convenient.
|
||||||
}
|
*/
|
||||||
|
default IStructureElementNoPlacement<T> noPlacement() {
|
||||||
|
return new IStructureElementNoPlacement<T>() {
|
||||||
|
|
||||||
default boolean resetC() {
|
@Override
|
||||||
return false;
|
public boolean check(T t, World world, int x, int y, int z) {
|
||||||
}
|
return IStructureElement.this.check(t, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
default boolean isNavigating() {
|
@Override
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/**
|
||||||
|
* This element either exists already, or does not yet have an implementation for survivalPlaceBlock, or some
|
||||||
|
* other unforeseen situations. TODO this definition doesn't seem right. Should we separate SKIP from EXISTS, or
|
||||||
|
* SKIP from ERROR?
|
||||||
|
*/
|
||||||
|
SKIP,
|
||||||
|
/**
|
||||||
|
* This element's space is occupied by other stuff and require player attention, or player missing required
|
||||||
|
* resource, or the block cannot be placed due to obscure mechanisms, or some other unforeseen situations.
|
||||||
|
*/
|
||||||
|
REJECT,
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+104
-28
@@ -1,41 +1,117 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
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.item.ItemStack;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use StructureUtility to instantiate
|
* Use StructureUtility to instantiate
|
||||||
*/
|
*/
|
||||||
public interface IStructureElementChain<T> extends IStructureElement<T> {
|
public interface IStructureElementChain<T> extends IStructureElement<T> {
|
||||||
IStructureElement<T>[] fallbacks();
|
|
||||||
|
|
||||||
@Override
|
IStructureElement<T>[] fallbacks();
|
||||||
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
|
@Override
|
||||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean check(T t, World world, int x, int y, int z) {
|
||||||
for (IStructureElement<T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
if (fallback.check(t, world, x, y, z)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
for (IStructureElement<T> fallback : fallbacks()) {
|
for (IStructureElement<T> fallback : fallbacks()) {
|
||||||
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
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 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());
|
||||||
|
is.add(e.getStacks());
|
||||||
|
}
|
||||||
|
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 haveSkip = false;
|
||||||
|
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:
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
|
haveSkip = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dump all that noise back into upstream
|
||||||
|
bufferedNoise.forEach(chatter);
|
||||||
|
// TODO need reconsider to ensure this is the right course of action
|
||||||
|
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
boolean haveSkip = false;
|
||||||
|
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:
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
|
haveSkip = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dump all that noise back into upstream
|
||||||
|
bufferedNoise.forEach(env.getChatter());
|
||||||
|
// TODO need reconsider to ensure this is the right course of action
|
||||||
|
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+25
-8
@@ -1,16 +1,33 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public interface IStructureElementCheckOnly<T> extends IStructureElement<T> {
|
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
|
@Override
|
||||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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.SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return PlaceResult.SKIP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+26
-4
@@ -1,11 +1,33 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public interface IStructureElementNoPlacement<T> extends IStructureElement<T> {
|
public interface IStructureElementNoPlacement<T> extends IStructureElement<T> {
|
||||||
@Override
|
|
||||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
@Override
|
||||||
return false;
|
default boolean placeBlock(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return PlaceResult.REJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default IStructureElementNoPlacement<T> noPlacement() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,28 +1,45 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use StructureUtility to instantiate
|
* Use StructureUtility to instantiate
|
||||||
*/
|
*/
|
||||||
interface IStructureNavigate<T> extends IStructureElement<T> {
|
interface IStructureNavigate<T> extends IStructureElement<T> {
|
||||||
@Override
|
|
||||||
default boolean check(T t, World world, int x, int y, int z) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean check(T t, World world, int x, int y, int z) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isNavigating() {
|
@Override
|
||||||
return true;
|
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return PlaceResult.SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isNavigating() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
interface IStructureWalker<T> {
|
||||||
|
|
||||||
|
boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
|
||||||
|
|
||||||
|
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||||
|
int c) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> IStructureWalker<T> ignoreBlockUnloaded(IStructureWalker<T> walker) {
|
||||||
|
return new IStructureWalker<T>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
|
||||||
|
return walker.visit(element, world, x, y, z, a, b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||||
|
int c) {
|
||||||
|
return walker.visit(element, world, x, y, z, a, b, c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> IStructureWalker<T> skipBlockUnloaded(IStructureWalker<T> walker) {
|
||||||
|
return new IStructureWalker<T>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
|
||||||
|
return walker.visit(element, world, x, y, z, a, b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
|
||||||
|
int c) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Architecture notes: this should never get any callback from survival build. All structure significant code belongs
|
||||||
|
* within the IStructureElement. This will make the user easier to maintain the IStructureDefinition. Structure Element
|
||||||
|
* knows the most about what this structure element should do, not the caller.
|
||||||
|
*/
|
||||||
|
public interface ISurvivalBuildEnvironment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the source of the items. This will be invoked exactly once per structure element build.
|
||||||
|
* <p>
|
||||||
|
* If the call chain ever got into the old API, this will be called at least once per
|
||||||
|
* {@link IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, ISurvivalBuildEnvironment, boolean)}
|
||||||
|
* call.
|
||||||
|
*/
|
||||||
|
IItemSource getSource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the origin of action. This will be invoked at least once per
|
||||||
|
* {@link IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, ISurvivalBuildEnvironment, boolean)}
|
||||||
|
* call.
|
||||||
|
*/
|
||||||
|
EntityPlayer getActor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default implementation with three parameters all being constants.
|
||||||
|
*
|
||||||
|
* @param source from where to drain resource
|
||||||
|
* @param actor source of action
|
||||||
|
*/
|
||||||
|
static ISurvivalBuildEnvironment create(IItemSource source, EntityPlayer actor) {
|
||||||
|
return new DefaultSurvivalBuildEnvironment(source, actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,11 @@ import net.minecraft.block.Block;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* a function to extract tier info from a block.
|
* a function to extract tier info from a block.
|
||||||
|
*
|
||||||
* @param <TIER>
|
* @param <TIER>
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ITierConverter<TIER> {
|
public interface ITierConverter<TIER> {
|
||||||
|
|
||||||
TIER convert(Block block, int meta);
|
TIER convert(Block block, int meta);
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,14 @@ package com.gtnewhorizon.structurelib.structure;
|
|||||||
/**
|
/**
|
||||||
* Implement this and use {@link StructureUtility#withContext(IStructureElement)} in case you need to record some
|
* Implement this and use {@link StructureUtility#withContext(IStructureElement)} in case you need to record some
|
||||||
* temporary states that don't need to live in your main context, which would usually live for the whole lifetime of the
|
* temporary states that don't need to live in your main context, which would usually live for the whole lifetime of the
|
||||||
* structure than just during structure check. Usually just save some few bytes, but becomes non trivial if you need to
|
* structure than just during structure check. Usually just save some few bytes, but becomes non-trivial if you need to
|
||||||
* record too much stuff.
|
* record too much stuff.
|
||||||
*
|
* <p>
|
||||||
* You will have to reset the context yourself though. The API can't guess when the old context becomes invalid.
|
* You will have to reset the context yourself though. The API can't guess when the old context becomes invalid.
|
||||||
|
*
|
||||||
* @param <CTX> the temporary state context
|
* @param <CTX> the temporary state context
|
||||||
*/
|
*/
|
||||||
public interface IWithExtendedContext<CTX> {
|
public interface IWithExtendedContext<CTX> {
|
||||||
|
|
||||||
CTX getCurrentContext();
|
CTX getCurrentContext();
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,63 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
import net.minecraft.item.ItemStack;
|
import java.util.function.Consumer;
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
class LazyStructureElement<T> implements IStructureElementDeferred<T> {
|
class LazyStructureElement<T> implements IStructureElementDeferred<T> {
|
||||||
private Function<T, IStructureElement<T>> to;
|
|
||||||
private IStructureElement<T> elem;
|
|
||||||
|
|
||||||
public LazyStructureElement(Function<T, IStructureElement<T>> to) {
|
private Function<T, IStructureElement<T>> to;
|
||||||
this.to = to;
|
private IStructureElement<T> elem;
|
||||||
}
|
|
||||||
|
|
||||||
private IStructureElement<T> get(T t) {
|
public LazyStructureElement(Function<T, IStructureElement<T>> to) {
|
||||||
if (to != null) {
|
this.to = to;
|
||||||
elem = to.apply(t);
|
}
|
||||||
to = null;
|
|
||||||
}
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private IStructureElement<T> get(T t) {
|
||||||
public boolean check(T t, World world, int x, int y, int z) {
|
if (to != null) {
|
||||||
return get(t).check(t, world, x, y, z);
|
elem = to.apply(t);
|
||||||
}
|
to = null;
|
||||||
|
}
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
public boolean check(T t, World world, int x, int y, int z) {
|
||||||
return get(t).placeBlock(t, world, x, y, z, trigger);
|
return get(t).check(t, world, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
public boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return get(t).spawnHint(t, world, x, y, z, trigger);
|
return get(t).placeBlock(t, world, x, y, z, trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return get(t).spawnHint(t, world, x, y, z, trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return get(t).getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||||
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
|
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,201 +1,242 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.gtnewhorizon.structurelib.StructureLib.DEBUG_MODE;
|
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||||
import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
|
|
||||||
|
|
||||||
public class StructureDefinition<T> implements IStructureDefinition<T> {
|
public class StructureDefinition<T> implements IStructureDefinition<T> {
|
||||||
private final Map<Character, IStructureElement<T>> elements;
|
|
||||||
private final Map<String, String> shapes;
|
|
||||||
private final Map<String, IStructureElement<T>[]> structures;
|
|
||||||
|
|
||||||
public static <B> Builder<B> builder() {
|
private final Map<Character, IStructureElement<T>> elements;
|
||||||
return new Builder<>();
|
private final Map<String, String> shapes;
|
||||||
}
|
private final Map<String, IStructureElement<T>[]> structures;
|
||||||
|
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||||
|
|
||||||
private StructureDefinition(
|
public static <B> Builder<B> builder() {
|
||||||
Map<Character, IStructureElement<T>> elements,
|
return new Builder<>();
|
||||||
Map<String, String> shapes,
|
}
|
||||||
Map<String, IStructureElement<T>[]> structures) {
|
|
||||||
this.elements = elements;
|
|
||||||
this.shapes = shapes;
|
|
||||||
this.structures = structures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder<T> {
|
private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
|
||||||
private static final char A = '\uA000';
|
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
|
||||||
private static final char B = '\uB000';
|
this.elements = elements;
|
||||||
private static final char C = '\uC000';
|
this.shapes = shapes;
|
||||||
private char d = '\uD000';
|
this.structures = structures;
|
||||||
private final Map<Vec3Impl, Character> navigates;
|
this.occupiedSpaces = occupiedSpaces;
|
||||||
private final Map<Character, IStructureElement<T>> elements;
|
}
|
||||||
private final Map<String, String> shapes;
|
|
||||||
|
|
||||||
private Builder() {
|
public static class Builder<T> {
|
||||||
navigates = new HashMap<>();
|
|
||||||
elements = new HashMap<>();
|
|
||||||
shapes = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Character, IStructureElement<T>> getElements() {
|
private static final char A = '\uA000';
|
||||||
return elements;
|
private static final char B = '\uB000';
|
||||||
}
|
private static final char C = '\uC000';
|
||||||
|
private char d = '\uD000';
|
||||||
|
private final Map<Vec3Impl, Character> navigates;
|
||||||
|
private final Map<Character, IStructureElement<T>> elements;
|
||||||
|
private final Map<String, String> shapes;
|
||||||
|
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
|
||||||
|
|
||||||
public Map<String, String> getShapes() {
|
private Builder() {
|
||||||
return shapes;
|
navigates = new HashMap<>();
|
||||||
}
|
elements = new HashMap<>();
|
||||||
|
shapes = new HashMap<>();
|
||||||
|
occupiedSpaces = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public Map<Character, IStructureElement<T>> getElements() {
|
||||||
* Adds shape
|
return elements;
|
||||||
* + is anything but air
|
}
|
||||||
* - is air checks
|
|
||||||
* space bar is skip
|
|
||||||
* ~ is also skip (but marks controller position, optional and logically it is a space...)
|
|
||||||
* rest needs to be defined
|
|
||||||
* <p>
|
|
||||||
* next char is next block(a)
|
|
||||||
* next string is next line(b)
|
|
||||||
* next string[] is next slice(c)
|
|
||||||
* <p>
|
|
||||||
* char A000-FFFF range is reserved for generated skips
|
|
||||||
*
|
|
||||||
* @param name unlocalized/code name
|
|
||||||
* @param structurePiece generated or written struct - DO NOT STORE IT ANYWHERE, or at least set them to null afterwards
|
|
||||||
* @return this builder
|
|
||||||
*/
|
|
||||||
public Builder<T> addShape(String name, String[][] structurePiece) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
if (structurePiece.length > 0) {
|
|
||||||
for (String[] strings : structurePiece) {
|
|
||||||
if (strings.length > 0) {
|
|
||||||
for (String string : strings) {
|
|
||||||
builder.append(string).append(B);
|
|
||||||
}
|
|
||||||
builder.setLength(builder.length() - 1);
|
|
||||||
}
|
|
||||||
builder.append(C);
|
|
||||||
}
|
|
||||||
builder.setLength(builder.length() - 1);
|
|
||||||
}
|
|
||||||
int a = 0, b = 0, c = 0;
|
|
||||||
for (int i = 0; i < builder.length(); i++) {
|
|
||||||
char ch = builder.charAt(i);
|
|
||||||
if (ch == ' ' || ch == '~') {
|
|
||||||
builder.setCharAt(i, A);
|
|
||||||
ch = A;
|
|
||||||
}
|
|
||||||
if (ch == A) {
|
|
||||||
a++;
|
|
||||||
} else if (ch == B) {
|
|
||||||
a = 0;
|
|
||||||
b++;
|
|
||||||
} else if (ch == C) {
|
|
||||||
a = 0;
|
|
||||||
b = 0;
|
|
||||||
c++;
|
|
||||||
} else if (a != 0 || b != 0 || c != 0) {
|
|
||||||
Vec3Impl vec3 = new Vec3Impl(a, b, c);
|
|
||||||
Character navigate = navigates.get(vec3);
|
|
||||||
if (navigate == null) {
|
|
||||||
navigate = d++;
|
|
||||||
navigates.put(vec3, navigate);
|
|
||||||
addElement(navigate, step(vec3));
|
|
||||||
}
|
|
||||||
builder.setCharAt(i - 1, navigate);
|
|
||||||
a = 0;
|
|
||||||
b = 0;
|
|
||||||
c = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
|
public Map<String, String> getShapes() {
|
||||||
|
return shapes;
|
||||||
|
}
|
||||||
|
|
||||||
if (built.contains("+")) {
|
/**
|
||||||
addElement('+', notAir());
|
* Adds shape. Shape is a two-dimensional string array. Each <b>character</b> inside any of these strings will
|
||||||
}
|
* be later mapped to a particular type of {@link IStructureElement} as indicated by
|
||||||
if (built.contains("-")) {
|
* {@link #addElement(Character, IStructureElement)}.
|
||||||
addElement('-', isAir());
|
* <p>
|
||||||
}
|
* next char is next block(a) next string is next line(b) next string[] is next slice(c)
|
||||||
shapes.put(name, built);
|
* <p>
|
||||||
return this;
|
* There are a few special reserved characters that are pre-mapped to some common structure elements
|
||||||
}
|
* <ul>
|
||||||
|
* <li>+ is anything but air</li>
|
||||||
|
* <li>- is air checks</li>
|
||||||
|
* <li>space bar is skip</li>
|
||||||
|
* <li>~ is also skip (but marks controller position, not optional)</li>
|
||||||
|
* <li>char {@code A000-FFFF} range is reserved for generated skips</li>
|
||||||
|
* </ul>
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param name unlocalized/code name
|
||||||
|
* @param structurePiece generated or written struct - DO NOT STORE IT ANYWHERE, or at least set them to null
|
||||||
|
* afterwards
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder<T> addShape(String name, String[][] structurePiece) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
if (structurePiece.length > 0) {
|
||||||
|
for (String[] strings : structurePiece) {
|
||||||
|
if (strings.length > 0) {
|
||||||
|
for (String string : strings) {
|
||||||
|
builder.append(string).append(B);
|
||||||
|
}
|
||||||
|
builder.setLength(builder.length() - 1);
|
||||||
|
}
|
||||||
|
builder.append(C);
|
||||||
|
}
|
||||||
|
builder.setLength(builder.length() - 1);
|
||||||
|
}
|
||||||
|
Set<Vec3Impl> occupiedSpace = new HashSet<>();
|
||||||
|
// these track the global current location
|
||||||
|
int aa = 0, bb = 0, cc = 0;
|
||||||
|
// these track the vec3 towards next meaningful element
|
||||||
|
int a = 0, b = 0, c = 0;
|
||||||
|
// I do know these are pretty bad variable naming, but I have no better idea
|
||||||
|
for (int i = 0; i < builder.length(); i++) {
|
||||||
|
char ch = builder.charAt(i);
|
||||||
|
if (ch == ' ') {
|
||||||
|
builder.setCharAt(i, A);
|
||||||
|
ch = A;
|
||||||
|
} else if (ch == '~') {
|
||||||
|
builder.setCharAt(i, A);
|
||||||
|
ch = A;
|
||||||
|
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
|
||||||
|
}
|
||||||
|
if (ch == A) {
|
||||||
|
aa++;
|
||||||
|
a++;
|
||||||
|
} else if (ch == B) {
|
||||||
|
aa = 0;
|
||||||
|
a = 0;
|
||||||
|
bb++;
|
||||||
|
b++;
|
||||||
|
} else if (ch == C) {
|
||||||
|
aa = 0;
|
||||||
|
bb = 0;
|
||||||
|
a = 0;
|
||||||
|
b = 0;
|
||||||
|
c++;
|
||||||
|
cc++;
|
||||||
|
} else if (a != 0 || b != 0 || c != 0) {
|
||||||
|
Vec3Impl vec3 = new Vec3Impl(a, b, c);
|
||||||
|
Character navigate = navigates.get(vec3);
|
||||||
|
if (navigate == null) {
|
||||||
|
navigate = d++;
|
||||||
|
navigates.put(vec3, navigate);
|
||||||
|
addElement(navigate, step(vec3));
|
||||||
|
}
|
||||||
|
builder.setCharAt(i - 1, navigate);
|
||||||
|
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
|
||||||
|
aa++;
|
||||||
|
a = 0;
|
||||||
|
b = 0;
|
||||||
|
c = 0;
|
||||||
|
} else {
|
||||||
|
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
|
||||||
|
aa++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
occupiedSpaces.put(name, occupiedSpace);
|
||||||
elements.putIfAbsent(name, structurePiece);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IStructureDefinition<T> build() {
|
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
|
||||||
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
|
||||||
if (DEBUG_MODE) {
|
|
||||||
return new StructureDefinition<>(new HashMap<>(elements), new HashMap<>(shapes), structures);
|
|
||||||
} else {
|
|
||||||
return structures::get;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
if (built.contains("+")) {
|
||||||
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
|
addElement('+', notAir());
|
||||||
Set<Integer> missing = findMissing();
|
}
|
||||||
if (missing.isEmpty()) {
|
if (built.contains("-")) {
|
||||||
return shapes.entrySet().stream()
|
addElement('-', isAir());
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey,
|
}
|
||||||
e -> e.getValue().chars()
|
shapes.put(name, built);
|
||||||
.mapToObj(c -> (char) c)
|
return this;
|
||||||
.distinct()
|
}
|
||||||
.map(elements::get)
|
|
||||||
.toArray(IStructureElement[]::new)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Missing Structure Element bindings for (chars as integers): " +
|
|
||||||
Arrays.toString(missing.toArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
/**
|
||||||
private Map<String, IStructureElement<T>[]> compileStructureMap() {
|
* @deprecated use the unboxed version
|
||||||
Set<Integer> missing = findMissing();
|
*/
|
||||||
if (missing.isEmpty()) {
|
@Deprecated
|
||||||
return shapes.entrySet().stream()
|
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey,
|
elements.putIfAbsent(name, structurePiece);
|
||||||
e -> e.getValue().chars().mapToObj(c -> elements.get((char) c)).toArray(IStructureElement[]::new)
|
return this;
|
||||||
));
|
}
|
||||||
} else {
|
|
||||||
throw new RuntimeException("Missing Structure Element bindings for (chars as integers): " +
|
|
||||||
Arrays.toString(missing.toArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<Integer> findMissing() {
|
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
|
||||||
return shapes.values().stream()
|
elements.putIfAbsent(name, structurePiece);
|
||||||
.flatMapToInt(CharSequence::chars)
|
return this;
|
||||||
.filter(i -> !elements.containsKey((char) i))
|
}
|
||||||
.boxed()
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Character, IStructureElement<T>> getElements() {
|
public IStructureDefinition<T> build() {
|
||||||
return elements;
|
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
|
||||||
}
|
return new StructureDefinition<>(
|
||||||
|
new HashMap<>(elements),
|
||||||
|
new HashMap<>(shapes),
|
||||||
|
structures,
|
||||||
|
occupiedSpaces);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> getShapes() {
|
@SuppressWarnings("unchecked")
|
||||||
return shapes;
|
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
|
||||||
}
|
Set<Integer> missing = findMissing();
|
||||||
|
if (missing.isEmpty()) {
|
||||||
|
return shapes.entrySet().stream().collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
e -> e.getValue().chars().mapToObj(c -> (char) c).distinct().map(elements::get)
|
||||||
|
.toArray(IStructureElement[]::new)));
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Missing Structure Element bindings for (chars as integers): "
|
||||||
|
+ Arrays.toString(missing.toArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, IStructureElement<T>[]> getStructures() {
|
@SuppressWarnings("unchecked")
|
||||||
return structures;
|
private Map<String, IStructureElement<T>[]> compileStructureMap() {
|
||||||
}
|
Set<Integer> missing = findMissing();
|
||||||
|
if (missing.isEmpty()) {
|
||||||
|
return shapes.entrySet().stream().collect(
|
||||||
|
Collectors.toMap(
|
||||||
|
Map.Entry::getKey,
|
||||||
|
e -> e.getValue().chars().mapToObj(c -> elements.get((char) c))
|
||||||
|
.toArray(IStructureElement[]::new)));
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Missing Structure Element bindings for (chars as integers): "
|
||||||
|
+ Arrays.toString(missing.toArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private Set<Integer> findMissing() {
|
||||||
public IStructureElement<T>[] getStructureFor(String name) {
|
return shapes.values().stream().flatMapToInt(CharSequence::chars)
|
||||||
IStructureElement<T>[] elements = structures.get(name);
|
.filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
|
||||||
if (elements == null)
|
}
|
||||||
throw new NoSuchElementException(name);
|
}
|
||||||
return elements;
|
|
||||||
}
|
public Map<Character, IStructureElement<T>> getElements() {
|
||||||
}
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getShapes() {
|
||||||
|
return shapes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, IStructureElement<T>[]> getStructures() {
|
||||||
|
return structures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IStructureElement<T>[] getStructureFor(String name) {
|
||||||
|
IStructureElement<T>[] elements = structures.get(name);
|
||||||
|
if (elements == null) throw new NoSuchElementException(name);
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC) {
|
||||||
|
Set<Vec3Impl> occupiedSpace = occupiedSpaces.get(name);
|
||||||
|
if (occupiedSpace == null) throw new NoSuchElementException(name);
|
||||||
|
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal bridge class for easing implementation
|
||||||
|
*/
|
||||||
|
abstract class StructureElement_Bridge<T> implements IStructureElement<T> {
|
||||||
|
|
||||||
|
// seal the deprecated method to prevent accidental overrides
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public final PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||||
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
|
return survivalPlaceBlock(t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the default implementation. we don't need that.
|
||||||
|
public abstract PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
+70
@@ -0,0 +1,70 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.StructureLibAPI;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||||
|
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
|
||||||
|
|
||||||
|
class SurvivalBuildStructureWalker<T> implements IStructureWalker<T> {
|
||||||
|
|
||||||
|
final T object;
|
||||||
|
final ItemStack trigger;
|
||||||
|
private final int elementBudget;
|
||||||
|
private final ISurvivalBuildEnvironment params;
|
||||||
|
private final boolean check;
|
||||||
|
private int built = -1;
|
||||||
|
|
||||||
|
private final AutoPlaceEnvironment env;
|
||||||
|
|
||||||
|
public SurvivalBuildStructureWalker(T object, ItemStack trigger, int elementBudget,
|
||||||
|
ISurvivalBuildEnvironment params, IStructureDefinition<?> definition, String piece, ExtendedFacing facing,
|
||||||
|
int[] baseOffsetABC, boolean check) {
|
||||||
|
this.object = object;
|
||||||
|
this.trigger = trigger;
|
||||||
|
this.elementBudget = elementBudget;
|
||||||
|
this.params = params;
|
||||||
|
this.check = check;
|
||||||
|
|
||||||
|
env = new AutoPlaceEnvironment(
|
||||||
|
params.getActor(),
|
||||||
|
params.getActor()::addChatComponentMessage,
|
||||||
|
definition,
|
||||||
|
piece,
|
||||||
|
facing,
|
||||||
|
baseOffsetABC);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
|
||||||
|
env.offsetABC[0] = a;
|
||||||
|
env.offsetABC[1] = b;
|
||||||
|
env.offsetABC[2] = c;
|
||||||
|
env.setSource(params.getSource());
|
||||||
|
PlaceResult placeResult = element.survivalPlaceBlock(object, world, x, y, z, trigger, env);
|
||||||
|
if (placeResult != PlaceResult.SKIP && built == -1) built = 0;
|
||||||
|
switch (placeResult) {
|
||||||
|
case SKIP:
|
||||||
|
return true;
|
||||||
|
case ACCEPT:
|
||||||
|
if (check) element.check(object, world, x, y, z);
|
||||||
|
return ++built < elementBudget;
|
||||||
|
case REJECT:
|
||||||
|
StructureLibAPI.markHintParticleError(params.getActor(), world, x, y, z);
|
||||||
|
return false;
|
||||||
|
case ACCEPT_STOP:
|
||||||
|
if (check) element.check(object, world, x, y, z);
|
||||||
|
built += 1;
|
||||||
|
// intentional fallthrough
|
||||||
|
case STOP:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBuilt() {
|
||||||
|
return built;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure.adders;
|
package com.gtnewhorizon.structurelib.structure.adders;
|
||||||
|
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
|
|
||||||
public interface IBlockAdder<T> {
|
public interface IBlockAdder<T> {
|
||||||
/**
|
|
||||||
* Callback on block added, needs to check if block is valid (and add it)
|
/**
|
||||||
*
|
* Callback on block added, needs to check if block is valid (and add it)
|
||||||
* @param block block attempted to add
|
*
|
||||||
* @param meta meta of block attempted to add
|
* @param block block attempted to add
|
||||||
* @return is structure still valid
|
* @param meta meta of block attempted to add
|
||||||
*/
|
* @return is structure still valid
|
||||||
boolean apply(T t, Block block, int meta);
|
*/
|
||||||
|
boolean apply(T t, Block block, int meta);
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,12 @@ package com.gtnewhorizon.structurelib.structure.adders;
|
|||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
|
||||||
public interface ITileAdder<T> {
|
public interface ITileAdder<T> {
|
||||||
/**
|
|
||||||
* Callback to add hatch, needs to check if tile is valid (and add it)
|
/**
|
||||||
*
|
* Callback to add hatch, needs to check if tile is valid (and add it)
|
||||||
* @param tileEntity tile
|
*
|
||||||
* @return managed to add hatch (structure still valid)
|
* @param tileEntity tile
|
||||||
*/
|
* @return managed to add hatch (structure still valid)
|
||||||
boolean apply(T t, TileEntity tileEntity);
|
*/
|
||||||
|
boolean apply(T t, TileEntity tileEntity);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Adder interfaces used in various standard structure elements.
|
||||||
|
*/
|
||||||
|
package com.gtnewhorizon.structurelib.structure.adders;
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* All public stuff in this package and its subpackages are considered part of the public API.
|
* This package provide support for multiblock structure checking.
|
||||||
* Backwards compatibility is maintained to the maximum extend possible.
|
* <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.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import net.minecraft.inventory.IInventory;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
public class InventoryIterable<Inv extends IInventory> implements Iterable<ItemStack> {
|
||||||
|
|
||||||
|
private final Inv inv;
|
||||||
|
private final int maxSlot;
|
||||||
|
|
||||||
|
public InventoryIterable(Inv inv) {
|
||||||
|
this(inv, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryIterable(Inv inv, int maxSlot) {
|
||||||
|
this.inv = inv;
|
||||||
|
this.maxSlot = maxSlot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Inv getInventory() {
|
||||||
|
return inv;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ItemStack> iterator() {
|
||||||
|
return new Iterator<ItemStack>() {
|
||||||
|
|
||||||
|
private int ptr = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return ptr < inv.getSizeInventory() && (maxSlot == -1 || ptr < maxSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack next() {
|
||||||
|
if (!hasNext()) throw new NoSuchElementException();
|
||||||
|
return inv.getStackInSlot(ptr++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
inv.setInventorySlotContents(ptr - 1, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,379 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, glee8e This file is part of StructureLib.
|
||||||
|
* <p>
|
||||||
|
* StructureLib is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
|
||||||
|
* Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any
|
||||||
|
* later version.
|
||||||
|
* <p>
|
||||||
|
* StructureLib 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 Lesser General Public License for more
|
||||||
|
* details.
|
||||||
|
* <p>
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along with Foobar; if not, write to the Free
|
||||||
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.entity.player.InventoryPlayer;
|
||||||
|
import net.minecraft.init.Items;
|
||||||
|
import net.minecraft.inventory.IInventory;
|
||||||
|
import net.minecraft.inventory.InventoryEnderChest;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
|
||||||
|
import com.gtnewhorizon.structurelib.util.InventoryUtility.ItemStackExtractor.APIType;
|
||||||
|
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is part of API, but is not stable. Use at your own risk.
|
||||||
|
*/
|
||||||
|
public class InventoryUtility {
|
||||||
|
|
||||||
|
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>();
|
||||||
|
private static final List<Predicate<? super EntityPlayerMP>> enableEnder = new CopyOnWriteArrayList<>();
|
||||||
|
/**
|
||||||
|
* The remove() of the Iterable returned must be implemented!
|
||||||
|
*/
|
||||||
|
private static final SortedRegistry<InventoryProvider<?>> inventoryProviders = new SortedRegistry<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
inventoryProviders.register("5000-main-inventory", new InventoryProvider<InventoryIterable<InventoryPlayer>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InventoryIterable<InventoryPlayer> getInventory(EntityPlayerMP player) {
|
||||||
|
return new InventoryIterable<>(player.inventory, player.inventory.mainInventory.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty(InventoryIterable<InventoryPlayer> inv) {
|
||||||
|
// player save its content using means other than inv.markDirty()
|
||||||
|
// here we only need to sync it to client
|
||||||
|
inv.getInventory().player.inventoryContainer.detectAndSendChanges();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
inventoryProviders
|
||||||
|
.register("7000-ender-inventory", new InventoryProvider<InventoryIterable<InventoryEnderChest>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InventoryIterable<InventoryEnderChest> getInventory(EntityPlayerMP player) {
|
||||||
|
if (enableEnder.stream().anyMatch(p -> p.test(player)))
|
||||||
|
return new InventoryIterable<>(player.getInventoryEnderChest());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty(InventoryIterable<InventoryEnderChest> inv) {
|
||||||
|
// inv.getInventory().markDirty();
|
||||||
|
// TODO this seems to be a noop
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerEnableEnderCondition(Predicate<? super EntityPlayerMP> predicate) {
|
||||||
|
enableEnder.add(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerStackExtractor(String key, ItemStackExtractor val) {
|
||||||
|
if (Arrays.stream(APIType.values()).noneMatch(val::isAPIImplemented))
|
||||||
|
throw new IllegalArgumentException("Must implement at least one API");
|
||||||
|
stackExtractors.register(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <Inv extends IInventory> void registerStackExtractor(String key,
|
||||||
|
Function<ItemStack, ? extends Inv> extractor) {
|
||||||
|
registerStackExtractor(key, newItemStackProvider(extractor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerInventoryProvider(String key, InventoryProvider<?> val) {
|
||||||
|
inventoryProviders.register(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <Inv extends IInventory> void registerInventoryProvider(String key,
|
||||||
|
Function<EntityPlayerMP, ? extends Inv> extractor) {
|
||||||
|
registerInventoryProvider(key, newInventoryProvider(extractor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Iterator<? extends ItemStackExtractor> getStackExtractors() {
|
||||||
|
return stackExtractors.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <Inv extends IInventory> InventoryProvider<InventoryIterable<Inv>> newInventoryProvider(
|
||||||
|
Function<EntityPlayerMP, ? extends Inv> extractor) {
|
||||||
|
return new InventoryProvider<InventoryIterable<Inv>>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InventoryIterable<Inv> getInventory(EntityPlayerMP player) {
|
||||||
|
Inv inv = extractor.apply(player);
|
||||||
|
return inv != null ? new InventoryIterable<>(inv) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty(InventoryIterable<Inv> inv) {
|
||||||
|
inv.getInventory().markDirty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStackExtractor newItemStackProvider(Function<ItemStack, ? extends IInventory> extractor) {
|
||||||
|
return new ItemStackExtractor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAPIImplemented(APIType type) {
|
||||||
|
return type == APIType.MAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int takeFromStack(Predicate<ItemStack> predicate, boolean simulate, int count,
|
||||||
|
ItemStackCounter store, ItemStack stack, ItemStack filter, EntityPlayerMP player) {
|
||||||
|
IInventory inv = extractor.apply(stack);
|
||||||
|
if (inv == null) return 0;
|
||||||
|
int found = takeFromInventory(
|
||||||
|
new InventoryIterable<>(inv),
|
||||||
|
predicate,
|
||||||
|
simulate,
|
||||||
|
count,
|
||||||
|
store,
|
||||||
|
filter,
|
||||||
|
player,
|
||||||
|
false);
|
||||||
|
if (found > 0) inv.markDirty();
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* take count amount of stacks that matches given filter. might take partial amount of stuff, so simulation is
|
||||||
|
* suggested if you ever need to take more than one.
|
||||||
|
*
|
||||||
|
* @param player source of stacks
|
||||||
|
* @param predicate item stack filter
|
||||||
|
* @param simulate whether to do removal
|
||||||
|
* @param count let's hope int size is enough...
|
||||||
|
* @return amount taken. never negative nor bigger than count...
|
||||||
|
*/
|
||||||
|
public static Map<ItemStack, Integer> takeFromInventory(EntityPlayerMP player, Predicate<ItemStack> predicate,
|
||||||
|
boolean simulate, int count) {
|
||||||
|
ItemStackCounterImpl store = new ItemStackCounterImpl();
|
||||||
|
int sum = 0;
|
||||||
|
for (InventoryProvider<?> provider : inventoryProviders) {
|
||||||
|
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, null);
|
||||||
|
if (sum >= count) return store.getStore();
|
||||||
|
}
|
||||||
|
return store.getStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* take count amount of stacks that matches given filter. might take partial amount of stuff, so simulation is
|
||||||
|
* suggested if you ever need to take more than one.
|
||||||
|
*
|
||||||
|
* @param player source of stacks
|
||||||
|
* @param filter the precise type of item to extract. stackSize matters
|
||||||
|
* @param simulate whether to do removal
|
||||||
|
* @return amount taken. never negative nor bigger than count...
|
||||||
|
*/
|
||||||
|
public static int takeFromInventory(EntityPlayerMP player, ItemStack filter, boolean simulate) {
|
||||||
|
int sum = 0;
|
||||||
|
int count = filter.stackSize;
|
||||||
|
ItemStackPredicate predicate = ItemStackPredicate.from(filter, NBTMode.EXACT);
|
||||||
|
ItemStackCounterImpl store = new ItemStackCounterImpl();
|
||||||
|
for (InventoryProvider<?> provider : inventoryProviders) {
|
||||||
|
sum += takeFromPlayer(player, predicate, simulate, count - sum, store, provider, filter);
|
||||||
|
if (sum >= count) return sum;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// workaround java generics issue
|
||||||
|
private static <R extends Iterable<ItemStack>> int takeFromPlayer(EntityPlayerMP player,
|
||||||
|
Predicate<ItemStack> predicate, boolean simulate, int count, ItemStackCounterImpl store,
|
||||||
|
InventoryProvider<R> provider, ItemStack filter) {
|
||||||
|
R inv = provider.getInventory(player);
|
||||||
|
if (inv == null) return 0;
|
||||||
|
int taken = takeFromInventory(inv, predicate, simulate, count, store, filter, player, true);
|
||||||
|
if (taken > 0) provider.markDirty(inv);
|
||||||
|
return taken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* take count amount of stacks that matches given filter. Will do 1 level of recursion to try find more stacks....
|
||||||
|
* e.g. from backpacks...
|
||||||
|
*
|
||||||
|
* @param inv source of stacks
|
||||||
|
* @param predicate item stack filter
|
||||||
|
* @param simulate whether to do removal
|
||||||
|
* @param count let's hope int size is enough...
|
||||||
|
* @param recursive do recursive lookup using {@link #getStackExtractors() stack extractors}
|
||||||
|
* @return amount taken. never negative nor bigger than count...
|
||||||
|
*/
|
||||||
|
public static Map<ItemStack, Integer> takeFromInventory(Iterable<ItemStack> inv, Predicate<ItemStack> predicate,
|
||||||
|
boolean simulate, int count, boolean recursive) {
|
||||||
|
ItemStackCounterImpl store = new ItemStackCounterImpl();
|
||||||
|
takeFromInventory(inv, predicate, simulate, count, store, null, null, recursive);
|
||||||
|
return store.getStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int takeFromInventory(@Nonnull Iterable<ItemStack> inv, @Nonnull Predicate<ItemStack> predicate,
|
||||||
|
boolean simulate, int count, @Nonnull ItemStackCounter store, @Nullable ItemStack filter,
|
||||||
|
@Nullable EntityPlayerMP player, boolean recursive) {
|
||||||
|
int found = 0;
|
||||||
|
ItemStack copiedFilter = null;
|
||||||
|
if (filter != null) {
|
||||||
|
copiedFilter = new ItemStack(filter.getItem(), filter.stackSize, Items.feather.getDamage(filter));
|
||||||
|
copiedFilter.setTagCompound(filter.stackTagCompound);
|
||||||
|
}
|
||||||
|
for (Iterator<ItemStack> iterator = inv.iterator(); iterator.hasNext();) {
|
||||||
|
ItemStack stack = iterator.next();
|
||||||
|
// invalid stack
|
||||||
|
if (stack == null || stack.getItem() == null || stack.stackSize <= 0) continue;
|
||||||
|
|
||||||
|
if (predicate.test(stack)) {
|
||||||
|
found += stack.stackSize;
|
||||||
|
if (found > count) {
|
||||||
|
int surplus = found - count;
|
||||||
|
store.add(stack, stack.stackSize - surplus);
|
||||||
|
if (!simulate) {
|
||||||
|
// leave the surplus in its place
|
||||||
|
stack.stackSize = surplus;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
store.add(stack, stack.stackSize);
|
||||||
|
if (!simulate) iterator.remove();
|
||||||
|
if (found == count) return count;
|
||||||
|
}
|
||||||
|
if (!recursive) continue;
|
||||||
|
for (ItemStackExtractor f : stackExtractors) {
|
||||||
|
if (filter != null && f.isAPIImplemented(APIType.EXTRACT_ONE_STACK)) {
|
||||||
|
copiedFilter.stackSize = count - found;
|
||||||
|
found += f.getItem(stack, copiedFilter, simulate, player);
|
||||||
|
} else {
|
||||||
|
found += f.takeFromStack(predicate, simulate, count - found, store, stack, filter, player);
|
||||||
|
}
|
||||||
|
if (found >= count) return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OptimizedExtractor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a particular type of item. The extractor can choose to not return all items contained within this
|
||||||
|
* item as long as it makes sense, but the author should inform the player of this.
|
||||||
|
* <p>
|
||||||
|
* Whether optimized extractor do recursive extraction is at the discretion of implementor.
|
||||||
|
*
|
||||||
|
* @param source from where an extraction should be attempted
|
||||||
|
* @param toExtract stack to extract. should not be mutated! match the NBT tag using EXACT mode.
|
||||||
|
* @param simulate true if only query but does not actually remove
|
||||||
|
* @param player executor of extraction, or null if from a machine
|
||||||
|
* @return amount extracted
|
||||||
|
*/
|
||||||
|
int extract(ItemStack source, ItemStack toExtract, boolean simulate, EntityPlayerMP player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface InventoryProvider<R extends Iterable<ItemStack>> {
|
||||||
|
|
||||||
|
R getInventory(EntityPlayerMP player);
|
||||||
|
|
||||||
|
void markDirty(R inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ItemStackExtractor {
|
||||||
|
|
||||||
|
enum APIType {
|
||||||
|
MAIN,
|
||||||
|
EXTRACT_ONE_STACK,
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAPIImplemented(APIType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a particular type of item. The extractor can choose to not return all items contained within this
|
||||||
|
* item as long as it makes sense, but the author should inform the player of this.
|
||||||
|
* <p>
|
||||||
|
* Whether this method does recursive extraction is at the discretion of implementor.
|
||||||
|
*
|
||||||
|
* @param predicate the main filtering predicate. never null. It's assumed predicate always returns true on
|
||||||
|
* filter, if that is not null
|
||||||
|
* @param store where to store extracted items. Should increment the
|
||||||
|
* @param source from where an extraction should be attempted
|
||||||
|
* @param filter stack to extract. should not be mutated! match the NBT tag using EXACT mode. might be null.
|
||||||
|
* @param simulate true if only query but does not actually remove
|
||||||
|
* @param player executor of extraction, or null if from a machine
|
||||||
|
* @return amount extracted, or -1 if this is not implemented
|
||||||
|
*/
|
||||||
|
int takeFromStack(Predicate<ItemStack> predicate, boolean simulate, int count, ItemStackCounter store,
|
||||||
|
ItemStack source, ItemStack filter, EntityPlayerMP player);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a particular type of item. The extractor can choose to not return all items contained within this
|
||||||
|
* item as long as it makes sense, but the author should inform the player of this.
|
||||||
|
* <p>
|
||||||
|
* Whether this method does recursive extraction is at the discretion of implementor.
|
||||||
|
*
|
||||||
|
* @param source from where an extraction should be attempted
|
||||||
|
* @param toExtract stack to extract. should not be mutated! match the NBT tag using EXACT mode.
|
||||||
|
* @param simulate true if only query but does not actually remove
|
||||||
|
* @param player executor of extraction, or null if from a machine
|
||||||
|
* @return amount extracted, or -1 if this is not implemented
|
||||||
|
*/
|
||||||
|
default int getItem(ItemStack source, ItemStack toExtract, boolean simulate, EntityPlayerMP player) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ItemStackExtractor createOnlyOptimized(@Nonnull OptimizedExtractor optimizedExtractor) {
|
||||||
|
return new ItemStackExtractor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAPIImplemented(APIType type) {
|
||||||
|
return type == APIType.EXTRACT_ONE_STACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int takeFromStack(Predicate<ItemStack> predicate, boolean simulate, int count,
|
||||||
|
ItemStackCounter store, ItemStack stack, ItemStack filter, EntityPlayerMP player) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItem(ItemStack source, ItemStack toExtract, boolean simulate, EntityPlayerMP player) {
|
||||||
|
return optimizedExtractor.extract(source, toExtract, simulate, player);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ItemStackCounter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add some amount of stack. Note stackSize is used instead of stack.stackSize.
|
||||||
|
*/
|
||||||
|
void add(ItemStack stack, int stackSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ItemStackCounterImpl implements ItemStackCounter {
|
||||||
|
|
||||||
|
private final Map<ItemStack, Integer> store = new ItemStackMap<>(true);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(ItemStack stack, int stackSize) {
|
||||||
|
if (stack == null || stack.getItem() == null || stackSize <= 0) throw new IllegalArgumentException();
|
||||||
|
store.merge(stack, stackSize, Integer::sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ItemStack, Integer> getStore() {
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
public class ItemStackArrayIterable implements Iterable<ItemStack> {
|
||||||
|
|
||||||
|
private final ItemStack[] stacks;
|
||||||
|
|
||||||
|
public ItemStackArrayIterable(ItemStack[] stacks) {
|
||||||
|
this.stacks = stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack[] getStacks() {
|
||||||
|
return stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ItemStack> iterator() {
|
||||||
|
return new Iterator<ItemStack>() {
|
||||||
|
|
||||||
|
private int ptr = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return ptr < stacks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack next() {
|
||||||
|
return stacks[ptr++];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
stacks[ptr - 1] = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.init.Items;
|
||||||
|
import net.minecraft.item.Item;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.NBTBase;
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
|
||||||
|
public final class ItemStackPredicate implements Predicate<ItemStack> {
|
||||||
|
|
||||||
|
public static ItemStackPredicate from(Item item) {
|
||||||
|
return new ItemStackPredicate(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStackPredicate from(ItemStack itemStack) {
|
||||||
|
return new ItemStackPredicate(itemStack.getItem()).setMeta(Items.feather.getDamage(itemStack));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStackPredicate from(ItemStack itemStack, NBTMode mode) {
|
||||||
|
return new ItemStackPredicate(itemStack.getItem()).setMeta(Items.feather.getDamage(itemStack))
|
||||||
|
.setTag(mode, itemStack.stackTagCompound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStackPredicate from(Block block) {
|
||||||
|
return new ItemStackPredicate(Item.getItemFromBlock(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Item item;
|
||||||
|
private int meta = -1;
|
||||||
|
|
||||||
|
private NBTTagCompound tag;
|
||||||
|
private NBTMode mode = NBTMode.IGNORE;
|
||||||
|
|
||||||
|
private ItemStackPredicate(Item item) {
|
||||||
|
this.item = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStackPredicate ignoreMeta() {
|
||||||
|
this.meta = -1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStackPredicate setMeta(int meta) {
|
||||||
|
this.meta = meta;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStackPredicate setTag(NBTMode mode, NBTTagCompound tag) {
|
||||||
|
this.mode = mode;
|
||||||
|
this.tag = tag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(ItemStack itemStack) {
|
||||||
|
if (item != null) if (itemStack.getItem() != item) return false;
|
||||||
|
if (meta != -1) if (Items.feather.getDamage(itemStack) != meta) return false;
|
||||||
|
return mode.test(tag, itemStack.stackTagCompound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NBTMode implements BiPredicate<NBTTagCompound, NBTTagCompound> {
|
||||||
|
|
||||||
|
IGNORE {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IN {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
|
||||||
|
if (lhs == null || lhs.hasNoTags()) return true;
|
||||||
|
if (rhs == null || rhs.hasNoTags()) return false;
|
||||||
|
for (String key : MiscUtils.getTagKeys(lhs)) {
|
||||||
|
if (!rhs.hasKey(key, lhs.func_150299_b(key))) return false;
|
||||||
|
NBTBase tag = lhs.getTag(key);
|
||||||
|
if (tag instanceof NBTTagCompound) {
|
||||||
|
if (!test((NBTTagCompound) tag, rhs.getCompoundTag(key))) return false;
|
||||||
|
} else {
|
||||||
|
if (!tag.equals(rhs.getTag(key))) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
EXACT {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
|
||||||
|
if (lhs != null && lhs.hasNoTags()) lhs = null;
|
||||||
|
if (rhs != null && rhs.hasNoTags()) rhs = null;
|
||||||
|
return Objects.equals(lhs, rhs);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IGNORE_KNOWN_INSIGNIFICANT_TAGS {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
|
||||||
|
// fast path for empty tags
|
||||||
|
if (rhs == null || rhs.hasNoTags()) return lhs == null || lhs.hasNoTags();
|
||||||
|
// TODO make an implementation without copying a potentially huge tag
|
||||||
|
rhs = (NBTTagCompound) rhs.copy();
|
||||||
|
for (String s : KNOWN_INSIGNIFICANT_TAGS) rhs.removeTag(s);
|
||||||
|
return EXACT.test(lhs, rhs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] KNOWN_INSIGNIFICANT_TAGS = { "display", // TODO expand/refine this
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* not related to a certain mod!
|
||||||
|
*/
|
||||||
|
public class MiscUtils {
|
||||||
|
|
||||||
|
private MiscUtils() {}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static Set<String> getTagKeys(NBTTagCompound tag) {
|
||||||
|
return tag.func_150296_c();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SortedRegistry<V> implements Iterable<V> {
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
// TODO allow a user to edit the order with a configuration file
|
||||||
|
baked = new ArrayList<>(store.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,28 @@
|
|||||||
package com.gtnewhorizon.structurelib.util;
|
package com.gtnewhorizon.structurelib.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import net.minecraft.dispenser.IPosition;
|
import net.minecraft.dispenser.IPosition;
|
||||||
import net.minecraftforge.common.util.ForgeDirection;
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
public class Vec3Impl implements Comparable<Vec3Impl> {
|
public class Vec3Impl implements Comparable<Vec3Impl> {
|
||||||
|
|
||||||
public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0);
|
public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0);
|
||||||
|
private static final Map<Vec3Impl, Vec3Impl> pool = new HashMap<>();
|
||||||
private final int val0;
|
private final int val0;
|
||||||
private final int val1;
|
private final int val1;
|
||||||
private final int val2;
|
private final int val2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* THIS WILL CAUSE THIS VECTOR TO PERMANENTLY RESIDE IN STATIC MEMORY. DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE
|
||||||
|
* DOING!!!
|
||||||
|
*/
|
||||||
|
public static Vec3Impl getFromPool(int in0, int in1, int in2) {
|
||||||
|
return pool.computeIfAbsent(new Vec3Impl(in0, in1, in2), Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
public Vec3Impl(int in0, int in1, int in2) {
|
public Vec3Impl(int in0, int in1, int in2) {
|
||||||
this.val0 = in0;
|
this.val0 = in0;
|
||||||
this.val1 = in1;
|
this.val1 = in1;
|
||||||
@@ -23,11 +37,15 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
|
|||||||
* Gets the coordinate.
|
* Gets the coordinate.
|
||||||
*/
|
*/
|
||||||
public int get(int index) {
|
public int get(int index) {
|
||||||
switch (index){
|
switch (index) {
|
||||||
case 0: return val0;
|
case 0:
|
||||||
case 1: return val1;
|
return val0;
|
||||||
case 2: return val2;
|
case 1:
|
||||||
default: return 0;
|
return val1;
|
||||||
|
case 2:
|
||||||
|
return val2;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +71,8 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl offset(ForgeDirection facing, int n) {
|
public Vec3Impl offset(ForgeDirection facing, int n) {
|
||||||
return n == 0 ? this : new Vec3Impl(val0 + facing.offsetX * n, val1 + facing.offsetY * n, val2 + facing.offsetZ * n);
|
return n == 0 ? this
|
||||||
|
: new Vec3Impl(val0 + facing.offsetX * n, val1 + facing.offsetY * n, val2 + facing.offsetZ * n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl add(Vec3Impl pos) {
|
public Vec3Impl add(Vec3Impl pos) {
|
||||||
@@ -64,16 +83,18 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
|
|||||||
return new Vec3Impl(val0 - pos.val0, val1 - pos.val1, val2 - pos.val2);
|
return new Vec3Impl(val0 - pos.val0, val1 - pos.val1, val2 - pos.val2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl add(int pos0,int pos1,int pos2) {
|
public Vec3Impl add(int pos0, int pos1, int pos2) {
|
||||||
return new Vec3Impl(val0 + pos0, val1 + pos1, val2 + pos2);
|
return new Vec3Impl(val0 + pos0, val1 + pos1, val2 + pos2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl sub(int pos0,int pos1,int pos2) {
|
public Vec3Impl sub(int pos0, int pos1, int pos2) {
|
||||||
return new Vec3Impl(val0 - pos0, val1 - pos1, val2 - pos2);
|
return new Vec3Impl(val0 - pos0, val1 - pos1, val2 - pos2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl crossProduct(Vec3Impl vec) {
|
public Vec3Impl crossProduct(Vec3Impl vec) {
|
||||||
return new Vec3Impl(val1 * vec.val2 - val2 * vec.val1, val2 * vec.val0 - val0 * vec.val2,
|
return new Vec3Impl(
|
||||||
|
val1 * vec.val2 - val2 * vec.val1,
|
||||||
|
val2 * vec.val0 - val0 * vec.val2,
|
||||||
val0 * vec.val1 - val1 * vec.val0);
|
val0 * vec.val1 - val1 * vec.val0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,17 +116,17 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
|
|||||||
|
|
||||||
public double distanceSq(double x, double y, double z, boolean useCenter) {
|
public double distanceSq(double x, double y, double z, boolean useCenter) {
|
||||||
double d0 = useCenter ? 0.5D : 0.0D;
|
double d0 = useCenter ? 0.5D : 0.0D;
|
||||||
double d1 = (double)val0 + d0 - x;
|
double d1 = (double) val0 + d0 - x;
|
||||||
double d2 = (double)val1 + d0 - y;
|
double d2 = (double) val1 + d0 - y;
|
||||||
double d3 = (double)val2 + d0 - z;
|
double d3 = (double) val2 + d0 - z;
|
||||||
return d1 * d1 + d2 * d2 + d3 * d3;
|
return d1 * d1 + d2 * d2 + d3 * d3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int manhattanDistance(Vec3Impl to) {
|
public int manhattanDistance(Vec3Impl to) {
|
||||||
float f = (float)Math.abs(to.val0 - val0);
|
float f = (float) Math.abs(to.val0 - val0);
|
||||||
float f1 = (float)Math.abs(to.val1 - val1);
|
float f1 = (float) Math.abs(to.val1 - val1);
|
||||||
float f2 = (float)Math.abs(to.val2 - val2);
|
float f2 = (float) Math.abs(to.val2 - val2);
|
||||||
return (int)(f + f1 + f2);
|
return (int) (f + f1 + f2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -114,14 +135,14 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Vec3Impl abs() {
|
public Vec3Impl abs() {
|
||||||
return new Vec3Impl(Math.abs(val0),Math.abs(val1),Math.abs(val2));
|
return new Vec3Impl(Math.abs(val0), Math.abs(val1), Math.abs(val2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
return true;
|
return true;
|
||||||
} else if (o instanceof Vec3Impl) {
|
} else if (o instanceof Vec3Impl) {
|
||||||
Vec3Impl vec3i = (Vec3Impl)o;
|
Vec3Impl vec3i = (Vec3Impl) o;
|
||||||
return val0 == vec3i.val0 && val1 == vec3i.val1 && val2 == vec3i.val2;
|
return val0 == vec3i.val0 && val1 == vec3i.val1 && val2 == vec3i.val2;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -130,4 +151,4 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (val1 + val2 * 31) * 31 + val0;
|
return (val1 + val2 * 31) * 31 + val0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,20 @@
|
|||||||
package com.gtnewhorizon.structurelib.util;
|
package com.gtnewhorizon.structurelib.util;
|
||||||
/*
|
/*
|
||||||
A subclass of java.util.random that implements the Xorshift random number
|
* A subclass of java.util.random that implements the Xorshift random number generator <p> - it is 30% faster than the
|
||||||
generator
|
* generator from Java's library - it produces random sequences of higher quality than java.util.Random - this class
|
||||||
<p>
|
* also provides a clone() function <p> Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt();
|
||||||
- it is 30% faster than the generator from Java's library - it produces
|
* //pull a random number <p> To use the class in legacy code, you may also instantiate an XSRandom object and assign it
|
||||||
random sequences of higher quality than java.util.Random - this class also
|
* to a java.util.Random object: java.util.Random rand = new XSRandom(); <p> for an explanation of the algorithm, see
|
||||||
provides a clone() function
|
* http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html
|
||||||
<p>
|
* @author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs http://www.elmenreich.tk <p> This code is released
|
||||||
Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt();
|
* under the GNU Lesser General Public License Version 3 http://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
//pull a random number
|
|
||||||
<p>
|
|
||||||
To use the class in legacy code, you may also instantiate an XSRandom object
|
|
||||||
and assign it to a java.util.Random object: java.util.Random rand = new
|
|
||||||
XSRandom();
|
|
||||||
<p>
|
|
||||||
for an explanation of the algorithm, see
|
|
||||||
http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html
|
|
||||||
|
|
||||||
@author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs
|
|
||||||
* http://www.elmenreich.tk
|
|
||||||
* <p>
|
|
||||||
* This code is released under the GNU Lesser General Public License Version 3
|
|
||||||
* http://www.gnu.org/licenses/lgpl-3.0.txt
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XSTR - Xorshift ThermiteRandom
|
* XSTR - Xorshift ThermiteRandom Modified by Bogdan-G 03.06.2016 version 0.0.4
|
||||||
* Modified by Bogdan-G
|
|
||||||
* 03.06.2016
|
|
||||||
* version 0.0.4
|
|
||||||
*/
|
*/
|
||||||
public class XSTR extends Random {
|
public class XSTR extends Random {
|
||||||
|
|
||||||
@@ -40,26 +23,25 @@ public class XSTR extends Random {
|
|||||||
private static final long GAMMA = 0x9e3779b97f4a7c15L;
|
private static final long GAMMA = 0x9e3779b97f4a7c15L;
|
||||||
private static final int PROBE_INCREMENT = 0x9e3779b9;
|
private static final int PROBE_INCREMENT = 0x9e3779b9;
|
||||||
private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
|
private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
|
||||||
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
|
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
|
||||||
private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
|
private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
|
||||||
private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
|
private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
|
||||||
public static final XSTR XSTR_INSTANCE=new XSTR(){
|
public static final XSTR XSTR_INSTANCE = new XSTR() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setSeed(long seed) {
|
public synchronized void setSeed(long seed) {
|
||||||
if(!Thread.currentThread().getStackTrace()[2].getClassName().equals(Random.class.getName())) {
|
if (!Thread.currentThread().getStackTrace()[2].getClassName().equals(Random.class.getName())) {
|
||||||
throw new NoSuchMethodError("This is meant to be shared!, leave seed state alone!");
|
throw new NoSuchMethodError("This is meant to be shared!, leave seed state alone!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MODIFIED BY: Robotia
|
* MODIFIED BY: Robotia Modification: Implemented Random class seed generator
|
||||||
Modification: Implemented Random class seed generator
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new pseudo random number generator. The seed is initialized to
|
* Creates a new pseudo random number generator. The seed is initialized to the current time, as if by
|
||||||
* the current time, as if by
|
|
||||||
* <code>setSeed(System.currentTimeMillis());</code>.
|
* <code>setSeed(System.currentTimeMillis());</code>.
|
||||||
*/
|
*/
|
||||||
public XSTR() {
|
public XSTR() {
|
||||||
@@ -79,8 +61,8 @@ public class XSTR extends Random {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new pseudo random number generator, starting with the specified
|
* Creates a new pseudo random number generator, starting with the specified seed, using
|
||||||
* seed, using <code>setSeed(seed);</code>.
|
* <code>setSeed(seed);</code>.
|
||||||
*
|
*
|
||||||
* @param seed the initial seed
|
* @param seed the initial seed
|
||||||
*/
|
*/
|
||||||
@@ -108,9 +90,8 @@ public class XSTR extends Random {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the seed for this pseudo random number generator. As described
|
* Sets the seed for this pseudo random number generator. As described above, two instances of the same random
|
||||||
* above, two instances of the same random class, starting with the same
|
* class, starting with the same seed, produce the same results, if the same methods are called.
|
||||||
* seed, produce the same results, if the same methods are called.
|
|
||||||
*
|
*
|
||||||
* @param seed the new seed
|
* @param seed the new seed
|
||||||
*/
|
*/
|
||||||
@@ -128,9 +109,8 @@ public class XSTR extends Random {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of George Marsaglia's elegant Xorshift random generator
|
* Implementation of George Marsaglia's elegant Xorshift random generator 30% faster and better quality than the
|
||||||
* 30% faster and better quality than the built-in java.util.random see also
|
* built-in java.util.random see also see http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
|
||||||
* see http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
|
|
||||||
*
|
*
|
||||||
* @param nbits will shift nbits bits
|
* @param nbits will shift nbits bits
|
||||||
* @return next seed
|
* @return next seed
|
||||||
@@ -170,15 +150,14 @@ public class XSTR extends Random {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a pseudorandom, uniformly distributed {@code int} value between 0
|
* Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the specified value
|
||||||
* (inclusive) and the specified value (exclusive), drawn from this random
|
* (exclusive), drawn from this random number generator's sequence. The general contract of {@code nextInt} is that
|
||||||
* number generator's sequence. The general contract of {@code nextInt} is
|
* one {@code int} value in the specified range is pseudorandomly generated and returned. All {@code bound} possible
|
||||||
* that one {@code int} value in the specified range is pseudorandomly
|
* {@code int} values are produced with (approximately) equal probability. The method {@code nextInt(int bound)} is
|
||||||
* generated and returned. All {@code bound} possible {@code int} values are
|
* implemented by class {@code Random} as if by:
|
||||||
* produced with (approximately) equal probability. The method
|
*
|
||||||
* {@code nextInt(int bound)} is implemented by class {@code Random} as if
|
* <pre>
|
||||||
* by:
|
* {@code
|
||||||
* <pre> {@code
|
|
||||||
* public int nextInt(int bound) {
|
* public int nextInt(int bound) {
|
||||||
* if (bound <= 0)
|
* if (bound <= 0)
|
||||||
* throw new IllegalArgumentException("bound must be positive");
|
* throw new IllegalArgumentException("bound must be positive");
|
||||||
@@ -192,57 +171,43 @@ public class XSTR extends Random {
|
|||||||
* val = bits % bound;
|
* val = bits % bound;
|
||||||
* } while (bits - val + (bound-1) < 0);
|
* } while (bits - val + (bound-1) < 0);
|
||||||
* return val;
|
* return val;
|
||||||
* }}</pre>
|
* }}
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* <p>The hedge "approx
|
|
||||||
* imately" is used in the foregoing description only because the next
|
|
||||||
* method is only approximately an unbiased source of independently chosen
|
|
||||||
* bits. If it were a perfect source of randomly chosen bits, then the
|
|
||||||
* algorithm shown would choose {@code int} values from the stated range
|
|
||||||
* with perfect uniformity.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The algorithm is slightly tricky. It rejects values that would result in
|
* The hedge "approx imately" is used in the foregoing description only because the next method is only
|
||||||
* an uneven distribution (due to the fact that 2^31 is not divisible by n).
|
* approximately an unbiased source of independently chosen bits. If it were a perfect source of randomly chosen
|
||||||
* The probability of a value being rejected depends on n. The worst case is
|
* bits, then the algorithm shown would choose {@code int} values from the stated range with perfect uniformity.
|
||||||
* n=2^30+1, for which the probability of a reject is 1/2, and the expected
|
|
||||||
* number of iterations before the loop terminates is 2.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The algorithm treats the case where n is a power of two specially: it
|
* The algorithm is slightly tricky. It rejects values that would result in an uneven distribution (due to the fact
|
||||||
* returns the correct number of high-order bits from the underlying
|
* that 2^31 is not divisible by n). The probability of a value being rejected depends on n. The worst case is
|
||||||
* pseudo-random number generator. In the absence of special treatment, the
|
* n=2^30+1, for which the probability of a reject is 1/2, and the expected number of iterations before the loop
|
||||||
* correct number of <i>low-order</i> bits would be returned. Linear
|
* terminates is 2.
|
||||||
* congruential pseudo-random number generators such as the one implemented
|
* <p>
|
||||||
* by this class are known to have short periods in the sequence of values
|
* The algorithm treats the case where n is a power of two specially: it returns the correct number of high-order
|
||||||
* of their low-order bits. Thus, this special case greatly increases the
|
* bits from the underlying pseudo-random number generator. In the absence of special treatment, the correct number
|
||||||
* length of the sequence of values returned by successive calls to this
|
* of <i>low-order</i> bits would be returned. Linear congruential pseudo-random number generators such as the one
|
||||||
* method if n is a small power of two.
|
* implemented by this class are known to have short periods in the sequence of values of their low-order bits.
|
||||||
|
* Thus, this special case greatly increases the length of the sequence of values returned by successive calls to
|
||||||
|
* this method if n is a small power of two.
|
||||||
*
|
*
|
||||||
* @param bound the upper bound (exclusive). Must be positive.
|
* @param bound the upper bound (exclusive). Must be positive.
|
||||||
* @return the next pseudorandom, uniformly distributed {@code int} value
|
* @return the next pseudorandom, uniformly distributed {@code int} value between zero (inclusive) and {@code bound}
|
||||||
* between zero (inclusive) and {@code bound} (exclusive) from this random
|
* (exclusive) from this random number generator's sequence
|
||||||
* number generator's sequence
|
|
||||||
* @throws IllegalArgumentException if bound is not positive
|
* @throws IllegalArgumentException if bound is not positive
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int nextInt(int bound) {
|
public int nextInt(int bound) {
|
||||||
//if (bound <= 0) {
|
// if (bound <= 0) {
|
||||||
//throw new RuntimeException("BadBound");
|
// throw new RuntimeException("BadBound");
|
||||||
//}
|
// }
|
||||||
|
|
||||||
/*int r = next(31);
|
/*
|
||||||
int m = bound - 1;
|
* int r = next(31); int m = bound - 1; if ((bound & m) == 0) // i.e., bound is a power of 2 { r = (int) ((bound
|
||||||
if ((bound & m) == 0) // i.e., bound is a power of 2
|
* * (long) r) >> 31); } else { for (int u = r; u - (r = u % bound) + m < 0; u = next(31)) ; } return r;
|
||||||
{
|
*/
|
||||||
r = (int) ((bound * (long) r) >> 31);
|
// speedup, new nextInt ~+40%
|
||||||
} else {
|
|
||||||
for (int u = r;
|
|
||||||
u - (r = u % bound) + m < 0;
|
|
||||||
u = next(31))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return r;*/
|
|
||||||
//speedup, new nextInt ~+40%
|
|
||||||
long last = seed ^ seed << 21;
|
long last = seed ^ seed << 21;
|
||||||
last ^= last >>> 35;
|
last ^= last >>> 35;
|
||||||
last ^= last << 4;
|
last ^= last << 4;
|
||||||
@@ -269,10 +234,11 @@ public class XSTR extends Random {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextBytes(byte[] bytes_arr) {
|
public void nextBytes(byte[] bytes_arr) {
|
||||||
for (int iba = 0, lenba = bytes_arr.length; iba < lenba; ) {
|
for (int iba = 0, lenba = bytes_arr.length; iba < lenba;) {
|
||||||
for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); nba-- > 0; rndba >>= Byte.SIZE) {
|
for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); nba--
|
||||||
|
> 0; rndba >>= Byte.SIZE) {
|
||||||
bytes_arr[iba++] = (byte) rndba;
|
bytes_arr[iba++] = (byte) rndba;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user