Compare commits

...

48 Commits

Author SHA1 Message Date
Sebastian Hartte 5be0adbc89 Fixed Mixins that were targeting the wrong class/methods. 2020-09-19 02:48:59 +02:00
Sebastian Hartte d8e5f4fed5 Fixed formatating after migrating mappings. 2020-09-19 02:41:53 +02:00
Sebastian Hartte 47dfc19b7c Renamed mixin after mapping update. 2020-09-19 02:40:51 +02:00
Sebastian Hartte 45f5851079 Updated versions, and mappings. 2020-09-19 02:40:35 +02:00
Sebastian Hartte ce058b006c Fixes #4748: badly filtered inventory leads to crashes, also expose the ME storage again. 2020-09-18 03:11:33 +02:00
Sebastian Hartte 5aef82b9ef Optimized Imports 2020-09-16 21:35:47 +02:00
Sebastian Hartte 483f93f977 Post-merge fixes and item generator fix. 2020-09-16 20:12:38 +02:00
Sebastian Hartte c0575e11b0 Merge remote-tracking branch 'origin/master' into fabric/master
# Conflicts:
#	build.gradle
#	gradle.properties
#	gradle/scripts/artifacts.gradle
#	gradle/scripts/curseforge.gradle
#	gradle/wrapper/gradle-wrapper.jar
#	gradle/wrapper/gradle-wrapper.properties
#	src/main/java/appeng/block/networking/CableBusBlock.java
#	src/main/java/appeng/client/gui/AEBaseScreen.java
#	src/main/java/appeng/client/gui/implementations/InterfaceTerminalScreen.java
#	src/main/java/appeng/client/gui/widgets/Scrollbar.java
#	src/main/java/appeng/core/sync/packets/JEIRecipePacket.java
#	src/main/java/appeng/tile/AEBaseBlockEntity.java
#	src/main/resources/META-INF/mods.toml
2020-09-16 18:57:15 +02:00
Sebastian Hartte 749dd35f88 Updated forge and increased memory for IDE-sync daemon. 2020-09-16 18:18:22 +02:00
Sebastian Hartte ca79a377bd Merge remote-tracking branch 'origin/8.0.x-1.16.1'
# Conflicts:
#	gradle/scripts/dependencies.gradle
2020-09-16 17:49:59 +02:00
Sebastian Hartte 475c8ff5a1 Adapt the same sorting logic used for items stored in the network for items obtained from patterns, which fixes items from patterns not being able to be shift-clicked into new patterns. 2020-09-16 17:44:49 +02:00
yueh 5b3ccf26d0 Merge all gradle files (#4732)
* Merge all gradle files

The specific build files are now much smaller, so a single one is mostly acceptable now.
Also updates Gradle to 5.6.3

* Disable the Mixin annotation processor when importing this project into IntelliJ since it should only be needed to generate refmaps on an actual build.

* Removed Sonarqube and JaCoCo plugins.

Co-authored-by: Sebastian Hartte <sebastian@hartte.de>
2020-09-14 14:14:00 +02:00
Sebastian Hartte e73dbe4891 Updated Forge / MC to 1.16.3
Keep Mappings
Allow higher range for MC + Forge versions
2020-09-14 02:02:59 +02:00
Sebastian Hartte 38244000c9 Fixes #4731: Take the position where the user clicked the handle into account when calculating the new scrollbar position.
Also fixes: The scrollbar did not subtract the handle height when determining the available space for moving the handle, thus introducing drift the further down a user scrolled.
Also fixes: The scrollbar did not resize it's trackbar when the ME inventory screens were resized and the number of rows changed, same for craft confirm.
2020-09-13 23:12:59 +02:00
Sebastian Hartte d87ff9d1e7 Formatting fixes. 2020-09-13 22:18:38 +02:00
Sebastian Hartte 4ba97ec899 Remove unnecessary debug prints, and rename methods. 2020-09-13 22:11:40 +02:00
Sebastian Hartte 0147e1ddfb Fixes #4730 and also introduces repeat-pageup/pagedown when holding the mouse on the area above or below the scrollbar handle. 2020-09-13 22:09:47 +02:00
yueh 621d2ee715 Fixes #4728: Mark all offloading threads as daemons (#4729) 2020-09-13 11:55:18 +02:00
Sebastian Hartte 31ecdfbe0a Use the correct method to update an entities position forcefully. 2020-09-11 18:04:17 +02:00
Sebastian Hartte f0bfbfec48 Should also actually _use_ the fixed invoker... 2020-09-11 17:25:39 +02:00
Sebastian Hartte 06a8843a65 Fix a Mixin bug that causes an Invoker conflict with Patchouli.
See https://github.com/SpongePowered/Mixin/issues/430
2020-09-11 17:20:20 +02:00
shartte d07937fa93 Render Performance Improvements (#4721)
* Return a fixed bounding box for frustrum culling of AE tile entity renderers to prevent Forge from re-retrieving the TE from the world and calculating the collision box (very costly for cables).

* Changed dynamic cable bus lighting to be based on block states to improved rendering speed and fix optifine issues. (Potential fix for #4716).
2020-09-10 22:56:50 +02:00
shartte 65f58913a4 Make players assume ownership of networks when they place security stations onto unsecured ones (#4714)
* Fixes #4712: When a security station is placed onto an unsecured network, the placer assumes ownership of the entire network. Otherwise the contiguous network would not necessarily reconnect in the same way when the chunk is reloaded due to differing player-ids throughout the network.
In addition, changes to the node's owner were not being persisted due to the host never being marked as dirty.

* Fix formatting
2020-09-10 22:34:16 +02:00
Sebastian Hartte 2bca23d75d Rebuilt the recipe transfer after upstream changes. 2020-09-10 22:05:06 +02:00
Sebastian Hartte b47543d166 Merge remote-tracking branch 'origin/master' into fabric/master
# Conflicts:
#	gradle.properties
#	src/main/java/appeng/core/Registration.java
#	src/main/java/appeng/core/sync/packets/JEIRecipePacket.java
#	src/main/java/appeng/entity/TinyTNTPrimedEntity.java
#	src/main/java/appeng/fluids/client/gui/widgets/FluidSlotWidget.java
#	src/main/java/appeng/fluids/container/FluidConfigurableContainer.java
#	src/main/java/appeng/integration/modules/jei/FacadeRegistryPlugin.java
#	src/main/java/appeng/integration/modules/jei/JEIPlugin.java
#	src/main/java/appeng/integration/modules/jei/RecipeTransferHandler.java
#	src/main/java/appeng/mixins/structure/BiomeAccessor.java
#	src/main/java/appeng/parts/automation/AnnihilationPlanePart.java
#	src/main/java/appeng/parts/automation/FormationPlanePart.java
2020-09-10 19:20:58 +02:00
Sebastian Hartte a340925d98 Fix Wireless screen translated text. 2020-09-10 19:06:00 +02:00
yueh b8e53adf40 Fixes #4719: Take the entity size into account when spawning item entities (#4720) 2020-09-10 17:54:02 +02:00
yueh 44544c598f Fixes #4253: Just transfer the recipe id instead of every ingredient. (#4689)
* Fixes #4253: Just transfer the recipe id instead of every ingredient.
* If possible handle any IRecipe for processing mode
* Improvements and support for AE2 facades.
* Switched to using JEI's displayed stacks (which means we can remove the shapeless variant).

Co-authored-by: Sebastian Hartte <sebastian@hartte.de>
2020-09-09 14:08:24 +02:00
Sebastian Hartte 0be7322c2c Formatting Fixes. 2020-09-09 02:27:12 +02:00
Sebastian Hartte a521e827db Merge remote-tracking branch 'origin/master' into fabric/master
# Conflicts:
#	src/main/java/appeng/core/api/definitions/ApiItems.java
2020-09-09 02:25:15 +02:00
Sebastian Hartte bcee2ec427 Slightly increased the height of the item hitbox for crystals to make their pickup by annihilation planes less "clippy". 2020-09-09 02:23:58 +02:00
Sebastian Hartte 17648633fe Merge fixes. 2020-09-09 02:23:14 +02:00
Sebastian Hartte fcbebd4ffc Merge remote-tracking branch 'origin/master' into fabric/master
# Conflicts:
#	gradle.properties
#	src/api/java/appeng/api/implementations/items/IGrowableCrystal.java
#	src/main/java/appeng/core/AEConfig.java
#	src/main/java/appeng/core/api/definitions/ApiBlocks.java
#	src/main/java/appeng/core/features/registries/LocatableRegistry.java
#	src/main/java/appeng/decorative/solid/QuartzGlassBlock.java
#	src/main/java/appeng/decorative/solid/SkyStoneBlock.java
#	src/main/java/appeng/entity/GrowingCrystalEntity.java
#	src/main/java/appeng/fluids/parts/FluidImportBusPart.java
#	src/main/java/appeng/items/misc/CrystalSeedItem.java
#	src/main/java/appeng/items/tools/powered/ColorApplicatorItem.java
#	src/main/java/appeng/me/cluster/implementations/QuantumCluster.java
#	src/main/java/appeng/parts/CableBusContainer.java
#	src/main/java/appeng/tile/AEBaseBlockEntity.java
2020-09-09 01:41:49 +02:00
Sebastian Hartte 9e41d543f5 Merge remote-tracking branch 'origin/8.0.x-1.16.1' 2020-09-09 00:59:58 +02:00
Sebastian Hartte 81cc0b804d Fixes #4699: AE's glass should only hide it's sides adjacent to other AE glass.
Also disables spawn on AE's glass same as Vanilla glass.
2020-09-09 00:58:14 +02:00
Sebastian Hartte e1fc9908b3 Fixes #4693: Fix color applicator not working on cable-buses. 2020-09-09 00:51:45 +02:00
shartte 9ed1535477 Fixes #4661: Do not update a tile entity when it's in an unloaded chunk (i.e. during chunk unloading) (#4708) 2020-09-08 22:28:30 +02:00
Sebastian Hartte 1ef345d4a4 Formatting fixes. 2020-09-08 22:24:27 +02:00
Sebastian Hartte ff7d1b9719 Fixes #4704: QNB is not finding a registered QNB because it believes the chunk to be unloaded.
Also adds debug logging to the locatable registry to better debug this issues in the future.
2020-09-08 22:20:33 +02:00
shartte 88c9370e4f Crystal Growth Refactor (#4702)
* Refactored crystal in-world purification:
- Removed iterative formula
- Set the default growth factor to 1 rather than 0.5 to simplify settings
- Made the growth-tick-progress-per-tick a configuration setting for 0-6 accelerators
- Unfinished seeds now slowly sink when ejected by an annihilation plane or other system (emulates a player throwing it in)
- Instead of checking for "isLiquid", we now check for a new tag (appliedenergistics2:crystal_purification_medium) to check if the crystal is submerged in an allowed fluid for growth, defaults to #minecraft:water.

* Removed configuration options for changing how accelerators work,
replaced with improved fluid tag option with a clamped multiplier [1,10].
2020-09-08 18:10:45 +02:00
Sebastian Hartte f2aae3ef0f Added a global Voxelshape cache for cable bus voxel shapes, since combining voxel shapes turns out to be an extremely expensive process (i.e. 1.7s for 10000x5 shapes). 2020-09-08 17:56:02 +02:00
Sebastian Hartte 12739f1e66 Remove unneeded cable-bus inventory-model, which caused the part models to be loaded twice leading to unnecessary memory-usage. 2020-09-08 17:53:14 +02:00
shartte 81d2170ada Implemented a cache with maximum quad-count it will cache for the cable bus, (#4670)
which also fixes concurrent access to the hashmap.

(cherry picked from commit b756d68be1)
2020-09-08 14:28:08 +02:00
Sebastian Hartte 0f469e9df6 Bumped Forge version. 2020-09-07 23:34:56 +02:00
Sebastian Hartte 0f34f09b32 Remove object-scanning subscriptions and remove unused test-file. 2020-09-06 20:59:37 +02:00
Sebastian Hartte 8e54a8fed9 Fixes a problem where tile entities would become unremovable (well, one more of these at least),
because they would reset their block state upon removal and loss of power.
2020-09-06 20:59:19 +02:00
Sebastian Hartte d9bb81a00f Fixes #4680: Facade advancement is showing an untextured facade. 2020-09-03 17:36:20 +02:00
yueh 39e038d916 Fixes #4683: Hopefully LazyOptional works as advertised (#4684) 2020-09-02 21:36:40 +02:00
84 changed files with 1670 additions and 1856 deletions
+211 -81
View File
@@ -47,84 +47,6 @@ repositories {
}
}
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
compileJava {
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
options.deprecation = false
}
// ensure everything uses UTF-8 and not some random codepage chosen by gradle
compileJava.options.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
// Create version number
ext.pr = System.getenv('PR_NUMBER') ?: ""
if (ext.pr) {
version = version + "+pr." + ext.pr
}
ext.branch = System.getenv('BRANCH') ?: ""
if (ext.branch) {
version = version + "+branch." + ext.branch
}
ext.tag = System.getenv('TAG') ?: ""
if (ext.tag && ext.tag.startsWith("fabric/v")) {
version = ext.tag.substring("fabric/v".length())
// Validate that the rest is a semver version
if (version ==~ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) {
println("::set-env name=VERSION::${version}")
} else {
throw new GradleException("Invalid semver: $version")
}
}
// Maven group and artifact name
group = artifact_group
archivesBaseName = artifact_basename
sourceSets {
api
main {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
resources {
srcDir 'src/generated/resources'
}
}
datagen {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
test {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
}
siteexport {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
configurations {
apiCompile.extendsFrom(compileClasspath)
datagenCompile.extendsFrom(compileClasspath)
siteexportCompile.extendsFrom(compileClasspath)
siteexportRuntime.extendsFrom(runtimeClasspath)
}
apply from: 'gradle/scripts/spotless.gradle'
minecraft {
accessWidener "src/main/resources/appliedenergistics2.accesswidener"
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
@@ -163,6 +85,87 @@ dependencies {
// unit test dependencies
testCompile "junit:junit:4.13"
}
group = artifact_group
archivesBaseName = artifact_basename
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
compileJava {
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
options.deprecation = false
}
// ensure everything uses UTF-8 and not some random codepage chosen by gradle
compileJava.options.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
///////////////////
// Version Number
ext.pr = System.getenv('PR_NUMBER') ?: ""
if (ext.pr) {
version = version + "+pr." + ext.pr
}
ext.branch = System.getenv('BRANCH') ?: ""
if (ext.branch) {
version = version + "+branch." + ext.branch
}
ext.tag = System.getenv('TAG') ?: ""
if (ext.tag && ext.tag.startsWith("fabric/v")) {
version = ext.tag.substring("fabric/v".length())
// Validate that the rest is a semver version
if (version ==~ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) {
println("::set-env name=VERSION::${version}")
} else {
throw new GradleException("Invalid semver: $version")
}
}
ext.isAlpha = project.version.contains("alpha")
ext.isBeta = project.version.contains("beta")
sourceSets {
api
main {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
resources {
srcDir 'src/generated/resources'
}
}
datagen {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
test {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
}
siteexport {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
configurations {
apiCompile.extendsFrom(compileClasspath)
datagenCompile.extendsFrom(compileClasspath)
siteexportCompile.extendsFrom(compileClasspath)
siteexportRuntime.extendsFrom(runtimeClasspath)
}
////////////////////
// Forge/Minecraft
minecraft {
accessWidener "src/main/resources/appliedenergistics2.accesswidener"
}
processResources {
inputs.property "version", project.version
@@ -177,10 +180,76 @@ processResources {
}
}
apply from: 'gradle/scripts/artifacts.gradle'
apply from: 'gradle/scripts/curseforge.gradle'
//////////////
// Aritfacts
processResources {
exclude '.cache'
}
// configure the maven publication
jar {
finalizedBy 'remapJar'
from sourceSets.main.output.classesDirs
from sourceSets.api.output.classesDirs
from sourceSets.main.output.resourcesDir
from sourceSets.api.output.resourcesDir
manifest {
attributes([
"Specification-Title": "Applied Energistics 2",
"Specification-Vendor": "TeamAppliedEnergistics",
"Specification-Version": "${project.version}",
"Implementation-Title": "${project.name}",
"Implementation-Version": "${project.version}",
"Implementation-Vendor" :"TeamAppliedEnergistics",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
"MixinConfigs": "appliedenergistics2.mixins.json"
])
}
}
task javadocs(type: Javadoc) {
classpath = sourceSets.main.compileClasspath
source = sourceSets.api.java
include "appeng/api/**"
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
options.charSet = 'UTF-8'
}
task javadocJar(type: Jar, dependsOn: javadocs) {
classifier = "javadoc"
from javadoc.destinationDir
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
from sourceSets.api.allSource
}
task apiJar(type: Jar) {
classifier = "api"
from sourceSets.api.output
include "appeng/api/**"
// TODO: when FG bug is fixed, remove allJava from the api jar.
// https://github.com/MinecraftForge/ForgeGradle/issues/369
// Gradle should be able to pull them from the -sources jar.
from sourceSets.api.allJava
}
artifacts {
archives javadocJar
archives sourcesJar
archives apiJar
}
//////////////////
// Maven publish
publishing {
if (!version.endsWith("-SNAPSHOT")) {
publications {
@@ -239,3 +308,64 @@ task runSiteExport(type: RunClientTask, dependsOn: downloadAssets, group: "ae2",
classpath sourceSets.main.output
classpath sourceSets.siteexport.output
}
/////////////
// Spotless
spotless {
java {
target 'src/*/java/appeng/**/*.java'
indentWithSpaces()
eclipse().configFile 'codeformat/codeformat.xml'
importOrderFile 'codeformat/ae2.importorder'
}
format 'json', {
target 'src/*/resources/**/*.json'
targetExclude 'src/generated/resources/**'
prettier().config(['parser': 'json'])
}
}
////////////////
// Curse Forge
if (System.getenv("CURSEFORGE") && !version.endsWith("-SNAPSHOT")) {
def cfReleaseType = "release"
if (ext.isAlpha) {
cfReleaseType = "alpha"
} else if (ext.isBeta) {
cfReleaseType = "beta"
}
curseforge {
apiKey = System.getenv("CURSEFORGE")
project {
id = project.curseforge_project
changelogType = "markdown"
changelog = System.getenv("CHANGELOG") ?: "Please visit our [releases](https://github.com/AppliedEnergistics/Applied-Energistics-2/releases) for a changelog"
releaseType = cfReleaseType
addGameVersion project.minecraft_version
addGameVersion "Fabric"
mainArtifact(remapJar.archiveFile) {
displayName = "${project.version} [FABRIC]"
}
}
options {
forgeGradleIntegration = false
}
afterEvaluate {
tasks.getByName("curseforge${project.curseforge_project}").dependsOn remapJar
}
}
}
////////////////
// IntelliJ Project Import
// The Mixin annotation process does not have an obfuscation source when running through the IntelliJ compiler,
// thus we have to prevent it from being activated as part of importing this Gradle project into IntelliJ.
if (System.getProperty("idea.sync.active") == "true") {
afterEvaluate {
tasks.withType(JavaCompile).all {
it.options.annotationProcessorPath = files()
}
}
}
+5 -10
View File
@@ -3,24 +3,19 @@ version=0.0.0-SNAPSHOT
artifact_group=appeng
artifact_basename=appliedenergistics2-fabric
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2G
# Fabric Properties
# Check these on https://modmuss50.me/fabric.html
minecraft_version=1.16.2
yarn_mappings=1.16.2+build.6
loader_version=0.9.2+build.206
minecraft_version=1.16.3
yarn_mappings=1.16.3+build.11
loader_version=0.9.3+build.207
#Fabric api
fabric_version=0.20.1+build.401-1.16
fabric_version=0.21.0+build.407-1.16
loom_version=0.4-SNAPSHOT
# Kotlin
kotlin_version=1.3.61
fabric_kotlin_version=1.3.61+build.1
loom_version=0.5-SNAPSHOT
# Dependencies
libblockattributes_version=0.8.0
-78
View File
@@ -1,78 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
processResources {
exclude '.cache'
}
jar {
finalizedBy 'remapJar'
from sourceSets.main.output.classesDirs
from sourceSets.api.output.classesDirs
from sourceSets.main.output.resourcesDir
from sourceSets.api.output.resourcesDir
manifest {
attributes([
"Specification-Title": "Applied Energistics 2",
"Specification-Vendor": "TeamAppliedEnergistics",
"Specification-Version": "${project.version}",
"Implementation-Title": "${project.name}",
"Implementation-Version": "${project.version}",
"Implementation-Vendor" :"TeamAppliedEnergistics",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
])
}
}
task javadocs(type: Javadoc) {
classpath = sourceSets.main.compileClasspath
source = sourceSets.api.java
include "appeng/api/**"
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
options.charSet = 'UTF-8'
}
task javadocJar(type: Jar, dependsOn: javadocs) {
classifier = "javadoc"
from javadoc.destinationDir
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
from sourceSets.api.allSource
}
task apiJar(type: Jar) {
classifier = "api"
from sourceSets.api.output
include "appeng/api/**"
}
artifacts {
archives javadocJar
archives sourcesJar
archives apiJar
}
-50
View File
@@ -1,50 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, TeamAppliedEnergistics, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*g GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
def isAlpha = project.version.contains("alpha")
def isBeta = project.version.contains("beta")
def cfReleaseType = "release"
if (isAlpha) {
cfReleaseType = "alpha"
} else if (isBeta) {
cfReleaseType = "beta"
}
if (System.getenv("CURSEFORGE") && !version.endsWith("-SNAPSHOT")) {
curseforge {
apiKey = System.getenv("CURSEFORGE")
project {
id = project.curseforge_project
changelogType = "markdown"
changelog = System.getenv("CHANGELOG") ?: "Please visit our [releases](https://github.com/AppliedEnergistics/Applied-Energistics-2/releases) for a changelog"
releaseType = cfReleaseType
addGameVersion project.minecraft_version
addGameVersion "Fabric"
mainArtifact(remapJar.archiveFile) {
displayName = "${project.version} [FABRIC]"
}
}
options {
forgeGradleIntegration = false
}
afterEvaluate {
tasks.getByName("curseforge${project.curseforge_project}").dependsOn remapJar
}
}
}
-42
View File
@@ -1,42 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
repositories {
mavenLocal()
maven { // modmaven, maven proxy
name 'modmaven'
url "https://modmaven.k-4u.nl/"
}
}
dependencies {
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
// compile against provided APIs
compileOnly "mezz.jei:jei-${jei_minecraft_version}:${jei_version}:api"
compileOnly "mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}:api"
// Runtime, Mods
runtimeOnly fg.deobf("mezz.jei:jei-${jei_minecraft_version}:${jei_version}")
runtimeOnly fg.deobf("mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}")
//runtimeOnly fg.deobf("team.chisel.ctm:CTM:${ctm_version}")
// unit test dependencies
testCompile "junit:junit:4.13"
}
-32
View File
@@ -1,32 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*g GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
spotless {
java {
target 'src/*/java/appeng/**/*.java'
indentWithSpaces()
eclipse().configFile 'codeformat/codeformat.xml'
importOrderFile 'codeformat/ae2.importorder'
}
format 'json', {
target 'src/*/resources/**/*.json'
targetExclude 'src/generated/resources/**'
prettier().config(['parser': 'json'])
}
}
-1
View File
@@ -10,7 +10,6 @@ pluginManagement {
plugins {
id "fabric-loom" version loom_version
id "org.jetbrains.kotlin.jvm" version kotlin_version
}
}
@@ -53,10 +53,10 @@ public interface ICraftingHelper {
* @param stack If null, a new item will be created to hold the encoded pattern.
* Otherwise the given item must already contains an encoded
* pattern that will be overwritten.
* @throws IllegalArgumentException If either in or out contain only empty
* ItemStacks.
* @return A new encoded pattern, or the given stack with the pattern encoded in
* it.
* @throws IllegalArgumentException If either in or out contain only empty
* ItemStacks.
*/
ItemStack encodeProcessingPattern(@Nullable ItemStack stack, ItemStack[] in, ItemStack[] out);
@@ -23,13 +23,23 @@
package appeng.api.implementations.items;
import net.minecraft.block.Block;
import net.minecraft.block.Material;
import javax.annotation.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public interface IGrowableCrystal {
ItemStack triggerGrowth(ItemStack is);
float getMultiplier(Block blk, Material mat);
/**
* Gets the crystal growth multiplier based on the material it is submerged in.
*
* @param state The blockstate of the block the crystal is currently in.
* @return The multiplier for the growth tick progress. Zero if the crystal
* cannot grow in the current material.
*/
float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos);
}
@@ -28,4 +28,9 @@ public enum GridNotification {
* the visible connections for this node have changed, useful for cable.
*/
CONNECTIONS_CHANGED,
/**
* the owner of the grid node has changed, and the node needs to be re-saved
*/
OWNER_CHANGED
}
@@ -87,7 +87,8 @@ public interface IGridBlock {
AEColor getGridColor();
/**
* Notifies your IGridBlock that changes were made to your connections
* Called by the {@link IGridNode} to notify its {@link IGridBlock} about
* events.
*/
void onGridNotification(@Nonnull GridNotification notification);
@@ -86,7 +86,7 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusBlockEntity> implemen
public CableBusBlock() {
super(defaultProps(AEMaterials.GLASS).nonOpaque().dropsNothing().dynamicBounds()
.lightLevel(state -> state.get(LIGHT_LEVEL)));
.luminance(state -> state.get(LIGHT_LEVEL)));
setDefaultState(getDefaultState().with(LIGHT_LEVEL, 0));
}
@@ -38,11 +38,11 @@ import appeng.helpers.Splotch;
*/
class PaintSplotchesBakedModel implements BakedModel, FabricBakedModel {
private static final SpriteIdentifier TEXTURE_PAINT1 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_PAINT1 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/paint1"));
private static final SpriteIdentifier TEXTURE_PAINT2 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_PAINT2 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/paint2"));
private static final SpriteIdentifier TEXTURE_PAINT3 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_PAINT3 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/paint3"));
private final Sprite[] textures;
@@ -36,18 +36,18 @@ import appeng.core.AppEng;
class QnbFormedBakedModel implements BakedModel, FabricBakedModel {
private static final SpriteIdentifier TEXTURE_LINK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_LINK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/quantum_link"));
private static final SpriteIdentifier TEXTURE_RING = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_RING = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/quantum_ring"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/quantum_ring_light"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "block/quantum_ring_light"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT_CORNER = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEX, new Identifier(AppEng.MOD_ID, "block/quantum_ring_light_corner"));
private static final SpriteIdentifier TEXTURE_CABLE_GLASS = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "part/cable/glass/transparent"));
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "block/quantum_ring_light_corner"));
private static final SpriteIdentifier TEXTURE_CABLE_GLASS = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "part/cable/glass/transparent"));
private static final SpriteIdentifier TEXTURE_COVERED_CABLE = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEX, new Identifier(AppEng.MOD_ID, "part/cable/covered/transparent"));
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "part/cable/covered/transparent"));
private static final float DEFAULT_RENDER_MIN = 2.0f;
private static final float DEFAULT_RENDER_MAX = 14.0f;
@@ -300,22 +300,36 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
}
}
if (this.getScrollBar() != null) {
this.getScrollBar().click(xCoord - this.x, yCoord - this.y);
// Forward left mouse button down events to the scrollbar
if (btn == 0 && this.getScrollBar() != null) {
if (this.getScrollBar().mouseDown(xCoord - this.x, yCoord - this.y)) {
return true;
}
}
return super.mouseClicked(xCoord, yCoord, btn);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double dragX, double dragY) {
public boolean mouseReleased(double mouseX, double mouseY, int button) {
// Forward left mouse button up events to the scrollbar
if (button == 0 && this.getScrollBar() != null) {
if (this.getScrollBar().mouseUp(mouseX - this.x, mouseY - this.y)) {
return true;
}
}
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double dragX, double dragY) {
final Slot slot = this.getSlot((int) mouseX, (int) mouseY);
final ItemStack itemstack = getPlayer().inventory.getCursorStack();
if (this.getScrollBar() != null) {
// FIXME: Coordinate system of mouseX/mouseY is unclear
this.getScrollBar().click((int) mouseX - this.x, (int) mouseY - this.y);
this.getScrollBar().mouseDragged((int) mouseX - this.x, (int) mouseY - this.y);
}
if (slot instanceof FakeSlot && !itemstack.isEmpty()) {
@@ -798,6 +812,11 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
public void tick() {
super.tick();
if (this.getScrollBar() != null) {
this.getScrollBar().tick();
}
for (Element child : children) {
if (child instanceof ITickingWidget) {
((ITickingWidget) child).tick();
@@ -93,6 +93,8 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
addButton(new ButtonWidget(this.x + 6, this.y + this.backgroundHeight - 25, 50, 20, GuiText.Cancel.text(),
btn -> subGui.goBack()));
this.setScrollBar();
}
@Override
@@ -70,8 +70,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
public InterfaceTerminalScreen(InterfaceTerminalContainer container, PlayerInventory playerInventory, Text title) {
super(container, playerInventory, title);
final Scrollbar scrollbar = new Scrollbar();
this.setScrollBar(scrollbar);
this.setScrollBar(new Scrollbar().setLeft(175).setTop(18).setHeight(106));
this.backgroundWidth = 195;
this.backgroundHeight = 222;
}
@@ -80,10 +79,6 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
public void init() {
super.init();
this.getScrollBar().setLeft(175);
this.getScrollBar().setHeight(106);
this.getScrollBar().setTop(18);
this.searchField = new AETextField(this.textRenderer, this.x + 104, this.y + 4, 65, 12);
this.searchField.setHasBorder(false);
this.searchField.setMaxLength(25);
@@ -283,6 +283,8 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
craftingGridOffsetX -= 25;
craftingGridOffsetY -= 6;
this.setScrollBar();
}
@Override
@@ -48,9 +48,9 @@ public class WirelessScreen extends AEBaseScreen<WirelessContainer> {
this.textRenderer.draw(matrices, GuiText.inventory.text(), 8, this.backgroundHeight - 96 + 3, 4210752);
if (handler.getRange() > 0) {
final String firstMessage = GuiText.Range.text() + ": " + (handler.getRange() / 10.0) + " m";
final String secondMessage = GuiText.PowerUsageRate.text() + ": "
+ Platform.formatPowerLong(handler.getDrain(), true);
final Text firstMessage = GuiText.Range.withSuffix(": " + (handler.getRange() / 10.0) + " m");
final Text secondMessage = GuiText.PowerUsageRate
.withSuffix(": " + Platform.formatPowerLong(handler.getDrain(), true));
final int strWidth = Math.max(this.textRenderer.getWidth(firstMessage),
this.textRenderer.getWidth(secondMessage));
@@ -0,0 +1,96 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.gui.widgets;
import java.time.Duration;
/**
* This class can be used to implement repeating events such as holding down a
* button to fire an event repeatedly while the button is still being held, or
* repeatedly scrolling down a page, while the mouse is held down on the
* scrollbar.
*/
public class EventRepeater {
/**
* -1 if no repeat event is scheduled. Otherwise contains the
* {@link System#nanoTime()} at which the next event should occur.
*/
private long nextEventTime = -1;
private EventCallback eventCallback = null;
private final long eventDelay; // In nanoseconds
private final long eventInterval; // In nanoseconds
public EventRepeater(Duration delay, Duration interval) {
this.eventDelay = delay.toNanos();
this.eventInterval = interval.toNanos();
}
public void tick() {
if (this.eventCallback == null) {
return; // No event scheduled
}
// Use nanoTime here because it is monotonically increasing, while
// System.currentTimeMillis is not
long nanoTime = System.nanoTime();
if (nanoTime < this.nextEventTime) {
return; // Event time not reached
}
// Before triggering, recompute the next event, since
// the event callback itself may reschedule/cancel, and
// we should not overwrite that
this.nextEventTime = nanoTime + this.eventInterval;
this.eventCallback.trigger();
}
/**
* Schedule the given callback to be called after a given initial delay, and
* then after the given interval repeatedly.
*
* <p>
* Replaces any previously queued callback.
*/
public void repeat(EventCallback callback) {
long time = System.nanoTime();
this.eventCallback = callback;
this.nextEventTime = time + eventDelay;
}
public boolean isRepeating() {
return this.eventCallback != null;
}
/**
* Stop repeating the event.
*/
public void stop() {
this.eventCallback = null;
}
@FunctionalInterface
public interface EventCallback {
void trigger();
}
}
@@ -18,18 +18,74 @@
package appeng.client.gui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import java.time.Duration;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.util.Rect2i;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import appeng.client.gui.AEBaseScreen;
public class Scrollbar implements IScrollSource {
/**
* Implements a vertical scrollbar using Vanilla's scrollbar handle texture from
* the creative tab.
* <p>
* It is expected that the background of the UI contains a pre-baked scrollbar
* track border, and that the exact rectangle of that track is set on this
* object via {@link #setLeft(int)}, {@link #setTop(int)} and
* {@link #setHeight(int)}. While the width of the track can also be set, the
* drawn handle will use vanilla's sprite width (see {@link #HANDLE_WIDTH}.
*/
public class Scrollbar extends DrawableHelper implements IScrollSource {
/**
* Width of the scrollbar handle sprite in the source texture.
*/
private static final int HANDLE_WIDTH = 12;
/**
* Height of the scrollbar handle sprite in the source texture.
*/
private static final int HANDLE_HEIGHT = 15;
/**
* Texture containing the scrollbar handle sprites.
*/
private static final Identifier TEXTURE = new Identifier("minecraft",
"textures/gui/container/creative_inventory/tabs.png");
/**
* Rectangle in the source texture that contains the sprite for an enabled
* handle.
*/
private static final Rect2i ENABLED = new Rect2i(232, 0, HANDLE_WIDTH, HANDLE_HEIGHT);
/**
* Rectangle in the source texture that contains the sprite for a disabled
* handle.
*/
private static final Rect2i DISABLED = new Rect2i(232 + HANDLE_WIDTH, 0, HANDLE_WIDTH, HANDLE_HEIGHT);
/**
* The screen x-coordinate of the scrollbar's inner track.
*/
private int displayX = 0;
/**
* The screen y-coordinate of the scrollbar's inner track.
*/
private int displayY = 0;
private int width = 12;
/**
* The inner width of the scrollbar track.
*/
private int width = HANDLE_WIDTH;
/**
* The inner height of the scrollbar track.
*/
private int height = 16;
private int pageSize = 1;
@@ -37,20 +93,58 @@ public class Scrollbar implements IScrollSource {
private int minScroll = 0;
private int currentScroll = 0;
private final DrawableHelper drawable = new DrawableHelper() {
};
/**
* True if the scrollbar's handle is currently being dragged.
*/
private boolean dragging;
/**
* The y-coordinate relative to the upper edge of the scrollbar handle, where
* the user pressed the mouse button to drag. While dragging, this is applied as
* an offset to the effective scrollbar position.
*/
private int dragYOffset;
private final EventRepeater eventRepeater = new EventRepeater(Duration.ofMillis(250), Duration.ofMillis(150));
/**
* Draws the handle of the scrollbar.
* <p>
* The GUI is assumed to already contain a prebaked scrollbar track in its
* background.
*/
public void draw(MatrixStack matrices, final AEBaseScreen<?> g) {
g.bindTexture("minecraft", "gui/container/creative_inventory/tabs.png");
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
setZOffset(g.getZOffset());
drawable.setZOffset(g.getZOffset());
// Draw the track (nice for debugging)
// fill(matrices, displayX, displayY, this.displayX + width, this.displayY +
// height, 0xffff0000);
g.bindTexture(TEXTURE);
int yOffset;
Rect2i sourceRect;
if (this.getRange() == 0) {
drawable.drawTexture(matrices, this.displayX, this.displayY, 232 + this.width, 0, this.width, 15);
yOffset = 0;
sourceRect = DISABLED;
} else {
final int offset = (this.currentScroll - this.minScroll) * (this.height - 15) / this.getRange();
drawable.drawTexture(matrices, this.displayX, offset + this.displayY, 232, 0, this.width, 15);
yOffset = getHandleYOffset();
sourceRect = ENABLED;
}
drawTexture(matrices, this.displayX, this.displayY + yOffset, sourceRect.getX(), sourceRect.getY(),
sourceRect.getWidth(), sourceRect.getHeight());
}
/**
* Returns the y-position of the scrollbar handle in relation to the upper edge
* of the scrollbar's track.
*/
private int getHandleYOffset() {
if (getRange() == 0) {
return 0;
}
int availableHeight = this.height - HANDLE_HEIGHT;
return (this.currentScroll - this.minScroll) * availableHeight / this.getRange();
}
private int getRange() {
@@ -114,24 +208,96 @@ public class Scrollbar implements IScrollSource {
return this.currentScroll;
}
public void click(final double x, final double y) {
if (this.getRange() == 0) {
public boolean mouseDown(double x, double y) {
this.dragging = false;
// Clicks to the left or right of the scrollbar don't do anything
if (x < displayX || x >= displayX + width) {
return false;
}
// Clicks to the top or bottom don't do anything either
int relY = (int) Math.round(y - displayY);
if (relY < 0 || relY >= height) {
return false;
}
// Do nothing when there's no range, but swallow the event
if (getRange() == 0) {
return true;
}
int handleYOffset = getHandleYOffset();
if (relY < handleYOffset) {
// Clicks above the handle will page up, repeatedly
pageUp();
eventRepeater.repeat(this::pageUp);
} else if (relY < handleYOffset + HANDLE_HEIGHT) {
// Clicks on the handle will initiate dragging it
this.dragging = true;
this.dragYOffset = relY - handleYOffset;
} else {
// Clicks below the handle will page down, repeatedly
pageDown();
eventRepeater.repeat(this::pageDown);
}
return true;
}
public boolean mouseUp(double x, double y) {
this.dragging = false;
this.eventRepeater.stop();
return false;
}
public void mouseDragged(double x, double y) {
if (this.getRange() == 0 || !this.dragging || this.eventRepeater.isRepeating()) {
return;
}
if (x > this.displayX && x <= this.displayX + this.width) {
if (y > this.displayY && y <= this.displayY + this.height) {
this.currentScroll = (int) (y - this.displayY);
this.currentScroll = this.minScroll + ((this.currentScroll * 2 * this.getRange() / this.height));
this.currentScroll = (this.currentScroll + 1) >> 1;
this.applyRange();
}
}
// Compute the position of the mouse (adjusted for where it grabbed the handle,
// so as if it grabbed
// the upper edge of it) within the scrollable area of the track (minus the
// handle height).
double handleUpperEdgeY = y - this.displayY - this.dragYOffset;
double availableHeight = this.height - HANDLE_HEIGHT;
double position = MathHelper.clamp(handleUpperEdgeY / availableHeight, 0.0, 1.0);
this.currentScroll = this.minScroll + (int) Math.round(position * this.getRange());
this.applyRange();
}
public void wheel(double delta) {
// Do nothing when there's no range
if (getRange() == 0) {
return;
}
delta = Math.max(Math.min(-delta, 1), -1);
this.currentScroll += delta * this.pageSize;
this.applyRange();
}
/**
* Ticks the scrollbar for the purposes of input-repeats (since mouse-downs are
* not repeat-triggered), used to repeatedly page-up or page-down when the mouse
* is held in the area above or below the scrollbar handle.
*/
public void tick() {
this.eventRepeater.tick();
}
private void pageUp() {
this.currentScroll -= this.pageSize;
this.applyRange();
}
private void pageDown() {
this.currentScroll += this.pageSize;
this.applyRange();
}
}
@@ -104,7 +104,7 @@ class CableBuilder {
throw new IllegalStateException("Cable type " + cableType + " does not support connections.");
}
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, textureFolder + color.name().toLowerCase(Locale.ROOT)));
}
@@ -74,7 +74,7 @@ public enum CableCoreType {
}
public SpriteIdentifier getTexture(AEColor color) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, this.textureFolder + "/" + color.name().toLowerCase(Locale.ROOT)));
}
@@ -18,7 +18,7 @@ import appeng.core.AppEng;
public class P2PTunnelFrequencyModel implements BasicUnbakedModel {
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "part/p2p_tunnel_frequency"));
@Nullable
@@ -47,7 +47,8 @@ public class SmartCableTextures {
new Identifier(AppEng.MOD_ID, "part/cable/smart/channels_12"), //
new Identifier(AppEng.MOD_ID, "part/cable/smart/channels_13"), //
new Identifier(AppEng.MOD_ID, "part/cable/smart/channels_14")//
}).map(e -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, e)).toArray(SpriteIdentifier[]::new);
}).map(e -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, e))
.toArray(SpriteIdentifier[]::new);
// Textures used to display channels on smart cables. There's two sets of 5
// textures each, and
@@ -115,7 +115,7 @@ public class CraftingCubeModel implements BasicUnbakedModel {
}
private static SpriteIdentifier texture(String name) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/crafting/" + name));
}
}
@@ -25,7 +25,7 @@ import appeng.core.AppEng;
public class BiometricCardModel implements BasicUnbakedModel {
public static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/biometric_card_base");
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/biometric_card_hash"));
@Override
@@ -26,11 +26,11 @@ public class ColorApplicatorModel implements BasicUnbakedModel {
private static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/color_applicator_colored");
private static final SpriteIdentifier TEXTURE_DARK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_DARK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_dark"));
private static final SpriteIdentifier TEXTURE_MEDIUM = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_MEDIUM = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_medium"));
private static final SpriteIdentifier TEXTURE_BRIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_BRIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_bright"));
@Override
@@ -62,13 +62,13 @@ class GlassBakedModel implements BakedModel, FabricBakedModel {
private static final byte[][][] OFFSETS = generateOffsets();
// Alternating textures based on position
static final SpriteIdentifier TEXTURE_A = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_A = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_a"));
static final SpriteIdentifier TEXTURE_B = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_B = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_b"));
static final SpriteIdentifier TEXTURE_C = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_C = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_c"));
static final SpriteIdentifier TEXTURE_D = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_D = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_d"));
// Frame texture
@@ -81,7 +81,7 @@ class GlassBakedModel implements BakedModel, FabricBakedModel {
private static SpriteIdentifier[] generateTexturesFrame() {
return IntStream.range(1, 16).mapToObj(Integer::toBinaryString).map(s -> Strings.padStart(s, 4, '0'))
.map(s -> new Identifier("appliedenergistics2:block/glass/quartz_glass_frame" + s))
.map(rl -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, rl))
.map(rl -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, rl))
.toArray(SpriteIdentifier[]::new);
}
@@ -25,7 +25,7 @@ import appeng.core.AppEng;
public class MemoryCardModel implements BasicUnbakedModel {
public static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/memory_card_base");
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/memory_card_hash"));
@Override
@@ -59,7 +59,7 @@ public class SpatialPylonModel implements BasicUnbakedModel {
}
private static SpriteIdentifier getTexturePath(SpatialPylonTextureType type) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/spatial_pylon/" + type.name().toLowerCase(Locale.ROOT)));
}
@@ -38,7 +38,7 @@ public final class InscriberTESR extends BlockEntityRenderer<InscriberBlockEntit
private static final float ITEM_RENDER_SCALE = 1.0f / 1.2f;
private static final SpriteIdentifier TEXTURE_INSIDE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_INSIDE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/inscriber_inside"));
public static final ImmutableList<SpriteIdentifier> SPRITES = ImmutableList.of(TEXTURE_INSIDE);
+28 -1
View File
@@ -30,6 +30,10 @@ import java.util.Map;
import java.util.function.DoubleSupplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.base.Strings;
import appeng.api.config.CondenserOutput;
import appeng.api.config.PowerMultiplier;
import appeng.api.config.PowerUnits;
@@ -46,6 +50,7 @@ import appeng.core.config.DoubleOption;
import appeng.core.config.EnumOption;
import appeng.core.config.IntegerOption;
import appeng.core.config.StringListOption;
import appeng.core.config.StringOption;
import appeng.core.settings.TickRates;
import appeng.util.EnumCycler;
@@ -398,7 +403,16 @@ public final class AEConfig {
return commonConfig.quartzOreBiomeBlacklist.get();
}
// Setters keep visibility as low as possible.
@Nullable
public String getImprovedFluidTag() {
return Strings.emptyToNull(commonConfig.improvedFluidTag.get());
}
public float getImprovedFluidMultiplier() {
return (float) commonConfig.improvedFluidMultiplier.get();
}
// Setters keep visibility as low as possible.
private static class ClientConfig {
@@ -510,6 +524,11 @@ public final class AEConfig {
public final IntegerOption condenserMatterBallsPower;
public final IntegerOption condenserSingularityPower;
// In-World Purification
// Settings for improved speed depending on fluid the crystal is in
public final StringOption improvedFluidTag;
public final DoubleOption improvedFluidMultiplier;
public final Map<TickRates, IntegerOption> tickRateMin = new HashMap<>();
public final Map<TickRates, IntegerOption> tickRateMax = new HashMap<>();
@@ -604,6 +623,14 @@ public final class AEConfig {
tickRateMin.put(tickRate, tickrates.addInt(tickRate.name() + "Min", tickRate.getDefaultMin()));
tickRateMax.put(tickRate, tickrates.addInt(tickRate.name() + "Max", tickRate.getDefaultMax()));
}
ConfigSection inWorldPurification = root.subsection("inWorldPurification",
"Settings for in-world purification of crystals.");
improvedFluidTag = inWorldPurification.addString("improvedFluidTag", "",
"A fluid tag that identifies fluids that improve crystal purification speed. Does not affect purification with water/lava.");
improvedFluidMultiplier = inWorldPurification.addDouble("improvedFluidMultiplier", 2.0, 1.0, 10.0,
"The speed multiplier to use when the crystals are submerged in the improved fluid.");
}
}
@@ -30,6 +30,7 @@ import net.minecraft.block.Material;
import net.minecraft.block.SlabBlock;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.sound.BlockSoundGroup;
@@ -267,17 +268,19 @@ public final class ApiBlocks implements IBlocks {
this.chiseledQuartzBlock = deco.block("chiseled_quartz_block", () -> new AEDecorativeBlock(QUARTZ_PROPERTIES))
.build();
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS)
.block("quartz_glass", () -> new QuartzGlassBlock(glassProps()))
.rendering(new BlockRenderingCustomizer() {
@Override
@Environment(EnvType.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderLayer.getCutout());
}
}).build();
AbstractBlock.TypedContextPredicate<EntityType<?>> neverAllowSpawn = (p1, p2, p3, p4) -> false;
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS).block("quartz_glass", () -> {
return new QuartzGlassBlock(glassProps().allowsSpawning(neverAllowSpawn));
}).rendering(new BlockRenderingCustomizer() {
@Override
@Environment(EnvType.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderLayer.getCutout());
}
}).build();
this.quartzVibrantGlass = deco
.block("quartz_vibrant_glass", () -> new QuartzLampBlock(glassProps().lightLevel(15)))
.block("quartz_vibrant_glass",
() -> new QuartzLampBlock(glassProps().lightLevel(15).allowsSpawning(neverAllowSpawn)))
.addFeatures(AEFeature.DECORATIVE_LIGHTS, AEFeature.QUARTZ_GLASS)
.rendering(new BlockRenderingCustomizer() {
@Override
@@ -318,7 +318,7 @@ public final class ApiItems implements IItems {
GrowingCrystalEntity.TYPE = registry
.<GrowingCrystalEntity>entity("growing_crystal", GrowingCrystalEntity::new, SpawnGroup.MISC)
.customize(builder -> builder.dimensions(EntityDimensions.fixed(0.25F, 0.25F))).build();
.customize(builder -> builder.dimensions(EntityDimensions.fixed(0.25F, 0.4F))).build();
// rv1
this.encodedPattern = registry.item("encoded_pattern", EncodedPatternItem::new)
@@ -58,6 +58,14 @@ public class ConfigSection {
return option;
}
public StringOption addString(String id, String defaultValue) {
return addString(id, defaultValue, comment);
}
public StringOption addString(String id, String defaultValue, String comment) {
return addOption(new StringOption(this, id, comment, defaultValue));
}
public IntegerOption addInt(String id, int defaultValue) {
return addInt(id, defaultValue, comment);
}
@@ -0,0 +1,63 @@
package appeng.core.config;
import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
public class StringOption extends BaseOption {
private final String defaultValue;
private String currentValue;
public StringOption(ConfigSection parent, String id, String comment, String defaultValue) {
super(parent, id, comment);
this.defaultValue = defaultValue;
this.currentValue = this.defaultValue;
}
public String get() {
return currentValue;
}
public void set(String value) {
Preconditions.checkNotNull(value);
if (value.equals(currentValue)) {
return;
}
currentValue = value;
parent.markDirty();
}
@Override
protected JsonElement write() {
return new JsonPrimitive(currentValue);
}
@Override
protected void read(JsonElement element) {
if (!element.isJsonPrimitive()) {
throw new ConfigValidationException(this, "Expected a JSON primitive: " + element);
}
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (!primitive.isString()) {
throw new ConfigValidationException(this, "Expected a JSON string, but found: " + primitive);
}
this.currentValue = primitive.getAsString();
}
@Override
public boolean isDifferentFromDefault() {
return !currentValue.equals(defaultValue);
}
@Override
public String getDefaultAsString() {
return defaultValue;
}
@Override
public String getCurrentValueAsString() {
return currentValue;
}
}
@@ -25,6 +25,7 @@ import appeng.api.events.LocatableEventAnnounce;
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
import appeng.api.features.ILocatable;
import appeng.api.features.ILocatableRegistry;
import appeng.core.AELog;
import appeng.util.Platform;
public final class LocatableRegistry implements ILocatableRegistry {
@@ -39,8 +40,10 @@ public final class LocatableRegistry implements ILocatableRegistry {
}
if (change == LocatableEvent.REGISTER) {
AELog.debug("Registering locatable %s: %s", target.getLocatableSerial(), target);
this.set.put(target.getLocatableSerial(), target);
} else if (change == LocatableEvent.UNREGISTER) {
AELog.debug("Unregistering locatable %s: %s", target.getLocatableSerial(), target);
this.set.remove(target.getLocatableSerial());
}
});
@@ -18,15 +18,27 @@
package appeng.core.sync.packets;
import java.util.Arrays;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.mojang.datafixers.util.Pair;
import io.netty.buffer.Unpooled;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import alexiil.mc.lib.attributes.item.FixedItemInv;
@@ -41,11 +53,14 @@ import appeng.api.networking.storage.IStorageGrid;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.container.implementations.PatternTermContainer;
import appeng.core.Api;
import appeng.core.sync.BasePacket;
import appeng.core.sync.BasePacketHandler;
import appeng.core.sync.network.INetworkInfo;
import appeng.helpers.IContainerCraftingPacket;
import appeng.items.storage.ViewCellItem;
import appeng.mixins.IngredientAccessor;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.inv.AdaptorFixedInv;
@@ -55,151 +70,312 @@ import appeng.util.prioritylist.IPartitionList;
public class JEIRecipePacket extends BasePacket {
private ItemStack[][] recipe;
/**
* Transmit only a recipe ID.
*/
private static final int INLINE_RECIPE_NONE = 1;
/**
* Transmit the information about the recipe we actually need. This is
* explicitly limited since this is untrusted client->server info.
*/
private static final int INLINE_RECIPE_SHAPED = 2;
private Identifier recipeId;
/**
* This is optional, in case the client already knows it could not resolve the
* recipe id.
*/
@Nullable
private Recipe<?> recipe;
private boolean crafting;
public JEIRecipePacket(final PacketByteBuf stream) {
final CompoundTag comp = stream.readCompoundTag();
if (comp != null) {
this.recipe = new ItemStack[9][];
for (int x = 0; x < this.recipe.length; x++) {
final ListTag list = comp.getList("#" + x, 10);
if (list.size() > 0) {
this.recipe[x] = new ItemStack[list.size()];
for (int y = 0; y < list.size(); y++) {
this.recipe[x][y] = ItemStack.fromTag(list.getCompound(y));
}
}
}
}
}
this.crafting = stream.readBoolean();
final String id = stream.readString(Short.MAX_VALUE);
this.recipeId = new Identifier(id);
// api
public JEIRecipePacket(final CompoundTag recipe) {
final PacketByteBuf data = new PacketByteBuf(Unpooled.buffer());
data.writeInt(this.getPacketID());
data.writeCompoundTag(recipe);
this.configureWrite(data);
}
@Override
public void serverPacketData(final INetworkInfo manager, final PlayerEntity player) {
final ServerPlayerEntity pmp = (ServerPlayerEntity) player;
final ScreenHandler con = pmp.currentScreenHandler;
if (!(con instanceof IContainerCraftingPacket)) {
return;
}
final IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
final IGridNode node = cct.getNetworkNode();
if (node == null) {
return;
}
final IGrid grid = node.getGrid();
if (grid == null) {
return;
}
final IStorageGrid inv = grid.getCache(IStorageGrid.class);
final IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
final ISecurityGrid security = grid.getCache(ISecurityGrid.class);
final ICraftingGrid crafting = grid.getCache(ICraftingGrid.class);
final FixedItemInv craftMatrix = cct.getInventoryByName("crafting");
final FixedItemInv playerInventory = cct.getInventoryByName("player");
if (inv != null && this.recipe != null && security != null) {
final IMEMonitor<IAEItemStack> storage = inv
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class));
final IPartitionList<IAEItemStack> filter = ViewCellItem.createFilter(cct.getViewCells());
for (int x = 0; x < craftMatrix.getSlotCount(); x++) {
ItemStack currentItem = craftMatrix.getInvStack(x);
// prepare slots
if (!currentItem.isEmpty()) {
// already the correct item?
ItemStack newItem = this.canUseInSlot(x, currentItem);
// put away old item
if (newItem != currentItem && security.hasPermission(player, SecurityPermissions.INJECT)) {
final IAEItemStack in = AEItemStack.fromItemStack(currentItem);
final IAEItemStack out = cct.useRealItems()
? Platform.poweredInsert(energy, storage, in, cct.getActionSource())
: null;
if (out != null) {
currentItem = out.createItemStack();
} else {
currentItem = ItemStack.EMPTY;
}
}
}
if (currentItem.isEmpty() && this.recipe[x] != null) {
// for each variant
for (int y = 0; y < this.recipe[x].length && currentItem.isEmpty(); y++) {
final IAEItemStack request = AEItemStack.fromItemStack(this.recipe[x][y]);
if (request != null) {
// try ae
if ((filter == null || filter.isListed(request))
&& security.hasPermission(player, SecurityPermissions.EXTRACT)) {
request.setStackSize(1);
IAEItemStack out;
if (cct.useRealItems()) {
out = Platform.poweredExtraction(energy, storage, request, cct.getActionSource());
} else {
// Query the crafting grid if there is a pattern providing the item
if (!crafting.getCraftingFor(request, null, 0, null).isEmpty()) {
out = request;
} else {
// Fall back using an existing item
out = storage.extractItems(request, Actionable.SIMULATE, cct.getActionSource());
}
}
if (out != null) {
currentItem = out.createItemStack();
}
}
// try inventory
if (currentItem.isEmpty()) {
AdaptorFixedInv ad = new AdaptorFixedInv(playerInventory);
if (cct.useRealItems()) {
currentItem = ad.removeItems(1, this.recipe[x][y], null);
} else {
currentItem = ad.simulateRemove(1, this.recipe[x][y], null);
}
}
}
}
}
ItemHandlerUtil.setStackInSlot(craftMatrix, x, currentItem);
}
con.onContentChanged(new WrapperInvItemHandler(craftMatrix));
int inlineRecipeType = stream.readVarInt();
switch (inlineRecipeType) {
case INLINE_RECIPE_NONE:
break;
case INLINE_RECIPE_SHAPED:
recipe = RecipeSerializer.SHAPED.read(this.recipeId, stream);
break;
default:
throw new IllegalArgumentException("Invalid inline recipe type.");
}
}
/**
* @param slot
* @param is itemstack
* @return is if it can be used, else EMPTY
* Sends a recipe identified by the given recipe ID to the server for either
* filling a crafting grid or a pattern.
*/
private ItemStack canUseInSlot(int slot, ItemStack is) {
if (this.recipe[slot] != null) {
for (ItemStack option : this.recipe[slot]) {
if (is.isItemEqual(option)) {
return is;
public JEIRecipePacket(final Identifier recipeId, final boolean crafting) {
PacketByteBuf data = createCommonHeader(recipeId, crafting, INLINE_RECIPE_NONE);
this.configureWrite(data);
}
/**
* Sends a recipe to the server for either filling a crafting grid or a pattern.
* <p>
* Prefer the id-based constructor above whereever possible.
*/
public JEIRecipePacket(final ShapedRecipe recipe, final boolean crafting) {
PacketByteBuf data = createCommonHeader(recipe.getId(), crafting, INLINE_RECIPE_SHAPED);
RecipeSerializer.SHAPED.write(data, recipe);
this.configureWrite(data);
}
private PacketByteBuf createCommonHeader(Identifier recipeId, boolean crafting, int inlineRecipeType) {
final PacketByteBuf data = new PacketByteBuf(Unpooled.buffer());
data.writeInt(this.getPacketID());
data.writeBoolean(crafting);
data.writeIdentifier(recipeId);
data.writeVarInt(inlineRecipeType);
return data;
}
/**
* Servside handler for this packet.
* <p>
* Makes use of {@link Preconditions#checkArgument(boolean)} as the
* {@link BasePacketHandler} is catching them and in general these cases should
* never happen except in an error case and should be logged then.
*/
@Override
public void serverPacketData(final INetworkInfo manager, final PlayerEntity player) {
// Setup and verification
final ServerPlayerEntity pmp = (ServerPlayerEntity) player;
final ScreenHandler con = pmp.currentScreenHandler;
Preconditions.checkArgument(con instanceof IContainerCraftingPacket);
Recipe<?> recipe = player.getEntityWorld().getRecipeManager().get(this.recipeId).orElse(null);
if (recipe == null && this.recipe != null) {
// Certain recipes (i.e. AE2 facades) are represented in JEI as ShapedRecipe's,
// while in reality they
// are special recipes. Those recipes are sent across the wire...
recipe = this.recipe;
}
Preconditions.checkArgument(recipe != null);
final IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
final IGridNode node = cct.getNetworkNode();
Preconditions.checkArgument(node != null);
final IGrid grid = node.getGrid();
Preconditions.checkArgument(grid != null);
final IStorageGrid inv = grid.getCache(IStorageGrid.class);
Preconditions.checkArgument(inv != null);
final ISecurityGrid security = grid.getCache(ISecurityGrid.class);
Preconditions.checkArgument(security != null);
final IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
final ICraftingGrid crafting = grid.getCache(ICraftingGrid.class);
final FixedItemInv craftMatrix = cct.getInventoryByName("crafting");
final FixedItemInv playerInventory = cct.getInventoryByName("player");
final IMEMonitor<IAEItemStack> storage = inv
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class));
final IPartitionList<IAEItemStack> filter = ViewCellItem.createFilter(cct.getViewCells());
final DefaultedList<Ingredient> ingredients = this.ensure3by3CraftingMatrix(recipe);
// Handle each slot
for (int x = 0; x < craftMatrix.getSlotCount(); x++) {
ItemStack currentItem = craftMatrix.getInvStack(x);
Ingredient ingredient = ingredients.get(x);
// prepare slots
if (!currentItem.isEmpty()) {
// already the correct item? True, skip everything else
ItemStack newItem = this.canUseInSlot(ingredient, currentItem);
// put away old item, if not correct
if (newItem != currentItem && security.hasPermission(player, SecurityPermissions.INJECT)) {
final IAEItemStack in = AEItemStack.fromItemStack(currentItem);
final IAEItemStack out = cct.useRealItems()
? Platform.poweredInsert(energy, storage, in, cct.getActionSource())
: null;
if (out != null) {
currentItem = out.createItemStack();
} else {
currentItem = ItemStack.EMPTY;
}
}
}
// Find item or pattern from the network
if (currentItem.isEmpty() && security.hasPermission(player, SecurityPermissions.EXTRACT)) {
IAEItemStack out;
if (cct.useRealItems()) {
IAEItemStack request = findBestMatchingItemStack(ingredient, filter, storage, cct);
out = request != null
? Platform.poweredExtraction(energy, storage, request.setStackSize(1),
cct.getActionSource())
: null;
} else {
out = findBestMatchingPattern(ingredient, filter, crafting, storage, cct);
if (out == null) {
out = findBestMatchingItemStack(ingredient, filter, storage, cct);
}
if (out == null && getMatchingStacks(ingredient).length > 0) {
out = AEItemStack.fromItemStack(getMatchingStacks(ingredient)[0]);
}
}
if (out != null) {
currentItem = out.createItemStack();
}
}
// If still nothing, search the player inventory.
if (currentItem.isEmpty()) {
ItemStack[] matchingStacks = getMatchingStacks(ingredient);
for (ItemStack matchingStack : matchingStacks) {
if (currentItem.isEmpty()) {
AdaptorFixedInv ad = new AdaptorFixedInv(playerInventory);
if (cct.useRealItems()) {
currentItem = ad.removeItems(1, matchingStack, null);
} else {
currentItem = ad.simulateRemove(1, matchingStack, null);
}
}
}
}
ItemHandlerUtil.setStackInSlot(craftMatrix, x, currentItem);
}
if (!this.crafting) {
this.handleProcessing(con, cct, recipe);
}
con.onContentChanged(new WrapperInvItemHandler(craftMatrix));
}
@SuppressWarnings("ConstantConditions")
private static ItemStack[] getMatchingStacks(Ingredient ingredient) {
IngredientAccessor accessor = (IngredientAccessor) (Object) ingredient;
accessor.appeng_cacheMatchingStacks();
if (ingredient.isEmpty()) {
return new ItemStack[0];
}
ItemStack[] stacks = accessor.getMatchingStacks();
if (stacks.length == 1 && stacks[0].isEmpty()) {
return new ItemStack[0];
}
return stacks;
}
/**
* Expand any recipe to a 3x3 matrix.
* <p>
* Will throw an {@link IllegalArgumentException} in case it has more than 9 or
* a shaped recipe is either wider or higher than 3. ingredients.
*/
private DefaultedList<Ingredient> ensure3by3CraftingMatrix(Recipe<?> recipe) {
DefaultedList<Ingredient> ingredients = recipe.getPreviewInputs();
DefaultedList<Ingredient> expandedIngredients = DefaultedList.ofSize(9, Ingredient.EMPTY);
Preconditions.checkArgument(ingredients.size() <= 9);
// shaped recipes can be smaller than 3x3, expand to 3x3 to match the crafting
// matrix
if (recipe instanceof ShapedRecipe) {
ShapedRecipe shapedRecipe = (ShapedRecipe) recipe;
int width = shapedRecipe.getWidth();
int height = shapedRecipe.getHeight();
Preconditions.checkArgument(width <= 3 && height <= 3);
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int source = w + h * width;
int target = w + h * 3;
Ingredient i = ingredients.get(source);
expandedIngredients.set(target, i);
}
}
}
return ItemStack.EMPTY;
// Anything else should be a flat list
else {
for (int i = 0; i < ingredients.size(); i++) {
expandedIngredients.set(i, ingredients.get(i));
}
}
return expandedIngredients;
}
/**
* @param is itemstack
* @return is if it can be used, else EMPTY
*/
private ItemStack canUseInSlot(Ingredient ingredient, ItemStack is) {
return Arrays.stream(getMatchingStacks(ingredient)).filter(p -> p.isItemEqual(is)).findFirst()
.orElse(ItemStack.EMPTY);
}
/**
* Finds the first matching itemstack with the highest stored amount.
*/
private IAEItemStack findBestMatchingItemStack(Ingredient ingredients, IPartitionList<IAEItemStack> filter,
IMEMonitor<IAEItemStack> storage, IContainerCraftingPacket cct) {
Stream<AEItemStack> stacks = Arrays.stream(getMatchingStacks(ingredients))//
.map(AEItemStack::fromItemStack) //
.filter(r -> r != null && (filter == null || filter.isListed(r)));
return getMostStored(stacks, storage, cct);
}
/**
* This tries to find the first pattern matching the list of ingredients.
* <p>
* As additional condition, it sorts by the stored amount to return the one with
* the highest stored amount.
*/
private IAEItemStack findBestMatchingPattern(Ingredient ingredients, IPartitionList<IAEItemStack> filter,
ICraftingGrid crafting, IMEMonitor<IAEItemStack> storage, IContainerCraftingPacket cct) {
Stream<IAEItemStack> stacks = Arrays.stream(getMatchingStacks(ingredients))//
.map(AEItemStack::fromItemStack)//
.filter(r -> r != null && (filter == null || filter.isListed(r)))//
.map(s -> s.setCraftable(!crafting.getCraftingFor(s, null, 0, null).isEmpty()))//
.filter(IAEItemStack::isCraftable);
return getMostStored(stacks, storage, cct);
}
/**
* From a stream of AE item stacks, pick the one with the highest available
* amount in the network. Returns null if the stream is empty.
*/
private static IAEItemStack getMostStored(Stream<? extends IAEItemStack> stacks, IMEMonitor<IAEItemStack> storage,
IContainerCraftingPacket cct) {
return stacks//
.map(s -> {
// Determine the stored count
IAEItemStack stored = storage.extractItems(s.copy().setStackSize(Long.MAX_VALUE),
Actionable.SIMULATE, cct.getActionSource());
return Pair.of(s, stored != null ? stored.getStackSize() : 0);
})//
.min((left, right) -> Long.compare(right.getSecond(), left.getSecond()))//
.map(Pair::getFirst)//
.orElse(null);
}
private void handleProcessing(ScreenHandler con, IContainerCraftingPacket cct, Recipe<?> recipe) {
if (con instanceof PatternTermContainer) {
PatternTermContainer patternTerm = (PatternTermContainer) con;
if (!patternTerm.craftingMode) {
final FixedItemInv output = cct.getInventoryByName("output");
ItemHandlerUtil.setStackInSlot(output, 0, recipe.getOutput());
ItemHandlerUtil.setStackInSlot(output, 1, ItemStack.EMPTY);
ItemHandlerUtil.setStackInSlot(output, 2, ItemStack.EMPTY);
}
}
}
}
@@ -19,11 +19,9 @@
package appeng.core.sync.packets;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -33,9 +33,9 @@ import net.minecraft.world.World;
import alexiil.mc.lib.attributes.AttributeList;
import alexiil.mc.lib.attributes.Simulation;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import alexiil.mc.lib.attributes.item.ItemExtractable;
import alexiil.mc.lib.attributes.item.filter.ExactItemStackFilter;
import alexiil.mc.lib.attributes.item.filter.ItemFilter;
import appeng.tile.AEBaseBlockEntity;
@@ -43,7 +43,7 @@ public class ItemGenBlockEntity extends AEBaseBlockEntity {
private static final Queue<ItemStack> POSSIBLE_ITEMS = new ArrayDeque<>();
private final ItemExtractable handler = new QueuedItemHandler();
private final QueuedItemHandler handler = new QueuedItemHandler();
public ItemGenBlockEntity(BlockEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
@@ -77,22 +77,24 @@ public class ItemGenBlockEntity extends AEBaseBlockEntity {
to.offer(handler);
}
class QueuedItemHandler implements ItemExtractable {
static class QueuedItemHandler implements FixedItemInv {
@Override
public ItemStack attemptExtraction(ItemFilter itemFilter, int maxAmount, Simulation simulation) {
// When item filter asks for a specific item, just return that
if (itemFilter instanceof ExactItemStackFilter) {
return ((ExactItemStackFilter) itemFilter).stack.copy();
}
public ItemExtractable getExtractable() {
return (itemFilter, i, simulation) -> {
// When item filter asks for a specific item, just return that
if (itemFilter instanceof ExactItemStackFilter) {
return ((ExactItemStackFilter) itemFilter).stack.copy();
}
final ItemStack is = POSSIBLE_ITEMS.peek();
final ItemStack is = POSSIBLE_ITEMS.peek();
if (is == null || !itemFilter.matches(is)) {
return ItemStack.EMPTY;
}
if (is == null || !itemFilter.matches(is)) {
return ItemStack.EMPTY;
}
return simulation == Simulation.SIMULATE ? is.copy() : this.getNextItem();
return simulation == Simulation.SIMULATE ? is.copy() : getNextItem();
};
}
private ItemStack getNextItem() {
@@ -101,7 +103,28 @@ public class ItemGenBlockEntity extends AEBaseBlockEntity {
POSSIBLE_ITEMS.add(is);
return is.copy();
}
}
;
@Override
public int getSlotCount() {
return 1;
}
@Override
public ItemStack getInvStack(int i) {
return POSSIBLE_ITEMS.peek().copy();
}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemStack) {
return false;
}
@Override
public boolean setInvStack(int i, ItemStack itemStack, Simulation simulation) {
if (itemStack.isEmpty()) {
getNextItem(); // Go to next item
}
return itemStack.isEmpty();
}
}
}
@@ -20,11 +20,8 @@ package appeng.decorative.solid;
import net.minecraft.block.AbstractGlassBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.util.math.Direction;
import appeng.helpers.AEMaterials;
public class QuartzGlassBlock extends AbstractGlassBlock {
public QuartzGlassBlock(Settings props) {
@@ -33,8 +30,7 @@ public class QuartzGlassBlock extends AbstractGlassBlock {
@Override
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side) {
final Material mat = adjacentBlockState.getMaterial();
if (mat == Material.GLASS || mat == AEMaterials.GLASS) {
if (adjacentBlockState.getBlock() instanceof QuartzGlassBlock) {
if (adjacentBlockState.getRenderType() == state.getRenderType()) {
return true;
}
@@ -19,13 +19,14 @@
package appeng.entity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import appeng.api.features.AEFeature;
@@ -36,12 +37,28 @@ import appeng.core.AEConfig;
import appeng.core.AppEng;
import appeng.items.misc.CrystalSeedItem;
import appeng.mixins.ItemEntityAccessor;
import appeng.util.Platform;
public class GrowingCrystalEntity extends AEBaseItemEntity {
public static EntityType<GrowingCrystalEntity> TYPE;
// Growth tick progress per tick by number of adjacent accelerators
// Expressed as 1/1000th of a growth tick, applied to progress_1000
// each time this entity ticks.
private static final int[] GROWTH_TICK_PROGRESS = { //
1, // no accelerators
40, // 1 accelerator
92, // 2 accelerators
159, // 3 accelerators
247, // 4 accelerators
361, // 5 accelerators
509 // 6 accelerators
};
/**
* The accumulated progress towards a single growth tick of the crystal in
* 1/1000th of a growth tick.
*/
private int progress_1000 = 0;
public GrowingCrystalEntity(EntityType<? extends GrowingCrystalEntity> type, World world) {
@@ -62,126 +79,140 @@ public class GrowingCrystalEntity extends AEBaseItemEntity {
super.tick();
final ItemStack is = this.getStack();
final Item gc = is.getItem();
if (!(gc instanceof IGrowableCrystal)) {
return;
}
applyGrowthTick((IGrowableCrystal) gc, is);
}
private void applyGrowthTick(IGrowableCrystal cry, ItemStack is) {
if (!AEConfig.instance().isFeatureEnabled(AEFeature.IN_WORLD_PURIFICATION)) {
return;
}
final ItemStack is = this.getStack();
final Item gc = is.getItem();
final int x = MathHelper.floor(this.getX());
final int y = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int z = MathHelper.floor(this.getZ());
if (gc instanceof IGrowableCrystal) // if it changes this just stops being an issue...
{
final int j = MathHelper.floor(this.getX());
final int i = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int k = MathHelper.floor(this.getZ());
BlockPos pos = new BlockPos(x, y, z);
final BlockState state = this.world.getBlockState(pos);
final BlockState state = this.world.getBlockState(new BlockPos(j, i, k));
final Material mat = state.getMaterial();
final IGrowableCrystal cry = (IGrowableCrystal) is.getItem();
final float multiplier = cry.getMultiplier(state, world, pos);
final float multiplier = cry.getMultiplier(state.getBlock(), mat);
final int speed = (int) Math.max(1, this.getSpeed(j, i, k) * multiplier);
if (multiplier <= 0) {
// Crystal is in unsuitable material, reset progress and quit
this.progress_1000 = 0;
return;
}
final boolean isClient = Platform.isClient();
final int progressPerTick = (int) Math.max(1, this.getSpeed(pos) * multiplier);
if (mat.isLiquid()) {
if (isClient) {
this.progress_1000++;
} else {
this.progress_1000 += speed;
}
} else {
if (world.isClient()) {
// On the client, we reuse the growth-tick-progress
// as a tick-counter for particle effects
int len = getTicksBetweenParticleEffects(progressPerTick);
if (++this.progress_1000 >= len) {
this.progress_1000 = 0;
AppEng.instance().spawnEffect(EffectType.Vibrant, this.world, this.getX(), this.getY() + 0.2,
this.getZ(), null);
}
} else {
this.progress_1000 += progressPerTick;
if (isClient) {
int len = 40;
if (this.progress_1000 >= 1000) {
// We need to copy the stack or the change detection will not work and not sync
// this new stack to the client
ItemStack newItem = is.copy();
if (speed > 2) {
len = 20;
}
if (speed > 90) {
len = 15;
}
if (speed > 150) {
len = 10;
}
if (speed > 240) {
len = 7;
}
if (speed > 360) {
len = 3;
}
if (speed > 500) {
len = 1;
}
if (this.progress_1000 >= len) {
this.progress_1000 = 0;
AppEng.instance().spawnEffect(EffectType.Vibrant, this.world, this.getX(), this.getY() + 0.2,
this.getZ(), null);
}
} else {
if (this.progress_1000 > 1000) {
// If we did not use a while loop here, the fastest growth for a crystal
// would be limited to a minimum of 30 seconds (based on 600 required growth
// ticks).
// Should a crystal decide to use a high multiplier for a certain material,
// it should be possible to go faster.
do {
newItem = cry.triggerGrowth(newItem);
this.progress_1000 -= 1000;
// We need to copy the stack or the change detection will not work and not sync
// this new stack to the client
ItemStack newItem = cry.triggerGrowth(is.copy());
this.setStack(newItem);
}
// We assume that if the item changes, the process is complete and we can break
} while (this.progress_1000 >= 1000 && newItem.getItem() == is.getItem());
this.setStack(newItem);
}
}
}
private int getSpeed(final int x, final int y, final int z) {
final int per = 80;
final float mul = 0.3f;
int qty = 0;
if (this.isAccelerated(x + 1, y, z)) {
qty += per + qty * mul;
private static int getTicksBetweenParticleEffects(int progressPerTick) {
if (progressPerTick > 500) {
return 1; // 20 times per second
} else if (progressPerTick > 360) {
return 3;
} else if (progressPerTick > 240) {
return 7;
} else if (progressPerTick > 150) {
return 10;
} else if (progressPerTick > 90) {
return 15;
} else if (progressPerTick > 2) {
return 20;
} else {
return 40; // Every 2 seconds
}
if (this.isAccelerated(x, y + 1, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y, z + 1)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x - 1, y, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y - 1, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y, z - 1)) {
qty += per + qty * mul;
}
return qty;
}
private boolean isAccelerated(final int x, final int y, final int z) {
final BlockEntity te = this.world.getBlockEntity(new BlockPos(x, y, z));
/**
* Gets the extra progress per tick in 1/1000th of a growth tick based on the
* surrounding accelerators.
*/
private int getSpeed(BlockPos pos) {
int acceleratorCount = getAcceleratorCount(pos);
if (acceleratorCount < 0) {
return GROWTH_TICK_PROGRESS[0];
} else if (acceleratorCount >= GROWTH_TICK_PROGRESS.length) {
return GROWTH_TICK_PROGRESS[GROWTH_TICK_PROGRESS.length - 1];
} else {
return GROWTH_TICK_PROGRESS[acceleratorCount];
}
}
private int getAcceleratorCount(BlockPos pos) {
int count = 0;
BlockPos.Mutable testPos = new BlockPos.Mutable();
for (Direction direction : Direction.values()) {
if (this.isPoweredAccelerator(testPos.set(pos, direction))) {
count++;
}
}
return count;
}
private boolean isPoweredAccelerator(BlockPos pos) {
final BlockEntity te = this.world.getBlockEntity(pos);
return te instanceof ICrystalGrowthAccelerator && ((ICrystalGrowthAccelerator) te).isPowered();
}
// Don't let seeds "float" on water surface
@Override
public void applyBuoyancy() {
ItemStack item = getStack();
// Make ungrown seeds sink, and fully grown seeds bouyant allowing for
// automation based around dropping seeds between 5 CGAs, then catchiung
// them on their way up.
if (item.getItem() instanceof CrystalSeedItem) {
Vec3d v = this.getVelocity();
// Apply a much smaller acceleration to make them slowly sink
double yAccel = this.hasNoGravity() ? 0 : -0.002;
// Apply the x/z slow-down, and the y acceleration
this.setVelocity(v.x * 0.99, v.y + yAccel, v.z * 0.99);
return;
}
super.applyBuoyancy();
@@ -74,6 +74,6 @@ public class TinyTNTPrimedRenderer extends EntityRenderer<TinyTNTPrimedEntity> {
@Override
public Identifier getTexture(final TinyTNTPrimedEntity entity) {
return SpriteAtlasTexture.BLOCK_ATLAS_TEX;
return SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE;
}
}
@@ -141,7 +141,7 @@ public class FluidImportBusPart extends SharedFluidBusPart {
return TickRateModulation.IDLE;
} catch (GridAccessException e) {
e.printStackTrace();
// skip
}
}
@@ -0,0 +1,43 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020 Team Appliedenergistics, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.integration.modules.jei;
import me.shedaniel.rei.api.AutoTransferHandler;
import me.shedaniel.rei.api.RecipeDisplay;
import appeng.container.implementations.CraftingTermContainer;
public class CraftingRecipeTransferHandler extends RecipeTransferHandler<CraftingTermContainer> {
public CraftingRecipeTransferHandler(Class<CraftingTermContainer> containerClass) {
super(containerClass);
}
@Override
protected AutoTransferHandler.Result doTransferRecipe(CraftingTermContainer container, RecipeDisplay recipe,
AutoTransferHandler.Context context) {
return null;
}
@Override
protected boolean isCrafting() {
return true;
}
}
@@ -108,6 +108,8 @@ class FacadeRegistryGenerator implements LiveRecipeGenerator<DefaultShapedDispla
ingredients.set(7, Ingredient.ofStacks(cableAnchor));
ingredients.set(4, Ingredient.ofStacks(textureItem));
result.setCount(4);
return new DefaultShapedDisplay(new ShapedRecipe(id, "", 3, 3, ingredients, result));
}
@@ -0,0 +1,52 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020 Team Appliedenergistics, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.integration.modules.jei;
import me.shedaniel.rei.api.AutoTransferHandler;
import me.shedaniel.rei.api.RecipeDisplay;
import me.shedaniel.rei.plugin.DefaultPlugin;
import appeng.container.implementations.PatternTermContainer;
public class PatternRecipeTransferHandler extends RecipeTransferHandler<PatternTermContainer> {
PatternRecipeTransferHandler(Class<PatternTermContainer> containerClass) {
super(containerClass);
}
protected AutoTransferHandler.Result doTransferRecipe(PatternTermContainer container, RecipeDisplay recipe,
AutoTransferHandler.Context context) {
if (container.isCraftingMode() && recipe.getRecipeCategory() != DefaultPlugin.CRAFTING) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.requires_processing_mode");
}
if (recipe.getResultingEntries().isEmpty()) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.no_output");
}
return null;
}
@Override
protected boolean isCrafting() {
return false;
}
}
@@ -18,27 +18,27 @@
package appeng.integration.modules.jei;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import me.shedaniel.rei.api.AutoTransferHandler;
import me.shedaniel.rei.api.EntryStack;
import me.shedaniel.rei.api.RecipeDisplay;
import me.shedaniel.rei.api.TransferRecipeDisplay;
import appeng.container.slot.CraftingMatrixSlot;
import appeng.container.slot.FakeCraftingMatrixSlot;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.JEIRecipePacket;
import appeng.mixins.SlotMixin;
import appeng.util.Platform;
import appeng.helpers.IContainerCraftingPacket;
class RecipeTransferHandler<T extends ScreenHandler> implements AutoTransferHandler {
abstract class RecipeTransferHandler<T extends ScreenHandler & IContainerCraftingPacket>
implements AutoTransferHandler {
private final Class<T> containerClass;
@@ -47,67 +47,85 @@ class RecipeTransferHandler<T extends ScreenHandler> implements AutoTransferHand
}
@Override
public Result handle(Context context) {
if (!(context.getRecipe() instanceof TransferRecipeDisplay)) {
return Result.createNotApplicable();
public AutoTransferHandler.Result handle(AutoTransferHandler.Context context) {
RecipeDisplay recipe = context.getRecipe();
if (!containerClass.isInstance(context.getContainerScreen().getScreenHandler())) {
return AutoTransferHandler.Result.createNotApplicable();
}
ScreenHandler container = context.getContainerScreen().getScreenHandler();
T container = containerClass.cast(context.getContainerScreen().getScreenHandler());
if (!containerClass.isInstance(container)) {
return Result.createNotApplicable();
final Identifier recipeId = recipe.getRecipeLocation().orElse(null);
// Check that the recipe can actually be looked up via the manager, i.e. our
// facade recipes
// have an ID, but are never registered with the recipe manager.
boolean canSendReference = true;
if (recipeId == null || !context.getMinecraft().world.getRecipeManager().get(recipeId).isPresent()) {
canSendReference = false;
}
if (!context.isActuallyCrafting()) {
// This is just to check whether the button is enabled
return Result.createSuccessful();
if (recipe instanceof TransferRecipeDisplay) {
TransferRecipeDisplay trd = (TransferRecipeDisplay) recipe;
if (trd.getWidth() > 3 || trd.getHeight() > 3) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.recipe_too_large");
}
} else if (recipe.getInputEntries().size() > 9) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.recipe_too_large");
}
TransferRecipeDisplay transferRecipe = (TransferRecipeDisplay) context.getRecipe();
final AutoTransferHandler.Result error = doTransferRecipe(container, recipe, context);
final CompoundTag recipe = new CompoundTag();
if (error != null) {
return error;
}
List<List<EntryStack>> ingredients = transferRecipe.getInputEntries();
for (int i = 0; i < ingredients.size(); i++) {
List<EntryStack> ingredientEntry = ingredients.get(i);
// Vanilla will pack ingredients if the recipe is less than 3x3
int x = i % transferRecipe.getWidth();
int y = (i - x) / transferRecipe.getWidth();
int slotIndex = 3 * y + x;
for (final Slot slot : container.slots) {
if (slot instanceof CraftingMatrixSlot || slot instanceof FakeCraftingMatrixSlot) {
int containerSlotInvIdx = ((SlotMixin) slot).getIndex();
if (containerSlotInvIdx == slotIndex) {
final ListTag tags = new ListTag();
final List<ItemStack> list = new ArrayList<>();
// prefer pure crystals.
for (EntryStack stack : ingredientEntry) {
if (Platform.isRecipePrioritized(stack.getItemStack())) {
list.add(0, stack.getItemStack());
} else {
list.add(stack.getItemStack());
}
}
for (final ItemStack is : list) {
final CompoundTag tag = new CompoundTag();
is.toTag(tag);
tags.add(tag);
}
recipe.put("#" + containerSlotInvIdx, tags);
break;
if (context.isActuallyCrafting()) {
if (canSendReference) {
NetworkHandler.instance().sendToServer(new JEIRecipePacket(recipeId, isCrafting()));
} else {
// To avoid earlier problems of too large packets being sent that crashed the
// client,
// as a fallback when the recipe ID could not be resolved, we'll just send the
// displayed
// items.
DefaultedList<Ingredient> flatIngredients = DefaultedList.ofSize(9, Ingredient.EMPTY);
ItemStack output = null;
for (EntryStack entryStack : recipe.getResultingEntries().get(0)) {
if (entryStack.getType() == EntryStack.Type.ITEM) {
output = entryStack.getItemStack();
}
}
if (output == null || output.isEmpty()) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.no_output");
}
// Now map the actual ingredients into the output/input
for (int i = 0; i < recipe.getInputEntries().size(); i++) {
List<EntryStack> inputEntry = recipe.getInputEntries().get(i);
if (inputEntry.isEmpty()) {
continue;
}
EntryStack first = inputEntry.get(0);
if (i < flatIngredients.size()) {
ItemStack displayedIngredient = first.getItemStack();
if (displayedIngredient != null) {
flatIngredients.set(i, Ingredient.ofStacks(Stream.of(displayedIngredient)));
}
}
}
ShapedRecipe fallbackRecipe = new ShapedRecipe(recipeId, "", 3, 3, flatIngredients, output);
NetworkHandler.instance().sendToServer(new JEIRecipePacket(fallbackRecipe, isCrafting()));
}
}
NetworkHandler.instance().sendToServer(new JEIRecipePacket(recipe));
return Result.createFailed(""); // this will return to the screen
return Result.createSuccessful().blocksFurtherHandling();
}
protected abstract AutoTransferHandler.Result doTransferRecipe(T container, RecipeDisplay recipe,
AutoTransferHandler.Context context);
protected abstract boolean isCrafting();
}
@@ -32,7 +32,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.BaseBoundsHandler;
import me.shedaniel.rei.api.DisplayHelper;
import me.shedaniel.rei.api.EntryRegistry;
@@ -124,8 +123,8 @@ public class ReiPlugin implements REIPluginV0 {
@Override
public void registerOthers(RecipeHelper recipeHelper) {
// Allow recipe transfer from JEI to crafting and pattern terminal
recipeHelper.registerAutoCraftingHandler(new RecipeTransferHandler<>(CraftingTermContainer.class));
recipeHelper.registerAutoCraftingHandler(new RecipeTransferHandler<>(PatternTermContainer.class));
recipeHelper.registerAutoCraftingHandler(new CraftingRecipeTransferHandler(CraftingTermContainer.class));
recipeHelper.registerAutoCraftingHandler(new PatternRecipeTransferHandler(PatternTermContainer.class));
recipeHelper.removeAutoCraftButton(GrinderRecipeCategory.UID);
recipeHelper.removeAutoCraftButton(InscriberRecipeCategory.UID);
@@ -227,7 +226,7 @@ public class ReiPlugin implements REIPluginV0 {
ItemStack stack = entryStack.getItemStack();
if (items.dummyFluidItem().isSameAs(stack) || items.facade().isSameAs(stack) // REI will add a broken facade
// with no NBT
// with no NBT
|| blocks.multiPart().isSameAs(stack) || blocks.matrixFrame().isSameAs(stack)
|| blocks.paint().isSameAs(stack)) {
return true;
@@ -47,6 +47,6 @@ public final class Tracer {
BlockState blockState = world.getBlockState(pos);
LookDirection playerRay = Platform.getPlayerRay(player);
return blockState.getCollisionShape(world, pos).rayTrace(playerRay.getA(), playerRay.getB(), pos);
return blockState.getCollisionShape(world, pos).raycast(playerRay.getA(), playerRay.getB(), pos);
}
}
@@ -26,23 +26,30 @@ import com.google.common.base.Preconditions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Block;
import net.minecraft.block.Material;
import net.fabricmc.fabric.api.tag.TagRegistry;
import net.minecraft.block.BlockState;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.tag.FluidTags;
import net.minecraft.tag.Tag;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import appeng.api.implementations.items.IGrowableCrystal;
import appeng.core.AEConfig;
import appeng.core.localization.ButtonToolTips;
import appeng.entity.GrowingCrystalEntity;
import appeng.hooks.AECustomEntityItem;
@@ -69,9 +76,15 @@ public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal, AEC
*/
private final ItemConvertible grownItem;
private final Tag<Fluid> improvedFluidTag;
public CrystalSeedItem(Settings properties, ItemConvertible grownItem) {
super(properties);
this.grownItem = Preconditions.checkNotNull(grownItem);
String improvedFluidTagName = AEConfig.instance().getImprovedFluidTag();
this.improvedFluidTag = improvedFluidTagName != null ? TagRegistry.fluid(new Identifier(improvedFluidTagName))
: null;
}
@Nullable
@@ -97,8 +110,21 @@ public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal, AEC
}
@Override
public float getMultiplier(final Block blk, final Material mat) {
return 0.5f;
public float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos) {
// Check for the improved fluid tag and return the improved multiplier
FluidState fluidState = state.getFluidState();
if (improvedFluidTag != null && fluidState.isIn(improvedFluidTag)) {
return AEConfig.instance().getImprovedFluidMultiplier();
}
// Check for the normal supported fluid
if (world != null && world.getRegistryKey() == World.NETHER) {
// In the nether, use Lava as the "normal" fluid
return fluidState.isIn(FluidTags.LAVA) ? 1 : 0;
} else {
return fluidState.isIn(FluidTags.WATER) ? 1 : 0;
}
}
@Override
@@ -38,7 +38,6 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Formatting;
@@ -320,21 +320,21 @@ public class ColorApplicatorItem extends AEBasePoweredItem
return w.setBlockState(pos, newState);
}
if (blk instanceof CableBusBlock && p != null) {
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
}
BlockEntity be = w.getBlockEntity(pos);
if (be instanceof IColorableTile) {
IColorableTile ct = (IColorableTile) be;
AEColor c = ct.getColor();
if (c != newColor) {
ct.recolourBlock(side, newColor, null);
ct.recolourBlock(side, newColor, p);
return true;
}
return false;
}
if (blk instanceof CableBusBlock && p != null) {
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
}
return false;
}
@@ -54,7 +54,7 @@ import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.RayTraceContext;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import appeng.api.config.Actionable;
@@ -219,7 +219,7 @@ public class EntropyManipulatorItem extends AEBasePoweredItem implements IBlockT
// considered for onItemUse
@Override
public TypedActionResult<ItemStack> use(final World w, final PlayerEntity p, final Hand hand) {
final BlockHitResult target = rayTrace(w, p, RayTraceContext.FluidHandling.ANY);
final BlockHitResult target = raycast(w, p, RaycastContext.FluidHandling.ANY);
if (target.getType() != HitResult.Type.BLOCK) {
BlockPos pos = target.getBlockPos();
@@ -48,7 +48,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RayTraceContext;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import alexiil.mc.lib.attributes.item.FixedItemInv;
@@ -200,7 +200,7 @@ public class MatterCannonItem extends AEBasePoweredItem implements IStorageCell<
final float f1 = 0.3F;
final Box boundingBox = entity1.getBoundingBox().expand(f1, f1, f1);
final Vec3d intersection = boundingBox.rayTrace(Vec3d, Vec3d1).orElse(null);
final Vec3d intersection = boundingBox.raycast(Vec3d, Vec3d1).orElse(null);
if (intersection != null) {
final double nd = Vec3d.squaredDistanceTo(intersection);
@@ -214,9 +214,9 @@ public class MatterCannonItem extends AEBasePoweredItem implements IStorageCell<
}
}
RayTraceContext rayTraceContext = new RayTraceContext(Vec3d, Vec3d1, RayTraceContext.ShapeType.COLLIDER,
RayTraceContext.FluidHandling.NONE, p);
HitResult pos = w.rayTrace(rayTraceContext);
RaycastContext rayTraceContext = new RaycastContext(Vec3d, Vec3d1, RaycastContext.ShapeType.COLLIDER,
RaycastContext.FluidHandling.NONE, p);
HitResult pos = w.raycast(rayTraceContext);
final Vec3d vec = new Vec3d(d0, d1, d2);
if (entity != null && pos.getType() != HitResult.Type.MISS && pos.getPos().squaredDistanceTo(vec) > closest) {
@@ -307,7 +307,7 @@ public class MatterCannonItem extends AEBasePoweredItem implements IStorageCell<
final float f1 = 0.3F;
final Box boundingBox = entity1.getBoundingBox().expand(f1, f1, f1);
final Vec3d intersection = boundingBox.rayTrace(Vec3d, Vec3d1).orElse(null);
final Vec3d intersection = boundingBox.raycast(Vec3d, Vec3d1).orElse(null);
if (intersection != null) {
final double nd = Vec3d.squaredDistanceTo(intersection);
@@ -322,10 +322,10 @@ public class MatterCannonItem extends AEBasePoweredItem implements IStorageCell<
}
}
RayTraceContext rayTraceContext = new RayTraceContext(Vec3d, Vec3d1, RayTraceContext.ShapeType.COLLIDER,
RayTraceContext.FluidHandling.NONE, p);
RaycastContext rayTraceContext = new RaycastContext(Vec3d, Vec3d1, RaycastContext.ShapeType.COLLIDER,
RaycastContext.FluidHandling.NONE, p);
final Vec3d vec = new Vec3d(d0, d1, d2);
HitResult pos = w.rayTrace(rayTraceContext);
HitResult pos = w.raycast(rayTraceContext);
if (entity != null && pos.getType() != HitResult.Type.MISS
&& pos.getPos().squaredDistanceTo(vec) > closest) {
pos = new EntityHitResult(entity, entityIntersection);
+2 -1
View File
@@ -327,8 +327,9 @@ public class GridNode implements IGridNode, IPathItem {
@Override
public void setPlayerID(final int playerID) {
if (playerID >= 0) {
if (playerID >= 0 && this.playerID != playerID) {
this.playerID = playerID;
gridProxy.onGridNotification(GridNotification.OWNER_CHANGED);
}
}
+5 -1
View File
@@ -96,7 +96,11 @@ public class CraftingGridCache
nextDetail) -> nextDetail.getPriority() - firstDetail.getPriority();
static {
final ThreadFactory factory = ar -> new Thread(ar, "AE Crafting Calculator");
final ThreadFactory factory = ar -> {
final Thread crafting = new Thread(ar, "AE Crafting Calculator");
crafting.setDaemon(true);
return crafting;
};
CRAFTING_POOL = Executors.newCachedThreadPool(factory);
}
+14 -2
View File
@@ -81,8 +81,16 @@ public class SecurityCache implements ISecurityGrid {
private void updateSecurityKey() {
final long lastCode = this.securityKey;
/**
* Placing a security station will propagate the security station's owner to all
* connected grid nodes to prevent the network from not reforming due to
* different owners later.
*/
int newOwner = -1;
if (this.securityProvider.size() == 1) {
this.securityKey = this.securityProvider.get(0).getSecurityKey();
ISecurityProvider securityProvider = this.securityProvider.get(0);
this.securityKey = securityProvider.getSecurityKey();
newOwner = securityProvider.getOwner();
} else {
this.securityKey = -1;
}
@@ -90,7 +98,11 @@ public class SecurityCache implements ISecurityGrid {
if (lastCode != this.securityKey) {
this.getGrid().postEvent(new MENetworkSecurityChange());
for (final IGridNode n : this.getGrid().getNodes()) {
((GridNode) n).setLastSecurityKey(this.securityKey);
GridNode gridNode = (GridNode) n;
gridNode.setLastSecurityKey(this.securityKey);
if (gridNode.getPlayerID() != newOwner) {
gridNode.setPlayerID(newOwner);
}
}
}
}
@@ -22,7 +22,6 @@ import java.util.Iterator;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import appeng.api.events.LocatableEventAnnounce;
@@ -157,12 +156,16 @@ public class QuantumCluster implements ILocatable, IAECluster {
if (qc != null) {
final World theWorld = qc.center.getWorld();
if (!qc.isDestroyed) {
ChunkPos cPos = new ChunkPos(qc.center.getPos());
if (theWorld.getChunkManager().isChunkLoaded(cPos.x, cPos.z)) {
// In future versions, we might actually want to delay the entire registration
// until the center
// tile begins ticking normally.
if (theWorld.isChunkLoaded(qc.center.getPos())) {
final World cur = theWorld.getServer().getWorld(theWorld.getRegistryKey());
final BlockEntity te = theWorld.getBlockEntity(qc.center.getPos());
return te != qc.center || theWorld != cur;
} else {
AELog.warn("Found a registered QNB with serial %s whose chunk seems to be unloaded: %s", qe, qc);
}
}
}
@@ -275,4 +278,17 @@ public class QuantumCluster implements ILocatable, IAECluster {
private void setRing(final QuantumBridgeBlockEntity[] ring) {
this.Ring = ring;
}
@Override
public String toString() {
if (center == null) {
return "QuantumCluster{no-center}";
}
World world = center.getWorld();
BlockPos pos = center.getPos();
return "QuantumCluster{" + world + "," + pos + "}";
}
}
@@ -279,6 +279,11 @@ public class AENetworkProxy implements IGridBlock {
@Override
public void onGridNotification(final GridNotification notification) {
if (notification == GridNotification.OWNER_CHANGED) {
gp.saveChanges();
return;
}
if (this.gp instanceof CablePart) {
((CablePart) this.gp).markForUpdate();
}
@@ -28,4 +28,7 @@ public interface IGridProxyable extends IGridHost {
DimensionalCoord getLocation();
void gridChanged();
void saveChanges();
}
@@ -0,0 +1,20 @@
package appeng.mixins;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
@Mixin(Ingredient.class)
public interface IngredientAccessor {
// Temporary fix for https://github.com/SpongePowered/Mixin/issues/430
@Invoker("cacheMatchingStacks")
void appeng_cacheMatchingStacks();
@Accessor
ItemStack[] getMatchingStacks();
}
@@ -12,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.biome.BuiltinBiomes;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -23,21 +23,21 @@ import appeng.spatial.SpatialStorageDimensionIds;
* This only needs to be here because the server-side will create a dynamic
* registry manager long before our mod is initialized.
*/
@Mixin(Biomes.class)
public class BiomesMixin {
@Mixin(BuiltinBiomes.class)
public class BuiltinBiomesMixin {
@Unique
private static final Logger LOGGER = LogManager.getLogger();
@Shadow
private static Int2ObjectMap<RegistryKey<Biome>> BIOMES;
private static Int2ObjectMap<RegistryKey<Biome>> BY_RAW_ID;
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void registerBiomes(CallbackInfo ci) {
BuiltinRegistries.add(BuiltinRegistries.BIOME, SpatialStorageDimensionIds.BIOME_KEY.getValue(),
SpatialStorageBiome.INSTANCE);
int rawId = BuiltinRegistries.BIOME.getRawId(SpatialStorageBiome.INSTANCE);
RegistryKey<Biome> prev = BIOMES.put(rawId, SpatialStorageDimensionIds.BIOME_KEY);
RegistryKey<Biome> prev = BY_RAW_ID.put(rawId, SpatialStorageDimensionIds.BIOME_KEY);
if (prev != null) {
LOGGER.warn("Biome with raw-id {} was already registered: {}", rawId, prev);
}
@@ -49,7 +49,7 @@ public class DimensionTypeMixin {
dimensionType);
}
@Inject(method = "method_28517", at = @At("RETURN"))
@Inject(method = "createDefaultDimensionOptions", at = @At("RETURN"))
private static void buildDimensionRegistry(Registry<DimensionType> dimensionTypes, Registry<Biome> biomes,
Registry<ChunkGeneratorSettings> chunkGeneratorSettings, long seed,
CallbackInfoReturnable<SimpleRegistry<DimensionOptions>> cir) {
@@ -1,10 +1,12 @@
package appeng.mixins.tags;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.fluid.Fluid;
import net.minecraft.tag.FluidTags;
import net.minecraft.tag.RequiredTagList;
import net.minecraft.tag.Tag;
@Mixin(FluidTags.class)
@@ -15,4 +17,9 @@ public interface FluidTagsAccessor {
throw new AssertionError();
}
@Accessor("REQUIRED_TAGS")
static RequiredTagList<Fluid> getRequiredTags() {
throw new AssertionError();
}
}
@@ -43,7 +43,6 @@ import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
@@ -82,7 +81,6 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
private final EnumSet<LayerFlags> myLayerFlags = EnumSet.noneOf(LayerFlags.class);
private YesNo hasRedstone = YesNo.UNDECIDED;
private IPartHost tcb;
// TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not.
private boolean requiresDynamicRender = false;
private boolean inWorld = false;
// Cached collision shape for living entities
@@ -1095,11 +1093,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
}
}
VoxelShape shape = VoxelShapes.empty();
for (final Box bx : boxes) {
shape = VoxelShapes.union(shape, VoxelShapes.cuboid(bx));
}
return shape;
return VoxelShapeCache.get(boxes);
}
private void invalidateShapes() {
@@ -38,7 +38,7 @@ import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RayTraceContext;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import appeng.api.definitions.IBlockDefinition;
@@ -76,9 +76,9 @@ public class PartPlacement {
// FIXME: This was changed alot.
final LookDirection dir = Platform.getPlayerRay(player);
RayTraceContext rtc = new RayTraceContext(dir.getA(), dir.getB(), RayTraceContext.ShapeType.OUTLINE,
RayTraceContext.FluidHandling.NONE, player);
final BlockHitResult mop = world.rayTrace(rtc);
RaycastContext rtc = new RaycastContext(dir.getA(), dir.getB(), RaycastContext.ShapeType.OUTLINE,
RaycastContext.FluidHandling.NONE, player);
final BlockHitResult mop = world.raycast(rtc);
ItemPlacementContext useContext = new ItemPlacementContext(new ItemUsageContext(player, hand, mop));
BlockEntity tile = world.getBlockEntity(pos);
@@ -0,0 +1,82 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.parts;
import java.util.List;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.Box;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
/**
* While creation of a {@link net.minecraft.util.shape.VoxelShape} with
* {@link net.minecraft.util.shape.VoxelShapes#cuboid(Box)} is fast enough,
* combining voxel shapes with
* {@link net.minecraft.util.shape.VoxelShapes#union(VoxelShape, VoxelShape)} or
* any other combination method, as well as {@link VoxelShape#simplify()} are
* <b>extremely slow</b>. For example: Creating a VoxelShape for a list of 5
* bounding boxes 10,000 times takes about 1.7 seconds.
*
* <p>
* To reduce the impact of this on cables, we introduce a global voxel shape
* cache so that cables can share their combined voxel shapes better.
*/
final class VoxelShapeCache {
// Why using a List here should not make much of a difference vs. using a Set:
// The part's bounding box depends on the side it is attached to, and the sides
// are iterated over in a fixed order, meaning the order of bounding boxes
// should
// be the same for a same set of parts.
private static final LoadingCache<List<Box>, VoxelShape> CACHE = CacheBuilder.newBuilder()//
.maximumSize(10000L)//
.build(new CacheLoader<List<Box>, VoxelShape>() {
@Override
public VoxelShape load(List<Box> key) {
return create(key);
}
});
private VoxelShapeCache() {
}
public static VoxelShape get(List<Box> boxes) {
return CACHE.getUnchecked(boxes);
}
private static VoxelShape create(List<Box> boxes) {
if (boxes.isEmpty()) {
return VoxelShapes.empty();
}
int i = 0;
VoxelShape shape = VoxelShapes.cuboid(boxes.get(i));
for (; i < boxes.size(); i++) {
Box box = boxes.get(i);
shape = VoxelShapes.combine(shape, VoxelShapes.cuboid(box), BooleanBiFunction.OR);
}
return shape.simplify();
}
}
@@ -25,7 +25,6 @@ import java.util.Random;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.AutomaticItemPlacementContext;
@@ -281,50 +280,62 @@ public class FormationPlanePart extends AbstractFormationPlanePart<IAEItemStack>
}
private static boolean spawnItemEntity(ServerWorld w, BlockEntity te, AEPartLocation side, ItemStack is) {
// the item offset based on the entity height plus some offset
final double itemOffset = .55 + EntityType.ITEM.getHeight();
// The center of the block the plane is located in
final double centerX = te.getPos().getX() + .5;
final double centerY = te.getPos().getY() + .5;
final double centerY = te.getPos().getY();
final double centerZ = te.getPos().getZ() + .5;
// When spawning downwards, we have to take the item height of 0.25 into account
// Otherwise it will get stuck and be spit out in a random direction as
// minecraft spawns it at its feet position and not center
final double additionalYOffset = side.yOffset == -1 ? -.3 : 0;
// Create an ItemEntity already at the position of the plane.
// We don't know the final position, but need it for its size.
Entity entity = new ItemEntity(w, centerX, centerY, centerZ, is.copy());
// Replace it if there is a custom entity
if (is.getItem() instanceof AECustomEntityItem) {
Entity result = ((AECustomEntityItem) is.getItem()).replaceItemEntity(w, (ItemEntity) entity, is);
// Destroy the old one, in case it's spawned somehow and replace with the new
// one.
if (result != entity) {
entity.remove();
entity = result;
}
}
// When spawning downwards, we have to take into account that it spawns it at
// their "feet" and not center like x or z. So we move it up to be flush with
// the plane
final double additionalYOffset = side.yOffset == -1 ? 1 - entity.getHeight() : 0;
// Calculate the maximum spawn area so an entity hitbox will always be inside
// the block.
final double spawnAreaHeight = Math.max(0, 1 - entity.getHeight());
final double spawnAreaWidth = Math.max(0, 1 - entity.getWidth());
// Calculate the offsets to spawn it into the adjacent block, taking the sign
// into account.
// Spawn it 0.8 blocks away from the center pos when facing in this direction
// Every other direction will select a position in a .5 block area around the
// block center.
final double offsetX = (side.xOffset == 0) ? ((RANDOM_OFFSET.nextFloat() / 2) - .25)
: (side.xOffset * itemOffset);
final double offsetY = (side.yOffset == 0) ? ((RANDOM_OFFSET.nextFloat() / 2) - .25)
: ((side.yOffset * itemOffset) + additionalYOffset);
final double offsetZ = (side.zOffset == 0) ? ((RANDOM_OFFSET.nextFloat() / 2) - .25)
: (side.zOffset * itemOffset);
final double offsetX = (side.xOffset == 0) //
? ((RANDOM_OFFSET.nextFloat() * spawnAreaWidth) - spawnAreaWidth / 2)
: (side.xOffset * (.525 + entity.getWidth() / 2));
final double offsetY = (side.yOffset == 0) //
? (RANDOM_OFFSET.nextFloat() * spawnAreaHeight)
: ((side.yOffset) + additionalYOffset);
final double offsetZ = (side.zOffset == 0) //
? ((RANDOM_OFFSET.nextFloat() * spawnAreaWidth) - spawnAreaWidth / 2)
: (side.zOffset * (.525 + entity.getWidth() / 2));
final double absoluteX = centerX + offsetX;
final double absoluteY = centerY + offsetY;
final double absoluteZ = centerZ + offsetZ;
final ItemEntity ei = new ItemEntity(w, absoluteX, absoluteY, absoluteZ, is.copy());
ei.setVelocity(side.xOffset * .1, side.yOffset * 0.1, side.zOffset * 0.1);
// Set to correct position and slow the motion down a bit
entity.updatePosition(absoluteX, absoluteY, absoluteZ);
entity.setVelocity(side.xOffset * .1, side.yOffset * 0.1, side.zOffset * 0.1);
Entity result;
if (is.getItem() instanceof AECustomEntityItem) {
result = ((AECustomEntityItem) is.getItem()).replaceItemEntity(w, ei, is);
if (result != ei) {
ei.remove();
}
} else {
result = ei;
}
if (!w.spawnEntity(result)) {
result.remove();
// Try to spawn it and destroy it in case it's not possible
if (!w.spawnEntity(entity)) {
entity.remove();
return false;
}
return true;
@@ -381,12 +392,12 @@ public class FormationPlanePart extends AbstractFormationPlanePart<IAEItemStack>
@Override
public BlockPos getBlockPos() {
return this.method_30344().getBlockPos();
return this.getHitResult().getBlockPos();
}
@Override
public boolean canPlace() {
return this.getWorld().getBlockState(this.method_30344().getBlockPos()).canReplace(this);
return this.getWorld().getBlockState(this.getHitResult().getBlockPos()).canReplace(this);
}
@Override
@@ -43,9 +43,9 @@ public class PlaneModel implements BasicUnbakedModel {
private final SpriteIdentifier backTexture;
public PlaneModel(Identifier frontTexture, Identifier sidesTexture, Identifier backTexture) {
this.frontTexture = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, frontTexture);
this.sidesTexture = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, sidesTexture);
this.backTexture = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, backTexture);
this.frontTexture = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, frontTexture);
this.sidesTexture = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, sidesTexture);
this.backTexture = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, backTexture);
}
@Nullable
@@ -33,7 +33,9 @@ public final class CompassThreadFactory implements ThreadFactory {
@Override
public Thread newThread(@Nonnull final Runnable job) {
Preconditions.checkNotNull(job);
final Thread compass = new Thread(job, "AE Compass Service");
compass.setDaemon(true);
return new Thread(job, "AE Compass Service");
return compass;
}
}
@@ -84,7 +84,7 @@ public class SpatialStorageChunkGenerator extends ChunkGenerator {
}
private static FixedBiomeSource createBiomeSource(Registry<Biome> biomeRegistry) {
return new FixedBiomeSource(biomeRegistry.method_31140(SpatialStorageDimensionIds.BIOME_KEY));
return new FixedBiomeSource(biomeRegistry.getOrThrow(SpatialStorageDimensionIds.BIOME_KEY));
}
public Registry<Biome> getBiomeRegistry() {
@@ -250,7 +250,7 @@ public class AEBaseBlockEntity extends BlockEntity implements IOrientable, IComm
this.renderFragment |= 1;
} else {
// TODO: Optimize Network Load
if (this.world != null) {
if (this.world != null && !this.isRemoved() && !notLoaded()) {
boolean alreadyUpdated = false;
// Let the block update it's own state with our internal state changes
BlockState currentState = getCachedState();
@@ -145,8 +145,8 @@ public class MolecularAssemblerRenderer extends BlockEntityRenderer<MolecularAss
* model from occluding our particles.
*/
private static RenderLayer createRenderType() {
RenderPhase.Texture mipmapBlockAtlasTexture = new RenderPhase.Texture(SpriteAtlasTexture.BLOCK_ATLAS_TEX, false,
true);
RenderPhase.Texture mipmapBlockAtlasTexture = new RenderPhase.Texture(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
false, true);
RenderPhase.Lightmap disableLightmap = new RenderPhase.Lightmap(false);
RenderLayer.MultiPhaseParameters glState = RenderLayer.MultiPhaseParameters.builder()
.texture(mipmapBlockAtlasTexture).transparency(RenderPhaseMixin.getTranslucentTransparency())
@@ -37,7 +37,6 @@ import alexiil.mc.lib.attributes.fluid.amount.FluidAmount;
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import alexiil.mc.lib.attributes.item.InvMarkDirtyListener;
import alexiil.mc.lib.attributes.item.ItemInsertable;
import alexiil.mc.lib.attributes.item.LimitedFixedItemInv;
import appeng.api.config.CondenserOutput;
@@ -80,7 +80,7 @@ public final class InscriberRecipes {
name += " " + tag.getString(MaterialItem.TAG_INSCRIBE_NAME);
}
final Ingredient startingItem = Ingredient.method_26964(Stream.of(input.copy()));
final Ingredient startingItem = Ingredient.ofStacks(Stream.of(input.copy()));
final ItemStack renamedItem = input.copy();
if (!name.isEmpty()) {
@@ -92,8 +92,8 @@ public final class InscriberRecipes {
final InscriberProcessType type = InscriberProcessType.INSCRIBE;
return new InscriberRecipe(NAMEPLATE_RECIPE_ID, "", startingItem, renamedItem,
plateA.isEmpty() ? Ingredient.EMPTY : Ingredient.method_26964(Stream.of(plateA)),
plateB.isEmpty() ? Ingredient.EMPTY : Ingredient.method_26964(Stream.of(plateB)), type);
plateA.isEmpty() ? Ingredient.EMPTY : Ingredient.ofStacks(Stream.of(plateA)),
plateB.isEmpty() ? Ingredient.EMPTY : Ingredient.ofStacks(Stream.of(plateB)), type);
}
/**
@@ -49,6 +49,7 @@ import alexiil.mc.lib.attributes.fluid.filter.ConstantFluidFilter;
import alexiil.mc.lib.attributes.fluid.filter.FluidFilter;
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import alexiil.mc.lib.attributes.item.LimitedFixedItemInv;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
@@ -111,7 +112,6 @@ import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.inv.InvOperation;
import appeng.util.inv.WrapperChainedItemHandler;
import appeng.util.inv.filter.IAEItemFilter;
import appeng.util.item.AEItemStack;
public class ChestBlockEntity extends AENetworkPowerBlockEntity
@@ -123,7 +123,9 @@ public class ChestBlockEntity extends AENetworkPowerBlockEntity
private static final int BIT_CELL_STATE_MASK = 0b111;
private static final int BIT_CELL_STATE_BITS = 3;
private final AppEngInternalInventory inputInventory = new AppEngInternalInventory(this, 1);
private final LimitedFixedItemInv externalInputInventory;
private final AppEngInternalInventory cellInventory = new AppEngInternalInventory(this, 1);
private final LimitedFixedItemInv externalCellInventory;
private final FixedItemInv internalInventory = new WrapperChainedItemHandler(this.inputInventory,
this.cellInventory);
@@ -154,14 +156,35 @@ public class ChestBlockEntity extends AENetworkPowerBlockEntity
this.setInternalPublicPowerStorage(true);
this.setInternalPowerFlow(AccessRestriction.WRITE);
this.inputInventory.setFilter(new InputInventoryFilter());
this.cellInventory.setFilter(new CellInventoryFilter());
this.externalInputInventory = inputInventory.createLimitedFixedInv();
this.externalInputInventory.getAllRule().disallowExtraction().filterInserts(this::canInsertItemsExternally);
this.externalCellInventory = cellInventory.createLimitedFixedInv();
this.externalCellInventory.getAllRule().filterInserts(ChestBlockEntity::canInsertCell);
}
private static boolean canInsertCell(ItemStack stack) {
return Api.instance().registries().cell().getHandler(stack) != null;
}
public ItemStack getCell() {
return this.cellInventory.getInvStack(0);
}
private boolean canInsertItemsExternally(ItemStack stack) {
if (ChestBlockEntity.this.isPowered()) {
ChestBlockEntity.this.updateHandler();
if (this.cellHandler == null) {
return false;
}
IItemStorageChannel itemChannel = Api.instance().storage().getStorageChannel(IItemStorageChannel.class);
return this.cellHandler.getChannel() == itemChannel
&& this.cellHandler.canAccept(AEItemStack.fromItemStack(stack));
}
return false;
}
@Override
protected void PowerEvent(final PowerEventType x) {
if (x == PowerEventType.REQUEST_POWER) {
@@ -475,9 +498,9 @@ public class ChestBlockEntity extends AENetworkPowerBlockEntity
@Override
protected FixedItemInv getItemHandlerForSide(@Nonnull Direction side) {
if (side == this.getForward()) {
return this.cellInventory;
return this.externalCellInventory;
} else {
return this.inputInventory;
return this.externalInputInventory;
}
}
@@ -703,7 +726,9 @@ public class ChestBlockEntity extends AENetworkPowerBlockEntity
to.offer(fluidInsertable);
}
// FIXME FABRIC: STORAGE_MONITORABLE_ACCESSOR
if (accessor != null && to.getSearchDirection() != getForward()) {
to.offer(accessor);
}
}
private class Accessor implements IStorageMonitorableAccessor {
@@ -758,37 +783,6 @@ public class ChestBlockEntity extends AENetworkPowerBlockEntity
}
private class InputInventoryFilter implements IAEItemFilter {
@Override
public boolean allowExtract(FixedItemInv inv, int slot, int amount) {
return false;
}
@Override
public boolean allowInsert(FixedItemInv inv, int slot, ItemStack stack) {
if (ChestBlockEntity.this.isPowered()) {
ChestBlockEntity.this.updateHandler();
return ChestBlockEntity.this.cellHandler != null && ChestBlockEntity.this.cellHandler
.getChannel() == Api.instance().storage().getStorageChannel(IItemStorageChannel.class);
}
return false;
}
}
private static class CellInventoryFilter implements IAEItemFilter {
@Override
public boolean allowExtract(FixedItemInv inv, int slot, int amount) {
return true;
}
@Override
public boolean allowInsert(FixedItemInv inv, int slot, ItemStack stack) {
return Api.instance().registries().cell().getHandler(stack) != null;
}
}
@Override
public ItemStack getItemStackRepresentation() {
return Api.instance().definitions().blocks().chest().maybeStack(1).orElse(ItemStack.EMPTY);
+4 -4
View File
@@ -61,7 +61,7 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.RayTraceContext;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
@@ -715,7 +715,7 @@ public class Platform {
f1 = 0.3F;
// FIXME: Three different bounding boxes available, should double-check
final Box boundingBox = entity1.getBoundingBox().expand(f1, f1, f1);
final Vec3d rtResult = boundingBox.rayTrace(vec3, vec31).orElse(null);
final Vec3d rtResult = boundingBox.raycast(vec3, vec31).orElse(null);
if (rtResult != null) {
final double nd = vec3.squaredDistanceTo(rtResult);
@@ -735,8 +735,8 @@ public class Platform {
if (hitBlocks) {
vec = new Vec3d(d0, d1, d2);
// FIXME: passing p as entity here might be incorrect
pos = w.rayTrace(new RayTraceContext(vec3, vec31, RayTraceContext.ShapeType.COLLIDER,
RayTraceContext.FluidHandling.ANY, p));
pos = w.raycast(new RaycastContext(vec3, vec31, RaycastContext.ShapeType.COLLIDER,
RaycastContext.FluidHandling.ANY, p));
}
if (entity != null && pos != null && pos.getPos().squaredDistanceTo(vec) > closest) {
@@ -7,7 +7,7 @@
"spatial.DimensionTypeMixin",
"spatial.DimensionOptionMixin",
"spatial.EntityMixin",
"spatial.BiomesMixin",
"spatial.BuiltinBiomesMixin",
"structure.StructuresConfigMixin",
"structure.ConfiguredStructureFeaturesAccessor",
"structure.StructureFeatureAccessor",
@@ -25,7 +25,8 @@
"TntAccessor",
"ServerWorldCustomItemEntityMixin",
"BootstrapMixin",
"ItemEntityAccessor"
"ItemEntityAccessor",
"IngredientAccessor"
],
"client": [
"spatial.SkyRenderMixin",
@@ -2,8 +2,8 @@
"gui.appliedenergistics2.Gray": "Grey",
"gui.appliedenergistics2.LightGray": "Light Grey",
"gui.appliedenergistics2.inWorldCraftingPresses": "Crafting Presses are found in the centre of meteorites which can be found in around the world, they can be located by using a meteorite compass.",
"gui.tooltips.appliedenergistics2.SearchMode_JEIAuto": "JEI Synchronised Auto",
"gui.tooltips.appliedenergistics2.SearchMode_JEIStandard": "JEI Synchronised Standard",
"gui.tooltips.appliedenergistics2.SearchMode_JEIAuto": "REI Synchronised Auto",
"gui.tooltips.appliedenergistics2.SearchMode_JEIStandard": "REI Synchronised Standard",
"gui.tooltips.appliedenergistics2.TerminalStyle_Small": "Small Centered Terminal",
"gui.tooltips.appliedenergistics2.TerminalStyle_Tall": "Tall Centered Terminal",
"item.appliedenergistics2.color_applicator": "Colour Applicator"
@@ -380,10 +380,10 @@
"gui.tooltips.appliedenergistics2.SearchMode": "Search Box Mode",
"gui.tooltips.appliedenergistics2.SearchMode_Auto": "Auto Search",
"gui.tooltips.appliedenergistics2.SearchMode_AutoKeep": "Auto Search Keep",
"gui.tooltips.appliedenergistics2.SearchMode_JEIAuto": "JEI Synchronized Auto",
"gui.tooltips.appliedenergistics2.SearchMode_JEIAutoKeep": "JEI Synchronized Auto Keep",
"gui.tooltips.appliedenergistics2.SearchMode_JEIStandard": "JEI Synchronized Standard",
"gui.tooltips.appliedenergistics2.SearchMode_JEIStandardKeep": "JEI Synchronized Standard Keep",
"gui.tooltips.appliedenergistics2.SearchMode_JEIAuto": "REI Synchronized Auto",
"gui.tooltips.appliedenergistics2.SearchMode_JEIAutoKeep": "REI Synchronized Auto Keep",
"gui.tooltips.appliedenergistics2.SearchMode_JEIStandard": "REI Synchronized Standard",
"gui.tooltips.appliedenergistics2.SearchMode_JEIStandardKeep": "REI Synchronized Standard Keep",
"gui.tooltips.appliedenergistics2.SearchMode_Standard": "Standard Search",
"gui.tooltips.appliedenergistics2.SearchMode_StandardKeep": "Standard Search Keep",
"gui.tooltips.appliedenergistics2.Singularity": "Condense Into Singularities\n%s per item",
@@ -705,5 +705,9 @@
"waila.appliedenergistics2.P2POutput": "Linked (Output Side)",
"waila.appliedenergistics2.P2PUnlinked": "Unlinked",
"waila.appliedenergistics2.Showing": "Showing",
"waila.appliedenergistics2.Unlocked": "Unlocked"
"waila.appliedenergistics2.Unlocked": "Unlocked",
"jei.appliedenergistics2.missing_id": "Cannot identify recipe",
"jei.appliedenergistics2.recipe_too_large": "Recipe larger than 3x3",
"jei.appliedenergistics2.requires_processing_mode": "Requires processing mode",
"jei.appliedenergistics2.no_output": "Recipe has no output"
}
@@ -1,7 +1,8 @@
{
"display": {
"icon": {
"item": "appliedenergistics2:facade"
"item": "appliedenergistics2:facade",
"nbt": "{item:\"minecraft:stone\"}"
},
"title": {
"translate": "achievement.ae2.Facade"
File diff suppressed because it is too large Load Diff