mirror of
https://github.com/GTNewHorizons/StructureLib.git
synced 2026-11-11 15:57:39 +00:00
Compare commits
147
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e3aefd9fd | ||
|
|
c4b091888b | ||
|
|
68fbdf8a81 | ||
|
|
f3bf5fc1ea | ||
|
|
9f9ab7acd9 | ||
|
|
0fe5a74863 | ||
|
|
d650c173bf | ||
|
|
137f651486 | ||
|
|
ff88cbf2d7 | ||
|
|
4922760b46 | ||
|
|
45bd25401f | ||
|
|
050f0b5a5c | ||
|
|
2c5fa02a9e | ||
|
|
287e8317c9 | ||
|
|
95d1533342 | ||
|
|
19179ec894 | ||
|
|
d8b301457f | ||
|
|
40edf05109 | ||
|
|
4e5a92aff7 | ||
|
|
a09e6058a9 | ||
|
|
e774dd5e10 | ||
|
|
0866dbee98 | ||
|
|
208f7ffc41 | ||
|
|
88a6021546 | ||
|
|
a05267a566 | ||
|
|
dd3413dcaa | ||
|
|
34394ef8a5 | ||
|
|
7c0d3f4bd6 | ||
|
|
cd4f3617be | ||
|
|
256bc60d35 | ||
|
|
70426ab7b3 | ||
|
|
0030c02e9a | ||
|
|
7382db1bbd | ||
|
|
b669cbecf1 | ||
|
|
6283931667 | ||
|
|
3b3bc4b9ad | ||
|
|
bef4fdd949 | ||
|
|
4a06c8f95b | ||
|
|
241bed1989 | ||
|
|
f5594c25db | ||
|
|
7cda901ae5 | ||
|
|
538459eab5 | ||
|
|
3a9ec8b6ac | ||
|
|
330a13271b | ||
|
|
bf5dd5566b | ||
|
|
b9d0143245 | ||
|
|
43fc39c99b | ||
|
|
6337536dc3 | ||
|
|
9b96560119 | ||
|
|
3ce1c877e3 | ||
|
|
3d150d95cb | ||
|
|
03e84e3d37 | ||
|
|
547833bedb | ||
|
|
61e0d4b7e7 | ||
|
|
6867ed50e6 | ||
|
|
e7a47c199b | ||
|
|
b2bc474382 | ||
|
|
eef6baea4b | ||
|
|
efd49701e1 | ||
|
|
a7f0d6be72 | ||
|
|
0ff38c2da9 | ||
|
|
307bb1714c | ||
|
|
7d8e6a0ea5 | ||
|
|
b6d2af2170 | ||
|
|
8309567076 | ||
|
|
23312198a6 | ||
|
|
fa5e2ae4ca | ||
|
|
a8a1547845 | ||
|
|
113e05b7e6 | ||
|
|
080785ad05 | ||
|
|
6c14ca0a1d | ||
|
|
ea38c4e1d6 | ||
|
|
50e8c97aa3 | ||
|
|
9a82239f2c | ||
|
|
8ed2be38eb | ||
|
|
582f59e73d | ||
|
|
464e44de85 | ||
|
|
392d2ace8a | ||
|
|
978938d450 | ||
|
|
7a2328c294 | ||
|
|
d2cd45e07b | ||
|
|
8a24900623 | ||
|
|
15a0034e03 | ||
|
|
7aae6dc31c | ||
|
|
875bc1990e | ||
|
|
74aefd22be | ||
|
|
72ff51d093 | ||
|
|
7d74a65ec0 | ||
|
|
0ac8908b37 | ||
|
|
2dd4349e54 | ||
|
|
f813104ec4 | ||
|
|
4fe9eb6ea5 | ||
|
|
c0814274ec | ||
|
|
23605fc22d | ||
|
|
8ee90a7338 | ||
|
|
9bd19b9254 | ||
|
|
df4c54ea8d | ||
|
|
e71edd50b0 | ||
|
|
1b7cee7c4e | ||
|
|
03f2c31191 | ||
|
|
9b318f68fb | ||
|
|
adc91bb1dd | ||
|
|
21198c4d39 | ||
|
|
e70ea765f8 | ||
|
|
56a083ce6f | ||
|
|
7389f349db | ||
|
|
5b6b51f80c | ||
|
|
1481b68133 | ||
|
|
ebf2d6f0e1 | ||
|
|
7e7064e006 | ||
|
|
ebcf62fd97 | ||
|
|
74dde96905 | ||
|
|
d73d2bffc2 | ||
|
|
94055bbd6a | ||
|
|
dc1b1fc4db | ||
|
|
fd38dfc31f | ||
|
|
cb82b73e78 | ||
|
|
91d4a8022e | ||
|
|
a12722c4b0 | ||
|
|
b2624a3d36 | ||
|
|
b34e901a93 | ||
|
|
e155c69b20 | ||
|
|
82622d1e15 | ||
|
|
a168702151 | ||
|
|
73f113ed9b | ||
|
|
c19ac860d4 | ||
|
|
d33cdd79c4 | ||
|
|
ed24f15033 | ||
|
|
15c34061bc | ||
|
|
728f25114b | ||
|
|
4a8c154855 | ||
|
|
f65010a547 | ||
|
|
609e50428e | ||
|
|
e8f5cd6489 | ||
|
|
8de7026a00 | ||
|
|
33d377cea7 | ||
|
|
da98b476b0 | ||
|
|
9dd145bc67 | ||
|
|
83b7c35ed1 | ||
|
|
bb0bebf3a2 | ||
|
|
a49ed1996b | ||
|
|
6d5d31f5ab | ||
|
|
3eafe426ba | ||
|
|
eea5227fe3 | ||
|
|
1f520cfd73 | ||
|
|
8357fdc826 | ||
|
|
27bb7b2ad9 |
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,17 @@
|
|||||||
|
name: Optimize images
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ master, main, release/** ]
|
||||||
|
paths:
|
||||||
|
- "**/*.png"
|
||||||
|
|
||||||
|
push:
|
||||||
|
branches: [ master, main, release/** ]
|
||||||
|
paths:
|
||||||
|
- "**/*.png"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
optimize-images:
|
||||||
|
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
|
||||||
|
secrets: inherit
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
name: Deploy Javadoc
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- feature/doc
|
||||||
|
- '**/*-javadoc'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
delete:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prune:
|
||||||
|
if: github.event_name == 'delete'
|
||||||
|
concurrency: javadoc-publish
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
result_hash: ${{ steps.deploy.outputs.hash }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: gh-pages
|
||||||
|
|
||||||
|
- name: Deploy 🚀
|
||||||
|
id: deploy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if git rm -r $TARGET_FOLDER ; then
|
||||||
|
echo Nothing to clean up
|
||||||
|
fi
|
||||||
|
|
||||||
|
find . -name javadoc -type d -print | sed 's#^\./\(.*\)/javadoc$#\1#' | sort --version-sort --reverse > index.txt
|
||||||
|
git add index.txt
|
||||||
|
git config user.name "${GITHUB_ACTOR}"
|
||||||
|
git config user.email "${GITHUB_ACTOR_ID}-${GITHUB_ACTOR}@users.noreply.github.com"
|
||||||
|
git commit -m "Clean up javadoc after ref $TARGET_FOLDER is deleted"
|
||||||
|
git push
|
||||||
|
echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
TARGET_FOLDER: ${{ github.ref_name }}
|
||||||
|
|
||||||
|
javadoc:
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
concurrency: javadoc-publish
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
result_hash: ${{ steps.deploy.outputs.hash }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: |
|
||||||
|
8
|
||||||
|
11
|
||||||
|
17
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
run: chmod +x gradlew
|
||||||
|
|
||||||
|
- name: Generate Javadoc
|
||||||
|
run: ./gradlew --no-daemon spotlessCheck javadoc
|
||||||
|
|
||||||
|
- name: Deploy 🚀
|
||||||
|
id: deploy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mv build/docs $RUNNER_TEMP/docs_output
|
||||||
|
rm build -rf
|
||||||
|
git checkout gh-pages
|
||||||
|
mkdir -p $TARGET_FOLDER
|
||||||
|
rm -rf $TARGET_FOLDER
|
||||||
|
mv $RUNNER_TEMP/docs_output $TARGET_FOLDER -v
|
||||||
|
git add $TARGET_FOLDER
|
||||||
|
if git diff --cached --exit-code > /dev/null ; then
|
||||||
|
echo Nothing to deploy
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
find . -name javadoc -type d -print | sed 's#^\./\(.*\)/javadoc$#\1#' | sort --version-sort --reverse > index.txt
|
||||||
|
git add index.txt
|
||||||
|
git config user.name "${GITHUB_ACTOR}"
|
||||||
|
git config user.email "${GITHUB_ACTOR_ID}-${GITHUB_ACTOR}@users.noreply.github.com"
|
||||||
|
git commit -m "Deploy javadoc from ref $TARGET_FOLDER"
|
||||||
|
git push
|
||||||
|
echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
TARGET_FOLDER: ${{ github.ref_name }}
|
||||||
@@ -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,5 @@ whitelist.json
|
|||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
src/main/resources/mixins.*.json
|
src/main/resources/mixins.*.json
|
||||||
|
*.bat
|
||||||
|
layout.json
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Any Github changes require admin approval
|
||||||
|
/.github/** @GTNewHorizons/admin
|
||||||
|
|
||||||
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
@@ -21,9 +21,16 @@ Originally created by TecTech authors, taken with permission. It's under MIT lic
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
Replace `master-SNAPSHOT` with a commit hash or a tag name (if any) of your choice to prevent unexpected upstream changes.
|
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,41 @@
|
|||||||
|
javadoc {
|
||||||
|
javadocTool = javaToolchains.javadocToolFor {
|
||||||
|
// Last version with frames, but it also has search
|
||||||
|
languageVersion = JavaLanguageVersion.of(25)
|
||||||
|
vendor = JvmVendorSpec.ADOPTIUM
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.file "src/main/javadoc/overview.html"
|
||||||
|
|
||||||
|
include "com/gtnewhorizon/structurelib/alignment/**"
|
||||||
|
include "com/gtnewhorizon/structurelib/constructable/**"
|
||||||
|
include "com/gtnewhorizon/structurelib/structure/**"
|
||||||
|
include "com/gtnewhorizon/structurelib/util/**"
|
||||||
|
include "com/gtnewhorizon/structurelib/StructureLibAPI.java"
|
||||||
|
include "com/gtnewhorizon/structurelib/StructureEvent.java"
|
||||||
|
|
||||||
|
classpath += sourceSets.patchedMc.output
|
||||||
|
|
||||||
|
options {
|
||||||
|
overview "src/main/javadoc/overview.html"
|
||||||
|
links 'https://docs.oracle.com/en/java/javase/25/docs/api/'
|
||||||
|
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('javadocJar', Jar) {
|
||||||
|
dependsOn javadoc
|
||||||
|
archiveClassifier = 'javadoc'
|
||||||
|
from javadoc.destinationDir
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
archives javadocJar
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-518
@@ -1,521 +1,5 @@
|
|||||||
//version: 4f3d1b46cf51f9d4ded5379b5d817a2a6f6b302d
|
//version: 1707058017
|
||||||
/*
|
|
||||||
DO NOT CHANGE THIS FILE!
|
|
||||||
|
|
||||||
Also, you may replace this file at any time if there is an update available.
|
|
||||||
Please check https://github.com/SinTh0r4s/ExampleMod1.7.10/blob/main/build.gradle for updates.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation
|
|
||||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
name = "forge"
|
|
||||||
url = "https://maven.minecraftforge.net"
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
name = "sonatype"
|
|
||||||
url = "https://oss.sonatype.org/content/repositories/snapshots/"
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
name = "Scala CI dependencies"
|
|
||||||
url = "https://repo1.maven.org/maven2/"
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
name = "jitpack"
|
|
||||||
url = "https://jitpack.io"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.github.GTNewHorizons:ForgeGradle:1.2.4'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'idea'
|
id 'com.gtnewhorizons.gtnhconvention'
|
||||||
id 'scala'
|
|
||||||
id("org.ajoberstar.grgit") version("3.1.1")
|
|
||||||
id("com.github.johnrengelman.shadow") version("4.0.4")
|
|
||||||
id("com.palantir.git-version") version("0.12.3")
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'forge'
|
|
||||||
|
|
||||||
def projectJavaVersion = JavaLanguageVersion.of(8)
|
|
||||||
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion.set(projectJavaVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
idea {
|
|
||||||
module {
|
|
||||||
inheritOutputDirs = true
|
|
||||||
downloadJavadoc = true
|
|
||||||
downloadSources = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPropertyExists("modName")
|
|
||||||
checkPropertyExists("modId")
|
|
||||||
checkPropertyExists("modGroup")
|
|
||||||
checkPropertyExists("autoUpdateBuildScript")
|
|
||||||
checkPropertyExists("minecraftVersion")
|
|
||||||
checkPropertyExists("forgeVersion")
|
|
||||||
checkPropertyExists("replaceGradleTokenInFile")
|
|
||||||
checkPropertyExists("gradleTokenModId")
|
|
||||||
checkPropertyExists("gradleTokenModName")
|
|
||||||
checkPropertyExists("gradleTokenVersion")
|
|
||||||
checkPropertyExists("gradleTokenGroupName")
|
|
||||||
checkPropertyExists("apiPackage")
|
|
||||||
checkPropertyExists("accessTransformersFile")
|
|
||||||
checkPropertyExists("usesMixins")
|
|
||||||
checkPropertyExists("mixinPlugin")
|
|
||||||
checkPropertyExists("mixinsPackage")
|
|
||||||
checkPropertyExists("coreModClass")
|
|
||||||
checkPropertyExists("containsMixinsAndOrCoreModOnly")
|
|
||||||
checkPropertyExists("usesShadowedDependencies")
|
|
||||||
checkPropertyExists("developmentEnvironmentUserName")
|
|
||||||
|
|
||||||
def checkPropertyExists(String propertyName) {
|
|
||||||
if (project.hasProperty(propertyName) == false) {
|
|
||||||
throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/SinTh0r4s/ExampleMod1.7.10/blob/main/gradle.properties")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
String javaSourceDir = "src/main/java/"
|
|
||||||
String scalaSourceDir = "src/main/scala/"
|
|
||||||
|
|
||||||
String targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/")
|
|
||||||
String targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/")
|
|
||||||
if((new File(targetPackageJava).exists() || new File(targetPackageScala).exists()) == false) {
|
|
||||||
throw new GradleException("Could not resolve \"modGroup\"! Could not find " + targetPackageJava + " or " + targetPackageScala)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(apiPackage) {
|
|
||||||
targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/")
|
|
||||||
targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/")
|
|
||||||
if((new File(targetPackageJava).exists() || new File(targetPackageScala).exists()) == false) {
|
|
||||||
throw new GradleException("Could not resolve \"apiPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(accessTransformersFile) {
|
|
||||||
String targetFile = "src/main/resources/META-INF/" + accessTransformersFile
|
|
||||||
if(new File(targetFile).exists() == false) {
|
|
||||||
throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
if(mixinsPackage.isEmpty() || mixinPlugin.isEmpty()) {
|
|
||||||
throw new GradleException("\"mixinPlugin\" requires \"mixinsPackage\" and \"mixinPlugin\" to be set!")
|
|
||||||
}
|
|
||||||
|
|
||||||
targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/")
|
|
||||||
targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/")
|
|
||||||
if((new File(targetPackageJava).exists() || new File(targetPackageScala).exists()) == false) {
|
|
||||||
throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala)
|
|
||||||
}
|
|
||||||
|
|
||||||
String targetFileJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".java"
|
|
||||||
String targetFileScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".scala"
|
|
||||||
String targetFileScalaJava = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".java"
|
|
||||||
if((new File(targetFileJava).exists() || new File(targetFileScala).exists() || new File(targetFileScalaJava).exists()) == false) {
|
|
||||||
throw new GradleException("Could not resolve \"mixinPlugin\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(coreModClass) {
|
|
||||||
String targetFileJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".java"
|
|
||||||
String targetFileScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".scala"
|
|
||||||
String targetFileScalaJava = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".java"
|
|
||||||
if((new File(targetFileJava).exists() || new File(targetFileScala).exists() || new File(targetFileScalaJava).exists()) == false) {
|
|
||||||
throw new GradleException("Could not resolve \"coreModClass\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations.all {
|
|
||||||
resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
|
|
||||||
|
|
||||||
// Make sure GregTech build won't time out
|
|
||||||
System.setProperty("org.gradle.internal.http.connectionTimeout", 120000 as String)
|
|
||||||
System.setProperty("org.gradle.internal.http.socketTimeout", 120000 as String)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix Jenkins' Git: chmod a file should not be detected as a change and append a '.dirty' to the version
|
|
||||||
'git config core.fileMode false'.execute()
|
|
||||||
// Pulls version from git tag
|
|
||||||
version = minecraftVersion + "-" + gitVersion()
|
|
||||||
group = modGroup
|
|
||||||
archivesBaseName = modId
|
|
||||||
|
|
||||||
minecraft {
|
|
||||||
version = minecraftVersion + "-" + forgeVersion + "-" + minecraftVersion
|
|
||||||
runDir = "run"
|
|
||||||
|
|
||||||
if (replaceGradleTokenInFile) {
|
|
||||||
replaceIn replaceGradleTokenInFile
|
|
||||||
if(gradleTokenModId) {
|
|
||||||
replace gradleTokenModId, modId
|
|
||||||
}
|
|
||||||
if(gradleTokenModName) {
|
|
||||||
replace gradleTokenModName, modName
|
|
||||||
}
|
|
||||||
if(gradleTokenVersion) {
|
|
||||||
replace gradleTokenVersion, versionDetails().lastTag
|
|
||||||
}
|
|
||||||
if(gradleTokenGroupName) {
|
|
||||||
replace gradleTokenGroupName, modGroup
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(file("addon.gradle").exists()) {
|
|
||||||
apply from: "addon.gradle"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: 'repositories.gradle'
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
implementation.extendsFrom(shadowImplementation)
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
name = "Overmind forge repo mirror"
|
|
||||||
url = "https://gregtech.overminddl1.com/"
|
|
||||||
}
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
maven {
|
|
||||||
name = "sponge"
|
|
||||||
url = "https://repo.spongepowered.org/repository/maven-public"
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
url = "https://jitpack.io"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
annotationProcessor("org.ow2.asm:asm-debug-all:5.0.3")
|
|
||||||
annotationProcessor("com.google.guava:guava:24.1.1-jre")
|
|
||||||
annotationProcessor("com.google.code.gson:gson:2.8.6")
|
|
||||||
annotationProcessor("org.spongepowered:mixin:0.8-SNAPSHOT")
|
|
||||||
// using 0.8 to workaround a issue in 0.7 which fails mixin application
|
|
||||||
compile("org.spongepowered:mixin:0.7.11-SNAPSHOT") {
|
|
||||||
// Mixin includes a lot of dependencies that are too up-to-date
|
|
||||||
exclude module: "launchwrapper"
|
|
||||||
exclude module: "guava"
|
|
||||||
exclude module: "gson"
|
|
||||||
exclude module: "commons-io"
|
|
||||||
exclude module: "log4j-core"
|
|
||||||
}
|
|
||||||
compile("com.github.GTNewHorizons:SpongeMixins:1.3.3:dev")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: 'dependencies.gradle'
|
|
||||||
|
|
||||||
def mixinConfigJson = "mixins." + modId + ".json"
|
|
||||||
def mixingConfigRefMap = "mixins." + modId + ".refmap.json"
|
|
||||||
def refMap = "${tasks.compileJava.temporaryDir}" + File.separator + mixingConfigRefMap
|
|
||||||
def mixinSrg = "${tasks.reobf.temporaryDir}" + File.separator + "mixins.srg"
|
|
||||||
|
|
||||||
task generateAssets {
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
new File(projectDir.toString() + "/src/main/resources/", mixinConfigJson).text = """{
|
|
||||||
"required": true,
|
|
||||||
"minVersion": "0.7.11",
|
|
||||||
"package": "${modGroup}.${mixinsPackage}",
|
|
||||||
"plugin": "${modGroup}.${mixinPlugin}",
|
|
||||||
"refmap": "${mixingConfigRefMap}",
|
|
||||||
"target": "@env(DEFAULT)",
|
|
||||||
"compatibilityLevel": "JAVA_8"
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task relocateShadowJar(type: ConfigureShadowRelocation) {
|
|
||||||
target = tasks.shadowJar
|
|
||||||
prefix = modGroup + ".shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
manifest {
|
|
||||||
attributes(getManifestAttributes())
|
|
||||||
}
|
|
||||||
|
|
||||||
minimize() // This will only allow shading for actually used classes
|
|
||||||
configurations = [project.configurations.shadowImplementation]
|
|
||||||
dependsOn(relocateShadowJar)
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
manifest {
|
|
||||||
attributes(getManifestAttributes())
|
|
||||||
}
|
|
||||||
|
|
||||||
if(usesShadowedDependencies.toBoolean()) {
|
|
||||||
dependsOn(shadowJar)
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reobf {
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
addExtraSrgFile mixinSrg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEvaluate {
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
tasks.compileJava {
|
|
||||||
options.compilerArgs += [
|
|
||||||
"-AreobfSrgFile=${tasks.reobf.srg}",
|
|
||||||
"-AoutSrgFile=${mixinSrg}",
|
|
||||||
"-AoutRefMapFile=${refMap}",
|
|
||||||
// Elan: from what I understand they are just some linter configs so you get some warning on how to properly code
|
|
||||||
"-XDenableSunApiLintControl",
|
|
||||||
"-XDignore.symbol.file"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runClient {
|
|
||||||
def arguments = []
|
|
||||||
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
arguments += [
|
|
||||||
"--mods=../build/libs/$modId-${version}.jar",
|
|
||||||
"--tweakClass org.spongepowered.asm.launch.MixinTweaker"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
if(developmentEnvironmentUserName) {
|
|
||||||
arguments += [
|
|
||||||
"--username",
|
|
||||||
developmentEnvironmentUserName
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
args(arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
runServer {
|
|
||||||
def arguments = []
|
|
||||||
|
|
||||||
if (usesMixins.toBoolean()) {
|
|
||||||
arguments += [
|
|
||||||
"--mods=../build/libs/$modId-${version}.jar",
|
|
||||||
"--tweakClass org.spongepowered.asm.launch.MixinTweaker"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
args(arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(JavaExec).configureEach {
|
|
||||||
javaLauncher.set(
|
|
||||||
javaToolchains.launcherFor {
|
|
||||||
languageVersion = projectJavaVersion
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources
|
|
||||||
{
|
|
||||||
// this will ensure that this task is redone when the versions change.
|
|
||||||
inputs.property "version", project.version
|
|
||||||
inputs.property "mcversion", project.minecraft.version
|
|
||||||
|
|
||||||
// replace stuff in mcmod.info, nothing else
|
|
||||||
from(sourceSets.main.resources.srcDirs) {
|
|
||||||
include 'mcmod.info'
|
|
||||||
|
|
||||||
// replace version and mcversion
|
|
||||||
expand "minecraftVersion": project.minecraft.version,
|
|
||||||
"modVersion": versionDetails().lastTag,
|
|
||||||
"modId": modId,
|
|
||||||
"modName": modName
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy everything else, thats not the mcmod.info
|
|
||||||
from(sourceSets.main.resources.srcDirs) {
|
|
||||||
exclude 'mcmod.info'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getManifestAttributes() {
|
|
||||||
def manifestAttributes = [:]
|
|
||||||
if(containsMixinsAndOrCoreModOnly.toBoolean() == false && (usesMixins.toBoolean() || coreModClass)) {
|
|
||||||
manifestAttributes += ["FMLCorePluginContainsFMLMod": true]
|
|
||||||
}
|
|
||||||
|
|
||||||
if(accessTransformersFile) {
|
|
||||||
manifestAttributes += ["FMLAT" : accessTransformersFile.toString()]
|
|
||||||
}
|
|
||||||
|
|
||||||
if(coreModClass) {
|
|
||||||
manifestAttributes += ["FMLCorePlugin": modGroup + "." + coreModClass]
|
|
||||||
}
|
|
||||||
|
|
||||||
if(usesMixins.toBoolean()) {
|
|
||||||
manifestAttributes += [
|
|
||||||
"TweakClass" : "org.spongepowered.asm.launch.MixinTweaker",
|
|
||||||
"MixinConfigs" : mixinConfigJson,
|
|
||||||
"ForceLoadAsMod" : containsMixinsAndOrCoreModOnly.toBoolean() == false
|
|
||||||
]
|
|
||||||
}
|
|
||||||
return manifestAttributes
|
|
||||||
}
|
|
||||||
|
|
||||||
task sourcesJar(type: Jar) {
|
|
||||||
from (sourceSets.main.allJava)
|
|
||||||
from (file("$projectDir/LICENSE"))
|
|
||||||
getArchiveClassifier().set('sources')
|
|
||||||
}
|
|
||||||
|
|
||||||
task shadowDevJar(type: ShadowJar) {
|
|
||||||
from sourceSets.main.output
|
|
||||||
getArchiveClassifier().set("dev")
|
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes(getManifestAttributes())
|
|
||||||
}
|
|
||||||
|
|
||||||
minimize() // This will only allow shading for actually used classes
|
|
||||||
configurations = [project.configurations.shadowImplementation]
|
|
||||||
}
|
|
||||||
|
|
||||||
task relocateShadowDevJar(type: ConfigureShadowRelocation) {
|
|
||||||
target = tasks.shadowDevJar
|
|
||||||
prefix = modGroup + ".shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
task circularResolverJar(type: Jar) {
|
|
||||||
dependsOn(relocateShadowDevJar)
|
|
||||||
dependsOn(shadowDevJar)
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
task devJar(type: Jar) {
|
|
||||||
from sourceSets.main.output
|
|
||||||
getArchiveClassifier().set("dev")
|
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes(getManifestAttributes())
|
|
||||||
}
|
|
||||||
|
|
||||||
if(usesShadowedDependencies.toBoolean()) {
|
|
||||||
dependsOn(circularResolverJar)
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task apiJar(type: Jar) {
|
|
||||||
from (sourceSets.main.allJava) {
|
|
||||||
include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString() + '/**'
|
|
||||||
}
|
|
||||||
|
|
||||||
from (sourceSets.main.output) {
|
|
||||||
include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString() + '/**'
|
|
||||||
}
|
|
||||||
|
|
||||||
from (sourceSets.main.resources.srcDirs) {
|
|
||||||
include("LICENSE")
|
|
||||||
}
|
|
||||||
|
|
||||||
getArchiveClassifier().set('api')
|
|
||||||
}
|
|
||||||
|
|
||||||
artifacts {
|
|
||||||
archives sourcesJar
|
|
||||||
archives devJar
|
|
||||||
if(apiPackage) {
|
|
||||||
archives apiJar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crude handler to append custom maven repositories to maven dependency
|
|
||||||
install{
|
|
||||||
repositories{
|
|
||||||
mavenInstaller{
|
|
||||||
pom.withXml {
|
|
||||||
def repositoriesNode = asNode().appendNode("repositories")
|
|
||||||
project.repositories.each { repository ->
|
|
||||||
def repositoryNode = repositoriesNode.appendNode("repository")
|
|
||||||
repositoryNode.appendNode("name", repository.name)
|
|
||||||
repositoryNode.appendNode("url", repository.properties.get("url"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating
|
|
||||||
task updateBuildScript {
|
|
||||||
doLast {
|
|
||||||
if (updateBuildScript()) return
|
|
||||||
|
|
||||||
print("Build script already up-to-date!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewBuildScriptVersionAvailable()) {
|
|
||||||
if (autoUpdateBuildScript.toBoolean()) {
|
|
||||||
updateBuildScript()
|
|
||||||
} else {
|
|
||||||
println("Build script update available! Run 'gradle updateBuildScript'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static URL availableBuildScriptUrl() {
|
|
||||||
new URL("https://raw.githubusercontent.com/SinTh0r4s/ExampleMod1.7.10/main/build.gradle")
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean updateBuildScript() {
|
|
||||||
if (isNewBuildScriptVersionAvailable()) {
|
|
||||||
def buildscriptFile = new File("build.gradle")
|
|
||||||
availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } }
|
|
||||||
print("Build script updated. Please REIMPORT the project or RESTART your IDE!")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean isNewBuildScriptVersionAvailable() {
|
|
||||||
Map parameters = ["connectTimeout": 2000, "readTimeout": 2000]
|
|
||||||
|
|
||||||
String currentBuildScript = new File("build.gradle").getText()
|
|
||||||
String currentBuildScriptHash = getVersionHash(currentBuildScript)
|
|
||||||
String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText()
|
|
||||||
String availableBuildScriptHash = getVersionHash(availableBuildScript)
|
|
||||||
|
|
||||||
boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash
|
|
||||||
return !isUpToDate
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getVersionHash(String buildScriptContent) {
|
|
||||||
String versionLine = buildScriptContent.find("^//version: [a-z0-9]*")
|
|
||||||
if(versionLine != null) {
|
|
||||||
return versionLine.split(": ").last()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
configure(updateBuildScript) {
|
|
||||||
group = 'forgegradle'
|
|
||||||
description = 'Updates the build script to the latest version'
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
// Add your dependencies here
|
// Add your dependencies here
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api("com.github.GTNewHorizons:GTNHLib:0.11.21:dev")
|
||||||
|
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.110-GTNH:dev")
|
||||||
|
|
||||||
|
testImplementation(platform('org.junit:junit-bom:5.9.2'))
|
||||||
|
testImplementation('org.junit.jupiter:junit-jupiter')
|
||||||
|
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
|
||||||
}
|
}
|
||||||
+207
-29
@@ -1,60 +1,238 @@
|
|||||||
|
# ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empty to disable.
|
||||||
|
# LOCAL to test local config updates.
|
||||||
|
gtnh.settings.blowdryerTag = 0.2.0
|
||||||
|
|
||||||
|
# Human-readable mod name, available for mcmod.info population.
|
||||||
modName = StructureLib
|
modName = StructureLib
|
||||||
|
|
||||||
# This is a case-sensitive string to identify your mod. Convention is to use lower case.
|
# Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation.
|
||||||
|
# Conventionally lowercase.
|
||||||
modId = structurelib
|
modId = structurelib
|
||||||
|
|
||||||
|
# Root package of the mod, used to find various classes in other properties,
|
||||||
|
# mcmod.info substitution, enabling assertions in run tasks, etc.
|
||||||
modGroup = com.gtnewhorizon.structurelib
|
modGroup = com.gtnewhorizon.structurelib
|
||||||
|
|
||||||
# WHY is there no version field?
|
# Whether to use modGroup as the maven publishing group.
|
||||||
# The build script relies on git to provide a version via tags. It is super easy and will enable you to always know the
|
# When false, com.github.GTNewHorizons is used.
|
||||||
# code base or your binary. Check out this tutorial: https://blog.mattclemente.com/2017/10/13/versioning-with-git-tags/
|
useModGroupForPublishing = false
|
||||||
|
|
||||||
# Will update your build.gradle automatically whenever an update is available
|
# Updates your build.gradle and settings.gradle automatically whenever an update is available.
|
||||||
autoUpdateBuildScript = false
|
autoUpdateBuildScript = false
|
||||||
|
|
||||||
|
# Version of Minecraft to target
|
||||||
minecraftVersion = 1.7.10
|
minecraftVersion = 1.7.10
|
||||||
|
|
||||||
|
# Version of Minecraft Forge to target
|
||||||
forgeVersion = 10.13.4.1614
|
forgeVersion = 10.13.4.1614
|
||||||
|
|
||||||
# Select a username for testing your mod with breakpoints. You may leave this empty for a random user name each time you
|
# Specify an MCP channel for dependency deobfuscation and the deobfParams task.
|
||||||
# restart Minecraft in development. Choose this dependent on your mod:
|
channel = stable
|
||||||
# Do you need consistent player progressing (for example Thaumcraft)? -> Select a name
|
|
||||||
# Do you need to test how your custom blocks interacts with a player that is not the owner? -> leave name empty
|
|
||||||
developmentEnvironmentUserName = "Developer"
|
|
||||||
|
|
||||||
# Define a source file of your project with:
|
# Specify an MCP mappings version for dependency deobfuscation and the deobfParams task.
|
||||||
# public static final String VERSION = "GRADLETOKEN_VERSION";
|
mappingsVersion = 12
|
||||||
# The string's content will be replaced with your mods version when compiled. You should use this to specify your mod's
|
|
||||||
# version in @Mod([...], version = VERSION, [...])
|
# Defines other MCP mappings for dependency deobfuscation.
|
||||||
# Leave these properties empty to skip individual token replacements
|
remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/
|
||||||
replaceGradleTokenInFile = StructureLib.java
|
|
||||||
|
# Select a default username for testing your mod. You can always override this per-run by running
|
||||||
|
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
|
||||||
|
developmentEnvironmentUserName = Developer
|
||||||
|
|
||||||
|
# Enables modern Java syntax support. Valid values:
|
||||||
|
# - false: No modern syntax, Java 8 only
|
||||||
|
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
|
||||||
|
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
|
||||||
|
# - modern: Native modern Java bytecode, no downgrading
|
||||||
|
enableModernJavaSyntax = jabel
|
||||||
|
|
||||||
|
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
|
||||||
|
# and should *not* be used in most situations. -1 disables this.
|
||||||
|
# forceToolchainVersion = -1
|
||||||
|
|
||||||
|
# Target JVM version for JVM Downgrader bytecode downgrading.
|
||||||
|
# Only used when enableModernJavaSyntax = jvmDowngrader
|
||||||
|
# downgradeTargetVersion = 8
|
||||||
|
|
||||||
|
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
|
||||||
|
# Classes will be available in META-INF/versions/N/ for each version N in this list.
|
||||||
|
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
|
||||||
|
# jvmDowngraderMultiReleaseVersions = 21,25
|
||||||
|
|
||||||
|
# Specifies how JVM Downgrader API stubs are provided. Options:
|
||||||
|
# - shade: Shade minimized stubs into the jar
|
||||||
|
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
|
||||||
|
# - external: Another dependency provides stubs (no constraint, no warning)
|
||||||
|
# - (empty): Warning reminding you to configure stubs
|
||||||
|
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
|
||||||
|
# jvmDowngraderStubsProvider =
|
||||||
|
|
||||||
|
# Enables injecting missing generics into the decompiled source code for a better coding experience.
|
||||||
|
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
|
||||||
|
enableGenericInjection = true
|
||||||
|
|
||||||
|
# Generate a class with a String field for the mod version named as defined below.
|
||||||
|
# If generateGradleTokenClass is empty or not missing, no such class will be generated.
|
||||||
|
# If gradleTokenVersion is empty or missing, the field will not be present in the class.
|
||||||
|
generateGradleTokenClass = com.gtnewhorizon.structurelib.Tags
|
||||||
|
|
||||||
|
# Name of the token containing the project's current version to generate/replace.
|
||||||
|
gradleTokenVersion = VERSION
|
||||||
|
|
||||||
|
# [DEPRECATED] Mod ID replacement token.
|
||||||
gradleTokenModId =
|
gradleTokenModId =
|
||||||
|
|
||||||
|
# [DEPRECATED] Mod name replacement token.
|
||||||
gradleTokenModName =
|
gradleTokenModName =
|
||||||
gradleTokenVersion = GRADLETOKEN_VERSION
|
|
||||||
|
# [DEPRECATED] Mod Group replacement token.
|
||||||
gradleTokenGroupName =
|
gradleTokenGroupName =
|
||||||
|
|
||||||
# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise you can
|
# [DEPRECATED]
|
||||||
|
# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java
|
||||||
|
# public static final String VERSION = "GRADLETOKEN_VERSION";
|
||||||
|
# The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's
|
||||||
|
# version in @Mod([...], version = VERSION, [...]).
|
||||||
|
# Leave these properties empty to skip individual token replacements.
|
||||||
|
replaceGradleTokenInFile =
|
||||||
|
|
||||||
|
# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can
|
||||||
# leave this property empty.
|
# leave this property empty.
|
||||||
# Example value: apiPackage = api + modGroup = com.myname.mymodid -> com.myname.mymodid.api
|
# Example value: (apiPackage = api) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.api
|
||||||
apiPackage =
|
apiPackage =
|
||||||
|
|
||||||
# Specify the configuration file for Forge's access transformers here. I must be placed into /src/main/resources/META-INF/
|
# Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/
|
||||||
# Example value: mymodid_at.cfg
|
# There can be multiple files in a space-separated list.
|
||||||
|
# Example value: mymodid_at.cfg nei_at.cfg
|
||||||
accessTransformersFile =
|
accessTransformersFile =
|
||||||
|
|
||||||
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
|
# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled!
|
||||||
usesMixins = false
|
usesMixins = true
|
||||||
|
|
||||||
|
# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main.
|
||||||
|
# This can speed up compile times thanks to not running the mixin annotation processor on all input sources.
|
||||||
|
# Mixin classes will have access to "main" classes, but not the other way around.
|
||||||
|
separateMixinSourceSet =
|
||||||
|
|
||||||
|
# Adds some debug arguments like verbose output and class export.
|
||||||
|
usesMixinDebug = false
|
||||||
|
|
||||||
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
|
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
|
||||||
mixinPlugin =
|
mixinPlugin =
|
||||||
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
|
|
||||||
mixinsPackage =
|
# Specify the package that contains all of your Mixins. The package must exist or
|
||||||
|
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
|
||||||
|
# it must match with this or the build will fail.
|
||||||
|
mixinsPackage = mixins
|
||||||
|
|
||||||
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
|
||||||
# This parameter is for legacy compatability only
|
# This parameter is for legacy compatibility only
|
||||||
# Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin
|
# Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin
|
||||||
coreModClass =
|
coreModClass = core.StructureLibCore
|
||||||
|
|
||||||
# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
|
# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
|
||||||
# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
|
# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
|
||||||
containsMixinsAndOrCoreModOnly = false
|
containsMixinsAndOrCoreModOnly = false
|
||||||
|
|
||||||
# If enabled, you may use 'shadowImplementation' for dependencies. They will be integrated in your jar. It is your
|
# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins.
|
||||||
# responsibility check the licence and request permission for distribution, if required.
|
forceEnableMixins = false
|
||||||
|
|
||||||
|
# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated into your jar. It is your
|
||||||
|
# responsibility to check the license and request permission for distribution if required.
|
||||||
usesShadowedDependencies = false
|
usesShadowedDependencies = false
|
||||||
|
|
||||||
|
# If disabled, won't remove unused classes from shadowed dependencies. Some libraries use reflection to access
|
||||||
|
# their own classes, making the minimization unreliable.
|
||||||
|
minimizeShadowedDependencies = true
|
||||||
|
|
||||||
|
# If disabled, won't rename the shadowed classes.
|
||||||
|
relocateShadowedDependencies = true
|
||||||
|
|
||||||
|
# Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories.
|
||||||
|
includeWellKnownRepositories = true
|
||||||
|
|
||||||
|
# A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated
|
||||||
|
# list of strings, with the acceptable keys being(case does not matter):
|
||||||
|
# cursemaven
|
||||||
|
# modrinth
|
||||||
|
excludeWellKnownRepositories =
|
||||||
|
|
||||||
|
# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven.
|
||||||
|
# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables.
|
||||||
|
# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle.
|
||||||
|
usesMavenPublishing = true
|
||||||
|
|
||||||
|
# Maven repository to publish the mod to.
|
||||||
|
# mavenPublishUrl = https\://nexus.gtnewhorizons.com/repository/releases/
|
||||||
|
|
||||||
|
# Publishing to Modrinth requires you to set the MODRINTH_TOKEN environment variable to your current Modrinth API token.
|
||||||
|
#
|
||||||
|
# The project's ID on Modrinth. Can be either the slug or the ID.
|
||||||
|
# Leave this empty if you don't want to publish to Modrinth.
|
||||||
|
modrinthProjectId =
|
||||||
|
|
||||||
|
# The project's relations on Modrinth. You can use this to refer to other projects on Modrinth.
|
||||||
|
# Syntax: scope1-type1:name1;scope2-type2:name2;...
|
||||||
|
# Where scope can be one of [required, optional, incompatible, embedded],
|
||||||
|
# type can be one of [project, version],
|
||||||
|
# and the name is the Modrinth project or version slug/id of the other mod.
|
||||||
|
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
|
||||||
|
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
|
||||||
|
modrinthRelations =
|
||||||
|
|
||||||
|
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
|
||||||
|
#
|
||||||
|
# The project's numeric ID on CurseForge. You can find this in the About Project box.
|
||||||
|
# Leave this empty if you don't want to publish on CurseForge.
|
||||||
|
curseForgeProjectId =
|
||||||
|
|
||||||
|
# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge.
|
||||||
|
# Syntax: type1:name1;type2:name2;...
|
||||||
|
# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible],
|
||||||
|
# and the name is the CurseForge project slug of the other mod.
|
||||||
|
# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft
|
||||||
|
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
|
||||||
|
curseForgeRelations =
|
||||||
|
|
||||||
|
# Optional parameter to customize the produced artifacts. Use this to preserve artifact naming when migrating older
|
||||||
|
# projects. New projects should not use this parameter.
|
||||||
|
# customArchiveBaseName =
|
||||||
|
|
||||||
|
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
|
||||||
|
# runClientWorkingDirectory = run/client
|
||||||
|
|
||||||
|
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
|
||||||
|
# runServerWorkingDirectory = run/server
|
||||||
|
|
||||||
|
# Optional parameter to have the build automatically fail if an illegal version is used.
|
||||||
|
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
|
||||||
|
# The check is ONLY performed if the version is a git tag.
|
||||||
|
# Note: the specified string must be escaped, so e.g. 1\\.1\\.\\d+ instead of 1\.1\.\d+
|
||||||
|
# versionPattern =
|
||||||
|
|
||||||
|
# Uncomment to prevent the source code from being published.
|
||||||
|
# noPublishedSources = true
|
||||||
|
|
||||||
|
# Uncomment this to disable Spotless checks.
|
||||||
|
# This should only be uncommented to keep it easier to sync with upstream/other forks.
|
||||||
|
# That is, if there is no other active fork/upstream, NEVER change this.
|
||||||
|
# disableSpotless = true
|
||||||
|
|
||||||
|
# Uncomment this to disable Checkstyle checks (currently wildcard import check).
|
||||||
|
# disableCheckstyle = true
|
||||||
|
|
||||||
|
# Override the IDEA build type. Valid values are: "" (leave blank, do not override), "idea" (force use native IDEA build), "gradle"
|
||||||
|
# (force use delegated build).
|
||||||
|
# This is meant to be set in $HOME/.gradle/gradle.properties.
|
||||||
|
# e.g. add "systemProp.org.gradle.project.ideaOverrideBuildType=idea" will override the build type to be native build.
|
||||||
|
# WARNING: If you do use this option, it will overwrite whatever you have in your existing projects. This might not be what you want!
|
||||||
|
# Usually there is no need to uncomment this here as other developers do not necessarily use the same build type as you.
|
||||||
|
# ideaOverrideBuildType = idea
|
||||||
|
|
||||||
|
# Whether IDEA should run spotless checks when pressing the Build button.
|
||||||
|
# This is meant to be set in $HOME/.gradle/gradle.properties.
|
||||||
|
# ideaCheckSpotlessOnBuild = true
|
||||||
|
|
||||||
|
# Non-GTNH properties
|
||||||
|
org.gradle.configuration-cache = true
|
||||||
|
org.gradle.parallel = true
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#This file is generated by updateDaemonJvm
|
||||||
|
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||||
|
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||||
|
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||||
|
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||||
|
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect
|
||||||
|
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect
|
||||||
|
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect
|
||||||
|
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect
|
||||||
|
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect
|
||||||
|
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d76df094a9cbbabd3b08251f9e61444a/redirect
|
||||||
|
toolchainVersion=25
|
||||||
Vendored
BIN
Binary file not shown.
+3
-1
@@ -1,5 +1,7 @@
|
|||||||
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-9.4.0-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
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 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.
|
||||||
@@ -15,80 +15,114 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# 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
|
|
||||||
|
|
||||||
# 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,92 +131,118 @@ 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.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
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=SC2039,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=SC2039,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" )
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
Vendored
+27
-34
@@ -13,8 +13,10 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@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 +27,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,13 +43,13 @@ 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. 1>&2
|
||||||
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. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -51,48 +57,35 @@ 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. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@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%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
: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
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// Add any additional repositiroes for your dependencies here
|
// Add any additional repositiroes for your dependencies here
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
// RetroFuturaGradle
|
||||||
|
name "GTNH Maven"
|
||||||
|
url "https://nexus.gtnewhorizons.com/repository/public/"
|
||||||
|
mavenContent {
|
||||||
|
includeGroup("com.gtnewhorizons")
|
||||||
|
includeGroupByRegex("com\\.gtnewhorizons\\..+")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradlePluginPortal()
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Loader;
|
||||||
|
|
||||||
|
// this is not an API! go check StructureLibAPI instead!
|
||||||
|
public class ChannelDescription {
|
||||||
|
|
||||||
|
private static final ItemStackMap<Set<ChannelDescription>> itemToChannels = new ItemStackMap<>(true);
|
||||||
|
private static final Map<String, ChannelDescription> registry = new HashMap<>();
|
||||||
|
private final String channel;
|
||||||
|
private final Map<String, String> descriptions = new HashMap<>();
|
||||||
|
private final ItemStackMap<Integer> items = new ItemStackMap<>(true);
|
||||||
|
|
||||||
|
public static ChannelDescription get(final String channel) {
|
||||||
|
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||||
|
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||||
|
return registry.computeIfAbsent(channel, ChannelDescription::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean has(final String channel) {
|
||||||
|
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||||
|
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||||
|
return registry.containsKey(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<Map.Entry<String, Integer>> iterate(final ItemStack block) {
|
||||||
|
Set<ChannelDescription> channels = itemToChannels.get(block);
|
||||||
|
if (channels == null) return Collections.emptySet();
|
||||||
|
return channels.stream().map(d -> new AbstractMap.SimpleImmutableEntry<>(d.channel, d.items.get(block)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(final String channel, final String modid, final String description) {
|
||||||
|
if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
|
||||||
|
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||||
|
registry.computeIfAbsent(channel, ChannelDescription::new).add(modid, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void item(final String channel, int channelValue, ItemStack stack) {
|
||||||
|
if (StructureLibAPI.isDebugEnabled()) {
|
||||||
|
if (!channel.toLowerCase(Locale.ROOT).equals(channel)) {
|
||||||
|
throw new IllegalArgumentException("Channel name can be lower case ONLY");
|
||||||
|
}
|
||||||
|
if (channelValue <= 0) {
|
||||||
|
throw new IllegalArgumentException("Channel value must be greater than 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ChannelDescription inst = registry.computeIfAbsent(channel, ChannelDescription::new);
|
||||||
|
inst.items.put(stack, channelValue);
|
||||||
|
itemToChannels.computeIfAbsent(stack, s -> new HashSet<>()).add(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, ChannelDescription> getAll() {
|
||||||
|
return Collections.unmodifiableMap(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChannelDescription(String channel) {
|
||||||
|
this.channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChannel() {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChannelDescription add(String modid, String description) {
|
||||||
|
if (Loader.instance().getIndexedModList().get(modid) == null) {
|
||||||
|
throw new IllegalArgumentException("Mod not found: " + modid);
|
||||||
|
}
|
||||||
|
descriptions.put(modid, description);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDescriptions() {
|
||||||
|
return Collections.unmodifiableMap(descriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ItemStack, Integer> getItems() {
|
||||||
|
return Collections.unmodifiableMap(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,653 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.entity.EntityClientPlayerMP;
|
||||||
|
import net.minecraft.client.gui.GuiNewChat;
|
||||||
|
import net.minecraft.client.particle.EntityFX;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.renderer.culling.Frustrum;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureMap;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.profiler.Profiler;
|
||||||
|
import net.minecraft.util.ChatComponentText;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.util.Vec3;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
import net.minecraftforge.common.config.ConfigElement;
|
||||||
|
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||||
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
|
||||||
|
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
|
||||||
|
import cpw.mods.fml.common.network.FMLNetworkEvent;
|
||||||
|
|
||||||
|
public class ClientProxy extends CommonProxy {
|
||||||
|
|
||||||
|
private static final short[] RGBA_NO_TINT = { 255, 255, 255, 255 };
|
||||||
|
private static final short[] RGBA_RED_TINT = { 255, 128, 128, 0 };
|
||||||
|
private static final Map<HintParticleInfo, HintGroup> allHints = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* All batches of hints.
|
||||||
|
*/
|
||||||
|
private static final List<HintGroup> allGroups = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current batch of hints. Belongs to the same logical multiblock.
|
||||||
|
*/
|
||||||
|
private static HintGroup currentHints;
|
||||||
|
/**
|
||||||
|
* Collection of all hints. Sorted from farthest (index 0) to nearest (index size - 1)
|
||||||
|
* <p>
|
||||||
|
* 10k should be more than enough, if not a bit wasteful
|
||||||
|
*/
|
||||||
|
private static final List<HintParticleInfo> allHintsForRender = new ArrayList<>(10000);
|
||||||
|
/**
|
||||||
|
* If the diff to current player position is too great sort the allHints. Initial value is very far off below y=0
|
||||||
|
* but not huge enough to make a NaN.
|
||||||
|
* <p>
|
||||||
|
* We are using this supposedly immutable object as a mutable object here. So be aware.
|
||||||
|
*/
|
||||||
|
private static final Vec3 lastPlayerPos = Vec3.createVectorHelper(0, -1e30, 0);
|
||||||
|
/**
|
||||||
|
* if true rebuild the allHints list from all hintOwners (and sort it)
|
||||||
|
*/
|
||||||
|
private static boolean allHintsDirty = false;
|
||||||
|
/**
|
||||||
|
* counter keeping track of how many renderThrough hint particle we have. enable optimization if no renderThrough
|
||||||
|
*/
|
||||||
|
private static int renderThrough;
|
||||||
|
/**
|
||||||
|
* A throttle map for local player. Integrated server will use the throttle map in super class instead.
|
||||||
|
*/
|
||||||
|
private static final Map<Object, Long> localThrottleMap = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
|
||||||
|
ensureHinting();
|
||||||
|
HintParticleInfo info = new HintParticleInfo(w, x, y, z, icons, RGBa);
|
||||||
|
|
||||||
|
// check and remove colliding holograms
|
||||||
|
if (ConfigurationHandler.INSTANCE.isRemoveCollidingHologram()) {
|
||||||
|
HintGroup dupe = allHints.get(info);
|
||||||
|
if (dupe != null && dupe != currentHints) {
|
||||||
|
allGroups.remove(dupe);
|
||||||
|
removeGroup(dupe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allHints.put(info, currentHints);
|
||||||
|
currentHints.getHints().add(info);
|
||||||
|
allHintsDirty = true;
|
||||||
|
|
||||||
|
EntityFX particle = new WeightlessParticleFX(
|
||||||
|
w,
|
||||||
|
x + RANDOM.nextFloat() * 0.5F,
|
||||||
|
y + RANDOM.nextFloat() * 0.5F,
|
||||||
|
z + RANDOM.nextFloat() * 0.5F,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
particle.setRBGColorF(0, 0.6F * RANDOM.nextFloat(), 0.8f);
|
||||||
|
Minecraft.getMinecraft().effectRenderer.addEffect(particle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {
|
||||||
|
hintParticleTinted(w, x, y, z, createIIconFromBlock(block, meta), RGBa);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
|
||||||
|
hintParticleTinted(w, x, y, z, icons, RGBA_NO_TINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hintParticle(World w, int x, int y, int z, Block block, int meta) {
|
||||||
|
hintParticleTinted(w, x, y, z, createIIconFromBlock(block, meta), RGBA_NO_TINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateHintParticleTint(EntityPlayer player, World w, int x, int y, int z, short[] rgBa) {
|
||||||
|
if (player instanceof EntityPlayerMP) return super.updateHintParticleTint(player, w, x, y, z, rgBa);
|
||||||
|
if (player != getCurrentPlayer()) return false; // how?
|
||||||
|
HintParticleInfo hint = getHintParticleInfo(w, x, y, z);
|
||||||
|
if (hint == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hint.setTint(rgBa);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HintParticleInfo getHintParticleInfo(World w, int x, int y, int z) {
|
||||||
|
HintParticleInfo info = new HintParticleInfo(w, x, y, z, null, null);
|
||||||
|
HintGroup existing = allHints.get(info);
|
||||||
|
if (existing != null) {
|
||||||
|
for (HintParticleInfo hint : existing.getHints()) {
|
||||||
|
if (hint.equals(info)) {
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markHintParticleError(EntityPlayer player, World w, int x, int y, int z) {
|
||||||
|
if (player instanceof EntityPlayerMP) return super.markHintParticleError(player, w, x, y, z);
|
||||||
|
if (player != getCurrentPlayer()) return false; // how?
|
||||||
|
HintParticleInfo hint = getHintParticleInfo(w, x, y, z);
|
||||||
|
if (hint == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hint.setTint(RGBA_RED_TINT);
|
||||||
|
hint.setRenderThrough();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
|
||||||
|
boolean forceUpdateLastSend) {
|
||||||
|
if (player instanceof EntityPlayerMP)
|
||||||
|
super.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend);
|
||||||
|
else if (player != null)
|
||||||
|
addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend, localThrottleMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeGroup(HintGroup group) {
|
||||||
|
for (HintParticleInfo hintParticleInfo : group.getHints()) {
|
||||||
|
allHints.remove(hintParticleInfo);
|
||||||
|
if (hintParticleInfo.renderThrough) renderThrough--;
|
||||||
|
}
|
||||||
|
allHintsDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IIcon[] createIIconFromBlock(Block block, int meta) {
|
||||||
|
IIcon[] ret = new IIcon[6];
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
ret[i] = block.getIcon(i, meta);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addClientSideChatMessages(String... messages) {
|
||||||
|
GuiNewChat chat = Minecraft.getMinecraft().ingameGUI.getChatGUI();
|
||||||
|
for (String s : messages) {
|
||||||
|
chat.printChatMessage(new ChatComponentText(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityPlayer getCurrentPlayer() {
|
||||||
|
return Minecraft.getMinecraft().thePlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCurrentPlayer(EntityPlayer player) {
|
||||||
|
return player == Minecraft.getMinecraft().thePlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startHinting(World w) {
|
||||||
|
if (!w.isRemote) return;
|
||||||
|
if (currentHints != null) endHinting(w);
|
||||||
|
currentHints = new HintGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureHinting() {
|
||||||
|
if (currentHints == null) currentHints = new HintGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endHinting(World w) {
|
||||||
|
if (!w.isRemote || currentHints == null) return;
|
||||||
|
while (!allGroups.isEmpty() && allGroups.size() >= ConfigurationHandler.INSTANCE.getMaxCoexistingHologram()) {
|
||||||
|
allGroups.remove(0);
|
||||||
|
}
|
||||||
|
if (!currentHints.getHints().isEmpty()) allGroups.add(currentHints);
|
||||||
|
// we use the player existence time here as some worlds don't really advance the time ticker
|
||||||
|
currentHints.setCreationTime(getCurrentPlayer().ticksExisted);
|
||||||
|
currentHints = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getOverworldTime() {
|
||||||
|
// there is no overworld, better just hope current world time is ok...
|
||||||
|
return Minecraft.getMinecraft().theWorld.getTotalWorldTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uploadChannels(ItemStack trigger) {
|
||||||
|
StructureLib.net.sendToServer(new SetChannelDataMessage(trigger));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preInit(FMLPreInitializationEvent e) {
|
||||||
|
FMLCommonHandler.instance().bus().register(new FMLEventHandler());
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void markTextureUsed(IIcon icon) {
|
||||||
|
if (StructureLib.COMPAT instanceof IStructureCompat)
|
||||||
|
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sendRegistryOrderToServer() {
|
||||||
|
if (FMLEventHandler.configSent) return;
|
||||||
|
FMLEventHandler.configSent = true;
|
||||||
|
for (String s : SortedRegistry.ALL_REGISTRIES.keySet()) {
|
||||||
|
Pair<List<String>, List<String>> registryOrder = ConfigurationHandler.INSTANCE.getRegistryOrder(s);
|
||||||
|
if (registryOrder == null || registryOrder.getKey().isEmpty() && registryOrder.getValue().isEmpty())
|
||||||
|
continue;
|
||||||
|
StructureLib.net
|
||||||
|
.sendToServer(new RegistryOrderSyncMessage(s, registryOrder.getKey(), registryOrder.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadComplete(FMLLoadCompleteEvent evt) {
|
||||||
|
super.loadComplete(evt);
|
||||||
|
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
|
||||||
|
SortedRegistry<?> r = e.getValue().get();
|
||||||
|
if (r == null) continue;
|
||||||
|
Pair<List<String>, List<String>> p = ConfigurationHandler.INSTANCE.getRegistryOrder(e.getKey());
|
||||||
|
r.registerOrdering(getSPPlayerUUID(), p.getKey(), p.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getSPPlayerUUID() {
|
||||||
|
return EntityPlayer.func_146094_a(Minecraft.getMinecraft().getSession().func_148256_e());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayConfigGUI(String category) {
|
||||||
|
ConfigElement<Object> element = new ConfigElement<>(
|
||||||
|
ConfigurationHandler.INSTANCE.getConfig().getCategory(category));
|
||||||
|
GuiConfig guiConfig = new GuiConfig(
|
||||||
|
null,
|
||||||
|
element.getChildElements(),
|
||||||
|
StructureLibAPI.MOD_ID,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
I18n.format(element.getLanguageKey()));
|
||||||
|
Minecraft.getMinecraft().displayGuiScreen(guiConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HintGroup {
|
||||||
|
|
||||||
|
private final List<HintParticleInfo> hints = new LinkedList<>();
|
||||||
|
private int creationTime = -1;
|
||||||
|
|
||||||
|
public List<HintParticleInfo> getHints() {
|
||||||
|
return hints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreationTime(int creationTime) {
|
||||||
|
this.creationTime = creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HintParticleInfo {
|
||||||
|
|
||||||
|
private final World w;
|
||||||
|
// these are the block coordinate for e.g. w.getBlock()
|
||||||
|
private final int x, y, z;
|
||||||
|
private final IIcon[] icons;
|
||||||
|
private short[] tint;
|
||||||
|
private boolean renderThrough;
|
||||||
|
|
||||||
|
private final long creationTime = System.currentTimeMillis(); // use tick time instead maybe
|
||||||
|
|
||||||
|
public HintParticleInfo(World w, int x, int y, int z, IIcon[] icons, short[] tint) {
|
||||||
|
this.w = w;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.icons = icons;
|
||||||
|
this.tint = tint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof HintParticleInfo that)) return false;
|
||||||
|
|
||||||
|
return x == that.x && y == that.y && z == that.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTint(short[] tint) {
|
||||||
|
this.tint = tint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenderThrough() {
|
||||||
|
if (!this.renderThrough) {
|
||||||
|
ClientProxy.renderThrough += 1;
|
||||||
|
allHintsDirty = true;
|
||||||
|
}
|
||||||
|
this.renderThrough = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = x;
|
||||||
|
result = 31 * result + y;
|
||||||
|
result = 31 * result + z;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInFrustrum(Frustrum frustrum) {
|
||||||
|
return frustrum.isBoxInFrustum(x + 0.25, y + 0.25, z + 0.25, x + 0.75, y + 0.75, z + 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Tessellator tes, double eyeX, double eyeY, double eyeZ, int eyeXint, int eyeYint,
|
||||||
|
int eyeZint) {
|
||||||
|
double size = 0.5;
|
||||||
|
|
||||||
|
int brightness = w.blockExists(x, 0, z) ? w.getLightBrightnessForSkyBlocks(x, y, z, 0) : 0;
|
||||||
|
tes.setBrightness(brightness);
|
||||||
|
|
||||||
|
tes.setColorRGBA(
|
||||||
|
(int) (tint[0] * .9F),
|
||||||
|
(int) (tint[1] * .95F),
|
||||||
|
(int) (tint[2] * 1F),
|
||||||
|
ConfigurationHandler.INSTANCE.getHintTransparency());
|
||||||
|
|
||||||
|
double X = (x - eyeXint) + 0.25;
|
||||||
|
double Y = (y - eyeYint) + 0.25;
|
||||||
|
double Z = (z - eyeZint) + 0.25;
|
||||||
|
double worldX = x + 0.25;
|
||||||
|
double worldY = y + 0.25;
|
||||||
|
double worldZ = z + 0.25;
|
||||||
|
|
||||||
|
// cube is a very special model. its facings can be rendered correctly by viewer distance without using
|
||||||
|
// surface normals and view vector
|
||||||
|
// here we do a 2 pass render.
|
||||||
|
// first pass we draw obstructed faces (i.e. faces that are further away from player)
|
||||||
|
// second pass we draw unobstructed faces
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
boolean unobstructedPass = j == 1;
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
if (icons[i] == null) continue;
|
||||||
|
|
||||||
|
markTextureUsed(icons[i]);
|
||||||
|
|
||||||
|
double u = icons[i].getMinU();
|
||||||
|
double U = icons[i].getMaxU();
|
||||||
|
double v = icons[i].getMinV();
|
||||||
|
double V = icons[i].getMaxV();
|
||||||
|
switch (i) { // {DOWN, UP, NORTH, SOUTH, WEST, EAST}
|
||||||
|
case 0:
|
||||||
|
// all these ifs is in form if ((is face unobstructed) != (is in unobstructred pass))
|
||||||
|
if ((worldY >= eyeY) != unobstructedPass) continue;
|
||||||
|
tes.setNormal(0, -1, 0);
|
||||||
|
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||||
|
tes.addVertexWithUV(X, Y, Z, u, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z, U, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z, U, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||||
|
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if ((worldY + size <= eyeY) != unobstructedPass) continue;
|
||||||
|
tes.setNormal(0, 1, 0);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z + size, u, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z + size, U, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z + size, U, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z, U, v);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if ((worldZ >= eyeZ) != unobstructedPass) continue;
|
||||||
|
tes.setNormal(0, 0, -1);
|
||||||
|
tes.addVertexWithUV(X, Y, Z, U, V);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z, U, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z, u, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z, u, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z, u, V);
|
||||||
|
tes.addVertexWithUV(X, Y, Z, U, V);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if ((worldZ + size <= eyeZ) != unobstructedPass) continue;
|
||||||
|
tes.setNormal(0, 0, 1);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z + size, U, v);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z + size, u, v);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z + size, u, v);
|
||||||
|
tes.addVertexWithUV(X, Y, Z + size, u, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z + size, U, V);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if ((worldX >= eyeX) != unobstructedPass) continue;
|
||||||
|
tes.setNormal(-1, 0, 0);
|
||||||
|
tes.addVertexWithUV(X, Y, Z + size, U, V);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z + size, U, v);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||||
|
tes.addVertexWithUV(X, Y + size, Z, u, v);
|
||||||
|
tes.addVertexWithUV(X, Y, Z, u, V);
|
||||||
|
tes.addVertexWithUV(X, Y, Z + size, U, V);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
if ((worldX + size <= eyeX) != unobstructedPass) continue;
|
||||||
|
tes.setNormal(1, 0, 0);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z, U, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z, U, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z + size, u, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y + size, Z + size, u, v);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z + size, u, V);
|
||||||
|
tes.addVertexWithUV(X + size, Y, Z, U, V);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCreationTime() {
|
||||||
|
return creationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getSquareDistanceTo(Vec3 point) {
|
||||||
|
return point.squareDistanceTo(x + 0.5, y + 0.5, z + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FMLEventHandler {
|
||||||
|
|
||||||
|
static boolean connected = false;
|
||||||
|
static boolean configSent = false;
|
||||||
|
|
||||||
|
private void resetPlayerLocation() {
|
||||||
|
lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX;
|
||||||
|
lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY;
|
||||||
|
lastPlayerPos.zCoord = Minecraft.getMinecraft().thePlayer.posZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onPlayerLogIn(FMLNetworkEvent.ClientConnectedToServerEvent e) {
|
||||||
|
connected = true;
|
||||||
|
configSent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onPlayerLogOut(FMLNetworkEvent.ClientDisconnectionFromServerEvent e) {
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent(priority = EventPriority.LOWEST)
|
||||||
|
public void onConfigChange(ConfigChangedEvent.PostConfigChangedEvent e) {
|
||||||
|
if (connected && e.modID.equals(StructureLibAPI.MOD_ID)) {
|
||||||
|
configSent = false;
|
||||||
|
sendRegistryOrderToServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onClientTick(ClientTickEvent e) {
|
||||||
|
if (e.phase == Phase.END && Minecraft.getMinecraft().thePlayer != null) {
|
||||||
|
Vec3 playerPos = Minecraft.getMinecraft().thePlayer.getPosition(1);
|
||||||
|
boolean sortRequired = false;
|
||||||
|
// The allGroups is implicitly sorted by creationTime
|
||||||
|
// here we exploit this ordering to reduce iteration size
|
||||||
|
int deadline = Minecraft.getMinecraft().thePlayer.ticksExisted
|
||||||
|
- ConfigurationHandler.INSTANCE.getHintLifespan();
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < allGroups.size(); i++) {
|
||||||
|
if (allGroups.get(i).getCreationTime() > deadline) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i != 0) {
|
||||||
|
sortRequired = true;
|
||||||
|
// clear this block and go no further
|
||||||
|
List<HintGroup> toRemove = allGroups.subList(0, i);
|
||||||
|
toRemove.forEach(ClientProxy::removeGroup);
|
||||||
|
toRemove.clear();
|
||||||
|
}
|
||||||
|
if (allHintsDirty) {
|
||||||
|
allHintsForRender.clear();
|
||||||
|
// we need greatly efficient sequential access and mildly efficient sorting
|
||||||
|
// rebuild is sort of rare, and it's expected to lag anyway
|
||||||
|
// so no min-heap here
|
||||||
|
for (HintGroup c : allGroups) allHintsForRender.addAll(c.getHints());
|
||||||
|
sortRequired = true;
|
||||||
|
}
|
||||||
|
if (sortRequired || playerPos.squareDistanceTo(lastPlayerPos) > 1e-2) {
|
||||||
|
// only redo sort if player moved some distance
|
||||||
|
// default is 0.1 block
|
||||||
|
// if there was a full rebuild, go sort it as well
|
||||||
|
allHintsForRender.sort(Comparator.comparingDouble(info -> -info.getSquareDistanceTo(playerPos)));
|
||||||
|
resetPlayerLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ForgeEventHandler {
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onEntityJoinWorld(EntityJoinWorldEvent e) {
|
||||||
|
if (e.world.isRemote && e.entity instanceof EntityClientPlayerMP) {
|
||||||
|
sendRegistryOrderToServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onWorldLoad(WorldEvent.Load e) {
|
||||||
|
if (e.world.isRemote) {
|
||||||
|
// flush hints. we are in a different world now.
|
||||||
|
allHintsForRender.clear();
|
||||||
|
allGroups.clear();
|
||||||
|
lastPlayerPos.yCoord = -1e30;
|
||||||
|
renderThrough = 0;
|
||||||
|
// clear throttles. hopefully a world switch is enough long as a cool down.
|
||||||
|
localThrottleMap.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onRenderWorldLast(RenderWorldLastEvent e) {
|
||||||
|
if (allHintsForRender.isEmpty()) return;
|
||||||
|
|
||||||
|
// seriously, I'm not a OpenGL expert, so I'm probably doing a lot of very stupid stuff here.
|
||||||
|
// Please consider contributing patches if you find something to optimize.
|
||||||
|
|
||||||
|
Profiler p = Minecraft.getMinecraft().mcProfiler;
|
||||||
|
|
||||||
|
p.startSection("HintParticle");
|
||||||
|
p.startSection("Prepare");
|
||||||
|
Frustrum frustrum = new Frustrum();
|
||||||
|
Entity entitylivingbase = Minecraft.getMinecraft().renderViewEntity;
|
||||||
|
double d0 = entitylivingbase.lastTickPosX
|
||||||
|
+ (entitylivingbase.posX - entitylivingbase.lastTickPosX) * e.partialTicks;
|
||||||
|
double d1 = entitylivingbase.lastTickPosY
|
||||||
|
+ (entitylivingbase.posY - entitylivingbase.lastTickPosY) * e.partialTicks;
|
||||||
|
double d2 = entitylivingbase.lastTickPosZ
|
||||||
|
+ (entitylivingbase.posZ - entitylivingbase.lastTickPosZ) * e.partialTicks;
|
||||||
|
int i0 = (int) d0, i1 = (int) d1, i2 = (int) d2;
|
||||||
|
frustrum.setPosition(d0, d1, d2);
|
||||||
|
|
||||||
|
GL11.glPushMatrix();
|
||||||
|
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT); // TODO figure out original states
|
||||||
|
// we need the back facing rendered because the thing is transparent
|
||||||
|
GL11.glDisable(GL11.GL_CULL_FACE);
|
||||||
|
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||||
|
GL11.glEnable(GL11.GL_BLEND); // enable blend so it is transparent
|
||||||
|
GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA);
|
||||||
|
// depth test begin as enabled
|
||||||
|
boolean renderThrough = false;
|
||||||
|
Minecraft.getMinecraft().renderEngine.bindTexture(TextureMap.locationBlocksTexture);
|
||||||
|
GL11.glTranslated(-d0 + i0, -d1 + i1, -d2 + i2);
|
||||||
|
Tessellator tes = Tessellator.instance;
|
||||||
|
tes.startDrawing(GL11.GL_TRIANGLES);
|
||||||
|
for (int i = 0, allHintsForRenderSize = allHintsForRender.size(), cubeDrawn = 0; i
|
||||||
|
< allHintsForRenderSize; i++, cubeDrawn++) {
|
||||||
|
HintParticleInfo hint = allHintsForRender.get(i);
|
||||||
|
if (!hint.isInFrustrum(frustrum)) continue;
|
||||||
|
if (renderThrough != hint.renderThrough) {
|
||||||
|
if (i > 0) {
|
||||||
|
p.endStartSection("Draw");
|
||||||
|
tes.draw();
|
||||||
|
tes.startDrawing(GL11.GL_TRIANGLES);
|
||||||
|
cubeDrawn = 0;
|
||||||
|
p.endStartSection("Prepare");
|
||||||
|
}
|
||||||
|
if (hint.renderThrough) GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||||
|
else GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||||
|
renderThrough = hint.renderThrough;
|
||||||
|
}
|
||||||
|
// Minecraft Tessellator draw in batch of 4096. This plays nicely with quads, but for trigs
|
||||||
|
// it might occasionally end up with one half in first batch and the other half in later batch.
|
||||||
|
// so we manually flush the buffer here if that would happen.
|
||||||
|
// 6 faces per particle, 2 trig per face, 3 vertex per trig
|
||||||
|
if ((cubeDrawn + 1) >= 4096 / (6 * 2 * 3)) {
|
||||||
|
tes.draw();
|
||||||
|
tes.startDrawing(GL11.GL_TRIANGLES);
|
||||||
|
}
|
||||||
|
hint.draw(tes, d0, d1, d2, i0, i1, i2);
|
||||||
|
}
|
||||||
|
p.endStartSection("Draw");
|
||||||
|
tes.draw();
|
||||||
|
p.endSection();
|
||||||
|
|
||||||
|
GL11.glPopAttrib();
|
||||||
|
GL11.glPopMatrix();
|
||||||
|
p.endSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.util.IChatComponent;
|
||||||
|
import net.minecraft.util.IIcon;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||||
|
|
||||||
|
public class CommonProxy {
|
||||||
|
|
||||||
|
public CommonProxy() {
|
||||||
|
super();
|
||||||
|
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {}
|
||||||
|
|
||||||
|
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {}
|
||||||
|
|
||||||
|
public void hintParticle(World w, int x, int y, int z, IIcon[] icons) {}
|
||||||
|
|
||||||
|
public void hintParticle(World w, int x, int y, int z, Block block, int meta) {}
|
||||||
|
|
||||||
|
public boolean updateHintParticleTint(EntityPlayer player, World w, int x, int y, int z, short[] rgBa) {
|
||||||
|
if (player instanceof EntityPlayerMP) { // just in case
|
||||||
|
StructureLib.net.sendTo(
|
||||||
|
new UpdateHintParticleMessage(x, (short) y, z, rgBa[0], rgBa[1], rgBa[2], rgBa[3]),
|
||||||
|
(EntityPlayerMP) player);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityPlayer getCurrentPlayer() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCurrentPlayer(EntityPlayer player) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addClientSideChatMessages(String... messages) {}
|
||||||
|
|
||||||
|
public void startHinting(World w) {}
|
||||||
|
|
||||||
|
public void endHinting(World w) {}
|
||||||
|
|
||||||
|
public void preInit(FMLPreInitializationEvent e) {}
|
||||||
|
|
||||||
|
public long getOverworldTime() {
|
||||||
|
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uploadChannels(ItemStack trigger) {}
|
||||||
|
|
||||||
|
public boolean markHintParticleError(EntityPlayer player, World w, int x, int y, int z) {
|
||||||
|
if (player instanceof EntityPlayerMP) { // just in case
|
||||||
|
StructureLib.net.sendTo(new ErrorHintParticleMessage(x, (short) y, z), (EntityPlayerMP) player);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||||
|
ConfigurationHandler.INSTANCE.loadRegistryOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<EntityPlayerMP, Map<Object, Long>> throttleMap = new WeakHashMap<>();
|
||||||
|
|
||||||
|
public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
|
||||||
|
boolean forceUpdateLastSend) {
|
||||||
|
if (player instanceof EntityPlayerMP) {
|
||||||
|
Map<Object, Long> submap = throttleMap.computeIfAbsent((EntityPlayerMP) player, p -> new HashMap<>());
|
||||||
|
addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend, submap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
|
||||||
|
short intervalRequired, boolean forceUpdateLastSend, Map<Object, Long> submap) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Long old;
|
||||||
|
if (forceUpdateLastSend) {
|
||||||
|
old = submap.put(throttleKey, now);
|
||||||
|
} else {
|
||||||
|
old = submap.get(throttleKey);
|
||||||
|
}
|
||||||
|
if (old == null || now - old >= intervalRequired) {
|
||||||
|
player.addChatComponentMessage(text);
|
||||||
|
if (!forceUpdateLastSend) submap.put(throttleKey, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayConfigGUI(String category) {}
|
||||||
|
|
||||||
|
public static class ForgeEventHandler {
|
||||||
|
|
||||||
|
private int ticksSinceLastPurge;
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onServerTickEnd(TickEvent.ServerTickEvent e) {
|
||||||
|
if (e.phase != TickEvent.Phase.END) return;
|
||||||
|
if (++ticksSinceLastPurge < 20 * 60 * 10) return;
|
||||||
|
ticksSinceLastPurge = 0;
|
||||||
|
MinecraftServer server = MinecraftServer.getServer();
|
||||||
|
// it's hard to tell why server could be null in a server tick handler, but who knows?
|
||||||
|
if (server == null) return;
|
||||||
|
SortedRegistry.cleanup(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,39 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
import java.io.File;
|
||||||
import cpw.mods.fml.common.FMLCommonHandler;
|
import java.lang.ref.WeakReference;
|
||||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import net.minecraftforge.common.config.ConfigCategory;
|
import net.minecraftforge.common.config.ConfigCategory;
|
||||||
import net.minecraftforge.common.config.Configuration;
|
import net.minecraftforge.common.config.Configuration;
|
||||||
import net.minecraftforge.common.config.Property;
|
import net.minecraftforge.common.config.Property;
|
||||||
|
|
||||||
import java.io.File;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import java.util.Map;
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||||
|
|
||||||
public enum ConfigurationHandler {
|
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;
|
||||||
|
private Map<String, Pair<List<String>, List<String>>> registryOrders;
|
||||||
|
|
||||||
ConfigurationHandler() {
|
ConfigurationHandler() {
|
||||||
FMLCommonHandler.instance().bus().register(this);
|
FMLCommonHandler.instance().bus().register(this);
|
||||||
@@ -39,11 +57,91 @@ 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.");
|
||||||
|
|
||||||
|
loadRegistryOrder();
|
||||||
|
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadRegistryOrder() {
|
||||||
|
loadRegistryOrderImpl();
|
||||||
|
setLanguageKeys();
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRegistryOrderImpl() {
|
||||||
|
registryOrders = new HashMap<>();
|
||||||
|
for (Map.Entry<String, WeakReference<SortedRegistry<?>>> e : SortedRegistry.ALL_REGISTRIES.entrySet()) {
|
||||||
|
SortedRegistry<?> r = e.getValue().get();
|
||||||
|
if (r == null) continue;
|
||||||
|
String category = "registries." + e.getKey();
|
||||||
|
if (FMLLaunchHandler.side().isClient()) {
|
||||||
|
// only meaningful on client, and would crash on server
|
||||||
|
config.setCategoryConfigEntryClass(category, RegistryOrderEntry.class);
|
||||||
|
}
|
||||||
|
Property pOrder = config.get(
|
||||||
|
category,
|
||||||
|
"ordering",
|
||||||
|
Iterables.toArray(r.getCurrentOrdering(), String.class),
|
||||||
|
"stuff not in this list will be automatically available after all entries listed here in their natural order, unless explicitly disabled in disabled config below.");
|
||||||
|
Property pDisable = config.get(category, "disabled", new String[0], "stuff in this list will be disabled");
|
||||||
|
List<String> all = Lists.newArrayList(r.getCurrentOrdering());
|
||||||
|
List<String> curVal = new ArrayList<>(Arrays.asList(pOrder.getStringList()));
|
||||||
|
List<String> disabled = new ArrayList<>(Arrays.asList(pDisable.getStringList()));
|
||||||
|
curVal.removeAll(disabled);
|
||||||
|
all.removeAll(disabled);
|
||||||
|
curVal.removeIf(s -> !all.remove(s));
|
||||||
|
curVal.addAll(all);
|
||||||
|
pOrder.set(curVal.toArray(new String[0]));
|
||||||
|
registryOrders.put(e.getKey(), Pair.of(curVal, disabled));
|
||||||
|
}
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveConfig() {
|
||||||
if (config.hasChanged()) {
|
if (config.hasChanged()) {
|
||||||
config.save();
|
config.save();
|
||||||
|
config.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +160,28 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair<List<String>, List<String>> getRegistryOrder(String name) {
|
||||||
|
return registryOrders.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
import net.minecraft.nbt.NBTTagList;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||||
|
|
||||||
|
public class DevelopHelper {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = Boolean.getBoolean("structurelib.develop_helper");
|
||||||
|
|
||||||
|
public static void onPreInit() {
|
||||||
|
if (!DEBUG) return;
|
||||||
|
StructureLib.LOGGER.info("Running develop helper");
|
||||||
|
StructureLibAPI.registerChannelDescription("sandstone", StructureLibAPI.MOD_ID, "variants of Sandstone");
|
||||||
|
StructureLibAPI.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 1, new ItemStack(Blocks.sandstone));
|
||||||
|
StructureLibAPI
|
||||||
|
.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 2, new ItemStack(Blocks.sandstone, 1, 1));
|
||||||
|
StructureLibAPI
|
||||||
|
.registerChannelItem("sandstone", StructureLibAPI.MOD_ID, 3, new ItemStack(Blocks.sandstone, 1, 2));
|
||||||
|
StructureLibAPI.registerChannelDescription("sandbricks", StructureLibAPI.MOD_ID, "variants of Sandbricks");
|
||||||
|
StructureLibAPI.registerChannelDescription("apple", StructureLibAPI.MOD_ID, "apple");
|
||||||
|
StructureLibAPI.registerChannelDescription("bright", StructureLibAPI.MOD_ID, "bright");
|
||||||
|
StructureLibAPI.registerChannelDescription("apple_blight", StructureLibAPI.MOD_ID, "apple_blight");
|
||||||
|
StructureLibAPI.registerChannelDescription("app_eagle", StructureLibAPI.MOD_ID, "app_eagle");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void x() {
|
||||||
|
NBTTagCompound command = new NBTTagCompound();
|
||||||
|
NBTTagList items = new NBTTagList();
|
||||||
|
int[] metas = { 0, 1, 2 };
|
||||||
|
for (int meta : metas) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound();
|
||||||
|
tag.setTag("Item", new ItemStack(Blocks.sandstone, 1, meta).writeToNBT(new NBTTagCompound()));
|
||||||
|
NBTTagList channels = new NBTTagList();
|
||||||
|
NBTTagCompound channel = new NBTTagCompound();
|
||||||
|
channel.setString("Channel", "sandstone");
|
||||||
|
channel.setInteger("Value", meta + 1);
|
||||||
|
channels.appendTag(channel);
|
||||||
|
tag.setTag("Channels", channels);
|
||||||
|
items.appendTag(tag);
|
||||||
|
}
|
||||||
|
command.setTag("Items", items);
|
||||||
|
FMLInterModComms.sendMessage("structurelib", "register_channel_item", command);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,695 @@
|
|||||||
|
/*
|
||||||
|
* Forge Mod Loader Copyright (c) 2012-2014 cpw. All rights reserved. This program and the accompanying materials are
|
||||||
|
* made available under the terms of the GNU Lesser Public License v2.1 which accompanies this distribution, and is
|
||||||
|
* available at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html Contributors (this class): bspkrs - implementation
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import static cpw.mods.fml.client.config.GuiUtils.RESET_CHAR;
|
||||||
|
import static cpw.mods.fml.client.config.GuiUtils.UNDO_CHAR;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.FontRenderer;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiListExtended;
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
import net.minecraft.client.gui.ScaledResolution;
|
||||||
|
import net.minecraft.client.renderer.OpenGlHelper;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
import net.minecraft.util.MathHelper;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.config.GuiButtonExt;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.config.GuiUnicodeGlyphButton;
|
||||||
|
import cpw.mods.fml.client.config.GuiUtils;
|
||||||
|
import cpw.mods.fml.client.config.HoverChecker;
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.client.event.ConfigChangedEvent.PostConfigChangedEvent;
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.Loader;
|
||||||
|
import cpw.mods.fml.common.eventhandler.Event.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is the base GuiScreen for all config GUI screens. It can be extended by mods to provide the top-level
|
||||||
|
* config screen that will be called when the Config button is clicked from the Main Menu Mods list.
|
||||||
|
*
|
||||||
|
* @author bspkrs
|
||||||
|
*/
|
||||||
|
public class GuiEditRegistryConfig extends GuiScreen {
|
||||||
|
|
||||||
|
public static final String OMITTED = "..";
|
||||||
|
public static final int LIST_WINDOW_PADDING = 20;
|
||||||
|
public static final int MID_SECTION_WIDTH = 10;
|
||||||
|
/**
|
||||||
|
* A reference to the screen object that created this. Used for navigating between screens.
|
||||||
|
*/
|
||||||
|
public final GuiScreen parentScreen;
|
||||||
|
private final IConfigElement<String> disabledElement;
|
||||||
|
private final String[] beforeDisabledValue;
|
||||||
|
private final IConfigElement<String> orderElement;
|
||||||
|
private final String[] beforeOrderValue;
|
||||||
|
final Collection<EntriesList> lists;
|
||||||
|
private final String registryName;
|
||||||
|
EntriesList disabledEntries;
|
||||||
|
EntriesList orderEntries;
|
||||||
|
public String title = "Config GUI";
|
||||||
|
public String titleLine2;
|
||||||
|
private GuiButtonExt btnDefault;
|
||||||
|
private GuiButtonExt btnUndoAll;
|
||||||
|
public final String modID;
|
||||||
|
/**
|
||||||
|
* When set to a non-null value the OnConfigChanged and PostConfigChanged events will be posted when the Done button
|
||||||
|
* is pressed if any configElements were changed (includes child screens). If not defined, the events will be posted
|
||||||
|
* if the parent gui is null or if the parent gui is not an instance of GuiConfig.
|
||||||
|
*/
|
||||||
|
public final String configID;
|
||||||
|
public final boolean isWorldRunning;
|
||||||
|
public final boolean allRequireWorldRestart;
|
||||||
|
public final boolean allRequireMcRestart;
|
||||||
|
public boolean needsRefresh = true;
|
||||||
|
private HoverChecker undoHoverChecker;
|
||||||
|
private HoverChecker resetHoverChecker;
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
public GuiEditRegistryConfig(GuiScreen parentScreen, IConfigElement disabled, IConfigElement order,
|
||||||
|
String registryName, String modID, String configID, boolean allRequireWorldRestart,
|
||||||
|
boolean allRequireMcRestart, String title, String titleLine2) {
|
||||||
|
this.mc = parentScreen.mc;
|
||||||
|
this.lists = Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
|
this.disabledElement = disabled;
|
||||||
|
this.beforeDisabledValue = (String[]) this.disabledElement.getList();
|
||||||
|
this.orderElement = order;
|
||||||
|
this.beforeOrderValue = (String[]) this.orderElement.getList();
|
||||||
|
this.parentScreen = parentScreen;
|
||||||
|
this.allRequireWorldRestart = allRequireWorldRestart;
|
||||||
|
this.allRequireMcRestart = allRequireMcRestart;
|
||||||
|
this.registryName = registryName;
|
||||||
|
this.modID = modID;
|
||||||
|
this.configID = configID;
|
||||||
|
this.isWorldRunning = mc.theWorld != null;
|
||||||
|
if (title != null) this.title = title;
|
||||||
|
this.titleLine2 = titleLine2;
|
||||||
|
if (this.titleLine2 != null && this.titleLine2.startsWith(" > "))
|
||||||
|
this.titleLine2 = this.titleLine2.replaceFirst(" > ", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the buttons (and other controls) to the screen in question.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initGui() {
|
||||||
|
Keyboard.enableRepeatEvents(true);
|
||||||
|
if (this.disabledEntries == null || this.needsRefresh) {
|
||||||
|
this.disabledEntries = new EntriesList(
|
||||||
|
(width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING,
|
||||||
|
LIST_WINDOW_PADDING,
|
||||||
|
I18n.format("structurelib.configgui.disabled"));
|
||||||
|
this.disabledEntries.initEntries(Arrays.asList(disabledElement.getList()));
|
||||||
|
} else {
|
||||||
|
this.disabledEntries.reinit((width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING, LIST_WINDOW_PADDING);
|
||||||
|
}
|
||||||
|
if (this.orderEntries == null || this.needsRefresh) {
|
||||||
|
this.orderEntries = new EntriesList(
|
||||||
|
(width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING,
|
||||||
|
(width + MID_SECTION_WIDTH) / 2,
|
||||||
|
I18n.format("structurelib.configgui.enabled"));
|
||||||
|
this.orderEntries.initEntries(Arrays.asList(orderElement.getList()));
|
||||||
|
} else {
|
||||||
|
this.orderEntries
|
||||||
|
.reinit((width - MID_SECTION_WIDTH) / 2 - LIST_WINDOW_PADDING, (width + MID_SECTION_WIDTH) / 2);
|
||||||
|
}
|
||||||
|
this.needsRefresh = false;
|
||||||
|
|
||||||
|
int undoGlyphWidth = mc.fontRenderer.getStringWidth(UNDO_CHAR) * 2;
|
||||||
|
int resetGlyphWidth = mc.fontRenderer.getStringWidth(RESET_CHAR) * 2;
|
||||||
|
int doneWidth = Math.max(mc.fontRenderer.getStringWidth(I18n.format("gui.done")) + 20, 100);
|
||||||
|
int undoWidth = mc.fontRenderer.getStringWidth(" " + I18n.format("fml.configgui.tooltip.undoChanges"))
|
||||||
|
+ undoGlyphWidth
|
||||||
|
+ 20;
|
||||||
|
int resetWidth = mc.fontRenderer.getStringWidth(" " + I18n.format("fml.configgui.tooltip.resetToDefault"))
|
||||||
|
+ resetGlyphWidth
|
||||||
|
+ 20;
|
||||||
|
int buttonWidthHalf = (doneWidth + 5 + undoWidth + 5 + resetWidth + 5) / 2;
|
||||||
|
this.buttonList.add(
|
||||||
|
new GuiButtonExt(
|
||||||
|
2000,
|
||||||
|
this.width / 2 - buttonWidthHalf,
|
||||||
|
this.height - 29,
|
||||||
|
doneWidth,
|
||||||
|
20,
|
||||||
|
I18n.format("gui.done")));
|
||||||
|
this.buttonList.add(
|
||||||
|
this.btnDefault = new GuiUnicodeGlyphButton(
|
||||||
|
2001,
|
||||||
|
this.width / 2 - buttonWidthHalf + doneWidth + 5 + undoWidth + 5,
|
||||||
|
this.height - 29,
|
||||||
|
resetWidth,
|
||||||
|
20,
|
||||||
|
" " + I18n.format("fml.configgui.tooltip.resetToDefault"),
|
||||||
|
RESET_CHAR,
|
||||||
|
2.0F));
|
||||||
|
this.buttonList.add(
|
||||||
|
btnUndoAll = new GuiUnicodeGlyphButton(
|
||||||
|
2002,
|
||||||
|
this.width / 2 - buttonWidthHalf + doneWidth + 5,
|
||||||
|
this.height - 29,
|
||||||
|
undoWidth,
|
||||||
|
20,
|
||||||
|
" " + I18n.format("fml.configgui.tooltip.undoChanges"),
|
||||||
|
UNDO_CHAR,
|
||||||
|
2.0F));
|
||||||
|
|
||||||
|
this.undoHoverChecker = new HoverChecker(this.btnUndoAll, 800);
|
||||||
|
this.resetHoverChecker = new HoverChecker(this.btnDefault, 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the screen is unloaded. Used to disable keyboard repeat events
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onGuiClosed() {
|
||||||
|
if (this.configID != null && this.parentScreen instanceof GuiConfig parentGuiConfig) {
|
||||||
|
parentGuiConfig.needsRefresh = true;
|
||||||
|
parentGuiConfig.initGui();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(this.parentScreen instanceof GuiConfig)) Keyboard.enableRepeatEvents(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean saveConfigElements() {
|
||||||
|
if (isChanged()) {
|
||||||
|
disabledElement.set(disabledEntries.entries.stream().map(e -> e.name).toArray(String[]::new));
|
||||||
|
orderElement.set(orderEntries.entries.stream().map(e -> e.name).toArray(String[]::new));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void actionPerformed(GuiButton button) {
|
||||||
|
if (button.id == 2000) {
|
||||||
|
try {
|
||||||
|
if ((configID != null || this.parentScreen == null || !(this.parentScreen instanceof GuiConfig))
|
||||||
|
&& isChanged()) {
|
||||||
|
saveConfigElements();
|
||||||
|
if (Loader.isModLoaded(modID)) {
|
||||||
|
ConfigChangedEvent event = new OnConfigChangedEvent(modID, configID, isWorldRunning, false);
|
||||||
|
FMLCommonHandler.instance().bus().post(event);
|
||||||
|
if (!event.getResult().equals(Result.DENY)) FMLCommonHandler.instance().bus()
|
||||||
|
.post(new PostConfigChangedEvent(modID, configID, isWorldRunning, false));
|
||||||
|
|
||||||
|
if (this.parentScreen instanceof GuiConfig) ((GuiConfig) this.parentScreen).needsRefresh = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mc.displayGuiScreen(this.parentScreen);
|
||||||
|
} else if (button.id == 2001) {
|
||||||
|
setToDefault();
|
||||||
|
} else if (button.id == 2002) {
|
||||||
|
undoChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return disabledEntries != null && orderEntries != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void undoChanges() {
|
||||||
|
disabledEntries.initEntries(Arrays.asList(beforeDisabledValue));
|
||||||
|
orderEntries.initEntries(Arrays.asList(beforeOrderValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToDefault() {
|
||||||
|
disabledEntries.initEntries(Arrays.asList(disabledElement.getList()));
|
||||||
|
orderEntries.initEntries(Arrays.asList(orderElement.getList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDefault() {
|
||||||
|
return !orderEntries.areContentEqual(orderElement.getDefaults())
|
||||||
|
|| !disabledEntries.areContentEqual(disabledElement.getDefaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChanged() {
|
||||||
|
return !orderEntries.areContentEqual(beforeOrderValue) || !disabledEntries.areContentEqual(beforeDisabledValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the mouse is clicked.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void mouseClicked(int x, int y, int mouseEvent) {
|
||||||
|
if (mouseEvent != 0 || !this.disabledEntries.func_148179_a(x, y, mouseEvent)
|
||||||
|
&& !this.orderEntries.func_148179_a(x, y, mouseEvent)) {
|
||||||
|
super.mouseClicked(x, y, mouseEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the mouse is moved or a mouse button is released. Signature: (mouseX, mouseY, which) which==-1 is
|
||||||
|
* mouseMove, which==0 or which==1 is mouseUp
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void mouseMovedOrUp(int x, int y, int mouseEvent) {
|
||||||
|
if (mouseEvent != 0 || !this.disabledEntries.func_148181_b(x, y, mouseEvent)
|
||||||
|
&& !this.orderEntries.func_148181_b(x, y, mouseEvent)) {
|
||||||
|
super.mouseMovedOrUp(x, y, mouseEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a key is typed. This is the equivalent of KeyListener.keyTyped(KeyEvent e).
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void keyTyped(char eventChar, int eventKey) {
|
||||||
|
if (eventKey == Keyboard.KEY_ESCAPE) this.mc.displayGuiScreen(parentScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimStringToWidth(String str, int width) {
|
||||||
|
int strwidth = mc.fontRenderer.getStringWidth(str);
|
||||||
|
int elipsisWidth = mc.fontRenderer.getStringWidth(OMITTED);
|
||||||
|
if (strwidth > width - elipsisWidth && elipsisWidth > strwidth) {
|
||||||
|
return mc.fontRenderer.trimStringToWidth(str, width - elipsisWidth).trim() + OMITTED;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the screen and all the components in it.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||||
|
for (EntriesList list : lists) {
|
||||||
|
list.handleDrag(mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
this.drawDefaultBackground();
|
||||||
|
for (EntriesList list : lists) {
|
||||||
|
list.drawScreen(mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
this.drawCenteredString(this.fontRendererObj, this.title, this.width / 2, 8, 16777215);
|
||||||
|
String title2 = this.titleLine2;
|
||||||
|
|
||||||
|
if (title2 != null) {
|
||||||
|
title2 = trimStringToWidth(title2, width - 6);
|
||||||
|
this.drawCenteredString(this.fontRendererObj, title2, this.width / 2, 18, 16777215);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.btnUndoAll.enabled = isChanged();
|
||||||
|
this.btnDefault.enabled = isDefault();
|
||||||
|
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||||
|
for (EntriesList list : lists) {
|
||||||
|
list.drawScreenPost(mouseX, mouseY, partialTicks);
|
||||||
|
}
|
||||||
|
if (this.undoHoverChecker.checkHover(mouseX, mouseY)) this.func_146283_a(
|
||||||
|
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.undoAll"), 300),
|
||||||
|
mouseX,
|
||||||
|
mouseY);
|
||||||
|
if (this.resetHoverChecker.checkHover(mouseX, mouseY)) this.func_146283_a(
|
||||||
|
this.mc.fontRenderer.listFormattedStringToWidth(I18n.format("fml.configgui.tooltip.resetAll"), 300),
|
||||||
|
mouseX,
|
||||||
|
mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EntriesList extends GuiListExtended {
|
||||||
|
|
||||||
|
final String heading;
|
||||||
|
final List<ListEntry> entries = new ArrayList<>();
|
||||||
|
int controlX;
|
||||||
|
|
||||||
|
public EntriesList(int width, int left, String heading) {
|
||||||
|
super(
|
||||||
|
mc,
|
||||||
|
width,
|
||||||
|
GuiEditRegistryConfig.this.height,
|
||||||
|
titleLine2 != null ? 33 : 23,
|
||||||
|
GuiEditRegistryConfig.this.height - 32,
|
||||||
|
20);
|
||||||
|
this.heading = heading;
|
||||||
|
if (heading != null) {
|
||||||
|
setHasListHeader(true, mc.fontRenderer.FONT_HEIGHT + 4);
|
||||||
|
}
|
||||||
|
setSlotXBoundsFromLeft(left);
|
||||||
|
this.controlX = this.getScrollBarX() - 20;
|
||||||
|
lists.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawScreen(int p_148128_1_, int p_148128_2_, float p_148128_3_) {
|
||||||
|
GL11.glEnable(GL11.GL_SCISSOR_TEST);
|
||||||
|
ScaledResolution scaledresolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||||
|
GL11.glScissor(
|
||||||
|
(left - 4) * scaledresolution.getScaleFactor(),
|
||||||
|
(int) ((scaledresolution.getScaledHeight_double() - (bottom + 4))
|
||||||
|
* scaledresolution.getScaleFactor()),
|
||||||
|
(width + 8) * scaledresolution.getScaleFactor(),
|
||||||
|
(height + 8) * scaledresolution.getScaleFactor());
|
||||||
|
super.drawScreen(p_148128_1_, p_148128_2_, p_148128_3_);
|
||||||
|
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void overlayBackground(int p_148136_1_, int p_148136_2_, int p_148136_3_, int p_148136_4_) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawContainerBackground(Tessellator tessellator) {
|
||||||
|
GL11.glEnable(GL11.GL_BLEND);
|
||||||
|
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 0, 1);
|
||||||
|
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||||
|
GL11.glShadeModel(GL11.GL_SMOOTH);
|
||||||
|
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setColorRGBA_I(0, 64);
|
||||||
|
tessellator.addVertex(left, bottom, 0);
|
||||||
|
tessellator.addVertex(right, bottom, 0);
|
||||||
|
tessellator.addVertex(right, top, 0);
|
||||||
|
tessellator.addVertex(left, top, 0);
|
||||||
|
tessellator.draw();
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
GL11.glShadeModel(GL11.GL_FLAT);
|
||||||
|
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||||
|
GL11.glDisable(GL11.GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawListHeader(int p_148129_1_, int p_148129_2_, Tessellator p_148129_3_) {
|
||||||
|
if (heading == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FontRenderer fr = mc.fontRenderer;
|
||||||
|
fr.drawString(
|
||||||
|
this.heading,
|
||||||
|
p_148129_1_ + this.width / 2 - fr.getStringWidth(this.heading) / 2,
|
||||||
|
Math.min(this.top + 3, p_148129_2_),
|
||||||
|
0xffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reinit(int width, int left) {
|
||||||
|
this.width = width;
|
||||||
|
this.bottom = GuiEditRegistryConfig.this.height - 32;
|
||||||
|
this.setSlotXBoundsFromLeft(left);
|
||||||
|
this.controlX = this.getScrollBarX() - 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initEntries(Iterable<?> entries) {
|
||||||
|
this.controlX = this.getScrollBarX() - 20;
|
||||||
|
this.entries.clear();
|
||||||
|
for (Object entry : entries) {
|
||||||
|
addEntry(new ListEntry(entry.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addEntry(ListEntry entry) {
|
||||||
|
entry.entriesList = this;
|
||||||
|
this.entries.add(entry);
|
||||||
|
this.controlX = Math.min(this.controlX, this.getScrollBarX() - entry.getControlSetWidth() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IGuiListEntry getListEntry(int i) {
|
||||||
|
return entries.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getSize() {
|
||||||
|
return entries.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLabelX() {
|
||||||
|
return left + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getControlX() {
|
||||||
|
return controlX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getListWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getScrollBarX() {
|
||||||
|
return left + getListWidth() - 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleDrag(int mouseX, int mouseY, float partialTicks) {
|
||||||
|
for (int i = 0, entriesSize = entries.size(); i < entriesSize; i++) {
|
||||||
|
ListEntry entry = entries.get(i);
|
||||||
|
if (entry.handleDrag(i, mouseX, mouseY, partialTicks)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawScreenPost(int mouseX, int mouseY, float partialTicks) {
|
||||||
|
for (ListEntry entry : entries) {
|
||||||
|
entry.drawToolTip(mouseX, mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean areContentEqual(Object[] vals) {
|
||||||
|
if (vals.length != entries.size()) return false;
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
ListEntry entry = entries.get(i);
|
||||||
|
if (!entry.name.equals(vals[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListEntry implements GuiListExtended.IGuiListEntry {
|
||||||
|
|
||||||
|
private EntriesList entriesList;
|
||||||
|
private final HoverChecker hoverCheckerText = new HoverChecker(0, 0, 0, 0, 0);
|
||||||
|
final String name;
|
||||||
|
final String localizedName;
|
||||||
|
private final String tooltip;
|
||||||
|
public boolean modified = false;
|
||||||
|
|
||||||
|
private final GuiButtonExt btnDrag = new GuiButtonDrag(1, 0, 0, 20, 20);
|
||||||
|
private final HoverChecker hoverCheckerBtnDrag = new HoverChecker(btnDrag, 800);
|
||||||
|
private boolean isDragging = false;
|
||||||
|
private long lastScroll = 0;
|
||||||
|
|
||||||
|
ListEntry(String name) {
|
||||||
|
this.name = name;
|
||||||
|
String nameKey = "structurelib.sortedregistry." + registryName + "." + name;
|
||||||
|
this.localizedName = trans(nameKey, name);
|
||||||
|
|
||||||
|
String tooltipKey = nameKey + ".tooltip";
|
||||||
|
String tooltip = trans(tooltipKey, null);
|
||||||
|
StringBuilder tooltipBuilder = new StringBuilder();
|
||||||
|
tooltipBuilder.append(EnumChatFormatting.GREEN);
|
||||||
|
tooltipBuilder.append(this.localizedName);
|
||||||
|
if (tooltip != null) {
|
||||||
|
tooltipBuilder.append('\n').append(EnumChatFormatting.YELLOW).append(tooltip);
|
||||||
|
}
|
||||||
|
tooltipBuilder.append('\n');
|
||||||
|
tooltipBuilder.append(EnumChatFormatting.DARK_GRAY);
|
||||||
|
tooltipBuilder.append(this.name);
|
||||||
|
this.tooltip = tooltipBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, Tessellator tessellator,
|
||||||
|
int mouseX, int mouseY, boolean isSelected) {
|
||||||
|
drawOutlineBox(x, y, listWidth, slotHeight, tessellator);
|
||||||
|
drawLabel(y, slotHeight);
|
||||||
|
drawControlSet(y, slotHeight, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawOutlineBox(int x, int y, int listWidth, int slotHeight, Tessellator tessellator) {
|
||||||
|
if (isDragging) {
|
||||||
|
// left x, right x, top y, bottom y
|
||||||
|
int lx = x, rx = entriesList.getScrollBarX() - 4, ty = y, by = y + slotHeight;
|
||||||
|
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setColorOpaque_I(0x808080);
|
||||||
|
tessellator.addVertexWithUV(lx - 4, by + 4, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(rx + 4, by + 4, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(rx + 4, ty - 4, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(lx - 4, ty - 4, 0.0D, 0.0D, 0.0D);
|
||||||
|
tessellator.setColorOpaque_I(0x202020);
|
||||||
|
tessellator.addVertexWithUV(lx - 2, by + 2, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(rx + 2, by + 2, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(rx + 2, ty - 2, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(lx - 2, ty - 2, 0.0D, 0.0D, 0.0D);
|
||||||
|
tessellator.draw();
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawControlSet(int y, int slotHeight, int mouseX, int mouseY) {
|
||||||
|
btnDrag.xPosition = entriesList.getControlX();
|
||||||
|
btnDrag.yPosition = y + (slotHeight - btnDrag.height) / 2;
|
||||||
|
btnDrag.drawButton(mc, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawLabel(int y, int slotHeight) {
|
||||||
|
String label = modified ? EnumChatFormatting.ITALIC + localizedName : localizedName;
|
||||||
|
int allowedWidth = entriesList.getControlX() - entriesList.getLabelX() - 4;
|
||||||
|
// probably not very wise to recompute this every render tick, but it doesn't matter for now
|
||||||
|
String toDraw = trimStringToWidth(label, allowedWidth);
|
||||||
|
hoverCheckerText.updateBounds(
|
||||||
|
y + slotHeight / 2 - mc.fontRenderer.FONT_HEIGHT / 2,
|
||||||
|
y + slotHeight / 2 + mc.fontRenderer.FONT_HEIGHT / 2,
|
||||||
|
entriesList.getLabelX(),
|
||||||
|
entriesList.getLabelX() + mc.fontRenderer.getStringWidth(toDraw));
|
||||||
|
|
||||||
|
mc.fontRenderer.drawString(
|
||||||
|
toDraw,
|
||||||
|
entriesList.getLabelX(),
|
||||||
|
y + slotHeight / 2 - mc.fontRenderer.FONT_HEIGHT / 2,
|
||||||
|
getLabelColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawToolTip(int mouseX, int mouseY) {
|
||||||
|
// ensure checkHover() is always called, or the hover threshold might fail under certain corner cases
|
||||||
|
if (hoverCheckerText.checkHover(mouseX, mouseY)) {
|
||||||
|
GuiEditRegistryConfig.this.func_146283_a(
|
||||||
|
mc.fontRenderer.listFormattedStringToWidth(tooltip, entriesList.getListWidth()),
|
||||||
|
mouseX,
|
||||||
|
mouseY);
|
||||||
|
}
|
||||||
|
if (hoverCheckerBtnDrag.checkHover(mouseX, mouseY) && !isDragging) {
|
||||||
|
GuiEditRegistryConfig.this.func_146283_a(
|
||||||
|
mc.fontRenderer.listFormattedStringToWidth(
|
||||||
|
I18n.format("structurelib.configgui.drag.tooltip"),
|
||||||
|
entriesList.getListWidth()),
|
||||||
|
mouseX,
|
||||||
|
mouseY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getLabelColor() {
|
||||||
|
return isDragging ? 0x888888 : 0xffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getControlSetWidth() {
|
||||||
|
return this.btnDrag.getButtonWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleDrag(int index, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
if (!isDragging) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (EntriesList list : lists) {
|
||||||
|
if (list == entriesList) continue;
|
||||||
|
if (mouseX > list.left && mouseX < list.right && mouseY > list.top && mouseY < list.bottom) {
|
||||||
|
int l;
|
||||||
|
if (list.entries.isEmpty()) l = 0;
|
||||||
|
else l = list.func_148124_c(mouseX, mouseY);
|
||||||
|
if (l < 0) {
|
||||||
|
if (mouseY < (list.bottom + list.top) / 2) {
|
||||||
|
l = 0;
|
||||||
|
} else {
|
||||||
|
l = list.entries.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entriesList.func_148143_b(true);
|
||||||
|
entriesList.entries.remove(index);
|
||||||
|
list.func_148143_b(false);
|
||||||
|
list.entries.add(l, this);
|
||||||
|
this.entriesList = list;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - lastScroll > 20) {
|
||||||
|
if (mouseY < entriesList.top) {
|
||||||
|
entriesList.scrollBy(-3);
|
||||||
|
lastScroll = now;
|
||||||
|
} else if (mouseY > entriesList.bottom) {
|
||||||
|
entriesList.scrollBy(3);
|
||||||
|
lastScroll = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int l = entriesList.func_148124_c(
|
||||||
|
MathHelper.clamp_int(mouseX, entriesList.left, entriesList.right),
|
||||||
|
MathHelper.clamp_int(mouseY, entriesList.top, entriesList.bottom));
|
||||||
|
if (l < 0 || l == index) return true;
|
||||||
|
entriesList.entries.remove(index);
|
||||||
|
entriesList.entries.add(l, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
|
||||||
|
isDragging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) {
|
||||||
|
if (btnDrag.mousePressed(mc, x, y)) {
|
||||||
|
isDragging = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String trans(String transKey, String fallback) {
|
||||||
|
String translated = I18n.format(transKey);
|
||||||
|
return translated.equals(transKey) ? fallback : translated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GuiButtonDrag extends GuiButtonExt {
|
||||||
|
|
||||||
|
public GuiButtonDrag(int id, int xPos, int yPos, int width, int height) {
|
||||||
|
super(id, xPos, yPos, width, height, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawButton(Minecraft mc, int mouseX, int mouseY) {
|
||||||
|
if (this.visible) {
|
||||||
|
this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition
|
||||||
|
&& mouseX < this.xPosition + this.width
|
||||||
|
&& mouseY < this.yPosition + this.height;
|
||||||
|
int k = this.getHoverState(this.field_146123_n);
|
||||||
|
GuiUtils.drawContinuousTexturedBox(
|
||||||
|
buttonTextures,
|
||||||
|
this.xPosition,
|
||||||
|
this.yPosition,
|
||||||
|
0,
|
||||||
|
46 + k * 20,
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
200,
|
||||||
|
20,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
this.zLevel);
|
||||||
|
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/fourwayarrow2.png"));
|
||||||
|
int x = this.xPosition + (this.width - 12) / 2;
|
||||||
|
int y = this.yPosition + (this.height - 12) / 2;
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.addVertexWithUV(x, y + 12, zLevel, 0, 1);
|
||||||
|
tessellator.addVertexWithUV(x + 12, y + 12, zLevel, 1, 1);
|
||||||
|
tessellator.addVertexWithUV(x + 12, y, zLevel, 1, 0);
|
||||||
|
tessellator.addVertexWithUV(x, y, zLevel, 0, 0);
|
||||||
|
tessellator.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
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,32 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.gui.EmptyContainer;
|
||||||
|
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.network.IGuiHandler;
|
||||||
|
|
||||||
|
public class GuiHandler implements IGuiHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||||
|
switch (ID) {
|
||||||
|
case 0:
|
||||||
|
return new EmptyContainer(player);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
|
||||||
|
switch (ID) {
|
||||||
|
case 0:
|
||||||
|
return new GuiScreenConfigureChannels(new EmptyContainer(player), player.inventory.getStackInSlot(x));
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,31 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
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,77 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.GuiScreen;
|
||||||
|
|
||||||
|
import cpw.mods.fml.client.config.GuiConfig;
|
||||||
|
import cpw.mods.fml.client.config.GuiConfigEntries;
|
||||||
|
import cpw.mods.fml.client.config.IConfigElement;
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked" })
|
||||||
|
public final class RegistryOrderEntry extends GuiConfigEntries.CategoryEntry {
|
||||||
|
|
||||||
|
public RegistryOrderEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList,
|
||||||
|
IConfigElement<?> configElement) {
|
||||||
|
super(owningScreen, owningEntryList, configElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected GuiScreen buildChildScreen() {
|
||||||
|
List<IConfigElement<?>> childElements = this.configElement.getChildElements();
|
||||||
|
IConfigElement<?> disabled = null, order = null;
|
||||||
|
for (IConfigElement<?> e : childElements) {
|
||||||
|
if (e.getName().equals("disabled")) disabled = e;
|
||||||
|
else if (e.getName().equals("ordering")) order = e;
|
||||||
|
}
|
||||||
|
if (disabled == null || order == null) {
|
||||||
|
throw new IllegalArgumentException("Did not find expected config entires");
|
||||||
|
}
|
||||||
|
return new GuiEditRegistryConfig(
|
||||||
|
this.owningScreen,
|
||||||
|
disabled,
|
||||||
|
order,
|
||||||
|
configElement.getName(),
|
||||||
|
this.owningScreen.modID,
|
||||||
|
null,
|
||||||
|
owningScreen.allRequireWorldRestart || this.configElement.requiresWorldRestart(),
|
||||||
|
owningScreen.allRequireMcRestart || this.configElement.requiresMcRestart(),
|
||||||
|
this.owningScreen.title,
|
||||||
|
((this.owningScreen.titleLine2 == null ? "" : this.owningScreen.titleLine2) + " > " + this.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private GuiEditRegistryConfig getChildScreen() {
|
||||||
|
return (GuiEditRegistryConfig) childScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean saveConfigElement() {
|
||||||
|
GuiEditRegistryConfig c = getChildScreen();
|
||||||
|
if (c != null && c.isInitialized()) return c.saveConfigElements();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChanged() {
|
||||||
|
GuiEditRegistryConfig c = getChildScreen();
|
||||||
|
return c != null && c.isInitialized() && c.isChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefault() {
|
||||||
|
GuiEditRegistryConfig c = getChildScreen();
|
||||||
|
return c != null && c.isInitialized() && c.isDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setToDefault() {
|
||||||
|
GuiEditRegistryConfig c = getChildScreen();
|
||||||
|
if (c != null && c.isInitialized()) c.setToDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoChanges() {
|
||||||
|
GuiEditRegistryConfig c = getChildScreen();
|
||||||
|
if (c != null && c.isInitialized()) c.undoChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.FMLCommonHandler;
|
||||||
|
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||||
|
import cpw.mods.fml.common.gameevent.PlayerEvent;
|
||||||
|
|
||||||
|
public class SortedRegistry<V> implements Iterable<V> {
|
||||||
|
|
||||||
|
static final ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES = new ConcurrentHashMap<>();
|
||||||
|
static {
|
||||||
|
RegistryOrderSyncMessage.Handler.setAllRegistries(ALL_REGISTRIES);
|
||||||
|
}
|
||||||
|
private final NavigableMap<String, V> store = new TreeMap<>();
|
||||||
|
/**
|
||||||
|
* Store stuff in an array for faster iteration access than a red black tree
|
||||||
|
*/
|
||||||
|
private List<V> baked = Collections.emptyList();
|
||||||
|
|
||||||
|
private final Map<UUID, Pair<List<String>, List<String>>> playerOrdering = new HashMap<>();
|
||||||
|
private final Map<UUID, List<V>> playerBaked = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an unsynchronized registry
|
||||||
|
*/
|
||||||
|
public SortedRegistry() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a synchronized registry whose ordering will be synced to the server
|
||||||
|
*
|
||||||
|
* @param name network name
|
||||||
|
*/
|
||||||
|
|
||||||
|
public SortedRegistry(String name) {
|
||||||
|
WeakReference<SortedRegistry<?>> oldValue = ALL_REGISTRIES.put(name, new WeakReference<>(this));
|
||||||
|
if (oldValue != null && oldValue.get() != null) {
|
||||||
|
throw new IllegalArgumentException("duplicate name");
|
||||||
|
}
|
||||||
|
FMLCommonHandler.instance().bus().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getRegistryNames() {
|
||||||
|
return ALL_REGISTRIES.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SortedRegistry<?> getRegistry(String name) {
|
||||||
|
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(name);
|
||||||
|
return ref != null ? ref.get() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup(MinecraftServer server) {
|
||||||
|
List<EntityPlayerMP> players = server.getConfigurationManager().playerEntityList;
|
||||||
|
Set<UUID> onlinePlayerIDs = players.stream().map(EntityPlayerMP::getUniqueID).collect(Collectors.toSet());
|
||||||
|
int removed = 0;
|
||||||
|
for (WeakReference<SortedRegistry<?>> ref : ALL_REGISTRIES.values()) {
|
||||||
|
SortedRegistry<?> registry = ref.get();
|
||||||
|
if (registry == null) continue;
|
||||||
|
for (Iterator<UUID> iterator = registry.playerOrdering.keySet().iterator(); iterator.hasNext();) {
|
||||||
|
UUID uuid = iterator.next();
|
||||||
|
if (!onlinePlayerIDs.contains(uuid)) {
|
||||||
|
iterator.remove();
|
||||||
|
registry.playerBaked.remove(uuid);
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removed > 0) {
|
||||||
|
StructureLib.LOGGER.debug("Removed {} registry record for logged out players", removed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onPlayerLogOut(PlayerEvent.PlayerLoggedOutEvent e) {
|
||||||
|
playerOrdering.remove(e.player.getUniqueID());
|
||||||
|
playerBaked.remove(e.player.getUniqueID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(String key, V val) {
|
||||||
|
if (key == null || val == null) throw new NullPointerException();
|
||||||
|
V old = store.putIfAbsent(key, val);
|
||||||
|
if (old != null) {
|
||||||
|
throw new IllegalArgumentException("Duplicate key: " + key);
|
||||||
|
}
|
||||||
|
baked = new ArrayList<>(store.values());
|
||||||
|
for (Map.Entry<UUID, Pair<List<String>, List<String>>> e : playerOrdering.entrySet()) {
|
||||||
|
makePlayerBaked(e.getKey(), e.getValue().getKey(), e.getValue().getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerOrdering(EntityPlayerMP player, List<String> ordering, List<String> disabled) {
|
||||||
|
registerOrdering(player.getUniqueID(), ordering, disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerOrdering(UUID playerID, List<String> ordering, List<String> disabled) {
|
||||||
|
playerOrdering.put(playerID, Pair.of(ordering, disabled));
|
||||||
|
makePlayerBaked(playerID, ordering, disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makePlayerBaked(UUID player, List<String> ordering, List<String> disabled) {
|
||||||
|
TreeMap<String, V> copy = new TreeMap<>(store);
|
||||||
|
for (String s : disabled) {
|
||||||
|
copy.remove(s);
|
||||||
|
}
|
||||||
|
List<V> b = new ArrayList<>();
|
||||||
|
for (String k : ordering) {
|
||||||
|
V v = copy.remove(k);
|
||||||
|
if (v != null) {
|
||||||
|
b.add(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.addAll(copy.values());
|
||||||
|
playerBaked.put(player, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return store.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return store.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(String key) {
|
||||||
|
return store.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsValue(V value) {
|
||||||
|
return store.containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V get(String key) {
|
||||||
|
return store.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<V> getPlayerOrdering(@Nullable EntityPlayerMP player) {
|
||||||
|
if (player == null) return baked;
|
||||||
|
List<V> playerPreference = playerBaked.get(player.getUniqueID());
|
||||||
|
if (playerPreference == null) return baked;
|
||||||
|
return playerPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<String> getPlayerOrderingKeys(@Nullable EntityPlayerMP player) {
|
||||||
|
if (player == null) return store.keySet().stream();
|
||||||
|
List<V> playerOrder = playerBaked.get(player.getUniqueID());
|
||||||
|
if (playerOrder == null) return store.keySet().stream();
|
||||||
|
|
||||||
|
// rebuild the order from the baked map
|
||||||
|
Map<V, String> reverseMap = new IdentityHashMap<>();
|
||||||
|
store.forEach((k, v) -> reverseMap.put(v, k));
|
||||||
|
return playerOrder.stream().map(reverseMap::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<String> getCurrentOrdering() {
|
||||||
|
return store.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return baked.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(Consumer<? super V> action) {
|
||||||
|
baked.forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<V> spliterator() {
|
||||||
|
return baked.spliterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.structure.IStructureElement;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.eventhandler.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribers are <b>required</b> to not keep reference to these events, nor to depend on values of these events after
|
||||||
|
* the method call returns.
|
||||||
|
* <p>
|
||||||
|
* All of these events will be fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}, if they are
|
||||||
|
* enabled by {@link StructureLibAPI#enableInstrument(Object)}.
|
||||||
|
* <p>
|
||||||
|
* All public stuff in this class and its enclosed classes are considered part of the public API. Backwards
|
||||||
|
* compatibility is maintained to the maximum extend possible.
|
||||||
|
*/
|
||||||
|
public abstract class StructureEvent extends Event {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In case the event is passed to another thread for processing.
|
||||||
|
*/
|
||||||
|
private final Object identifier = StructureLibAPI.instrument.get();
|
||||||
|
|
||||||
|
StructureEvent() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The instrument identifier. Subscribers should filter by these identifiers to prevent listening to a structure
|
||||||
|
* event for other people.
|
||||||
|
*/
|
||||||
|
public Object getInstrumentIdentifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired <b>just before</b> any structure element is visited.
|
||||||
|
*/
|
||||||
|
public static final class StructureElementVisitedEvent extends StructureEvent {
|
||||||
|
|
||||||
|
private final World world;
|
||||||
|
private final int x, y, z;
|
||||||
|
private final int a, b, c;
|
||||||
|
private final IStructureElement<?> element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire this event on {@link MinecraftForge#EVENT_BUS} by populating it with given arguments. Do nothing if
|
||||||
|
* instrumenting isn't enabled for current thread.
|
||||||
|
* <p>
|
||||||
|
* Any {@link Exception} thrown by event listeners will be logged and ignored. {@link Error} will be propagated
|
||||||
|
* as is.
|
||||||
|
*/
|
||||||
|
public static void fireEvent(World world, int x, int y, int z, int a, int b, int c,
|
||||||
|
IStructureElement<?> element) {
|
||||||
|
if (StructureLibAPI.isInstrumentEnabled()) {
|
||||||
|
try {
|
||||||
|
MinecraftForge.EVENT_BUS.post(new StructureElementVisitedEvent(world, x, y, z, a, b, c, element));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// logging is done by event bus, we just ignore the consequence here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StructureElementVisitedEvent(World world, int x, int y, int z, int a, int b, int c,
|
||||||
|
IStructureElement<?> element) {
|
||||||
|
this.world = world;
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
this.c = c;
|
||||||
|
this.element = element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
|
||||||
|
* <p>
|
||||||
|
* Submit a feature request if you need the baseOffset.
|
||||||
|
*/
|
||||||
|
public int getA() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* B position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
|
||||||
|
* <p>
|
||||||
|
* Submit a feature request if you need the baseOffset.
|
||||||
|
*/
|
||||||
|
public int getB() {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* C position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
|
||||||
|
* <p>
|
||||||
|
* Submit a feature request if you need the baseOffset.
|
||||||
|
*/
|
||||||
|
public int getC() {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Element being visited.
|
||||||
|
*/
|
||||||
|
public IStructureElement<?> getElement() {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "StructureElementVisitedEvent{" + "a="
|
||||||
|
+ a
|
||||||
|
+ ", b="
|
||||||
|
+ b
|
||||||
|
+ ", c="
|
||||||
|
+ c
|
||||||
|
+ ", element="
|
||||||
|
+ element
|
||||||
|
+ ", world="
|
||||||
|
+ getWorld()
|
||||||
|
+ ", x="
|
||||||
|
+ getX()
|
||||||
|
+ ", y="
|
||||||
|
+ getY()
|
||||||
|
+ ", z="
|
||||||
|
+ getZ()
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of this event.
|
||||||
|
*/
|
||||||
|
public World getWorld() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of this event. Absolute world coordinate.
|
||||||
|
*/
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of this event. Absolute world coordinate.
|
||||||
|
*/
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of this event. Absolute world coordinate.
|
||||||
|
*/
|
||||||
|
public int getZ() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,78 +1,207 @@
|
|||||||
package com.gtnewhorizon.structurelib;
|
package com.gtnewhorizon.structurelib;
|
||||||
|
|
||||||
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
import static com.gtnewhorizon.structurelib.StructureLibAPI.CHANNEL_SHOW_ERROR;
|
||||||
import com.gtnewhorizon.structurelib.block.BlockHint;
|
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
|
||||||
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.item.ItemStack;
|
||||||
|
import net.minecraft.launchwrapper.Launch;
|
||||||
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
|
import net.minecraft.nbt.NBTTagList;
|
||||||
|
import net.minecraftforge.common.util.Constants;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.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.command.CommandRegistryDebug;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemBlockHint;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
|
||||||
|
import com.gtnewhorizon.structurelib.item.ItemFrontRotationTool;
|
||||||
|
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.RegistryOrderSyncMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.util.InventoryUtility;
|
||||||
|
import com.gtnewhorizon.structurelib.util.XSTR;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
import cpw.mods.fml.common.SidedProxy;
|
||||||
|
import cpw.mods.fml.common.event.FMLInterModComms;
|
||||||
|
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||||
|
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||||
|
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
|
||||||
|
import cpw.mods.fml.common.registry.GameRegistry;
|
||||||
|
import cpw.mods.fml.relauncher.Side;
|
||||||
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class does not contain a stable API. Refrain from using this class.
|
* 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 = MOD_ID,
|
||||||
|
name = "StructureLib",
|
||||||
|
version = Tags.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(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);
|
||||||
};
|
net.registerMessage(RegistryOrderSyncMessage.Handler.class, RegistryOrderSyncMessage.class, 5, Side.SERVER);
|
||||||
|
|
||||||
@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;
|
||||||
}
|
@Mod.Instance(STRUCTURECOMPAT_MODID)
|
||||||
|
static Object COMPAT;
|
||||||
|
|
||||||
public static boolean isCurrentPlayer(EntityPlayer player) {
|
static Block blockHint;
|
||||||
return proxy.isCurrentPlayer(player);
|
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());
|
||||||
|
|
||||||
|
InventoryUtility.init();
|
||||||
|
|
||||||
|
ChannelDescription.set(CHANNEL_SHOW_ERROR, MOD_ID, "channels.structurelib.show_errors");
|
||||||
|
|
||||||
|
DevelopHelper.onPreInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void loadComplete(FMLLoadCompleteEvent e) {
|
||||||
|
proxy.loadComplete(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void serverStarting(FMLServerStartingEvent e) {
|
||||||
|
e.registerServerCommand(new CommandConfigureChannels());
|
||||||
|
e.registerServerCommand(new CommandRegistryDebug());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mod.EventHandler
|
||||||
|
public void handleIMC(FMLInterModComms.IMCEvent event) {
|
||||||
|
for (FMLInterModComms.IMCMessage message : event.getMessages()) {
|
||||||
|
switch (message.key) {
|
||||||
|
case "register_channel":
|
||||||
|
processRegisterChannel(message);
|
||||||
|
break;
|
||||||
|
case "register_channel_item":
|
||||||
|
processRegisterChannelItem(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRegisterChannel(FMLInterModComms.IMCMessage message) {
|
||||||
|
NBTTagList tags = message.getNBTValue().getTagList("Channels", Constants.NBT.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < tags.tagCount(); i++) {
|
||||||
|
NBTTagCompound tag = tags.getCompoundTagAt(i);
|
||||||
|
StructureLibAPI.registerChannelDescription(
|
||||||
|
tag.getString("Channel"),
|
||||||
|
message.getSender(),
|
||||||
|
tag.getString("Description"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRegisterChannelItem(FMLInterModComms.IMCMessage message) {
|
||||||
|
NBTTagList items = message.getNBTValue().getTagList("Items", Constants.NBT.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < items.tagCount(); i++) {
|
||||||
|
NBTTagCompound itemTag = items.getCompoundTagAt(i);
|
||||||
|
ItemStack item = ItemStack.loadItemStackFromNBT(itemTag.getCompoundTag("Item"));
|
||||||
|
NBTTagList tags = itemTag.getTagList("Channels", Constants.NBT.TAG_COMPOUND);
|
||||||
|
for (int j = 0; j < tags.tagCount(); j++) {
|
||||||
|
NBTTagCompound tag = tags.getCompoundTagAt(j);
|
||||||
|
StructureLibAPI.registerChannelItem(
|
||||||
|
tag.getString("Channel"),
|
||||||
|
message.getSender(),
|
||||||
|
tag.getInteger("Value"),
|
||||||
|
item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addClientSideChatMessages(String... messages) {
|
||||||
|
proxy.addClientSideChatMessages(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntityPlayer getCurrentPlayer() {
|
||||||
|
return proxy.getCurrentPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCurrentPlayer(EntityPlayer player) {
|
||||||
|
return proxy.isCurrentPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getOverworldTime() {
|
||||||
|
return proxy.getOverworldTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StructureLib instance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonProxy proxy() {
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,131 +1,388 @@
|
|||||||
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.gtnhlib.util.AnimatedTooltipHandler;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||||
|
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
|
||||||
|
import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
|
||||||
|
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.network.NetworkRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A stable interface into the structure lib's internals. Backwards compatibility is maintained to the maximum extend possible.
|
* A stable interface into the structure lib's internals. Backwards compatibility is maintained to the maximum extend
|
||||||
|
* possible.
|
||||||
*/
|
*/
|
||||||
public class StructureLibAPI {
|
public 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;
|
||||||
|
public static final String CHANNEL_SHOW_ERROR = "show_error";
|
||||||
|
static final ThreadLocal<Object> instrument = new ThreadLocal<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start current batch of hinting. All hints particles generated during one batch will be considered to belong to one hologram.
|
* 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(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
public static void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
|
/**
|
||||||
proxy.hintParticleTinted(w, x, y, z, icons, RGBa);
|
* Stop instrumenting.
|
||||||
}
|
*
|
||||||
|
* @throws IllegalStateException if instrumenting hasn't been enabled.
|
||||||
|
*/
|
||||||
|
public static void disableInstrument() {
|
||||||
|
if (!isInstrumentEnabled()) throw new IllegalStateException();
|
||||||
|
instrument.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
public static void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {
|
/**
|
||||||
proxy.hintParticleTinted(w, x, y, z, block, meta, RGBa);
|
* Check if instrumenting is enabled.
|
||||||
}
|
*
|
||||||
|
* @return true if enabled, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean isInstrumentEnabled() {
|
||||||
|
return instrument.get() != null;
|
||||||
|
}
|
||||||
|
|
||||||
public static void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
|
/**
|
||||||
proxy.hintParticle(w, x, y, z, icons);
|
* Start a batch of hinting. All hints particles generated during one batch will be considered to belong to one
|
||||||
}
|
* hologram.
|
||||||
|
* <p>
|
||||||
|
* You don't need to call this unless your constructable tool didn't call
|
||||||
|
* {@link com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility#handle(ItemStack, EntityPlayer, World, int, int, int, int)}
|
||||||
|
*/
|
||||||
|
public static void startHinting(World w) {
|
||||||
|
proxy.startHinting(w);
|
||||||
|
}
|
||||||
|
|
||||||
public static void hintParticle(World w, int x, int y, int z, Block block, int meta) {
|
/**
|
||||||
proxy.hintParticle(w, x, y, z, block, meta);
|
* End current batch of hinting. All hints particles generated during one batch will be considered to belong to one
|
||||||
}
|
* hologram.
|
||||||
|
* <p>
|
||||||
|
* You don't need to call this unless your constructable tool didn't call
|
||||||
|
* {@link com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility#handle(ItemStack, EntityPlayer, World, int, int, int, int)}
|
||||||
|
*/
|
||||||
|
public static void endHinting(World w) {
|
||||||
|
proxy.endHinting(w);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the ExtendedFacing of this tile entity.
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a channel description here. The description should describe how a channel will be used inside your mod.
|
||||||
|
*
|
||||||
|
* @param channel channel key
|
||||||
|
* @param modid your modid
|
||||||
|
* @param description localization key for your description
|
||||||
|
*/
|
||||||
|
public static void registerChannelDescription(final String channel, final String modid, final String description) {
|
||||||
|
ChannelDescription.set(channel, modid, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an item stack as an indicator for a (channel, value) pair.
|
||||||
|
*
|
||||||
|
* @param channel channel key
|
||||||
|
* @param modid your modid
|
||||||
|
* @param channelValue value
|
||||||
|
* @param stack item stack. having a metadata of
|
||||||
|
* {@link net.minecraftforge.oredict.OreDictionary#WILDCARD_VALUE} will match without comparing
|
||||||
|
* NBT or metadata value. having a tag with {@code tag.getBoolean("*") == true} will match
|
||||||
|
* without comparing NBT.
|
||||||
|
*/
|
||||||
|
public static void registerChannelItem(final String channel, final String modid, final int channelValue,
|
||||||
|
final ItemStack stack) {
|
||||||
|
ChannelDescription.item(channel, channelValue, stack);
|
||||||
|
AnimatedTooltipHandler.addItemTooltip(
|
||||||
|
stack,
|
||||||
|
AnimatedTooltipHandler.translatedText("structurelib.tooltip.channelvalue", channelValue, channel));
|
||||||
|
AnimatedTooltipHandler.addItemTooltip(
|
||||||
|
stack,
|
||||||
|
AnimatedTooltipHandler.translatedText("structurelib.tooltip.indicator_dnd", channelValue, channel));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment;
|
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,53 @@ 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());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Check if this object support a flip change, assuming both direction and rotation is not changed.
|
||||||
|
*/
|
||||||
|
default boolean isFlipChangeAllowed() {
|
||||||
|
ExtendedFacing facing = getExtendedFacing();
|
||||||
|
for (Flip flip : Flip.VALUES) {
|
||||||
|
if (flip == getFlip()) continue;
|
||||||
|
if (isNewExtendedFacingValid(facing.with(flip))) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this object support a rotation change, assuming both direction and flip is not changed.
|
||||||
|
*/
|
||||||
|
default boolean isRotationChangeAllowed() {
|
||||||
|
ExtendedFacing facing = getExtendedFacing();
|
||||||
|
for (Rotation rotation : Rotation.VALUES) {
|
||||||
|
if (rotation == getRotation()) continue;
|
||||||
|
if (isNewExtendedFacingValid(facing.with(rotation))) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,45 @@
|
|||||||
package com.gtnewhorizon.structurelib.alignment;
|
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 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be placed in any orientation.
|
||||||
|
*/
|
||||||
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
|
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must be standing upright; i.e., top side of the block facing up.
|
||||||
|
*/
|
||||||
|
IAlignmentLimits UPRIGHT = (direction, rotation, flip) -> direction.offsetY == 0
|
||||||
|
&& (flip.isVerticallyFliped() ? rotation.isUpsideDown() : rotation.isNotRotated());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be in any orientation except upside down; i.e., top side of the block facing down.
|
||||||
|
*/
|
||||||
|
IAlignmentLimits NOT_UPSIDE_DOWN = (direction, rotation, flip) -> direction.offsetY != 0
|
||||||
|
|| (flip.isVerticallyFliped() ? !rotation.isNotRotated() : !rotation.isUpsideDown());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Front side of the block must be facing up.
|
||||||
|
*/
|
||||||
|
IAlignmentLimits FACING_UP = (direction, rotation, flip) -> direction == ForgeDirection.UP;
|
||||||
|
|
||||||
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
|
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 +59,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 +77,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 +99,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 +139,7 @@ public interface IAlignmentLimits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefers rotation over flip, so both flip will get translated to opposite rotation and no flip
|
* Prefers rotation to flip, so both flip will get translated to opposite rotation and no flip
|
||||||
*
|
*
|
||||||
* @param flip the preferred flip to be used Horizontal or vertical
|
* @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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+85
-51
@@ -1,82 +1,116 @@
|
|||||||
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 playerMP) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+171
-98
@@ -1,21 +1,33 @@
|
|||||||
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.collectingAndThen;
|
||||||
|
import static java.util.stream.Collectors.groupingBy;
|
||||||
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import net.minecraft.util.StatCollector;
|
||||||
|
import net.minecraft.util.Vec3;
|
||||||
|
import net.minecraftforge.common.util.ForgeDirection;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.IAlignment;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
|
||||||
|
import com.gtnewhorizon.structurelib.util.Vec3Impl;
|
||||||
|
|
||||||
public enum ExtendedFacing {
|
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 +125,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 +141,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 +169,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 +316,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 +345,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,63 @@ 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) {
|
// For numbered hints, top and bottom are blank
|
||||||
int tMeta = aWorld.getBlockMetadata(xCoord, yCoord, zCoord);
|
if (aSide <= 1 && aMeta <= 11) {
|
||||||
return getIcon(aSide, tMeta);
|
return hint[13];
|
||||||
}
|
}
|
||||||
|
return hint[aMeta];
|
||||||
|
}
|
||||||
|
|
||||||
@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
|
||||||
|
public void getSubBlocks(Item item, CreativeTabs tab, List<ItemStack> list) {
|
||||||
|
for (int i = 0; i <= 15; i++) {
|
||||||
|
list.add(new ItemStack(item, 1, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
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<String> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
|
||||||
|
if (p_71516_2_.length == 1)
|
||||||
|
return getListOfStringsMatchingLastWord(p_71516_2_, "get", "set", "unset", "wipe", "getall");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processCommand(ICommandSender sender, String[] args) {
|
||||||
|
if (args.length == 0) throw new WrongUsageException(getCommandUsage(sender));
|
||||||
|
|
||||||
|
if (!(sender instanceof EntityPlayerMP player)) throw new WrongUsageException("must be a player");
|
||||||
|
|
||||||
|
ItemStack heldItem = player.getHeldItem();
|
||||||
|
if (heldItem == null || !(heldItem.getItem() instanceof ItemConstructableTrigger)) {
|
||||||
|
throw new WrongUsageException("must hold a hologram");
|
||||||
|
}
|
||||||
|
|
||||||
|
String channel;
|
||||||
|
|
||||||
|
switch (args[0]) {
|
||||||
|
case "getall":
|
||||||
|
if (!ChannelDataAccessor.hasSubChannel(heldItem)) {
|
||||||
|
sender.addChatMessage(new ChatComponentText("No subchannel"));
|
||||||
|
} else {
|
||||||
|
sender.addChatMessage(
|
||||||
|
new ChatComponentTranslation(
|
||||||
|
"item.structurelib.constructableTrigger.desc.lshift.0",
|
||||||
|
ChannelDataAccessor.countChannelData(heldItem)));
|
||||||
|
ChannelDataAccessor.iterateChannelData(heldItem).map(e -> e.getKey() + ": " + e.getValue())
|
||||||
|
.map(ChatComponentText::new).forEach(player::addChatMessage);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case "wipe":
|
||||||
|
ChannelDataAccessor.wipeChannelData(heldItem);
|
||||||
|
return;
|
||||||
|
case "get":
|
||||||
|
case "unset":
|
||||||
|
if (args.length < 2) throw new WrongUsageException(getCommandUsage(sender));
|
||||||
|
channel = args.length == 2 ? args[1] : Arrays.stream(args).skip(1).collect(Collectors.joining());
|
||||||
|
break;
|
||||||
|
case "set":
|
||||||
|
if (args.length < 3) throw new WrongUsageException(getCommandUsage(sender));
|
||||||
|
channel = args.length == 3 ? args[1]
|
||||||
|
: Arrays.stream(args).skip(1).limit(args.length - 2).collect(Collectors.joining());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new WrongUsageException(getCommandUsage(sender));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (args[0]) {
|
||||||
|
case "get":
|
||||||
|
if (ChannelDataAccessor.hasSubChannel(heldItem, channel)) sender.addChatMessage(
|
||||||
|
new ChatComponentText(
|
||||||
|
channel + " value: " + ChannelDataAccessor.getChannelData(heldItem, channel)));
|
||||||
|
else sender.addChatMessage(new ChatComponentText(channel + " value: N/A"));
|
||||||
|
break;
|
||||||
|
case "clear":
|
||||||
|
if (ChannelDataAccessor.hasSubChannel(heldItem, channel))
|
||||||
|
sender.addChatMessage(new ChatComponentText(channel + " no value"));
|
||||||
|
else {
|
||||||
|
ChannelDataAccessor.unsetChannelData(heldItem, channel);
|
||||||
|
sender.addChatMessage(new ChatComponentText(channel + " cleared"));
|
||||||
|
player.inventoryContainer.detectAndSendChanges();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "set":
|
||||||
|
int value;
|
||||||
|
try {
|
||||||
|
value = Integer.parseInt(args[args.length - 1]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new WrongUsageException("%s not a valid value", args[args.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value <= 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.command;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.command.CommandBase;
|
||||||
|
import net.minecraft.command.ICommandSender;
|
||||||
|
import net.minecraft.command.WrongUsageException;
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.util.ChatComponentText;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.SortedRegistry;
|
||||||
|
|
||||||
|
public class CommandRegistryDebug extends CommandBase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "sl_registry_debug";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandUsage(ICommandSender p_71518_1_) {
|
||||||
|
return "/sl_registry_debug <registry name>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRequiredPermissionLevel() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> addTabCompletionOptions(ICommandSender p_71516_1_, String[] p_71516_2_) {
|
||||||
|
if (p_71516_2_.length == 1) return getListOfStringsMatchingLastWord(
|
||||||
|
p_71516_2_,
|
||||||
|
SortedRegistry.getRegistryNames().toArray(new String[0]));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processCommand(ICommandSender sender, String[] args) {
|
||||||
|
if (args.length != 1) throw new WrongUsageException(getCommandUsage(sender));
|
||||||
|
|
||||||
|
if (!(sender instanceof EntityPlayerMP player)) throw new WrongUsageException("must be a player");
|
||||||
|
|
||||||
|
SortedRegistry<?> registry = SortedRegistry.getRegistry(args[0]);
|
||||||
|
if (registry == null) throw new WrongUsageException("registry not found");
|
||||||
|
|
||||||
|
player.addChatMessage(new ChatComponentText("Current order:"));
|
||||||
|
registry.getPlayerOrderingKeys(player).map(ChatComponentText::new).forEach(player::addChatMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.core;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
|
||||||
|
|
||||||
|
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||||
|
|
||||||
|
@IFMLLoadingPlugin.MCVersion("1.7.10")
|
||||||
|
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getASMTransformerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModContainerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSetupClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectData(Map<String, Object> data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAccessTransformerClass() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMixinConfig() {
|
||||||
|
return "mixins.structurelib.early.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getMixins(Set<String> loadedCoreMods) {
|
||||||
|
return Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,11 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.event;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
|
||||||
|
public interface BlockChangeListener {
|
||||||
|
|
||||||
|
void onBlockChange(World world, Chunk chunk, int x, int y, int z, Block oldBlock, Block newBlock, int oldMeta,
|
||||||
|
int newMeta);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.event;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
|
||||||
|
@SuppressWarnings("ForLoopReplaceableByForEach")
|
||||||
|
public class BlockChangeNotifier {
|
||||||
|
|
||||||
|
private static final List<BlockChangeListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
public static synchronized void addListener(BlockChangeListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onBlockChange(World world, Chunk chunk, int x, int y, int z, Block oldBlock, Block newBlock,
|
||||||
|
int oldMeta, int newMeta) {
|
||||||
|
if (oldBlock == newBlock && oldMeta == newMeta) return;
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = world.getChunkFromBlockCoords(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int numListeners = listeners.size();
|
||||||
|
for (int i = 0; i < numListeners; i++) {
|
||||||
|
final BlockChangeListener listener = listeners.get(i);
|
||||||
|
listener.onBlockChange(world, chunk, x, y, z, oldBlock, newBlock, oldMeta, newMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.gui;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.inventory.Container;
|
||||||
|
|
||||||
|
public class EmptyContainer extends Container {
|
||||||
|
|
||||||
|
private final EntityPlayer player;
|
||||||
|
|
||||||
|
public EmptyContainer(EntityPlayer player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canInteractWith(EntityPlayer player) {
|
||||||
|
return player == this.player;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,686 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.gui;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.GuiTextField;
|
||||||
|
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.inventory.Container;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.EnumChatFormatting;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.StatCollector;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.lwjgl.input.Keyboard;
|
||||||
|
import org.lwjgl.input.Mouse;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
import com.google.common.base.Splitter;
|
||||||
|
import com.gtnewhorizon.structurelib.ChannelDescription;
|
||||||
|
import com.gtnewhorizon.structurelib.StructureLib;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
|
||||||
|
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
|
||||||
|
|
||||||
|
import codechicken.nei.VisiblityData;
|
||||||
|
import codechicken.nei.api.INEIGuiHandler;
|
||||||
|
import codechicken.nei.api.TaggedInventoryArea;
|
||||||
|
import cpw.mods.fml.common.Loader;
|
||||||
|
import cpw.mods.fml.common.Optional;
|
||||||
|
import gnu.trove.map.TIntObjectMap;
|
||||||
|
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||||
|
|
||||||
|
@Optional.Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = "NotEnoughItems")
|
||||||
|
public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScreen, INEIGuiHandler {
|
||||||
|
|
||||||
|
private static final String I18N_PREFIX = "item.structurelib.constructableTrigger.gui.";
|
||||||
|
|
||||||
|
private static final int ADD_BTN = 0;
|
||||||
|
private static final int UNSET_BTN = 1;
|
||||||
|
private static final int WIPE_BTN = 2;
|
||||||
|
|
||||||
|
private static final int KEY_MAX_WIDTH = 50;
|
||||||
|
private static final int INFO_BOX_SIZE = 11;
|
||||||
|
private final ItemStack trigger;
|
||||||
|
private final GuiChannelsList list;
|
||||||
|
private final GuiAutoCompleteList autoCompleteList;
|
||||||
|
private final Map<String, List<String>> tooltipSplitCache = new HashMap<>();
|
||||||
|
private final TIntObjectMap<Map<String, String>> trimCache = new TIntObjectHashMap<>();
|
||||||
|
private GuiTextField key, value;
|
||||||
|
protected int guiTop, guiLeft;
|
||||||
|
private List<String> info;
|
||||||
|
private boolean wipePending = false;
|
||||||
|
private long wipePendingTime = 0;
|
||||||
|
private static final long WIPE_CONFIRM_TIMEOUT_MS = 3000;
|
||||||
|
|
||||||
|
public GuiScreenConfigureChannels(Container container, ItemStack trigger) {
|
||||||
|
super(container);
|
||||||
|
|
||||||
|
this.trigger = trigger;
|
||||||
|
list = new GuiChannelsList(152, 91, 12, 21, 14);
|
||||||
|
list.addSelectionListener((list, selectedIndex) -> {
|
||||||
|
Entry<String, Integer> e = list.getElementAt(selectedIndex);
|
||||||
|
key.setText(e.getKey());
|
||||||
|
value.setText(e.getValue().toString());
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
autoCompleteList = new GuiAutoCompleteList(151 + 12 - 45 - 2, 50, 45, 119 + 12, 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initGui() {
|
||||||
|
super.initGui();
|
||||||
|
// so you can keep holding backspace to delete a long sequence of chars
|
||||||
|
Keyboard.enableRepeatEvents(true);
|
||||||
|
|
||||||
|
guiLeft = (this.width - this.getXSize()) / 2;
|
||||||
|
guiTop = (this.height - this.getYSize()) / 2;
|
||||||
|
|
||||||
|
key = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 119, 151 + 12 - 45, 12) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeText(String text) {
|
||||||
|
// force lower case
|
||||||
|
super.writeText(text.toLowerCase(Locale.ROOT));
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) {
|
||||||
|
super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
|
||||||
|
boolean flag = p_146192_1_ >= this.xPosition && p_146192_1_ < this.xPosition + this.width
|
||||||
|
&& p_146192_2_ >= this.yPosition
|
||||||
|
&& p_146192_2_ < this.yPosition + this.height;
|
||||||
|
if (flag && p_146192_3_ == 1) {
|
||||||
|
key.setText("");
|
||||||
|
value.setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setText(String p_146180_1_) {
|
||||||
|
super.setText(p_146180_1_);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
value = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 139, 151 + 12 - 45, 12) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeText(String text) {
|
||||||
|
// ignore write requests containing non digit characters.
|
||||||
|
// we don't accept decimals or negative numbers, so we can safely use isDigit instead of something more
|
||||||
|
// advanced
|
||||||
|
// use codePoints() in case someone pasted an emoji by mistake
|
||||||
|
if (text != null && text.codePoints().allMatch(Character::isDigit)) {
|
||||||
|
super.writeText(text);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFocused(boolean p_146195_1_) {
|
||||||
|
if (!p_146195_1_ && isFocused() && !StringUtils.isBlank(getText())) {
|
||||||
|
int result;
|
||||||
|
try {
|
||||||
|
result = Math.max(Integer.parseInt(getText()), 1);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
result = 1;
|
||||||
|
}
|
||||||
|
setText(String.valueOf(result));
|
||||||
|
}
|
||||||
|
super.setFocused(p_146195_1_);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) {
|
||||||
|
super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
|
||||||
|
boolean flag = p_146192_1_ >= this.xPosition && p_146192_1_ < this.xPosition + this.width
|
||||||
|
&& p_146192_2_ >= this.yPosition
|
||||||
|
&& p_146192_2_ < this.yPosition + this.height;
|
||||||
|
if (flag && p_146192_3_ == 1) {
|
||||||
|
setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setText(String p_146180_1_) {
|
||||||
|
super.setText(p_146180_1_);
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
list.onGuiInit(this);
|
||||||
|
autoCompleteList.onGuiInit(this);
|
||||||
|
|
||||||
|
addButton(new GuiButton(ADD_BTN, guiLeft + 12, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "add")));
|
||||||
|
addButton(new GuiButton(UNSET_BTN, guiLeft + 65, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "unset")));
|
||||||
|
addButton(new GuiButton(WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "wipe")));
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
tooltipSplitCache.clear();
|
||||||
|
info = StreamSupport
|
||||||
|
.stream(
|
||||||
|
Splitter.on("\\n").split(StatCollector.translateToLocal(I18N_PREFIX + "info")).spliterator(),
|
||||||
|
false)
|
||||||
|
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 1 / 3).stream())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGuiLeft() {
|
||||||
|
return guiLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGuiTop() {
|
||||||
|
return guiTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getXSize() {
|
||||||
|
return 176;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getYSize() {
|
||||||
|
return 188;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addButton(GuiButton button) {
|
||||||
|
getButtonList().add(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeButton(GuiButton button) {
|
||||||
|
getButtonList().remove(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOverlayOffsetX() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doActionPerformed(GuiButton but) {
|
||||||
|
actionPerformed(but);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GuiButton> getButtonList() {
|
||||||
|
return buttonList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMouseOverValue() {
|
||||||
|
int mx = Mouse.getEventX() * this.width / this.mc.displayWidth;
|
||||||
|
int my = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1;
|
||||||
|
return mx >= value.xPosition && mx < value.xPosition + value.width
|
||||||
|
&& my >= value.yPosition
|
||||||
|
&& my < value.yPosition + value.height;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMouseInput() {
|
||||||
|
int delta = Mouse.getEventDWheel();
|
||||||
|
if (delta != 0) {
|
||||||
|
if (isMouseOverValue()) {
|
||||||
|
if (delta > 0) {
|
||||||
|
value.setText(String.valueOf(getValue() + 1));
|
||||||
|
} else {
|
||||||
|
value.setText(String.valueOf(Math.max(getValue() - 1, 1)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.handleDWheel(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.handleMouseInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void mouseClicked(int mX, int mY, int button) {
|
||||||
|
super.mouseClicked(mX, mY, button);
|
||||||
|
key.mouseClicked(mX, mY, button);
|
||||||
|
value.mouseClicked(mX, mY, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void keyTyped(char aChar, int aKey) {
|
||||||
|
GuiScrollableList<?> l = key.isFocused() ? autoCompleteList : list;
|
||||||
|
switch (aKey) {
|
||||||
|
case Keyboard.KEY_TAB:
|
||||||
|
if (key.isFocused()) {
|
||||||
|
if (autoCompleteList.getSelectedElement() != null) {
|
||||||
|
key.setText(autoCompleteList.getSelectedElement());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key.setFocused(false);
|
||||||
|
value.setFocused(true);
|
||||||
|
} else {
|
||||||
|
key.setFocused(true);
|
||||||
|
value.setFocused(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_RETURN:
|
||||||
|
case Keyboard.KEY_NUMPADENTER:
|
||||||
|
if (key.isFocused() && autoCompleteList.getSelectedElement() != null) {
|
||||||
|
key.setText(autoCompleteList.getSelectedElement());
|
||||||
|
key.setFocused(false);
|
||||||
|
value.setFocused(true);
|
||||||
|
value.setText("1");
|
||||||
|
value.setSelectionPos(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GuiButton add = getButtonList().get(ADD_BTN);
|
||||||
|
if (add.enabled) doActionPerformed(add);
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_UP:
|
||||||
|
if (l.selectedIndex > 0) l.setSelection(l.selectedIndex - 1);
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_DOWN:
|
||||||
|
if (l.selectedIndex < l.getNumElements() - 1) l.setSelection(l.selectedIndex + 1);
|
||||||
|
return;
|
||||||
|
case Keyboard.KEY_ESCAPE:
|
||||||
|
if (key.isFocused()) {
|
||||||
|
if (autoCompleteList.getSelectedElement() != null) {
|
||||||
|
autoCompleteList.reset();
|
||||||
|
} else {
|
||||||
|
key.setFocused(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (key.textboxKeyTyped(aChar, aKey) || value.textboxKeyTyped(aChar, aKey)) {
|
||||||
|
updateButtons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.keyTyped(aChar, aKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtons() {
|
||||||
|
// this will be called from setText of key and value. NEVER UPDATE THE VALUE OF THESE HERE OR GET A
|
||||||
|
// STACKOVERFLOW!
|
||||||
|
String keyText = key.getText();
|
||||||
|
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
|
||||||
|
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
|
||||||
|
: I18n.format(I18N_PREFIX + "add");
|
||||||
|
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(key.getText())
|
||||||
|
&& !StringUtils.isBlank(value.getText())
|
||||||
|
&& Integer.parseInt(value.getText()) > 0;
|
||||||
|
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
|
||||||
|
|
||||||
|
autoCompleteList.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getValue() {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value.getText());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void actionPerformed(GuiButton btn) {
|
||||||
|
if (btn == null) return;
|
||||||
|
switch (btn.id) {
|
||||||
|
case ADD_BTN:
|
||||||
|
int value = getValue();
|
||||||
|
if (value <= 0) return;
|
||||||
|
ChannelDataAccessor.setChannelData(trigger, key.getText(), value);
|
||||||
|
break;
|
||||||
|
case UNSET_BTN:
|
||||||
|
ChannelDataAccessor.unsetChannelData(trigger, key.getText());
|
||||||
|
break;
|
||||||
|
case WIPE_BTN:
|
||||||
|
if (!wipePending) {
|
||||||
|
wipePending = true;
|
||||||
|
wipePendingTime = System.currentTimeMillis();
|
||||||
|
btn.displayString = I18n.format(I18N_PREFIX + "wipe.confirm");
|
||||||
|
} else {
|
||||||
|
wipePending = false;
|
||||||
|
btn.displayString = I18n.format(I18N_PREFIX + "wipe");
|
||||||
|
ChannelDataAccessor.wipeChannelData(trigger);
|
||||||
|
key.setText("");
|
||||||
|
this.value.setText("");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
|
||||||
|
super.actionPerformed(btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuiClosed() {
|
||||||
|
super.onGuiClosed();
|
||||||
|
Keyboard.enableRepeatEvents(false);
|
||||||
|
StructureLib.instance().proxy().uploadChannels(trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateScreen() {
|
||||||
|
super.updateScreen();
|
||||||
|
key.updateCursorCounter();
|
||||||
|
value.updateCursorCounter();
|
||||||
|
if (wipePending && System.currentTimeMillis() - wipePendingTime > WIPE_CONFIRM_TIMEOUT_MS) {
|
||||||
|
wipePending = false;
|
||||||
|
getButtonList().get(WIPE_BTN).displayString = I18n.format(I18N_PREFIX + "wipe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawGuiContainerBackgroundLayer(float partialTick, int mX, int mY) {
|
||||||
|
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/channels.png"));
|
||||||
|
int topLeftX = (this.width - this.getXSize()) / 2;
|
||||||
|
int topLeftY = (this.height - this.getYSize()) / 2;
|
||||||
|
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
|
||||||
|
list.drawScreen(mX, mY, partialTick);
|
||||||
|
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
|
||||||
|
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
|
||||||
|
key.drawTextBox();
|
||||||
|
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
|
||||||
|
value.drawTextBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) {
|
||||||
|
if (key.isFocused() && autoCompleteList.getNumElements() > 0) {
|
||||||
|
autoCompleteList.drawScreen(mouseX, mouseY, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
|
||||||
|
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||||
|
String highlight = null;
|
||||||
|
if (key.isFocused() && autoCompleteList.getNumElements() > 0) {
|
||||||
|
highlight = autoCompleteList.getSelectedElement();
|
||||||
|
}
|
||||||
|
if (highlight == null && ChannelDescription.has(key.getText())) {
|
||||||
|
highlight = key.getText();
|
||||||
|
}
|
||||||
|
if (highlight != null) {
|
||||||
|
drawChannelDescriptionTooltip(highlight);
|
||||||
|
}
|
||||||
|
if (mouseX > xSize + guiLeft - INFO_BOX_SIZE - 12 && mouseX < xSize + guiLeft - 12
|
||||||
|
&& mouseY > guiTop + 7
|
||||||
|
&& mouseY < guiTop + INFO_BOX_SIZE + 7) {
|
||||||
|
drawHoveringText(info, mouseX, mouseY, fontRendererObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawChannelDescriptionTooltip(String candidate) {
|
||||||
|
List<String> tooltip = tooltipSplitCache.computeIfAbsent(candidate, this::getChannelDescriptionTooltip);
|
||||||
|
drawHoveringText(tooltip, guiLeft + 151 + 12, guiTop + 122, fontRendererObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getChannelDescriptionTooltip(String candidate) {
|
||||||
|
List<String> tooltip = new ArrayList<>();
|
||||||
|
int maxLine = width - (guiLeft + 151 + 12) - 12;
|
||||||
|
for (Entry<String, String> e : ChannelDescription.get(candidate).getDescriptions().entrySet()) {
|
||||||
|
tooltip.addAll(
|
||||||
|
fontRendererObj.listFormattedStringToWidth(
|
||||||
|
StatCollector.translateToLocalFormatted(
|
||||||
|
I18N_PREFIX + "channels.from",
|
||||||
|
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
|
||||||
|
maxLine));
|
||||||
|
tooltip.addAll(
|
||||||
|
fontRendererObj.listFormattedStringToWidth(StatCollector.translateToLocal(e.getValue()), maxLine));
|
||||||
|
}
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String trim(String str, int keyMaxWidth) {
|
||||||
|
Map<String, String> cache = trimCache.get(keyMaxWidth);
|
||||||
|
if (cache == null) {
|
||||||
|
trimCache.put(keyMaxWidth, cache = new HashMap<>());
|
||||||
|
}
|
||||||
|
return cache.computeIfAbsent(str, e -> {
|
||||||
|
String s = fontRendererObj.trimStringToWidth(e, keyMaxWidth);
|
||||||
|
if (s.length() != e.length()) {
|
||||||
|
StringBuilder buffer = new StringBuilder(s).deleteCharAt(s.length() - 1).append("...");
|
||||||
|
while ((s = fontRendererObj.trimStringToWidth(buffer.toString(), keyMaxWidth)).length()
|
||||||
|
!= buffer.length())
|
||||||
|
// drop last original char
|
||||||
|
buffer.deleteCharAt(s.length() - 4);
|
||||||
|
do {
|
||||||
|
buffer.append('.');
|
||||||
|
} while (fontRendererObj.getStringWidth(buffer.toString()) <= keyMaxWidth);
|
||||||
|
s = buffer.deleteCharAt(s.length() - 1).insert(s.length() - 3, EnumChatFormatting.GRAY).toString();
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Optional.Method(modid = "NotEnoughItems")
|
||||||
|
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
|
||||||
|
if (button != 0 || mousex <= guiLeft
|
||||||
|
|| mousey <= guiTop
|
||||||
|
|| mousex >= guiLeft + getXSize()
|
||||||
|
|| mousey >= guiTop + getYSize()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Collection<Entry<String, Integer>> channels = ChannelDescription.iterate(draggedStack);
|
||||||
|
switch (channels.size()) {
|
||||||
|
case 0:
|
||||||
|
return false;
|
||||||
|
case 1:
|
||||||
|
default: // TODO implement a GUI to select which pair to use, instead of blindly using the first
|
||||||
|
Entry<String, Integer> e = channels.iterator().next();
|
||||||
|
ChannelDataAccessor.setChannelData(trigger, e.getKey(), e.getValue());
|
||||||
|
key.setText(e.getKey());
|
||||||
|
value.setText(e.getValue().toString());
|
||||||
|
draggedStack.stackSize = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GuiAutoCompleteList extends GuiScrollableList<String> {
|
||||||
|
|
||||||
|
private List<Entry<String, String>> cache; // <key, display string>
|
||||||
|
|
||||||
|
public GuiAutoCompleteList(int width, int height, int originX, int originY, int slotHeight) {
|
||||||
|
super(width, height, originX, originY, slotHeight);
|
||||||
|
margin = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Entry<String, String>> getOptions() {
|
||||||
|
if (cache == null) {
|
||||||
|
cache = search();
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Entry<String, String>> search() {
|
||||||
|
List<Entry<String, String>> cache = new ArrayList<>();
|
||||||
|
Set<String> added = new HashSet<>();
|
||||||
|
String term = key.getText();
|
||||||
|
String[] subjects = ChannelDescription.getAll().keySet().toArray(new String[0]);
|
||||||
|
Arrays.sort(subjects);
|
||||||
|
if (StringUtils.isBlank(term)) {
|
||||||
|
// nothing. just gives out everything
|
||||||
|
for (String subject : subjects) {
|
||||||
|
cache.add(new AbstractMap.SimpleImmutableEntry<>(subject, EnumChatFormatting.GRAY + subject));
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
// prefix match
|
||||||
|
for (String s : subjects) {
|
||||||
|
if (s.startsWith(term)) {
|
||||||
|
cache.add(
|
||||||
|
new AbstractMap.SimpleImmutableEntry<>(
|
||||||
|
s,
|
||||||
|
term + EnumChatFormatting.GRAY + s.substring(term.length())));
|
||||||
|
added.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// subsequence match
|
||||||
|
for (String subject : subjects) {
|
||||||
|
if (!added.contains(subject) && subject.length() > term.length()) {
|
||||||
|
int i = 0, j = 0;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean mode = true;
|
||||||
|
sb.append(EnumChatFormatting.WHITE);
|
||||||
|
while (i < term.length() && j < subject.length()) {
|
||||||
|
if (term.codePointAt(i) == subject.codePointAt(j)) {
|
||||||
|
if (!mode) {
|
||||||
|
sb.append(EnumChatFormatting.WHITE);
|
||||||
|
mode = true;
|
||||||
|
}
|
||||||
|
sb.appendCodePoint(term.codePointAt(i));
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
if (mode) {
|
||||||
|
sb.append(EnumChatFormatting.GRAY);
|
||||||
|
mode = false;
|
||||||
|
}
|
||||||
|
sb.appendCodePoint(subject.codePointAt(j));
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == term.length()) {
|
||||||
|
if (j < subject.length()) {
|
||||||
|
sb.append(EnumChatFormatting.GRAY);
|
||||||
|
sb.append(subject, j, subject.length());
|
||||||
|
}
|
||||||
|
cache.add(new AbstractMap.SimpleImmutableEntry<>(subject, sb.toString()));
|
||||||
|
added.add(subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
cache = search();
|
||||||
|
selectedIndex = cache.isEmpty() ? -1 : 0;
|
||||||
|
resetScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getElementAt(int index) {
|
||||||
|
return index >= 0 && index < cache.size() ? getOptions().get(index).getKey() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumElements() {
|
||||||
|
return getOptions().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||||
|
boolean isHovering) {
|
||||||
|
int color = elementIndex == selectedIndex ? 0xffffff : 0xcccccc;
|
||||||
|
fontRendererObj.drawString(getOptions().get(elementIndex).getValue(), x + margin, y + margin / 2, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GuiChannelsList extends GuiScrollableList<Entry<String, Integer>> {
|
||||||
|
|
||||||
|
private List<Entry<String, Integer>> cache;
|
||||||
|
|
||||||
|
public GuiChannelsList(int width, int height, int originX, int originY, int slotHeight) {
|
||||||
|
super(width, height, originX, originY, slotHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<String, Integer> getElementAt(int index) {
|
||||||
|
return ChannelDataAccessor.iterateChannelData(trigger).sorted().skip(index).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNumElements() {
|
||||||
|
return ChannelDataAccessor.countChannelData(trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
|
||||||
|
if (mXRelative >= margin + 1 && mXRelative <= margin + 5
|
||||||
|
&& mYRelative >= margin / 2 + 1
|
||||||
|
&& mYRelative <= margin / 2 + 5) {
|
||||||
|
Entry<String, Integer> e = getElementAt(elementIndex);
|
||||||
|
if (e != null) {
|
||||||
|
ChannelDataAccessor.unsetChannelData(trigger, e.getKey());
|
||||||
|
updateButtons();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.elementClicked(elementIndex, doubleClick, mXRelative, mYRelative);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void prepareDrawElements() {
|
||||||
|
cache = ChannelDataAccessor.iterateChannelData(trigger).sorted().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||||
|
boolean isHovering) {
|
||||||
|
if (elementIndex < 0 || elementIndex >= cache.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entry<String, Integer> e = cache.get(elementIndex);
|
||||||
|
if (e == null) return;
|
||||||
|
if (elementIndex > 0) {
|
||||||
|
// args x1, x2, y1, color
|
||||||
|
drawHorizontalLine(minX + 1, maxX - 1, y - 2, 0xffaaaaaa);
|
||||||
|
}
|
||||||
|
int keyMaxWidth = KEY_MAX_WIDTH * 2;
|
||||||
|
if (isHovering) {
|
||||||
|
mc.renderEngine.bindTexture(new ResourceLocation("structurelib", "textures/gui/channels.png"));
|
||||||
|
GL11.glColor4f(1, 1, 1, 1);
|
||||||
|
drawTexturedModalRect(x + margin + 1, y + margin / 2 + 1, 251, 251, 5, 5);
|
||||||
|
}
|
||||||
|
fontRendererObj.drawString(trim(e.getKey(), keyMaxWidth - 9), x + margin + 8, y + margin / 2, 0xffffff);
|
||||||
|
int valueX = x + margin + keyMaxWidth + margin;
|
||||||
|
fontRendererObj.drawString(trim(e.getValue().toString(), maxX - valueX), valueX, y + margin / 2, 0xffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawScreen(int mX, int mY, float partialTick) {
|
||||||
|
super.drawScreen(mX, mY, partialTick);
|
||||||
|
// args x1, y1, y2, color
|
||||||
|
drawVerticalLine(minX + margin + margin / 2 + KEY_MAX_WIDTH * 2, minY + 1, maxY - 1, 0xffaaaaaa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,468 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.gui;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiButton;
|
||||||
|
import net.minecraft.client.gui.ScaledResolution;
|
||||||
|
import net.minecraft.client.renderer.Tessellator;
|
||||||
|
|
||||||
|
import org.lwjgl.input.Mouse;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shamelessly stolen from EnderCore, with some amount of edit to fit in one file. Original code from CrazyPants.
|
||||||
|
* <p>
|
||||||
|
* As the code has been released into public domain, it is now LGPL as part of this library as a whole. You can go to
|
||||||
|
* EnderCore's repository to obtain a public domain copy though
|
||||||
|
* <p>
|
||||||
|
* Major edits
|
||||||
|
* <ul>
|
||||||
|
* <li>Separate dwheel handling to handleDWheel. The next event calls often cause the mouse click event to not fire on
|
||||||
|
* other widgets</li>
|
||||||
|
* <li>scrollbar are now rendered within width</li>
|
||||||
|
* <li>added prepareDrawElements</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author CrazyPants
|
||||||
|
* @author glee8e
|
||||||
|
*/
|
||||||
|
public abstract class GuiScrollableList<T> {
|
||||||
|
|
||||||
|
protected final int slotHeight;
|
||||||
|
private final Minecraft mc = Minecraft.getMinecraft();
|
||||||
|
protected final int originX;
|
||||||
|
protected final int originY;
|
||||||
|
protected final int width;
|
||||||
|
protected final int height;
|
||||||
|
protected int minY;
|
||||||
|
protected int maxY;
|
||||||
|
protected int minX;
|
||||||
|
protected int maxX;
|
||||||
|
protected int mouseX;
|
||||||
|
protected int mouseY;
|
||||||
|
protected int selectedIndex = -1;
|
||||||
|
protected int margin = 4;
|
||||||
|
protected final List<ListSelectionListener<T>> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
private int scrollUpButtonID;
|
||||||
|
private int scrollDownButtonID;
|
||||||
|
private float initialClickY = -2.0F;
|
||||||
|
private float scrollMultiplier;
|
||||||
|
private float amountScrolled;
|
||||||
|
private long lastClickedTime;
|
||||||
|
|
||||||
|
private boolean showSelectionBox = true;
|
||||||
|
|
||||||
|
public GuiScrollableList(int width, int height, int originX, int originY, int slotHeight) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.originX = originX;
|
||||||
|
this.originY = originY;
|
||||||
|
this.slotHeight = slotHeight;
|
||||||
|
|
||||||
|
minY = originY;
|
||||||
|
maxY = minY + height;
|
||||||
|
minX = originX;
|
||||||
|
maxX = minX + width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGuiInit(IGuiScreen gui) {
|
||||||
|
minY = originY + gui.getGuiTop();
|
||||||
|
maxY = minY + height;
|
||||||
|
minX = originX + gui.getGuiLeft();
|
||||||
|
maxX = minX + width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSelectionListener(ListSelectionListener<T> listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeSelectionListener(ListSelectionListener<T> listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getSelectedElement() {
|
||||||
|
return getElementAt(selectedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelection(T selection) {
|
||||||
|
setSelection(getIndexOf(selection));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelection(int index) {
|
||||||
|
if (index == selectedIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedIndex = index;
|
||||||
|
for (ListSelectionListener<T> listener : listeners) {
|
||||||
|
listener.selectionChanged(this, selectedIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndexOf(T element) {
|
||||||
|
if (element == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < getNumElements(); i++) {
|
||||||
|
if (element.equals(getElementAt(i))) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract T getElementAt(int index);
|
||||||
|
|
||||||
|
public abstract int getNumElements();
|
||||||
|
|
||||||
|
protected abstract void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
|
||||||
|
boolean isHovering);
|
||||||
|
|
||||||
|
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowSelectionBox(boolean val) {
|
||||||
|
showSelectionBox = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getContentHeight() {
|
||||||
|
return getNumElements() * slotHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScrollButtonIds(int scrollUpButtonID, int scrollDownButtonID) {
|
||||||
|
this.scrollUpButtonID = scrollUpButtonID;
|
||||||
|
this.scrollDownButtonID = scrollDownButtonID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clampScrollToBounds() {
|
||||||
|
int i = getContentOverhang();
|
||||||
|
if (i < 0) {
|
||||||
|
i *= -1;
|
||||||
|
}
|
||||||
|
if (amountScrolled < 0.0F) {
|
||||||
|
amountScrolled = 0.0F;
|
||||||
|
}
|
||||||
|
if (amountScrolled > i) {
|
||||||
|
amountScrolled = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getContentOverhang() {
|
||||||
|
return getContentHeight() - (height - margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void actionPerformed(GuiButton b) {
|
||||||
|
if (b.enabled) {
|
||||||
|
if (b.id == scrollUpButtonID) {
|
||||||
|
amountScrolled -= slotHeight * 2f / 3;
|
||||||
|
initialClickY = -2.0F;
|
||||||
|
clampScrollToBounds();
|
||||||
|
} else if (b.id == scrollDownButtonID) {
|
||||||
|
amountScrolled += slotHeight * 2f / 3;
|
||||||
|
initialClickY = -2.0F;
|
||||||
|
clampScrollToBounds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resetScroll() {
|
||||||
|
amountScrolled = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draws the slot to the screen, pass in mouse's current x and y and partial ticks
|
||||||
|
*/
|
||||||
|
public void drawScreen(int mX, int mY, float partialTick) {
|
||||||
|
GL11.glPushMatrix();
|
||||||
|
GL11.glLoadIdentity();
|
||||||
|
GL11.glTranslatef(0.0F, 0.0F, -2000.0F);
|
||||||
|
this.mouseX = mX;
|
||||||
|
this.mouseY = mY;
|
||||||
|
|
||||||
|
processMouseEvents();
|
||||||
|
|
||||||
|
if (getContentOverhang() > 0) maxX = minX + width - 6;
|
||||||
|
else maxX = minX + width;
|
||||||
|
|
||||||
|
clampScrollToBounds();
|
||||||
|
|
||||||
|
GL11.glDisable(GL11.GL_LIGHTING);
|
||||||
|
GL11.glDisable(GL11.GL_FOG);
|
||||||
|
|
||||||
|
ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight);
|
||||||
|
int sx = minX * sr.getScaleFactor();
|
||||||
|
int sw = width * sr.getScaleFactor();
|
||||||
|
int sy = mc.displayHeight - (maxY * sr.getScaleFactor());
|
||||||
|
int sh = height * sr.getScaleFactor();
|
||||||
|
GL11.glEnable(GL11.GL_SCISSOR_TEST);
|
||||||
|
GL11.glScissor(sx, sy, sw, sh);
|
||||||
|
|
||||||
|
Tessellator tessellator = Tessellator.instance;
|
||||||
|
drawContainerBackground(tessellator);
|
||||||
|
|
||||||
|
int amountScrolled = (int) this.amountScrolled;
|
||||||
|
int contentYOffset = this.minY + margin - amountScrolled;
|
||||||
|
|
||||||
|
boolean hoveringPrecondition = mX >= minX && mX <= maxX && mY >= minY && mY <= maxY;
|
||||||
|
int slotHeight = this.slotHeight - margin;
|
||||||
|
|
||||||
|
if (selectedIndex != -1) {
|
||||||
|
int elementY = contentYOffset + selectedIndex * this.slotHeight;
|
||||||
|
if (elementY + slotHeight > maxY) {
|
||||||
|
this.amountScrolled = (selectedIndex + 1) * this.slotHeight - this.height + margin;
|
||||||
|
amountScrolled = (int) this.amountScrolled;
|
||||||
|
contentYOffset = this.minY + margin - amountScrolled;
|
||||||
|
} else if (elementY < minY) {
|
||||||
|
this.amountScrolled = selectedIndex * this.slotHeight;
|
||||||
|
amountScrolled = (int) this.amountScrolled;
|
||||||
|
contentYOffset = this.minY + margin - amountScrolled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareDrawElements();
|
||||||
|
|
||||||
|
for (int i = 0; i < getNumElements(); ++i) {
|
||||||
|
|
||||||
|
int elementY = contentYOffset + i * this.slotHeight;
|
||||||
|
|
||||||
|
if (elementY <= maxY && elementY + slotHeight >= minY) {
|
||||||
|
|
||||||
|
if (showSelectionBox && i == selectedIndex) {
|
||||||
|
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setColorOpaque_I(8421504);
|
||||||
|
tessellator.addVertexWithUV(minX, elementY + slotHeight + 2, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(maxX, elementY + slotHeight + 2, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(maxX, elementY - 2, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(minX, elementY - 2, 0.0D, 0.0D, 0.0D);
|
||||||
|
tessellator.setColorOpaque_I(0);
|
||||||
|
tessellator.addVertexWithUV(minX + 1, elementY + slotHeight + 1, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(maxX - 1, elementY + slotHeight + 1, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(maxX - 1, elementY - 1, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(minX + 1, elementY - 1, 0.0D, 0.0D, 0.0D);
|
||||||
|
tessellator.draw();
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isHovering = hoveringPrecondition && elementY - 2 <= mY && mY < elementY + slotHeight + 2;
|
||||||
|
drawElement(i, minX, elementY, slotHeight, tessellator, isHovering);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL11.glDisable(GL11.GL_SCISSOR_TEST);
|
||||||
|
|
||||||
|
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||||
|
GL11.glEnable(GL11.GL_BLEND);
|
||||||
|
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||||
|
GL11.glShadeModel(GL11.GL_SMOOTH);
|
||||||
|
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setColorRGBA_I(0, 0);
|
||||||
|
tessellator.addVertexWithUV(this.minX, this.minY + margin, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(this.maxX, this.minY + margin, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.setColorRGBA_I(0, 255);
|
||||||
|
tessellator.addVertexWithUV(this.maxX, this.minY, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(this.minX, this.minY, 0.0D, 0.0D, 0.0D);
|
||||||
|
tessellator.draw();
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
tessellator.setColorRGBA_I(0, 255);
|
||||||
|
tessellator.addVertexWithUV(this.minX, this.maxY, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(this.maxX, this.maxY, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.setColorRGBA_I(0, 0);
|
||||||
|
tessellator.addVertexWithUV(this.maxX, this.maxY - margin, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(this.minX, this.maxY - margin, 0.0D, 0.0D, 0.0D);
|
||||||
|
tessellator.draw();
|
||||||
|
|
||||||
|
renderScrollBar(tessellator);
|
||||||
|
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
GL11.glShadeModel(GL11.GL_FLAT);
|
||||||
|
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||||
|
GL11.glDisable(GL11.GL_BLEND);
|
||||||
|
GL11.glPopMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void prepareDrawElements() {}
|
||||||
|
|
||||||
|
protected void renderScrollBar(Tessellator tessellator) {
|
||||||
|
|
||||||
|
int contentHeightOverBounds = getContentOverhang();
|
||||||
|
if (contentHeightOverBounds > 0) {
|
||||||
|
|
||||||
|
int clear = (maxY - minY) * (maxY - minY) / getContentHeight();
|
||||||
|
|
||||||
|
if (clear < 32) {
|
||||||
|
clear = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear > maxY - minY - 8) {
|
||||||
|
clear = maxY - minY - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int y = (int) this.amountScrolled * (maxY - minY - clear) / contentHeightOverBounds + minY;
|
||||||
|
if (y < minY) {
|
||||||
|
y = minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||||
|
int scrollBarMinX = getScrollBarX();
|
||||||
|
int scrollBarMaxX = scrollBarMinX + 6;
|
||||||
|
tessellator.startDrawingQuads();
|
||||||
|
|
||||||
|
tessellator.setColorRGBA_I(0, 255);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMinX, maxY, 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMaxX, maxY, 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMaxX, minY, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMinX, minY, 0.0D, 0.0D, 0.0D);
|
||||||
|
|
||||||
|
tessellator.setColorRGBA_F(0.3f, 0.3f, 0.3f, 1);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMinX, (y + clear), 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMaxX, (y + clear), 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMaxX, y, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMinX, y, 0.0D, 0.0D, 0.0D);
|
||||||
|
|
||||||
|
tessellator.setColorRGBA_F(0.7f, 0.7f, 0.7f, 1);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMinX, (y + clear - 1), 0.0D, 0.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV((scrollBarMaxX - 1), (y + clear - 1), 0.0D, 1.0D, 1.0D);
|
||||||
|
tessellator.addVertexWithUV((scrollBarMaxX - 1), y, 0.0D, 1.0D, 0.0D);
|
||||||
|
tessellator.addVertexWithUV(scrollBarMinX, y, 0.0D, 0.0D, 0.0D);
|
||||||
|
|
||||||
|
tessellator.draw();
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processMouseEvents() {
|
||||||
|
if (Mouse.isButtonDown(0)) {
|
||||||
|
processMouseBown();
|
||||||
|
} else {
|
||||||
|
initialClickY = -1.0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleDWheel(int mouseWheelDelta) {
|
||||||
|
if (!Mouse.isButtonDown(0)) {
|
||||||
|
if (mouseWheelDelta > 0) {
|
||||||
|
amountScrolled -= slotHeight / 2f;
|
||||||
|
} else /* if (mouseWheelDelta < 0) */ {
|
||||||
|
amountScrolled += slotHeight / 2f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processMouseBown() {
|
||||||
|
int contentHeightOverBounds;
|
||||||
|
if (initialClickY == -1.0F) {
|
||||||
|
|
||||||
|
if (mouseY >= minY && mouseY <= maxY && mouseX >= minX && mouseX <= maxX + 6) {
|
||||||
|
|
||||||
|
boolean clickInBounds = true;
|
||||||
|
|
||||||
|
int y = mouseY - minY + (int) amountScrolled - margin;
|
||||||
|
int mouseOverElement = y / slotHeight;
|
||||||
|
|
||||||
|
if (mouseX >= minX && mouseX <= maxX
|
||||||
|
&& mouseOverElement >= 0
|
||||||
|
&& y >= 0
|
||||||
|
&& mouseOverElement < getNumElements()) {
|
||||||
|
boolean doubleClick = mouseOverElement == selectedIndex
|
||||||
|
&& Minecraft.getSystemTime() - lastClickedTime < 250L;
|
||||||
|
if (elementClicked(
|
||||||
|
mouseOverElement,
|
||||||
|
doubleClick,
|
||||||
|
mouseX - minX,
|
||||||
|
y - slotHeight * mouseOverElement)) {
|
||||||
|
setSelection(mouseOverElement);
|
||||||
|
lastClickedTime = Minecraft.getSystemTime();
|
||||||
|
}
|
||||||
|
} else if (mouseX >= minX && mouseX <= maxX && y < 0) {
|
||||||
|
clickInBounds = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scrollBarMinX = getScrollBarX();
|
||||||
|
int scrollBarMaxX = scrollBarMinX + 6;
|
||||||
|
if (mouseX >= scrollBarMinX && mouseX <= scrollBarMaxX) {
|
||||||
|
|
||||||
|
scrollMultiplier = -1.0F;
|
||||||
|
contentHeightOverBounds = getContentOverhang();
|
||||||
|
|
||||||
|
if (contentHeightOverBounds < 1) {
|
||||||
|
contentHeightOverBounds = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int empty = (int) ((float) ((maxY - minY) * (maxY - minY)) / (float) getContentHeight());
|
||||||
|
if (empty < 32) {
|
||||||
|
empty = 32;
|
||||||
|
}
|
||||||
|
if (empty > maxY - minY - 8) {
|
||||||
|
empty = maxY - minY - 8;
|
||||||
|
}
|
||||||
|
scrollMultiplier /= (float) (maxY - minY - empty) / (float) contentHeightOverBounds;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
scrollMultiplier = 1.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clickInBounds) {
|
||||||
|
initialClickY = mouseY;
|
||||||
|
} else {
|
||||||
|
initialClickY = -2.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
initialClickY = -2.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (initialClickY >= 0.0F) {
|
||||||
|
// Scrolling
|
||||||
|
amountScrolled -= (mouseY - initialClickY) * scrollMultiplier;
|
||||||
|
initialClickY = mouseY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getScrollBarX() {
|
||||||
|
return maxX;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void drawContainerBackground(Tessellator tess) {
|
||||||
|
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||||
|
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||||
|
tess.startDrawingQuads();
|
||||||
|
tess.setColorOpaque_I(0x202020);
|
||||||
|
tess.addVertex(minX, maxY, 0.0D);
|
||||||
|
tess.addVertex(maxX, maxY, 0.0D);
|
||||||
|
tess.addVertex(maxX, minY, 0.0D);
|
||||||
|
tess.addVertex(minX, minY, 0.0D);
|
||||||
|
tess.draw();
|
||||||
|
GL11.glEnable(GL11.GL_TEXTURE_2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IGuiScreen {
|
||||||
|
|
||||||
|
int getGuiLeft();
|
||||||
|
|
||||||
|
int getGuiTop();
|
||||||
|
|
||||||
|
int getXSize();
|
||||||
|
|
||||||
|
int getYSize();
|
||||||
|
|
||||||
|
void addButton(GuiButton button);
|
||||||
|
|
||||||
|
void removeButton(GuiButton button);
|
||||||
|
|
||||||
|
int getOverlayOffsetX();
|
||||||
|
|
||||||
|
void doActionPerformed(GuiButton but);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ListSelectionListener<T> {
|
||||||
|
|
||||||
|
void selectionChanged(GuiScrollableList<T> list, int selectedIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
package com.gtnewhorizon.structurelib.item;
|
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;
|
public void addInformation(ItemStack stack, EntityPlayer player, List<String> tooltip, boolean adv) {
|
||||||
}
|
tooltip.add(translateToLocal("structurelib.blockhint.desc.0")); // Helps while building
|
||||||
|
switch (stack.getItemDamage()) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
case 8:
|
||||||
|
case 9:
|
||||||
|
case 10:
|
||||||
|
case 11:
|
||||||
|
// Placeholder for a certain group.
|
||||||
|
tooltip.add(
|
||||||
|
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||||
|
+ translateToLocal("structurelib.blockhint.desc.1"));
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
// General placeholder.
|
||||||
|
tooltip.add(
|
||||||
|
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||||
|
+ translateToLocal("structurelib.blockhint.desc.2"));
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
// Make sure it contains Air material.
|
||||||
|
tooltip.add(
|
||||||
|
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||||
|
+ translateToLocal("structurelib.blockhint.desc.3"));
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
// Make sure it does not contain Air material.
|
||||||
|
tooltip.add(
|
||||||
|
EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
|
||||||
|
+ translateToLocal("structurelib.blockhint.desc.4"));
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
// ERROR, what did u expect?
|
||||||
|
tooltip.add(EnumChatFormatting.BLUE + translateToLocal("structurelib.blockhint.desc.5"));
|
||||||
|
break;
|
||||||
|
default: // WTF?
|
||||||
|
tooltip.add("Damn son where did you get that!?");
|
||||||
|
tooltip.add(EnumChatFormatting.BLUE + "From outer space... I guess...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getUnlocalizedName(ItemStack aStack) {
|
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,48 @@ 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 (getMovingObjectPositionFromPlayer(world, player, true) == null) {
|
||||||
|
// channel gui is opened from server and config gui from client
|
||||||
|
if (!world.isRemote && !player.isSneaking()) {
|
||||||
|
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
|
||||||
|
} else if (world.isRemote && player.isSneaking()) {
|
||||||
|
StructureLib.instance().proxy().displayConfigGUI("registries");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onItemRightClick(stack, world, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
|
||||||
|
float hitX, float hitY, float hitZ) {
|
||||||
return ConstructableUtility.handle(stack, player, world, x, y, z, side);
|
return ConstructableUtility.handle(stack, player, world, x, y, z, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) {
|
public void addInformation(ItemStack stack, EntityPlayer player, List<String> tooltip, boolean adv) {
|
||||||
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,
|
tooltip.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(stack)));
|
||||||
|
ChannelDataAccessor.iterateChannelData(stack).map(e -> e.getKey() + ": " + e.getValue())
|
||||||
|
.forEach(tooltip::add);
|
||||||
|
} else {
|
||||||
|
// Tooltip text:
|
||||||
|
// Triggers Constructable Interface
|
||||||
|
// Shows multiblock construction details,
|
||||||
|
// just Use on a multiblock controller.
|
||||||
|
// (Sneak Use in creative to build)
|
||||||
|
// Quantity affects tier/mode/type
|
||||||
|
|
||||||
|
tooltip.add(translateToLocal("item.structurelib.constructableTrigger.desc.0"));
|
||||||
|
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.1"));
|
||||||
|
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.2"));
|
||||||
|
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.3"));
|
||||||
|
tooltip.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.4"));
|
||||||
|
if (ChannelDataAccessor.hasSubChannel(stack))
|
||||||
|
tooltip.add(translateToLocal("item.structurelib.constructableTrigger.desc.5"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,47 @@
|
|||||||
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 stack, EntityPlayer player, List<String> tooltip, boolean adv) {
|
||||||
|
// Tooltip Text:
|
||||||
|
// Triggers Front Rotation Interface
|
||||||
|
// Rotates only the front panel,
|
||||||
|
// which allows structure rotation.
|
||||||
|
|
||||||
|
tooltip.add(translateToLocal("item.structurelib.frontRotationTool.desc.0"));
|
||||||
|
tooltip.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.1"));
|
||||||
|
tooltip.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.2"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.mixins;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.Mod;
|
||||||
|
|
||||||
|
public enum TargetedMod {
|
||||||
|
|
||||||
|
VANILLA("Minecraft", null);
|
||||||
|
|
||||||
|
/** The "name" in the {@link Mod @Mod} annotation */
|
||||||
|
public final String modName;
|
||||||
|
/** Class that implements the IFMLLoadingPlugin interface */
|
||||||
|
public final String coreModClass;
|
||||||
|
/** The "modid" in the {@link Mod @Mod} annotation */
|
||||||
|
public final String modId;
|
||||||
|
|
||||||
|
TargetedMod(String modName, String coreModClass) {
|
||||||
|
this(modName, coreModClass, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetedMod(String modName, String coreModClass, String modId) {
|
||||||
|
this.modName = modName;
|
||||||
|
this.coreModClass = coreModClass;
|
||||||
|
this.modId = modId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TargetedMod{modName='" + modName + "', coreModClass='" + coreModClass + "', modId='" + modId + "'}";
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.mixins.early.blockChangeNotifier;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraftforge.common.ForgeHooks;
|
||||||
|
import net.minecraftforge.common.util.BlockSnapshot;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.event.BlockChangeNotifier;
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
|
||||||
|
@Mixin(ForgeHooks.class)
|
||||||
|
public class MixinForgeHooks {
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = "onPlaceItemIntoWorld",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/World;markAndNotifyBlock(IIILnet/minecraft/world/chunk/Chunk;Lnet/minecraft/block/Block;Lnet/minecraft/block/Block;I)V",
|
||||||
|
shift = At.Shift.AFTER),
|
||||||
|
remap = false)
|
||||||
|
private static void onBlockChange(ItemStack itemstack, EntityPlayer player, World world, int x, int y, int z,
|
||||||
|
int side, float hitX, float hitY, float hitZ, CallbackInfoReturnable<Boolean> cir,
|
||||||
|
@Local(name = "blocksnapshot") BlockSnapshot blocksnapshot, @Local(name = "newBlock") Block newBlock,
|
||||||
|
@Local(name = "metadata") int newMetadata) {
|
||||||
|
BlockChangeNotifier.onBlockChange(
|
||||||
|
world,
|
||||||
|
null,
|
||||||
|
blocksnapshot.x,
|
||||||
|
blocksnapshot.y,
|
||||||
|
blocksnapshot.z,
|
||||||
|
blocksnapshot.replacedBlock,
|
||||||
|
newBlock,
|
||||||
|
blocksnapshot.meta,
|
||||||
|
newMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.mixins.early.blockChangeNotifier;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.event.BlockChangeNotifier;
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import com.llamalad7.mixinextras.sugar.Share;
|
||||||
|
import com.llamalad7.mixinextras.sugar.ref.LocalIntRef;
|
||||||
|
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||||
|
|
||||||
|
@Mixin(World.class)
|
||||||
|
public class MixinWorld {
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = "Lnet/minecraft/world/World;setBlock(IIILnet/minecraft/block/Block;II)Z",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/World;markAndNotifyBlock(IIILnet/minecraft/world/chunk/Chunk;Lnet/minecraft/block/Block;Lnet/minecraft/block/Block;I)V",
|
||||||
|
shift = At.Shift.AFTER,
|
||||||
|
remap = false))
|
||||||
|
public void setBlockObserve(int x, int y, int z, Block newBlock, int newMeta, int flags,
|
||||||
|
CallbackInfoReturnable<Boolean> cir, @Local(name = "block1") Block originalBlock,
|
||||||
|
@Share("originalMeta") LocalIntRef originalMeta, @Share("chunk") LocalRef<Chunk> chunkLocalRef) {
|
||||||
|
BlockChangeNotifier.onBlockChange(
|
||||||
|
(World) ((Object) (this)),
|
||||||
|
chunkLocalRef.get(),
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
originalBlock,
|
||||||
|
newBlock,
|
||||||
|
originalMeta.get(),
|
||||||
|
newMeta);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(
|
||||||
|
method = "Lnet/minecraft/world/World;setBlock(IIILnet/minecraft/block/Block;II)Z",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/world/chunk/Chunk;func_150807_a(IIILnet/minecraft/block/Block;I)Z",
|
||||||
|
shift = At.Shift.BEFORE))
|
||||||
|
public void captureChunkAndOriginalMetadata(int x, int y, int z, Block blockIn, int metadataIn, int flags,
|
||||||
|
CallbackInfoReturnable<Boolean> cir, @Local(name = "chunk") Chunk chunk,
|
||||||
|
@Share("originalMeta") LocalIntRef originalMeta, @Share("chunk") LocalRef<Chunk> chunkLocalRef) {
|
||||||
|
chunkLocalRef.set(chunk);
|
||||||
|
originalMeta.set(chunk.getBlockMetadata(x & 15, y, z & 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +1,30 @@
|
|||||||
package com.gtnewhorizon.structurelib.net;
|
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,77 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.net;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import com.gtnewhorizon.structurelib.SortedRegistry;
|
||||||
|
|
||||||
|
import cpw.mods.fml.common.network.ByteBufUtils;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
|
||||||
|
import cpw.mods.fml.common.network.simpleimpl.MessageContext;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
public class RegistryOrderSyncMessage implements IMessage {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private List<String> order;
|
||||||
|
private List<String> disabled;
|
||||||
|
|
||||||
|
public RegistryOrderSyncMessage() {}
|
||||||
|
|
||||||
|
public RegistryOrderSyncMessage(String name, List<String> order, List<String> disabled) {
|
||||||
|
this.name = name;
|
||||||
|
this.order = order;
|
||||||
|
this.disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fromBytes(ByteBuf buf) {
|
||||||
|
name = ByteBufUtils.readUTF8String(buf);
|
||||||
|
int size = ByteBufUtils.readVarInt(buf, 2);
|
||||||
|
order = new ArrayList<>(size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
order.add(ByteBufUtils.readUTF8String(buf));
|
||||||
|
}
|
||||||
|
size = ByteBufUtils.readVarInt(buf, 2);
|
||||||
|
disabled = new ArrayList<>(size);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
disabled.add(ByteBufUtils.readUTF8String(buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toBytes(ByteBuf buf) {
|
||||||
|
ByteBufUtils.writeUTF8String(buf, name);
|
||||||
|
ByteBufUtils.writeVarInt(buf, order.size(), 2);
|
||||||
|
for (String s : order) {
|
||||||
|
ByteBufUtils.writeUTF8String(buf, s);
|
||||||
|
}
|
||||||
|
ByteBufUtils.writeVarInt(buf, disabled.size(), 2);
|
||||||
|
for (String s : disabled) {
|
||||||
|
ByteBufUtils.writeUTF8String(buf, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Handler implements IMessageHandler<RegistryOrderSyncMessage, IMessage> {
|
||||||
|
|
||||||
|
private static ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> ALL_REGISTRIES;
|
||||||
|
|
||||||
|
public static void setAllRegistries(ConcurrentHashMap<String, WeakReference<SortedRegistry<?>>> r) {
|
||||||
|
if (ALL_REGISTRIES == null) ALL_REGISTRIES = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IMessage onMessage(RegistryOrderSyncMessage message, MessageContext ctx) {
|
||||||
|
if (ALL_REGISTRIES == null) return null;
|
||||||
|
WeakReference<SortedRegistry<?>> ref = ALL_REGISTRIES.get(message.name);
|
||||||
|
if (ref == null) return null;
|
||||||
|
SortedRegistry<?> r = ref.get();
|
||||||
|
if (r == null) return null;
|
||||||
|
r.registerOrdering(ctx.getServerHandler().playerEntity, message.order, message.disabled);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,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,29 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
class ChatThrottleKey {
|
||||||
|
|
||||||
|
static class NoExplicitChannel {
|
||||||
|
|
||||||
|
private static final int hashOffset = NoExplicitChannel.class.hashCode();
|
||||||
|
private final String subchannel;
|
||||||
|
|
||||||
|
public NoExplicitChannel(String subchannel) {
|
||||||
|
this.subchannel = subchannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof NoExplicitChannel that)) return false;
|
||||||
|
|
||||||
|
return subchannel.equals(that.subchannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// as this one will be placed within a map along with instances of different classes, it'd be best to add
|
||||||
|
// an offset based off class type to avoid collision
|
||||||
|
return subchannel.hashCode() + hashOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
|
||||||
|
class DefaultSurvivalBuildEnvironment implements ISurvivalBuildEnvironment {
|
||||||
|
|
||||||
|
private final IItemSource source;
|
||||||
|
private final EntityPlayer actor;
|
||||||
|
|
||||||
|
public DefaultSurvivalBuildEnvironment(IItemSource source, EntityPlayer actor) {
|
||||||
|
this.source = source;
|
||||||
|
this.actor = actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IItemSource getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityPlayer getActor() {
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,5 +3,6 @@ package com.gtnewhorizon.structurelib.structure;
|
|||||||
import net.minecraft.world.World;
|
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,450 @@
|
|||||||
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.ChannelDataAccessor;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
|
||||||
|
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the structure definition of your multi. You will have one of these for each multi.
|
||||||
|
*
|
||||||
|
* <h2>Cache the instances of this class</h2>
|
||||||
|
* <p>
|
||||||
|
* You will construct this definition once per multi, either at class load or at first call to your variant of
|
||||||
|
* {@code getStructureDefinition()}. You can use a {@link ClassValue ClassValue<IStructureDefinition<?>>} if you
|
||||||
|
* have a template for subclass. You can also use {@link StructureUtility#defer(Function) defer()} or its overloads for
|
||||||
|
* simpler cases, but it is slightly less performant as it will allocate new objects on each structure check, whereas
|
||||||
|
* {@link ClassValue} will allocate once and cache it onwards.
|
||||||
|
* <p>
|
||||||
|
* <h2>Constructing</h2>
|
||||||
|
* <p>
|
||||||
|
* {@linkplain IStructureDefinition} is constructed using {@link IStructureDefinition#builder()}
|
||||||
|
* <p>
|
||||||
|
* Builder has two main methods:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addShape(String, String[][])
|
||||||
|
* addShape()} is a 2d string array describing the layout of structure. You can optionally use
|
||||||
|
* `StructureUtility#transpose()` to transpose the array to make the structure look nicer in source code.
|
||||||
|
* <li>{@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addElement(Character, IStructureElement)
|
||||||
|
* addElement()}. It takes a char identifier (mapped to a char in any of
|
||||||
|
* {@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addShape(String, String[][]) addShape()}
|
||||||
|
* calls) and a {@link IStructureElement}.
|
||||||
|
* </ul>
|
||||||
|
* <h2>Position A, B and C</h2>
|
||||||
|
*
|
||||||
|
* Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped.
|
||||||
|
* <ul>
|
||||||
|
* <li>The "A" position would be the number of blocks on the left side of the controller, not counting controller
|
||||||
|
* itself.</li>
|
||||||
|
* <li>The "B" position would be the number of blocks on the top side of the controller, not counting controller
|
||||||
|
* itself.</li>
|
||||||
|
* <li>The "C" position would be the number of blocks between you and controller, not counting controller itself.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> Type of the context object. Usually this will be the multiblock controller.
|
||||||
|
*/
|
||||||
public interface IStructureDefinition<T> {
|
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<? super 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])) {
|
default void iterate(String piece, World world, ExtendedFacing extendedFacing, int basePositionX, int basePositionY,
|
||||||
if (!element.check(object, world, xyz[0], xyz[1], xyz[2])) {
|
int basePositionZ, int basePositionA, int basePositionB, int basePositionC, IStructureWalker<T> predicate) {
|
||||||
if (DEBUG_MODE) {
|
StructureUtility.iterateV2(
|
||||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] failed @ " +
|
getStructureFor(piece),
|
||||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
world,
|
||||||
}
|
extendedFacing,
|
||||||
return false;
|
basePositionX,
|
||||||
}
|
basePositionY,
|
||||||
} else {
|
basePositionZ,
|
||||||
if (DEBUG_MODE) {
|
basePositionA,
|
||||||
StructureLib.LOGGER.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] !blockExists @ " +
|
basePositionB,
|
||||||
Arrays.toString(xyz) + " " + Arrays.toString(abc));
|
basePositionC,
|
||||||
}
|
predicate,
|
||||||
return false;
|
"customIterate");
|
||||||
}
|
}
|
||||||
abc[0] += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (IStructureElement<T> element : elements) {
|
|
||||||
if (element.isNavigating()) {
|
|
||||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
|
||||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
|
||||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
|
||||||
} else {
|
|
||||||
extendedFacing.getWorldOffset(abc, xyz);
|
|
||||||
xyz[0] += basePositionX;
|
|
||||||
xyz[1] += basePositionY;
|
|
||||||
xyz[2] += basePositionZ;
|
|
||||||
|
|
||||||
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
|
||||||
}
|
* @param basePositionC see class javadoc
|
||||||
abc[0] += 1;
|
* @param hintsOnly whether to spawn hints or do creative build. no effect if
|
||||||
}
|
* {@code checkBlocksIfNotNullForceCheckAllIfTrue} is not null.
|
||||||
}
|
* @param checkBlocksIfNotNullForceCheckAllIfTrue creative build or spawn hints if null. Force check not loaded
|
||||||
}
|
* chunk if true. Check only loaded chunk is false.
|
||||||
} else {
|
* @return true if iteration completed successfully.
|
||||||
if (hintsOnly) {
|
* @param <T> type of context object.
|
||||||
for (IStructureElement<T> element : elements) {
|
*/
|
||||||
if (element.isNavigating()) {
|
static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<? super T>[] elements, World world,
|
||||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
ExtendedFacing extendedFacing, int basePositionX, int basePositionY, int basePositionZ, int basePositionA,
|
||||||
abc[1] = (element.resetB() ? basePositionB : abc[1]) + element.getStepB();
|
int basePositionB, int basePositionC, boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||||
abc[2] = (element.resetC() ? basePositionC : abc[2]) + element.getStepC();
|
if (!world.isRemote && hintsOnly) {
|
||||||
} else {
|
return false;
|
||||||
extendedFacing.getWorldOffset(abc, xyz);
|
}
|
||||||
xyz[0] += basePositionX;
|
|
||||||
xyz[1] += basePositionY;
|
|
||||||
xyz[2] += basePositionZ;
|
|
||||||
|
|
||||||
element.spawnHint(object, world, xyz[0], xyz[1], xyz[2], trigger);
|
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
|
||||||
|
boolean success;
|
||||||
|
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
|
||||||
|
success = StructureUtility.iterateV2(
|
||||||
|
elements,
|
||||||
|
world,
|
||||||
|
extendedFacing,
|
||||||
|
basePositionX,
|
||||||
|
basePositionY,
|
||||||
|
basePositionZ,
|
||||||
|
basePositionA,
|
||||||
|
basePositionB,
|
||||||
|
basePositionC,
|
||||||
|
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
|
||||||
|
"check");
|
||||||
|
} else {
|
||||||
|
success = StructureUtility.iterateV2(
|
||||||
|
elements,
|
||||||
|
world,
|
||||||
|
extendedFacing,
|
||||||
|
basePositionX,
|
||||||
|
basePositionY,
|
||||||
|
basePositionZ,
|
||||||
|
basePositionA,
|
||||||
|
basePositionB,
|
||||||
|
basePositionC,
|
||||||
|
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
|
||||||
|
"check force");
|
||||||
|
}
|
||||||
|
if (StructureLibAPI.isDebugEnabled() && success) {
|
||||||
|
StructureLib.LOGGER
|
||||||
|
.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
} else {
|
||||||
|
if (hintsOnly) {
|
||||||
|
StructureUtility.iterateV2(
|
||||||
|
elements,
|
||||||
|
world,
|
||||||
|
extendedFacing,
|
||||||
|
basePositionX,
|
||||||
|
basePositionY,
|
||||||
|
basePositionZ,
|
||||||
|
basePositionA,
|
||||||
|
basePositionB,
|
||||||
|
basePositionC,
|
||||||
|
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||||
|
e.spawnHint(object, world, x, y, z, trigger);
|
||||||
|
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
|
||||||
|
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
|
||||||
|
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
"spawnHint");
|
||||||
|
} else {
|
||||||
|
StructureUtility.iterateV2(
|
||||||
|
elements,
|
||||||
|
world,
|
||||||
|
extendedFacing,
|
||||||
|
basePositionX,
|
||||||
|
basePositionY,
|
||||||
|
basePositionZ,
|
||||||
|
basePositionA,
|
||||||
|
basePositionB,
|
||||||
|
basePositionC,
|
||||||
|
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
|
||||||
|
e.placeBlock(object, world, x, y, z, trigger);
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
"placeBlock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
abc[0] += 1;
|
/**
|
||||||
}
|
* Create a new instance of builder.
|
||||||
}
|
*
|
||||||
} else {
|
* @param <T> type of context object
|
||||||
for (IStructureElement<T> element : elements) {
|
*/
|
||||||
if (element.isNavigating()) {
|
static <T> StructureDefinition.Builder<T> builder() {
|
||||||
abc[0] = (element.resetA() ? basePositionA : abc[0]) + element.getStepA();
|
return StructureDefinition.builder();
|
||||||
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,384 @@
|
|||||||
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.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.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.
|
||||||
|
*
|
||||||
|
* @param <T> Type of the context object. {@link IStructureElement} is contravariant over T, so parameters and fields of
|
||||||
|
* type {@link IStructureElement} should be declared as {@code IStructureElement<? super T>}
|
||||||
*/
|
*/
|
||||||
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);
|
/**
|
||||||
|
* Pure (stateless and side effect free) function to test if current block could be valid. Used to give user hints
|
||||||
|
* about incorrectly placed blocks given a controller and trigger item. If couldBeValid(...) == false for a set of
|
||||||
|
* channels, with those same tiers, check(...) == false. Not required, defaults to true which is always safe but
|
||||||
|
* won't give the user error hints.
|
||||||
|
*/
|
||||||
|
default boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
default int getStepA() {
|
/**
|
||||||
return 1;
|
* Returns a human-readable description of what block(s) this element accepts. Used for diagnostic messages when
|
||||||
}
|
* structure checks fail (e.g. "expected Input Bus at (1, 2, 3)").
|
||||||
|
* <p>
|
||||||
|
* Unlike {@link #getBlocksToPlace}, which returns the full set of concrete block variants for autoplacing (e.g.
|
||||||
|
* every tier of Input Bus), this method returns <b>category-level</b> descriptions suitable for display to the
|
||||||
|
* player (e.g. just "Input Bus"). It also requires no parameters, no world, trigger item, or environment, making it
|
||||||
|
* usable in contexts where those are unavailable.
|
||||||
|
* <p>
|
||||||
|
* Implementations should return <b>lang keys</b> (e.g. {@code "tile.blockGlass.name"}) rather than pre-translated
|
||||||
|
* display names. The caller is responsible for translating these keys on the client side via
|
||||||
|
* {@code StatCollector.translateToLocal()}. If a key has no matching translation, {@code translateToLocal} returns
|
||||||
|
* the input string unchanged, so pre-translated fallback strings also work.
|
||||||
|
*
|
||||||
|
* @param context the context object (e.g. the multiblock controller), used to determine state-dependent
|
||||||
|
* descriptions
|
||||||
|
* @return a list of accepted block descriptions as lang keys (or display names as fallback), or null if no
|
||||||
|
* description is available
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default List<String> getDescription(T context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
default int getStepB() {
|
boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
default int getStepC() {
|
boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean resetA() {
|
/**
|
||||||
return false;
|
* Try place the block by taking resource from given IItemSource.
|
||||||
}
|
* <p>
|
||||||
|
* You might want to use
|
||||||
|
* {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
|
||||||
|
* or its overloads to implement this.
|
||||||
|
*
|
||||||
|
* @param trigger trigger item
|
||||||
|
* @param s drain resources from this place
|
||||||
|
* @param actor source of action. for very critical errors you can also just send the error messages here,
|
||||||
|
* bypassing any filter that chatter might have.
|
||||||
|
* @param chatter send error messages here. Caller will choose an appropriate way to forward it to player if the
|
||||||
|
* other fallbacks also fails.
|
||||||
|
* @deprecated caller should call the non deprecated overload. implementor for reusable {@link IStructureElement}
|
||||||
|
* should still implement this. implementor for private implementations can freely ignore this as long
|
||||||
|
* as you know the caller will not call this overload.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||||
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
|
if (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
|
||||||
|
if (StructureLibAPI.isDebugEnabled()) LOGGER.error(
|
||||||
|
"Default implementation of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
|
||||||
|
getClass().getName());
|
||||||
|
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
|
||||||
|
return PlaceResult.REJECT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
default boolean resetB() {
|
/**
|
||||||
return false;
|
* A simplified version of survivalPlaceBlock. Return null if not implemented.
|
||||||
}
|
* <p>
|
||||||
|
* <b>BY OVERRIDING THIS TO NULL, YOU ACKNOWLEDGE THAT THE {@link #check(Object, World, int, int, int)} OF THIS
|
||||||
|
* CLASS IS SIDE EFFECT FREE AND CAN HAVE ITS CHECK CALLED WITHOUT BREAKING ANYTHING. OTHERWISE OVERRIDE THE
|
||||||
|
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)} TO PROVIDE A
|
||||||
|
* SIDEEFFECT FREE {@link PlaceResult#SKIP}.</b>
|
||||||
|
* <p>
|
||||||
|
* It should be noticed that this filter is only advisory and the actual
|
||||||
|
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)} or
|
||||||
|
* {@link #check(Object, World, int, int, int)} are free to reject/accept/place blocks not contained in this list,
|
||||||
|
* e.g. when the element can be placed anywhere in the structure, but no more than 3 overall; when the structure
|
||||||
|
* element cannot know all blocks it will accept at the time of calling; when this is backed by a legacy
|
||||||
|
* {@link IStructureElement} that just didn't implement this API.
|
||||||
|
*
|
||||||
|
* @param trigger trigger item
|
||||||
|
* @param env autoplacing environment
|
||||||
|
* @return null if not implemented, otherwise an instance describing what kind of blocks will be placed by
|
||||||
|
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
default boolean resetC() {
|
/**
|
||||||
return false;
|
* Try place the block by taking resource from given {@link IItemSource}.
|
||||||
}
|
* <p>
|
||||||
|
* You might want to use
|
||||||
|
* {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
|
||||||
|
* or its overloads to implement this.
|
||||||
|
* <p>
|
||||||
|
* The default implementation provided will
|
||||||
|
* <ol>
|
||||||
|
* <li>Get {@link BlocksToPlace} via
|
||||||
|
* {@link #getBlocksToPlace(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}. If not null
|
||||||
|
* <ol>
|
||||||
|
* <li>call {@link #check(Object, World, int, int, int)}. If this returns {@code true}, {@link PlaceResult#SKIP}
|
||||||
|
* will be returned without further action.</li>
|
||||||
|
* <li>Use the {@link BlocksToPlace} retrieved earlier and passed in {@link IItemSource} to determine an item to
|
||||||
|
* place</li>
|
||||||
|
* <li>Hand over control to
|
||||||
|
* {@link StructureUtility#survivalPlaceBlock(ItemStack, NBTMode, NBTTagCompound, boolean, World, int, int, int, IItemSource, EntityPlayer, Consumer)}</li>
|
||||||
|
* </ol>
|
||||||
|
* </li>
|
||||||
|
* <li>Call legacy
|
||||||
|
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)} if
|
||||||
|
* {@code env.getActor()} contains an {@link EntityPlayerMP}</li>
|
||||||
|
* <li>Emit warning under debug mode (or throw exception under panic mode), then return
|
||||||
|
* {@link PlaceResult#SKIP}</li>
|
||||||
|
* </ol>
|
||||||
|
* It should be noticed that the default implementation is likely to cause unexpected issues if the
|
||||||
|
* {@link #check(Object, World, int, int, int)} is not side effect free, or is not idempotent. E.g. the
|
||||||
|
* {@link #check(Object, World, int, int, int)} will add current coord to a {@link java.util.List}. As this method
|
||||||
|
* might be invoked at a location that is already accepted, this will cause the machine to have multiple of this
|
||||||
|
* coord in its list.
|
||||||
|
*
|
||||||
|
* @param trigger trigger item
|
||||||
|
* @param env autoplacing environment
|
||||||
|
*/
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||||
|
IItemSource source = env.getSource();
|
||||||
|
EntityPlayer actor = env.getActor();
|
||||||
|
Consumer<IChatComponent> chatter = env.getChatter();
|
||||||
|
if (e != null) {
|
||||||
|
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
|
||||||
|
if (e.getStacks() == null) {
|
||||||
|
ItemStack taken = source.takeOne(e.getPredicate(), true);
|
||||||
|
return StructureUtility.survivalPlaceBlock(
|
||||||
|
taken,
|
||||||
|
NBTMode.EXACT,
|
||||||
|
taken.stackTagCompound,
|
||||||
|
true,
|
||||||
|
world,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
source,
|
||||||
|
actor,
|
||||||
|
chatter);
|
||||||
|
}
|
||||||
|
for (ItemStack stack : e.getStacks()) {
|
||||||
|
if (!source.takeOne(stack, true)) continue;
|
||||||
|
return StructureUtility.survivalPlaceBlock(
|
||||||
|
stack,
|
||||||
|
NBTMode.EXACT,
|
||||||
|
stack.stackTagCompound,
|
||||||
|
true,
|
||||||
|
world,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
source,
|
||||||
|
actor,
|
||||||
|
chatter);
|
||||||
|
}
|
||||||
|
return PlaceResult.REJECT;
|
||||||
|
}
|
||||||
|
if (actor instanceof EntityPlayerMP)
|
||||||
|
return survivalPlaceBlock(t, world, x, y, z, trigger, source, (EntityPlayerMP) actor, chatter);
|
||||||
|
if (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
|
||||||
|
if (StructureLibAPI.isDebugEnabled()) LOGGER.info(
|
||||||
|
"Fallback shim code of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
|
||||||
|
getClass().getName());
|
||||||
|
return PlaceResult.REJECT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
default boolean isNavigating() {
|
/**
|
||||||
return false;
|
* Forget the messed up class dependency graph for now. this is just so convenient.
|
||||||
}
|
*/
|
||||||
|
default IStructureElementNoPlacement<T> noPlacement() {
|
||||||
|
return new IStructureElementNoPlacement<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(T t, World world, int x, int y, int z) {
|
||||||
|
return IStructureElement.this.check(t, world, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return IStructureElement.this.couldBeValid(t, world, x, y, z, trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return IStructureElement.this.spawnHint(t, world, x, y, z, trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<String> getDescription(T context) {
|
||||||
|
return IStructureElement.this.getDescription(context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default int getStepA() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
default int getStepB() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default int getStepC() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean resetA() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean resetB() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean resetC() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isNavigating() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PlaceResult {
|
||||||
|
/**
|
||||||
|
* The element's check passed, the position is already valid. No placement is needed. In the walker: continue
|
||||||
|
* iteration (no error, not counted as placed). In ofChain: short-circuits, remaining alternatives are skipped.
|
||||||
|
*/
|
||||||
|
SKIP,
|
||||||
|
/**
|
||||||
|
* The element cannot handle this position (occupied, missing resources, etc.). In the walker: stop iteration
|
||||||
|
* with error particles. In ofChain: try next alternative.
|
||||||
|
*/
|
||||||
|
REJECT,
|
||||||
|
/**
|
||||||
|
* This element has no survivalPlaceBlock implementation (default fallback), or otherwise cannot place but is
|
||||||
|
* not an error condition. In the walker: continue iteration silently (like SKIP). In ofChain: try next
|
||||||
|
* alternative (like REJECT), so a better-suited element can handle it.
|
||||||
|
*/
|
||||||
|
REJECT_CONTINUE,
|
||||||
|
/**
|
||||||
|
* Autoplace cannot proceed within this tick. To proceed further would require stopping the placement and wait
|
||||||
|
* for next round.
|
||||||
|
*/
|
||||||
|
STOP,
|
||||||
|
/**
|
||||||
|
* Element placed successfully.
|
||||||
|
*/
|
||||||
|
ACCEPT,
|
||||||
|
/**
|
||||||
|
* Combination of ACCEPT and STOP.
|
||||||
|
* <p>
|
||||||
|
* Element placed successfully. To proceed further would require stopping the placement and wait for next round.
|
||||||
|
*/
|
||||||
|
ACCEPT_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class BlocksToPlace {
|
||||||
|
|
||||||
|
public static final BlocksToPlace errored = createEmpty();
|
||||||
|
private final Predicate<ItemStack> predicate;
|
||||||
|
private final Iterable<ItemStack> stacks;
|
||||||
|
|
||||||
|
public static BlocksToPlace createEmpty() {
|
||||||
|
return new BlocksToPlace(s -> false, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlocksToPlace create(ItemStack... stacks) {
|
||||||
|
return create(Arrays.asList(stacks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlocksToPlace create(Iterable<ItemStack> stacks) {
|
||||||
|
Predicate<ItemStack> predicate = null;
|
||||||
|
|
||||||
|
for (ItemStack stack : stacks) {
|
||||||
|
ItemStackPredicate p = ItemStackPredicate.from(stack, NBTMode.EXACT);
|
||||||
|
if (predicate == null) predicate = p;
|
||||||
|
else predicate = predicate.or(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BlocksToPlace(predicate, stacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlocksToPlace create(Block block, int meta) {
|
||||||
|
Item itemBlock = Item.getItemFromBlock(block);
|
||||||
|
if (itemBlock instanceof ISpecialItemBlock) {
|
||||||
|
meta = ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta);
|
||||||
|
}
|
||||||
|
return create(itemBlock, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlocksToPlace create(Item item, int meta) {
|
||||||
|
return new BlocksToPlace(
|
||||||
|
ItemStackPredicate.from(item).setMeta(meta),
|
||||||
|
Collections.singletonList(new ItemStack(item, 1, meta)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlocksToPlace create(ItemStack itemStack) {
|
||||||
|
return new BlocksToPlace(ItemStackPredicate.from(itemStack), Collections.singletonList(itemStack));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlocksToPlace create(Predicate<ItemStack> predicate) {
|
||||||
|
return new BlocksToPlace(predicate, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlocksToPlace(Predicate<ItemStack> predicate, Iterable<ItemStack> stacks) {
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.stacks = stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the blocks to place filter. This is usually slower as it requires walking through inventories one by one.
|
||||||
|
* <p>
|
||||||
|
* Suitable for use with {@link IItemSource#takeOne(Predicate, boolean)}
|
||||||
|
*
|
||||||
|
* @return a predicate. never null.
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Predicate<ItemStack> getPredicate() {
|
||||||
|
return predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ItemStacks that this one is known to accept. Can be null if this info is not available at the time
|
||||||
|
* this instance is constructed.
|
||||||
|
* <p>
|
||||||
|
* Suitable for use with {@link IItemSource#takeOne(ItemStack, boolean)}.
|
||||||
|
* <p>
|
||||||
|
* This should be equivalent to the predicate returned from {@link #getPredicate()}
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public Iterable<ItemStack> getStacks() {
|
||||||
|
return stacks;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+134
-28
@@ -1,41 +1,147 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.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<? super 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<? super 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 couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
for (IStructureElement<T> fallback : fallbacks()) {
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
if (fallback.couldBeValid(t, world, x, y, z, trigger)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
|
if (fallback.spawnHint(t, world, x, y, z, trigger)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean placeBlock(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
|
if (fallback.placeBlock(t, world, x, y, z, trigger)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
default List<String> getDescription(T context) {
|
||||||
|
Set<String> descriptions = new LinkedHashSet<>();
|
||||||
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
|
List<String> desc = fallback.getDescription(context);
|
||||||
|
if (desc != null) {
|
||||||
|
descriptions.addAll(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return descriptions.isEmpty() ? null : new ArrayList<>(descriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
Predicate<ItemStack> predicate = null;
|
||||||
|
List<Iterable<ItemStack>> is = new ArrayList<>();
|
||||||
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
|
BlocksToPlace e = fallback.getBlocksToPlace(t, world, x, y, z, trigger, env);
|
||||||
|
if (e == null) continue;
|
||||||
|
if (predicate == null) predicate = e.getPredicate();
|
||||||
|
else predicate = predicate.or(e.getPredicate());
|
||||||
|
Iterable<ItemStack> stacks = e.getStacks();
|
||||||
|
if (stacks != null) is.add(stacks);
|
||||||
|
}
|
||||||
|
if (predicate == null) return null;
|
||||||
|
return new BlocksToPlace(predicate, Iterables.concat(is));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||||
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
|
boolean allContinue = true;
|
||||||
|
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||||
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
|
PlaceResult result = fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, bufferedNoise::add);
|
||||||
|
switch (result) {
|
||||||
|
case REJECT_CONTINUE:
|
||||||
|
break;
|
||||||
|
case REJECT:
|
||||||
|
allContinue = false;
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
|
bufferedNoise.forEach(chatter);
|
||||||
|
return PlaceResult.SKIP;
|
||||||
|
default:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dump all that noise back into upstream
|
||||||
|
bufferedNoise.forEach(chatter);
|
||||||
|
return allContinue ? PlaceResult.REJECT_CONTINUE : PlaceResult.REJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
boolean allContinue = true;
|
||||||
|
List<IChatComponent> bufferedNoise = new ArrayList<>();
|
||||||
|
for (IStructureElement<? super T> fallback : fallbacks()) {
|
||||||
|
PlaceResult result = fallback
|
||||||
|
.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
|
||||||
|
switch (result) {
|
||||||
|
case REJECT_CONTINUE:
|
||||||
|
break;
|
||||||
|
case REJECT:
|
||||||
|
allContinue = false;
|
||||||
|
break;
|
||||||
|
case SKIP:
|
||||||
|
bufferedNoise.forEach(env.getChatter());
|
||||||
|
return PlaceResult.SKIP;
|
||||||
|
default:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dump all that noise back into upstream
|
||||||
|
bufferedNoise.forEach(env.getChatter());
|
||||||
|
return allContinue ? PlaceResult.REJECT_CONTINUE : PlaceResult.REJECT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+30
-8
@@ -1,16 +1,38 @@
|
|||||||
package com.gtnewhorizon.structurelib.structure;
|
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 couldBeValid(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
|
||||||
|
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
|
||||||
|
return PlaceResult.REJECT_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return PlaceResult.REJECT_CONTINUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+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,50 @@
|
|||||||
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 extends IStructureElement<Object> {
|
||||||
@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(Object 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 couldBeValid(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default boolean isNavigating() {
|
@Override
|
||||||
return true;
|
default boolean spawnHint(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean placeBlock(Object t, World world, int x, int y, int z, ItemStack trigger) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default PlaceResult survivalPlaceBlock(Object 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(Object t, World world, int x, int y, int z, ItemStack trigger,
|
||||||
|
AutoPlaceEnvironment env) {
|
||||||
|
return PlaceResult.SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isNavigating() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.gtnewhorizon.structurelib.structure;
|
||||||
|
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface that is used to iterate over an arbitrary structure.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface IStructureWalker<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called once per structure element.
|
||||||
|
*
|
||||||
|
* @param element The current element
|
||||||
|
* @param world The current world
|
||||||
|
* @param x The absolute X position of the element in world coordinates
|
||||||
|
* @param y The absolute Y position of the element in world coordinates
|
||||||
|
* @param z The absolute Z position of the element in world coordinates
|
||||||
|
* @param a The relative A position of the element in structure-local coordinates (pre transform)
|
||||||
|
* @param b The relative B position of the element in structure-local coordinates (pre transform)
|
||||||
|
* @param c The relative C position of the element in structure-local coordinates (pre transform)
|
||||||
|
* @return False to stop iterating, true to continue.
|
||||||
|
*/
|
||||||
|
boolean visit(IStructureElement<? super T> element, World world, int x, int y, int z, int a, int b, int c);
|
||||||
|
|
||||||
|
default boolean blockNotLoaded(IStructureElement<? super 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<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(IStructureElement<? super 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<? super 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<>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(IStructureElement<? super 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<? super 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);
|
||||||
}
|
}
|
||||||
Loaded 100 of 145 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user