Compare commits

...
28 Commits
Author SHA1 Message Date
Glease ea38c4e1d6 add API to get hologram item 2023-02-11 01:26:46 +08:00
Glease 50e8c97aa3 fix javadoc 2023-02-11 01:26:46 +08:00
Raven Szewczyk 9a82239f2c [ci skip] upgraded build system 2023-01-31 09:56:09 +00:00
Raven Szewczyk 8ed2be38eb Update dependencies, fix javadoc generation 2023-01-30 13:00:51 +00:00
Raven Szewczyk 582f59e73d [ci skip] Add git-blame-ignore-revs for spotlessApply 2023-01-30 12:52:27 +00:00
Raven Szewczyk 464e44de85 [ci skip] spotlessApply with the new settings 2023-01-30 12:52:27 +00:00
Raven Szewczyk 392d2ace8a [ci skip] Update buildscript to RetroFuturaGradle 2023-01-30 12:52:24 +00:00
Glease 978938d450 more documentation efforts 2022-09-16 18:57:04 +08:00
Glease 7a2328c294 push members from StructureEvent down to concrete class 2022-09-16 18:57:04 +08:00
Glease d2cd45e07b drop MP 2022-09-16 18:57:04 +08:00
Glease 8a24900623 Spotless 2022-09-16 18:57:04 +08:00
Glease 15a0034e03 make API compatibility statement 2022-09-16 18:57:04 +08:00
Glease 7aae6dc31c more documentation efforts... 2022-09-16 18:57:04 +08:00
Glease 875bc1990e Add instrumenting API 2022-09-16 18:57:04 +08:00
Glease 74aefd22be remove unused imports 2022-09-16 18:57:04 +08:00
Glease 72ff51d093 expose what IStructureDefinition this constructable is using 2022-09-16 18:57:04 +08:00
Glease 7d74a65ec0 expose what blocks this element might place 2022-09-16 18:57:04 +08:00
Glease 0ac8908b37 Improve documentation on IItemSource 2022-09-16 18:57:04 +08:00
Glease 2dd4349e54 fix default implementation for IItemSource having inverted output 2022-09-16 18:57:04 +08:00
Glease f813104ec4 fix tests 2022-09-16 18:57:04 +08:00
github-actions[bot]GleaseGitHub GTNH Actions
4fe9eb6ea5 Spotless apply for branch feature/survival-autoplace-argument-object for #13 (#14)
Co-authored-by: Glease <4586901+Glease@users.noreply.github.com>
Co-authored-by: GitHub GTNH Actions <>
2022-09-16 18:57:04 +08:00
Glease c0814274ec Use argument object for survival autoplace 2022-09-16 18:57:04 +08:00
Glease 23605fc22d fix particles not sorted when rendering
some experimental code left in during initial rework...
2022-09-09 19:48:12 +08:00
Glease 8ee90a7338 fix crash upon opening hologram gui 2022-09-09 18:09:59 +08:00
Glease 9bd19b9254 fix frustrum culling using wrong bounding box 2022-09-09 17:43:12 +08:00
Glease df4c54ea8d address inspections 2022-09-06 16:34:46 +08:00
くぁーりぃ e71edd50b0 Allow client to auto build multiblocks (#12) 2022-09-05 19:42:57 +08:00
iouterandGlease 1b7cee7c4e Update zh_CN.lang (#11)
Co-authored-by: Glease <4586901+Glease@users.noreply.github.com>
2022-09-03 16:14:16 +08:00
88 changed files with 4313 additions and 2930 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
# Ignore spotlessApply reformat
464e44de8541a46833f50c3bd363d50e1677a685
+42 -20
View File
@@ -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
+13 -2
View File
@@ -6,6 +6,9 @@ javadoc {
include "com/gtnewhorizon/structurelib/structure/**" include "com/gtnewhorizon/structurelib/structure/**"
include "com/gtnewhorizon/structurelib/util/**" include "com/gtnewhorizon/structurelib/util/**"
include "com/gtnewhorizon/structurelib/StructureLibAPI.java" include "com/gtnewhorizon/structurelib/StructureLibAPI.java"
include "com/gtnewhorizon/structurelib/StructureEvent.java"
classpath += sourceSets.patchedMc.output
options { options {
overview "src/main/javadoc/overview.html" overview "src/main/javadoc/overview.html"
@@ -14,11 +17,19 @@ javadoc {
} }
} }
task javadocJar(type: Jar, dependsOn: javadoc) { tasks.register('javadocJar', Jar) {
classifier = 'javadoc' dependsOn javadoc
archiveClassifier = 'javadoc'
from javadoc.destinationDir from javadoc.destinationDir
} }
artifacts { artifacts {
archives javadocJar archives javadocJar
} }
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
+880 -560
View File
File diff suppressed because it is too large. Load diff
+5 -2
View File
@@ -1,6 +1,9 @@
// Add your dependencies here // Add your dependencies here
dependencies { dependencies {
compile("com.github.GTNewHorizons:GTNHLib:0.0.1:dev") api("com.github.GTNewHorizons:GTNHLib:0.0.10:dev")
runtime("com.github.GTNewHorizons:NotEnoughItems:2.2.20-GTNH:dev") runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.3.25-GTNH:dev")
testImplementation(platform('org.junit:junit-bom:5.9.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
} }
Binary file not shown.
+2 -1
View File
@@ -1,5 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored Regular → Executable
+171 -115
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -17,78 +17,113 @@
# #
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" # Resolve links: $0 may be a link
APP_BASE_NAME=`basename "$0"` app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@@ -105,84 +140,105 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
MAX_FD="$MAX_FD_LIMIT" # shellcheck disable=SC3045
fi MAX_FD=$( ulimit -H -n ) ||
ulimit -n $MAX_FD warn "Could not query maximum file descriptor limit"
if [ $? -ne 0 ] ; then esac
warn "Could not set maximum file descriptor limit: $MAX_FD" case $MAX_FD in #(
fi '' | soft) :;; #(
else *)
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
fi # shellcheck disable=SC3045
fi ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=$(save "$@") # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong JAVACMD=$( cygpath --unix "$JAVACMD" )
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")" # Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"
Vendored
+15 -23
View File
@@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,10 +25,14 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@@ -37,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if %ERRORLEVEL% equ 0 goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -51,7 +55,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -61,38 +65,26 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
+21 -3
View File
@@ -1,10 +1,28 @@
pluginManagement {
repositories {
maven {
// RetroFuturaGradle
name "GTNH Maven"
url "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
allowInsecureProtocol = true
mavenContent {
includeGroup("com.gtnewhorizons.retrofuturagradle")
}
}
gradlePluginPortal()
mavenCentral()
mavenLocal()
}
}
plugins { plugins {
id 'com.diffplug.blowdryerSetup' version '1.6.0' id 'com.diffplug.blowdryerSetup' version '1.6.0'
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' // Provides java toolchains
} }
apply plugin: 'com.diffplug.blowdryerSetup'
blowdryerSetup { blowdryerSetup {
github('GTNewHorizons/ExampleMod1.7.10', 'tag', '0.1.4') repoSubfolder 'gtnhShared'
github('GTNewHorizons/ExampleMod1.7.10', 'tag', '0.2.0')
//devLocal '.' // Use this when testing config updates locally //devLocal '.' // Use this when testing config updates locally
} }
@@ -2,14 +2,8 @@ package com.gtnewhorizon.structurelib;
import static com.gtnewhorizon.structurelib.StructureLib.RANDOM; import static com.gtnewhorizon.structurelib.StructureLib.RANDOM;
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import java.util.*; import java.util.*;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiNewChat; import net.minecraft.client.gui.GuiNewChat;
@@ -31,12 +25,22 @@ import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import com.gtnewhorizon.structurelib.entity.fx.WeightlessParticleFX;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
public class ClientProxy extends CommonProxy { public class ClientProxy extends CommonProxy {
private static final short[] RGBA_NO_TINT = {255, 255, 255, 255}; 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 short[] RGBA_RED_TINT = { 255, 128, 128, 0 };
private static final Map<HintParticleInfo, HintGroup> allHints = new HashMap<>(); private static final Map<HintParticleInfo, HintGroup> allHints = new HashMap<>();
/** /**
* All batches of hints. * All batches of hints.
@@ -55,11 +59,10 @@ public class ClientProxy extends CommonProxy {
*/ */
private static final List<HintParticleInfo> allHintsForRender = new ArrayList<>(10000); private static final List<HintParticleInfo> allHintsForRender = new ArrayList<>(10000);
/** /**
* If the diff to current player position is too great sort the allHints. * If the diff to current player position is too great sort the allHints. Initial value is very far off below y=0
* Initial value is very far off below y=0 but not huge enough to make a NaN. * but not huge enough to make a NaN.
* <p> * <p>
* We are using this supposedly immutable object as a mutable object here. * We are using this supposedly immutable object as a mutable object here. So be aware.
* So be aware.
*/ */
private static final Vec3 lastPlayerPos = Vec3.createVectorHelper(0, -1e30, 0); private static final Vec3 lastPlayerPos = Vec3.createVectorHelper(0, -1e30, 0);
/** /**
@@ -158,11 +161,7 @@ public class ClientProxy extends CommonProxy {
} }
@Override @Override
public void addThrottledChat( public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
Object throttleKey,
EntityPlayer player,
IChatComponent text,
short intervalRequired,
boolean forceUpdateLastSend) { boolean forceUpdateLastSend) {
if (player instanceof EntityPlayerMP) if (player instanceof EntityPlayerMP)
super.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend); super.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend);
@@ -250,6 +249,7 @@ public class ClientProxy extends CommonProxy {
} }
private static class HintGroup { private static class HintGroup {
private final List<HintParticleInfo> hints = new LinkedList<>(); private final List<HintParticleInfo> hints = new LinkedList<>();
private int creationTime = -1; private int creationTime = -1;
@@ -267,8 +267,13 @@ public class ClientProxy extends CommonProxy {
} }
private static class HintParticleInfo { private static class HintParticleInfo {
private final World w; private final World w;
// these are the block coordinate for e.g. w.getBlock()
private final int x, y, z; private final int x, y, z;
// these are the lower bounds for rendering. the upper bound would be X + size
// currently size is fixed to be 0.5
// it is not made into a constants because this will allow for easy changing during debug
private final double X, Y, Z; private final double X, Y, Z;
private final IIcon[] icons; private final IIcon[] icons;
private short[] tint; private short[] tint;
@@ -319,10 +324,10 @@ public class ClientProxy extends CommonProxy {
} }
public boolean isInFrustrum(Frustrum frustrum) { public boolean isInFrustrum(Frustrum frustrum) {
return frustrum.isBoxInFrustum(x, y, z, X, Y, Z); return frustrum.isBoxInFrustum(X, Y, Z, X + 0.5, Y + 0.5, Z + 0.5);
} }
public void draw(Tessellator tes) { public void draw(Tessellator tes, double eyeX, double eyeY, double eyeZ) {
double size = 0.5; double size = 0.5;
int brightness = w.blockExists(x, 0, z) ? w.getLightBrightnessForSkyBlocks(x, y, z, 0) : 0; int brightness = w.blockExists(x, 0, z) ? w.getLightBrightnessForSkyBlocks(x, y, z, 0) : 0;
@@ -343,43 +348,63 @@ public class ClientProxy extends CommonProxy {
double U = icons[i].getMaxU(); double U = icons[i].getMaxU();
double v = icons[i].getMinV(); double v = icons[i].getMinV();
double V = icons[i].getMaxV(); double V = icons[i].getMaxV();
switch (i) { // {DOWN, UP, NORTH, SOUTH, WEST, EAST}
case 0: // cube is a very special model. its facings can be rendered correctly by viewer distance without using
tes.addVertexWithUV(X, Y, Z + size, u, V); // surface normals and view vector
tes.addVertexWithUV(X, Y, Z, u, v); // here we do a 2 pass render.
tes.addVertexWithUV(X + size, Y, Z, U, v); // first pass we draw obstructed faces (i.e. faces that are further away from player)
tes.addVertexWithUV(X + size, Y, Z + size, U, V); // second pass we draw unobstructed faces
break; for (int j = 0; j < 2; j++) {
case 1: switch (i) { // {DOWN, UP, NORTH, SOUTH, WEST, EAST}
tes.addVertexWithUV(X, Y + size, Z, u, v); case 0:
tes.addVertexWithUV(X, Y + size, Z + size, u, V); if ((Y >= eyeY) != (j == 1)) continue;
tes.addVertexWithUV(X + size, Y + size, Z + size, U, V); tes.setNormal(0, -1, 0);
tes.addVertexWithUV(X + size, Y + size, Z, U, v); tes.addVertexWithUV(X, Y, Z + size, u, V);
break; tes.addVertexWithUV(X, Y, Z, u, v);
case 2: tes.addVertexWithUV(X + size, Y, Z, U, v);
tes.addVertexWithUV(X, Y, Z, U, V); tes.addVertexWithUV(X + size, Y, Z + size, U, V);
tes.addVertexWithUV(X, Y + size, Z, U, v); break;
tes.addVertexWithUV(X + size, Y + size, Z, u, v); case 1:
tes.addVertexWithUV(X + size, Y, Z, u, V); if ((Y + size <= eyeY) != (j == 1)) continue;
break; tes.setNormal(0, 1, 0);
case 3: tes.addVertexWithUV(X, Y + size, Z, u, v);
tes.addVertexWithUV(X + size, Y, Z + size, 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, Y + size, Z + size, u, v); tes.addVertexWithUV(X + size, Y + size, Z, U, v);
tes.addVertexWithUV(X, Y, Z + size, u, V); break;
break; case 2:
case 4: if ((Z >= eyeZ) != (j == 1)) continue;
tes.addVertexWithUV(X, Y, Z + size, U, V); tes.setNormal(0, 0, -1);
tes.addVertexWithUV(X, Y + size, Z + size, U, v); tes.addVertexWithUV(X, Y, Z, 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 + size, Y + size, Z, u, v);
break; tes.addVertexWithUV(X + size, Y, Z, u, V);
case 5: break;
tes.addVertexWithUV(X + size, Y, Z, U, V); case 3:
tes.addVertexWithUV(X + size, Y + size, Z, U, v); if ((Z <= eyeZ) != (j == 1)) continue;
tes.addVertexWithUV(X + size, Y + size, Z + size, u, v); tes.setNormal(0, 0, 1);
tes.addVertexWithUV(X + size, Y, Z + size, u, V); tes.addVertexWithUV(X + size, Y, Z + size, U, V);
break; 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:
if ((X >= eyeX) != (j == 1)) 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, Z, u, V);
break;
case 5:
if ((X + size <= eyeX) != (j == 1)) 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, Z + size, u, V);
break;
}
} }
} }
} }
@@ -394,6 +419,7 @@ public class ClientProxy extends CommonProxy {
} }
public static class FMLEventHandler { public static class FMLEventHandler {
private void resetPlayerLocation() { private void resetPlayerLocation() {
lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX; lastPlayerPos.xCoord = Minecraft.getMinecraft().thePlayer.posX;
lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY; lastPlayerPos.yCoord = Minecraft.getMinecraft().thePlayer.posY;
@@ -430,12 +456,11 @@ public class ClientProxy extends CommonProxy {
for (HintGroup c : allGroups) allHintsForRender.addAll(c.getHints()); for (HintGroup c : allGroups) allHintsForRender.addAll(c.getHints());
sortRequired = true; sortRequired = true;
} }
if (renderThrough > 0 && (sortRequired || playerPos.squareDistanceTo(lastPlayerPos) > 1e-2)) { if (sortRequired || playerPos.squareDistanceTo(lastPlayerPos) > 1e-2) {
// only sort if there is anything that need depth test disabled
// only redo sort if player moved some distance // only redo sort if player moved some distance
// default is 0.1 block // default is 0.1 block
// if there was a full rebuild, go sort it as well // if there was a full rebuild, go sort it as well
allHintsForRender.sort(Comparator.comparingDouble(info -> info.getSquareDistanceTo(playerPos))); allHintsForRender.sort(Comparator.comparingDouble(info -> -info.getSquareDistanceTo(playerPos)));
resetPlayerLocation(); resetPlayerLocation();
} }
} }
@@ -443,6 +468,7 @@ public class ClientProxy extends CommonProxy {
} }
public static class ForgeEventHandler { public static class ForgeEventHandler {
@SubscribeEvent @SubscribeEvent
public void onWorldLoad(WorldEvent.Load e) { public void onWorldLoad(WorldEvent.Load e) {
if (e.world.isRemote) { if (e.world.isRemote) {
@@ -481,6 +507,7 @@ public class ClientProxy extends CommonProxy {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT); // TODO figure out original states 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 // we need the back facing rendered because the thing is transparent
GL11.glDisable(GL11.GL_CULL_FACE); GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_ALPHA_TEST);
GL11.glEnable(GL11.GL_BLEND); // enable blend so it is transparent GL11.glEnable(GL11.GL_BLEND); // enable blend so it is transparent
GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA); GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA);
// depth test begin as enabled // depth test begin as enabled
@@ -503,7 +530,8 @@ public class ClientProxy extends CommonProxy {
else GL11.glEnable(GL11.GL_DEPTH_TEST); else GL11.glEnable(GL11.GL_DEPTH_TEST);
renderThrough = hint.renderThrough; renderThrough = hint.renderThrough;
} }
hint.draw(tes); // TODO verify if we need to add eyeHeight
hint.draw(tes, d0, d1, d2);
} }
p.endStartSection("Draw"); p.endStartSection("Draw");
tes.draw(); tes.draw();
@@ -1,11 +1,9 @@
package com.gtnewhorizon.structurelib; package com.gtnewhorizon.structurelib;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
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;
@@ -15,7 +13,12 @@ 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 com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
public class CommonProxy { 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, IIcon[] icons, short[] RGBa) {}
public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {} public void hintParticleTinted(World w, int x, int y, int z, Block block, int meta, short[] RGBa) {}
@@ -68,11 +71,7 @@ public class CommonProxy {
private final Map<EntityPlayerMP, Map<Object, Long>> throttleMap = new WeakHashMap<>(); private final Map<EntityPlayerMP, Map<Object, Long>> throttleMap = new WeakHashMap<>();
public void addThrottledChat( public void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired,
Object throttleKey,
EntityPlayer player,
IChatComponent text,
short intervalRequired,
boolean forceUpdateLastSend) { boolean forceUpdateLastSend) {
if (player instanceof EntityPlayerMP) { if (player instanceof EntityPlayerMP) {
Map<Object, Long> submap = throttleMap.computeIfAbsent((EntityPlayerMP) player, p -> new HashMap<>()); Map<Object, Long> submap = throttleMap.computeIfAbsent((EntityPlayerMP) player, p -> new HashMap<>());
@@ -80,13 +79,8 @@ public class CommonProxy {
} }
} }
protected static void addThrottledChat( protected static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
Object throttleKey, short intervalRequired, boolean forceUpdateLastSend, Map<Object, Long> submap) {
EntityPlayer player,
IChatComponent text,
short intervalRequired,
boolean forceUpdateLastSend,
Map<Object, Long> submap) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
Long old; Long old;
if (forceUpdateLastSend) { if (forceUpdateLastSend) {
@@ -1,15 +1,18 @@
package com.gtnewhorizon.structurelib; package com.gtnewhorizon.structurelib;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;
import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.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 cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
public enum ConfigurationHandler { public enum ConfigurationHandler {
INSTANCE; INSTANCE;
private Configuration config; private Configuration config;
@@ -54,8 +57,8 @@ public enum ConfigurationHandler {
"client.hologram", "client.hologram",
true, true,
"An attempt will be made to remove an existing hologram if it collides with a new hologram."); "An attempt will be made to remove an existing hologram if it collides with a new hologram.");
hintLifespan = hintLifespan = config
config.getInt("hintLifespan", "client.hologram", 400, 1, 20000, "Ticks before a hologram disappears."); .getInt("hintLifespan", "client.hologram", 400, 1, 20000, "Ticks before a hologram disappears.");
hintTransparency = config.getInt( hintTransparency = config.getInt(
"hintTransparency", "hintTransparency",
"client.hologram", "client.hologram",
@@ -78,7 +81,7 @@ public enum ConfigurationHandler {
300, 300,
0, 0,
20000, 20000,
"Unit: milisecond. Minimal interval between two auto place round.\n" "Unit: millisecond. Minimal interval between two auto place round.\n"
+ "As expected, server side settings will overrides client settings.\n" + "As expected, server side settings will overrides client settings.\n"
+ "Note this relates to the wall clock, not in game ticks.\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."); + "Value smaller than default is likely to be perceived as no minimal interval whatsoever.");
@@ -2,11 +2,15 @@ package com.gtnewhorizon.structurelib;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Configuration;
enum ConfigurationVersion { enum ConfigurationVersion {
V1 { V1 {
@Override @Override
protected void step(Configuration c) {} protected void step(Configuration c) {}
}; };
@@ -30,10 +34,8 @@ enum ConfigurationVersion {
} }
public static ConfigurationVersion identify(Configuration c) { public static ConfigurationVersion identify(Configuration c) {
return Arrays.stream(VALUES) return Arrays.stream(VALUES).filter(v -> Objects.equals(c.getLoadedConfigVersion(), v.getVersionMarker()))
.filter(v -> Objects.equals(c.getLoadedConfigVersion(), v.getVersionMarker())) .findFirst().orElse(V1);
.findFirst()
.orElse(V1);
} }
@Nullable @Nullable
@@ -1,51 +0,0 @@
package com.gtnewhorizon.structurelib;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.ARBVertexBufferObject;
import org.lwjgl.opengl.ContextCapabilities;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GLContext;
class GLHelper {
public static final boolean vboSupported;
private static final boolean arbVbo;
static {
ContextCapabilities contextcapabilities = GLContext.getCapabilities();
arbVbo = !contextcapabilities.OpenGL15 && contextcapabilities.GL_ARB_vertex_buffer_object;
vboSupported = contextcapabilities.OpenGL15 || arbVbo;
}
public static int glGenBuffers() {
return arbVbo ? ARBVertexBufferObject.glGenBuffersARB() : GL15.glGenBuffers();
}
public static void glBindBuffer(int target, int buffer) {
if (arbVbo) {
ARBVertexBufferObject.glBindBufferARB(target, buffer);
} else {
GL15.glBindBuffer(target, buffer);
}
}
public static void glBufferData(int target, ByteBuffer data, int usage) {
if (arbVbo) {
ARBVertexBufferObject.glBufferDataARB(target, data, usage);
} else {
GL15.glBufferData(target, data, usage);
}
}
public static void glDeleteBuffers(int buffer) {
if (arbVbo) {
ARBVertexBufferObject.glDeleteBuffersARB(buffer);
} else {
GL15.glDeleteBuffers(buffer);
}
}
public static boolean useVbo() {
return vboSupported;
}
}
@@ -1,11 +1,14 @@
package com.gtnewhorizon.structurelib; package com.gtnewhorizon.structurelib;
import cpw.mods.fml.client.IModGuiFactory;
import java.util.Set; import java.util.Set;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen; 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) {}
@@ -1,11 +1,13 @@
package com.gtnewhorizon.structurelib; package com.gtnewhorizon.structurelib;
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
import cpw.mods.fml.common.network.IGuiHandler;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World; import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.gui.GuiScreenConfigureChannels;
import cpw.mods.fml.common.network.IGuiHandler;
public class GuiHandler implements IGuiHandler { public class GuiHandler implements IGuiHandler {
@Override @Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
return null; return null;
@@ -1,14 +1,17 @@
package com.gtnewhorizon.structurelib; package com.gtnewhorizon.structurelib;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.IConfigElement;
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.minecraft.client.gui.GuiScreen;
import net.minecraftforge.common.config.ConfigElement; import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.common.config.Configuration; 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( super(
guiScreen, guiScreen,
@@ -16,16 +19,13 @@ public class GuiModConfig extends GuiConfig {
StructureLibAPI.MOD_ID, StructureLibAPI.MOD_ID,
false, false,
false, false,
GuiConfig.getAbridgedConfigPath( GuiConfig.getAbridgedConfigPath(ConfigurationHandler.INSTANCE.getConfig().toString()));
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());
} }
} }
@@ -3,5 +3,6 @@ package com.gtnewhorizon.structurelib;
import net.minecraft.util.IIcon; import net.minecraft.util.IIcon;
public interface IStructureCompat { public interface IStructureCompat {
void markTextureUsed(IIcon o); void markTextureUsed(IIcon o);
} }
@@ -0,0 +1,159 @@
package com.gtnewhorizon.structurelib;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.gtnewhorizon.structurelib.structure.IStructureElement;
import cpw.mods.fml.common.eventhandler.Event;
/**
* Subscribers are <b>required</b> to not keep reference to these events, nor to depend on values of these events after
* the method call returns.
* <p>
* All of these events will be fired on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}, if they are
* enabled by {@link StructureLibAPI#enableInstrument(Object)}.
* <p>
* All public stuff in this class and its enclosed classes are considered part of the public API. Backwards
* compatibility is maintained to the maximum extend possible.
*/
public abstract class StructureEvent extends Event {
/*
* In case the event is passed to another thread for processing.
*/
private final Object identifier = StructureLibAPI.instrument.get();
StructureEvent() {}
/**
* The instrument identifier. Subscribers should filter by these identifiers to prevent listening to a structure
* event for other people.
*/
public Object getInstrumentIdentifier() {
return identifier;
}
/**
* Fired <b>just before</b> any structure element is visited.
*/
public static final class StructureElementVisitedEvent extends StructureEvent {
private final World world;
private final int x, y, z;
private final int a, b, c;
private final IStructureElement<?> element;
/**
* Fire this event on {@link MinecraftForge#EVENT_BUS} by populating it with given arguments. Do nothing if
* instrumenting isn't enabled for current thread.
* <p>
* Any {@link Exception} thrown by event listeners will be logged and ignored. {@link Error} will be propagated
* as is.
*/
public static void fireEvent(World world, int x, int y, int z, int a, int b, int c,
IStructureElement<?> element) {
if (StructureLibAPI.isInstrumentEnabled()) {
try {
MinecraftForge.EVENT_BUS.post(new StructureElementVisitedEvent(world, x, y, z, a, b, c, element));
} catch (Exception e) {
// logging is done by event bus, we just ignore the consequence here
}
}
}
StructureElementVisitedEvent(World world, int x, int y, int z, int a, int b, int c,
IStructureElement<?> element) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
this.a = a;
this.b = b;
this.c = c;
this.element = element;
}
/**
* A position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
* <p>
* Submit a feature request if you need the baseOffset.
*/
public int getA() {
return a;
}
/**
* B position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
* <p>
* Submit a feature request if you need the baseOffset.
*/
public int getB() {
return b;
}
/**
* C position in ABC coordinate, relative to (0,0,0) in the piece, i.e. without baseOffset
* <p>
* Submit a feature request if you need the baseOffset.
*/
public int getC() {
return c;
}
/**
* Element being visited.
*/
public IStructureElement<?> getElement() {
return element;
}
@Override
public String toString() {
return "StructureElementVisitedEvent{" + "a="
+ a
+ ", b="
+ b
+ ", c="
+ c
+ ", element="
+ element
+ ", world="
+ getWorld()
+ ", x="
+ getX()
+ ", y="
+ getY()
+ ", z="
+ getZ()
+ '}';
}
/**
* Location of this event.
*/
public World getWorld() {
return world;
}
/**
* Location of this event. Absolute world coordinate.
*/
public int getX() {
return x;
}
/**
* Location of this event. Absolute world coordinate.
*/
public int getY() {
return y;
}
/**
* Location of this event. Absolute world coordinate.
*/
public int getZ() {
return z;
}
}
}
@@ -1,5 +1,15 @@
package com.gtnewhorizon.structurelib; package com.gtnewhorizon.structurelib;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.gtnewhorizon.structurelib.block.BlockHint; import com.gtnewhorizon.structurelib.block.BlockHint;
import com.gtnewhorizon.structurelib.command.CommandConfigureChannels; import com.gtnewhorizon.structurelib.command.CommandConfigureChannels;
import com.gtnewhorizon.structurelib.item.ItemBlockHint; import com.gtnewhorizon.structurelib.item.ItemBlockHint;
@@ -10,6 +20,7 @@ import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.SetChannelDataMessage; import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage; import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import com.gtnewhorizon.structurelib.util.XSTR; import com.gtnewhorizon.structurelib.util.XSTR;
import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy; import cpw.mods.fml.common.SidedProxy;
@@ -20,14 +31,6 @@ import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.common.registry.GameRegistry;
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.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.launchwrapper.Launch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/** /**
* 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.
@@ -39,21 +42,25 @@ import org.apache.logging.log4j.Logger;
acceptableRemoteVersions = "*", acceptableRemoteVersions = "*",
guiFactory = "com.gtnewhorizon.structurelib.GuiFactory") guiFactory = "com.gtnewhorizon.structurelib.GuiFactory")
public class StructureLib { public class StructureLib {
private static final String STRUCTURECOMPAT_MODID = "structurecompat"; private static final String STRUCTURECOMPAT_MODID = "structurecompat";
public static boolean DEBUG_MODE; public static boolean DEBUG_MODE;
public static boolean PANIC_MODE = Boolean.getBoolean("structurelib.panic"); public static boolean PANIC_MODE = Boolean.getBoolean("structurelib.panic");
public static Logger LOGGER = LogManager.getLogger("StructureLib"); public static final Logger LOGGER = LogManager.getLogger("StructureLib");
@SidedProxy( @SidedProxy(
serverSide = "com.gtnewhorizon.structurelib.CommonProxy", serverSide = "com.gtnewhorizon.structurelib.CommonProxy",
clientSide = "com.gtnewhorizon.structurelib.ClientProxy") clientSide = "com.gtnewhorizon.structurelib.ClientProxy")
static CommonProxy proxy; static CommonProxy proxy;
static SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(StructureLibAPI.MOD_ID); static final SimpleNetworkWrapper net = NetworkRegistry.INSTANCE.newSimpleChannel(StructureLibAPI.MOD_ID);
static { static {
net.registerMessage( net.registerMessage(
AlignmentMessage.ServerHandler.class, AlignmentMessage.AlignmentQuery.class, 0, Side.SERVER); AlignmentMessage.ServerHandler.class,
AlignmentMessage.AlignmentQuery.class,
0,
Side.SERVER);
net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT); net.registerMessage(AlignmentMessage.ClientHandler.class, AlignmentMessage.AlignmentData.class, 1, Side.CLIENT);
net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT); net.registerMessage(UpdateHintParticleMessage.Handler.class, UpdateHintParticleMessage.class, 2, Side.CLIENT);
net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER); net.registerMessage(SetChannelDataMessage.Handler.class, SetChannelDataMessage.class, 3, Side.SERVER);
@@ -80,6 +87,7 @@ public class StructureLib {
static Item itemFrontRotationTool; static Item itemFrontRotationTool;
static Item itemConstructableTrigger; static Item itemConstructableTrigger;
public static final CreativeTabs creativeTab = new CreativeTabs("structurelib") { public static final CreativeTabs creativeTab = new CreativeTabs("structurelib") {
@Override @Override
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public Item getTabIconItem() { public Item getTabIconItem() {
@@ -93,7 +101,8 @@ public class StructureLib {
GameRegistry.registerBlock(blockHint = new BlockHint(), ItemBlockHint.class, "blockhint"); GameRegistry.registerBlock(blockHint = new BlockHint(), ItemBlockHint.class, "blockhint");
itemBlockHint = ItemBlock.getItemFromBlock(StructureLibAPI.getBlockHint()); itemBlockHint = ItemBlock.getItemFromBlock(StructureLibAPI.getBlockHint());
GameRegistry.registerItem( GameRegistry.registerItem(
itemFrontRotationTool = new ItemFrontRotationTool(), itemFrontRotationTool.getUnlocalizedName()); itemFrontRotationTool = new ItemFrontRotationTool(),
itemFrontRotationTool.getUnlocalizedName());
GameRegistry.registerItem( GameRegistry.registerItem(
itemConstructableTrigger = new ItemConstructableTrigger(), itemConstructableTrigger = new ItemConstructableTrigger(),
itemConstructableTrigger.getUnlocalizedName()); itemConstructableTrigger.getUnlocalizedName());
@@ -101,10 +110,7 @@ public class StructureLib {
NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler()); NetworkRegistry.INSTANCE.registerGuiHandler(instance(), new GuiHandler());
if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) { if (Loader.isModLoaded(STRUCTURECOMPAT_MODID)) {
COMPAT = Loader.instance() COMPAT = Loader.instance().getIndexedModList().get(STRUCTURECOMPAT_MODID).getMod();
.getIndexedModList()
.get(STRUCTURECOMPAT_MODID)
.getMod();
} }
} }
@@ -2,12 +2,6 @@ package com.gtnewhorizon.structurelib;
import static com.gtnewhorizon.structurelib.StructureLib.proxy; import static com.gtnewhorizon.structurelib.StructureLib.proxy;
import com.gtnewhorizon.structurelib.alignment.IAlignment;
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
import com.gtnewhorizon.structurelib.structure.IItemSource;
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;
@@ -17,10 +11,20 @@ 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 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 String MOD_ID = "structurelib";
public static final int HINT_BLOCK_META_GENERIC_0 = 0; 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_1 = 1;
@@ -38,20 +42,74 @@ public class StructureLibAPI {
public static final int HINT_BLOCK_META_AIR = 13; 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_NOT_AIR = 14;
public static final int HINT_BLOCK_META_ERROR = 15; public static final int HINT_BLOCK_META_ERROR = 15;
static final ThreadLocal<Object> instrument = new ThreadLocal<>();
/** /**
* Start a 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
* appropriate. Otherwise, these events will not be sent at all, regardless of whether a listener for these events
* are registered.
* <p> * <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)} * Identifiers are required to be value-comparable, i.e. overrides {@link Object#equals(Object)}. Suggested
* identifier includes
* <ul>
* <li>Mod Instance or {@link cpw.mods.fml.common.ModContainer}</li>
* <li>{@link com.gtnewhorizon.structurelib.alignment.constructable.IConstructable} and its friends, if you are
* triggering structure check via this interface.</li>
* <li>{@link net.minecraft.util.ResourceLocation}</li>
* </ul>
* <p>
* It's an API abuse for normal {@link IStructureDefinition} users to depend on this feature. They have full control
* and full knowledge of the {@link IStructureDefinition} they are using. Using events to instrument the structure
* check/autoplace is completely unnecessary and does nothing but add overhead.
* <p>
* Instruments are required to call {@link #disableInstrument()} whenever it is done with its business, as there can
* be only one instrument for any given thread.
*
* @param identifier an identifier sent along the actual event.
* @throws IllegalStateException if instrument has already been enabled.
*/
public static void enableInstrument(Object identifier) {
if (isInstrumentEnabled()) throw new IllegalStateException();
instrument.set(instrument);
}
/**
* Stop instrumenting.
*
* @throws IllegalStateException if instrumenting hasn't been enabled.
*/
public static void disableInstrument() {
if (!isInstrumentEnabled()) throw new IllegalStateException();
instrument.set(null);
}
/**
* Check if instrumenting is enabled.
*
* @return true if enabled, false otherwise
*/
public static boolean isInstrumentEnabled() {
return instrument.get() != null;
}
/**
* 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) { public static void startHinting(World w) {
proxy.startHinting(w); proxy.startHinting(w);
} }
/** /**
* End current batch of hinting. All hints particles generated during one batch will be considered to belong to one hologram. * End current batch of hinting. All hints particles generated during one batch will be considered to belong to one
* hologram.
* <p> * <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)} * 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) { public static void endHinting(World w) {
proxy.endHinting(w); proxy.endHinting(w);
@@ -59,12 +117,14 @@ public class StructureLibAPI {
/** /**
* Generate a new hint particle on client side at given location using given textures with a tint. * Generate a new hint particle on client side at given location using given textures with a tint.
* @param w World to spawn. Usually the client world. *
* @param x x coord * @param w World to spawn. Usually the client world.
* @param y y coord * @param x x coord
* @param z z coord * @param y y coord
* @param z z coord
* @param icons 6 texture. in forge direction order. * @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. * @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) { public static void hintParticleTinted(World w, int x, int y, int z, IIcon[] icons, short[] RGBa) {
proxy.hintParticleTinted(w, x, y, z, icons, RGBa); proxy.hintParticleTinted(w, x, y, z, icons, RGBa);
@@ -72,13 +132,15 @@ public class StructureLibAPI {
/** /**
* Generate a new hint particle on client side at given location using textures from given block with a tint. * Generate a new hint particle on client side at given location using textures from given block with a tint.
* @param w World to spawn. Usually the client world. *
* @param x x coord * @param w World to spawn. Usually the client world.
* @param y y coord * @param x x coord
* @param z z coord * @param y y coord
* @param z z coord
* @param block block to take texture from * @param block block to take texture from
* @param meta the meta of 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. * @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) { 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); proxy.hintParticleTinted(w, x, y, z, block, meta, RGBa);
@@ -86,10 +148,11 @@ public class StructureLibAPI {
/** /**
* Generate a new hint particle on client side at given location using given textures. * Generate a new hint particle on client side at given location using given textures.
* @param w World to spawn. Usually the client world. *
* @param x x coord * @param w World to spawn. Usually the client world.
* @param y y coord * @param x x coord
* @param z z coord * @param y y coord
* @param z z coord
* @param icons 6 texture. in forge direction order. * @param icons 6 texture. in forge direction order.
*/ */
public static void hintParticle(World w, int x, int y, int z, IIcon[] icons) { public static void hintParticle(World w, int x, int y, int z, IIcon[] icons) {
@@ -98,12 +161,13 @@ public class StructureLibAPI {
/** /**
* Generate a new hint particle on client side at given location using textures from given block. * Generate a new hint particle on client side at given location using textures from given block.
* @param w World to spawn. Usually the client world. *
* @param x x coord * @param w World to spawn. Usually the client world.
* @param y y coord * @param x x coord
* @param z z coord * @param y y coord
* @param z z coord
* @param block block to take texture from * @param block block to take texture from
* @param meta the meta of 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) { public static void hintParticle(World w, int x, int y, int z, Block block, int meta) {
proxy.hintParticle(w, x, y, z, block, meta); proxy.hintParticle(w, x, y, z, block, meta);
@@ -111,10 +175,9 @@ public class StructureLibAPI {
/** /**
* Update the tint of given hint particle. Do nothing if particle not found. * Update the tint of given hint particle. Do nothing if particle not found.
* * <p>
* Can be called on either client side or server side. * Can be called on either client side or server side. Server side will schedule a network message to update it for
* Server side will schedule a network message to update it for this player only. * this player only. Will do nothing if no hint particle is found at given location.
* Will do nothing if no hint particle is found at given location.
* *
* @return false if nothing updated. true if updated or update instruction sent. * @return false if nothing updated. true if updated or update instruction sent.
*/ */
@@ -132,11 +195,13 @@ public class StructureLibAPI {
} }
/** /**
* Query the ExtendedFacing of this tile entity from client side. Can be sent only on client side. * Query the ExtendedFacing of this tile entity from client side. Can be sent only on client side. The
* The ExtendedFacing will later be set onto given tile entity via {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} * ExtendedFacing will later be set onto given tile entity via
* upon arrival of server reply. * {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} upon arrival of
* server reply.
* <p> * <p>
* The server side will query the {@link ExtendedFacing} of this provider using {@link IAlignment#getExtendedFacing()} * 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 * @throws IllegalArgumentException if is not tile entity or provided a null alignment
*/ */
@@ -147,7 +212,8 @@ public class StructureLibAPI {
/** /**
* Send the ExtendedFacing of this Tile Entity to all players. Can be called on server side only. * Send the ExtendedFacing of this Tile Entity to all players. Can be called on server side only.
* <p> * <p>
* The receiving tile entity will receive the {@link ExtendedFacing} via its {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method. * 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 * @throws IllegalArgumentException if is not tile entity or provided a null alignment
*/ */
@@ -158,7 +224,8 @@ public class StructureLibAPI {
/** /**
* Send the ExtendedFacing of this Tile Entity to given player. Can be called on server side only. * Send the ExtendedFacing of this Tile Entity to given player. Can be called on server side only.
* <p> * <p>
* The receiving tile entity will receive the {@link ExtendedFacing} via its {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method. * 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 * @throws IllegalArgumentException if is not tile entity or provided a null alignment
*/ */
@@ -167,9 +234,11 @@ public class StructureLibAPI {
} }
/** /**
* Send the ExtendedFacing of this Tile Entity to all players around target point. Can be called on server side only. * Send the ExtendedFacing of this Tile Entity to all players around target point. Can be called on server side
* only.
* <p> * <p>
* The receiving tile entity will receive the {@link ExtendedFacing} via its {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method. * 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 * @throws IllegalArgumentException if is not tile entity or provided a null alignment
*/ */
@@ -180,7 +249,8 @@ public class StructureLibAPI {
/** /**
* Send the ExtendedFacing of this Tile Entity to all players in that dimension. Can be called on server side only. * Send the ExtendedFacing of this Tile Entity to all players in that dimension. Can be called on server side only.
* <p> * <p>
* The receiving tile entity will receive the {@link ExtendedFacing} via its {@link com.gtnewhorizon.structurelib.alignment.IAlignment#setExtendedFacing(ExtendedFacing)} method. * 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 * @throws IllegalArgumentException if is not tile entity or provided a null alignment
*/ */
@@ -202,6 +272,13 @@ public class StructureLibAPI {
return StructureLib.itemBlockHint; return StructureLib.itemBlockHint;
} }
/**
* Get the hologram item.
*/
public static Item getDefaultHologramItem() {
return StructureLib.itemConstructableTrigger;
}
/** /**
* Check if structure debug mode is on. * Check if structure debug mode is on.
* *
@@ -216,12 +293,25 @@ public class StructureLibAPI {
} }
/** /**
* Determines if given block can be replaced without much effort. The exact predicate clauses is not stable and * Determines if given block can be replaced without much effort. The exact predicate clauses is not stable and will
* will be changed, but the general idea will always stay the same. * be changed, but the general idea will always stay the same.
* <p> * <p>
* Use this in your {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, java.util.function.Consumer)} * 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) { 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 // TODO extend this function a bit
Block block = w.getBlock(x, y, z); Block block = w.getBlock(x, y, z);
return block.isAir(w, x, y, z) || block.isReplaceable(w, x, y, z); return block.isAir(w, x, y, z) || block.isReplaceable(w, x, y, z);
@@ -229,34 +319,34 @@ public class StructureLibAPI {
/** /**
* Send chat to player, but throttled. * 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 throttleKey throttle key. Must properly implement {@link Object#hashCode()} and
* @param player player to send chat to * {@link Object#equals(Object)} to handle value equality.
* @param text chat to send * @param player player to send chat to
* @param intervalRequired interval required before last recorded time to actually send the message. unit in millisecond. * @param text chat to send
* we purposefully chose to not use a bigger data type to limit how long this interval can be * @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( public static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
Object throttleKey, EntityPlayer player, IChatComponent text, short intervalRequired) { short intervalRequired) {
addThrottledChat(throttleKey, player, text, intervalRequired, false); addThrottledChat(throttleKey, player, text, intervalRequired, false);
} }
/** /**
* Send chat to player, but throttled. * 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 throttleKey throttle key. Must properly implement {@link Object#hashCode()} and
* @param player player to send chat to * {@link Object#equals(Object)} to handle value equality.
* @param text chat to send * @param player player to send chat to
* @param intervalRequired interval required before last recorded time to actually send the message. unit in millisecond. * @param text chat to send
* we purposefully chose to not use a bigger data type to limit how long this interval can be * @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. * @param forceUpdateLastSend if true, always update the last send time, even if the message isn't actually sent.
*/ */
public static void addThrottledChat( public static void addThrottledChat(Object throttleKey, EntityPlayer player, IChatComponent text,
Object throttleKey, short intervalRequired, boolean forceUpdateLastSend) {
EntityPlayer player,
IChatComponent text,
short intervalRequired,
boolean forceUpdateLastSend) {
proxy.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend); proxy.addThrottledChat(throttleKey, player, text, intervalRequired, forceUpdateLastSend);
} }
} }
@@ -1,8 +1,9 @@
package com.gtnewhorizon.structurelib.alignment; package com.gtnewhorizon.structurelib.alignment;
import net.minecraftforge.common.util.ForgeDirection;
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
import net.minecraftforge.common.util.ForgeDirection;
class AlignmentLimits implements IAlignmentLimits { class AlignmentLimits implements IAlignmentLimits {
@@ -7,6 +7,7 @@ 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() {} private AlignmentUtility() {}
public static boolean handle(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ) { public static boolean handle(EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ) {
@@ -1,12 +1,14 @@
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 DIRECTIONS_COUNT = Direction.VALUES.length;
int ROTATIONS_COUNT = Rotation.VALUES.length; int ROTATIONS_COUNT = Rotation.VALUES.length;
int FLIPS_COUNT = Flip.VALUES.length; int FLIPS_COUNT = Flip.VALUES.length;
@@ -15,8 +17,8 @@ public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
ExtendedFacing getExtendedFacing(); ExtendedFacing getExtendedFacing();
/** /**
* Set the facing without additional checks. * Set the facing without additional checks. Tools altering facing should use
* Tools altering facing should use {@link #toolSetExtendedFacing(ExtendedFacing)} instead. * {@link #toolSetExtendedFacing(ExtendedFacing)} instead.
*/ */
void setExtendedFacing(ExtendedFacing alignment); void setExtendedFacing(ExtendedFacing alignment);
@@ -57,9 +59,8 @@ public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
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; for (int i = 0, j = getDirection().ordinal() + 1, valuesLength = Direction.VALUES.length; i
i < valuesLength; < valuesLength; i++) {
i++) {
if (toolSetDirection(Direction.VALUES[(j + i) % valuesLength].getForgeDirection())) { if (toolSetDirection(Direction.VALUES[(j + i) % valuesLength].getForgeDirection())) {
return true; return true;
} }
@@ -97,8 +98,11 @@ public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
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( if (checkedSetExtendedFacing(
getDirection(), Rotation.VALUES[(j + i) % rotations], Flip.VALUES[(jj + ii) % flips]))) { ExtendedFacing.of(
getDirection(),
Rotation.VALUES[(j + i) % rotations],
Flip.VALUES[(jj + ii) % flips]))) {
return true; return true;
} }
} }
@@ -140,9 +144,8 @@ public interface IAlignment extends IAlignmentLimits, IAlignmentProvider {
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; for (int i = 0, j = getExtendedFacing().ordinal() + 1, valuesLength = ExtendedFacing.VALUES.length; i
i < valuesLength; < valuesLength; i++) {
i++) {
if (checkedSetExtendedFacing(ExtendedFacing.VALUES[(j + i) % valuesLength])) { if (checkedSetExtendedFacing(ExtendedFacing.VALUES[(j + i) % valuesLength])) {
return true; return true;
} }
@@ -2,14 +2,16 @@ package com.gtnewhorizon.structurelib.alignment;
import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT; import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
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 net.minecraftforge.common.util.ForgeDirection; 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 {
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true; IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
@@ -37,6 +39,7 @@ public interface IAlignmentLimits {
} }
class Builder { class Builder {
protected final boolean[] validStates = new boolean[STATES_COUNT]; protected final boolean[] validStates = new boolean[STATES_COUNT];
private Builder() {} private Builder() {}
@@ -54,15 +57,13 @@ public interface IAlignmentLimits {
} }
public Builder deny(ForgeDirection fd) { public Builder deny(ForgeDirection fd) {
ExtendedFacing.getAllWith(fd).stream() ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = false); .forEach(v -> validStates[v] = false);
return this; return this;
} }
public Builder allow(ForgeDirection fd) { public Builder allow(ForgeDirection fd) {
ExtendedFacing.getAllWith(fd).stream() ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = true); .forEach(v -> validStates[v] = true);
return this; return this;
} }
@@ -78,29 +79,25 @@ public interface IAlignmentLimits {
} }
public Builder deny(Rotation fd) { public Builder deny(Rotation fd) {
ExtendedFacing.getAllWith(fd).stream() ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = false); .forEach(v -> validStates[v] = false);
return this; return this;
} }
public Builder allow(Rotation fd) { public Builder allow(Rotation fd) {
ExtendedFacing.getAllWith(fd).stream() ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = true); .forEach(v -> validStates[v] = true);
return this; return this;
} }
public Builder deny(Flip fd) { public Builder deny(Flip fd) {
ExtendedFacing.getAllWith(fd).stream() ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = false); .forEach(v -> validStates[v] = false);
return this; return this;
} }
public Builder allow(Flip fd) { public Builder allow(Flip fd) {
ExtendedFacing.getAllWith(fd).stream() ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = true); .forEach(v -> validStates[v] = true);
return this; return this;
} }
@@ -122,7 +119,7 @@ public interface IAlignmentLimits {
} }
/** /**
* Prefers rotation over flip, so both flip will get translated to opposite rotation and no flip * Prefers rotation to flip, so both flip will get translated to opposite rotation and no flip
* *
* @param flip the preferred flip to be used Horizontal or vertical * @param flip the preferred flip to be used Horizontal or vertical
* @return this * @return this
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.alignment;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public interface IAlignmentProvider { public interface IAlignmentProvider {
@Nullable @Nullable
IAlignment getAlignment(); IAlignment getAlignment();
} }
@@ -2,12 +2,14 @@ package com.gtnewhorizon.structurelib.alignment;
import static java.lang.Math.abs; import static java.lang.Math.abs;
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import net.minecraft.util.Vec3; import net.minecraft.util.Vec3;
import net.minecraftforge.common.util.ForgeDirection; 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;
@@ -20,61 +22,49 @@ public class IntegerAxisSwap {
|| 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()) || abs(this.forFirstAxis.get2()) + abs(this.forSecondAxis.get2()) + abs(this.forThirdAxis.get2())
!= 1) { != 1) {
throw new IllegalArgumentException("Axis are overlapping/missing! " + 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.get0() * point.get0() + forFirstAxis.get1() * point.get1()
+ forFirstAxis.get1() * point.get1()
+ forFirstAxis.get2() * point.get2(), + forFirstAxis.get2() * point.get2(),
forSecondAxis.get0() * point.get0() forSecondAxis.get0() * point.get0() + forSecondAxis.get1() * point.get1()
+ forSecondAxis.get1() * point.get1()
+ forSecondAxis.get2() * point.get2(), + forSecondAxis.get2() * point.get2(),
forThirdAxis.get0() * point.get0() forThirdAxis.get0() * point.get0() + forThirdAxis.get1() * point.get1()
+ forThirdAxis.get1() * point.get1()
+ forThirdAxis.get2() * point.get2()); + forThirdAxis.get2() * point.get2());
} }
public Vec3Impl inverseTranslate(Vec3Impl point) { public Vec3Impl inverseTranslate(Vec3Impl point) {
return new Vec3Impl( return new Vec3Impl(
forFirstAxis.get0() * point.get0() forFirstAxis.get0() * point.get0() + forSecondAxis.get0() * point.get1()
+ forSecondAxis.get0() * point.get1()
+ forThirdAxis.get0() * point.get2(), + forThirdAxis.get0() * point.get2(),
forFirstAxis.get1() * point.get0() forFirstAxis.get1() * point.get0() + forSecondAxis.get1() * point.get1()
+ forSecondAxis.get1() * point.get1()
+ forThirdAxis.get1() * point.get2(), + forThirdAxis.get1() * point.get2(),
forFirstAxis.get2() * point.get0() forFirstAxis.get2() * point.get0() + forSecondAxis.get2() * point.get1()
+ forSecondAxis.get2() * point.get1()
+ forThirdAxis.get2() * point.get2()); + 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.get0() * point.xCoord + forFirstAxis.get1() * point.yCoord
+ forFirstAxis.get1() * point.yCoord
+ forFirstAxis.get2() * point.zCoord, + forFirstAxis.get2() * point.zCoord,
forSecondAxis.get0() * point.xCoord forSecondAxis.get0() * point.xCoord + forSecondAxis.get1() * point.yCoord
+ forSecondAxis.get1() * point.yCoord
+ forSecondAxis.get2() * point.zCoord, + forSecondAxis.get2() * point.zCoord,
forThirdAxis.get0() * point.xCoord forThirdAxis.get0() * point.xCoord + forThirdAxis.get1() * point.yCoord
+ forThirdAxis.get1() * point.yCoord
+ forThirdAxis.get2() * point.zCoord); + forThirdAxis.get2() * point.zCoord);
} }
public Vec3 inverseTranslate(Vec3 point) { public Vec3 inverseTranslate(Vec3 point) {
return Vec3.createVectorHelper( return Vec3.createVectorHelper(
forFirstAxis.get0() * point.xCoord forFirstAxis.get0() * point.xCoord + forSecondAxis.get0() * point.yCoord
+ forSecondAxis.get0() * point.yCoord
+ forThirdAxis.get0() * point.zCoord, + forThirdAxis.get0() * point.zCoord,
forFirstAxis.get1() * point.xCoord forFirstAxis.get1() * point.xCoord + forSecondAxis.get1() * point.yCoord
+ forSecondAxis.get1() * point.yCoord
+ forThirdAxis.get1() * point.zCoord, + forThirdAxis.get1() * point.zCoord,
forFirstAxis.get2() * point.xCoord forFirstAxis.get2() * point.xCoord + forSecondAxis.get2() * point.yCoord
+ forSecondAxis.get2() * point.yCoord
+ forThirdAxis.get2() * point.zCoord); + forThirdAxis.get2() * point.zCoord);
} }
@@ -3,23 +3,27 @@ package com.gtnewhorizon.structurelib.alignment.constructable;
import static com.gtnewhorizon.structurelib.util.MiscUtils.getTagKeys; import static com.gtnewhorizon.structurelib.util.MiscUtils.getTagKeys;
import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND; import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import java.util.Locale; import java.util.Locale;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.stream.Stream; import java.util.stream.Stream;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.common.util.Constants.NBT;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import com.gtnewhorizon.structurelib.StructureLibAPI;
/** /**
* Provide accessor methods for channel data from trigger item. * Provide accessor methods for channel data from trigger item.
* *
* <a href="{@docRoot}/overview-summary.html#channels">Channels section on overview</a> * <a href="{@docRoot}/overview-summary.html#channels">Channels section on overview</a>
*/ */
public class ChannelDataAccessor { public class ChannelDataAccessor {
private static final String SECONDARY_HINT_TAG = "channels"; private static final String SECONDARY_HINT_TAG = "channels";
private ChannelDataAccessor() {} private ChannelDataAccessor() {}
@@ -27,29 +31,25 @@ public class ChannelDataAccessor {
/** /**
* Return a trigger item that * Return a trigger item that
* <ul> * <ul>
* <li>Still carry all the subchannel in some unspecified ways</li> * <li>Still carry all the subchannel in some unspecified ways</li>
* <li>Have its master channel data replaced with specified subchannel's data</li> * <li>Have its master channel data replaced with specified subchannel's data</li>
* </ul> * </ul>
* *
* If given subchannel does not exist, masterStack may or may not be returned to save performance. * If given subchannel does not exist, masterStack may or may not be returned to save performance. Code within
* Code within StructureLib can depend on this behavior, you cannot. * 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 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 * @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. * Note: all channel identifiers are supposed to be lower case and not empty.
* @return a trigger item with new master channel * @return a trigger item with new master channel
*/ */
public static ItemStack withChannel(ItemStack masterStack, String channel) { public static ItemStack withChannel(ItemStack masterStack, String channel) {
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException(); if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
if (StructureLibAPI.isDebugEnabled() if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
&& !channel.toLowerCase(Locale.ROOT).equals(channel))
throw new IllegalArgumentException("Channel name can be lower case ONLY"); throw new IllegalArgumentException("Channel name can be lower case ONLY");
if (!masterStack.hasTagCompound() if (!masterStack.hasTagCompound() || !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|| !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND) || !masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).hasKey(channel, NBT.TAG_INT))
|| !masterStack return masterStack;
.stackTagCompound
.getCompoundTag(SECONDARY_HINT_TAG)
.hasKey(channel, NBT.TAG_INT)) return masterStack;
ItemStack ret = new ItemStack( ItemStack ret = new ItemStack(
masterStack.getItem(), masterStack.getItem(),
masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel), masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel),
@@ -60,6 +60,7 @@ public class ChannelDataAccessor {
/** /**
* Check if given trigger item contains any subchannel * Check if given trigger item contains any subchannel
*
* @param masterStack trigger stack to check * @param masterStack trigger stack to check
* @return true if contains any subchannel * @return true if contains any subchannel
*/ */
@@ -70,57 +71,47 @@ public class ChannelDataAccessor {
/** /**
* Check if given trigger item contains specified subchannel * Check if given trigger item contains specified subchannel
*
* @param masterStack trigger stack to check * @param masterStack trigger stack to check
* @param channel channel identifier. * @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
* Note: all channel identifiers are supposed to be lower case and not empty.
* @return true if contains specified subchannel * @return true if contains specified subchannel
*/ */
public static boolean hasSubChannel(ItemStack masterStack, String channel) { public static boolean hasSubChannel(ItemStack masterStack, String channel) {
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException(); if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
if (StructureLibAPI.isDebugEnabled() if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
&& !channel.toLowerCase(Locale.ROOT).equals(channel))
throw new IllegalArgumentException("Channel name can be lower case ONLY"); throw new IllegalArgumentException("Channel name can be lower case ONLY");
return !channel.isEmpty() return !channel.isEmpty() && masterStack.hasTagCompound()
&& masterStack.hasTagCompound()
&& masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND) && masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
&& masterStack && masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).hasKey(channel, NBT.TAG_INT);
.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. * Get the subchannel data from given trigger item. Will use master channel instead if not present.
*
* @param masterStack trigger stack to query from * @param masterStack trigger stack to query from
* @param channel channel identifier. * @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
* Note: all channel identifiers are supposed to be lower case and not empty.
* @return channel data * @return channel data
*/ */
public static int getChannelData(ItemStack masterStack, String channel) { public static int getChannelData(ItemStack masterStack, String channel) {
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException(); if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
if (StructureLibAPI.isDebugEnabled() if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
&& !channel.toLowerCase(Locale.ROOT).equals(channel))
throw new IllegalArgumentException("Channel name can be lower case ONLY"); throw new IllegalArgumentException("Channel name can be lower case ONLY");
if (!masterStack.hasTagCompound() if (!masterStack.hasTagCompound() || !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND)
|| !masterStack.stackTagCompound.hasKey(SECONDARY_HINT_TAG, TAG_COMPOUND) || !masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).hasKey(channel, NBT.TAG_INT))
|| !masterStack return masterStack.stackSize;
.stackTagCompound
.getCompoundTag(SECONDARY_HINT_TAG)
.hasKey(channel, NBT.TAG_INT)) return masterStack.stackSize;
return masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel); return masterStack.stackTagCompound.getCompoundTag(SECONDARY_HINT_TAG).getInteger(channel);
} }
/** /**
* Set the subchannel data on given trigger item * Set the subchannel data on given trigger item
*
* @param masterStack trigger stack to check * @param masterStack trigger stack to check
* @param channel channel identifier. * @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
* Note: all channel identifiers are supposed to be lower case and not empty. * @param data subchannel data. should always be a positive value
* @param data subchannel data. should always be a positive value
*/ */
public static void setChannelData(ItemStack masterStack, String channel, int data) { public static void setChannelData(ItemStack masterStack, String channel, int data) {
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException(); if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
if (StructureLibAPI.isDebugEnabled() if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
&& !channel.toLowerCase(Locale.ROOT).equals(channel))
throw new IllegalArgumentException("Channel name can be lower case ONLY"); throw new IllegalArgumentException("Channel name can be lower case ONLY");
if (data <= 0) throw new IllegalArgumentException(); if (data <= 0) throw new IllegalArgumentException();
if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound(); if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound();
@@ -131,14 +122,13 @@ public class ChannelDataAccessor {
/** /**
* Clear the given subchannel from given trigger item, if it exists * Clear the given subchannel from given trigger item, if it exists
*
* @param masterStack trigger stack to unset * @param masterStack trigger stack to unset
* @param channel channel identifier. * @param channel channel identifier. Note: all channel identifiers are supposed to be lower case and not empty.
* Note: all channel identifiers are supposed to be lower case and not empty.
*/ */
public static void unsetChannelData(ItemStack masterStack, String channel) { public static void unsetChannelData(ItemStack masterStack, String channel) {
if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException(); if (StringUtils.isEmpty(channel) || masterStack == null) throw new IllegalArgumentException();
if (StructureLibAPI.isDebugEnabled() if (StructureLibAPI.isDebugEnabled() && !channel.toLowerCase(Locale.ROOT).equals(channel))
&& !channel.toLowerCase(Locale.ROOT).equals(channel))
throw new IllegalArgumentException("Channel name can be lower case ONLY"); throw new IllegalArgumentException("Channel name can be lower case ONLY");
if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound(); if (masterStack.stackTagCompound == null) masterStack.stackTagCompound = new NBTTagCompound();
NBTTagCompound main = masterStack.stackTagCompound; NBTTagCompound main = masterStack.stackTagCompound;
@@ -151,6 +141,7 @@ public class ChannelDataAccessor {
/** /**
* Wipe all subchannel data on given trigger item * Wipe all subchannel data on given trigger item
*
* @param masterStack trigger stack to wipe * @param masterStack trigger stack to wipe
*/ */
public static void wipeChannelData(ItemStack masterStack) { public static void wipeChannelData(ItemStack masterStack) {
@@ -160,10 +151,11 @@ public class ChannelDataAccessor {
/** /**
* Iterate over all subchannel data on trigger stack. Does not include master channel!! * Iterate over all subchannel data on trigger stack. Does not include master channel!!
*
* @param masterStack trigger stack to check * @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 * @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 * mutation nor removal. Can still cause {@link java.util.ConcurrentModificationException} if underlying
* data is modified while stream is working. * channel data is modified while stream is working.
*/ */
public static Stream<Entry<String, Integer>> iterateChannelData(ItemStack masterStack) { public static Stream<Entry<String, Integer>> iterateChannelData(ItemStack masterStack) {
if (!hasSubChannel(masterStack)) return Stream.empty(); if (!hasSubChannel(masterStack)) return Stream.empty();
@@ -173,6 +165,7 @@ public class ChannelDataAccessor {
/** /**
* Get the number of subchannels present on given trigger item. Does not include master channel. * Get the number of subchannels present on given trigger item. Does not include master channel.
*
* @param masterStack trigger stack to query from * @param masterStack trigger stack to query from
* @return subchannel count * @return subchannel count
*/ */
@@ -1,12 +1,7 @@
package com.gtnewhorizon.structurelib.alignment.constructable; package com.gtnewhorizon.structurelib.alignment.constructable;
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 java.util.WeakHashMap; import java.util.WeakHashMap;
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;
@@ -16,6 +11,14 @@ 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 static final WeakHashMap<EntityPlayerMP, Long> lastUse = new WeakHashMap<>(); private static final WeakHashMap<EntityPlayerMP, Long> lastUse = new WeakHashMap<>();
@@ -23,16 +26,16 @@ public class ConstructableUtility {
private ConstructableUtility() {} private ConstructableUtility() {}
public static boolean handle( public static boolean handle(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ,
ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, int aSide) { 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( private static boolean handle0(ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ,
ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, int aSide) { 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;
@@ -43,9 +46,10 @@ public class ConstructableUtility {
long timePast = System.currentTimeMillis() - getLastUseMilis(aPlayer); long timePast = System.currentTimeMillis() - getLastUseMilis(aPlayer);
if (timePast < ConfigurationHandler.INSTANCE.getAutoPlaceInterval()) { if (timePast < ConfigurationHandler.INSTANCE.getAutoPlaceInterval()) {
aPlayer.addChatComponentMessage(new ChatComponentTranslation( aPlayer.addChatComponentMessage(
"item.structurelib.constructableTrigger.too_fast", new ChatComponentTranslation(
ConfigurationHandler.INSTANCE.getAutoPlaceInterval() - timePast)); "item.structurelib.constructableTrigger.too_fast",
ConfigurationHandler.INSTANCE.getAutoPlaceInterval() - timePast));
return true; return true;
} }
} else if (!StructureLib.isCurrentPlayer(aPlayer)) { } else if (!StructureLib.isCurrentPlayer(aPlayer)) {
@@ -58,8 +62,7 @@ public class ConstructableUtility {
else if (tTileEntity instanceof IConstructable) { else if (tTileEntity instanceof IConstructable) {
constructable = (IConstructable) tTileEntity; constructable = (IConstructable) tTileEntity;
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) { } else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
ExtendedFacing facing = tTileEntity instanceof IAlignment ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing()
? ((IAlignment) tTileEntity).getExtendedFacing()
: ExtendedFacing.of(ForgeDirection.getOrientation(aSide)); : ExtendedFacing.of(ForgeDirection.getOrientation(aSide));
constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass()) constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass())
.toConstructable(tTileEntity, facing); .toConstructable(tTileEntity, facing);
@@ -74,12 +77,10 @@ public class ConstructableUtility {
if (aPlayer.capabilities.isCreativeMode) { if (aPlayer.capabilities.isCreativeMode) {
constructable.construct(aStack, false); constructable.construct(aStack, false);
} else if (constructable instanceof ISurvivalConstructable) { } else if (constructable instanceof ISurvivalConstructable) {
int built = ((ISurvivalConstructable) constructable) int built = ((ISurvivalConstructable) constructable).survivalConstruct(
.survivalConstruct( aStack,
aStack, ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(), ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
IItemSource.fromPlayer(playerMP),
playerMP);
if (built > 0) { if (built > 0) {
playerMP.addChatMessage(new ChatComponentTranslation("structurelib.autoplace.built_stat", built)); playerMP.addChatMessage(new ChatComponentTranslation("structurelib.autoplace.built_stat", built));
} else if (built == -1) { } else if (built == -1) {
@@ -1,15 +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,6 +6,7 @@ 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.
*/ */
@@ -1,19 +1,23 @@
package com.gtnewhorizon.structurelib.alignment.constructable; package com.gtnewhorizon.structurelib.alignment.constructable;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IItemSource;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.HashMap; import java.util.HashMap;
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.world.World; import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IItemSource;
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<>();
/** /**
@@ -36,16 +40,13 @@ 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, IItemSource, EntityPlayerMP, java.util.function.Consumer)} * Construct the structure using
* {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, java.util.function.Consumer)}
*
* @return -1 if done, a helping pointer * @return -1 if done, a helping pointer
*/ */
default int survivalConstruct( default int survivalConstruct(ItemStack stackSize, int elementBudge, IItemSource source,
ItemStack stackSize, EntityPlayerMP actorProfile, T tileEntity, ExtendedFacing aSide) {
int elementBudge,
IItemSource source,
EntityPlayerMP actorProfile,
T tileEntity,
ExtendedFacing aSide) {
return -1; return -1;
} }
@@ -58,6 +59,7 @@ public interface IMultiblockInfoContainer<T> {
*/ */
default IConstructable toConstructable(T tileEntity, ExtendedFacing aSide) { default IConstructable toConstructable(T tileEntity, ExtendedFacing aSide) {
return new IConstructable() { return new IConstructable() {
@Override @Override
public void construct(ItemStack stackSize, boolean hintsOnly) { public void construct(ItemStack stackSize, boolean hintsOnly) {
IMultiblockInfoContainer.this.construct(stackSize, hintsOnly, tileEntity, aSide); IMultiblockInfoContainer.this.construct(stackSize, hintsOnly, tileEntity, aSide);
@@ -70,12 +72,13 @@ public interface IMultiblockInfoContainer<T> {
}; };
} }
static <T> ISurvivalConstructable toConstructable( static <T> ISurvivalConstructable toConstructable(IMultiblockInfoContainer<T> thiz, T tileEntity,
IMultiblockInfoContainer<T> thiz, T tileEntity, ExtendedFacing aSide) { ExtendedFacing aSide) {
return new ISurvivalConstructable() { return new ISurvivalConstructable() {
@Override @Override
public int survivalConstruct( public int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source,
ItemStack stackSize, int elementBudget, IItemSource source, EntityPlayerMP actor) { EntityPlayerMP actor) {
return thiz.survivalConstruct(stackSize, elementBudget, source, actor, tileEntity, aSide); return thiz.survivalConstruct(stackSize, elementBudget, source, actor, tileEntity, aSide);
} }
@@ -1,17 +1,85 @@
package com.gtnewhorizon.structurelib.alignment.constructable; package com.gtnewhorizon.structurelib.alignment.constructable;
import com.gtnewhorizon.structurelib.structure.IItemSource; 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.world.World; 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 { public interface ISurvivalConstructable extends IConstructable {
/** /**
* Construct the structure using {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, java.util.function.Consumer)} * 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. You can tune this up a bit if the structure is too big, but * @param elementBudget The server configured element budget. The implementor can choose to tune this up a bit if
* generally should not be a 4 digits number to not overwhelm the server * 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 * @return -1 if done, otherwise number of elements placed this round
*/ */
int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source, EntityPlayerMP actor); @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<>();
} }
@@ -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),
@@ -14,10 +21,12 @@ 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() {
@@ -31,4 +40,8 @@ public enum Direction {
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);
}
} }
@@ -6,16 +6,19 @@ import static java.lang.Math.abs;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.stream.Collectors.*; import static java.util.stream.Collectors.*;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.structurelib.alignment.IAlignment;
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import java.util.*; import java.util.*;
import net.minecraft.util.StatCollector; import net.minecraft.util.StatCollector;
import net.minecraft.util.Vec3; import net.minecraft.util.Vec3;
import net.minecraftforge.common.util.ForgeDirection; 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"),
@@ -119,31 +122,31 @@ public enum ExtendedFacing {
public static final int STATES_COUNT = VALUES.length; public static final int STATES_COUNT = VALUES.length;
static { static {
stream(values()) stream(values()).forEach(
.forEach(extendedFacing -> extendedFacing -> FOR_FACING.compute(extendedFacing.direction, ((forgeDirection, extendedFacings) -> {
FOR_FACING.compute(extendedFacing.direction, ((forgeDirection, extendedFacings) -> { if (extendedFacings == null) {
if (extendedFacings == null) { extendedFacings = new ArrayList<>();
extendedFacings = new ArrayList<>(); }
} extendedFacings.add(extendedFacing);
extendedFacings.add(extendedFacing); return extendedFacings;
return extendedFacings; })));
})));
} }
private static final Map<String, ExtendedFacing> NAME_LOOKUP = private static final Map<String, ExtendedFacing> NAME_LOOKUP = stream(VALUES)
stream(VALUES).collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing)); .collect(toMap(ExtendedFacing::getName2, (extendedFacing) -> extendedFacing));
private static final EnumMap<ForgeDirection, ImmutableSet<ExtendedFacing>> LOOKUP_BY_DIRECTION = stream(VALUES) private static final EnumMap<ForgeDirection, ImmutableSet<ExtendedFacing>> LOOKUP_BY_DIRECTION = stream(VALUES)
.collect(groupingBy( .collect(
ExtendedFacing::getDirection, groupingBy(
() -> new EnumMap<>(ForgeDirection.class), ExtendedFacing::getDirection,
collectingAndThen(toSet(), ImmutableSet::copyOf))); () -> new EnumMap<>(ForgeDirection.class),
private static final EnumMap<Rotation, ImmutableSet<ExtendedFacing>> LOOKUP_BY_ROTATION = stream(VALUES) collectingAndThen(toSet(), ImmutableSet::copyOf)));
.collect(groupingBy( private static final EnumMap<Rotation, ImmutableSet<ExtendedFacing>> LOOKUP_BY_ROTATION = stream(VALUES).collect(
groupingBy(
ExtendedFacing::getRotation, ExtendedFacing::getRotation,
() -> new EnumMap<>(Rotation.class), () -> new EnumMap<>(Rotation.class),
collectingAndThen(toSet(), ImmutableSet::copyOf))); collectingAndThen(toSet(), ImmutableSet::copyOf)));
private static final EnumMap<Flip, ImmutableSet<ExtendedFacing>> LOOKUP_BY_FLIP = stream(VALUES) private static final EnumMap<Flip, ImmutableSet<ExtendedFacing>> LOOKUP_BY_FLIP = stream(VALUES).collect(
.collect(groupingBy( groupingBy(
ExtendedFacing::getFlip, ExtendedFacing::getFlip,
() -> new EnumMap<>(Flip.class), () -> new EnumMap<>(Flip.class),
collectingAndThen(toSet(), ImmutableSet::copyOf))); collectingAndThen(toSet(), ImmutableSet::copyOf)));
@@ -333,7 +336,7 @@ 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 {@code L-->R,U-->D,F-->B}) * @param abcOffset A,B,C offset (facing relative {@code L-->R,U-->D,F-->B})
* @return X, Y, Z offset in world * @return X, Y, Z offset in world
@@ -355,10 +358,32 @@ public enum ExtendedFacing {
} }
/** /**
* Translates world offset to relative front facing offset * do note the ABC coordinate system is (X, -Y, Z).
*/
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 * @param xyzOffset X,Y,Z offset in world
* @return A, B, C offset (facing relative {@code L-->R,U-->D,F-->B}) * @return A, B, C offset (facing relative {@code L-->R,U-->D,F-->B})
*/ */
public Vec3 getOffsetABC(Vec3 xyzOffset) { public Vec3 getOffsetABC(Vec3 xyzOffset) {
return integerAxisSwap.translate(xyzOffset); return integerAxisSwap.translate(xyzOffset);
@@ -7,9 +7,11 @@ import static java.util.stream.Collectors.toMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import javax.annotation.Nonnull; 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"),
@@ -7,9 +7,11 @@ import static java.util.stream.Collectors.toMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import javax.annotation.Nonnull; 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,8 +21,8 @@ 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 = private static final Map<String, Rotation> NAME_LOOKUP = stream(VALUES)
stream(VALUES).collect(toMap(Rotation::getName2, (rotation) -> rotation)); .collect(toMap(Rotation::getName2, (rotation) -> rotation));
Rotation(int oppositeIn, String nameIn) { Rotation(int oppositeIn, String nameIn) {
this.opposite = oppositeIn; this.opposite = oppositeIn;
@@ -1,7 +1,7 @@
/** /**
* This package provide a framework for rotating a given block, or a multiblock structure. * This package provide a framework for rotating a given block, or a multiblock structure.
* <p> * <p>
* All public stuff in this package and its subpackages are considered part of the public API. * All public stuff in this package and its subpackages are considered part of the public API. Backwards compatibility
* Backwards compatibility is maintained to the maximum extend possible. * is maintained to the maximum extend possible.
*/ */
package com.gtnewhorizon.structurelib.alignment; package com.gtnewhorizon.structurelib.alignment;
@@ -1,10 +1,7 @@
package com.gtnewhorizon.structurelib.block; package com.gtnewhorizon.structurelib.block;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.List; import java.util.List;
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;
@@ -14,7 +11,14 @@ 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 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]; private static final IIcon[] hint = new IIcon[16];
public BlockHint() { public BlockHint() {
@@ -1,10 +1,9 @@
package com.gtnewhorizon.structurelib.command; package com.gtnewhorizon.structurelib.command;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.minecraft.command.CommandBase; import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender; import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException; import net.minecraft.command.WrongUsageException;
@@ -13,7 +12,11 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.ChatComponentTranslation;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
public class CommandConfigureChannels extends CommandBase { public class CommandConfigureChannels extends CommandBase {
@Override @Override
public String getCommandName() { public String getCommandName() {
return "sl_channel"; return "sl_channel";
@@ -58,13 +61,12 @@ public class CommandConfigureChannels extends CommandBase {
if (!ChannelDataAccessor.hasSubChannel(heldItem)) { if (!ChannelDataAccessor.hasSubChannel(heldItem)) {
sender.addChatMessage(new ChatComponentText("No subchannel")); sender.addChatMessage(new ChatComponentText("No subchannel"));
} else { } else {
sender.addChatMessage(new ChatComponentTranslation( sender.addChatMessage(
"item.structurelib.constructableTrigger.desc.lshift.0", new ChatComponentTranslation(
ChannelDataAccessor.countChannelData(heldItem))); "item.structurelib.constructableTrigger.desc.lshift.0",
ChannelDataAccessor.iterateChannelData(heldItem) ChannelDataAccessor.countChannelData(heldItem)));
.map(e -> e.getKey() + ": " + e.getValue()) ChannelDataAccessor.iterateChannelData(heldItem).map(e -> e.getKey() + ": " + e.getValue())
.map(ChatComponentText::new) .map(ChatComponentText::new).forEach(player::addChatMessage);
.forEach(player::addChatMessage);
} }
return; return;
case "wipe": case "wipe":
@@ -73,13 +75,11 @@ public class CommandConfigureChannels extends CommandBase {
case "get": case "get":
case "unset": case "unset":
if (args.length < 2) throw new WrongUsageException(getCommandUsage(sender)); if (args.length < 2) throw new WrongUsageException(getCommandUsage(sender));
channel = channel = args.length == 2 ? args[1] : Arrays.stream(args).skip(1).collect(Collectors.joining());
args.length == 2 ? args[1] : Arrays.stream(args).skip(1).collect(Collectors.joining());
break; break;
case "set": case "set":
if (args.length < 3) throw new WrongUsageException(getCommandUsage(sender)); if (args.length < 3) throw new WrongUsageException(getCommandUsage(sender));
channel = args.length == 3 channel = args.length == 3 ? args[1]
? args[1]
: Arrays.stream(args).skip(1).limit(args.length - 2).collect(Collectors.joining()); : Arrays.stream(args).skip(1).limit(args.length - 2).collect(Collectors.joining());
break; break;
default: default:
@@ -88,9 +88,9 @@ public class CommandConfigureChannels extends CommandBase {
switch (args[0]) { switch (args[0]) {
case "get": case "get":
if (ChannelDataAccessor.hasSubChannel(heldItem, channel)) if (ChannelDataAccessor.hasSubChannel(heldItem, channel)) sender.addChatMessage(
sender.addChatMessage(new ChatComponentText( new ChatComponentText(
channel + " value: " + ChannelDataAccessor.getChannelData(heldItem, channel))); channel + " value: " + ChannelDataAccessor.getChannelData(heldItem, channel)));
else sender.addChatMessage(new ChatComponentText(channel + " value: N/A")); else sender.addChatMessage(new ChatComponentText(channel + " value: N/A"));
break; break;
case "clear": case "clear":
@@ -1,20 +1,16 @@
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_, public WeightlessParticleFX(World p_i1205_1_, double p_i1205_2_, double p_i1205_4_, double p_i1205_6_,
double p_i1205_2_, double p_i1205_8_, double p_i1205_10_, double p_i1205_12_) {
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);
@@ -1,12 +1,10 @@
package com.gtnewhorizon.structurelib.gui; package com.gtnewhorizon.structurelib.gui;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.gui.GuiTextField;
@@ -15,12 +13,18 @@ import net.minecraft.client.resources.I18n;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse; import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.gui.GuiScrollableList.IGuiScreen;
public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen { public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen {
private static final int KEY_MAX_WIDTH = 50; private static final int KEY_MAX_WIDTH = 50;
private final ItemStack trigger; private final ItemStack trigger;
private final GuiChannelsList list; private final GuiChannelsList list;
@@ -48,6 +52,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
guiTop = (this.height - this.getYSize()) / 2; guiTop = (this.height - this.getYSize()) / 2;
key = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 119, 151 + 12 - 45, 12) { key = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 119, 151 + 12 - 45, 12) {
@Override @Override
public void writeText(String text) { public void writeText(String text) {
// force lower case // force lower case
@@ -58,8 +63,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
@Override @Override
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) { 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_); super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
boolean flag = p_146192_1_ >= this.xPosition boolean flag = p_146192_1_ >= this.xPosition && p_146192_1_ < this.xPosition + this.width
&& p_146192_1_ < this.xPosition + this.width
&& p_146192_2_ >= this.yPosition && p_146192_2_ >= this.yPosition
&& p_146192_2_ < this.yPosition + this.height; && p_146192_2_ < this.yPosition + this.height;
if (flag && p_146192_3_ == 1) { if (flag && p_146192_3_ == 1) {
@@ -75,6 +79,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
} }
}; };
value = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 139, 151 + 12 - 45, 12) { value = new GuiTextField(fontRendererObj, guiLeft + 45, guiTop + 139, 151 + 12 - 45, 12) {
@Override @Override
public void writeText(String text) { public void writeText(String text) {
// ignore write requests containing non digit characters. // ignore write requests containing non digit characters.
@@ -104,8 +109,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
@Override @Override
public void mouseClicked(int p_146192_1_, int p_146192_2_, int p_146192_3_) { 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_); super.mouseClicked(p_146192_1_, p_146192_2_, p_146192_3_);
boolean flag = p_146192_1_ >= this.xPosition boolean flag = p_146192_1_ >= this.xPosition && p_146192_1_ < this.xPosition + this.width
&& p_146192_1_ < this.xPosition + this.width
&& p_146192_2_ >= this.yPosition && p_146192_2_ >= this.yPosition
&& p_146192_2_ < this.yPosition + this.height; && p_146192_2_ < this.yPosition + this.height;
if (flag && p_146192_3_ == 1) { if (flag && p_146192_3_ == 1) {
@@ -122,22 +126,30 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
list.onGuiInit(this); list.onGuiInit(this);
addButton(new GuiButton( addButton(
0, guiLeft + 12, guiTop + 157, 47, 20, I18n.format("item.structurelib.constructableTrigger.gui.add"))); new GuiButton(
addButton(new GuiButton( 0,
1, guiLeft + 12,
guiLeft + 65, guiTop + 157,
guiTop + 157, 47,
47, 20,
20, I18n.format("item.structurelib.constructableTrigger.gui.add")));
I18n.format("item.structurelib.constructableTrigger.gui.unset"))); addButton(
addButton(new GuiButton( new GuiButton(
2, 1,
guiLeft + 118, guiLeft + 65,
guiTop + 157, guiTop + 157,
47, 47,
20, 20,
I18n.format("item.structurelib.constructableTrigger.gui.wipe"))); I18n.format("item.structurelib.constructableTrigger.gui.unset")));
addButton(
new GuiButton(
2,
guiLeft + 118,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
updateButtons(); updateButtons();
} }
@@ -236,9 +248,9 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
private void updateButtons() { private void updateButtons() {
// this will be called from setText of key and value. NEVER UPDATE THE VALUE OF THESE HERE OR GET A // this will be called from setText of key and value. NEVER UPDATE THE VALUE OF THESE HERE OR GET A
// STACKOVERFLOW! // STACKOVERFLOW!
boolean existing = ChannelDataAccessor.hasSubChannel(trigger, key.getText()); String keyText = key.getText();
getButtonList().get(0).displayString = existing boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
? I18n.format("item.structurelib.constructableTrigger.gui.set") getButtonList().get(0).displayString = existing ? I18n.format("item.structurelib.constructableTrigger.gui.set")
: I18n.format("item.structurelib.constructableTrigger.gui.add"); : I18n.format("item.structurelib.constructableTrigger.gui.add");
getButtonList().get(0).enabled = !StringUtils.isBlank(value.getText()); getButtonList().get(0).enabled = !StringUtils.isBlank(value.getText());
getButtonList().get(1).enabled = existing && !StringUtils.isBlank(value.getText()); getButtonList().get(1).enabled = existing && !StringUtils.isBlank(value.getText());
@@ -299,14 +311,21 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
super.drawScreen(mX, mY, partialTick); super.drawScreen(mX, mY, partialTick);
list.drawScreen(mX, mY, partialTick); list.drawScreen(mX, mY, partialTick);
fontRendererObj.drawString( fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.key"), guiLeft + 12, guiTop + 122, 0); I18n.format("item.structurelib.constructableTrigger.gui.key"),
guiLeft + 12,
guiTop + 122,
0);
key.drawTextBox(); key.drawTextBox();
fontRendererObj.drawString( fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.value"), guiLeft + 12, guiTop + 142, 0); I18n.format("item.structurelib.constructableTrigger.gui.value"),
guiLeft + 12,
guiTop + 142,
0);
value.drawTextBox(); value.drawTextBox();
} }
private class GuiChannelsList extends GuiScrollableList<Entry<String, Integer>> { private class GuiChannelsList extends GuiScrollableList<Entry<String, Integer>> {
private List<Entry<String, Integer>> cache; private List<Entry<String, Integer>> cache;
public GuiChannelsList(int width, int height, int originX, int originY, int slotHeight) { public GuiChannelsList(int width, int height, int originX, int originY, int slotHeight) {
@@ -315,11 +334,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
@Override @Override
public Entry<String, Integer> getElementAt(int index) { public Entry<String, Integer> getElementAt(int index) {
return ChannelDataAccessor.iterateChannelData(trigger) return ChannelDataAccessor.iterateChannelData(trigger).sorted().skip(index).findFirst().orElse(null);
.sorted()
.skip(index)
.findFirst()
.orElse(null);
} }
@Override @Override
@@ -329,8 +344,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
@Override @Override
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) { protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
if (mXRelative >= margin + 1 if (mXRelative >= margin + 1 && mXRelative <= margin + 5
&& mXRelative <= margin + 5
&& mYRelative >= margin / 2 + 1 && mYRelative >= margin / 2 + 1
&& mYRelative <= margin / 2 + 5) { && mYRelative <= margin / 2 + 5) {
Entry<String, Integer> e = getElementAt(elementIndex); Entry<String, Integer> e = getElementAt(elementIndex);
@@ -348,8 +362,8 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
} }
@Override @Override
protected void drawElement( protected void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
int elementIndex, int x, int y, int height, Tessellator tessellator, boolean isHovering) { boolean isHovering) {
if (elementIndex < 0 || elementIndex >= cache.size()) { if (elementIndex < 0 || elementIndex >= cache.size()) {
return; return;
} }
@@ -381,8 +395,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
// TODO optimize this to not create a billion string, or maybe cache this // TODO optimize this to not create a billion string, or maybe cache this
String s = fontRendererObj.trimStringToWidth(e, keyMaxWidth); String s = fontRendererObj.trimStringToWidth(e, keyMaxWidth);
if (s.length() != e.length()) { if (s.length() != e.length()) {
StringBuilder buffer = StringBuilder buffer = new StringBuilder(s).deleteCharAt(s.length() - 1).append("...");
new StringBuilder(s).deleteCharAt(s.length() - 1).append("...");
while ((s = fontRendererObj.trimStringToWidth(buffer.toString(), keyMaxWidth)).length() while ((s = fontRendererObj.trimStringToWidth(buffer.toString(), keyMaxWidth)).length()
!= buffer.length()) != buffer.length())
// drop last original char // drop last original char
@@ -390,9 +403,7 @@ public class GuiScreenConfigureChannels extends GuiScreen implements IGuiScreen
do { do {
buffer.append('.'); buffer.append('.');
} while (fontRendererObj.getStringWidth(buffer.toString()) <= keyMaxWidth); } while (fontRendererObj.getStringWidth(buffer.toString()) <= keyMaxWidth);
s = buffer.deleteCharAt(s.length() - 1) s = buffer.deleteCharAt(s.length() - 1).insert(s.length() - 3, EnumChatFormatting.GRAY).toString();
.insert(s.length() - 3, EnumChatFormatting.GRAY)
.toString();
} }
return s; return s;
} }
@@ -2,37 +2,40 @@ package com.gtnewhorizon.structurelib.gui;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.Tessellator;
import org.lwjgl.input.Mouse; import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
/** /**
* Shamelessly stolen from EnderCore, with some amount of edit to fit in one file. Original code from CrazyPants. * Shamelessly stolen from EnderCore, with some amount of edit to fit in one file. Original code from CrazyPants.
* <p> * <p>
* As the code has been released into public domain, it is now LGPL as part of this library as a whole. * 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
* You can go to EnderCore's repository to obtain a public domain copy though * EnderCore's repository to obtain a public domain copy though
* *
* Major edits * Major edits
* <ul> * <ul>
* <li>Separate dwheel handling to handleDWheel. The next event calls often cause the mouse click event to * <li>Separate dwheel handling to handleDWheel. The next event calls often cause the mouse click event to not fire on
* not fire on other widgets</li> * other widgets</li>
* <li>scrollbar are now rendered within width</li> * <li>scrollbar are now rendered within width</li>
* <li>added prepareDrawElements</li> * <li>added prepareDrawElements</li>
* </ul> * </ul>
* *
* @author CrazyPants * @author CrazyPants
* @author glee8e * @author glee8e
*/ */
public abstract class GuiScrollableList<T> { public abstract class GuiScrollableList<T> {
protected final int slotHeight; protected final int slotHeight;
private final Minecraft mc = Minecraft.getMinecraft(); private final Minecraft mc = Minecraft.getMinecraft();
protected int originX; protected final int originX;
protected int originY; protected final int originY;
protected int width; protected final int width;
protected int height; protected final int height;
protected int minY; protected int minY;
protected int maxY; protected int maxY;
protected int minX; protected int minX;
@@ -41,7 +44,7 @@ public abstract class GuiScrollableList<T> {
protected int mouseY; protected int mouseY;
protected int selectedIndex = -1; protected int selectedIndex = -1;
protected int margin = 4; protected int margin = 4;
protected List<ListSelectionListener<T>> listeners = new CopyOnWriteArrayList<>(); protected final List<ListSelectionListener<T>> listeners = new CopyOnWriteArrayList<>();
private int scrollUpButtonID; private int scrollUpButtonID;
private int scrollDownButtonID; private int scrollDownButtonID;
private float initialClickY = -2.0F; private float initialClickY = -2.0F;
@@ -113,8 +116,8 @@ public abstract class GuiScrollableList<T> {
public abstract int getNumElements(); public abstract int getNumElements();
protected abstract void drawElement( protected abstract void drawElement(int elementIndex, int x, int y, int height, Tessellator tessellator,
int elementIndex, int x, int y, int height, Tessellator tessellator, boolean isHovering); boolean isHovering);
protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) { protected boolean elementClicked(int elementIndex, boolean doubleClick, int mXRelative, int mYRelative) {
return true; return true;
@@ -153,11 +156,11 @@ public abstract class GuiScrollableList<T> {
public void actionPerformed(GuiButton b) { public void actionPerformed(GuiButton b) {
if (b.enabled) { if (b.enabled) {
if (b.id == scrollUpButtonID) { if (b.id == scrollUpButtonID) {
amountScrolled -= slotHeight * 2 / 3; amountScrolled -= slotHeight * 2f / 3;
initialClickY = -2.0F; initialClickY = -2.0F;
clampScrollToBounds(); clampScrollToBounds();
} else if (b.id == scrollDownButtonID) { } else if (b.id == scrollDownButtonID) {
amountScrolled += slotHeight * 2 / 3; amountScrolled += slotHeight * 2f / 3;
initialClickY = -2.0F; initialClickY = -2.0F;
clampScrollToBounds(); clampScrollToBounds();
} }
@@ -165,8 +168,7 @@ public abstract class GuiScrollableList<T> {
} }
/** /**
* draws the slot to the screen, pass in mouse's current x and y and partial * draws the slot to the screen, pass in mouse's current x and y and partial ticks
* ticks
*/ */
public void drawScreen(int mX, int mY, float partialTick) { public void drawScreen(int mX, int mY, float partialTick) {
this.mouseX = mX; this.mouseX = mX;
@@ -325,9 +327,9 @@ public abstract class GuiScrollableList<T> {
public void handleDWheel(int mouseWheelDelta) { public void handleDWheel(int mouseWheelDelta) {
if (!Mouse.isButtonDown(0)) { if (!Mouse.isButtonDown(0)) {
if (mouseWheelDelta > 0) { if (mouseWheelDelta > 0) {
amountScrolled -= slotHeight / 2; amountScrolled -= slotHeight / 2f;
} else /* if (mouseWheelDelta < 0) */ { } else /* if (mouseWheelDelta < 0) */ {
amountScrolled += slotHeight / 2; amountScrolled += slotHeight / 2f;
} }
} }
} }
@@ -343,15 +345,17 @@ public abstract class GuiScrollableList<T> {
int y = mouseY - minY + (int) amountScrolled - margin; int y = mouseY - minY + (int) amountScrolled - margin;
int mouseOverElement = y / slotHeight; int mouseOverElement = y / slotHeight;
if (mouseX >= minX if (mouseX >= minX && mouseX <= maxX
&& mouseX <= maxX
&& mouseOverElement >= 0 && mouseOverElement >= 0
&& y >= 0 && y >= 0
&& mouseOverElement < getNumElements()) { && mouseOverElement < getNumElements()) {
boolean doubleClick = boolean doubleClick = mouseOverElement == selectedIndex
mouseOverElement == selectedIndex && Minecraft.getSystemTime() - lastClickedTime < 250L; && Minecraft.getSystemTime() - lastClickedTime < 250L;
if (elementClicked( if (elementClicked(
mouseOverElement, doubleClick, mouseX - minX, y - slotHeight * mouseOverElement)) { mouseOverElement,
doubleClick,
mouseX - minX,
y - slotHeight * mouseOverElement)) {
setSelection(mouseOverElement); setSelection(mouseOverElement);
lastClickedTime = Minecraft.getSystemTime(); lastClickedTime = Minecraft.getSystemTime();
} }
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.item;
import static net.minecraft.util.StatCollector.translateToLocal; import static net.minecraft.util.StatCollector.translateToLocal;
import java.util.List; 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;
@@ -11,6 +12,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.EnumChatFormatting;
public class ItemBlockHint extends ItemBlock { public class ItemBlockHint extends ItemBlock {
public ItemBlockHint(Block p_i45328_1_) { public ItemBlockHint(Block p_i45328_1_) {
super(p_i45328_1_); super(p_i45328_1_);
setMaxDamage(0); setMaxDamage(0);
@@ -35,33 +37,35 @@ public class ItemBlockHint extends ItemBlock {
case 9: case 9:
case 10: case 10:
case 11: case 11:
aList.add(EnumChatFormatting.AQUA.toString() aList.add(
+ EnumChatFormatting.BOLD EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
+ translateToLocal("structurelib.blockhint.desc.1")); // Placeholder for a certain group. + translateToLocal("structurelib.blockhint.desc.1")); // Placeholder for a certain
// group.
break; break;
case 12: case 12:
aList.add(EnumChatFormatting.AQUA.toString() aList.add(
+ EnumChatFormatting.BOLD EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
+ translateToLocal("structurelib.blockhint.desc.2")); // General placeholder. + translateToLocal("structurelib.blockhint.desc.2")); // General placeholder.
break; break;
case 13: case 13:
aList.add(EnumChatFormatting.AQUA.toString() aList.add(
+ EnumChatFormatting.BOLD EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
+ translateToLocal("structurelib.blockhint.desc.3")); // Make sure it contains Air material. + translateToLocal("structurelib.blockhint.desc.3")); // Make sure it contains Air
// material.
break; break;
case 14: case 14:
aList.add(EnumChatFormatting.AQUA.toString() aList.add(
+ EnumChatFormatting.BOLD EnumChatFormatting.AQUA.toString() + EnumChatFormatting.BOLD
+ translateToLocal( + translateToLocal("structurelib.blockhint.desc.4")); // Make sure it does not contain
"structurelib.blockhint.desc.4")); // Make sure it does not contain Air material. // Air material.
break; break;
case 15: case 15:
aList.add(EnumChatFormatting.BLUE.toString() aList.add(EnumChatFormatting.BLUE + translateToLocal("structurelib.blockhint.desc.5")); // ERROR, what
+ translateToLocal("structurelib.blockhint.desc.5")); // ERROR, what did u expect? // did u expect?
break; break;
default: // WTF? default: // WTF?
aList.add("Damn son where did you get that!?"); aList.add("Damn son where did you get that!?");
aList.add(EnumChatFormatting.BLUE.toString() + "From outer space... I guess..."); aList.add(EnumChatFormatting.BLUE + "From outer space... I guess...");
} }
} }
@@ -5,17 +5,21 @@ import static net.minecraft.util.EnumChatFormatting.BLUE;
import static net.minecraft.util.StatCollector.translateToLocal; import static net.minecraft.util.StatCollector.translateToLocal;
import static net.minecraft.util.StatCollector.translateToLocalFormatted; import static net.minecraft.util.StatCollector.translateToLocalFormatted;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
import java.util.List; 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 org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtility;
public class ItemConstructableTrigger extends Item { public class ItemConstructableTrigger extends Item {
public ItemConstructableTrigger() { public ItemConstructableTrigger() {
setUnlocalizedName("structurelib.constructableTrigger"); setUnlocalizedName("structurelib.constructableTrigger");
setTextureName(MOD_ID + ":itemConstructableTrigger"); setTextureName(MOD_ID + ":itemConstructableTrigger");
@@ -30,17 +34,8 @@ public class ItemConstructableTrigger extends Item {
} }
@Override @Override
public boolean onItemUseFirst( public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
ItemStack stack, float hitX, float hitY, float hitZ) {
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);
} }
@@ -48,27 +43,25 @@ public class ItemConstructableTrigger extends Item {
@Override @Override
public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) { public void addInformation(ItemStack aStack, EntityPlayer ep, List aList, boolean boo) {
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) { if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
aList.add(translateToLocalFormatted( aList.add(
"item.structurelib.constructableTrigger.desc.lshift.0", translateToLocalFormatted(
ChannelDataAccessor.countChannelData(aStack))); "item.structurelib.constructableTrigger.desc.lshift.0",
ChannelDataAccessor.iterateChannelData(aStack) ChannelDataAccessor.countChannelData(aStack)));
.map(e -> e.getKey() + ": " + e.getValue()) ChannelDataAccessor.iterateChannelData(aStack).map(e -> e.getKey() + ": " + e.getValue())
.forEach(aList::add); .forEach(aList::add);
} else { } else {
aList.add(translateToLocal( aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.0")); // Triggers Constructable
"item.structurelib.constructableTrigger.desc.0")); // Triggers Constructable Interface // Interface
aList.add(BLUE aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.1")); // Shows multiblock
+ translateToLocal( // construction
"item.structurelib.constructableTrigger.desc.1")); // Shows multiblock construction details, // details,
aList.add(BLUE aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.2")); // just Use on a
+ translateToLocal( // multiblock
"item.structurelib.constructableTrigger.desc.2")); // just Use on a multiblock controller. // controller.
aList.add(BLUE aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.3")); // (Sneak Use in
+ translateToLocal( // creative to build)
"item.structurelib.constructableTrigger.desc.3")); // (Sneak Use in creative to build) aList.add(BLUE + translateToLocal("item.structurelib.constructableTrigger.desc.4")); // Quantity affects
aList.add(BLUE // tier/mode/type
+ translateToLocal(
"item.structurelib.constructableTrigger.desc.4")); // Quantity affects tier/mode/type
if (ChannelDataAccessor.hasSubChannel(aStack)) if (ChannelDataAccessor.hasSubChannel(aStack))
aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.5")); aList.add(translateToLocal("item.structurelib.constructableTrigger.desc.5"));
} }
@@ -3,18 +3,22 @@ package com.gtnewhorizon.structurelib.item;
import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID; import static com.gtnewhorizon.structurelib.StructureLibAPI.MOD_ID;
import static net.minecraft.util.StatCollector.translateToLocal; import static net.minecraft.util.StatCollector.translateToLocal;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.AlignmentUtility;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.List; 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 com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.alignment.AlignmentUtility;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class ItemFrontRotationTool extends Item { public class ItemFrontRotationTool extends Item {
public ItemFrontRotationTool() { public ItemFrontRotationTool() {
setMaxStackSize(1); setMaxStackSize(1);
setUnlocalizedName("structurelib.frontRotationTool"); setUnlocalizedName("structurelib.frontRotationTool");
@@ -23,17 +27,8 @@ public class ItemFrontRotationTool extends Item {
} }
@Override @Override
public boolean onItemUseFirst( public boolean onItemUseFirst(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
ItemStack stack, float hitX, float hitY, float hitZ) {
EntityPlayer player,
World world,
int x,
int y,
int z,
int side,
float hitX,
float hitY,
float hitZ) {
return AlignmentUtility.handle(player, world, x, y, z); return AlignmentUtility.handle(player, world, x, y, z);
} }
@@ -41,9 +36,13 @@ public class ItemFrontRotationTool extends Item {
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public void addInformation(ItemStack p_77624_1_, EntityPlayer p_77624_2_, List aList, boolean p_77624_4_) { public void addInformation(ItemStack p_77624_1_, EntityPlayer p_77624_2_, List aList, boolean p_77624_4_) {
aList.add(translateToLocal("item.structurelib.frontRotationTool.desc.0")); // Triggers Front Rotation Interface aList.add(translateToLocal("item.structurelib.frontRotationTool.desc.0")); // Triggers Front Rotation Interface
aList.add(EnumChatFormatting.BLUE aList.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.1")); // Rotates
+ translateToLocal("item.structurelib.frontRotationTool.desc.1")); // Rotates only the front panel, // only the
aList.add(EnumChatFormatting.BLUE // front
+ translateToLocal("item.structurelib.frontRotationTool.desc.2")); // which allows structure rotation. // panel,
aList.add(EnumChatFormatting.BLUE + translateToLocal("item.structurelib.frontRotationTool.desc.2")); // which
// allows
// structure
// rotation.
} }
} }
@@ -1,20 +1,23 @@
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;
@@ -45,10 +48,6 @@ public abstract class AlignmentMessage implements IMessage {
ByteBufUtils.writeTag(pBuffer, tFXTag); ByteBufUtils.writeTag(pBuffer, tFXTag);
} }
/**
* {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}
* @param provider
*/
private AlignmentMessage(IAlignmentProvider provider) { private AlignmentMessage(IAlignmentProvider provider) {
if (!(provider instanceof TileEntity)) throw new IllegalArgumentException("Provider must be a TileEntity"); if (!(provider instanceof TileEntity)) throw new IllegalArgumentException("Provider must be a TileEntity");
IAlignment alignment = provider.getAlignment(); IAlignment alignment = provider.getAlignment();
@@ -70,6 +69,7 @@ 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) {
@@ -82,6 +82,7 @@ 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) {
@@ -102,11 +103,11 @@ 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() TileEntity te = StructureLib.getCurrentPlayer().worldObj
.worldObj
.getTileEntity(pMessage.mPosX, pMessage.mPosY, pMessage.mPosZ); .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();
@@ -120,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);
@@ -4,12 +4,14 @@ import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
import com.gtnewhorizon.structurelib.StructureLib; import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI; import com.gtnewhorizon.structurelib.StructureLibAPI;
import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext; import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class ErrorHintParticleMessage implements IMessage { public class ErrorHintParticleMessage implements IMessage {
private int x; private int x;
private short y; private short y;
private int z; private int z;
@@ -37,17 +39,21 @@ public class ErrorHintParticleMessage implements IMessage {
} }
public static class Handler implements IMessageHandler<ErrorHintParticleMessage, IMessage> { public static class Handler implements IMessageHandler<ErrorHintParticleMessage, IMessage> {
@Override @Override
public IMessage onMessage(ErrorHintParticleMessage msg, MessageContext ctx) { public IMessage onMessage(ErrorHintParticleMessage msg, MessageContext ctx) {
boolean updateResult = StructureLibAPI.markHintParticleError( boolean updateResult = StructureLibAPI.markHintParticleError(
StructureLib.getCurrentPlayer(), StructureLib.getCurrentPlayer().worldObj, msg.x, msg.y, msg.z); StructureLib.getCurrentPlayer(),
if (StructureLibAPI.isDebugEnabled()) StructureLib.getCurrentPlayer().worldObj,
LOGGER.debug( msg.x,
"Server instructed to mark hint particle at ({}, {}, {}) error, result {}!", msg.y,
msg.x, msg.z);
msg.y, if (StructureLibAPI.isDebugEnabled()) LOGGER.debug(
msg.z, "Server instructed to mark hint particle at ({}, {}, {}) error, result {}!",
updateResult); msg.x,
msg.y,
msg.z,
updateResult);
return null; return null;
} }
} }
@@ -1,23 +1,28 @@
package com.gtnewhorizon.structurelib.net; 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.StructureLib;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor; import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger; import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext; import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
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;
public class SetChannelDataMessage implements IMessage { public class SetChannelDataMessage implements IMessage {
private List<Map.Entry<String, Integer>> data = new ArrayList<>();
private final List<Map.Entry<String, Integer>> data = new ArrayList<>();
public SetChannelDataMessage() {} public SetChannelDataMessage() {}
@@ -44,6 +49,7 @@ public class SetChannelDataMessage implements IMessage {
} }
public static class Handler implements IMessageHandler<SetChannelDataMessage, IMessage> { public static class Handler implements IMessageHandler<SetChannelDataMessage, IMessage> {
@Override @Override
public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) { public IMessage onMessage(SetChannelDataMessage message, MessageContext ctx) {
EntityPlayerMP player = ctx.getServerHandler().playerEntity; EntityPlayerMP player = ctx.getServerHandler().playerEntity;
@@ -4,12 +4,14 @@ import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
import com.gtnewhorizon.structurelib.StructureLib; import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI; import com.gtnewhorizon.structurelib.StructureLibAPI;
import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.common.network.simpleimpl.MessageContext; import cpw.mods.fml.common.network.simpleimpl.MessageContext;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class UpdateHintParticleMessage implements IMessage { public class UpdateHintParticleMessage implements IMessage {
private int x; private int x;
private short y; private short y;
private int z; private int z;
@@ -53,6 +55,7 @@ public class UpdateHintParticleMessage implements IMessage {
} }
public static class Handler implements IMessageHandler<UpdateHintParticleMessage, IMessage> { public static class Handler implements IMessageHandler<UpdateHintParticleMessage, IMessage> {
@Override @Override
public IMessage onMessage(UpdateHintParticleMessage msg, MessageContext ctx) { public IMessage onMessage(UpdateHintParticleMessage msg, MessageContext ctx) {
boolean updateResult = StructureLibAPI.updateHintParticleTint( boolean updateResult = StructureLibAPI.updateHintParticleTint(
@@ -61,16 +64,13 @@ public class UpdateHintParticleMessage implements IMessage {
msg.x, msg.x,
msg.y, msg.y,
msg.z, msg.z,
new short[] { new short[] { msg.r, msg.g, msg.b, msg.a, });
msg.r, msg.g, msg.b, msg.a, if (StructureLibAPI.isDebugEnabled()) LOGGER.debug(
}); "Server instructed to update hint particle at ({}, {}, {}), result {}!",
if (StructureLibAPI.isDebugEnabled()) msg.x,
LOGGER.debug( msg.y,
"Server instructed to update hint particle at ({}, {}, {}), result {}!", msg.z,
msg.x, updateResult);
msg.y,
msg.z,
updateResult);
return null; return null;
} }
} }
@@ -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,
}
}
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.structure;
class ChatThrottleKey { class ChatThrottleKey {
static class NoExplicitChannel { static class NoExplicitChannel {
private static final int hashOffset = NoExplicitChannel.class.hashCode(); private static final int hashOffset = NoExplicitChannel.class.hashCode();
private final String subchannel; private final String subchannel;
@@ -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,9 +4,10 @@ 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: * Default block setting calls {@link World#setBlock(int x, int y, int z, Block block, int meta, int updateType)}
* {@code world.setBlock(x,y,z,this/block,meta,2)} where updateType 2 means to update lighting and stuff * 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 world world that should be affected
* @param x x position to set * @param x x position to set
@@ -1,29 +1,61 @@
package com.gtnewhorizon.structurelib.structure; 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.InventoryIterable;
import com.gtnewhorizon.structurelib.util.InventoryUtility; import com.gtnewhorizon.structurelib.util.InventoryUtility;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate; import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode; import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
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;
/**
* Represent an item source. Take only, cannot be placed back.
*/
public interface IItemSource { public interface IItemSource {
/** /**
* We are probably not going to need to get a lot of items from this method * Retrieve some amount of items from this item source matching given filter.
* so I don't care about the overhead of storing all fetched items even if downstream code doesn't need it * <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 @Nonnull
Map<ItemStack, Integer> take(Predicate<ItemStack> predicate, boolean simulate, int count); 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) { default ItemStack takeOne(Predicate<ItemStack> predicate, boolean simulate) {
if (predicate == null) throw new IllegalArgumentException();
Map<ItemStack, Integer> take = take(predicate, simulate, 1); Map<ItemStack, Integer> take = take(predicate, simulate, 1);
return take.isEmpty() ? null : take.keySet().iterator().next(); 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) { default boolean takeAll(Predicate<ItemStack> predicate, boolean simulate, int count) {
// fast path for 1 item take requests // fast path for 1 item take requests
if (count == 1) return takeOne(predicate, simulate) != null; if (count == 1) return takeOne(predicate, simulate) != null;
@@ -33,24 +65,56 @@ public interface IItemSource {
return true; 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) { default boolean takeOne(ItemStack stack, boolean simulate) {
if (stack == null || stack.getItem() == null || stack.stackSize != 1) throw new IllegalArgumentException(); if (stack == null || stack.getItem() == null || stack.stackSize != 1) throw new IllegalArgumentException();
ItemStack took = takeOne(ItemStackPredicate.from(stack, NBTMode.EXACT), simulate); ItemStack took = takeOne(ItemStackPredicate.from(stack, NBTMode.EXACT), simulate);
return took == null || took.stackSize <= 0; 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) { default boolean takeAll(ItemStack stack, boolean simulate) {
if (stack == null || stack.getItem() == null) throw new IllegalArgumentException();
// fast path for 1 item take requests // fast path for 1 item take requests
if (stack.stackSize == 1) return takeOne(stack, simulate); if (stack.stackSize == 1) return takeOne(stack, simulate);
ItemStackPredicate predicate = ItemStackPredicate.from(stack, NBTMode.EXACT); ItemStackPredicate predicate = ItemStackPredicate.from(stack, NBTMode.EXACT);
Map<ItemStack, Integer> have = take(predicate, true, stack.stackSize); Map<ItemStack, Integer> have = take(predicate, true, stack.stackSize);
if (have.values().stream().mapToInt(Integer::intValue).sum() < stack.stackSize) return false; if (have.values().stream().mapToInt(Integer::intValue).sum() < stack.stackSize) return false;
take(predicate, simulate, stack.stackSize); if (simulate) return true;
take(predicate, false, stack.stackSize);
return true; return true;
} }
/**
* Construct an IItemSource from inventories associated with given player.
* <p>
* This will be backed by {@link InventoryUtility}
*/
static IItemSource fromPlayer(EntityPlayerMP player) { static IItemSource fromPlayer(EntityPlayerMP player) {
return new IItemSource() { return new IItemSource() {
@Nonnull @Nonnull
@Override @Override
public Map<ItemStack, Integer> take(Predicate<ItemStack> p, boolean s, int c) { public Map<ItemStack, Integer> take(Predicate<ItemStack> p, boolean s, int c) {
@@ -71,6 +135,11 @@ public interface IItemSource {
}; };
} }
/**
* Construct an IItemSource from given inventory
* <p>
* This will be backed by {@link InventoryUtility}
*/
static IItemSource fromInventory(IInventory inv) { static IItemSource fromInventory(IInventory inv) {
return (p, s, c) -> InventoryUtility.takeFromInventory(new InventoryIterable<>(inv), p, s, c, true); return (p, s, c) -> InventoryUtility.takeFromInventory(new InventoryIterable<>(inv), p, s, c, true);
} }
@@ -3,5 +3,6 @@ package com.gtnewhorizon.structurelib.structure;
import net.minecraft.block.Block; import net.minecraft.block.Block;
public interface ISpecialItemBlock { public interface ISpecialItemBlock {
int getItemMetaFromBlockMeta(Block block, int blockMeta); int getItemMetaFromBlockMeta(Block block, int blockMeta);
} }
@@ -3,24 +3,28 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded; import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded;
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded; import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import java.util.function.Function; import java.util.function.Function;
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.world.World; import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI;
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. * 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> * <h2>Cache the instances of this class</h2>
* <p> * <p>
* You will construct this definition once per multi, either at class load or at first call to your variant of {@code getStructureDefinition()}. * You will construct this definition once per multi, either at class load or at first call to your variant of
* You can use a {@link ClassValue ClassValue&lt;IStructureDefinition&lt;?>>} if you have a template for subclass. * {@code getStructureDefinition()}. You can use a {@link ClassValue ClassValue&lt;IStructureDefinition&lt;?>>} if you
* You can also use {@link StructureUtility#defer(Function) defer()} or its overloads for simpler cases, but it is * have a template for subclass. You can also use {@link StructureUtility#defer(Function) defer()} or its overloads for
* slightly less performant as it will allocate new objects on each structure check, whereas {@link ClassValue} will * simpler cases, but it is slightly less performant as it will allocate new objects on each structure check, whereas
* allocate once and cache it onwards. * {@link ClassValue} will allocate once and cache it onwards.
* <p> * <p>
* <h2>Constructing</h2> * <h2>Constructing</h2>
* <p> * <p>
@@ -28,59 +32,62 @@ import net.minecraft.world.World;
* <p> * <p>
* Builder has two main methods: * Builder has two main methods:
* <ul> * <ul>
* <li>{@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addShape(String, String[][]) addShape()} * <li>{@link com.gtnewhorizon.structurelib.structure.StructureDefinition.Builder#addShape(String, String[][])
* is a 2d string array describing the layout of structure. You can optionally use `StructureUtility#transpose()` * addShape()} is a 2d string array describing the layout of structure. You can optionally use
* to transpose the array to make the structure look nicer in source code. * `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}. * <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> * </ul>
* <h2>Position A, B and C</h2> * <h2>Position A, B and C</h2>
* *
* Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped. * Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped.
* <ul> * <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 "A" position would be the number of blocks on the left side of the controller, not counting controller
* <li>The "B" position would be the number of blocks on the top side of the controller, not counting controller itself.</li> * 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> * <li>The "C" position would be the number of blocks between you and controller, not counting controller itself.</li>
* </ul> * </ul>
*
* @param <T> Type of the context object. Usually this will be the multiblock controller. * @param <T> Type of the context object. Usually this will be the multiblock controller.
*/ */
public interface IStructureDefinition<T> { public interface IStructureDefinition<T> {
/** /**
* Used internally * Used internally
* <p> * <p>
* Might, but is not required, to throw {@link java.util.NoSuchElementException} if the given structure piece is not found. * 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 * @param name same name as for other methods here
* @return the array of elements to process * @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 * @throws java.util.NoSuchElementException if the given structure piece is not found and the moon phase perfectly
* matches
*/ */
IStructureElement<T>[] getStructureFor(String name); IStructureElement<T>[] getStructureFor(String name);
boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC);
/** /**
* Run a structure check. * Run a structure check.
* @param object context object. usually multiblock controller. *
* @param piece the structure piece's string identifier. * @param object context object. usually multiblock controller.
* @param world the world object this check takes place in. * @param piece the structure piece's string identifier.
* @param extendedFacing the current structure's orientation. * @param world the world object this check takes place in.
* @param basePositionX X location of the structure * @param extendedFacing the current structure's orientation.
* @param basePositionY Y location of the structure * @param basePositionX X location of the structure
* @param basePositionZ Z location of the structure * @param basePositionY Y location of the structure
* @param basePositionA see class javadoc * @param basePositionZ Z location of the structure
* @param basePositionB see class javadoc * @param basePositionA see class javadoc
* @param basePositionC see class javadoc * @param basePositionB see class javadoc
* @param basePositionC see class javadoc
* @param forceCheckAllBlocks check all location even if chunk not currently loaded. * @param forceCheckAllBlocks check all location even if chunk not currently loaded.
* @return true if successful, false otherwise * @return true if successful, false otherwise
*/ */
default boolean check( default boolean check(T object, String piece, World world, ExtendedFacing extendedFacing, int basePositionX,
T object, int basePositionY, int basePositionZ, int basePositionA, int basePositionB, int basePositionC,
String piece,
World world,
ExtendedFacing extendedFacing,
int basePositionX,
int basePositionY,
int basePositionZ,
int basePositionA,
int basePositionB,
int basePositionC,
boolean forceCheckAllBlocks) { boolean forceCheckAllBlocks) {
return iterate( return iterate(
object, object,
@@ -100,29 +107,21 @@ public interface IStructureDefinition<T> {
/** /**
* Spawn hint particles. Should not be called on server side. * Spawn hint particles. Should not be called on server side.
* @param object context object. usually multiblock controller. *
* @param piece the structure piece's string identifier. * @param object context object. usually multiblock controller.
* @param world the world object this check takes place in. * @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 extendedFacing the current structure's orientation.
* @param basePositionX X location of the structure * @param basePositionX X location of the structure
* @param basePositionY Y location of the structure * @param basePositionY Y location of the structure
* @param basePositionZ Z location of the structure * @param basePositionZ Z location of the structure
* @param basePositionA see class javadoc * @param basePositionA see class javadoc
* @param basePositionB see class javadoc * @param basePositionB see class javadoc
* @param basePositionC see class javadoc * @param basePositionC see class javadoc
* @return true if successful, false otherwise * @return true if successful, false otherwise
*/ */
default boolean hints( default boolean hints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
T object, int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
ItemStack trigger,
String piece,
World world,
ExtendedFacing extendedFacing,
int basePositionX,
int basePositionY,
int basePositionZ,
int basePositionA,
int basePositionB,
int basePositionC) { int basePositionC) {
return iterate( return iterate(
object, object,
@@ -141,33 +140,24 @@ public interface IStructureDefinition<T> {
} }
/** /**
* Build the multi in creative mode on a best effort basis. Does not guarantee the structure is correct after a * Build the multi in creative mode on a best-effort basis. Does not guarantee the structure is correct after a
* successful call. * successful call.
* *
* @param object context object. usually multiblock controller. * @param object context object. usually multiblock controller.
* @param trigger The trigger item that contains channel data. * @param trigger The trigger item that contains channel data.
* @param piece the structure piece's string identifier. * @param piece the structure piece's string identifier.
* @param world the world object this check takes place in. * @param world the world object this check takes place in.
* @param extendedFacing the current structure's orientation. * @param extendedFacing the current structure's orientation.
* @param basePositionX X location of the structure * @param basePositionX X location of the structure
* @param basePositionY Y location of the structure * @param basePositionY Y location of the structure
* @param basePositionZ Z location of the structure * @param basePositionZ Z location of the structure
* @param basePositionA see class javadoc * @param basePositionA see class javadoc
* @param basePositionB see class javadoc * @param basePositionB see class javadoc
* @param basePositionC see class javadoc * @param basePositionC see class javadoc
* @return true if sucessful, false otherwise * @return true if sucessful, false otherwise
*/ */
default boolean build( default boolean build(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
T object, int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
ItemStack trigger,
String piece,
World world,
ExtendedFacing extendedFacing,
int basePositionX,
int basePositionY,
int basePositionZ,
int basePositionA,
int basePositionB,
int basePositionC) { int basePositionC) {
return iterate( return iterate(
object, object,
@@ -186,38 +176,29 @@ public interface IStructureDefinition<T> {
} }
/** /**
* Call {@link #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} if {@code hintsOnly}, * Call {@link #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} if
* call {@link #hints(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} otherwise. * {@code hintsOnly}, call
* {@link #hints(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)} otherwise.
* *
* @param object context object. usually multiblock controller. * @param object context object. usually multiblock controller.
* @param trigger The trigger item that contains channel data. * @param trigger The trigger item that contains channel data.
* @param piece the structure piece's string identifier. * @param piece the structure piece's string identifier.
* @param world the world object this check takes place in. * @param world the world object this check takes place in.
* @param extendedFacing the current structure's orientation. * @param extendedFacing the current structure's orientation.
* @param basePositionX X location of the structure * @param basePositionX X location of the structure
* @param basePositionY Y location of the structure * @param basePositionY Y location of the structure
* @param basePositionZ Z location of the structure * @param basePositionZ Z location of the structure
* @param basePositionA see class javadoc * @param basePositionA see class javadoc
* @param basePositionB see class javadoc * @param basePositionB see class javadoc
* @param basePositionC see class javadoc * @param basePositionC see class javadoc
* @param hintsOnly whether to spawn hints or do creative build. * @param hintsOnly whether to spawn hints or do creative build.
* @return true if sucessful, false otherwise * @return true if sucessful, false otherwise
* @see #hints(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int) * @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) * @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
*/ */
default boolean buildOrHints( default boolean buildOrHints(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
T object, int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
ItemStack trigger, int basePositionC, boolean hintsOnly) {
String piece,
World world,
ExtendedFacing extendedFacing,
int basePositionX,
int basePositionY,
int basePositionZ,
int basePositionA,
int basePositionB,
int basePositionC,
boolean hintsOnly) {
return iterate( return iterate(
object, object,
trigger, trigger,
@@ -237,45 +218,91 @@ public interface IStructureDefinition<T> {
/** /**
* Cause a survival build. * Cause a survival build.
* *
* @param object context object. usually multiblock controller. * @param object context object. usually multiblock controller.
* @param trigger The trigger item that contains channel data. * @param trigger The trigger item that contains channel data.
* @param piece the structure piece's string identifier. * @param piece the structure piece's string identifier.
* @param world the world object this check takes place in. * @param world the world object this check takes place in.
* @param extendedFacing the current structure's orientation. * @param extendedFacing the current structure's orientation.
* @param basePositionX X location of the structure * @param basePositionX X location of the structure
* @param basePositionY Y location of the structure * @param basePositionY Y location of the structure
* @param basePositionZ Z location of the structure * @param basePositionZ Z location of the structure
* @param basePositionA see class javadoc * @param basePositionA see class javadoc
* @param basePositionB see class javadoc * @param basePositionB see class javadoc
* @param basePositionC see class javadoc * @param basePositionC see class javadoc
* @param elementBudget build up to this many elements * @param elementBudget build up to this many elements
* @param source from where to drain resource * @param source from where to drain resource
* @param actor source of action * @param actor source of action
* @param check whether {@link IStructureElement#check(Object, World, int, int, int)} should be called if anything * @param check whether {@link IStructureElement#check(Object, World, int, int, int)} should be called if
* would be placed. use with caution. * anything would be placed. use with caution. if in doubt, call
* if in doubt, call {@link #check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)} * {@link #check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}
* after this call and set this parameter to false * after this call and set this parameter to false
* @return number of elements built, or -1 if structure done * @return number of elements built, or -1 if structure done
* @see #build(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 int survivalBuild( @Deprecated
T object, default int survivalBuild(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
ItemStack trigger, int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
String piece, int basePositionC, int elementBudget, IItemSource source, EntityPlayerMP actor, boolean check) {
World world, EntityPlayer realActor;
ExtendedFacing extendedFacing, if (actor == null) {
int basePositionX, realActor = ISurvivalConstructable.__get_player();
int basePositionY, if (realActor == null) throw new IllegalArgumentException();
int basePositionZ, } else {
int basePositionA, realActor = actor;
int basePositionB, }
int basePositionC, return survivalBuild(
int elementBudget, object,
IItemSource source, trigger,
EntityPlayerMP actor, piece,
boolean check) { world,
SurvivalBuildStructureWalker<T> walker = extendedFacing,
new SurvivalBuildStructureWalker<>(object, trigger, source, actor, elementBudget, check); basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
elementBudget,
ISurvivalBuildEnvironment.create(source, realActor),
check);
}
/**
* Cause a survival build.
*
* @param object context object. usually multiblock controller.
* @param trigger The trigger item that contains channel data.
* @param piece the structure piece's string identifier.
* @param world the world object this check takes place in.
* @param extendedFacing the current structure's orientation.
* @param basePositionX X location of the structure
* @param basePositionY Y location of the structure
* @param basePositionZ Z location of the structure
* @param basePositionA see class javadoc
* @param basePositionB see class javadoc
* @param basePositionC see class javadoc
* @param elementBudget build up to this many elements
* @param env build environment.
* @param check whether {@link IStructureElement#check(Object, World, int, int, int)} should be called if
* anything would be placed. use with caution. if in doubt, call
* {@link IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}
* after this call and set this parameter to false
* @return number of elements built, or -1 if structure done
* @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
*/
default int survivalBuild(T object, ItemStack trigger, String piece, World world, ExtendedFacing extendedFacing,
int basePositionX, int basePositionY, int basePositionZ, int basePositionA, int basePositionB,
int basePositionC, int elementBudget, ISurvivalBuildEnvironment env, boolean check) {
SurvivalBuildStructureWalker<T> walker = new SurvivalBuildStructureWalker<>(
object,
trigger,
elementBudget,
env,
this,
piece,
extendedFacing,
new int[] { basePositionA, basePositionB, basePositionC },
check);
StructureUtility.iterateV2( StructureUtility.iterateV2(
getStructureFor(piece), getStructureFor(piece),
world, world,
@@ -293,38 +320,29 @@ public interface IStructureDefinition<T> {
/** /**
* Low level utility. * Low level utility.
* @param object context object. usually multiblock controller. *
* @param trigger The trigger item that contains channel data. * @param object context object. usually multiblock controller.
* @param elements the structure piece * @param trigger The trigger item that contains channel data.
* @param world the world object this check takes place in. * @param elements the structure piece
* @param extendedFacing the current structure's orientation. * @param world the world object this check takes place in.
* @param basePositionX X location of the structure * @param extendedFacing the current structure's orientation.
* @param basePositionY Y location of the structure * @param basePositionX X location of the structure
* @param basePositionZ Z location of the structure * @param basePositionY Y location of the structure
* @param basePositionA see class javadoc * @param basePositionZ Z location of the structure
* @param basePositionB see class javadoc * @param basePositionA see class javadoc
* @param basePositionC see class javadoc * @param basePositionB see class javadoc
* @param hintsOnly whether to spawn hints or do creative build. no effect if {@code checkBlocksIfNotNullForceCheckAllIfTrue} is not null. * @param basePositionC see class javadoc
* @param checkBlocksIfNotNullForceCheckAllIfTrue creative build or spawn hints if null. Force check not loaded chunk * @param hintsOnly whether to spawn hints or do creative build. no effect if
* if true. Check only loaded chunk is false. * {@code checkBlocksIfNotNullForceCheckAllIfTrue} is not null.
* @param checkBlocksIfNotNullForceCheckAllIfTrue creative build or spawn hints if null. Force check not loaded
* chunk if true. Check only loaded chunk is false.
* @return true if iteration completed successfully. * @return true if iteration completed successfully.
* @param <T> type of context object. * @param <T> type of context object.
*/ */
static <T> boolean iterate( static <T> boolean iterate(T object, ItemStack trigger, IStructureElement<T>[] elements, World world,
T object, ExtendedFacing extendedFacing, int basePositionX, int basePositionY, int basePositionZ, int basePositionA,
ItemStack trigger, int basePositionB, int basePositionC, boolean hintsOnly, Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
IStructureElement<T>[] elements, if (!world.isRemote && hintsOnly) {
World world,
ExtendedFacing extendedFacing,
int basePositionX,
int basePositionY,
int basePositionZ,
int basePositionA,
int basePositionB,
int basePositionC,
boolean hintsOnly,
Boolean checkBlocksIfNotNullForceCheckAllIfTrue) {
if (world.isRemote ^ hintsOnly) {
return false; return false;
} }
@@ -341,7 +359,7 @@ public interface IStructureDefinition<T> {
basePositionA, basePositionA,
basePositionB, basePositionB,
basePositionC, basePositionC,
(e, w, x, y, z) -> e.check(object, w, x, y, z), (e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
"check"); "check");
} else { } else {
success = StructureUtility.iterateV2( success = StructureUtility.iterateV2(
@@ -354,12 +372,12 @@ public interface IStructureDefinition<T> {
basePositionA, basePositionA,
basePositionB, basePositionB,
basePositionC, basePositionC,
skipBlockUnloaded((e, w, x, y, z) -> e.check(object, w, x, y, z)), skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
"check force"); "check force");
} }
if (StructureLibAPI.isDebugEnabled() && success) { if (StructureLibAPI.isDebugEnabled() && success) {
StructureLib.LOGGER.info( StructureLib.LOGGER
"Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass"); .info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
} }
return success; return success;
} else { } else {
@@ -374,7 +392,7 @@ public interface IStructureDefinition<T> {
basePositionA, basePositionA,
basePositionB, basePositionB,
basePositionC, basePositionC,
ignoreBlockUnloaded((e, w, x, y, z) -> { ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.spawnHint(object, world, x, y, z, trigger); e.spawnHint(object, world, x, y, z, trigger);
return true; return true;
}), }),
@@ -390,7 +408,7 @@ public interface IStructureDefinition<T> {
basePositionA, basePositionA,
basePositionB, basePositionB,
basePositionC, basePositionC,
ignoreBlockUnloaded((e, w, x, y, z) -> { ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.placeBlock(object, world, x, y, z, trigger); e.placeBlock(object, world, x, y, z, trigger);
return true; return true;
}), }),
@@ -402,7 +420,7 @@ public interface IStructureDefinition<T> {
/** /**
* Create a new instance of builder. * Create a new instance of builder.
* @return *
* @param <T> type of context object * @param <T> type of context object
*/ */
static <T> StructureDefinition.Builder<T> builder() { static <T> StructureDefinition.Builder<T> builder() {
@@ -3,18 +3,33 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER; import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE; import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
import com.gtnewhorizon.structurelib.StructureLibAPI; import java.util.Arrays;
import java.util.Collections;
import java.util.function.Consumer; 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.block.Block;
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.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.IChatComponent; import net.minecraft.util.IChatComponent;
import net.minecraft.world.World; import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
/** /**
* Use StructureUtility to instantiate * Use StructureUtility to instantiate. These are the building blocks for your {@link IStructureDefinition}. It
* represents what a particular element can be and how this particular element should be autoplaced.
*/ */
public interface IStructureElement<T> { public interface IStructureElement<T> {
boolean check(T t, World world, int x, int y, int z); boolean 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 spawnHint(T t, World world, int x, int y, int z, ItemStack trigger);
@@ -24,35 +39,141 @@ public interface IStructureElement<T> {
/** /**
* Try place the block by taking resource from given IItemSource. * Try place the block by taking resource from given IItemSource.
* <p> * <p>
* You might want to use {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, EntityPlayerMP)} * 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. * or its overloads to implement this.
* *
* @param trigger trigger item * @param trigger trigger item
* @param s drain resources from this place * @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 * @param actor source of action. for very critical errors you can also just send the error messages here,
* any filter that chatter might have. * 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 * @param chatter send error messages here. Caller will choose an appropriate way to forward it to player if the
* other fallbacks also fails. * 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.
*/ */
default PlaceResult survivalPlaceBlock( @Deprecated
T t, default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
World world, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
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 (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
if (StructureLibAPI.isDebugEnabled()) if (StructureLibAPI.isDebugEnabled()) LOGGER.error(
LOGGER.error( "Default implementation of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
"Default implementation of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}", getClass().getName());
getClass().getName());
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT; if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
return PlaceResult.SKIP; return PlaceResult.SKIP;
} }
/**
* 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;
}
/**
* Try place the block by taking resource from given {@link IItemSource}.
* <p>
* You might want to use
* {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
* or its overloads to implement this.
* <p>
* The default implementation provided will
* <ol>
* <li>Get {@link BlocksToPlace} via
* {@link #getBlocksToPlace(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}. If not null
* <ol>
* <li>call {@link #check(Object, World, int, int, int)}. If this returns {@code true}, {@link PlaceResult#SKIP}
* will be returned without further action.</li>
* <li>Use the {@link BlocksToPlace} retrieved earlier and passed in {@link IItemSource} to determine an item to
* place</li>
* <li>Hand over control to
* {@link StructureUtility#survivalPlaceBlock(ItemStack, NBTMode, NBTTagCompound, boolean, World, int, int, int, IItemSource, EntityPlayer, Consumer)}</li>
* </ol>
* </li>
* <li>Call legacy
* {@link #survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)} if
* {@code env.getActor()} contains an {@link EntityPlayerMP}</li>
* <li>Emit warning under debug mode (or throw exception under panic mode), then return
* {@link PlaceResult#SKIP}</li>
* </ol>
* It should be noticed that the default implementation is likely to cause unexpected issues if the
* {@link #check(Object, World, int, int, int)} is not side effect free, or is not idempotent. E.g. the
* {@link #check(Object, World, int, int, int)} will add current coord to a {@link java.util.List}. As this method
* might be invoked at a location that is already accepted, this will cause the machine to have multiple of this
* coord in its list.
*
* @param trigger trigger item
* @param env autoplacing environment
*/
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
IItemSource source = env.getSource();
EntityPlayer actor = env.getActor();
Consumer<IChatComponent> chatter = env.getChatter();
if (e != null) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
if (e.getStacks() == null) {
ItemStack taken = source.takeOne(e.getPredicate(), true);
return StructureUtility.survivalPlaceBlock(
taken,
NBTMode.EXACT,
taken.stackTagCompound,
true,
world,
x,
y,
z,
source,
actor,
chatter);
}
for (ItemStack stack : e.getStacks()) {
if (!source.takeOne(stack, true)) continue;
return StructureUtility.survivalPlaceBlock(
stack,
NBTMode.EXACT,
stack.stackTagCompound,
true,
world,
x,
y,
z,
source,
actor,
chatter);
}
return PlaceResult.REJECT;
}
if (actor instanceof EntityPlayerMP)
return survivalPlaceBlock(t, world, x, y, z, trigger, source, (EntityPlayerMP) actor, chatter);
if (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
if (StructureLibAPI.isDebugEnabled()) LOGGER.info(
"Fallback shim code of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
getClass().getName());
return PlaceResult.SKIP;
}
/** /**
* Forget the messed up class dependency graph for now. this is just so convenient. * Forget the messed up class dependency graph for now. this is just so convenient.
*/ */
@@ -101,18 +222,19 @@ public interface IStructureElement<T> {
enum PlaceResult { enum PlaceResult {
/** /**
* This element either exists already, or does not yet have an implementation for survivalPlaceBlock, or * This element either exists already, or does not yet have an implementation for survivalPlaceBlock, or some
* some other unforeseen situations. * other unforeseen situations. TODO this definition doesn't seem right. Should we separate SKIP from EXISTS, or
* TODO this definition doesn't seem right. Should we separate SKIP from EXISTS, or SKIP from ERROR? * SKIP from ERROR?
*/ */
SKIP, SKIP,
/** /**
* This element's space is occupied by other stuff and require player attention, or player missing required resource, * This element's space is occupied by other stuff and require player attention, or player missing required
* or the block cannot be placed due to obscure mechanisms, or some other unforeseen situations. * resource, or the block cannot be placed due to obscure mechanisms, or some other unforeseen situations.
*/ */
REJECT, REJECT,
/** /**
* Autoplace cannot proceed within this tick. To proceed further would require stopping the placement and wait for next round. * Autoplace cannot proceed within this tick. To proceed further would require stopping the placement and wait
* for next round.
*/ */
STOP, STOP,
/** /**
@@ -126,4 +248,83 @@ public interface IStructureElement<T> {
*/ */
ACCEPT_STOP; 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;
}
}
} }
@@ -3,15 +3,22 @@ package com.gtnewhorizon.structurelib.structure;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate;
import javax.annotation.Nullable;
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.util.IChatComponent; 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(); IStructureElement<T>[] fallbacks();
@Override @Override
@@ -44,17 +51,26 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
return false; return false;
} }
@Nullable
@Override @Override
default PlaceResult survivalPlaceBlock( default BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
T t, AutoPlaceEnvironment env) {
World world, Predicate<ItemStack> predicate = null;
int x, List<Iterable<ItemStack>> is = new ArrayList<>();
int y, for (IStructureElement<T> fallback : fallbacks()) {
int z, BlocksToPlace e = fallback.getBlocksToPlace(t, world, x, y, z, trigger, env);
ItemStack trigger, if (e == null) continue;
IItemSource s, if (predicate == null) predicate = e.getPredicate();
EntityPlayerMP actor, else predicate = predicate.or(e.getPredicate());
Consumer<IChatComponent> chatter) { is.add(e.getStacks());
}
if (predicate == null) return null;
return new BlocksToPlace(predicate, Iterables.concat(is));
}
@Override
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
boolean haveSkip = false; boolean haveSkip = false;
List<IChatComponent> bufferedNoise = new ArrayList<>(); List<IChatComponent> bufferedNoise = new ArrayList<>();
for (IStructureElement<T> fallback : fallbacks()) { for (IStructureElement<T> fallback : fallbacks()) {
@@ -74,4 +90,28 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
// TODO need reconsider to ensure this is the right course of action // TODO need reconsider to ensure this is the right course of action
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT; return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
} }
@Override
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
boolean haveSkip = false;
List<IChatComponent> bufferedNoise = new ArrayList<>();
for (IStructureElement<T> fallback : fallbacks()) {
PlaceResult result = fallback
.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
switch (result) {
case REJECT:
break;
case SKIP:
haveSkip = true;
break;
default:
return result;
}
}
// dump all that noise back into upstream
bufferedNoise.forEach(env.getChatter());
// TODO need reconsider to ensure this is the right course of action
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
}
} }
@@ -1,12 +1,14 @@
package com.gtnewhorizon.structurelib.structure; package com.gtnewhorizon.structurelib.structure;
import java.util.function.Consumer; import java.util.function.Consumer;
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.util.IChatComponent; 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 @Override
default boolean placeBlock(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;
@@ -18,16 +20,14 @@ public interface IStructureElementCheckOnly<T> extends IStructureElement<T> {
} }
@Override @Override
default PlaceResult survivalPlaceBlock( default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
T t, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
World world, return PlaceResult.SKIP;
int x, }
int y,
int z, @Override
ItemStack trigger, default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, AutoPlaceEnvironment env) {
EntityPlayerMP actor,
Consumer<IChatComponent> chatter) {
return PlaceResult.SKIP; return PlaceResult.SKIP;
} }
} }
@@ -3,4 +3,5 @@ package com.gtnewhorizon.structurelib.structure;
/** /**
* Use StructureUtility to instantiate * Use StructureUtility to instantiate
*/ */
public interface IStructureElementDeferred<T> extends IStructureElement<T> {} public interface IStructureElementDeferred<T> extends IStructureElement<T> {
}
@@ -1,28 +1,28 @@
package com.gtnewhorizon.structurelib.structure; package com.gtnewhorizon.structurelib.structure;
import java.util.function.Consumer; import java.util.function.Consumer;
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.util.IChatComponent; 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 @Override
default boolean placeBlock(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 @Override
default PlaceResult survivalPlaceBlock( default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
T t, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
World world, return PlaceResult.REJECT;
int x, }
int y,
int z, @Override
ItemStack trigger, default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, AutoPlaceEnvironment env) {
EntityPlayerMP actor,
Consumer<IChatComponent> chatter) {
return PlaceResult.REJECT; return PlaceResult.REJECT;
} }
@@ -1,6 +1,7 @@
package com.gtnewhorizon.structurelib.structure; package com.gtnewhorizon.structurelib.structure;
import java.util.function.Consumer; import java.util.function.Consumer;
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.util.IChatComponent; import net.minecraft.util.IChatComponent;
@@ -10,6 +11,7 @@ import net.minecraft.world.World;
* Use StructureUtility to instantiate * Use StructureUtility to instantiate
*/ */
interface IStructureNavigate<T> extends IStructureElement<T> { interface IStructureNavigate<T> extends IStructureElement<T> {
@Override @Override
default boolean check(T t, World world, int x, int y, int z) { default boolean check(T t, World world, int x, int y, int z) {
return true; return true;
@@ -26,16 +28,14 @@ interface IStructureNavigate<T> extends IStructureElement<T> {
} }
@Override @Override
default PlaceResult survivalPlaceBlock( default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
T t, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
World world, return PlaceResult.SKIP;
int x, }
int y,
int z, @Override
ItemStack trigger, default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, AutoPlaceEnvironment env) {
EntityPlayerMP actor,
Consumer<IChatComponent> chatter) {
return PlaceResult.SKIP; return PlaceResult.SKIP;
} }
@@ -2,36 +2,42 @@ package com.gtnewhorizon.structurelib.structure;
import net.minecraft.world.World; import net.minecraft.world.World;
public interface IStructureWalker<T> { interface IStructureWalker<T> {
boolean visit(IStructureElement<T> element, World world, int x, int y, int z);
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z) { boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
int c) {
return false; return false;
} }
static <T> IStructureWalker<T> ignoreBlockUnloaded(IStructureWalker<T> walker) { static <T> IStructureWalker<T> ignoreBlockUnloaded(IStructureWalker<T> walker) {
return new IStructureWalker<T>() { return new IStructureWalker<T>() {
@Override @Override
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z) { public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
return walker.visit(element, world, x, y, z); return walker.visit(element, world, x, y, z, a, b, c);
} }
@Override @Override
public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z) { public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
return walker.visit(element, world, x, y, z); int c) {
return walker.visit(element, world, x, y, z, a, b, c);
} }
}; };
} }
static <T> IStructureWalker<T> skipBlockUnloaded(IStructureWalker<T> walker) { static <T> IStructureWalker<T> skipBlockUnloaded(IStructureWalker<T> walker) {
return new IStructureWalker<T>() { return new IStructureWalker<T>() {
@Override @Override
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z) { public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
return walker.visit(element, world, x, y, z); return walker.visit(element, world, x, y, z, a, b, c);
} }
@Override @Override
public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z) { public boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
int c) {
return true; 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);
}
}
@@ -9,5 +9,6 @@ import net.minecraft.block.Block;
*/ */
@FunctionalInterface @FunctionalInterface
public interface ITierConverter<TIER> { public interface ITierConverter<TIER> {
TIER convert(Block block, int meta); TIER convert(Block block, int meta);
} }
@@ -3,7 +3,7 @@ package com.gtnewhorizon.structurelib.structure;
/** /**
* Implement this and use {@link StructureUtility#withContext(IStructureElement)} in case you need to record some * Implement this and use {@link StructureUtility#withContext(IStructureElement)} in case you need to record some
* temporary states that don't need to live in your main context, which would usually live for the whole lifetime of the * temporary states that don't need to live in your main context, which would usually live for the whole lifetime of the
* structure than just during structure check. Usually just save some few bytes, but becomes non trivial if you need to * structure than just during structure check. Usually just save some few bytes, but becomes non-trivial if you need to
* record too much stuff. * record too much stuff.
* <p> * <p>
* You will have to reset the context yourself though. The API can't guess when the old context becomes invalid. * You will have to reset the context yourself though. The API can't guess when the old context becomes invalid.
@@ -11,5 +11,6 @@ package com.gtnewhorizon.structurelib.structure;
* @param <CTX> the temporary state context * @param <CTX> the temporary state context
*/ */
public interface IWithExtendedContext<CTX> { public interface IWithExtendedContext<CTX> {
CTX getCurrentContext(); CTX getCurrentContext();
} }
@@ -2,12 +2,16 @@ package com.gtnewhorizon.structurelib.structure;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import javax.annotation.Nullable;
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.util.IChatComponent; import net.minecraft.util.IChatComponent;
import net.minecraft.world.World; import net.minecraft.world.World;
class LazyStructureElement<T> implements IStructureElementDeferred<T> { class LazyStructureElement<T> implements IStructureElementDeferred<T> {
private Function<T, IStructureElement<T>> to; private Function<T, IStructureElement<T>> to;
private IStructureElement<T> elem; private IStructureElement<T> elem;
@@ -38,17 +42,22 @@ class LazyStructureElement<T> implements IStructureElementDeferred<T> {
return get(t).spawnHint(t, world, x, y, z, trigger); return get(t).spawnHint(t, world, x, y, z, trigger);
} }
@Nullable
@Override @Override
public PlaceResult survivalPlaceBlock( public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
T t, AutoPlaceEnvironment env) {
World world, return get(t).getBlocksToPlace(t, world, x, y, z, trigger, env);
int x, }
int y,
int z, @Override
ItemStack trigger, public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
EntityPlayerMP actor,
Consumer<IChatComponent> chatter) {
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter); return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
} }
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
}
} }
@@ -2,30 +2,32 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.*; import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
public class StructureDefinition<T> implements IStructureDefinition<T> { public class StructureDefinition<T> implements IStructureDefinition<T> {
private final Map<Character, IStructureElement<T>> elements; private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String> shapes; private final Map<String, String> shapes;
private final Map<String, IStructureElement<T>[]> structures; private final Map<String, IStructureElement<T>[]> structures;
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
public static <B> Builder<B> builder() { public static <B> Builder<B> builder() {
return new Builder<>(); return new Builder<>();
} }
private StructureDefinition( private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
Map<Character, IStructureElement<T>> elements, Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
Map<String, String> shapes,
Map<String, IStructureElement<T>[]> structures) {
this.elements = elements; this.elements = elements;
this.shapes = shapes; this.shapes = shapes;
this.structures = structures; this.structures = structures;
this.occupiedSpaces = occupiedSpaces;
} }
public static class Builder<T> { public static class Builder<T> {
private static final char A = '\uA000'; private static final char A = '\uA000';
private static final char B = '\uB000'; private static final char B = '\uB000';
private static final char C = '\uC000'; private static final char C = '\uC000';
@@ -33,11 +35,13 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
private final Map<Vec3Impl, Character> navigates; private final Map<Vec3Impl, Character> navigates;
private final Map<Character, IStructureElement<T>> elements; private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String> shapes; private final Map<String, String> shapes;
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
private Builder() { private Builder() {
navigates = new HashMap<>(); navigates = new HashMap<>();
elements = new HashMap<>(); elements = new HashMap<>();
shapes = new HashMap<>(); shapes = new HashMap<>();
occupiedSpaces = new HashMap<>();
} }
public Map<Character, IStructureElement<T>> getElements() { public Map<Character, IStructureElement<T>> getElements() {
@@ -49,21 +53,25 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
} }
/** /**
* Adds shape * Adds shape. Shape is a two-dimensional string array. Each <b>character</b> inside any of these strings will
* + is anything but air * be later mapped to a particular type of {@link IStructureElement} as indicated by
* - is air checks * {@link #addElement(Character, IStructureElement)}.
* space bar is skip
* ~ is also skip (but marks controller position, optional and logically it is a space...)
* rest needs to be defined
* <p> * <p>
* next char is next block(a) * next char is next block(a) next string is next line(b) next string[] is next slice(c)
* next string is next line(b) * <p>
* next string[] is next slice(c) * There are a few special reserved characters that are pre-mapped to some common structure elements
* <ul>
* <li>+ is anything but air</li>
* <li>- is air checks</li>
* <li>space bar is skip</li>
* <li>~ is also skip (but marks controller position, not optional)</li>
* <li>char {@code A000-FFFF} range is reserved for generated skips</li>
* </ul>
* <p> * <p>
* char A000-FFFF range is reserved for generated skips
* *
* @param name unlocalized/code name * @param name unlocalized/code name
* @param structurePiece generated or written struct - DO NOT STORE IT ANYWHERE, or at least set them to null afterwards * @param structurePiece generated or written struct - DO NOT STORE IT ANYWHERE, or at least set them to null
* afterwards
* @return this builder * @return this builder
*/ */
public Builder<T> addShape(String name, String[][] structurePiece) { public Builder<T> addShape(String name, String[][] structurePiece) {
@@ -80,22 +88,37 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
} }
builder.setLength(builder.length() - 1); builder.setLength(builder.length() - 1);
} }
Set<Vec3Impl> occupiedSpace = new HashSet<>();
// these track the global current location
int aa = 0, bb = 0, cc = 0;
// these track the vec3 towards next meaningful element
int a = 0, b = 0, c = 0; int a = 0, b = 0, c = 0;
// I do know these are pretty bad variable naming, but I have no better idea
for (int i = 0; i < builder.length(); i++) { for (int i = 0; i < builder.length(); i++) {
char ch = builder.charAt(i); char ch = builder.charAt(i);
if (ch == ' ' || ch == '~') { if (ch == ' ') {
builder.setCharAt(i, A); builder.setCharAt(i, A);
ch = A; ch = A;
} else if (ch == '~') {
builder.setCharAt(i, A);
ch = A;
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
} }
if (ch == A) { if (ch == A) {
aa++;
a++; a++;
} else if (ch == B) { } else if (ch == B) {
aa = 0;
a = 0; a = 0;
bb++;
b++; b++;
} else if (ch == C) { } else if (ch == C) {
aa = 0;
bb = 0;
a = 0; a = 0;
b = 0; b = 0;
c++; c++;
cc++;
} else if (a != 0 || b != 0 || c != 0) { } else if (a != 0 || b != 0 || c != 0) {
Vec3Impl vec3 = new Vec3Impl(a, b, c); Vec3Impl vec3 = new Vec3Impl(a, b, c);
Character navigate = navigates.get(vec3); Character navigate = navigates.get(vec3);
@@ -105,12 +128,19 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
addElement(navigate, step(vec3)); addElement(navigate, step(vec3));
} }
builder.setCharAt(i - 1, navigate); builder.setCharAt(i - 1, navigate);
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
aa++;
a = 0; a = 0;
b = 0; b = 0;
c = 0; c = 0;
} else {
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
aa++;
} }
} }
occupiedSpaces.put(name, occupiedSpace);
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", ""); String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
if (built.contains("+")) { if (built.contains("+")) {
@@ -123,33 +153,42 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
return this; return this;
} }
/**
* @deprecated use the unboxed version
*/
@Deprecated
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) { public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece); elements.putIfAbsent(name, structurePiece);
return this; return this;
} }
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
public IStructureDefinition<T> build() { public IStructureDefinition<T> build() {
Map<String, IStructureElement<T>[]> structures = compileStructureMap(); Map<String, IStructureElement<T>[]> structures = compileStructureMap();
if (StructureLibAPI.isDebugEnabled()) { return new StructureDefinition<>(
return new StructureDefinition<>(new HashMap<>(elements), new HashMap<>(shapes), structures); new HashMap<>(elements),
} else { new HashMap<>(shapes),
return structures::get; structures,
} occupiedSpaces);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<String, IStructureElement<T>[]> compileElementSetMap() { private Map<String, IStructureElement<T>[]> compileElementSetMap() {
Set<Integer> missing = findMissing(); Set<Integer> missing = findMissing();
if (missing.isEmpty()) { if (missing.isEmpty()) {
return shapes.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() return shapes.entrySet().stream().collect(
.chars() Collectors.toMap(
.mapToObj(c -> (char) c) Map.Entry::getKey,
.distinct() e -> e.getValue().chars().mapToObj(c -> (char) c).distinct().map(elements::get)
.map(elements::get) .toArray(IStructureElement[]::new)));
.toArray(IStructureElement[]::new)));
} else { } else {
throw new RuntimeException("Missing Structure Element bindings for (chars as integers): " throw new RuntimeException(
+ Arrays.toString(missing.toArray())); "Missing Structure Element bindings for (chars as integers): "
+ Arrays.toString(missing.toArray()));
} }
} }
@@ -157,22 +196,21 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
private Map<String, IStructureElement<T>[]> compileStructureMap() { private Map<String, IStructureElement<T>[]> compileStructureMap() {
Set<Integer> missing = findMissing(); Set<Integer> missing = findMissing();
if (missing.isEmpty()) { if (missing.isEmpty()) {
return shapes.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() return shapes.entrySet().stream().collect(
.chars() Collectors.toMap(
.mapToObj(c -> elements.get((char) c)) Map.Entry::getKey,
.toArray(IStructureElement[]::new))); e -> e.getValue().chars().mapToObj(c -> elements.get((char) c))
.toArray(IStructureElement[]::new)));
} else { } else {
throw new RuntimeException("Missing Structure Element bindings for (chars as integers): " throw new RuntimeException(
+ Arrays.toString(missing.toArray())); "Missing Structure Element bindings for (chars as integers): "
+ Arrays.toString(missing.toArray()));
} }
} }
private Set<Integer> findMissing() { private Set<Integer> findMissing() {
return shapes.values().stream() return shapes.values().stream().flatMapToInt(CharSequence::chars)
.flatMapToInt(CharSequence::chars) .filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
.filter(i -> !elements.containsKey((char) i))
.boxed()
.collect(Collectors.toSet());
} }
} }
@@ -194,4 +232,11 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
if (elements == null) throw new NoSuchElementException(name); if (elements == null) throw new NoSuchElementException(name);
return elements; return elements;
} }
@Override
public boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC) {
Set<Vec3Impl> occupiedSpace = occupiedSpaces.get(name);
if (occupiedSpace == null) throw new NoSuchElementException(name);
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
}
} }
@@ -0,0 +1,26 @@
package com.gtnewhorizon.structurelib.structure;
import java.util.function.Consumer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IChatComponent;
import net.minecraft.world.World;
/**
* Internal bridge class for easing implementation
*/
abstract class StructureElement_Bridge<T> implements IStructureElement<T> {
// seal the deprecated method to prevent accidental overrides
@Deprecated
@Override
public final PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger, IItemSource s,
EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
return survivalPlaceBlock(t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter));
}
// clear the default implementation. we don't need that.
public abstract PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env);
}
File diff suppressed because it is too large. Load diff
@@ -1,34 +1,48 @@
package com.gtnewhorizon.structurelib.structure; package com.gtnewhorizon.structurelib.structure;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
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 com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
class SurvivalBuildStructureWalker<T> implements IStructureWalker<T> { class SurvivalBuildStructureWalker<T> implements IStructureWalker<T> {
private final T object;
private final ItemStack trigger; final T object;
private final IItemSource source; final ItemStack trigger;
private final EntityPlayerMP actor;
private final int elementBudget; private final int elementBudget;
private final ISurvivalBuildEnvironment params;
private final boolean check; private final boolean check;
private int built = -1; private int built = -1;
public SurvivalBuildStructureWalker( private final AutoPlaceEnvironment env;
T object, ItemStack trigger, IItemSource source, EntityPlayerMP actor, int elementBudget, boolean check) {
public SurvivalBuildStructureWalker(T object, ItemStack trigger, int elementBudget,
ISurvivalBuildEnvironment params, IStructureDefinition<?> definition, String piece, ExtendedFacing facing,
int[] baseOffsetABC, boolean check) {
this.object = object; this.object = object;
this.trigger = trigger; this.trigger = trigger;
this.source = source;
this.actor = actor;
this.elementBudget = elementBudget; this.elementBudget = elementBudget;
this.params = params;
this.check = check; this.check = check;
env = new AutoPlaceEnvironment(
params.getActor(),
params.getActor()::addChatComponentMessage,
definition,
piece,
facing,
baseOffsetABC);
} }
@Override @Override
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z) { public boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
PlaceResult placeResult = element.survivalPlaceBlock( env.offsetABC[0] = a;
object, world, x, y, z, trigger, source, actor, actor::addChatComponentMessage); env.offsetABC[1] = b;
env.offsetABC[2] = c;
env.setSource(params.getSource());
PlaceResult placeResult = element.survivalPlaceBlock(object, world, x, y, z, trigger, env);
if (placeResult != PlaceResult.SKIP && built == -1) built = 0; if (placeResult != PlaceResult.SKIP && built == -1) built = 0;
switch (placeResult) { switch (placeResult) {
case SKIP: case SKIP:
@@ -37,7 +51,7 @@ class SurvivalBuildStructureWalker<T> implements IStructureWalker<T> {
if (check) element.check(object, world, x, y, z); if (check) element.check(object, world, x, y, z);
return ++built < elementBudget; return ++built < elementBudget;
case REJECT: case REJECT:
StructureLibAPI.markHintParticleError(actor, world, x, y, z); StructureLibAPI.markHintParticleError(params.getActor(), world, x, y, z);
return false; return false;
case ACCEPT_STOP: case ACCEPT_STOP:
if (check) element.check(object, world, x, y, z); if (check) element.check(object, world, x, y, z);
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.structure.adders;
import net.minecraft.block.Block; import net.minecraft.block.Block;
public interface IBlockAdder<T> { public interface IBlockAdder<T> {
/** /**
* Callback on block added, needs to check if block is valid (and add it) * Callback on block added, needs to check if block is valid (and add it)
* *
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.structure.adders;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
public interface ITileAdder<T> { public interface ITileAdder<T> {
/** /**
* Callback to add hatch, needs to check if tile is valid (and add it) * Callback to add hatch, needs to check if tile is valid (and add it)
* *
@@ -1,7 +1,7 @@
/** /**
* This package provide support for multiblock structure checking. * This package provide support for multiblock structure checking.
* <p> * <p>
* All public stuff in this package and its subpackages are considered part of the public API. * All public stuff in this package and its subpackages are considered part of the public API. Backwards compatibility
* Backwards compatibility is maintained to the maximum extend possible. * is maintained to the maximum extend possible.
*/ */
package com.gtnewhorizon.structurelib.structure; package com.gtnewhorizon.structurelib.structure;
@@ -2,10 +2,12 @@ package com.gtnewhorizon.structurelib.util;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import net.minecraft.inventory.IInventory; import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
public class InventoryIterable<Inv extends IInventory> implements Iterable<ItemStack> { public class InventoryIterable<Inv extends IInventory> implements Iterable<ItemStack> {
private final Inv inv; private final Inv inv;
private final int maxSlot; private final int maxSlot;
@@ -25,6 +27,7 @@ public class InventoryIterable<Inv extends IInventory> implements Iterable<ItemS
@Override @Override
public Iterator<ItemStack> iterator() { public Iterator<ItemStack> iterator() {
return new Iterator<ItemStack>() { return new Iterator<ItemStack>() {
private int ptr = 0; private int ptr = 0;
@Override @Override
@@ -1,32 +1,27 @@
/** /**
* Copyright (c) 2022, glee8e * Copyright (c) 2022, glee8e This file is part of StructureLib.
* This file is part of StructureLib.
* <p> * <p>
* StructureLib is free software; you can redistribute it and/or modify * StructureLib is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
* it under the terms of the GNU Lesser General Public License as published by * Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any
* the Free Software Foundation; either version 2 of the License, or * later version.
* (at your option) any later version.
* <p> * <p>
* StructureLib is distributed in the hope that it will be useful, * StructureLib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* but WITHOUT ANY WARRANTY; without even the implied warranty of * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * details.
* GNU Lesser General Public License for more details.
* <p> * <p>
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License along with Foobar; if not, write to the Free
* along with Foobar; if not, write to the Free Software * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
package com.gtnewhorizon.structurelib.util; package com.gtnewhorizon.structurelib.util;
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
import com.gtnewhorizon.structurelib.util.InventoryUtility.ItemStackExtractor.APIType;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
import java.util.*; import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.init.Items; import net.minecraft.init.Items;
@@ -34,10 +29,15 @@ import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryEnderChest; import net.minecraft.inventory.InventoryEnderChest;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap;
import com.gtnewhorizon.structurelib.util.InventoryUtility.ItemStackExtractor.APIType;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
/** /**
* This class is part of API, but is not stable. Use at your own risk. * This class is part of API, but is not stable. Use at your own risk.
*/ */
public class InventoryUtility { public class InventoryUtility {
private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>(); private static final SortedRegistry<ItemStackExtractor> stackExtractors = new SortedRegistry<>();
private static final List<Predicate<? super EntityPlayerMP>> enableEnder = new CopyOnWriteArrayList<>(); private static final List<Predicate<? super EntityPlayerMP>> enableEnder = new CopyOnWriteArrayList<>();
/** /**
@@ -47,6 +47,7 @@ public class InventoryUtility {
static { static {
inventoryProviders.register("5000-main-inventory", new InventoryProvider<InventoryIterable<InventoryPlayer>>() { inventoryProviders.register("5000-main-inventory", new InventoryProvider<InventoryIterable<InventoryPlayer>>() {
@Override @Override
public InventoryIterable<InventoryPlayer> getInventory(EntityPlayerMP player) { public InventoryIterable<InventoryPlayer> getInventory(EntityPlayerMP player) {
return new InventoryIterable<>(player.inventory, player.inventory.mainInventory.length); return new InventoryIterable<>(player.inventory, player.inventory.mainInventory.length);
@@ -59,8 +60,9 @@ public class InventoryUtility {
inv.getInventory().player.inventoryContainer.detectAndSendChanges(); inv.getInventory().player.inventoryContainer.detectAndSendChanges();
} }
}); });
inventoryProviders.register( inventoryProviders
"7000-ender-inventory", new InventoryProvider<InventoryIterable<InventoryEnderChest>>() { .register("7000-ender-inventory", new InventoryProvider<InventoryIterable<InventoryEnderChest>>() {
@Override @Override
public InventoryIterable<InventoryEnderChest> getInventory(EntityPlayerMP player) { public InventoryIterable<InventoryEnderChest> getInventory(EntityPlayerMP player) {
if (enableEnder.stream().anyMatch(p -> p.test(player))) if (enableEnder.stream().anyMatch(p -> p.test(player)))
@@ -86,8 +88,8 @@ public class InventoryUtility {
stackExtractors.register(key, val); stackExtractors.register(key, val);
} }
public static <Inv extends IInventory> void registerStackExtractor( public static <Inv extends IInventory> void registerStackExtractor(String key,
String key, Function<ItemStack, ? extends Inv> extractor) { Function<ItemStack, ? extends Inv> extractor) {
registerStackExtractor(key, newItemStackProvider(extractor)); registerStackExtractor(key, newItemStackProvider(extractor));
} }
@@ -95,8 +97,8 @@ public class InventoryUtility {
inventoryProviders.register(key, val); inventoryProviders.register(key, val);
} }
public static <Inv extends IInventory> void registerInventoryProvider( public static <Inv extends IInventory> void registerInventoryProvider(String key,
String key, Function<EntityPlayerMP, ? extends Inv> extractor) { Function<EntityPlayerMP, ? extends Inv> extractor) {
registerInventoryProvider(key, newInventoryProvider(extractor)); registerInventoryProvider(key, newInventoryProvider(extractor));
} }
@@ -107,6 +109,7 @@ public class InventoryUtility {
public static <Inv extends IInventory> InventoryProvider<InventoryIterable<Inv>> newInventoryProvider( public static <Inv extends IInventory> InventoryProvider<InventoryIterable<Inv>> newInventoryProvider(
Function<EntityPlayerMP, ? extends Inv> extractor) { Function<EntityPlayerMP, ? extends Inv> extractor) {
return new InventoryProvider<InventoryIterable<Inv>>() { return new InventoryProvider<InventoryIterable<Inv>>() {
@Override @Override
public InventoryIterable<Inv> getInventory(EntityPlayerMP player) { public InventoryIterable<Inv> getInventory(EntityPlayerMP player) {
Inv inv = extractor.apply(player); Inv inv = extractor.apply(player);
@@ -122,24 +125,26 @@ public class InventoryUtility {
public static ItemStackExtractor newItemStackProvider(Function<ItemStack, ? extends IInventory> extractor) { public static ItemStackExtractor newItemStackProvider(Function<ItemStack, ? extends IInventory> extractor) {
return new ItemStackExtractor() { return new ItemStackExtractor() {
@Override @Override
public boolean isAPIImplemented(APIType type) { public boolean isAPIImplemented(APIType type) {
return type == APIType.MAIN; return type == APIType.MAIN;
} }
@Override @Override
public int takeFromStack( public int takeFromStack(Predicate<ItemStack> predicate, boolean simulate, int count,
Predicate<ItemStack> predicate, ItemStackCounter store, ItemStack stack, ItemStack filter, EntityPlayerMP player) {
boolean simulate,
int count,
ItemStackCounter store,
ItemStack stack,
ItemStack filter,
EntityPlayerMP player) {
IInventory inv = extractor.apply(stack); IInventory inv = extractor.apply(stack);
if (inv == null) return 0; if (inv == null) return 0;
int found = takeFromInventory( int found = takeFromInventory(
new InventoryIterable<>(inv), predicate, simulate, count, store, filter, player, false); new InventoryIterable<>(inv),
predicate,
simulate,
count,
store,
filter,
player,
false);
if (found > 0) inv.markDirty(); if (found > 0) inv.markDirty();
return found; return found;
} }
@@ -147,8 +152,8 @@ public class InventoryUtility {
} }
/** /**
* take count amount of stacks that matches given filter. might take partial amount of stuff, so simulation is suggested * take count amount of stacks that matches given filter. might take partial amount of stuff, so simulation is
* if you ever need to take more than one. * suggested if you ever need to take more than one.
* *
* @param player source of stacks * @param player source of stacks
* @param predicate item stack filter * @param predicate item stack filter
@@ -156,8 +161,8 @@ public class InventoryUtility {
* @param count let's hope int size is enough... * @param count let's hope int size is enough...
* @return amount taken. never negative nor bigger than count... * @return amount taken. never negative nor bigger than count...
*/ */
public static Map<ItemStack, Integer> takeFromInventory( public static Map<ItemStack, Integer> takeFromInventory(EntityPlayerMP player, Predicate<ItemStack> predicate,
EntityPlayerMP player, Predicate<ItemStack> predicate, boolean simulate, int count) { boolean simulate, int count) {
ItemStackCounterImpl store = new ItemStackCounterImpl(); ItemStackCounterImpl store = new ItemStackCounterImpl();
int sum = 0; int sum = 0;
for (InventoryProvider<?> provider : inventoryProviders) { for (InventoryProvider<?> provider : inventoryProviders) {
@@ -168,8 +173,8 @@ public class InventoryUtility {
} }
/** /**
* take count amount of stacks that matches given filter. might take partial amount of stuff, so simulation is suggested * take count amount of stacks that matches given filter. might take partial amount of stuff, so simulation is
* if you ever need to take more than one. * suggested if you ever need to take more than one.
* *
* @param player source of stacks * @param player source of stacks
* @param filter the precise type of item to extract. stackSize matters * @param filter the precise type of item to extract. stackSize matters
@@ -189,14 +194,9 @@ public class InventoryUtility {
} }
// workaround java generics issue // workaround java generics issue
private static <R extends Iterable<ItemStack>> int takeFromPlayer( private static <R extends Iterable<ItemStack>> int takeFromPlayer(EntityPlayerMP player,
EntityPlayerMP player, Predicate<ItemStack> predicate, boolean simulate, int count, ItemStackCounterImpl store,
Predicate<ItemStack> predicate, InventoryProvider<R> provider, ItemStack filter) {
boolean simulate,
int count,
ItemStackCounterImpl store,
InventoryProvider<R> provider,
ItemStack filter) {
R inv = provider.getInventory(player); R inv = provider.getInventory(player);
if (inv == null) return 0; if (inv == null) return 0;
int taken = takeFromInventory(inv, predicate, simulate, count, store, filter, player, true); int taken = takeFromInventory(inv, predicate, simulate, count, store, filter, player, true);
@@ -215,29 +215,23 @@ public class InventoryUtility {
* @param recursive do recursive lookup using {@link #getStackExtractors() stack extractors} * @param recursive do recursive lookup using {@link #getStackExtractors() stack extractors}
* @return amount taken. never negative nor bigger than count... * @return amount taken. never negative nor bigger than count...
*/ */
public static Map<ItemStack, Integer> takeFromInventory( public static Map<ItemStack, Integer> takeFromInventory(Iterable<ItemStack> inv, Predicate<ItemStack> predicate,
Iterable<ItemStack> inv, Predicate<ItemStack> predicate, boolean simulate, int count, boolean recursive) { boolean simulate, int count, boolean recursive) {
ItemStackCounterImpl store = new ItemStackCounterImpl(); ItemStackCounterImpl store = new ItemStackCounterImpl();
takeFromInventory(inv, predicate, simulate, count, store, null, null, recursive); takeFromInventory(inv, predicate, simulate, count, store, null, null, recursive);
return store.getStore(); return store.getStore();
} }
private static int takeFromInventory( private static int takeFromInventory(@Nonnull Iterable<ItemStack> inv, @Nonnull Predicate<ItemStack> predicate,
@Nonnull Iterable<ItemStack> inv, boolean simulate, int count, @Nonnull ItemStackCounter store, @Nullable ItemStack filter,
@Nonnull Predicate<ItemStack> predicate, @Nullable EntityPlayerMP player, boolean recursive) {
boolean simulate,
int count,
@Nonnull ItemStackCounter store,
@Nullable ItemStack filter,
@Nullable EntityPlayerMP player,
boolean recursive) {
int found = 0; int found = 0;
ItemStack copiedFilter = null; ItemStack copiedFilter = null;
if (filter != null) { if (filter != null) {
copiedFilter = new ItemStack(filter.getItem(), filter.stackSize, Items.feather.getDamage(filter)); copiedFilter = new ItemStack(filter.getItem(), filter.stackSize, Items.feather.getDamage(filter));
copiedFilter.setTagCompound(filter.stackTagCompound); copiedFilter.setTagCompound(filter.stackTagCompound);
} }
for (Iterator<ItemStack> iterator = inv.iterator(); iterator.hasNext(); ) { for (Iterator<ItemStack> iterator = inv.iterator(); iterator.hasNext();) {
ItemStack stack = iterator.next(); ItemStack stack = iterator.next();
// invalid stack // invalid stack
if (stack == null || stack.getItem() == null || stack.stackSize <= 0) continue; if (stack == null || stack.getItem() == null || stack.stackSize <= 0) continue;
@@ -272,9 +266,10 @@ public class InventoryUtility {
} }
public interface OptimizedExtractor { public interface OptimizedExtractor {
/** /**
* Extract a particular type of item. The extractor can choose to not return all items contained within this item * Extract a particular type of item. The extractor can choose to not return all items contained within this
* as long as it makes sense, but the author should inform the player of this. * item as long as it makes sense, but the author should inform the player of this.
* <p> * <p>
* Whether optimized extractor do recursive extraction is at the discretion of implementor. * Whether optimized extractor do recursive extraction is at the discretion of implementor.
* *
@@ -288,12 +283,14 @@ public class InventoryUtility {
} }
public interface InventoryProvider<R extends Iterable<ItemStack>> { public interface InventoryProvider<R extends Iterable<ItemStack>> {
R getInventory(EntityPlayerMP player); R getInventory(EntityPlayerMP player);
void markDirty(R inv); void markDirty(R inv);
} }
public interface ItemStackExtractor { public interface ItemStackExtractor {
enum APIType { enum APIType {
MAIN, MAIN,
EXTRACT_ONE_STACK, EXTRACT_ONE_STACK,
@@ -302,13 +299,13 @@ public class InventoryUtility {
boolean isAPIImplemented(APIType type); boolean isAPIImplemented(APIType type);
/** /**
* Extract a particular type of item. The extractor can choose to not return all items contained within this item * Extract a particular type of item. The extractor can choose to not return all items contained within this
* as long as it makes sense, but the author should inform the player of this. * item as long as it makes sense, but the author should inform the player of this.
* <p> * <p>
* Whether this method does recursive extraction is at the discretion of implementor. * Whether this method does recursive extraction is at the discretion of implementor.
* *
* @param predicate the main filtering predicate. never null. It's assumed predicate always returns true on filter, * @param predicate the main filtering predicate. never null. It's assumed predicate always returns true on
* if that is not null * filter, if that is not null
* @param store where to store extracted items. Should increment the * @param store where to store extracted items. Should increment the
* @param source from where an extraction should be attempted * @param source from where an extraction should be attempted
* @param filter stack to extract. should not be mutated! match the NBT tag using EXACT mode. might be null. * @param filter stack to extract. should not be mutated! match the NBT tag using EXACT mode. might be null.
@@ -316,18 +313,12 @@ public class InventoryUtility {
* @param player executor of extraction, or null if from a machine * @param player executor of extraction, or null if from a machine
* @return amount extracted, or -1 if this is not implemented * @return amount extracted, or -1 if this is not implemented
*/ */
int takeFromStack( int takeFromStack(Predicate<ItemStack> predicate, boolean simulate, int count, ItemStackCounter store,
Predicate<ItemStack> predicate, ItemStack source, ItemStack filter, EntityPlayerMP player);
boolean simulate,
int count,
ItemStackCounter store,
ItemStack source,
ItemStack filter,
EntityPlayerMP player);
/** /**
* Extract a particular type of item. The extractor can choose to not return all items contained within this item * Extract a particular type of item. The extractor can choose to not return all items contained within this
* as long as it makes sense, but the author should inform the player of this. * item as long as it makes sense, but the author should inform the player of this.
* <p> * <p>
* Whether this method does recursive extraction is at the discretion of implementor. * Whether this method does recursive extraction is at the discretion of implementor.
* *
@@ -343,20 +334,15 @@ public class InventoryUtility {
static ItemStackExtractor createOnlyOptimized(@Nonnull OptimizedExtractor optimizedExtractor) { static ItemStackExtractor createOnlyOptimized(@Nonnull OptimizedExtractor optimizedExtractor) {
return new ItemStackExtractor() { return new ItemStackExtractor() {
@Override @Override
public boolean isAPIImplemented(APIType type) { public boolean isAPIImplemented(APIType type) {
return type == APIType.EXTRACT_ONE_STACK; return type == APIType.EXTRACT_ONE_STACK;
} }
@Override @Override
public int takeFromStack( public int takeFromStack(Predicate<ItemStack> predicate, boolean simulate, int count,
Predicate<ItemStack> predicate, ItemStackCounter store, ItemStack stack, ItemStack filter, EntityPlayerMP player) {
boolean simulate,
int count,
ItemStackCounter store,
ItemStack stack,
ItemStack filter,
EntityPlayerMP player) {
return 0; return 0;
} }
@@ -369,6 +355,7 @@ public class InventoryUtility {
} }
public interface ItemStackCounter { public interface ItemStackCounter {
/** /**
* Add some amount of stack. Note stackSize is used instead of stack.stackSize. * Add some amount of stack. Note stackSize is used instead of stack.stackSize.
*/ */
@@ -376,6 +363,7 @@ public class InventoryUtility {
} }
private static class ItemStackCounterImpl implements ItemStackCounter { private static class ItemStackCounterImpl implements ItemStackCounter {
private final Map<ItemStack, Integer> store = new ItemStackMap<>(true); private final Map<ItemStack, Integer> store = new ItemStackMap<>(true);
@Override @Override
@@ -1,9 +1,11 @@
package com.gtnewhorizon.structurelib.util; package com.gtnewhorizon.structurelib.util;
import java.util.Iterator; import java.util.Iterator;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
public class ItemStackArrayIterable implements Iterable<ItemStack> { public class ItemStackArrayIterable implements Iterable<ItemStack> {
private final ItemStack[] stacks; private final ItemStack[] stacks;
public ItemStackArrayIterable(ItemStack[] stacks) { public ItemStackArrayIterable(ItemStack[] stacks) {
@@ -17,6 +19,7 @@ public class ItemStackArrayIterable implements Iterable<ItemStack> {
@Override @Override
public Iterator<ItemStack> iterator() { public Iterator<ItemStack> iterator() {
return new Iterator<ItemStack>() { return new Iterator<ItemStack>() {
private int ptr = 0; private int ptr = 0;
@Override @Override
@@ -3,6 +3,7 @@ package com.gtnewhorizon.structurelib.util;
import java.util.Objects; import java.util.Objects;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
import java.util.function.Predicate; import java.util.function.Predicate;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.Item; import net.minecraft.item.Item;
@@ -11,6 +12,7 @@ import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
public final class ItemStackPredicate implements Predicate<ItemStack> { public final class ItemStackPredicate implements Predicate<ItemStack> {
public static ItemStackPredicate from(Item item) { public static ItemStackPredicate from(Item item) {
return new ItemStackPredicate(item); return new ItemStackPredicate(item);
} }
@@ -20,8 +22,7 @@ public final class ItemStackPredicate implements Predicate<ItemStack> {
} }
public static ItemStackPredicate from(ItemStack itemStack, NBTMode mode) { public static ItemStackPredicate from(ItemStack itemStack, NBTMode mode) {
return new ItemStackPredicate(itemStack.getItem()) return new ItemStackPredicate(itemStack.getItem()).setMeta(Items.feather.getDamage(itemStack))
.setMeta(Items.feather.getDamage(itemStack))
.setTag(mode, itemStack.stackTagCompound); .setTag(mode, itemStack.stackTagCompound);
} }
@@ -63,13 +64,16 @@ public final class ItemStackPredicate implements Predicate<ItemStack> {
} }
public enum NBTMode implements BiPredicate<NBTTagCompound, NBTTagCompound> { public enum NBTMode implements BiPredicate<NBTTagCompound, NBTTagCompound> {
IGNORE { IGNORE {
@Override @Override
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) { public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
return true; return true;
} }
}, },
IN { IN {
@Override @Override
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) { public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
if (lhs == null || lhs.hasNoTags()) return true; if (lhs == null || lhs.hasNoTags()) return true;
@@ -87,6 +91,7 @@ public final class ItemStackPredicate implements Predicate<ItemStack> {
} }
}, },
EXACT { EXACT {
@Override @Override
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) { public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
if (lhs != null && lhs.hasNoTags()) lhs = null; if (lhs != null && lhs.hasNoTags()) lhs = null;
@@ -95,6 +100,7 @@ public final class ItemStackPredicate implements Predicate<ItemStack> {
} }
}, },
IGNORE_KNOWN_INSIGNIFICANT_TAGS { IGNORE_KNOWN_INSIGNIFICANT_TAGS {
@Override @Override
public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) { public boolean test(NBTTagCompound lhs, NBTTagCompound rhs) {
// fast path for empty tags // fast path for empty tags
@@ -105,8 +111,8 @@ public final class ItemStackPredicate implements Predicate<ItemStack> {
return EXACT.test(lhs, rhs); return EXACT.test(lhs, rhs);
} }
}; };
private static final String[] KNOWN_INSIGNIFICANT_TAGS = {
"display", // TODO expand/refine this private static final String[] KNOWN_INSIGNIFICANT_TAGS = { "display", // TODO expand/refine this
}; };
} }
} }
@@ -1,12 +1,14 @@
package com.gtnewhorizon.structurelib.util; package com.gtnewhorizon.structurelib.util;
import java.util.Set; import java.util.Set;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
/** /**
* not related to a certain mod! * not related to a certain mod!
*/ */
public class MiscUtils { public class MiscUtils {
private MiscUtils() {} private MiscUtils() {}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@@ -4,6 +4,7 @@ import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
public class SortedRegistry<V> implements Iterable<V> { public class SortedRegistry<V> implements Iterable<V> {
private final NavigableMap<String, V> store = new TreeMap<>(); private final NavigableMap<String, V> store = new TreeMap<>();
/** /**
* Store stuff in an array for faster iteration access than a red black tree * Store stuff in an array for faster iteration access than a red black tree
@@ -1,14 +1,28 @@
package com.gtnewhorizon.structurelib.util; package com.gtnewhorizon.structurelib.util;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import net.minecraft.dispenser.IPosition; import net.minecraft.dispenser.IPosition;
import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.common.util.ForgeDirection;
public class Vec3Impl implements Comparable<Vec3Impl> { public class Vec3Impl implements Comparable<Vec3Impl> {
public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0); public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0);
private static final Map<Vec3Impl, Vec3Impl> pool = new HashMap<>();
private final int val0; private final int val0;
private final int val1; private final int val1;
private final int val2; private final int val2;
/**
* THIS WILL CAUSE THIS VECTOR TO PERMANENTLY RESIDE IN STATIC MEMORY. DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE
* DOING!!!
*/
public static Vec3Impl getFromPool(int in0, int in1, int in2) {
return pool.computeIfAbsent(new Vec3Impl(in0, in1, in2), Function.identity());
}
public Vec3Impl(int in0, int in1, int in2) { public Vec3Impl(int in0, int in1, int in2) {
this.val0 = in0; this.val0 = in0;
this.val1 = in1; this.val1 = in1;
@@ -57,8 +71,7 @@ public class Vec3Impl implements Comparable<Vec3Impl> {
} }
public Vec3Impl offset(ForgeDirection facing, int n) { public Vec3Impl offset(ForgeDirection facing, int n) {
return n == 0 return n == 0 ? this
? this
: new Vec3Impl(val0 + facing.offsetX * n, val1 + facing.offsetY * n, val2 + facing.offsetZ * n); : new Vec3Impl(val0 + facing.offsetX * n, val1 + facing.offsetY * n, val2 + facing.offsetZ * n);
} }
@@ -1,37 +1,20 @@
package com.gtnewhorizon.structurelib.util; package com.gtnewhorizon.structurelib.util;
/* /*
A subclass of java.util.random that implements the Xorshift random number * A subclass of java.util.random that implements the Xorshift random number generator <p> - it is 30% faster than the
generator * generator from Java's library - it produces random sequences of higher quality than java.util.Random - this class
<p> * also provides a clone() function <p> Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt();
- it is 30% faster than the generator from Java's library - it produces * //pull a random number <p> To use the class in legacy code, you may also instantiate an XSRandom object and assign it
random sequences of higher quality than java.util.Random - this class also * to a java.util.Random object: java.util.Random rand = new XSRandom(); <p> for an explanation of the algorithm, see
provides a clone() function * http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html
<p> * @author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs http://www.elmenreich.tk <p> This code is released
Usage: XSRandom rand = new XSRandom(); //Instantiation x = rand.nextInt(); * under the GNU Lesser General Public License Version 3 http://www.gnu.org/licenses/lgpl-3.0.txt
//pull a random number */
<p>
To use the class in legacy code, you may also instantiate an XSRandom object
and assign it to a java.util.Random object: java.util.Random rand = new
XSRandom();
<p>
for an explanation of the algorithm, see
http://demesos.blogspot.com/2011/09/pseudo-random-number-generators.html
@author Wilfried Elmenreich University of Klagenfurt/Lakeside Labs
* http://www.elmenreich.tk
* <p>
* This code is released under the GNU Lesser General Public License Version 3
* http://www.gnu.org/licenses/lgpl-3.0.txt
*/
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
/** /**
* XSTR - Xorshift ThermiteRandom * XSTR - Xorshift ThermiteRandom Modified by Bogdan-G 03.06.2016 version 0.0.4
* Modified by Bogdan-G
* 03.06.2016
* version 0.0.4
*/ */
public class XSTR extends Random { public class XSTR extends Random {
@@ -40,10 +23,11 @@ public class XSTR extends Random {
private static final long GAMMA = 0x9e3779b97f4a7c15L; private static final long GAMMA = 0x9e3779b97f4a7c15L;
private static final int PROBE_INCREMENT = 0x9e3779b9; private static final int PROBE_INCREMENT = 0x9e3779b9;
private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL; private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;
private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53) private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24) private static final float FLOAT_UNIT = 0x1.0p-24f; // 1.0f / (1 << 24)
private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L); private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L);
public static final XSTR XSTR_INSTANCE = new XSTR() { public static final XSTR XSTR_INSTANCE = new XSTR() {
@Override @Override
public synchronized void setSeed(long seed) { public synchronized void setSeed(long seed) {
if (!Thread.currentThread().getStackTrace()[2].getClassName().equals(Random.class.getName())) { if (!Thread.currentThread().getStackTrace()[2].getClassName().equals(Random.class.getName())) {
@@ -53,13 +37,11 @@ public class XSTR extends Random {
}; };
/* /*
MODIFIED BY: Robotia * MODIFIED BY: Robotia Modification: Implemented Random class seed generator
Modification: Implemented Random class seed generator */
*/
/** /**
* Creates a new pseudo random number generator. The seed is initialized to * Creates a new pseudo random number generator. The seed is initialized to the current time, as if by
* the current time, as if by
* <code>setSeed(System.currentTimeMillis());</code>. * <code>setSeed(System.currentTimeMillis());</code>.
*/ */
public XSTR() { public XSTR() {
@@ -79,8 +61,8 @@ public class XSTR extends Random {
} }
/** /**
* Creates a new pseudo random number generator, starting with the specified * Creates a new pseudo random number generator, starting with the specified seed, using
* seed, using <code>setSeed(seed);</code>. * <code>setSeed(seed);</code>.
* *
* @param seed the initial seed * @param seed the initial seed
*/ */
@@ -108,9 +90,8 @@ public class XSTR extends Random {
} }
/** /**
* Sets the seed for this pseudo random number generator. As described * Sets the seed for this pseudo random number generator. As described above, two instances of the same random
* above, two instances of the same random class, starting with the same * class, starting with the same seed, produce the same results, if the same methods are called.
* seed, produce the same results, if the same methods are called.
* *
* @param seed the new seed * @param seed the new seed
*/ */
@@ -128,9 +109,8 @@ public class XSTR extends Random {
} }
/** /**
* Implementation of George Marsaglia's elegant Xorshift random generator * Implementation of George Marsaglia's elegant Xorshift random generator 30% faster and better quality than the
* 30% faster and better quality than the built-in java.util.random see also * built-in java.util.random see also see http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
* see http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
* *
* @param nbits will shift nbits bits * @param nbits will shift nbits bits
* @return next seed * @return next seed
@@ -170,15 +150,14 @@ public class XSTR extends Random {
} }
/** /**
* Returns a pseudorandom, uniformly distributed {@code int} value between 0 * Returns a pseudorandom, uniformly distributed {@code int} value between 0 (inclusive) and the specified value
* (inclusive) and the specified value (exclusive), drawn from this random * (exclusive), drawn from this random number generator's sequence. The general contract of {@code nextInt} is that
* number generator's sequence. The general contract of {@code nextInt} is * one {@code int} value in the specified range is pseudorandomly generated and returned. All {@code bound} possible
* that one {@code int} value in the specified range is pseudorandomly * {@code int} values are produced with (approximately) equal probability. The method {@code nextInt(int bound)} is
* generated and returned. All {@code bound} possible {@code int} values are * implemented by class {@code Random} as if by:
* produced with (approximately) equal probability. The method *
* {@code nextInt(int bound)} is implemented by class {@code Random} as if * <pre>
* by: * {@code
* <pre> {@code
* public int nextInt(int bound) { * public int nextInt(int bound) {
* if (bound <= 0) * if (bound <= 0)
* throw new IllegalArgumentException("bound must be positive"); * throw new IllegalArgumentException("bound must be positive");
@@ -192,35 +171,29 @@ public class XSTR extends Random {
* val = bits % bound; * val = bits % bound;
* } while (bits - val + (bound-1) < 0); * } while (bits - val + (bound-1) < 0);
* return val; * return val;
* }}</pre> * }}
* </pre>
* *
* <p>The hedge "approx
* imately" is used in the foregoing description only because the next
* method is only approximately an unbiased source of independently chosen
* bits. If it were a perfect source of randomly chosen bits, then the
* algorithm shown would choose {@code int} values from the stated range
* with perfect uniformity.
* <p> * <p>
* The algorithm is slightly tricky. It rejects values that would result in * The hedge "approx imately" is used in the foregoing description only because the next method is only
* an uneven distribution (due to the fact that 2^31 is not divisible by n). * approximately an unbiased source of independently chosen bits. If it were a perfect source of randomly chosen
* The probability of a value being rejected depends on n. The worst case is * bits, then the algorithm shown would choose {@code int} values from the stated range with perfect uniformity.
* n=2^30+1, for which the probability of a reject is 1/2, and the expected
* number of iterations before the loop terminates is 2.
* <p> * <p>
* The algorithm treats the case where n is a power of two specially: it * The algorithm is slightly tricky. It rejects values that would result in an uneven distribution (due to the fact
* returns the correct number of high-order bits from the underlying * that 2^31 is not divisible by n). The probability of a value being rejected depends on n. The worst case is
* pseudo-random number generator. In the absence of special treatment, the * n=2^30+1, for which the probability of a reject is 1/2, and the expected number of iterations before the loop
* correct number of <i>low-order</i> bits would be returned. Linear * terminates is 2.
* congruential pseudo-random number generators such as the one implemented * <p>
* by this class are known to have short periods in the sequence of values * The algorithm treats the case where n is a power of two specially: it returns the correct number of high-order
* of their low-order bits. Thus, this special case greatly increases the * bits from the underlying pseudo-random number generator. In the absence of special treatment, the correct number
* length of the sequence of values returned by successive calls to this * of <i>low-order</i> bits would be returned. Linear congruential pseudo-random number generators such as the one
* method if n is a small power of two. * implemented by this class are known to have short periods in the sequence of values of their low-order bits.
* Thus, this special case greatly increases the length of the sequence of values returned by successive calls to
* this method if n is a small power of two.
* *
* @param bound the upper bound (exclusive). Must be positive. * @param bound the upper bound (exclusive). Must be positive.
* @return the next pseudorandom, uniformly distributed {@code int} value * @return the next pseudorandom, uniformly distributed {@code int} value between zero (inclusive) and {@code bound}
* between zero (inclusive) and {@code bound} (exclusive) from this random * (exclusive) from this random number generator's sequence
* number generator's sequence
* @throws IllegalArgumentException if bound is not positive * @throws IllegalArgumentException if bound is not positive
* @since 1.2 * @since 1.2
*/ */
@@ -230,18 +203,10 @@ public class XSTR extends Random {
// throw new RuntimeException("BadBound"); // throw new RuntimeException("BadBound");
// } // }
/*int r = next(31); /*
int m = bound - 1; * int r = next(31); int m = bound - 1; if ((bound & m) == 0) // i.e., bound is a power of 2 { r = (int) ((bound
if ((bound & m) == 0) // i.e., bound is a power of 2 * * (long) r) >> 31); } else { for (int u = r; u - (r = u % bound) + m < 0; u = next(31)) ; } return r;
{ */
r = (int) ((bound * (long) r) >> 31);
} else {
for (int u = r;
u - (r = u % bound) + m < 0;
u = next(31))
;
}
return r;*/
// speedup, new nextInt ~+40% // speedup, new nextInt ~+40%
long last = seed ^ seed << 21; long last = seed ^ seed << 21;
last ^= last >>> 35; last ^= last >>> 35;
@@ -269,10 +234,9 @@ public class XSTR extends Random {
@Override @Override
public void nextBytes(byte[] bytes_arr) { public void nextBytes(byte[] bytes_arr) {
for (int iba = 0, lenba = bytes_arr.length; iba < lenba; ) { for (int iba = 0, lenba = bytes_arr.length; iba < lenba;) {
for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); for (int rndba = nextInt(), nba = Math.min(lenba - iba, Integer.SIZE / Byte.SIZE); nba--
nba-- > 0; > 0; rndba >>= Byte.SIZE) {
rndba >>= Byte.SIZE) {
bytes_arr[iba++] = (byte) rndba; bytes_arr[iba++] = (byte) rndba;
} }
} }
+21 -1
View File
@@ -97,6 +97,18 @@ StructureLib API documentation.
breaking client code. breaking client code.
</p> </p>
<h2>{@link com.gtnewhorizon.structurelib.StructureEvent}</h2>
<p>
This class is the base class of all events that will be posted while a IStructureDefinition are visited.
These events are disabled by default, and can be enabled by calling {@link com.gtnewhorizon.structurelib.StructureLibAPI#enableInstrument(Object)}.
To receive these events, register an event handler to {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS}.
Handlers should filter the events received by {@link com.gtnewhorizon.structurelib.StructureEvent#getInstrumentIdentifier()}.
</p>
<p>
<b>These events are intended for third party tools to inspect structure details.</b> Normal users should not use
these at all.
</p>
<h1>Important Concepts</h1> <h1>Important Concepts</h1>
<h2>Context Object</h2> <h2>Context Object</h2>
<p> <p>
@@ -209,7 +221,7 @@ import com.gtnewhorizon.structurelib.structure.IStructureElement;
public class MyHatchElement&lt;T> implements IStructureElement&lt;T> { public class MyHatchElement&lt;T> implements IStructureElement&lt;T> {
//... //...
@Override {@literal @}Override
public PlaceResult survivalPlaceBlock( public PlaceResult survivalPlaceBlock(
Object o, Object o,
World world, World world,
@@ -231,5 +243,13 @@ public class MyHatchElement&lt;T> implements IStructureElement&lt;T> {
The code for placeBlock() and spawnHint() will be left as an exercise for the reader. The code for placeBlock() and spawnHint() will be left as an exercise for the reader.
<h2>Instrumenting StructureLib</h2>
<p>
StructureLib support third party tools to poke into structure's internals via events. This is done by listening to {@link com.gtnewhorizon.structurelib.StructureEvent}
and its subclasses on the {@link net.minecraftforge.common.MinecraftForge#EVENT_BUS forge main event bus}.
</p>
<p>
Please refer to {@link com.gtnewhorizon.structurelib.StructureEvent} to see what events are available.
</p>
</BODY> </BODY>
</HTML> </HTML>
@@ -28,7 +28,7 @@ item.structurelib.constructableTrigger.desc.3= (潜行右键可以直接完成
item.structurelib.constructableTrigger.desc.4=此物品堆叠数量会影响显示的机器等级/模式/类型 item.structurelib.constructableTrigger.desc.4=此物品堆叠数量会影响显示的机器等级/模式/类型
item.structurelib.constructableTrigger.desc.5=按下 §l[左SHIFT]§r 以显示子信道 item.structurelib.constructableTrigger.desc.5=按下 §l[左SHIFT]§r 以显示子信道
item.structurelib.constructableTrigger.desc.lshift.0=包含 %d 个子信道 item.structurelib.constructableTrigger.desc.lshift.0=包含 %d 个子信道
item.structurelib.constructableTrigger.too_fast=请在下一次自动搭建前等待%d毫秒... item.structurelib.constructableTrigger.too_fast=请在下一次自动搭建前等待 %d 毫秒...
item.structurelib.constructableTrigger.gui.key=信道名: item.structurelib.constructableTrigger.gui.key=信道名:
item.structurelib.constructableTrigger.gui.value=值: item.structurelib.constructableTrigger.gui.value=值:
item.structurelib.constructableTrigger.gui.add=添加 item.structurelib.constructableTrigger.gui.add=添加
@@ -39,8 +39,9 @@ item.structurelib.constructableTrigger.gui.wipe=§4全部清空
structurelib.autoplace.built_stat=已放置 %s 个方块 structurelib.autoplace.built_stat=已放置 %s 个方块
structurelib.autoplace.complete=§a这个多方块结构已经完成自动搭建! structurelib.autoplace.complete=§a这个多方块结构已经完成自动搭建!
structurelib.autoplace.error.not_enabled=§c这个多方块结构不支持在生存模式下自动搭建! structurelib.autoplace.error.not_enabled=§c这个多方块结构不支持在生存模式下自动搭建!
structurelib.autoplace.error.no_simple_block=§c建议放置§r%s§c,但没有找到它 structurelib.autoplace.error.no_simple_block=§c建议放置 §r%s§c ,但没有找到它
structurelib.autoplace.error.no_item_stack=§c建议使用§r%s§c放置方块,但没有找到它 structurelib.autoplace.error.no_item_stack=§c建议使用 §r%s§c 放置方块,但没有找到它
structurelib.autoplace.warning.no_explicit_channel=§6这个结构依赖于不存在的子信道 §r%s§6 。已使用主信道值替代。
structurelib.blockhint.desc.0=帮助你搭建机器 structurelib.blockhint.desc.0=帮助你搭建机器
structurelib.blockhint.0.name=提示方块:1号 structurelib.blockhint.0.name=提示方块:1号
@@ -0,0 +1,29 @@
package com.gtnewhorizon.structurelib.alignment.enumerable;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import net.minecraftforge.common.util.ForgeDirection;
import org.junit.jupiter.api.Test;
class ExtendedFacingTest {
@Test
void ensureGetWorldDirectionDoesNotCrashNorReturnNull() {
for (ExtendedFacing ef : ExtendedFacing.VALUES) {
for (ForgeDirection src : ForgeDirection.VALID_DIRECTIONS) {
assertNotNull(ef.getWorldDirection(src));
}
}
}
@Test
void ensureGetWorldDirectionIsCorrect() {
assertEquals(ForgeDirection.DOWN, ExtendedFacing.EAST_NORMAL_NONE.getWorldDirection(ForgeDirection.UP));
assertEquals(ForgeDirection.DOWN, ExtendedFacing.NORTH_NORMAL_NONE.getWorldDirection(ForgeDirection.UP));
assertEquals(ForgeDirection.NORTH, ExtendedFacing.NORTH_NORMAL_NONE.getWorldDirection(ForgeDirection.NORTH));
assertEquals(ForgeDirection.NORTH, ExtendedFacing.NORTH_CLOCKWISE_NONE.getWorldDirection(ForgeDirection.NORTH));
assertEquals(ForgeDirection.DOWN, ExtendedFacing.NORTH_CLOCKWISE_NONE.getWorldDirection(ForgeDirection.EAST));
}
}