Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5da5315a5b | |||
| f3c44c64c2 | |||
| b8f48407cd | |||
| 489b0f635e | |||
| e6fb553a53 | |||
| cacd824db3 | |||
| 876bb52087 | |||
| 1b4969c8e7 | |||
| f2427ee347 | |||
| 4aa9c92a68 | |||
| 971a9d22e8 | |||
| b5a90357c0 | |||
| 5be0adbc89 | |||
| d8e5f4fed5 | |||
| 47dfc19b7c | |||
| 45f5851079 | |||
| 76efe6026d | |||
| 4e03cbd356 | |||
| ce058b006c | |||
| 9005db6f1c | |||
| 58afe755b8 | |||
| f368f9dac2 | |||
| e5b45d51cc | |||
| 5a1d5356d1 | |||
| 5aef82b9ef | |||
| 483f93f977 | |||
| 1759f92164 | |||
| e50c13e570 | |||
| c0575e11b0 | |||
| 749dd35f88 | |||
| ca79a377bd | |||
| 475c8ff5a1 | |||
| 5b3ccf26d0 | |||
| e73dbe4891 | |||
| 38244000c9 | |||
| d87ff9d1e7 | |||
| 4ba97ec899 | |||
| 0147e1ddfb | |||
| 621d2ee715 | |||
| 31ecdfbe0a | |||
| f0bfbfec48 | |||
| 06a8843a65 | |||
| d07937fa93 | |||
| 65f58913a4 | |||
| 2bca23d75d | |||
| b47543d166 | |||
| a340925d98 | |||
| b8e53adf40 | |||
| 44544c598f | |||
| 0be7322c2c | |||
| a521e827db | |||
| bcee2ec427 | |||
| 17648633fe | |||
| fcbebd4ffc | |||
| 9e41d543f5 | |||
| 81cc0b804d | |||
| e1fc9908b3 | |||
| 9ed1535477 | |||
| 1ef345d4a4 | |||
| ff7d1b9719 | |||
| 88c9370e4f | |||
| f2aae3ef0f | |||
| 12739f1e66 | |||
| 81d2170ada | |||
| 0f469e9df6 | |||
| 0f34f09b32 | |||
| 8e54a8fed9 | |||
| d9bb81a00f | |||
| 39e038d916 |
@@ -2,7 +2,8 @@ name: 'Build PRs'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
+211
-81
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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'])
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -106,6 +106,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
|
||||
private Stopwatch dbl_clickTimer = Stopwatch.createStarted();
|
||||
private ItemStack dbl_whichItem = ItemStack.EMPTY;
|
||||
private Slot bl_clicked;
|
||||
private boolean handlingRightClick;
|
||||
protected final List<CustomSlotWidget> guiSlots = new ArrayList<>();
|
||||
|
||||
public AEBaseScreen(T container, PlayerInventory playerInventory, Text title) {
|
||||
@@ -284,12 +285,18 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
|
||||
public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) {
|
||||
this.drag_click.clear();
|
||||
|
||||
// Forward right-clicks as-if they were left-clicks
|
||||
if (btn == 1) {
|
||||
for (final Object o : this.buttons) {
|
||||
final AbstractButtonWidget widget = (AbstractButtonWidget) o;
|
||||
if (widget.isMouseOver(xCoord, yCoord)) {
|
||||
return super.mouseClicked(xCoord, yCoord, 0);
|
||||
handlingRightClick = true;
|
||||
try {
|
||||
for (final Object o : this.buttons) {
|
||||
final AbstractButtonWidget widget = (AbstractButtonWidget) o;
|
||||
if (widget.isMouseOver(xCoord, yCoord)) {
|
||||
return super.mouseClicked(xCoord, yCoord, 0);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
handlingRightClick = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,22 +307,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()) {
|
||||
@@ -622,15 +643,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
|
||||
}
|
||||
|
||||
protected Text getGuiDisplayName(final Text in) {
|
||||
return this.hasCustomInventoryName() ? new LiteralText(this.getInventoryName()) : in;
|
||||
}
|
||||
|
||||
private boolean hasCustomInventoryName() {
|
||||
return this.handler.getCustomName() != null;
|
||||
}
|
||||
|
||||
private String getInventoryName() {
|
||||
return this.handler.getCustomName();
|
||||
return title.getString().isEmpty() ? in : title;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,6 +811,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();
|
||||
@@ -805,6 +823,14 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true while the current event being handled is a click of the right
|
||||
* mouse button.
|
||||
*/
|
||||
public boolean isHandlingRightClick() {
|
||||
return handlingRightClick;
|
||||
}
|
||||
|
||||
public List<Rectangle> getExclusionZones() {
|
||||
return Lists.newArrayList(new Rectangle(x, y, backgroundWidth, backgroundHeight));
|
||||
}
|
||||
|
||||
@@ -46,22 +46,28 @@ public class CraftAmountScreen extends AEBaseScreen<CraftAmountContainer> {
|
||||
public void init() {
|
||||
super.init();
|
||||
|
||||
this.amountToCraft = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.CRAFT_ITEM_COUNT, value -> {
|
||||
});
|
||||
this.amountToCraft = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.CRAFT_ITEM_COUNT);
|
||||
this.amountToCraft.setValue(1);
|
||||
this.amountToCraft.setTextFieldBounds(62, 57, 50);
|
||||
this.amountToCraft.setMinValue(1);
|
||||
this.amountToCraft.setHideValidationIcon(true);
|
||||
this.amountToCraft.addButtons(children::add, this::addButton);
|
||||
|
||||
this.next = this
|
||||
.addButton(new ButtonWidget(this.x + 128, this.y + 51, 38, 20, GuiText.Next.text(), this::confirm));
|
||||
this.amountToCraft.setOnConfirm(() -> this.confirm(this.next));
|
||||
|
||||
subGui.addBackButton(this::addButton, 154, 0);
|
||||
|
||||
changeFocus(true);
|
||||
}
|
||||
|
||||
private void confirm(ButtonWidget button) {
|
||||
NetworkHandler.instance()
|
||||
.sendToServer(new CraftRequestPacket((int) this.amountToCraft.getValue(), hasShiftDown()));
|
||||
int amount = this.amountToCraft.getIntValue().orElse(0);
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
NetworkHandler.instance().sendToServer(new CraftRequestPacket(amount, hasShiftDown()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,23 +83,9 @@ public class CraftAmountScreen extends AEBaseScreen<CraftAmountContainer> {
|
||||
this.bindTexture("guis/craft_amt.png");
|
||||
drawTexture(matrices, offsetX, offsetY, 0, 0, this.backgroundWidth, this.backgroundHeight);
|
||||
|
||||
this.next.active = this.amountToCraft.getValue() > 0;
|
||||
this.next.active = this.amountToCraft.getIntValue().orElse(0) > 0;
|
||||
|
||||
this.amountToCraft.render(matrices, offsetX, offsetY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) {
|
||||
if (keyCode == 28) {
|
||||
this.next.onPress();
|
||||
return true;
|
||||
} else {
|
||||
return super.keyPressed(keyCode, scanCode, p_keyPressed_3_);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getBackground() {
|
||||
return "guis/craftAmt.png";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.util.List;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
@@ -73,10 +75,6 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
|
||||
this.setScrollBar(scrollbar);
|
||||
}
|
||||
|
||||
boolean isAutoStart() {
|
||||
return this.handler.isAutoStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
@@ -87,12 +85,14 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
|
||||
this.addButton(this.start);
|
||||
|
||||
this.selectCPU = new ButtonWidget(this.x + (219 - 180) / 2, this.y + this.backgroundHeight - 68, 180, 20,
|
||||
GuiText.CraftingCPU.withSuffix(": ").append(GuiText.Automatic.text()), btn -> selectNextCpu());
|
||||
getNextCpuButtonLabel(), btn -> selectNextCpu());
|
||||
this.selectCPU.active = false;
|
||||
this.addButton(this.selectCPU);
|
||||
|
||||
addButton(new ButtonWidget(this.x + 6, this.y + this.backgroundHeight - 25, 50, 20, GuiText.Cancel.text(),
|
||||
btn -> subGui.goBack()));
|
||||
|
||||
this.setScrollBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,22 +133,22 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
|
||||
}
|
||||
|
||||
private void updateCPUButtonText() {
|
||||
Text btnTextText = GuiText.CraftingCPU.withSuffix(": ").append(GuiText.Automatic.text());
|
||||
if (this.handler.getSelectedCpu() >= 0)// && status.selectedCpu < status.cpus.size() )
|
||||
{
|
||||
if (this.handler.getName() != null) {
|
||||
final String name = this.handler.getName().asTruncatedString(20);
|
||||
btnTextText = GuiText.CraftingCPU.withSuffix(": " + name);
|
||||
} else {
|
||||
btnTextText = GuiText.CraftingCPU.withSuffix(": #" + this.handler.getSelectedCpu());
|
||||
}
|
||||
}
|
||||
this.selectCPU.setMessage(getNextCpuButtonLabel());
|
||||
}
|
||||
|
||||
private Text getNextCpuButtonLabel() {
|
||||
if (this.handler.hasNoCPU()) {
|
||||
btnTextText = GuiText.NoCraftingCPUs.text();
|
||||
return GuiText.NoCraftingCPUs.text();
|
||||
}
|
||||
|
||||
this.selectCPU.setMessage(btnTextText);
|
||||
Text cpuName;
|
||||
if (this.handler.cpuName == null) {
|
||||
cpuName = GuiText.Automatic.text();
|
||||
} else {
|
||||
cpuName = this.handler.cpuName;
|
||||
}
|
||||
|
||||
return GuiText.CraftingCPU.withSuffix(": ").append(cpuName);
|
||||
}
|
||||
|
||||
private boolean isSimulation() {
|
||||
@@ -440,7 +440,7 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) {
|
||||
if (!this.checkHotbarKeys(keyCode, scanCode)) {
|
||||
if (keyCode == 28) {
|
||||
if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
|
||||
this.start();
|
||||
return true;
|
||||
}
|
||||
@@ -449,7 +449,7 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
|
||||
}
|
||||
|
||||
private void selectNextCpu() {
|
||||
final boolean backwards = getClient().mouse.wasRightButtonClicked();
|
||||
final boolean backwards = isHandlingRightClick();
|
||||
NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next"));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,12 +44,12 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
|
||||
super.init();
|
||||
|
||||
this.selectCPU = new ButtonWidget(this.x + 8, this.y + this.backgroundHeight - 25, 150, 20,
|
||||
GuiText.CraftingCPU.withSuffix(": ").append(GuiText.NoCraftingCPUs.text()), btn -> selectNextCpu());
|
||||
getNextCpuButtonLabel(), btn -> selectNextCpu());
|
||||
this.addButton(this.selectCPU);
|
||||
|
||||
subGui.addBackButton(btn -> {
|
||||
addButton(btn);
|
||||
btn.setHideEdge(13);
|
||||
btn.setHideEdge(true);
|
||||
}, 213, -4);
|
||||
}
|
||||
|
||||
@@ -60,23 +60,14 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
|
||||
}
|
||||
|
||||
private void updateCPUButtonText() {
|
||||
Text btnTextText = GuiText.NoCraftingJobs.text();
|
||||
|
||||
if (this.handler.selectedCpu >= 0)// && status.selectedCpu < status.cpus.size() )
|
||||
{
|
||||
if (this.handler.myName != null) {
|
||||
final String name = this.handler.myName.asTruncatedString(20);
|
||||
btnTextText = GuiText.CPUs.withSuffix(": " + name);
|
||||
} else {
|
||||
btnTextText = GuiText.CPUs.withSuffix(": #" + this.handler.selectedCpu);
|
||||
}
|
||||
}
|
||||
this.selectCPU.setMessage(getNextCpuButtonLabel());
|
||||
}
|
||||
|
||||
private Text getNextCpuButtonLabel() {
|
||||
if (this.handler.noCPU) {
|
||||
btnTextText = GuiText.NoCraftingJobs.text();
|
||||
return GuiText.NoCraftingJobs.text();
|
||||
}
|
||||
|
||||
this.selectCPU.setMessage(btnTextText);
|
||||
return GuiText.CraftingCPU.withSuffix(": ").append(handler.cpuName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,9 +75,8 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
|
||||
return in; // the cup name is on the button
|
||||
}
|
||||
|
||||
// FIXME: Extract to separate class? Shared with GuiCraftConfirm
|
||||
private void selectNextCpu() {
|
||||
final boolean backwards = client.mouse.wasRightButtonClicked();
|
||||
final boolean backwards = isHandlingRightClick();
|
||||
NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next"));
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ public class InterfaceScreen extends UpgradeableScreen<InterfaceContainer> {
|
||||
}
|
||||
|
||||
private void selectNextInterfaceMode() {
|
||||
final boolean backwards = getClient().mouse.wasRightButtonClicked();
|
||||
final boolean backwards = isHandlingRightClick();
|
||||
NetworkHandler.instance().sendToServer(new ConfigButtonPacket(Settings.INTERFACE_TERMINAL, backwards));
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -34,8 +34,6 @@ import appeng.client.gui.widgets.ServerSettingToggleButton;
|
||||
import appeng.client.gui.widgets.SettingToggleButton;
|
||||
import appeng.container.implementations.LevelEmitterContainer;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
|
||||
public class LevelEmitterScreen extends UpgradeableScreen<LevelEmitterContainer> {
|
||||
|
||||
@@ -51,15 +49,18 @@ public class LevelEmitterScreen extends UpgradeableScreen<LevelEmitterContainer>
|
||||
public void init() {
|
||||
super.init();
|
||||
|
||||
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_ITEM_COUNT,
|
||||
this::onLevelChange);
|
||||
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_ITEM_COUNT);
|
||||
this.level.setTextFieldBounds(25, 44, 75);
|
||||
this.level.addButtons(children::add, this::addButton);
|
||||
handler.setTextField(this.level);
|
||||
this.level.setValue(handler.getReportingValue());
|
||||
this.level.setOnChange(this::saveReportingValue);
|
||||
this.level.setOnConfirm(this::onClose);
|
||||
|
||||
this.changeFocus(true);
|
||||
}
|
||||
|
||||
private void onLevelChange(long level) {
|
||||
NetworkHandler.instance().sendToServer(new ConfigValuePacket("LevelEmitter.Value", String.valueOf(level)));
|
||||
private void saveReportingValue() {
|
||||
this.level.getLongValue().ifPresent(handler::setReportingValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import me.shedaniel.math.Rectangle;
|
||||
@@ -51,6 +52,7 @@ import appeng.client.gui.widgets.SettingToggleButton;
|
||||
import appeng.client.gui.widgets.TabButton;
|
||||
import appeng.client.me.InternalSlotME;
|
||||
import appeng.client.me.ItemRepo;
|
||||
import appeng.client.render.StackSizeRenderer;
|
||||
import appeng.container.implementations.CraftingStatusContainer;
|
||||
import appeng.container.implementations.MEMonitorableContainer;
|
||||
import appeng.container.slot.AppEngSlot;
|
||||
@@ -237,7 +239,7 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
|
||||
if (this.viewCell || this instanceof WirelessTermScreen) {
|
||||
this.craftingStatusBtn = this.addButton(new TabButton(this.x + 170, this.y - 4, 2 + 11 * 16,
|
||||
GuiText.CraftingStatus.text(), this.itemRenderer, btn -> showCraftingStatus()));
|
||||
this.craftingStatusBtn.setHideEdge(13);
|
||||
this.craftingStatusBtn.setHideEdge(true);
|
||||
}
|
||||
|
||||
this.isAutoFocus = SearchBoxMode.AUTOSEARCH == searchMode || SearchBoxMode.JEI_AUTOSEARCH == searchMode
|
||||
@@ -283,6 +285,8 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
|
||||
craftingGridOffsetX -= 25;
|
||||
craftingGridOffsetY -= 6;
|
||||
|
||||
this.setScrollBar();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -292,6 +296,16 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
|
||||
|
||||
this.currentMouseX = mouseX;
|
||||
this.currentMouseY = mouseY;
|
||||
|
||||
// Show the number of active crafting jobs
|
||||
if (this.craftingStatusBtn != null && handler.activeCraftingJobs != -1) {
|
||||
// The stack size renderer expects a 16x16 slot, while the button is normally
|
||||
// bigger
|
||||
int x = this.craftingStatusBtn.x + (this.craftingStatusBtn.getWidth() - 16) / 2;
|
||||
int y = this.craftingStatusBtn.y + (this.craftingStatusBtn.getHeight() - 16) / 2;
|
||||
StackSizeRenderer.renderSizeLabel(textRenderer, x - this.x, y - this.y,
|
||||
new LiteralText(String.valueOf(handler.activeCraftingJobs)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -356,6 +370,7 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
|
||||
if (this.searchField != null) {
|
||||
this.searchField.render(matrices, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String getBackground() {
|
||||
|
||||
@@ -2,41 +2,58 @@ package appeng.client.gui.implementations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawableHelper;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
|
||||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.NumberEntryType;
|
||||
import appeng.client.gui.widgets.ITickingWidget;
|
||||
import appeng.client.gui.widgets.NumberBox;
|
||||
import appeng.client.gui.widgets.ConfirmableTextField;
|
||||
import appeng.client.gui.widgets.ValidationIcon;
|
||||
import appeng.core.AEConfig;
|
||||
|
||||
/**
|
||||
* A utility widget that consists of a text-field to enter a number with
|
||||
* attached buttons to increment/decrement the number in fixed intervals.
|
||||
*/
|
||||
public class NumberEntryWidget implements ITickingWidget {
|
||||
public class NumberEntryWidget extends DrawableHelper {
|
||||
|
||||
private static final Text INVALID_NUMBER = new TranslatableText("gui.appliedenergistics2.validation.InvalidNumber");
|
||||
private static final String NUMBER_LESS_THAN_MIN_VALUE = "gui.appliedenergistics2.validation.NumberLessThanMinValue";
|
||||
private static final Text PLUS = Text.of("+");
|
||||
private static final Text MINUS = Text.of("-");
|
||||
private static final int TEXT_COLOR_ERROR = 0xFF1900;
|
||||
private static final int TEXT_COLOR_NORMAL = 0xFFFFFF;
|
||||
|
||||
private final AEBaseScreen<?> parent;
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
|
||||
private final NumberBox level;
|
||||
private final ConfirmableTextField textField;
|
||||
private final NumberEntryType type;
|
||||
private List<ButtonWidget> buttons;
|
||||
private long minValue;
|
||||
private ValidationIcon validationIcon;
|
||||
|
||||
public NumberEntryWidget(AEBaseScreen<?> parent, int x, int y, int width, int height, NumberEntryType type,
|
||||
LongConsumer changeListener) {
|
||||
// Called when the value changes
|
||||
private Runnable onChange;
|
||||
|
||||
// Called when the user presses enter while there's a valid number in the field
|
||||
private Runnable onConfirm;
|
||||
|
||||
private boolean hideValidationIcon;
|
||||
|
||||
public NumberEntryWidget(AEBaseScreen<?> parent, int x, int y, int width, int height, NumberEntryType type) {
|
||||
this.parent = parent;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
@@ -45,27 +62,50 @@ public class NumberEntryWidget implements ITickingWidget {
|
||||
TextRenderer font = parent.getClient().textRenderer;
|
||||
int inputX = parent.getX() + x;
|
||||
int inputY = parent.getY() + y;
|
||||
this.level = new NumberBox(font, inputX, inputY, width, font.fontHeight, type.getInputType(), changeListener);
|
||||
this.level.setHasBorder(false);
|
||||
this.level.setMaxLength(16);
|
||||
this.level.setEditableColor(0xFFFFFF);
|
||||
this.level.setVisible(true);
|
||||
parent.setInitialFocus(this.level);
|
||||
this.textField = new ConfirmableTextField(font, inputX, inputY, width, font.fontHeight, LiteralText.EMPTY);
|
||||
this.textField.setHasBorder(false);
|
||||
this.textField.setMaxLength(16);
|
||||
this.textField.setEditableColor(TEXT_COLOR_NORMAL);
|
||||
this.textField.setVisible(true);
|
||||
this.textField.setSelected(true);
|
||||
parent.setInitialFocus(this.textField);
|
||||
this.textField.setChangedListener(text -> {
|
||||
validate();
|
||||
if (onChange != null) {
|
||||
this.onChange.run();
|
||||
}
|
||||
});
|
||||
this.textField.setOnConfirm(() -> {
|
||||
// Only confirm if it's actually valid
|
||||
if (this.onConfirm != null && getLongValue().isPresent()) {
|
||||
this.onConfirm.run();
|
||||
}
|
||||
});
|
||||
validate();
|
||||
}
|
||||
|
||||
public void setOnConfirm(Runnable callback) {
|
||||
this.onConfirm = callback;
|
||||
}
|
||||
|
||||
public void setOnChange(Runnable callback) {
|
||||
this.onChange = callback;
|
||||
}
|
||||
|
||||
public void setActive(boolean active) {
|
||||
this.level.active = active;
|
||||
this.textField.active = active;
|
||||
this.buttons.forEach(b -> b.active = active);
|
||||
}
|
||||
|
||||
public void setTextFieldBounds(int x, int y, int width) {
|
||||
this.level.x = parent.getX() + x;
|
||||
this.level.y = parent.getY() + y;
|
||||
this.level.setWidth(width);
|
||||
this.textField.x = parent.getX() + x;
|
||||
this.textField.y = parent.getY() + y;
|
||||
this.textField.setWidth(width);
|
||||
}
|
||||
|
||||
public void setMinValue(long minValue) {
|
||||
this.level.setMinValue(minValue);
|
||||
this.minValue = minValue;
|
||||
validate();
|
||||
}
|
||||
|
||||
public void addButtons(Consumer<Element> addChildren, Consumer<ButtonWidget> addButton) {
|
||||
@@ -78,54 +118,116 @@ public class NumberEntryWidget implements ITickingWidget {
|
||||
int left = parent.getX() + x;
|
||||
int top = parent.getY() + y;
|
||||
|
||||
List<ButtonWidget> buttons = new ArrayList<>(8);
|
||||
List<ButtonWidget> buttons = new ArrayList<>(9);
|
||||
|
||||
buttons.add(new ButtonWidget(left, top, 22, 20, makeLabel(PLUS, a), btn -> addQty(a)));
|
||||
buttons.add(new ButtonWidget(left + 28, top, 28, 20, makeLabel(PLUS, b), btn -> addQty(b)));
|
||||
buttons.add(new ButtonWidget(left + 62, top, 32, 20, makeLabel(PLUS, c), btn -> addQty(c)));
|
||||
buttons.add(new ButtonWidget(left + 100, top, 38, 20, makeLabel(PLUS, d), btn -> addQty(d)));
|
||||
|
||||
// Need to add these now for sensible tab-order
|
||||
buttons.forEach(addButton);
|
||||
|
||||
// Placing this here will give a sensible tab order
|
||||
addChildren.accept(this.level);
|
||||
addChildren.accept(this.textField);
|
||||
|
||||
buttons.add(new ButtonWidget(left, top + 42, 22, 20, makeLabel(MINUS, a), btn -> addQty(-a)));
|
||||
buttons.add(new ButtonWidget(left + 28, top + 42, 28, 20, makeLabel(MINUS, b), btn -> addQty(-b)));
|
||||
buttons.add(new ButtonWidget(left + 62, top + 42, 32, 20, makeLabel(MINUS, c), btn -> addQty(-c)));
|
||||
buttons.add(new ButtonWidget(left + 100, top + 42, 38, 20, makeLabel(MINUS, d), btn -> addQty(-d)));
|
||||
|
||||
// This element is not focusable
|
||||
if (!hideValidationIcon) {
|
||||
this.validationIcon = new ValidationIcon(left + 104, top + 27);
|
||||
buttons.add(this.validationIcon);
|
||||
}
|
||||
|
||||
// Add the rest to the tab order
|
||||
buttons.subList(4, buttons.size()).forEach(addButton);
|
||||
|
||||
this.buttons = buttons;
|
||||
this.buttons.forEach(addButton);
|
||||
|
||||
// we need to re-validate because the icon may now be present and needs it's
|
||||
// initial state
|
||||
this.validate();
|
||||
}
|
||||
|
||||
private void addQty(final long i) {
|
||||
long currentValue = this.level.getValue();
|
||||
long minValue = this.level.getMinValue();
|
||||
this.level.setText(String.valueOf(Math.max(minValue, currentValue + i)));
|
||||
/**
|
||||
* Returns the integer value currently in the text-field, if it is a valid
|
||||
* number and is within the allowed min/max value.
|
||||
*/
|
||||
public OptionalInt getIntValue() {
|
||||
String text = textField.getText().trim();
|
||||
try {
|
||||
int value = Integer.parseInt(text, 10);
|
||||
if (value < minValue) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
return OptionalInt.of(value);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) {
|
||||
this.level.render(matrices, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
public void setValue(long value, boolean skipNotify) {
|
||||
this.level.setValue(value, skipNotify);
|
||||
/**
|
||||
* Returns the long value currently in the text-field, if it is a valid number
|
||||
* and is within the allowed min/max value.
|
||||
*/
|
||||
public OptionalLong getLongValue() {
|
||||
String text = textField.getText().trim();
|
||||
try {
|
||||
long value = Long.parseLong(text, 10);
|
||||
if (value < minValue) {
|
||||
return OptionalLong.empty();
|
||||
}
|
||||
return OptionalLong.of(value);
|
||||
} catch (NumberFormatException ignored) {
|
||||
return OptionalLong.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
setValue(value, false);
|
||||
this.textField.setText(String.valueOf(Math.max(minValue, value)));
|
||||
this.textField.setCursorToEnd();
|
||||
this.textField.setSelectionStart(0);
|
||||
validate();
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return level.getValue();
|
||||
private void addQty(final long i) {
|
||||
getLongValue().ifPresent(currentValue -> setValue(currentValue + i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
this.level.tick();
|
||||
public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) {
|
||||
this.textField.render(matrices, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
List<Text> validationErrors = new ArrayList<>();
|
||||
|
||||
String text = textField.getText().trim();
|
||||
try {
|
||||
long value = Long.parseLong(text, 10);
|
||||
if (value < minValue) {
|
||||
validationErrors.add(new TranslatableText(NUMBER_LESS_THAN_MIN_VALUE, minValue));
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
validationErrors.add(INVALID_NUMBER);
|
||||
}
|
||||
|
||||
boolean valid = validationErrors.isEmpty();
|
||||
this.textField.setEditableColor(valid ? TEXT_COLOR_NORMAL : TEXT_COLOR_ERROR);
|
||||
if (this.validationIcon != null) {
|
||||
this.validationIcon.setValid(valid);
|
||||
this.validationIcon.setTooltip(validationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
private Text makeLabel(Text prefix, int amount) {
|
||||
return prefix.copy().append(String.valueOf(amount));
|
||||
}
|
||||
|
||||
public void setHideValidationIcon(boolean hideValidationIcon) {
|
||||
this.hideValidationIcon = hideValidationIcon;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
package appeng.client.gui.implementations;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.text.Text;
|
||||
@@ -26,8 +28,6 @@ import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.NumberEntryType;
|
||||
import appeng.container.implementations.PriorityContainer;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
|
||||
public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
|
||||
|
||||
@@ -38,24 +38,38 @@ public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
|
||||
public PriorityScreen(PriorityContainer container, PlayerInventory playerInventory, Text title) {
|
||||
super(container, playerInventory, title);
|
||||
this.subGui = new AESubScreen(this, container.getPriorityHost());
|
||||
|
||||
// This is the effective size of the background image
|
||||
backgroundWidth = 175;
|
||||
backgroundHeight = 128;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
|
||||
this.priority = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.PRIORITY, this::onPriorityChange);
|
||||
this.priority = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.PRIORITY);
|
||||
this.priority.setTextFieldBounds(62, 57, 50);
|
||||
this.priority.setMinValue(Integer.MIN_VALUE);
|
||||
handler.setTextField(this.priority);
|
||||
this.priority.setValue(this.handler.getPriorityValue());
|
||||
this.priority.addButtons(children::add, this::addButton);
|
||||
|
||||
this.subGui.addBackButton(this::addButton, 154, 0);
|
||||
|
||||
this.priority.setOnChange(this::savePriority);
|
||||
this.priority.setOnConfirm(() -> {
|
||||
savePriority();
|
||||
this.subGui.goBack();
|
||||
});
|
||||
|
||||
changeFocus(true);
|
||||
}
|
||||
|
||||
private void onPriorityChange(long priority) {
|
||||
NetworkHandler.instance()
|
||||
.sendToServer(new ConfigValuePacket("PriorityHost.Priority", String.valueOf(priority)));
|
||||
private void savePriority() {
|
||||
OptionalInt priority = this.priority.getIntValue();
|
||||
if (priority.isPresent()) {
|
||||
handler.setPriority(priority.getAsInt());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,13 +80,10 @@ public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
|
||||
@Override
|
||||
public void drawBG(MatrixStack matrices, final int offsetX, final int offsetY, final int mouseX, final int mouseY,
|
||||
float partialTicks) {
|
||||
this.bindTexture(getBackground());
|
||||
this.bindTexture("guis/priority.png");
|
||||
drawTexture(matrices, offsetX, offsetY, 0, 0, this.backgroundWidth, this.backgroundHeight);
|
||||
|
||||
this.priority.render(matrices, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
protected String getBackground() {
|
||||
return "guis/priority.png";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,51 @@
|
||||
/*
|
||||
* 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.client.gui.widgets;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class ConfirmableTextField extends TextFieldWidget {
|
||||
|
||||
private Runnable onConfirm;
|
||||
|
||||
public ConfirmableTextField(TextRenderer fontRenderer, int x, int y, int width, int height, Text text) {
|
||||
super(fontRenderer, x, y, width, height, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (isActive() && (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER)) {
|
||||
if (this.onConfirm != null) {
|
||||
this.onConfirm.run();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
public void setOnConfirm(Runnable onConfirm) {
|
||||
this.onConfirm = onConfirm;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.sound.SoundManager;
|
||||
import net.minecraft.client.texture.TextureManager;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.LiteralText;
|
||||
@@ -33,6 +34,10 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
|
||||
|
||||
private boolean halfSize = false;
|
||||
|
||||
private boolean disableClickSound = false;
|
||||
|
||||
private boolean disableBackground = false;
|
||||
|
||||
public IconButton(final int x, final int y, PressAction onPress) {
|
||||
super(x, y, 16, 16, LiteralText.EMPTY, onPress);
|
||||
}
|
||||
@@ -42,6 +47,13 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
|
||||
this.active = vis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playDownSound(SoundManager soundManager) {
|
||||
if (!disableClickSound) {
|
||||
super.playDownSound(soundManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderButton(MatrixStack matrices, final int mouseX, final int mouseY, float partial) {
|
||||
|
||||
@@ -72,7 +84,9 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
|
||||
final int uv_y = iconIndex / 16;
|
||||
final int uv_x = iconIndex - uv_y * 16;
|
||||
|
||||
drawTexture(matrices, 0, 0, 256 - 16, 256 - 16, 16, 16);
|
||||
if (!disableBackground) {
|
||||
drawTexture(matrices, 0, 0, 256 - 16, 256 - 16, 16, 16);
|
||||
}
|
||||
drawTexture(matrices, 0, 0, uv_x * 16, uv_y * 16, 16, 16);
|
||||
RenderSystem.popMatrix();
|
||||
} else {
|
||||
@@ -85,12 +99,18 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
|
||||
final int uv_y = iconIndex / 16;
|
||||
final int uv_x = iconIndex - uv_y * 16;
|
||||
|
||||
drawTexture(matrices, this.x, this.y, 256 - 16, 256 - 16, 16, 16);
|
||||
if (!disableBackground) {
|
||||
drawTexture(matrices, this.x, this.y, 256 - 16, 256 - 16, 16, 16);
|
||||
}
|
||||
drawTexture(matrices, this.x, this.y, uv_x * 16, uv_y * 16, 16, 16);
|
||||
}
|
||||
RenderSystem.enableDepthTest();
|
||||
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
if (isHovered()) {
|
||||
renderToolTip(matrices, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
protected abstract int getIconIndex();
|
||||
@@ -133,4 +153,20 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
|
||||
this.halfSize = halfSize;
|
||||
}
|
||||
|
||||
public boolean isDisableClickSound() {
|
||||
return disableClickSound;
|
||||
}
|
||||
|
||||
public void setDisableClickSound(boolean disableClickSound) {
|
||||
this.disableClickSound = disableClickSound;
|
||||
}
|
||||
|
||||
public boolean isDisableBackground() {
|
||||
return disableBackground;
|
||||
}
|
||||
|
||||
public void setDisableBackground(boolean disableBackground) {
|
||||
this.disableBackground = disableBackground;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* 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.client.gui.widgets;
|
||||
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import net.minecraft.text.LiteralText;
|
||||
|
||||
public class NumberBox extends TextFieldWidget {
|
||||
|
||||
private final LongConsumer changeListener;
|
||||
|
||||
private long lastValue;
|
||||
|
||||
private long minValue = 0;
|
||||
|
||||
private long maxValue;
|
||||
|
||||
public NumberBox(final TextRenderer fontRenderer, final int x, final int y, final int width, final int height,
|
||||
final Class<?> type, LongConsumer changeListener) {
|
||||
super(fontRenderer, x, y, width, height, new LiteralText("0"));
|
||||
this.setText("0");
|
||||
setChangedListener(this::handleTextChanged);
|
||||
this.lastValue = 0;
|
||||
this.changeListener = changeListener;
|
||||
if (type == int.class || type == Integer.class) {
|
||||
maxValue = Integer.MAX_VALUE;
|
||||
} else {
|
||||
maxValue = Long.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This method MUST NOT BE NAMED like a method in the superclass because
|
||||
// of a ForgeGradle remapping bug
|
||||
// since it'll not remap this method's name, but it will remap the name of the
|
||||
// method reference above.
|
||||
private void handleTextChanged(String text) {
|
||||
if (text.isEmpty()) {
|
||||
setText("0"); // Will call onTextChanged recursively
|
||||
return;
|
||||
}
|
||||
|
||||
boolean canBeNegative = canBeNegative();
|
||||
if (canBeNegative && text.equals("-")) {
|
||||
// Allow this as a special case to make typing in a negative number easier
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sanitized = new StringBuilder(text);
|
||||
boolean encounteredNonZero = false;
|
||||
for (int i = 0; i < sanitized.length(); i++) {
|
||||
char ch = sanitized.charAt(i);
|
||||
if (canBeNegative && i == 0 && ch == '-') {
|
||||
continue; // Allow leading minus sign
|
||||
}
|
||||
if (ch >= '1' && ch <= '9') {
|
||||
encounteredNonZero = true;
|
||||
continue;
|
||||
}
|
||||
if (ch != '0' || !encounteredNonZero) {
|
||||
sanitized.deleteCharAt(i--);
|
||||
}
|
||||
}
|
||||
if (sanitized.length() == 0) {
|
||||
sanitized.append('0');
|
||||
}
|
||||
String sanitizedStr = sanitized.toString();
|
||||
if (!sanitizedStr.equals(text)) {
|
||||
setText(sanitizedStr); // Will call onTextChanged recursively
|
||||
return;
|
||||
}
|
||||
if (getValue() < minValue) {
|
||||
setText(String.valueOf(minValue)); // Will call onTextChanged recursively
|
||||
return;
|
||||
}
|
||||
if (getValue() > maxValue) {
|
||||
setText(String.valueOf(maxValue)); // Will call onTextChanged recursively
|
||||
return;
|
||||
}
|
||||
|
||||
reportChange();
|
||||
}
|
||||
|
||||
private void reportChange() {
|
||||
long value = getValue();
|
||||
if (value != lastValue) {
|
||||
lastValue = value;
|
||||
changeListener.accept(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue(long value, boolean skipNotify) {
|
||||
// This check avoid changing the cursor position needlessly
|
||||
if (value == this.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (skipNotify) {
|
||||
lastValue = value;
|
||||
}
|
||||
setText(String.valueOf(value));
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
if (getText().equals("-") && canBeNegative()) {
|
||||
return lastValue; // Allow this as a special case to type in a negative number more easily
|
||||
}
|
||||
|
||||
return Long.parseLong(getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {
|
||||
if (super.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Swallow key presses for numbers because they would otherwise trigger the
|
||||
// hotbar swapping unintentionally
|
||||
return isFocused() && p_keyPressed_1_ >= '0' && p_keyPressed_1_ <= '9';
|
||||
}
|
||||
|
||||
public void setMinValue(long minValue) {
|
||||
this.minValue = minValue;
|
||||
}
|
||||
|
||||
public long getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
private boolean canBeNegative() {
|
||||
return minValue < 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
@@ -47,6 +48,7 @@ import appeng.api.config.StorageFilter;
|
||||
import appeng.api.config.TerminalStyle;
|
||||
import appeng.api.config.ViewItems;
|
||||
import appeng.api.config.YesNo;
|
||||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.core.localization.ButtonToolTips;
|
||||
import appeng.util.EnumCycler;
|
||||
|
||||
@@ -89,9 +91,11 @@ public class SettingToggleButton<T extends Enum<T>> extends IconButton {
|
||||
registerApp(16 * 7, Settings.CONDENSER_OUTPUT, CondenserOutput.TRASH, ButtonToolTips.CondenserOutput,
|
||||
ButtonToolTips.Trash);
|
||||
registerApp(16 * 7 + 1, Settings.CONDENSER_OUTPUT, CondenserOutput.MATTER_BALLS,
|
||||
ButtonToolTips.CondenserOutput, ButtonToolTips.MatterBalls);
|
||||
ButtonToolTips.CondenserOutput,
|
||||
ButtonToolTips.MatterBalls.text(CondenserOutput.MATTER_BALLS.requiredPower));
|
||||
registerApp(16 * 7 + 2, Settings.CONDENSER_OUTPUT, CondenserOutput.SINGULARITY,
|
||||
ButtonToolTips.CondenserOutput, ButtonToolTips.Singularity);
|
||||
ButtonToolTips.CondenserOutput,
|
||||
ButtonToolTips.Singularity.text(CondenserOutput.SINGULARITY.requiredPower));
|
||||
|
||||
registerApp(16 * 9 + 1, Settings.ACCESS, AccessRestriction.READ, ButtonToolTips.IOMode,
|
||||
ButtonToolTips.Read);
|
||||
@@ -230,7 +234,13 @@ public class SettingToggleButton<T extends Enum<T>> extends IconButton {
|
||||
}
|
||||
|
||||
private void triggerPress() {
|
||||
boolean backwards = MinecraftClient.getInstance().mouse.wasRightButtonClicked();
|
||||
boolean backwards = false;
|
||||
// This isn't great, but we don't get any information about right-clicks
|
||||
// otherwise
|
||||
Screen currentScreen = MinecraftClient.getInstance().currentScreen;
|
||||
if (currentScreen instanceof AEBaseScreen) {
|
||||
backwards = ((AEBaseScreen<?>) currentScreen).isHandlingRightClick();
|
||||
}
|
||||
onPress.handle(this, backwards);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import net.minecraft.util.Identifier;
|
||||
public class TabButton extends ButtonWidget implements ITooltip {
|
||||
public static final Identifier TEXTURE_STATES = new Identifier("appliedenergistics2", "textures/guis/states.png");
|
||||
private final ItemRenderer itemRenderer;
|
||||
private int hideEdge = 0;
|
||||
private boolean hideEdge;
|
||||
private int myIcon = -1;
|
||||
private ItemStack myItem;
|
||||
|
||||
@@ -69,14 +69,17 @@ public class TabButton extends ButtonWidget implements ITooltip {
|
||||
|
||||
RenderSystem.enableAlphaTest();
|
||||
|
||||
int uv_x = (this.hideEdge > 0 ? 11 : 13);
|
||||
// Selects the button border from the sprite-sheet, where each type occupies a
|
||||
// 2x2 slot
|
||||
int uv_x = 8 + (this.hideEdge ? 0 : 2);
|
||||
int uv_y = 12 + (this.isFocused() ? 2 : 0);
|
||||
|
||||
final int offsetX = this.hideEdge > 0 ? 1 : 0;
|
||||
final int offsetX = this.hideEdge ? 1 : 0;
|
||||
|
||||
drawTexture(matrices, this.x, this.y, uv_x * 16, 0, 25, 22);
|
||||
drawTexture(matrices, this.x, this.y, uv_x * 16, uv_y * 16, 25, 22);
|
||||
|
||||
if (this.myIcon >= 0) {
|
||||
final int uv_y = this.myIcon / 16;
|
||||
uv_y = this.myIcon / 16;
|
||||
uv_x = this.myIcon - uv_y * 16;
|
||||
|
||||
drawTexture(matrices, offsetX + this.x + 3, this.y + 3, uv_x * 16, uv_y * 16, 16, 16);
|
||||
@@ -122,11 +125,11 @@ public class TabButton extends ButtonWidget implements ITooltip {
|
||||
return this.visible;
|
||||
}
|
||||
|
||||
public int getHideEdge() {
|
||||
public boolean getHideEdge() {
|
||||
return this.hideEdge;
|
||||
}
|
||||
|
||||
public void setHideEdge(final int hideEdge) {
|
||||
public void setHideEdge(final boolean hideEdge) {
|
||||
this.hideEdge = hideEdge;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package appeng.client.gui.widgets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
/**
|
||||
* Displays a small icon that shows validation errors for some input control.
|
||||
*/
|
||||
public class ValidationIcon extends IconButton {
|
||||
|
||||
private final List<Text> tooltip = new ArrayList<>();
|
||||
|
||||
private boolean valid;
|
||||
|
||||
public ValidationIcon(int x, int y) {
|
||||
super(x, y, btn -> {
|
||||
});
|
||||
setDisableBackground(true);
|
||||
setDisableClickSound(true);
|
||||
setHalfSize(true);
|
||||
}
|
||||
|
||||
public void setValid(boolean valid) {
|
||||
setVisibility(!valid);
|
||||
if (valid) {
|
||||
this.tooltip.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTooltip(List<Text> lines) {
|
||||
this.tooltip.clear();
|
||||
this.tooltip.addAll(lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIconIndex() {
|
||||
return 16 * 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeFocus(boolean flag) {
|
||||
return false; // Cannot focus this element
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderToolTip(MatrixStack matrices, int mouseX, int mouseY) {
|
||||
if (this.tooltip.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
Screen screen = client.currentScreen;
|
||||
if (screen == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
screen.renderTooltip(matrices, this.tooltip, x, y);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -66,10 +66,8 @@ import appeng.container.slot.PlayerInvSlot;
|
||||
import appeng.core.AELog;
|
||||
import appeng.core.Api;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
import appeng.core.sync.packets.InventoryActionPacket;
|
||||
import appeng.core.sync.packets.TargetItemStackPacket;
|
||||
import appeng.helpers.ICustomNameObject;
|
||||
import appeng.helpers.InventoryAction;
|
||||
import appeng.me.helpers.PlayerSource;
|
||||
import appeng.mixins.ScreenHandlerListeners;
|
||||
@@ -88,11 +86,9 @@ public abstract class AEBaseContainer extends ScreenHandler {
|
||||
private final IGuiItemObject obj;
|
||||
private final HashMap<Integer, SyncData> syncData = new HashMap<>();
|
||||
private boolean isContainerValid = true;
|
||||
private String customName;
|
||||
private ContainerLocator locator;
|
||||
private IMEInventoryHandler<IAEItemStack> cellInv;
|
||||
private IEnergySource powerSrc;
|
||||
private boolean sentCustomName;
|
||||
private int ticksSinceCheck = 900;
|
||||
private IAEItemStack clientRequestedTargetItem = null;
|
||||
|
||||
@@ -317,9 +313,7 @@ public abstract class AEBaseContainer extends ScreenHandler {
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
this.sendCustomName();
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
if (this.tileEntity != null
|
||||
&& this.tileEntity.getWorld().getBlockEntity(this.tileEntity.getPos()) != this.tileEntity) {
|
||||
this.setValidContainer(false);
|
||||
@@ -886,43 +880,6 @@ public abstract class AEBaseContainer extends ScreenHandler {
|
||||
this.sendContentUpdates();
|
||||
}
|
||||
|
||||
private void sendCustomName() {
|
||||
// FIXME: Trash this, this is handled by NamedContainerProvider now
|
||||
if (!this.sentCustomName) {
|
||||
this.sentCustomName = true;
|
||||
if (Platform.isServer()) {
|
||||
ICustomNameObject name = null;
|
||||
|
||||
if (this.part instanceof ICustomNameObject) {
|
||||
name = (ICustomNameObject) this.part;
|
||||
}
|
||||
|
||||
if (this.tileEntity instanceof ICustomNameObject) {
|
||||
name = (ICustomNameObject) this.tileEntity;
|
||||
}
|
||||
|
||||
if (this.obj instanceof ICustomNameObject) {
|
||||
name = (ICustomNameObject) this.obj;
|
||||
}
|
||||
|
||||
if (this instanceof ICustomNameObject) {
|
||||
name = (ICustomNameObject) this;
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
if (name.hasCustomInventoryName()) {
|
||||
this.setCustomName(name.getCustomInventoryName().getString());
|
||||
}
|
||||
|
||||
if (this.getCustomName() != null) {
|
||||
NetworkHandler.instance().sendTo(new ConfigValuePacket("CustomName", this.getCustomName()),
|
||||
(ServerPlayerEntity) this.getPlayerInventory().player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void swapSlotContents(final int slotA, final int slotB) {
|
||||
final Slot a = this.getSlot(slotA);
|
||||
final Slot b = this.getSlot(slotB);
|
||||
@@ -1012,14 +969,6 @@ public abstract class AEBaseContainer extends ScreenHandler {
|
||||
this.cellInv = cellInv;
|
||||
}
|
||||
|
||||
public String getCustomName() {
|
||||
return this.customName;
|
||||
}
|
||||
|
||||
public void setCustomName(final String customName) {
|
||||
this.customName = customName;
|
||||
}
|
||||
|
||||
public PlayerInventory getPlayerInventory() {
|
||||
return this.invPlayer;
|
||||
}
|
||||
@@ -1048,4 +997,18 @@ public abstract class AEBaseContainer extends ScreenHandler {
|
||||
this.powerSrc = powerSrc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this container instance lives on the client.
|
||||
*/
|
||||
protected boolean isClient() {
|
||||
return invPlayer.player.getEntityWorld().isClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this container instance lives on the server.
|
||||
*/
|
||||
protected boolean isServer() {
|
||||
return !isClient();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ public class SyncData {
|
||||
NetworkHandler.instance().sendTo(new ConfigValuePacket("SyncDat." + this.channel, json),
|
||||
(ServerPlayerEntity) o);
|
||||
}
|
||||
this.clientVersion = val;
|
||||
return;
|
||||
}
|
||||
|
||||
// Types other than Text must be non-null
|
||||
|
||||
@@ -50,7 +50,6 @@ import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.core.Api;
|
||||
import appeng.tile.misc.CellWorkbenchBlockEntity;
|
||||
import appeng.util.EnumCycler;
|
||||
import appeng.util.Platform;
|
||||
import appeng.util.helpers.ItemHandlerUtil;
|
||||
import appeng.util.inv.WrapperSupplierItemHandler;
|
||||
import appeng.util.iterators.NullIterator;
|
||||
@@ -143,7 +142,7 @@ public class CellWorkbenchContainer extends UpgradeableContainer {
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
final ItemStack is = this.workBench.getInventoryByName("cell").getInvStack(0);
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
for (final ScreenHandlerListener listener : this.getListeners()) {
|
||||
if (this.prevStack != is) {
|
||||
// if the bars changed an item was probably made, so just send shit!
|
||||
|
||||
@@ -34,7 +34,6 @@ import appeng.container.interfaces.IProgressProvider;
|
||||
import appeng.container.slot.OutputSlot;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.tile.misc.CondenserBlockEntity;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class CondenserContainer extends AEBaseContainer implements IProgressProvider {
|
||||
|
||||
@@ -76,13 +75,13 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
final double maxStorage = this.condenser.getStorage();
|
||||
final double requiredEnergy = this.condenser.getRequiredPower();
|
||||
|
||||
this.requiredEnergy = requiredEnergy == 0 ? (int) maxStorage : (int) Math.min(requiredEnergy, maxStorage);
|
||||
this.storedPower = (int) this.condenser.getStoredPower();
|
||||
this.setOutput((CondenserOutput) this.condenser.getConfigManager().getSetting(Settings.CONDENSER_OUTPUT));
|
||||
this.output = (CondenserOutput) this.condenser.getConfigManager().getSetting(Settings.CONDENSER_OUTPUT);
|
||||
}
|
||||
|
||||
super.sendContentUpdates();
|
||||
@@ -102,7 +101,4 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv
|
||||
return this.output;
|
||||
}
|
||||
|
||||
private void setOutput(final CondenserOutput output) {
|
||||
this.output = output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package appeng.container.implementations;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
|
||||
@@ -12,9 +14,7 @@ import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
import appeng.api.features.IWirelessTermHandler;
|
||||
@@ -44,6 +44,8 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
|
||||
private final SecurityPermissions requiredPermission;
|
||||
|
||||
private Function<I, Text> containerTitleStrategy = this::getDefaultContainerTitle;
|
||||
|
||||
public ContainerHelper(ContainerFactory<C, I> factory, Class<I> interfaceClass) {
|
||||
this(factory, interfaceClass, null);
|
||||
}
|
||||
@@ -55,19 +57,47 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a custom strategy for obtaining a custom container name.
|
||||
* <p>
|
||||
* The stratgy should return {@link LiteralText#EMPTY} if there's no custom
|
||||
* name.
|
||||
*/
|
||||
public ContainerHelper<C, I> withContainerTitle(Function<I, Text> containerTitleStrategy) {
|
||||
this.containerTitleStrategy = containerTitleStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a container that is based around a single block entity. The tile
|
||||
* entity's position is encoded in the packet buffer.
|
||||
*/
|
||||
public C fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf packetBuf) {
|
||||
return fromNetwork(windowId, inv, packetBuf, (accessObj, container, buffer) -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #open}, but allows or additional data to be read from the
|
||||
* packet, and passed onto the container.
|
||||
*/
|
||||
public C fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf packetBuf,
|
||||
InitialDataDeserializer<C, I> initialDataDeserializer) {
|
||||
I host = getHostFromLocator(inv.player, ContainerLocator.read(packetBuf));
|
||||
if (host != null) {
|
||||
return factory.create(windowId, inv, host);
|
||||
C container = factory.create(windowId, inv, host);
|
||||
initialDataDeserializer.deserializeInitialData(host, container, packetBuf);
|
||||
return container;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean open(PlayerEntity player, ContainerLocator locator) {
|
||||
return open(player, locator, (accessObj, buffer) -> {
|
||||
});
|
||||
}
|
||||
|
||||
public boolean open(PlayerEntity player, ContainerLocator locator, InitialDataSerializer<I> initialDataSerializer) {
|
||||
if (!(player instanceof ServerPlayerEntity)) {
|
||||
// Cannot open containers on the client or for non-players
|
||||
// FIXME logging?
|
||||
@@ -84,9 +114,9 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
return false;
|
||||
}
|
||||
|
||||
Text title = findContainerTitle(player.world, locator, accessInterface);
|
||||
Text title = containerTitleStrategy.apply(accessInterface);
|
||||
|
||||
player.openHandledScreen(new HandlerFactory(locator, title, accessInterface));
|
||||
player.openHandledScreen(new HandlerFactory(locator, title, accessInterface, initialDataSerializer));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -99,15 +129,20 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
|
||||
private final Text title;
|
||||
|
||||
public HandlerFactory(ContainerLocator locator, Text title, I accessInterface) {
|
||||
private final InitialDataSerializer<I> initialDataSerializer;
|
||||
|
||||
public HandlerFactory(ContainerLocator locator, Text title, I accessInterface,
|
||||
InitialDataSerializer<I> initialDataSerializer) {
|
||||
this.locator = locator;
|
||||
this.title = title;
|
||||
this.accessInterface = accessInterface;
|
||||
this.initialDataSerializer = initialDataSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) {
|
||||
locator.write(buf);
|
||||
initialDataSerializer.serializeInitialData(accessInterface, buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,28 +162,6 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
|
||||
}
|
||||
|
||||
private Text findContainerTitle(World world, ContainerLocator locator, I accessInterface) {
|
||||
|
||||
if (accessInterface instanceof ICustomNameObject) {
|
||||
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
|
||||
if (customNameObject.hasCustomInventoryName()) {
|
||||
return customNameObject.getCustomInventoryName();
|
||||
}
|
||||
}
|
||||
|
||||
// Use block name at position
|
||||
// FIXME: this is not right, we'd need to check the part's item stack, or custom
|
||||
// naming interface impl
|
||||
// FIXME: Should move this up, because at this point, it's hard to know where
|
||||
// the terminal host came from (part or tile)
|
||||
if (locator.hasBlockPos()) {
|
||||
return new TranslatableText(world.getBlockState(locator.getBlockPos()).getBlock().getTranslationKey());
|
||||
}
|
||||
|
||||
return new LiteralText("Unknown");
|
||||
|
||||
}
|
||||
|
||||
private I getHostFromLocator(PlayerEntity player, ContainerLocator locator) {
|
||||
if (locator.hasItemIndex()) {
|
||||
return getHostFromPlayerInventory(player, locator);
|
||||
@@ -225,6 +238,24 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
C create(int windowId, PlayerInventory playerInv, I accessObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy used to serialize initial data for opening the container on the
|
||||
* client-side into the packet that is sent to the client.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InitialDataSerializer<I> {
|
||||
void serializeInitialData(I host, PacketByteBuf buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy used to deserialize initial data for opening the container on the
|
||||
* client-side from the packet received by the server.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InitialDataDeserializer<C, I> {
|
||||
void deserializeInitialData(I host, C container, PacketByteBuf buffer);
|
||||
}
|
||||
|
||||
private boolean checkPermission(PlayerEntity player, Object accessInterface) {
|
||||
|
||||
if (requiredPermission != null) {
|
||||
@@ -235,4 +266,15 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
|
||||
|
||||
}
|
||||
|
||||
private Text getDefaultContainerTitle(I accessInterface) {
|
||||
if (accessInterface instanceof ICustomNameObject) {
|
||||
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
|
||||
if (customNameObject.hasCustomInventoryName()) {
|
||||
return customNameObject.getCustomInventoryName();
|
||||
}
|
||||
}
|
||||
|
||||
return LiteralText.EMPTY;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,14 +19,8 @@
|
||||
package appeng.container.implementations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
@@ -66,9 +60,8 @@ import appeng.me.helpers.PlayerSource;
|
||||
import appeng.parts.reporting.CraftingTerminalPart;
|
||||
import appeng.parts.reporting.PatternTerminalPart;
|
||||
import appeng.parts.reporting.TerminalPart;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class CraftConfirmContainer extends AEBaseContainer {
|
||||
public class CraftConfirmContainer extends AEBaseContainer implements CraftingCPUCyclingContainer {
|
||||
|
||||
public static ScreenHandlerType<CraftConfirmContainer> TYPE;
|
||||
|
||||
@@ -83,98 +76,51 @@ public class CraftConfirmContainer extends AEBaseContainer {
|
||||
return helper.open(player, locator);
|
||||
}
|
||||
|
||||
private final ArrayList<CraftingCPURecord> cpus = new ArrayList<>();
|
||||
private final CraftingCPUCycler cpuCycler;
|
||||
|
||||
private ICraftingCPU selectedCpu;
|
||||
|
||||
private Future<ICraftingJob> job;
|
||||
private ICraftingJob result;
|
||||
@GuiSync(0)
|
||||
public long bytesUsed;
|
||||
@GuiSync(1)
|
||||
public long cpuBytesAvail;
|
||||
@GuiSync(2)
|
||||
public int cpuCoProcessors;
|
||||
@GuiSync(3)
|
||||
public boolean autoStart = false;
|
||||
@GuiSync(4)
|
||||
public boolean simulation = true;
|
||||
@GuiSync(5)
|
||||
public int selectedCpu = -1;
|
||||
|
||||
// Indicates whether any CPUs are available
|
||||
@GuiSync(6)
|
||||
public boolean noCPU = true;
|
||||
|
||||
// Properties of the currently selected crafting CPU, this can be null
|
||||
// if no CPUs are available, or if an automatic one is selected
|
||||
@GuiSync(1)
|
||||
public long cpuBytesAvail;
|
||||
@GuiSync(2)
|
||||
public int cpuCoProcessors;
|
||||
@GuiSync(7)
|
||||
public Text myName;
|
||||
public Text cpuName;
|
||||
|
||||
public CraftConfirmContainer(int id, PlayerInventory ip, ITerminalHost te) {
|
||||
super(TYPE, id, ip, te);
|
||||
this.cpuCycler = new CraftingCPUCycler(this::cpuMatches, this::onCPUSelectionChanged);
|
||||
// A player can select no crafting CPU to use a suitable one automatically
|
||||
this.cpuCycler.setAllowNoSelection(true);
|
||||
}
|
||||
|
||||
public void cycleCpu(final boolean next) {
|
||||
if (next) {
|
||||
this.setSelectedCpu(this.getSelectedCpu() + 1);
|
||||
} else {
|
||||
this.setSelectedCpu(this.getSelectedCpu() - 1);
|
||||
}
|
||||
|
||||
if (this.getSelectedCpu() < -1) {
|
||||
this.setSelectedCpu(this.cpus.size() - 1);
|
||||
} else if (this.getSelectedCpu() >= this.cpus.size()) {
|
||||
this.setSelectedCpu(-1);
|
||||
}
|
||||
|
||||
if (this.getSelectedCpu() == -1) {
|
||||
this.setCpuAvailableBytes(0);
|
||||
this.setCpuCoProcessors(0);
|
||||
this.setName(null);
|
||||
} else {
|
||||
CraftingCPURecord cpu = this.cpus.get(this.getSelectedCpu());
|
||||
this.setName(cpu.getName());
|
||||
this.setCpuAvailableBytes(cpu.getSize());
|
||||
this.setCpuCoProcessors(cpu.getProcessors());
|
||||
}
|
||||
@Override
|
||||
public void cycleSelectedCPU(final boolean next) {
|
||||
this.cpuCycler.cycleCpu(next);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isClient()) {
|
||||
if (isClient()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ICraftingGrid cc = this.getGrid().getCache(ICraftingGrid.class);
|
||||
final ImmutableSet<ICraftingCPU> cpuSet = cc.getCpus();
|
||||
|
||||
int matches = 0;
|
||||
boolean changed = false;
|
||||
for (final ICraftingCPU c : cpuSet) {
|
||||
boolean found = false;
|
||||
for (final CraftingCPURecord ccr : this.cpus) {
|
||||
if (ccr.getCpu() == c) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean matched = this.cpuMatches(c);
|
||||
|
||||
if (matched) {
|
||||
matches++;
|
||||
}
|
||||
|
||||
if (found == !matched) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed || this.cpus.size() != matches) {
|
||||
this.cpus.clear();
|
||||
for (final ICraftingCPU c : cpuSet) {
|
||||
if (this.cpuMatches(c)) {
|
||||
this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c));
|
||||
}
|
||||
}
|
||||
|
||||
this.sendCPUs();
|
||||
}
|
||||
|
||||
this.setNoCPU(this.cpus.isEmpty());
|
||||
this.cpuCycler.detectAndSendChanges(this.getGrid());
|
||||
|
||||
super.sendContentUpdates();
|
||||
|
||||
@@ -277,22 +223,6 @@ public class CraftConfirmContainer extends AEBaseContainer {
|
||||
return c.getAvailableStorage() >= this.getUsedBytes() && !c.isBusy();
|
||||
}
|
||||
|
||||
private void sendCPUs() {
|
||||
Collections.sort(this.cpus);
|
||||
|
||||
if (this.getSelectedCpu() >= this.cpus.size()) {
|
||||
this.setSelectedCpu(-1);
|
||||
this.setCpuAvailableBytes(0);
|
||||
this.setCpuCoProcessors(0);
|
||||
this.setName(null);
|
||||
} else if (this.getSelectedCpu() != -1) {
|
||||
CraftingCPURecord cpu = this.cpus.get(this.getSelectedCpu());
|
||||
this.setName(cpu.getName());
|
||||
this.setCpuAvailableBytes(cpu.getSize());
|
||||
this.setCpuCoProcessors(cpu.getProcessors());
|
||||
}
|
||||
}
|
||||
|
||||
public void startJob() {
|
||||
ScreenHandlerType<?> originalGui = null;
|
||||
|
||||
@@ -315,9 +245,7 @@ public class CraftConfirmContainer extends AEBaseContainer {
|
||||
|
||||
if (this.result != null && !this.isSimulation()) {
|
||||
final ICraftingGrid cc = this.getGrid().getCache(ICraftingGrid.class);
|
||||
final ICraftingLink g = cc.submitJob(this.result, null,
|
||||
this.getSelectedCpu() == -1 ? null : this.cpus.get(this.getSelectedCpu()).getCpu(), true,
|
||||
this.getActionSrc());
|
||||
final ICraftingLink g = cc.submitJob(this.result, null, this.selectedCpu, true, this.getActionSrc());
|
||||
this.setAutoStart(false);
|
||||
if (g != null && originalGui != null && this.getLocator() != null) {
|
||||
ContainerOpener.openContainer(originalGui, getPlayerInventory().player, getLocator());
|
||||
@@ -347,6 +275,22 @@ public class CraftConfirmContainer extends AEBaseContainer {
|
||||
}
|
||||
}
|
||||
|
||||
private void onCPUSelectionChanged(CraftingCPURecord cpuRecord, boolean cpusAvailable) {
|
||||
noCPU = !cpusAvailable;
|
||||
|
||||
if (cpuRecord == null) {
|
||||
cpuBytesAvail = 0;
|
||||
cpuCoProcessors = 0;
|
||||
cpuName = null;
|
||||
selectedCpu = null;
|
||||
} else {
|
||||
cpuBytesAvail = cpuRecord.getSize();
|
||||
cpuCoProcessors = cpuRecord.getProcessors();
|
||||
cpuName = cpuRecord.getName();
|
||||
selectedCpu = cpuRecord.getCpu();
|
||||
}
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return this.getPlayerInv().player.world;
|
||||
}
|
||||
@@ -371,42 +315,18 @@ public class CraftConfirmContainer extends AEBaseContainer {
|
||||
return this.cpuBytesAvail;
|
||||
}
|
||||
|
||||
private void setCpuAvailableBytes(final long cpuBytesAvail) {
|
||||
this.cpuBytesAvail = cpuBytesAvail;
|
||||
}
|
||||
|
||||
public int getCpuCoProcessors() {
|
||||
return this.cpuCoProcessors;
|
||||
}
|
||||
|
||||
private void setCpuCoProcessors(final int cpuCoProcessors) {
|
||||
this.cpuCoProcessors = cpuCoProcessors;
|
||||
}
|
||||
|
||||
public int getSelectedCpu() {
|
||||
return this.selectedCpu;
|
||||
}
|
||||
|
||||
private void setSelectedCpu(final int selectedCpu) {
|
||||
this.selectedCpu = selectedCpu;
|
||||
}
|
||||
|
||||
public Text getName() {
|
||||
return this.myName;
|
||||
}
|
||||
|
||||
private void setName(@Nullable final Text myName) {
|
||||
this.myName = myName;
|
||||
return this.cpuName;
|
||||
}
|
||||
|
||||
public boolean hasNoCPU() {
|
||||
return this.noCPU;
|
||||
}
|
||||
|
||||
private void setNoCPU(final boolean noCPU) {
|
||||
this.noCPU = noCPU;
|
||||
}
|
||||
|
||||
public boolean isSimulation() {
|
||||
return this.simulation;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.screen.ScreenHandlerListener;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.LiteralText;
|
||||
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
import appeng.api.networking.IGrid;
|
||||
@@ -46,24 +46,28 @@ import appeng.core.Api;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
import appeng.core.sync.packets.MEInventoryUpdatePacket;
|
||||
import appeng.helpers.ICustomNameObject;
|
||||
import appeng.me.cluster.implementations.CraftingCPUCluster;
|
||||
import appeng.tile.crafting.CraftingBlockEntity;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class CraftingCPUContainer extends AEBaseContainer
|
||||
implements IMEMonitorHandlerReceiver<IAEItemStack>, ICustomNameObject {
|
||||
public class CraftingCPUContainer extends AEBaseContainer implements IMEMonitorHandlerReceiver<IAEItemStack> {
|
||||
|
||||
public static ScreenHandlerType<CraftingCPUContainer> TYPE;
|
||||
|
||||
private static final ContainerHelper<CraftingCPUContainer, CraftingBlockEntity> helper = new ContainerHelper<>(
|
||||
CraftingCPUContainer::new, CraftingBlockEntity.class, SecurityPermissions.CRAFT);
|
||||
CraftingCPUContainer::new, CraftingBlockEntity.class, SecurityPermissions.CRAFT)
|
||||
.withContainerTitle(craftingTileEntity -> {
|
||||
// Use the cluster's custom name instead of the right-clicked block entities one
|
||||
CraftingCPUCluster cluster = craftingTileEntity.getCluster();
|
||||
if (cluster != null && cluster.getName() != null) {
|
||||
return cluster.getName();
|
||||
}
|
||||
return LiteralText.EMPTY;
|
||||
});
|
||||
|
||||
private final IItemList<IAEItemStack> list = Api.instance().storage().getStorageChannel(IItemStorageChannel.class)
|
||||
.createList();
|
||||
private IGrid network;
|
||||
private final IGrid network;
|
||||
private CraftingCPUCluster monitor = null;
|
||||
private Text cpuName = null;
|
||||
|
||||
@GuiSync(0)
|
||||
public long eta = -1;
|
||||
@@ -77,14 +81,16 @@ public class CraftingCPUContainer extends AEBaseContainer
|
||||
final IActionHost host = (IActionHost) (te instanceof IActionHost ? te : null);
|
||||
|
||||
if (host != null && host.getActionableNode() != null) {
|
||||
this.setNetwork(host.getActionableNode().getGrid());
|
||||
this.network = host.getActionableNode().getGrid();
|
||||
} else {
|
||||
this.network = null;
|
||||
}
|
||||
|
||||
if (te instanceof CraftingBlockEntity) {
|
||||
this.setCPU(((CraftingBlockEntity) te).getCluster());
|
||||
}
|
||||
|
||||
if (this.getNetwork() == null && Platform.isServer()) {
|
||||
if (this.getNetwork() == null && isServer()) {
|
||||
this.setValidContainer(false);
|
||||
}
|
||||
}
|
||||
@@ -114,15 +120,13 @@ public class CraftingCPUContainer extends AEBaseContainer
|
||||
}
|
||||
|
||||
if (c instanceof CraftingCPUCluster) {
|
||||
this.cpuName = c.getName();
|
||||
this.setMonitor((CraftingCPUCluster) c);
|
||||
this.monitor = (CraftingCPUCluster) c;
|
||||
this.list.resetStatus();
|
||||
this.getMonitor().getListOfItem(this.list, CraftingItemList.ALL);
|
||||
this.getMonitor().addListener(this, null);
|
||||
this.setEstimatedTime(0);
|
||||
} else {
|
||||
this.setMonitor(null);
|
||||
this.cpuName = null;
|
||||
this.monitor = null;
|
||||
this.setEstimatedTime(-1);
|
||||
}
|
||||
}
|
||||
@@ -153,7 +157,7 @@ public class CraftingCPUContainer extends AEBaseContainer
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isServer() && this.getMonitor() != null && !this.list.isEmpty()) {
|
||||
if (isServer() && this.getMonitor() != null && !this.list.isEmpty()) {
|
||||
try {
|
||||
if (this.getEstimatedTime() >= 0) {
|
||||
final long elapsedTime = this.getMonitor().getElapsedTime();
|
||||
@@ -218,16 +222,6 @@ public class CraftingCPUContainer extends AEBaseContainer
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getCustomInventoryName() {
|
||||
return this.cpuName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCustomInventoryName() {
|
||||
return this.cpuName != null;
|
||||
}
|
||||
|
||||
public long getEstimatedTime() {
|
||||
return this.eta;
|
||||
}
|
||||
@@ -240,15 +234,8 @@ public class CraftingCPUContainer extends AEBaseContainer
|
||||
return this.monitor;
|
||||
}
|
||||
|
||||
private void setMonitor(final CraftingCPUCluster monitor) {
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
IGrid getNetwork() {
|
||||
return this.network;
|
||||
}
|
||||
|
||||
private void setNetwork(final IGrid network) {
|
||||
this.network = network;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package appeng.container.implementations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import net.minecraft.text.LiteralText;
|
||||
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.crafting.ICraftingCPU;
|
||||
import appeng.api.networking.crafting.ICraftingGrid;
|
||||
|
||||
/**
|
||||
* Utility class for dialogs that can cycle through crafting CPUs
|
||||
*/
|
||||
class CraftingCPUCycler {
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ChangeListener {
|
||||
void onChange(CraftingCPURecord selectedCpu, boolean cpusAvailable);
|
||||
}
|
||||
|
||||
private final Predicate<ICraftingCPU> cpuFilter;
|
||||
private final ChangeListener changeListener;
|
||||
private final List<CraftingCPURecord> cpus = new ArrayList<>();
|
||||
private int selectedCpu = -1;
|
||||
private boolean initialDataSent = false;
|
||||
private boolean allowNoSelection;
|
||||
|
||||
public CraftingCPUCycler(Predicate<ICraftingCPU> cpuFilter, ChangeListener changeListener) {
|
||||
this.cpuFilter = cpuFilter;
|
||||
this.changeListener = changeListener;
|
||||
}
|
||||
|
||||
public void detectAndSendChanges(IGrid network) {
|
||||
final ICraftingGrid cc = network.getCache(ICraftingGrid.class);
|
||||
final ImmutableSet<ICraftingCPU> cpuSet = cc.getCpus();
|
||||
|
||||
int matches = 0;
|
||||
boolean changed = !initialDataSent;
|
||||
initialDataSent = true;
|
||||
for (final ICraftingCPU c : cpuSet) {
|
||||
boolean found = false;
|
||||
for (final CraftingCPURecord ccr : this.cpus) {
|
||||
if (ccr.getCpu() == c) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean matched = this.cpuFilter.test(c);
|
||||
|
||||
if (matched) {
|
||||
matches++;
|
||||
}
|
||||
|
||||
if (found == !matched) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed || this.cpus.size() != matches) {
|
||||
this.cpus.clear();
|
||||
for (final ICraftingCPU c : cpuSet) {
|
||||
if (this.cpuFilter.test(c)) {
|
||||
this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort and assign numeric IDs in case they have no names
|
||||
Collections.sort(this.cpus);
|
||||
for (int i = 0; i < this.cpus.size(); i++) {
|
||||
CraftingCPURecord cpu = cpus.get(i);
|
||||
if (cpu.getName() == null) {
|
||||
cpu.setName(new LiteralText("#" + (i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
this.notifyListener();
|
||||
}
|
||||
}
|
||||
|
||||
public void cycleCpu(final boolean next) {
|
||||
if (next) {
|
||||
this.selectedCpu++;
|
||||
} else {
|
||||
this.selectedCpu--;
|
||||
}
|
||||
|
||||
// If "no CPU" is a valid selection, then -1 is the first potential item
|
||||
int lowerLimit = this.allowNoSelection ? -1 : 0;
|
||||
|
||||
if (this.selectedCpu < lowerLimit) {
|
||||
this.selectedCpu = this.cpus.size() - 1;
|
||||
} else if (this.selectedCpu >= this.cpus.size()) {
|
||||
this.selectedCpu = lowerLimit;
|
||||
}
|
||||
|
||||
this.notifyListener();
|
||||
}
|
||||
|
||||
public boolean isAllowNoSelection() {
|
||||
return allowNoSelection;
|
||||
}
|
||||
|
||||
public void setAllowNoSelection(boolean allowNoSelection) {
|
||||
this.allowNoSelection = allowNoSelection;
|
||||
}
|
||||
|
||||
private void notifyListener() {
|
||||
if (this.selectedCpu >= this.cpus.size()) {
|
||||
this.selectedCpu = -1;
|
||||
}
|
||||
|
||||
// Force the selected CPU to the first available CPU unless no-selection is
|
||||
// explicitly allowed
|
||||
if (!this.allowNoSelection && this.selectedCpu == -1 && !this.cpus.isEmpty()) {
|
||||
this.selectedCpu = 0;
|
||||
}
|
||||
|
||||
if (this.selectedCpu != -1) {
|
||||
this.changeListener.onChange(this.cpus.get(this.selectedCpu), true);
|
||||
} else {
|
||||
this.changeListener.onChange(null, !this.cpus.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package appeng.container.implementations;
|
||||
|
||||
/**
|
||||
* Implemented on screens that show information about a crafting CPU and allow
|
||||
* the CPU to be cycled. Is triggered by receiving a config value packet with
|
||||
* name <code>Terminal.Cpu</code>.
|
||||
*/
|
||||
public interface CraftingCPUCyclingContainer {
|
||||
|
||||
void cycleSelectedCPU(boolean forward);
|
||||
|
||||
}
|
||||
@@ -25,16 +25,16 @@ import net.minecraft.text.Text;
|
||||
import appeng.api.networking.crafting.ICraftingCPU;
|
||||
|
||||
public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
|
||||
private final Text myName;
|
||||
private final ICraftingCPU cpu;
|
||||
private final long size;
|
||||
private final int processors;
|
||||
private Text name;
|
||||
|
||||
public CraftingCPURecord(final long size, final int coProcessors, final ICraftingCPU server) {
|
||||
this.size = size;
|
||||
this.processors = coProcessors;
|
||||
this.cpu = server;
|
||||
this.myName = server.getName();
|
||||
this.name = server.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,10 +50,6 @@ public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
|
||||
return this.cpu;
|
||||
}
|
||||
|
||||
Text getName() {
|
||||
return this.myName;
|
||||
}
|
||||
|
||||
int getProcessors() {
|
||||
return this.processors;
|
||||
}
|
||||
@@ -61,4 +57,13 @@ public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
|
||||
long getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public Text getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(Text name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,6 @@
|
||||
|
||||
package appeng.container.implementations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
@@ -31,14 +25,13 @@ import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.crafting.ICraftingCPU;
|
||||
import appeng.api.networking.crafting.ICraftingGrid;
|
||||
import appeng.api.storage.ITerminalHost;
|
||||
import appeng.container.ContainerLocator;
|
||||
import appeng.container.guisync.GuiSync;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class CraftingStatusContainer extends CraftingCPUContainer {
|
||||
public class CraftingStatusContainer extends CraftingCPUContainer implements CraftingCPUCyclingContainer {
|
||||
|
||||
public static ScreenHandlerType<CraftingStatusContainer> TYPE;
|
||||
|
||||
@@ -53,13 +46,13 @@ public class CraftingStatusContainer extends CraftingCPUContainer {
|
||||
return helper.open(player, locator);
|
||||
}
|
||||
|
||||
private final List<CraftingCPURecord> cpus = new ArrayList<>();
|
||||
@GuiSync(5)
|
||||
public int selectedCpu = -1;
|
||||
private final CraftingCPUCycler cpuCycler = new CraftingCPUCycler(this::cpuMatches, this::onCPUSelectionChanged);
|
||||
|
||||
@GuiSync(6)
|
||||
public boolean noCPU = true;
|
||||
|
||||
@GuiSync(7)
|
||||
public Text myName;
|
||||
public Text cpuName;
|
||||
|
||||
public CraftingStatusContainer(int id, final PlayerInventory ip, final ITerminalHost te) {
|
||||
super(TYPE, id, ip, te);
|
||||
@@ -67,43 +60,9 @@ public class CraftingStatusContainer extends CraftingCPUContainer {
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isServer() && this.getNetwork() != null) {
|
||||
final ICraftingGrid cc = this.getNetwork().getCache(ICraftingGrid.class);
|
||||
final ImmutableSet<ICraftingCPU> cpuSet = cc.getCpus();
|
||||
|
||||
int matches = 0;
|
||||
boolean changed = false;
|
||||
for (final ICraftingCPU c : cpuSet) {
|
||||
boolean found = false;
|
||||
for (final CraftingCPURecord ccr : this.cpus) {
|
||||
if (ccr.getCpu() == c) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean matched = this.cpuMatches(c);
|
||||
|
||||
if (matched) {
|
||||
matches++;
|
||||
}
|
||||
|
||||
if (found == !matched) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed || this.cpus.size() != matches) {
|
||||
this.cpus.clear();
|
||||
for (final ICraftingCPU c : cpuSet) {
|
||||
if (this.cpuMatches(c)) {
|
||||
this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c));
|
||||
}
|
||||
}
|
||||
|
||||
this.sendCPUs();
|
||||
}
|
||||
|
||||
this.noCPU = this.cpus.isEmpty();
|
||||
IGrid network = this.getNetwork();
|
||||
if (isServer() && network != null) {
|
||||
cpuCycler.detectAndSendChanges(network);
|
||||
}
|
||||
|
||||
super.sendContentUpdates();
|
||||
@@ -113,52 +72,20 @@ public class CraftingStatusContainer extends CraftingCPUContainer {
|
||||
return c.isBusy();
|
||||
}
|
||||
|
||||
private void sendCPUs() {
|
||||
Collections.sort(this.cpus);
|
||||
|
||||
if (this.selectedCpu >= this.cpus.size()) {
|
||||
this.selectedCpu = -1;
|
||||
this.myName = null;
|
||||
} else if (this.selectedCpu != -1) {
|
||||
this.myName = this.cpus.get(this.selectedCpu).getName();
|
||||
}
|
||||
|
||||
if (this.selectedCpu == -1 && this.cpus.size() > 0) {
|
||||
this.selectedCpu = 0;
|
||||
}
|
||||
|
||||
if (this.selectedCpu != -1) {
|
||||
if (this.cpus.get(this.selectedCpu).getCpu() != this.getMonitor()) {
|
||||
this.setCPU(this.cpus.get(this.selectedCpu).getCpu());
|
||||
}
|
||||
private void onCPUSelectionChanged(CraftingCPURecord cpuRecord, boolean cpusAvailable) {
|
||||
noCPU = !cpusAvailable;
|
||||
if (cpuRecord == null) {
|
||||
cpuName = null;
|
||||
setCPU(null);
|
||||
} else {
|
||||
this.setCPU(null);
|
||||
cpuName = cpuRecord.getName();
|
||||
setCPU(cpuRecord.getCpu());
|
||||
}
|
||||
}
|
||||
|
||||
public void cycleCpu(final boolean next) {
|
||||
if (next) {
|
||||
this.selectedCpu++;
|
||||
} else {
|
||||
this.selectedCpu--;
|
||||
}
|
||||
|
||||
if (this.selectedCpu < -1) {
|
||||
this.selectedCpu = this.cpus.size() - 1;
|
||||
} else if (this.selectedCpu >= this.cpus.size()) {
|
||||
this.selectedCpu = -1;
|
||||
}
|
||||
|
||||
if (this.selectedCpu == -1 && this.cpus.size() > 0) {
|
||||
this.selectedCpu = 0;
|
||||
}
|
||||
|
||||
if (this.selectedCpu == -1) {
|
||||
this.myName = null;
|
||||
this.setCPU(null);
|
||||
} else {
|
||||
this.myName = this.cpus.get(this.selectedCpu).getName();
|
||||
this.setCPU(this.cpus.get(this.selectedCpu).getCpu());
|
||||
}
|
||||
@Override
|
||||
public void cycleSelectedCPU(boolean forward) {
|
||||
this.cpuCycler.cycleCpu(forward);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import appeng.container.slot.FakeTypeOnlySlot;
|
||||
import appeng.container.slot.OptionalTypeOnlyFakeSlot;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.parts.automation.FormationPlanePart;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class FormationPlaneContainer extends UpgradeableContainer {
|
||||
|
||||
@@ -108,7 +107,7 @@ public class FormationPlaneContainer extends UpgradeableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
|
||||
this.setPlaceMode((YesNo) this.getUpgradeable().getConfigManager().getSetting(Settings.PLACE_BLOCK));
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import appeng.container.guisync.GuiSync;
|
||||
import appeng.container.slot.OutputSlot;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.tile.storage.IOPortBlockEntity;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class IOPortContainer extends UpgradeableContainer {
|
||||
|
||||
@@ -112,7 +111,7 @@ public class IOPortContainer extends UpgradeableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.setOperationMode(
|
||||
(OperationMode) this.getUpgradeable().getConfigManager().getSetting(Settings.OPERATION_MODE));
|
||||
this.setFullMode(
|
||||
|
||||
@@ -36,7 +36,6 @@ import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.core.Api;
|
||||
import appeng.tile.misc.InscriberBlockEntity;
|
||||
import appeng.tile.misc.InscriberRecipes;
|
||||
import appeng.util.Platform;
|
||||
|
||||
/**
|
||||
* @author AlgorithmX2
|
||||
@@ -120,7 +119,7 @@ public class InscriberContainer extends UpgradeableContainer implements IProgres
|
||||
public void sendContentUpdates() {
|
||||
this.standardDetectAndSendChanges();
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.maxProcessingTime = this.ti.getMaxProcessingTime();
|
||||
this.processingTime = this.ti.getProcessingTime();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ import appeng.parts.reporting.InterfaceTerminalPart;
|
||||
import appeng.tile.inventory.AppEngInternalInventory;
|
||||
import appeng.tile.misc.InterfaceBlockEntity;
|
||||
import appeng.util.InventoryAdaptor;
|
||||
import appeng.util.Platform;
|
||||
import appeng.util.helpers.ItemHandlerUtil;
|
||||
import appeng.util.inv.AdaptorFixedInv;
|
||||
import appeng.util.inv.WrapperCursorItemHandler;
|
||||
@@ -88,7 +87,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer {
|
||||
public InterfaceTerminalContainer(int id, final PlayerInventory ip, final InterfaceTerminalPart anchor) {
|
||||
super(TYPE, id, ip, anchor);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.grid = anchor.getActionableNode().getGrid();
|
||||
}
|
||||
|
||||
@@ -97,7 +96,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer {
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isClient()) {
|
||||
if (isClient()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
package appeng.container.implementations;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
@@ -33,13 +31,13 @@ import appeng.api.config.RedstoneMode;
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
import appeng.api.config.Settings;
|
||||
import appeng.api.config.YesNo;
|
||||
import appeng.client.gui.implementations.NumberEntryWidget;
|
||||
import appeng.container.ContainerLocator;
|
||||
import appeng.container.guisync.GuiSync;
|
||||
import appeng.container.slot.FakeTypeOnlySlot;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
import appeng.parts.automation.LevelEmitterPart;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class LevelEmitterContainer extends UpgradeableContainer {
|
||||
|
||||
@@ -49,38 +47,46 @@ public class LevelEmitterContainer extends UpgradeableContainer {
|
||||
LevelEmitterContainer::new, LevelEmitterPart.class, SecurityPermissions.BUILD);
|
||||
|
||||
public static LevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) {
|
||||
return helper.fromNetwork(windowId, inv, buf);
|
||||
return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> {
|
||||
container.reportingValue = buffer.readVarLong();
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean open(PlayerEntity player, ContainerLocator locator) {
|
||||
return helper.open(player, locator);
|
||||
return helper.open(player, locator, (host, buffer) -> {
|
||||
buffer.writeVarLong(host.getReportingValue());
|
||||
});
|
||||
}
|
||||
|
||||
private final LevelEmitterPart lvlEmitter;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
private NumberEntryWidget textField;
|
||||
@GuiSync(2)
|
||||
public LevelType lvType;
|
||||
@GuiSync(3)
|
||||
public long EmitterValue = -1;
|
||||
@GuiSync(4)
|
||||
public YesNo cmType;
|
||||
|
||||
// Only synced once on container-open, and only used on client
|
||||
private long reportingValue;
|
||||
|
||||
public LevelEmitterContainer(int id, final PlayerInventory ip, final LevelEmitterPart te) {
|
||||
super(TYPE, id, ip, te);
|
||||
this.lvlEmitter = te;
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public void setTextField(final NumberEntryWidget level) {
|
||||
this.textField = level;
|
||||
this.textField.setValue(this.EmitterValue);
|
||||
public long getReportingValue() {
|
||||
return reportingValue;
|
||||
}
|
||||
|
||||
public void setLevel(final long l, final PlayerEntity player) {
|
||||
this.lvlEmitter.setReportingValue(l);
|
||||
this.EmitterValue = l;
|
||||
public void setReportingValue(long reportingValue) {
|
||||
if (isClient()) {
|
||||
if (reportingValue != this.reportingValue) {
|
||||
this.reportingValue = reportingValue;
|
||||
NetworkHandler.instance()
|
||||
.sendToServer(new ConfigValuePacket("LevelEmitter.Value", String.valueOf(reportingValue)));
|
||||
}
|
||||
} else {
|
||||
this.lvlEmitter.setReportingValue(reportingValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,7 +122,6 @@ public class LevelEmitterContainer extends UpgradeableContainer {
|
||||
|
||||
@Override
|
||||
public int availableUpgrades() {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -124,8 +129,7 @@ public class LevelEmitterContainer extends UpgradeableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
this.EmitterValue = this.lvlEmitter.getReportingValue();
|
||||
if (isServer()) {
|
||||
this.setCraftingMode(
|
||||
(YesNo) this.getUpgradeable().getConfigManager().getSetting(Settings.CRAFT_VIA_REDSTONE));
|
||||
this.setLevelMode((LevelType) this.getUpgradeable().getConfigManager().getSetting(Settings.LEVEL_TYPE));
|
||||
@@ -137,15 +141,6 @@ public class LevelEmitterContainer extends UpgradeableContainer {
|
||||
this.standardDetectAndSendChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(final String field, final Object oldValue, final Object newValue) {
|
||||
if (field.equals("EmitterValue")) {
|
||||
if (this.textField != null) {
|
||||
this.textField.setValue(this.EmitterValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public YesNo getCraftingMode() {
|
||||
return this.cmType;
|
||||
@@ -163,4 +158,5 @@ public class LevelEmitterContainer extends UpgradeableContainer {
|
||||
private void setLevelMode(final LevelType lvType) {
|
||||
this.lvType = lvType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ import appeng.api.implementations.tiles.IViewCellStorage;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.IGridHost;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.networking.crafting.ICraftingCPU;
|
||||
import appeng.api.networking.crafting.ICraftingGrid;
|
||||
import appeng.api.networking.energy.IEnergyGrid;
|
||||
import appeng.api.networking.energy.IEnergySource;
|
||||
import appeng.api.networking.security.IActionHost;
|
||||
@@ -73,7 +75,6 @@ import appeng.core.sync.packets.MEInventoryUpdatePacket;
|
||||
import appeng.me.helpers.ChannelPowerSrc;
|
||||
import appeng.util.ConfigManager;
|
||||
import appeng.util.IConfigManagerHost;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class MEMonitorableContainer extends AEBaseContainer
|
||||
implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver<IAEItemStack> {
|
||||
@@ -101,6 +102,13 @@ public class MEMonitorableContainer extends AEBaseContainer
|
||||
public boolean canAccessViewCells = false;
|
||||
@GuiSync(98)
|
||||
public boolean hasPower = false;
|
||||
/**
|
||||
* The number of active crafting jobs in the network. -1 means unknown and will
|
||||
* hide the label on the screen.
|
||||
*/
|
||||
@GuiSync(100)
|
||||
public int activeCraftingJobs = -1;
|
||||
|
||||
private IConfigManagerHost gui;
|
||||
private IConfigManager serverCM;
|
||||
private IGridNode networkNode;
|
||||
@@ -122,7 +130,7 @@ public class MEMonitorableContainer extends AEBaseContainer
|
||||
this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL);
|
||||
this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.serverCM = monitorable.getConfigManager();
|
||||
|
||||
this.monitor = monitorable
|
||||
@@ -183,12 +191,14 @@ public class MEMonitorableContainer extends AEBaseContainer
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
if (this.monitor != this.host
|
||||
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class))) {
|
||||
this.setValidContainer(false);
|
||||
}
|
||||
|
||||
this.updateActiveCraftingJobs();
|
||||
|
||||
for (final Settings set : this.serverCM.getSettings()) {
|
||||
final Enum<?> sideLocal = this.serverCM.getSetting(set);
|
||||
final Enum<?> sideRemote = this.clientCM.getSetting(set);
|
||||
@@ -286,8 +296,38 @@ public class MEMonitorableContainer extends AEBaseContainer
|
||||
this.queueInventory(c);
|
||||
}
|
||||
|
||||
private void updateActiveCraftingJobs() {
|
||||
IGridNode hostNode = networkNode;
|
||||
if (hostNode == null) {
|
||||
// Wireless terminals do not directly expose the target grid (even though they
|
||||
// have one)
|
||||
if (host instanceof IActionHost) {
|
||||
hostNode = ((IActionHost) host).getActionableNode();
|
||||
}
|
||||
}
|
||||
IGrid grid = null;
|
||||
if (hostNode != null) {
|
||||
grid = hostNode.getGrid();
|
||||
}
|
||||
|
||||
if (grid == null) {
|
||||
// No grid to query crafting jobs from
|
||||
this.activeCraftingJobs = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
int activeJobs = 0;
|
||||
ICraftingGrid craftingGrid = grid.getCache(ICraftingGrid.class);
|
||||
for (ICraftingCPU cpus : craftingGrid.getCpus()) {
|
||||
if (cpus.isBusy()) {
|
||||
activeJobs++;
|
||||
}
|
||||
}
|
||||
this.activeCraftingJobs = activeJobs;
|
||||
}
|
||||
|
||||
private void queueInventory(final ScreenHandlerListener c) {
|
||||
if (Platform.isServer() && c instanceof PlayerEntity && this.monitor != null) {
|
||||
if (isServer() && c instanceof PlayerEntity && this.monitor != null) {
|
||||
try {
|
||||
MEInventoryUpdatePacket piu = new MEInventoryUpdatePacket();
|
||||
final IItemList<IAEItemStack> monitorCache = this.monitor.getStorageList();
|
||||
@@ -356,7 +396,7 @@ public class MEMonitorableContainer extends AEBaseContainer
|
||||
|
||||
@Override
|
||||
public IConfigManager getConfigManager() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
return this.serverCM;
|
||||
}
|
||||
return this.clientCM;
|
||||
|
||||
@@ -42,7 +42,6 @@ import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.core.Api;
|
||||
import appeng.items.misc.EncodedPatternItem;
|
||||
import appeng.tile.crafting.MolecularAssemblerBlockEntity;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class MolecularAssemblerContainer extends UpgradeableContainer implements IProgressProvider {
|
||||
|
||||
@@ -148,7 +147,7 @@ public class MolecularAssemblerContainer extends UpgradeableContainer implements
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.setRedStoneMode(
|
||||
(RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_CONTROLLED));
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import appeng.container.guisync.GuiSync;
|
||||
import appeng.core.Api;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.MEInventoryUpdatePacket;
|
||||
import appeng.util.Platform;
|
||||
import appeng.util.item.AEItemStack;
|
||||
|
||||
public class NetworkStatusContainer extends AEBaseContainer {
|
||||
@@ -83,7 +82,7 @@ public class NetworkStatusContainer extends AEBaseContainer {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.network == null && Platform.isServer()) {
|
||||
if (this.network == null && isServer()) {
|
||||
this.setValidContainer(false);
|
||||
}
|
||||
}
|
||||
@@ -100,7 +99,7 @@ public class NetworkStatusContainer extends AEBaseContainer {
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
this.delay++;
|
||||
if (Platform.isServer() && this.delay > 15 && this.network != null) {
|
||||
if (isServer() && this.delay > 15 && this.network != null) {
|
||||
this.delay = 0;
|
||||
|
||||
final IEnergyGrid eg = this.network.getCache(IEnergyGrid.class);
|
||||
|
||||
@@ -297,9 +297,9 @@ public class PatternTermContainer extends MEMonitorableContainer
|
||||
@Override
|
||||
public boolean isSlotEnabled(final int idx) {
|
||||
if (idx == 1) {
|
||||
return Platform.isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode();
|
||||
return isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode();
|
||||
} else if (idx == 2) {
|
||||
return Platform.isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode();
|
||||
return isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -401,7 +401,7 @@ public class PatternTermContainer extends MEMonitorableContainer
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
super.sendContentUpdates();
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
if (this.isCraftingMode() != this.getPatternTerminal().isCraftingRecipe()) {
|
||||
this.setCraftingMode(this.getPatternTerminal().isCraftingRecipe());
|
||||
this.updateOrderOfOutputSlots();
|
||||
@@ -423,7 +423,7 @@ public class PatternTermContainer extends MEMonitorableContainer
|
||||
|
||||
@Override
|
||||
public void onSlotChange(final Slot s) {
|
||||
if (s == this.patternSlotOUT && Platform.isServer()) {
|
||||
if (s == this.patternSlotOUT && isServer()) {
|
||||
for (final ScreenHandlerListener listener : this.getListeners()) {
|
||||
for (int i = 0; i < this.slots.size(); i++) {
|
||||
Slot slot = this.slots.get(i);
|
||||
@@ -438,7 +438,7 @@ public class PatternTermContainer extends MEMonitorableContainer
|
||||
this.sendContentUpdates();
|
||||
}
|
||||
|
||||
if (s == this.craftSlot && Platform.isClient()) {
|
||||
if (s == this.craftSlot && isClient()) {
|
||||
this.getAndUpdateOutput();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
package appeng.container.implementations;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
@@ -28,12 +26,11 @@ import net.minecraft.screen.ScreenHandlerType;
|
||||
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
import appeng.api.parts.IPart;
|
||||
import appeng.client.gui.implementations.NumberEntryWidget;
|
||||
import appeng.container.AEBaseContainer;
|
||||
import appeng.container.ContainerLocator;
|
||||
import appeng.container.guisync.GuiSync;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
import appeng.helpers.IPriorityHost;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class PriorityContainer extends AEBaseContainer {
|
||||
|
||||
@@ -43,59 +40,53 @@ public class PriorityContainer extends AEBaseContainer {
|
||||
PriorityContainer::new, IPriorityHost.class, SecurityPermissions.BUILD);
|
||||
|
||||
public static PriorityContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) {
|
||||
return helper.fromNetwork(windowId, inv, buf);
|
||||
return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> {
|
||||
container.priorityValue = buffer.readVarInt();
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean open(PlayerEntity player, ContainerLocator locator) {
|
||||
return helper.open(player, locator);
|
||||
return helper.open(player, locator, (host, buffer) -> buffer.writeVarInt(host.getPriority()));
|
||||
}
|
||||
|
||||
private final IPriorityHost priHost;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
private NumberEntryWidget textField;
|
||||
@GuiSync(2)
|
||||
public long PriorityValue = -1;
|
||||
private int priorityValue;
|
||||
|
||||
public PriorityContainer(int id, final PlayerInventory ip, final IPriorityHost te) {
|
||||
super(TYPE, id, ip, (BlockEntity) (te instanceof BlockEntity ? te : null),
|
||||
(IPart) (te instanceof IPart ? te : null));
|
||||
this.priHost = te;
|
||||
this.priorityValue = te.getPriority();
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public void setTextField(final NumberEntryWidget level) {
|
||||
this.textField = level;
|
||||
this.textField.setValue(this.PriorityValue, true);
|
||||
}
|
||||
|
||||
public void setPriority(final int newValue, final PlayerEntity player) {
|
||||
this.priHost.setPriority(newValue);
|
||||
this.PriorityValue = newValue;
|
||||
public void setPriority(final int newValue) {
|
||||
if (newValue != priorityValue) {
|
||||
if (isClient()) {
|
||||
// If for whatever reason the client enters the value first, do not update based
|
||||
// on incoming server data
|
||||
this.priorityValue = newValue;
|
||||
NetworkHandler.instance()
|
||||
.sendToServer(new ConfigValuePacket("PriorityHost.Priority", String.valueOf(newValue)));
|
||||
} else {
|
||||
this.priHost.setPriority(newValue);
|
||||
this.priorityValue = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
super.sendContentUpdates();
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
this.PriorityValue = this.priHost.getPriority();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(final String field, final Object oldValue, final Object newValue) {
|
||||
if (field.equals("PriorityValue")) {
|
||||
if (this.textField != null) {
|
||||
this.textField.setValue(this.PriorityValue, true);
|
||||
}
|
||||
}
|
||||
|
||||
super.onUpdate(field, oldValue, newValue);
|
||||
public int getPriorityValue() {
|
||||
return priorityValue;
|
||||
}
|
||||
|
||||
public IPriorityHost getPriorityHost() {
|
||||
return this.priHost;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ import appeng.core.Api;
|
||||
import appeng.items.contents.QuartzKnifeObj;
|
||||
import appeng.items.materials.MaterialItem;
|
||||
import appeng.tile.inventory.AppEngInternalInventory;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class QuartzKnifeContainer extends AEBaseContainer {
|
||||
|
||||
@@ -142,7 +141,7 @@ public class QuartzKnifeContainer extends AEBaseContainer {
|
||||
}
|
||||
|
||||
private void makePlate() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
if (!inSlot.getSlot(0).extract(1).isEmpty()) {
|
||||
final ItemStack item = QuartzKnifeContainer.this.toolInv.getItemStack();
|
||||
final ItemStack before = item.copy();
|
||||
|
||||
@@ -35,7 +35,6 @@ import appeng.container.guisync.GuiSync;
|
||||
import appeng.container.slot.OutputSlot;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.tile.spatial.SpatialIOPortBlockEntity;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class SpatialIOPortContainer extends AEBaseContainer {
|
||||
|
||||
@@ -65,7 +64,7 @@ public class SpatialIOPortContainer extends AEBaseContainer {
|
||||
public SpatialIOPortContainer(int id, final PlayerInventory ip, final SpatialIOPortBlockEntity spatialIOPort) {
|
||||
super(TYPE, id, ip, spatialIOPort, null);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.network = spatialIOPort.getGridNode(AEPartLocation.INTERNAL).getGrid();
|
||||
}
|
||||
|
||||
@@ -89,7 +88,7 @@ public class SpatialIOPortContainer extends AEBaseContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.delay++;
|
||||
if (this.delay > 15 && this.network != null) {
|
||||
this.delay = 0;
|
||||
|
||||
@@ -45,7 +45,6 @@ import appeng.container.slot.OptionalTypeOnlyFakeSlot;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.core.Api;
|
||||
import appeng.parts.misc.StorageBusPart;
|
||||
import appeng.util.Platform;
|
||||
import appeng.util.helpers.ItemHandlerUtil;
|
||||
import appeng.util.iterators.NullIterator;
|
||||
|
||||
@@ -125,7 +124,7 @@ public class StorageBusContainer extends UpgradeableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
|
||||
this.setReadWriteMode(
|
||||
(AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS));
|
||||
|
||||
@@ -52,7 +52,6 @@ import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.items.contents.NetworkToolViewer;
|
||||
import appeng.items.tools.NetworkToolItem;
|
||||
import appeng.parts.automation.ExportBusPart;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class UpgradeableContainer extends AEBaseContainer implements IOptionalSlotHost {
|
||||
|
||||
@@ -200,7 +199,7 @@ public class UpgradeableContainer extends AEBaseContainer implements IOptionalSl
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
final IConfigManager cm = this.getUpgradeable().getConfigManager();
|
||||
this.loadSettingsFromHost(cm);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import appeng.container.guisync.GuiSync;
|
||||
import appeng.container.interfaces.IProgressProvider;
|
||||
import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.tile.misc.VibrationChamberBlockEntity;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class VibrationChamberContainer extends AEBaseContainer implements IProgressProvider {
|
||||
|
||||
@@ -65,7 +64,7 @@ public class VibrationChamberContainer extends AEBaseContainer implements IProgr
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.remainingBurnTime = this.vibrationChamber.getMaxBurnTime() <= 0 ? 0
|
||||
: (int) (100.0 * this.vibrationChamber.getBurnTime() / this.vibrationChamber.getMaxBurnTime());
|
||||
this.burnSpeed = this.remainingBurnTime <= 0 ? 0 : this.vibrationChamber.getBurnSpeed();
|
||||
|
||||
@@ -28,7 +28,6 @@ import appeng.container.ContainerLocator;
|
||||
import appeng.core.AEConfig;
|
||||
import appeng.core.localization.PlayerMessages;
|
||||
import appeng.helpers.WirelessTerminalGuiObject;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class WirelessTermContainer extends MEPortableCellContainer {
|
||||
|
||||
@@ -57,7 +56,7 @@ public class WirelessTermContainer extends MEPortableCellContainer {
|
||||
super.sendContentUpdates();
|
||||
|
||||
if (!this.wirelessTerminalGUIObject.rangeCheck()) {
|
||||
if (Platform.isServer() && this.isValidContainer()) {
|
||||
if (isServer() && this.isValidContainer()) {
|
||||
this.getPlayerInv().player.sendSystemMessage(PlayerMessages.OutOfRange.get(), Util.NIL_UUID);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ import appeng.core.sync.network.INetworkInfo;
|
||||
public abstract class BasePacket {
|
||||
|
||||
/**
|
||||
* Sadly {@link PacketBuffer#readString()} gets inlined by Proguard which means
|
||||
* Sadly {@link PacketByteBuf#readString()} gets inlined by Proguard which means
|
||||
* it's not available on the Server. This field has the default string length
|
||||
* that is used for writeString, which then also should be used for readString
|
||||
* when it has no special length requirements.
|
||||
|
||||
@@ -37,7 +37,7 @@ import appeng.container.AEBaseContainer;
|
||||
import appeng.container.implementations.CellWorkbenchContainer;
|
||||
import appeng.container.implementations.CraftConfirmContainer;
|
||||
import appeng.container.implementations.CraftingCPUContainer;
|
||||
import appeng.container.implementations.CraftingStatusContainer;
|
||||
import appeng.container.implementations.CraftingCPUCyclingContainer;
|
||||
import appeng.container.implementations.LevelEmitterContainer;
|
||||
import appeng.container.implementations.NetworkToolContainer;
|
||||
import appeng.container.implementations.PatternTermContainer;
|
||||
@@ -97,12 +97,9 @@ public class ConfigValuePacket extends BasePacket {
|
||||
final ItemStack is = player.getStackInHand(hand);
|
||||
final IMouseWheelItem si = (IMouseWheelItem) is.getItem();
|
||||
si.onWheel(is, this.Value.equals("WheelUp"));
|
||||
} else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftingStatusContainer) {
|
||||
final CraftingStatusContainer qk = (CraftingStatusContainer) c;
|
||||
qk.cycleCpu(this.Value.equals("Next"));
|
||||
} else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftConfirmContainer) {
|
||||
final CraftConfirmContainer qk = (CraftConfirmContainer) c;
|
||||
qk.cycleCpu(this.Value.equals("Next"));
|
||||
} else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftingCPUCyclingContainer) {
|
||||
final CraftingCPUCyclingContainer qk = (CraftingCPUCyclingContainer) c;
|
||||
qk.cycleSelectedCPU(this.Value.equals("Next"));
|
||||
} else if (this.Name.equals("Terminal.Start") && c instanceof CraftConfirmContainer) {
|
||||
final CraftConfirmContainer qk = (CraftConfirmContainer) c;
|
||||
qk.startJob();
|
||||
@@ -117,13 +114,13 @@ public class ConfigValuePacket extends BasePacket {
|
||||
sc.toggleSetting(this.Value, player);
|
||||
} else if (this.Name.equals("PriorityHost.Priority") && c instanceof PriorityContainer) {
|
||||
final PriorityContainer pc = (PriorityContainer) c;
|
||||
pc.setPriority(Integer.parseInt(this.Value), player);
|
||||
pc.setPriority(Integer.parseInt(this.Value));
|
||||
} else if (this.Name.equals("LevelEmitter.Value") && c instanceof LevelEmitterContainer) {
|
||||
final LevelEmitterContainer lvc = (LevelEmitterContainer) c;
|
||||
lvc.setLevel(Long.parseLong(this.Value), player);
|
||||
lvc.setReportingValue(Long.parseLong(this.Value));
|
||||
} else if (this.Name.equals("FluidLevelEmitter.Value") && c instanceof FluidLevelEmitterContainer) {
|
||||
final FluidLevelEmitterContainer lvc = (FluidLevelEmitterContainer) c;
|
||||
lvc.setLevel(Long.parseLong(this.Value), player);
|
||||
lvc.setReportingValue(Long.parseLong(this.Value));
|
||||
} else if (this.Name.startsWith("PatternTerminal.") && c instanceof PatternTermContainer) {
|
||||
final PatternTermContainer cpt = (PatternTermContainer) c;
|
||||
if (this.Name.equals("PatternTerminal.CraftMode")) {
|
||||
@@ -191,9 +188,7 @@ public class ConfigValuePacket extends BasePacket {
|
||||
public void clientPacketData(final INetworkInfo network, final PlayerEntity player) {
|
||||
final ScreenHandler c = player.currentScreenHandler;
|
||||
|
||||
if (this.Name.equals("CustomName") && c instanceof AEBaseContainer) {
|
||||
((AEBaseContainer) c).setCustomName(this.Value);
|
||||
} else if (this.Name.startsWith("SyncDat.")) {
|
||||
if (this.Name.startsWith("SyncDat.")) {
|
||||
((AEBaseContainer) c).stringSync(Integer.parseInt(this.Name.substring(8)), this.Value);
|
||||
} else if (this.Name.equals("CraftingStatus") && this.Value.equals("Clear")) {
|
||||
final Screen gs = MinecraftClient.getInstance().currentScreen;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ import appeng.client.gui.implementations.NumberEntryWidget;
|
||||
import appeng.client.gui.implementations.UpgradeableScreen;
|
||||
import appeng.client.gui.widgets.ServerSettingToggleButton;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
import appeng.fluids.client.gui.widgets.FluidSlotWidget;
|
||||
import appeng.fluids.container.FluidLevelEmitterContainer;
|
||||
|
||||
@@ -28,17 +26,24 @@ public class FluidLevelEmitterScreen extends UpgradeableScreen<FluidLevelEmitter
|
||||
public void init() {
|
||||
super.init();
|
||||
|
||||
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_FLUID_VOLUME,
|
||||
this::onLevelChange);
|
||||
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_FLUID_VOLUME);
|
||||
this.level.setTextFieldBounds(25, 44, 75);
|
||||
handler.setTextField(this.level);
|
||||
this.level.addButtons(children::add, this::addButton);
|
||||
this.level.setValue(handler.getReportingValue());
|
||||
this.level.setOnChange(this::saveReportingValue);
|
||||
this.level.setOnConfirm(this::onClose);
|
||||
|
||||
this.changeFocus(true);
|
||||
|
||||
final int y = 40;
|
||||
final int x = 80 + 57;
|
||||
this.guiSlots.add(new FluidSlotWidget(this.handler.getFluidConfigInventory(), 0, 0, x, y));
|
||||
}
|
||||
|
||||
private void saveReportingValue() {
|
||||
this.level.getLongValue().ifPresent(handler::setReportingValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addButtons() {
|
||||
this.redstoneMode = new ServerSettingToggleButton<>(this.x - 18, this.y + 28, Settings.REDSTONE_EMITTER,
|
||||
@@ -79,8 +84,4 @@ public class FluidLevelEmitterScreen extends UpgradeableScreen<FluidLevelEmitter
|
||||
protected void handleButtonVisibility() {
|
||||
}
|
||||
|
||||
private void onLevelChange(long level) {
|
||||
NetworkHandler.instance().sendToServer(new ConfigValuePacket("FluidLevelEmitter.Value", String.valueOf(level)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import appeng.container.implementations.UpgradeableContainer;
|
||||
import appeng.fluids.helper.FluidSyncHelper;
|
||||
import appeng.fluids.util.AEFluidStack;
|
||||
import appeng.fluids.util.IAEFluidTank;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public abstract class FluidConfigurableContainer extends UpgradeableContainer implements IFluidSyncContainer {
|
||||
private FluidSyncHelper sync = null;
|
||||
@@ -76,7 +75,7 @@ public abstract class FluidConfigurableContainer extends UpgradeableContainer im
|
||||
|
||||
@Override
|
||||
protected void standardDetectAndSendChanges() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.getSyncHelper().sendDiff(this.getListeners());
|
||||
|
||||
// clear out config items that are no longer valid (eg capacity upgrade removed)
|
||||
|
||||
@@ -36,7 +36,6 @@ import appeng.fluids.helper.DualityFluidInterface;
|
||||
import appeng.fluids.helper.FluidSyncHelper;
|
||||
import appeng.fluids.helper.IFluidInterfaceHost;
|
||||
import appeng.fluids.util.IAEFluidTank;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class FluidInterfaceContainer extends FluidConfigurableContainer {
|
||||
|
||||
@@ -81,7 +80,7 @@ public class FluidInterfaceContainer extends FluidConfigurableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.tankSync.sendDiff(this.getListeners());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package appeng.fluids.container;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
@@ -10,13 +8,12 @@ import net.minecraft.screen.ScreenHandlerType;
|
||||
import appeng.api.config.RedstoneMode;
|
||||
import appeng.api.config.SecurityPermissions;
|
||||
import appeng.api.config.Settings;
|
||||
import appeng.client.gui.implementations.NumberEntryWidget;
|
||||
import appeng.container.ContainerLocator;
|
||||
import appeng.container.guisync.GuiSync;
|
||||
import appeng.container.implementations.ContainerHelper;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.ConfigValuePacket;
|
||||
import appeng.fluids.parts.FluidLevelEmitterPart;
|
||||
import appeng.fluids.util.IAEFluidTank;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
|
||||
public static ScreenHandlerType<FluidLevelEmitterContainer> TYPE;
|
||||
@@ -25,34 +22,41 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
|
||||
FluidLevelEmitterContainer::new, FluidLevelEmitterPart.class, SecurityPermissions.BUILD);
|
||||
|
||||
public static FluidLevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) {
|
||||
return helper.fromNetwork(windowId, inv, buf);
|
||||
return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> {
|
||||
container.reportingValue = buffer.readVarLong();
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean open(PlayerEntity player, ContainerLocator locator) {
|
||||
return helper.open(player, locator);
|
||||
return helper.open(player, locator, (host, buffer) -> {
|
||||
buffer.writeVarLong(host.getReportingValue());
|
||||
});
|
||||
}
|
||||
|
||||
private final FluidLevelEmitterPart lvlEmitter;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
private NumberEntryWidget textField;
|
||||
@GuiSync(3)
|
||||
public long EmitterValue = -1;
|
||||
// Only synced once on container-open, and only used on client
|
||||
private long reportingValue;
|
||||
|
||||
public FluidLevelEmitterContainer(int id, final PlayerInventory ip, final FluidLevelEmitterPart te) {
|
||||
super(TYPE, id, ip, te);
|
||||
this.lvlEmitter = te;
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public void setTextField(final NumberEntryWidget level) {
|
||||
this.textField = level;
|
||||
this.textField.setValue(this.EmitterValue);
|
||||
public long getReportingValue() {
|
||||
return reportingValue;
|
||||
}
|
||||
|
||||
public void setLevel(final long l, final PlayerEntity player) {
|
||||
this.lvlEmitter.setReportingValue(l);
|
||||
this.EmitterValue = l;
|
||||
public void setReportingValue(long reportingValue) {
|
||||
if (isClient()) {
|
||||
if (reportingValue != this.reportingValue) {
|
||||
this.reportingValue = reportingValue;
|
||||
NetworkHandler.instance()
|
||||
.sendToServer(new ConfigValuePacket("FluidLevelEmitter.Value", String.valueOf(reportingValue)));
|
||||
}
|
||||
} else {
|
||||
this.lvlEmitter.setReportingValue(reportingValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,8 +78,7 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
this.EmitterValue = this.lvlEmitter.getReportingValue();
|
||||
if (isServer()) {
|
||||
this.setRedStoneMode(
|
||||
(RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_EMITTER));
|
||||
}
|
||||
@@ -83,15 +86,6 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
|
||||
this.standardDetectAndSendChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(final String field, final Object oldValue, final Object newValue) {
|
||||
if (field.equals("EmitterValue")) {
|
||||
if (this.textField != null) {
|
||||
this.textField.setValue(this.EmitterValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAEFluidTank getFluidConfigInventory() {
|
||||
return this.lvlEmitter.getConfig();
|
||||
|
||||
@@ -44,7 +44,6 @@ import appeng.container.slot.RestrictedInputSlot;
|
||||
import appeng.core.Api;
|
||||
import appeng.fluids.parts.FluidStorageBusPart;
|
||||
import appeng.fluids.util.IAEFluidTank;
|
||||
import appeng.util.Platform;
|
||||
import appeng.util.iterators.NullIterator;
|
||||
|
||||
/**
|
||||
@@ -129,7 +128,7 @@ public class FluidStorageBusContainer extends FluidConfigurableContainer {
|
||||
public void sendContentUpdates() {
|
||||
this.verifyPermissions(SecurityPermissions.BUILD, false);
|
||||
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
|
||||
this.setReadWriteMode(
|
||||
(AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS));
|
||||
|
||||
@@ -124,7 +124,7 @@ public class FluidTerminalContainer extends AEBaseContainer
|
||||
this.clientCM.registerSetting(Settings.SORT_BY, SortOrder.NAME);
|
||||
this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING);
|
||||
this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL);
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
this.serverCM = terminal.getConfigManager();
|
||||
this.monitor = terminal
|
||||
.getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class));
|
||||
@@ -196,7 +196,7 @@ public class FluidTerminalContainer extends AEBaseContainer
|
||||
}
|
||||
|
||||
private void queueInventory(final ScreenHandlerListener c) {
|
||||
if (Platform.isServer() && c instanceof PlayerEntity && this.monitor != null) {
|
||||
if (isServer() && c instanceof PlayerEntity && this.monitor != null) {
|
||||
try {
|
||||
MEFluidInventoryUpdatePacket piu = new MEFluidInventoryUpdatePacket();
|
||||
final IItemList<IAEFluidStack> monitorCache = this.monitor.getStorageList();
|
||||
@@ -221,14 +221,14 @@ public class FluidTerminalContainer extends AEBaseContainer
|
||||
|
||||
@Override
|
||||
public IConfigManager getConfigManager() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
return this.serverCM;
|
||||
}
|
||||
return this.clientCM;
|
||||
}
|
||||
|
||||
public void setTargetStack(final IAEFluidStack stack) {
|
||||
if (Platform.isClient()) {
|
||||
if (isClient()) {
|
||||
if (stack == null && this.clientRequestedTargetFluid == null) {
|
||||
return;
|
||||
}
|
||||
@@ -251,7 +251,7 @@ public class FluidTerminalContainer extends AEBaseContainer
|
||||
|
||||
@Override
|
||||
public void sendContentUpdates() {
|
||||
if (Platform.isServer()) {
|
||||
if (isServer()) {
|
||||
if (this.monitor != this.terminal
|
||||
.getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class))) {
|
||||
this.setValidContainer(false);
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
|
||||
package appeng.fluids.helper;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Optional;
|
||||
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import alexiil.mc.lib.attributes.AttributeList;
|
||||
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
|
||||
import alexiil.mc.lib.attributes.fluid.amount.FluidAmount;
|
||||
import alexiil.mc.lib.attributes.item.FixedItemInv;
|
||||
|
||||
import appeng.api.config.Actionable;
|
||||
@@ -319,8 +320,8 @@ public class DualityFluidInterface
|
||||
toStore.setStackSize(-toStore.getStackSize());
|
||||
|
||||
// make sure strange things didn't happen...
|
||||
final FluidVolume canExtract = this.tanks.drain(slot, toStore.getFluidStack(), false);
|
||||
if (canExtract.isEmpty() || canExtract.getAmount() != toStore.getStackSize()) {
|
||||
final FluidAmount canExtract = this.tanks.drain(slot, toStore.getFluidStack(), false);
|
||||
if (canExtract.isZero() || canExtract.asLong(1000, RoundingMode.DOWN) != toStore.getStackSize()) {
|
||||
changed = true;
|
||||
} else {
|
||||
IAEFluidStack notStored = Platform.poweredInsert(src, dest, toStore, this.interfaceRequestSource);
|
||||
@@ -329,8 +330,8 @@ public class DualityFluidInterface
|
||||
if (toStore.getStackSize() > 0) {
|
||||
// extract items!
|
||||
changed = true;
|
||||
final FluidVolume removed = this.tanks.drain(slot, toStore.getFluidStack(), true);
|
||||
if (removed.isEmpty() || toStore.getStackSize() != removed.getAmount()) {
|
||||
final FluidAmount removed = this.tanks.drain(slot, toStore.getFluidStack(), true);
|
||||
if (removed.isZero() || toStore.getStackSize() != removed.asLong(1000, RoundingMode.DOWN)) {
|
||||
throw new IllegalStateException("bad attempt at managing tanks. ( drain )");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class FluidImportBusPart extends SharedFluidBusPart {
|
||||
|
||||
return TickRateModulation.IDLE;
|
||||
} catch (GridAccessException e) {
|
||||
e.printStackTrace();
|
||||
// skip
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@ import appeng.core.AELog;
|
||||
import appeng.util.Platform;
|
||||
|
||||
public class AEFluidInventory implements IAEFluidTank {
|
||||
|
||||
/**
|
||||
* While this may seem redundant, it helps since this class heavily mixes AE
|
||||
* fluids stacks, which use null to represent "nothing", and Minecraft's
|
||||
* FluidStack, which uses #isEmpty() to represent nothing.
|
||||
*/
|
||||
private static final IAEFluidStack EMPTY_AE_FLUIDSTACK = null;
|
||||
|
||||
private final IAEFluidStack[] fluids;
|
||||
private final IAEFluidInventory handler;
|
||||
private final int capacity;
|
||||
@@ -48,13 +56,13 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
public void setFluidInSlot(final int slot, final IAEFluidStack fluid) {
|
||||
if (slot >= 0 && slot < this.getSlots()) {
|
||||
if (Objects.equals(this.fluids[slot], fluid)) {
|
||||
if (fluid != null && fluid.getStackSize() != this.fluids[slot].getStackSize()) {
|
||||
if (fluid != EMPTY_AE_FLUIDSTACK && fluid.getStackSize() != this.fluids[slot].getStackSize()) {
|
||||
this.fluids[slot].setStackSize(Math.min(fluid.getStackSize(), this.capacity));
|
||||
this.onContentChanged(slot);
|
||||
}
|
||||
} else {
|
||||
if (fluid == null) {
|
||||
this.fluids[slot] = null;
|
||||
if (fluid == EMPTY_AE_FLUIDSTACK) {
|
||||
this.fluids[slot] = EMPTY_AE_FLUIDSTACK;
|
||||
} else {
|
||||
this.fluids[slot] = fluid.copy();
|
||||
this.fluids[slot].setStackSize(Math.min(fluid.getStackSize(), this.capacity));
|
||||
@@ -76,7 +84,7 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
if (slot >= 0 && slot < this.getSlots()) {
|
||||
return this.fluids[slot];
|
||||
}
|
||||
return null;
|
||||
return EMPTY_AE_FLUIDSTACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,7 +107,7 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
if (tank < 0 || tank >= fluids.length) {
|
||||
return FluidVolumeUtil.EMPTY;
|
||||
}
|
||||
return fluids[tank] == null ? FluidVolumeUtil.EMPTY : fluids[tank].getFluidStack();
|
||||
return fluids[tank] == EMPTY_AE_FLUIDSTACK ? FluidVolumeUtil.EMPTY : fluids[tank].getFluidStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,13 +127,13 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
|
||||
final IAEFluidStack fluid = this.fluids[slot];
|
||||
|
||||
if (fluid != null && !fluid.getFluidStack().equals(resource)) {
|
||||
if (fluid != EMPTY_AE_FLUIDSTACK && !fluid.getFluidStack().equals(resource)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amountToStore = this.capacity;
|
||||
|
||||
if (fluid != null) {
|
||||
if (fluid != EMPTY_AE_FLUIDSTACK) {
|
||||
amountToStore -= fluid.getStackSize();
|
||||
}
|
||||
|
||||
@@ -143,19 +151,19 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
return amountToStore;
|
||||
}
|
||||
|
||||
public FluidVolume drain(final int slot, final FluidVolume resource, final boolean doDrain) {
|
||||
public FluidAmount drain(final int slot, final FluidVolume resource, final boolean doDrain) {
|
||||
final IAEFluidStack fluid = this.fluids[slot];
|
||||
if (resource.isEmpty() || fluid == null || !fluid.getFluidStack().equals(resource)) {
|
||||
return null;
|
||||
if (resource.isEmpty() || fluid == EMPTY_AE_FLUIDSTACK || !fluid.getFluidStack().equals(resource)) {
|
||||
return FluidAmount.ZERO;
|
||||
}
|
||||
int toDrain = (int) resource.getAmount_F().asLong(1000, RoundingMode.DOWN);
|
||||
return this.drain(slot, toDrain, doDrain);
|
||||
}
|
||||
|
||||
public FluidVolume drain(final int slot, final int maxDrain, boolean doDrain) {
|
||||
public FluidAmount drain(final int slot, final int maxDrain, boolean doDrain) {
|
||||
final IAEFluidStack fluid = this.fluids[slot];
|
||||
if (fluid == null || maxDrain <= 0) {
|
||||
return null;
|
||||
if (fluid == EMPTY_AE_FLUIDSTACK || maxDrain <= 0) {
|
||||
return FluidAmount.ZERO;
|
||||
}
|
||||
|
||||
int drained = maxDrain;
|
||||
@@ -163,15 +171,15 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
drained = (int) fluid.getStackSize();
|
||||
}
|
||||
|
||||
FluidVolume stack = fluid.getFluidStack().withAmount(FluidAmount.of(drained, 1000));
|
||||
FluidAmount amount = FluidAmount.of(drained, 1000);
|
||||
if (doDrain) {
|
||||
fluid.setStackSize(fluid.getStackSize() - drained);
|
||||
if (fluid.getStackSize() <= 0) {
|
||||
this.fluids[slot] = null;
|
||||
this.fluids[slot] = EMPTY_AE_FLUIDSTACK;
|
||||
}
|
||||
this.onContentChanged(slot);
|
||||
}
|
||||
return stack;
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void writeToNBT(final CompoundTag data, final String name) {
|
||||
@@ -185,7 +193,7 @@ public class AEFluidInventory implements IAEFluidTank {
|
||||
try {
|
||||
final CompoundTag c = new CompoundTag();
|
||||
|
||||
if (this.fluids[x] != null) {
|
||||
if (this.fluids[x] != EMPTY_AE_FLUIDSTACK) {
|
||||
this.fluids[x].writeToNBT(c);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.LinkedListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.world.World;
|
||||
@@ -73,7 +73,7 @@ public class TickHandler {
|
||||
ServerTickEvents.END_SERVER_TICK.register(this::onAfterServerTick);
|
||||
ServerTickEvents.START_WORLD_TICK.register(this::onBeforeWorldTick);
|
||||
ServerTickEvents.END_WORLD_TICK.register(this::onAfterWorldTick);
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(this::onServerStopping);
|
||||
ServerWorldEvents.UNLOAD.register(this::onUnloadWorld);
|
||||
}
|
||||
|
||||
public static TickHandler instance() {
|
||||
@@ -135,38 +135,13 @@ public class TickHandler {
|
||||
this.getRepo().clear();
|
||||
}
|
||||
|
||||
// FIXME FABRIC It does not look like worlds can ever unload in Fabric.
|
||||
// FIXME FABRIC public void unloadWorld(final WorldEvent.Unload ev) {
|
||||
// FIXME FABRIC if (Platform.isServer()) // for no there is no reason to care about this on the client...
|
||||
// FIXME FABRIC {
|
||||
// FIXME FABRIC final List<IGridNode> toDestroy = new ArrayList<>();
|
||||
// FIXME FABRIC
|
||||
// FIXME FABRIC this.getRepo().updateNetworks();
|
||||
// FIXME FABRIC for (final Grid g : this.getRepo().networks) {
|
||||
// FIXME FABRIC for (final IGridNode n : g.getNodes()) {
|
||||
// FIXME FABRIC if (n.getWorld() == ev.getWorld()) {
|
||||
// FIXME FABRIC toDestroy.add(n);
|
||||
// FIXME FABRIC }
|
||||
// FIXME FABRIC }
|
||||
// FIXME FABRIC }
|
||||
// FIXME FABRIC
|
||||
// FIXME FABRIC for (final IGridNode n : toDestroy) {
|
||||
// FIXME FABRIC n.destroy();
|
||||
// FIXME FABRIC }
|
||||
// FIXME FABRIC }
|
||||
// FIXME FABRIC }
|
||||
|
||||
/**
|
||||
* This is primarily useful for an integrated server being stopped, and can be
|
||||
* fully replaced by the event above once world unload events hit.
|
||||
*/
|
||||
private void onServerStopping(MinecraftServer server) {
|
||||
public void onUnloadWorld(MinecraftServer server, ServerWorld world) {
|
||||
final List<IGridNode> toDestroy = new ArrayList<>();
|
||||
this.server.updateNetworks();
|
||||
for (final Grid g : this.server.networks) {
|
||||
|
||||
this.getRepo().updateNetworks();
|
||||
for (final Grid g : this.getRepo().networks) {
|
||||
for (final IGridNode n : g.getNodes()) {
|
||||
WorldAccess nodeWorld = n.getWorld();
|
||||
if (nodeWorld instanceof ServerWorld && ((ServerWorld) nodeWorld).getServer() == server) {
|
||||
if (n.getWorld() == world) {
|
||||
toDestroy.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user