Compare commits

...

34 Commits

Author SHA1 Message Date
shartte e6fb553a53 Fixes #4741: Rework Crafting CPU cycling and extract commonalities into a separate class. (#4742)
Also Fixes:
- Enter key confirms the "Confirm Crafting" dialog again
- Right Mouse Button correctly cycles backwards through CPUs, while left mouse button forward
- Fixes an internal problem with syncing CPU names
- Naming of unnamed CPUs begins at #1 now
2020-09-20 00:26:34 +02:00
shartte 971a9d22e8 Improvements to the usability of the number entry widgets (Priority, Level Emitter, Craft Amount): (#4737)
- Fixed tab order
- Introduced focus state for corner buttons
- Allowed the player to enter any text, but add validation that only persist it if it is a valid number
- Enter will now confirm these dialogs and return to the previous dialog
- The text field is automatically focused and its contents are selected
2020-09-19 16:27:29 +02:00
Sebastian Hartte f368f9dac2 Fixes #4697: Show number of required items in condenser tooltips. 2020-09-17 14:33:43 +02:00
Sebastian Hartte e5b45d51cc Minor style fixes. 2020-09-17 11:43:29 +02:00
shartte 5a1d5356d1 Fixes #4733: Fix a crash bug when an external machine tries draining fluids from a fluid interface without a filter. (#4738) 2020-09-16 23:13:18 +02:00
yueh 5b3ccf26d0 Merge all gradle files (#4732)
* Merge all gradle files

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

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

* Removed Sonarqube and JaCoCo plugins.

Co-authored-by: Sebastian Hartte <sebastian@hartte.de>
2020-09-14 14:14:00 +02:00
Sebastian Hartte 38244000c9 Fixes #4731: Take the position where the user clicked the handle into account when calculating the new scrollbar position.
Also fixes: The scrollbar did not subtract the handle height when determining the available space for moving the handle, thus introducing drift the further down a user scrolled.
Also fixes: The scrollbar did not resize it's trackbar when the ME inventory screens were resized and the number of rows changed, same for craft confirm.
2020-09-13 23:12:59 +02:00
Sebastian Hartte d87ff9d1e7 Formatting fixes. 2020-09-13 22:18:38 +02:00
Sebastian Hartte 4ba97ec899 Remove unnecessary debug prints, and rename methods. 2020-09-13 22:11:40 +02:00
Sebastian Hartte 0147e1ddfb Fixes #4730 and also introduces repeat-pageup/pagedown when holding the mouse on the area above or below the scrollbar handle. 2020-09-13 22:09:47 +02:00
shartte d07937fa93 Render Performance Improvements (#4721)
* Return a fixed bounding box for frustrum culling of AE tile entity renderers to prevent Forge from re-retrieving the TE from the world and calculating the collision box (very costly for cables).

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

* Fix formatting
2020-09-10 22:34:16 +02:00
Sebastian Hartte 81cc0b804d Fixes #4699: AE's glass should only hide it's sides adjacent to other AE glass.
Also disables spawn on AE's glass same as Vanilla glass.
2020-09-09 00:58:14 +02:00
Sebastian Hartte e1fc9908b3 Fixes #4693: Fix color applicator not working on cable-buses. 2020-09-09 00:51:45 +02:00
shartte 9ed1535477 Fixes #4661: Do not update a tile entity when it's in an unloaded chunk (i.e. during chunk unloading) (#4708) 2020-09-08 22:28:30 +02:00
Sebastian Hartte 1ef345d4a4 Formatting fixes. 2020-09-08 22:24:27 +02:00
Sebastian Hartte ff7d1b9719 Fixes #4704: QNB is not finding a registered QNB because it believes the chunk to be unloaded.
Also adds debug logging to the locatable registry to better debug this issues in the future.
2020-09-08 22:20:33 +02:00
shartte 88c9370e4f Crystal Growth Refactor (#4702)
* Refactored crystal in-world purification:
- Removed iterative formula
- Set the default growth factor to 1 rather than 0.5 to simplify settings
- Made the growth-tick-progress-per-tick a configuration setting for 0-6 accelerators
- Unfinished seeds now slowly sink when ejected by an annihilation plane or other system (emulates a player throwing it in)
- Instead of checking for "isLiquid", we now check for a new tag (appliedenergistics2:crystal_purification_medium) to check if the crystal is submerged in an allowed fluid for growth, defaults to #minecraft:water.

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

(cherry picked from commit b756d68be1)
2020-09-08 14:28:08 +02:00
Sebastian Hartte 0f34f09b32 Remove object-scanning subscriptions and remove unused test-file. 2020-09-06 20:59:37 +02:00
Sebastian Hartte 8e54a8fed9 Fixes a problem where tile entities would become unremovable (well, one more of these at least),
because they would reset their block state upon removal and loss of power.
2020-09-06 20:59:19 +02:00
Sebastian Hartte d9bb81a00f Fixes #4680: Facade advancement is showing an untextured facade. 2020-09-03 17:36:20 +02:00
yueh 39e038d916 Fixes #4683: Hopefully LazyOptional works as advertised (#4684) 2020-09-02 21:36:40 +02:00
yueh f0d0595c8f Fixes #4669: Take NBT into account for comparing. (#4675)
This only covers combining different items together.
Fuzzy filtering is still a bit too fuzzy for corner cases.
2020-09-01 21:59:08 +02:00
shartte 7c99fca789 Fixes removal of facades not invalidating the cached server-side collision boxes. (#4663)
(cherry picked from commit 957bc4c5cc)
2020-09-01 01:07:30 +02:00
yueh ab393b0ed1 Serialize the complete NBT data to avoid mismatches (#4665)
This might increase the network traffic a bit, but the amount of mods actually using the share tag seems to be very limited.
For now it is worth the risk as it solves the problem. In case we run into actual issues, there might still be other solutions, which will be way more complex and can potentially introduce additional problems.

(cherry picked from commit e90dd2f9c6)
2020-09-01 01:07:28 +02:00
yueh 133ed1f09d Remove GZipStreams from packets. (#4664)
This should now be handled by the normal network manager (hopefully).
Also renamed the `CompressedNBTPacket` to `MEInterfaceUpdatePacket` as that is the only use case.
2020-08-31 23:09:28 +02:00
shartte db9ec2b070 Remove the use of SlotItemHandler since we are only faking an inventory-backed slot, (#4654)
we might as well use what we had used pre-2017, which is simply extending a normal Slot.
This should also fix #4652.
2020-08-29 22:03:35 +02:00
Sebastian Hartte 00d4c20d9f Strip pom.xml dependencies. 2020-08-26 11:39:03 +02:00
Sebastian Hartte 5efc085693 Fix tiles that have a "powered" block state from not being able to be broken when
they are connected to a powered grid.
2020-08-24 23:54:37 +02:00
Sebastian Hartte aff94cec8c Fix search box interactions in the interface terminal. 2020-08-24 22:33:04 +02:00
Sebastian Hartte 2184d03d0b Actually save the rotation when a tile is rotated. 2020-08-24 01:06:26 +02:00
103 changed files with 1868 additions and 2512 deletions
+163 -23
View File
@@ -30,11 +30,10 @@ buildscript {
}
plugins {
id "maven-publish"
id "com.diffplug.gradle.spotless" version "4.3.0"
id "com.matthewprenger.cursegradle" version "1.4.0"
id "org.sonarqube" version "2.8"
id "jacoco"
id "maven-publish"
id "com.diffplug.gradle.spotless" version "4.3.0"
id "com.matthewprenger.cursegradle" version "1.4.0"
id "idea"
}
apply plugin: 'net.minecraftforge.gradle'
@@ -45,8 +44,32 @@ repositories {
mavenLocal()
jcenter()
mavenCentral()
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-${minecraft_version}:${jei_version}:api"
compileOnly "mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}:api"
// Runtime, Mods
runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}")
runtimeOnly fg.deobf("mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}")
// unit test dependencies
testCompile "junit:junit:4.13"
// Annotation Processors
annotationProcessor 'org.spongepowered:mixin:0.8:processor'
}
group = artifact_group
archivesBaseName = artifact_basename
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
compileJava {
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
@@ -58,7 +81,8 @@ tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
// Create version number
///////////////////
// Version Number
version = version_major + "." + version_minor + "." + version_patch
ext.pr = System.getenv('PR_NUMBER') ?: ""
@@ -76,10 +100,8 @@ if (ext.release) {
version = ext.release
}
// Maven group and artifact name
group = artifact_group
archivesBaseName = artifact_basename
ext.isAlpha = project.version.contains("alpha")
ext.isBeta = project.version.contains("beta")
sourceSets {
api
@@ -103,9 +125,8 @@ configurations {
apiCompile.extendsFrom(compile)
}
apply from: 'gradle/scripts/dependencies.gradle'
apply from: 'gradle/scripts/spotless.gradle'
////////////////////
// Forge/Minecraft
minecraft {
mappings channel: "snapshot", version: project.mcp_mappings
@@ -147,18 +168,14 @@ minecraft {
}
}
///////////
// Mixins
mixin {
add sourceSets.main, "appliedenergistics2.mixins.refmap.json"
}
jar {
manifest {
attributes([
"MixinConfigs": "appliedenergistics2.mixins.json"
])
}
}
////////////////
// Jar Signing
def signProps = [:]
if (System.getenv("KEY_STORE_FILE")) {
signProps['keyStore'] = System.getenv("KEY_STORE_FILE")
@@ -181,15 +198,88 @@ task signJar(type: net.minecraftforge.gradle.common.task.SignJar, dependsOn: 're
}
}
apply from: 'gradle/scripts/artifacts.gradle'
apply from: 'gradle/scripts/curseforge.gradle'
//////////////
// Aritfacts
processResources {
exclude '.cache'
}
jar {
finalizedBy 'reobfJar'
finalizedBy 'signJar'
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
}
task sourcesJar(type: Jar) {
classifier = "sources"
from sourceSets.main.allJava
from sourceSets.api.allJava
}
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 {
publications {
maven(MavenPublication) {
groupId = project.group
artifactId = project.archivesBaseName
version = project.version
// ForgeGradle will generate wild dependency definitions, see https://github.com/MinecraftForge/ForgeGradle/issues/584
// Since we don't actually depend on anything, just remove the entire node.
pom.withXml {
asNode().remove(asNode().dependencies)
}
from components.java
artifact sourcesJar
artifact javadocJar
@@ -215,3 +305,53 @@ publishing {
}
}
}
/////////////
// 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")) {
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
}
}
}
////////////////
// 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()
}
}
}
-81
View File
@@ -1,81 +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 'reobfJar'
finalizedBy 'signJar'
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
}
task sourcesJar(type: Jar) {
classifier = "sources"
from sourceSets.main.allJava
from sourceSets.api.allJava
}
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
}
-39
View File
@@ -1,39 +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")) {
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
}
}
}
-42
View File
@@ -1,42 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
repositories {
mavenLocal()
maven { // modmaven, maven proxy
name 'modmaven'
url "https://modmaven.k-4u.nl/"
}
}
dependencies {
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
// compile against provided APIs
compileOnly "mezz.jei:jei-${minecraft_version}:${jei_version}:api"
compileOnly "mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}:api"
// Runtime, Mods
runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}")
runtimeOnly fg.deobf("mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}")
//runtimeOnly fg.deobf("team.chisel.ctm:CTM:${ctm_version}")
// unit test dependencies
testCompile "junit:junit:4.13"
}
-32
View File
@@ -1,32 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*g GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
spotless {
java {
target 'src/*/java/appeng/**/*.java'
indentWithSpaces()
eclipse().configFile 'codeformat/codeformat.xml'
importOrderFile 'codeformat/ae2.importorder'
}
format 'json', {
target 'src/*/resources/**/*.json'
targetExclude 'src/generated/resources/**'
prettier().config(['parser': 'json'])
}
}
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -23,13 +23,23 @@
package appeng.api.implementations.items;
import net.minecraft.block.Block;
import net.minecraft.block.material.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);
@@ -39,6 +39,8 @@ import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.DyeColor;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
@@ -82,8 +84,12 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusTileEntity> implement
private static final ICableBusContainer NULL_CABLE_BUS = new NullCableBusContainer();
private static final IntegerProperty LIGHT_LEVEL = IntegerProperty.create("light_level", 0, 15);
public CableBusBlock() {
super(defaultProps(AEMaterials.GLASS).notSolid().noDrops().variableOpacity());
super(defaultProps(AEMaterials.GLASS).notSolid().noDrops().variableOpacity()
.setLightLevel(state -> state.get(LIGHT_LEVEL)));
setDefaultState(getDefaultState().with(LIGHT_LEVEL, 0));
}
@Override
@@ -126,11 +132,9 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusTileEntity> implement
}
@Override
public int getLightValue(final BlockState state, final IBlockReader world, final BlockPos pos) {
if (state.getBlock() != this) {
return state.getBlock().getLightValue(state, world, pos);
}
return this.cb(world, pos).getLightValue();
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) {
super.fillStateContainer(builder);
builder.add(LIGHT_LEVEL);
}
@Override
@@ -386,4 +390,13 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusTileEntity> implement
}
}
@Override
protected BlockState updateBlockStateFromTileEntity(BlockState currentState, CableBusTileEntity te) {
if (currentState.getBlock() != this) {
return currentState;
}
int lightLevel = te.getCableBus().getLightValue();
return super.updateBlockStateFromTileEntity(currentState, te).with(LIGHT_LEVEL, lightLevel);
}
}
@@ -106,6 +106,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
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, ITextComponent title) {
@@ -229,7 +230,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
if (this.getScrollBar() != null) {
this.getScrollBar().draw(this);
this.getScrollBar().draw(matrixStack, this);
}
this.drawFG(matrixStack, ox, oy, x, y);
@@ -275,12 +276,18 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
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 Widget widget = (Widget) o;
if (widget.isMouseOver(xCoord, yCoord)) {
return super.mouseClicked(xCoord, yCoord, 0);
handlingRightClick = true;
try {
for (final Object o : this.buttons) {
final Widget widget = (Widget) o;
if (widget.isMouseOver(xCoord, yCoord)) {
return super.mouseClicked(xCoord, yCoord, 0);
}
}
} finally {
handlingRightClick = false;
}
}
@@ -291,22 +298,36 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
}
}
if (this.getScrollBar() != null) {
this.getScrollBar().click(xCoord - this.guiLeft, yCoord - this.guiTop);
// Forward left mouse button down events to the scrollbar
if (btn == 0 && this.getScrollBar() != null) {
if (this.getScrollBar().mouseDown(xCoord - this.guiLeft, yCoord - this.guiTop)) {
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.guiLeft, mouseY - this.guiTop)) {
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.getItemStack();
if (this.getScrollBar() != null) {
// FIXME: Coordinate system of mouseX/mouseY is unclear
this.getScrollBar().click((int) mouseX - this.guiLeft, (int) mouseY - this.guiTop);
this.getScrollBar().mouseDragged((int) mouseX - this.guiLeft, (int) mouseY - this.guiTop);
}
if (slot instanceof FakeSlot && !itemstack.isEmpty()) {
@@ -581,7 +602,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
if (slot instanceof SlotME) {
final IAEItemStack item = ((SlotME) slot).getAEStack();
if (item != null) {
((AEBaseContainer) this.container).setTargetStack(item);
this.container.setTargetStack(item);
final InventoryAction direction = wheel > 0 ? InventoryAction.ROLL_DOWN : InventoryAction.ROLL_UP;
final int times = (int) Math.abs(wheel);
final int inventorySize = this.getInventorySlots().size();
@@ -614,15 +635,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
}
protected ITextComponent getGuiDisplayName(final ITextComponent in) {
return this.hasCustomInventoryName() ? new StringTextComponent(this.getInventoryName()) : in;
}
private boolean hasCustomInventoryName() {
return this.container.getCustomName() != null;
}
private String getInventoryName() {
return this.container.getCustomName();
return title.getString().isEmpty() ? in : title;
}
/**
@@ -796,6 +809,11 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
public void tick() {
super.tick();
if (this.getScrollBar() != null) {
this.getScrollBar().tick();
}
for (IGuiEventListener child : children) {
if (child instanceof ITickingWidget) {
((ITickingWidget) child).tick();
@@ -803,4 +821,12 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends ContainerS
}
}
/**
* Returns true while the current event being handled is a click of the right
* mouse button.
*/
public boolean isHandlingRightClick() {
return handlingRightClick;
}
}
@@ -8,19 +8,18 @@ import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.SlotItemHandler;
/**
* A proxy for a slot that will always return an itemstack with size 1, if there
* is an item in the slot. Used to prevent the default item count from
* rendering.
*/
class Size1Slot extends SlotItemHandler {
class Size1Slot extends Slot {
private final SlotItemHandler delegate;
private final Slot delegate;
public Size1Slot(SlotItemHandler delegate) {
super(delegate.getItemHandler(), delegate.getSlotIndex(), delegate.xPos, delegate.yPos);
public Size1Slot(Slot delegate) {
super(delegate.inventory, delegate.getSlotIndex(), delegate.xPos, delegate.yPos);
this.delegate = delegate;
}
@@ -55,35 +55,23 @@ final class AESubScreen {
IPriorityHost priorityHost = (IPriorityHost) containerTarget;
this.previousContainerIcon = priorityHost.getItemStackRepresentation();
this.previousContainerType = ChestContainer.TYPE;
}
else if (containerTarget instanceof IPriorityHost) {
} else if (containerTarget instanceof IPriorityHost) {
IPriorityHost priorityHost = (IPriorityHost) containerTarget;
this.previousContainerIcon = priorityHost.getItemStackRepresentation();
this.previousContainerType = priorityHost.getContainerType();
}
else if (containerTarget instanceof WirelessTerminalGuiObject) {
} else if (containerTarget instanceof WirelessTerminalGuiObject) {
this.previousContainerIcon = definitions.items().wirelessTerminal().maybeStack(1).orElse(ItemStack.EMPTY);
this.previousContainerType = WirelessTermContainer.TYPE;
}
else if (containerTarget instanceof TerminalPart) {
} else if (containerTarget instanceof TerminalPart) {
this.previousContainerIcon = parts.terminal().maybeStack(1).orElse(ItemStack.EMPTY);
this.previousContainerType = MEMonitorableContainer.TYPE;
}
else if (containerTarget instanceof CraftingTerminalPart) {
} else if (containerTarget instanceof CraftingTerminalPart) {
this.previousContainerIcon = parts.craftingTerminal().maybeStack(1).orElse(ItemStack.EMPTY);
this.previousContainerType = CraftingTermContainer.TYPE;
}
else if (containerTarget instanceof PatternTerminalPart) {
} else if (containerTarget instanceof PatternTerminalPart) {
this.previousContainerIcon = parts.patternTerminal().maybeStack(1).orElse(ItemStack.EMPTY);
this.previousContainerType = PatternTermContainer.TYPE;
}
else {
} else {
this.previousContainerIcon = null;
this.previousContainerType = null;
}
@@ -48,22 +48,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 Button(this.guiLeft + 128, this.guiTop + 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(Button 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
@@ -80,23 +86,9 @@ public class CraftAmountScreen extends AEBaseScreen<CraftAmountContainer> {
this.bindTexture("guis/craft_amt.png");
GuiUtils.drawTexturedModalRect(offsetX, offsetY, 0, 0, this.xSize, this.ySize, getBlitOffset());
this.next.active = this.amountToCraft.getValue() > 0;
this.next.active = this.amountToCraft.getIntValue().orElse(0) > 0;
this.amountToCraft.render(matrixStack, 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";
}
}
@@ -27,6 +27,8 @@ import com.google.common.base.Joiner;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.client.util.InputMappings;
import net.minecraft.entity.player.PlayerInventory;
@@ -76,10 +78,6 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
this.setScrollBar(scrollbar);
}
boolean isAutoStart() {
return this.container.isAutoStart();
}
@Override
public void init() {
super.init();
@@ -90,13 +88,14 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
this.addButton(this.start);
this.selectCPU = new Button(this.guiLeft + (219 - 180) / 2, this.guiTop + this.ySize - 68, 180, 20,
new StringTextComponent(GuiText.CraftingCPU.getLocal() + ": " + GuiText.Automatic),
btn -> selectNextCpu());
getNextCpuButtonLabel(), btn -> selectNextCpu());
this.selectCPU.active = false;
this.addButton(this.selectCPU);
addButton(new Button(this.guiLeft + 6, this.guiTop + this.ySize - 25, 50, 20, GuiText.Cancel.text(),
btn -> subGui.goBack()));
this.setScrollBar();
}
@Override
@@ -137,22 +136,22 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
}
private void updateCPUButtonText() {
String btnTextText = GuiText.CraftingCPU.getLocal() + ": " + GuiText.Automatic.getLocal();
if (this.container.getSelectedCpu() >= 0)// && status.selectedCpu < status.cpus.size() )
{
if (this.container.getName() != null) {
final String name = this.container.getName().getStringTruncated(20);
btnTextText = GuiText.CraftingCPU.getLocal() + ": " + name;
} else {
btnTextText = GuiText.CraftingCPU.getLocal() + ": #" + this.container.getSelectedCpu();
}
}
this.selectCPU.setMessage(getNextCpuButtonLabel());
}
private ITextComponent getNextCpuButtonLabel() {
if (this.container.hasNoCPU()) {
btnTextText = GuiText.NoCraftingCPUs.getLocal();
return GuiText.NoCraftingCPUs.text();
}
this.selectCPU.setMessage(new StringTextComponent(btnTextText));
ITextComponent cpuName;
if (this.container.cpuName == null) {
cpuName = GuiText.Automatic.text();
} else {
cpuName = this.container.cpuName;
}
return GuiText.CraftingCPU.withSuffix(": ").append(cpuName);
}
private boolean isSimulation() {
@@ -445,7 +444,7 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
@Override
public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) {
if (!this.checkHotbarKeys(InputMappings.getInputByCode(keyCode, scanCode))) {
if (keyCode == 28) {
if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
this.start();
return true;
}
@@ -454,7 +453,7 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
}
private void selectNextCpu() {
final boolean backwards = minecraft.mouseHelper.isRightDown();
final boolean backwards = isHandlingRightClick();
NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next"));
}
@@ -16,10 +16,6 @@
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
/**
*
*/
package appeng.client.gui.implementations;
import com.mojang.blaze3d.matrix.MatrixStack;
@@ -27,7 +23,6 @@ import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import appeng.container.implementations.CraftingStatusContainer;
import appeng.core.localization.GuiText;
@@ -50,14 +45,13 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
public void init() {
super.init();
this.selectCPU = new Button(this.guiLeft + 8, this.guiTop + this.ySize - 25, 150, 20,
new StringTextComponent(GuiText.CraftingCPU.getLocal() + ": " + GuiText.NoCraftingCPUs),
this.selectCPU = new Button(this.guiLeft + 8, this.guiTop + this.ySize - 25, 150, 20, getNextCpuButtonLabel(),
btn -> selectNextCpu());
this.addButton(this.selectCPU);
subGui.addBackButton(btn -> {
addButton(btn);
btn.setHideEdge(13);
btn.setHideEdge(true);
}, 213, -4);
}
@@ -68,23 +62,14 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
}
private void updateCPUButtonText() {
String btnTextText = GuiText.NoCraftingJobs.getLocal();
if (this.container.selectedCpu >= 0)// && status.selectedCpu < status.cpus.size() )
{
if (this.container.myName != null) {
final String name = this.container.myName.getStringTruncated(20);
btnTextText = GuiText.CPUs.getLocal() + ": " + name;
} else {
btnTextText = GuiText.CPUs.getLocal() + ": #" + this.container.selectedCpu;
}
}
this.selectCPU.setMessage(getNextCpuButtonLabel());
}
private ITextComponent getNextCpuButtonLabel() {
if (this.container.noCPU) {
btnTextText = GuiText.NoCraftingJobs.getLocal();
return GuiText.NoCraftingJobs.text();
}
this.selectCPU.setMessage(new StringTextComponent(btnTextText));
return GuiText.CraftingCPU.withSuffix(": ").append(container.cpuName);
}
@Override
@@ -92,9 +77,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 = minecraft.mouseHelper.isRightDown();
final boolean backwards = isHandlingRightClick();
NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next"));
}
@@ -90,7 +90,7 @@ public class InterfaceScreen extends UpgradeableScreen<InterfaceContainer> {
}
private void selectNextInterfaceMode() {
final boolean backwards = getMinecraft().mouseHelper.isRightDown();
final boolean backwards = isHandlingRightClick();
NetworkHandler.instance().sendToServer(new ConfigButtonPacket(Settings.INTERFACE_TERMINAL, backwards));
}
@@ -71,8 +71,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
public InterfaceTerminalScreen(InterfaceTerminalContainer container, PlayerInventory playerInventory,
ITextComponent title) {
super(container, playerInventory, title);
final Scrollbar scrollbar = new Scrollbar();
this.setScrollBar(scrollbar);
this.setScrollBar(new Scrollbar().setLeft(175).setTop(18).setHeight(106));
this.xSize = 195;
this.ySize = 222;
}
@@ -81,16 +80,14 @@ 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.font, this.guiLeft + 104, this.guiTop + 4, 65, 12);
this.searchField.setEnableBackgroundDrawing(false);
this.searchField.setMaxStringLength(25);
this.searchField.setTextColor(0xFFFFFF);
this.searchField.setVisible(true);
this.searchField.setFocused2(true);
this.searchField.setResponder(str -> this.refreshList());
this.addListener(this.searchField);
this.changeFocus(true);
}
@Override
@@ -131,11 +128,8 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
@Override
public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) {
if (this.searchField.mouseClicked(xCoord, yCoord, btn)) {
if (btn == 1 && this.searchField.isMouseOver(xCoord, yCoord)) {
this.searchField.setText("");
this.refreshList();
}
if (btn == 1 && this.searchField.isMouseOver(xCoord, yCoord)) {
this.searchField.setText("");
return true;
}
@@ -173,11 +167,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
if (character == ' ' && this.searchField.getText().isEmpty()) {
return true;
}
if (this.searchField.charTyped(character, key)) {
this.refreshList();
return true;
}
return false;
return super.charTyped(character, key);
}
@Override
@@ -245,7 +235,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
/**
* Rebuilds the list of interfaces.
*
* <p>
* Respects a search term if present (ignores case) and adding only matching
* patterns.
*/
@@ -338,12 +328,11 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
/**
* Tries to retrieve a cache for a with search term as keyword.
*
* <p>
* If this cache should be empty, it will populate it with an earlier cache if
* available or at least the cache for the empty string.
*
* @param searchTerm the corresponding search
*
* @return a Set matching a superset of the search term
*/
private Set<Object> getCacheForSearchTerm(final String searchTerm) {
@@ -35,8 +35,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> {
@@ -52,15 +50,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);
container.setTextField(this.level);
this.level.setValue(container.getReportingValue());
this.level.setOnChange(this::saveReportingValue);
this.level.setOnConfirm(this::closeScreen);
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(container::setReportingValue);
}
@Override
@@ -237,7 +237,7 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
if (this.viewCell || this instanceof WirelessTermScreen) {
this.craftingStatusBtn = this.addButton(new TabButton(this.guiLeft + 170, this.guiTop - 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 +283,8 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
craftingGridOffsetX -= 25;
craftingGridOffsetY -= 6;
this.setScrollBar();
}
@Override
@@ -2,43 +2,60 @@ 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 com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.gui.AbstractGui;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.IGuiEventListener;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TranslationTextComponent;
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 AbstractGui {
private static final ITextComponent INVALID_NUMBER = new TranslationTextComponent(
"gui.appliedenergistics2.validation.InvalidNumber");
private static final String NUMBER_LESS_THAN_MIN_VALUE = "gui.appliedenergistics2.validation.NumberLessThanMinValue";
private static final ITextComponent PLUS = new StringTextComponent("+");
private static final ITextComponent MINUS = new StringTextComponent("-");
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<Button> 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;
@@ -47,28 +64,51 @@ public class NumberEntryWidget implements ITickingWidget {
FontRenderer font = parent.getMinecraft().fontRenderer;
int inputX = parent.getGuiLeft() + x;
int inputY = parent.getGuiTop() + y;
this.level = new NumberBox(font, inputX, inputY, width, font.FONT_HEIGHT, type.getInputType(), changeListener);
this.level.setEnableBackgroundDrawing(false);
this.level.setMaxStringLength(16);
this.level.setTextColor(0xFFFFFF);
this.level.setVisible(true);
this.level.setFocused2(true);
parent.setFocusedDefault(this.level);
this.textField = new ConfirmableTextField(font, inputX, inputY, width, font.FONT_HEIGHT,
StringTextComponent.EMPTY);
this.textField.setEnableBackgroundDrawing(false);
this.textField.setMaxStringLength(16);
this.textField.setTextColor(TEXT_COLOR_NORMAL);
this.textField.setVisible(true);
this.textField.setFocused2(true);
parent.setFocusedDefault(this.textField);
this.textField.setResponder(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.setEnabled(active);
this.textField.setEnabled(active);
this.buttons.forEach(b -> b.active = active);
}
public void setTextFieldBounds(int x, int y, int width) {
this.level.x = parent.getGuiLeft() + x;
this.level.y = parent.getGuiTop() + y;
this.level.setWidth(width);
this.textField.x = parent.getGuiLeft() + x;
this.textField.y = parent.getGuiTop() + y;
this.textField.setWidth(width);
}
public void setMinValue(long minValue) {
this.level.setMinValue(minValue);
this.minValue = minValue;
validate();
}
public void addButtons(Consumer<IGuiEventListener> addChildren, Consumer<Button> addButton) {
@@ -81,54 +121,116 @@ public class NumberEntryWidget implements ITickingWidget {
int left = parent.getGuiLeft() + x;
int top = parent.getGuiTop() + y;
List<Button> buttons = new ArrayList<>(8);
List<Button> buttons = new ArrayList<>(9);
buttons.add(new Button(left, top, 22, 20, makeLabel(PLUS, a), btn -> addQty(a)));
buttons.add(new Button(left + 28, top, 28, 20, makeLabel(PLUS, b), btn -> addQty(b)));
buttons.add(new Button(left + 62, top, 32, 20, makeLabel(PLUS, c), btn -> addQty(c)));
buttons.add(new Button(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 Button(left, top + 42, 22, 20, makeLabel(MINUS, a), btn -> addQty(-a)));
buttons.add(new Button(left + 28, top + 42, 28, 20, makeLabel(MINUS, b), btn -> addQty(-b)));
buttons.add(new Button(left + 62, top + 42, 32, 20, makeLabel(MINUS, c), btn -> addQty(-c)));
buttons.add(new Button(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 matrixStack, int mouseX, int mouseY, float partialTicks) {
this.level.render(matrixStack, 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.setCursorPositionEnd();
this.textField.setSelectionPos(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<ITextComponent> validationErrors = new ArrayList<>();
String text = textField.getText().trim();
try {
long value = Long.parseLong(text, 10);
if (value < minValue) {
validationErrors.add(new TranslationTextComponent(NUMBER_LESS_THAN_MIN_VALUE, minValue));
}
} catch (NumberFormatException ignored) {
validationErrors.add(INVALID_NUMBER);
}
boolean valid = validationErrors.isEmpty();
this.textField.setTextColor(valid ? TEXT_COLOR_NORMAL : TEXT_COLOR_ERROR);
if (this.validationIcon != null) {
this.validationIcon.setValid(valid);
this.validationIcon.setTooltip(validationErrors);
}
}
private ITextComponent makeLabel(ITextComponent prefix, int amount) {
return prefix.copyRaw().appendString(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 com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
@@ -27,8 +29,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> {
@@ -39,24 +39,38 @@ public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
public PriorityScreen(PriorityContainer container, PlayerInventory playerInventory, ITextComponent title) {
super(container, playerInventory, title);
this.subGui = new AESubScreen(this, container.getPriorityHost());
// This is the effective size of the background image
xSize = 175;
ySize = 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);
container.setTextField(this.priority);
this.priority.setValue(this.container.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()) {
container.setPriority(priority.getAsInt());
}
}
@Override
@@ -68,13 +82,10 @@ public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
@Override
public void drawBG(MatrixStack matrixStack, final int offsetX, final int offsetY, final int mouseX,
final int mouseY, float partialTicks) {
this.bindTexture(getBackground());
this.bindTexture("guis/priority.png");
blit(matrixStack, offsetX, offsetY, 0, 0, this.xSize, this.ySize);
this.priority.render(matrixStack, mouseX, mouseY, partialTicks);
}
protected String getBackground() {
return "guis/priority.png";
}
}
@@ -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.gui.FontRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.util.text.ITextComponent;
public class ConfirmableTextField extends TextFieldWidget {
private Runnable onConfirm;
public ConfirmableTextField(FontRenderer fontRenderer, int x, int y, int width, int height, ITextComponent text) {
super(fontRenderer, x, y, width, height, text);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (canWrite() && (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.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.ResourceLocation;
@@ -35,6 +36,10 @@ public abstract class IconButton extends Button implements ITooltip {
private boolean halfSize = false;
private boolean disableClickSound = false;
private boolean disableBackground = false;
public IconButton(final int x, final int y, IPressable onPress) {
super(x, y, 16, 16, StringTextComponent.EMPTY, onPress);
}
@@ -44,6 +49,13 @@ public abstract class IconButton extends Button implements ITooltip {
this.active = vis;
}
@Override
public void playDownSound(SoundHandler soundHandler) {
if (!disableClickSound) {
super.playDownSound(soundHandler);
}
}
@Override
public void renderButton(MatrixStack matrixStack, final int mouseX, final int mouseY, float partial) {
@@ -74,7 +86,9 @@ public abstract class IconButton extends Button implements ITooltip {
final int uv_y = iconIndex / 16;
final int uv_x = iconIndex - uv_y * 16;
GuiUtils.drawTexturedModalRect(0, 0, 256 - 16, 256 - 16, 16, 16, 0);
if (!disableBackground) {
GuiUtils.drawTexturedModalRect(0, 0, 256 - 16, 256 - 16, 16, 16, 0);
}
GuiUtils.drawTexturedModalRect(0, 0, uv_x * 16, uv_y * 16, 16, 16, 0);
RenderSystem.popMatrix();
} else {
@@ -87,12 +101,18 @@ public abstract class IconButton extends Button implements ITooltip {
final int uv_y = iconIndex / 16;
final int uv_x = iconIndex - uv_y * 16;
GuiUtils.drawTexturedModalRect(this.x, this.y, 256 - 16, 256 - 16, 16, 16, 0);
if (!disableBackground) {
GuiUtils.drawTexturedModalRect(this.x, this.y, 256 - 16, 256 - 16, 16, 16, 0);
}
GuiUtils.drawTexturedModalRect(this.x, this.y, uv_x * 16, uv_y * 16, 16, 16, 0);
}
RenderSystem.enableDepthTest();
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
if (isHovered()) {
renderToolTip(matrixStack, mouseX, mouseY);
}
}
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
}
protected abstract int getIconIndex();
@@ -135,4 +155,20 @@ public abstract class IconButton extends Button 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.gui.FontRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.util.text.StringTextComponent;
public class NumberBox extends TextFieldWidget {
private final LongConsumer changeListener;
private long lastValue;
private long minValue = 0;
private long maxValue;
public NumberBox(final FontRenderer 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 StringTextComponent("0"));
this.setText("0");
setResponder(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,17 +18,75 @@
package appeng.client.gui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import java.time.Duration;
import net.minecraftforge.fml.client.gui.GuiUtils;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.gui.AbstractGui;
import net.minecraft.client.renderer.Rectangle2d;
import net.minecraft.util.ResourceLocation;
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 AbstractGui 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 ResourceLocation TEXTURE = new ResourceLocation("minecraft",
"textures/gui/container/creative_inventory/tabs.png");
/**
* Rectangle in the source texture that contains the sprite for an enabled
* handle.
*/
private static final Rectangle2d ENABLED = new Rectangle2d(232, 0, HANDLE_WIDTH, HANDLE_HEIGHT);
/**
* Rectangle in the source texture that contains the sprite for a disabled
* handle.
*/
private static final Rectangle2d DISABLED = new Rectangle2d(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;
@@ -36,18 +94,58 @@ public class Scrollbar implements IScrollSource {
private int minScroll = 0;
private int currentScroll = 0;
public void draw(final AEBaseScreen<?> g) {
g.bindTexture("minecraft", "gui/container/creative_inventory/tabs.png");
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
/**
* 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) {
setBlitOffset(g.getBlitOffset());
// Draw the track (nice for debugging)
// fill(matrices, displayX, displayY, this.displayX + width, this.displayY +
// height, 0xffff0000);
g.bindTexture(TEXTURE);
int yOffset;
Rectangle2d sourceRect;
if (this.getRange() == 0) {
GuiUtils.drawTexturedModalRect(this.displayX, this.displayY, 232 + this.width, 0, this.width, 15,
g.getBlitOffset());
yOffset = 0;
sourceRect = DISABLED;
} else {
final int offset = (this.currentScroll - this.minScroll) * (this.height - 15) / this.getRange();
GuiUtils.drawTexturedModalRect(this.displayX, offset + this.displayY, 232, 0, this.width, 15,
g.getBlitOffset());
yOffset = getHandleYOffset();
sourceRect = ENABLED;
}
blit(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() {
@@ -111,24 +209,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.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
@@ -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 = Minecraft.getInstance().mouseHelper.isRightDown();
boolean backwards = false;
// This isn't great, but we don't get any information about right-clicks
// otherwise
Screen currentScreen = Minecraft.getInstance().currentScreen;
if (currentScreen instanceof AEBaseScreen) {
backwards = ((AEBaseScreen<?>) currentScreen).isHandlingRightClick();
}
onPress.handle(this, backwards);
}
@@ -33,7 +33,7 @@ public class TabButton extends Button implements ITooltip {
public static final ResourceLocation TEXTURE_STATES = new ResourceLocation("appliedenergistics2",
"textures/guis/states.png");
private final ItemRenderer itemRenderer;
private int hideEdge = 0;
private boolean hideEdge;
private int myIcon = -1;
private ItemStack myItem;
@@ -71,14 +71,17 @@ public class TabButton extends Button 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;
blit(matrixStack, this.x, this.y, uv_x * 16, 0, 25, 22);
blit(matrixStack, 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;
blit(matrixStack, offsetX + this.x + 3, this.y + 3, uv_x * 16, uv_y * 16, 16, 16);
@@ -128,11 +131,11 @@ public class TabButton extends Button 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,65 @@
package appeng.client.gui.widgets;
import java.util.ArrayList;
import java.util.List;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.text.ITextComponent;
/**
* Displays a small icon that shows validation errors for some input control.
*/
public class ValidationIcon extends IconButton {
private final List<ITextComponent> 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<ITextComponent> 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;
}
Minecraft client = Minecraft.getInstance();
Screen screen = client.currentScreen;
if (screen == null) {
return;
}
screen.renderToolTip(matrices, this.tooltip, x, y, client.fontRenderer);
}
}
@@ -41,11 +41,6 @@ import appeng.fluids.util.FluidSorters;
import appeng.util.Platform;
import appeng.util.prioritylist.IPartitionList;
/**
* @author BrockWS
* @version rv6 - 22/05/2018
* @since rv6 22/05/2018
*/
public class FluidRepo {
private final IItemList<IAEFluidStack> list = Api.instance().storage().getStorageChannel(IFluidStorageChannel.class)
.createList();
+11 -12
View File
@@ -21,23 +21,22 @@ package appeng.client.me;
import javax.annotation.Nonnull;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.SlotItemHandler;
import appeng.api.storage.data.IAEFluidStack;
import appeng.fluids.container.slots.IMEFluidSlot;
/**
* @author BrockWS
* @version rv6 - 22/05/2018
* @since rv6 22/05/2018
*/
public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
public class SlotFluidME extends Slot implements IMEFluidSlot {
private InternalFluidSlotME slot;
private static final IInventory EMPTY_INVENTORY = new Inventory(0);
private final InternalFluidSlotME slot;
public SlotFluidME(InternalFluidSlotME slot) {
super(null, 0, slot.getxPosition(), slot.getyPosition());
super(EMPTY_INVENTORY, 0, slot.getxPosition(), slot.getyPosition());
this.slot = slot;
}
@@ -50,7 +49,7 @@ public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
}
@Override
public boolean isItemValid(final ItemStack par1ItemStack) {
public boolean isItemValid(final ItemStack stack) {
return false;
}
@@ -69,7 +68,7 @@ public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
}
@Override
public void putStack(final ItemStack par1ItemStack) {
public void putStack(final ItemStack stack) {
}
@@ -85,7 +84,7 @@ public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
}
@Override
public boolean canTakeStack(final PlayerEntity par1PlayerEntity) {
public boolean canTakeStack(final PlayerEntity player) {
return false;
}
}
+15 -11
View File
@@ -19,23 +19,27 @@
package appeng.client.me;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.SlotItemHandler;
import appeng.api.storage.data.IAEItemStack;
public class SlotME extends SlotItemHandler {
public class SlotME extends Slot {
private final InternalSlotME mySlot;
private static final IInventory EMPTY_INVENTORY = new Inventory(0);
public SlotME(final InternalSlotME me) {
super(null, 0, me.getxPosition(), me.getyPosition());
this.mySlot = me;
private final InternalSlotME slot;
public SlotME(final InternalSlotME slot) {
super(EMPTY_INVENTORY, 0, slot.getxPosition(), slot.getyPosition());
this.slot = slot;
}
public IAEItemStack getAEStack() {
if (this.mySlot.hasPower()) {
return this.mySlot.getAEStack();
if (this.slot.hasPower()) {
return this.slot.getAEStack();
}
return null;
}
@@ -47,15 +51,15 @@ public class SlotME extends SlotItemHandler {
@Override
public ItemStack getStack() {
if (this.mySlot.hasPower()) {
return this.mySlot.getStack();
if (this.slot.hasPower()) {
return this.slot.getStack();
}
return ItemStack.EMPTY;
}
@Override
public boolean getHasStack() {
if (this.mySlot.hasPower()) {
if (this.slot.hasPower()) {
return !this.getStack().isEmpty();
}
return false;
@@ -21,7 +21,6 @@ package appeng.client.render.cablebus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -30,6 +29,11 @@ import java.util.Random;
import javax.annotation.Nullable;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.Weigher;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.model.BakedQuad;
@@ -50,7 +54,10 @@ import appeng.api.util.AEColor;
public class CableBusBakedModel implements IBakedModel {
private static final Map<CableBusRenderState, List<BakedQuad>> CABLE_MODEL_CACHE = new HashMap<>();
// The number of quads overall that will be cached
private static final int CACHE_QUAD_COUNT = 5000;
private final LoadingCache<CableBusRenderState, List<BakedQuad>> cableModelCache;
private final CableBuilder cableBuilder;
@@ -66,6 +73,17 @@ public class CableBusBakedModel implements IBakedModel {
this.facadeBuilder = facadeBuilder;
this.partModels = partModels;
this.particleTexture = particleTexture;
this.cableModelCache = CacheBuilder.newBuilder()//
.maximumWeight(CACHE_QUAD_COUNT)//
.weigher((Weigher<CableBusRenderState, List<BakedQuad>>) (key, value) -> value.size())//
.build(new CacheLoader<CableBusRenderState, List<BakedQuad>>() {
@Override
public List<BakedQuad> load(CableBusRenderState renderState) {
final List<BakedQuad> model = new ArrayList<>();
addCableQuads(renderState, model);
return model;
}
});
}
@Override
@@ -92,11 +110,7 @@ public class CableBusBakedModel implements IBakedModel {
if (layer == RenderType.getCutout()) {
// First, handle the cable at the center of the cable bus
final List<BakedQuad> cableModel = CABLE_MODEL_CACHE.computeIfAbsent(renderState, k -> {
final List<BakedQuad> model = new ArrayList<>();
this.addCableQuads(renderState, model);
return model;
});
final List<BakedQuad> cableModel = cableModelCache.getUnchecked(renderState);
quads.addAll(cableModel);
// Then handle attachments
@@ -341,8 +355,4 @@ public class CableBusBakedModel implements IBakedModel {
return ItemOverrideList.EMPTY;
}
public static void clearCache() {
CABLE_MODEL_CACHE.clear();
}
}
@@ -18,7 +18,6 @@ public class CableBusModelLoader implements IModelLoader<CableBusModel> {
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
CableBusBakedModel.clearCache();
}
@Override
@@ -113,7 +113,7 @@ abstract class CraftingCubeBakedModel implements IDynamicBakedModel {
return quads;
}
private void addRing(CubeBuilder builder, @Nullable Direction side, EnumSet<Direction> connections) {
private void addRing(CubeBuilder builder, Direction side, EnumSet<Direction> connections) {
// Fill in the corners
builder.setTexture(this.ringCorner);
this.addCornerCap(builder, connections, side, Direction.UP, Direction.EAST, Direction.NORTH);
@@ -65,10 +65,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.util.InventoryAdaptor;
@@ -86,11 +84,9 @@ public abstract class AEBaseContainer extends Container {
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;
@@ -297,9 +293,7 @@ public abstract class AEBaseContainer extends Container {
@Override
public void detectAndSendChanges() {
this.sendCustomName();
if (Platform.isServer()) {
if (isServer()) {
if (this.tileEntity != null
&& this.tileEntity.getWorld().getTileEntity(this.tileEntity.getPos()) != this.tileEntity) {
this.setValidContainer(false);
@@ -866,43 +860,6 @@ public abstract class AEBaseContainer extends Container {
this.detectAndSendChanges();
}
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);
@@ -992,14 +949,6 @@ public abstract class AEBaseContainer extends Container {
this.cellInv = cellInv;
}
public String getCustomName() {
return this.customName;
}
public void setCustomName(final String customName) {
this.customName = customName;
}
public PlayerInventory getPlayerInventory() {
return this.invPlayer;
}
@@ -1028,4 +977,18 @@ public abstract class AEBaseContainer extends Container {
this.powerSrc = powerSrc;
}
/**
* Returns whether this container instance lives on the client.
*/
protected boolean isClient() {
return invPlayer.player.getEntityWorld().isRemote();
}
/**
* 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 ITextComponent must be non-null
@@ -49,7 +49,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.tile.misc.CellWorkbenchTileEntity;
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 detectAndSendChanges() {
final ItemStack is = this.workBench.getInventoryByName("cell").getStackInSlot(0);
if (Platform.isServer()) {
if (isServer()) {
for (final IContainerListener listener : this.listeners) {
if (this.prevStack != is) {
// if the bars changed an item was probably made, so just send shit!
@@ -33,7 +33,6 @@ import appeng.container.interfaces.IProgressProvider;
import appeng.container.slot.OutputSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.tile.misc.CondenserTileEntity;
import appeng.util.Platform;
public class CondenserContainer extends AEBaseContainer implements IProgressProvider {
@@ -75,13 +74,13 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv
@Override
public void detectAndSendChanges() {
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.detectAndSendChanges();
@@ -101,7 +100,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 net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.player.ServerPlayerEntity;
@@ -11,8 +13,6 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkHooks;
import appeng.api.config.SecurityPermissions;
@@ -43,6 +43,8 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
private final SecurityPermissions requiredPermission;
private Function<I, ITextComponent> containerTitleStrategy = this::getDefaultContainerTitle;
public ContainerHelper(ContainerFactory<C, I> factory, Class<I> interfaceClass) {
this(factory, interfaceClass, null);
}
@@ -54,19 +56,47 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
this.factory = factory;
}
/**
* Specifies a custom strategy for obtaining a custom container name.
*
* The stratgy should return {@link StringTextComponent#EMPTY} if there's no
* custom name.
*/
public ContainerHelper<C, I> withContainerTitle(Function<I, ITextComponent> containerTitleStrategy) {
this.containerTitleStrategy = containerTitleStrategy;
return this;
}
/**
* Opens a container that is based around a single tile entity. The tile
* entity's position is encoded in the packet buffer.
*/
public C fromNetwork(int windowId, PlayerInventory inv, PacketBuffer 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, PacketBuffer 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?
@@ -83,7 +113,7 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
return false;
}
ITextComponent title = findContainerTitle(player.world, locator, accessInterface);
ITextComponent title = containerTitleStrategy.apply(accessInterface);
INamedContainerProvider container = new SimpleNamedContainerProvider((wnd, p, pl) -> {
C c = factory.create(wnd, p, accessInterface);
@@ -92,34 +122,14 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
c.setLocator(locator);
return c;
}, title);
NetworkHooks.openGui((ServerPlayerEntity) player, container, locator::write);
NetworkHooks.openGui((ServerPlayerEntity) player, container, buffer -> {
locator.write(buffer);
initialDataSerializer.serializeInitialData(accessInterface, buffer);
});
return true;
}
private ITextComponent 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 TranslationTextComponent(
world.getBlockState(locator.getBlockPos()).getBlock().getTranslationKey());
}
return new StringTextComponent("Unknown");
}
private I getHostFromLocator(PlayerEntity player, ContainerLocator locator) {
if (locator.hasItemIndex()) {
return getHostFromPlayerInventory(player, locator);
@@ -196,6 +206,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, PacketBuffer 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, PacketBuffer buffer);
}
private boolean checkPermission(PlayerEntity player, Object accessInterface) {
if (requiredPermission != null) {
@@ -206,4 +234,15 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
}
private ITextComponent getDefaultContainerTitle(I accessInterface) {
if (accessInterface instanceof ICustomNameObject) {
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
if (customNameObject.hasCustomInventoryName()) {
return customNameObject.getCustomInventoryName();
}
}
return StringTextComponent.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.entity.player.ServerPlayerEntity;
@@ -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 ContainerType<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 ITextComponent myName;
public ITextComponent 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 detectAndSendChanges() {
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.detectAndSendChanges();
@@ -278,22 +224,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() {
ContainerType<?> originalGui = null;
@@ -316,9 +246,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());
@@ -348,6 +276,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;
}
@@ -372,42 +316,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 ITextComponent getName() {
return this.myName;
}
private void setName(@Nullable final ITextComponent 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.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.IContainerListener;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
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.CraftingTileEntity;
import appeng.util.Platform;
public class CraftingCPUContainer extends AEBaseContainer
implements IMEMonitorHandlerReceiver<IAEItemStack>, ICustomNameObject {
public class CraftingCPUContainer extends AEBaseContainer implements IMEMonitorHandlerReceiver<IAEItemStack> {
public static ContainerType<CraftingCPUContainer> TYPE;
private static final ContainerHelper<CraftingCPUContainer, CraftingTileEntity> helper = new ContainerHelper<>(
CraftingCPUContainer::new, CraftingTileEntity.class, SecurityPermissions.CRAFT);
CraftingCPUContainer::new, CraftingTileEntity.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 StringTextComponent.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 ITextComponent 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 CraftingTileEntity) {
this.setCPU(((CraftingTileEntity) 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 detectAndSendChanges() {
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 ITextComponent 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.util.text.StringTextComponent;
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 StringTextComponent("#" + (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.util.text.ITextComponent;
import appeng.api.networking.crafting.ICraftingCPU;
public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
private final ITextComponent myName;
private final ICraftingCPU cpu;
private final long size;
private final int processors;
private ITextComponent 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;
}
ITextComponent 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 ITextComponent getName() {
return name;
}
public void setName(ITextComponent 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.inventory.container.ContainerType;
@@ -31,14 +25,13 @@ import net.minecraft.network.PacketBuffer;
import net.minecraft.util.text.ITextComponent;
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 ContainerType<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 ITextComponent myName;
public ITextComponent 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 detectAndSendChanges() {
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.detectAndSendChanges();
@@ -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);
}
}
@@ -35,7 +35,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 {
@@ -107,7 +106,7 @@ public class FormationPlaneContainer extends UpgradeableContainer {
public void detectAndSendChanges() {
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));
}
@@ -34,7 +34,6 @@ import appeng.container.guisync.GuiSync;
import appeng.container.slot.OutputSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.tile.storage.IOPortTileEntity;
import appeng.util.Platform;
public class IOPortContainer extends UpgradeableContainer {
@@ -111,7 +110,7 @@ public class IOPortContainer extends UpgradeableContainer {
public void detectAndSendChanges() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.setOperationMode(
(OperationMode) this.getUpgradeable().getConfigManager().getSetting(Settings.OPERATION_MODE));
this.setFullMode(
@@ -35,7 +35,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.tile.misc.InscriberRecipes;
import appeng.tile.misc.InscriberTileEntity;
import appeng.util.Platform;
/**
* @author AlgorithmX2
@@ -119,7 +118,7 @@ public class InscriberContainer extends UpgradeableContainer implements IProgres
public void detectAndSendChanges() {
this.standardDetectAndSendChanges();
if (Platform.isServer()) {
if (isServer()) {
this.maxProcessingTime = this.ti.getMaxProcessingTime();
this.processingTime = this.ti.getProcessingTime();
}
@@ -42,7 +42,7 @@ import appeng.api.networking.security.IActionHost;
import appeng.container.AEBaseContainer;
import appeng.container.ContainerLocator;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.CompressedNBTPacket;
import appeng.core.sync.packets.MEInterfaceUpdatePacket;
import appeng.helpers.DualityInterface;
import appeng.helpers.IInterfaceHost;
import appeng.helpers.InventoryAction;
@@ -52,7 +52,6 @@ import appeng.parts.reporting.InterfaceTerminalPart;
import appeng.tile.inventory.AppEngInternalInventory;
import appeng.tile.misc.InterfaceTileEntity;
import appeng.util.InventoryAdaptor;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.inv.AdaptorItemHandler;
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 detectAndSendChanges() {
if (Platform.isClient()) {
if (isClient()) {
return;
}
@@ -177,7 +176,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer {
if (!this.data.isEmpty()) {
try {
NetworkHandler.instance().sendTo(new CompressedNBTPacket(this.data),
NetworkHandler.instance().sendTo(new MEInterfaceUpdatePacket(this.data),
(ServerPlayerEntity) this.getPlayerInv().player);
} catch (final IOException e) {
// :P
@@ -22,8 +22,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.IItemHandler;
import appeng.api.config.FuzzyMode;
@@ -32,13 +30,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 {
@@ -48,38 +46,46 @@ public class LevelEmitterContainer extends UpgradeableContainer {
LevelEmitterContainer::new, LevelEmitterPart.class, SecurityPermissions.BUILD);
public static LevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketBuffer 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;
@OnlyIn(Dist.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;
}
@OnlyIn(Dist.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
@@ -115,7 +121,6 @@ public class LevelEmitterContainer extends UpgradeableContainer {
@Override
public int availableUpgrades() {
return 1;
}
@@ -123,8 +128,7 @@ public class LevelEmitterContainer extends UpgradeableContainer {
public void detectAndSendChanges() {
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));
@@ -136,15 +140,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;
@@ -162,4 +157,5 @@ public class LevelEmitterContainer extends UpgradeableContainer {
private void setLevelMode(final LevelType lvType) {
this.lvType = lvType;
}
}
@@ -73,7 +73,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> {
@@ -122,7 +121,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,7 +182,7 @@ public class MEMonitorableContainer extends AEBaseContainer
@Override
public void detectAndSendChanges() {
if (Platform.isServer()) {
if (isServer()) {
if (this.monitor != this.host
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class))) {
this.setValidContainer(false);
@@ -287,7 +286,7 @@ public class MEMonitorableContainer extends AEBaseContainer
}
private void queueInventory(final IContainerListener 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 +355,7 @@ public class MEMonitorableContainer extends AEBaseContainer
@Override
public IConfigManager getConfigManager() {
if (Platform.isServer()) {
if (isServer()) {
return this.serverCM;
}
return this.clientCM;
@@ -41,7 +41,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.items.misc.EncodedPatternItem;
import appeng.tile.crafting.MolecularAssemblerTileEntity;
import appeng.util.Platform;
public class MolecularAssemblerContainer extends UpgradeableContainer implements IProgressProvider {
@@ -147,7 +146,7 @@ public class MolecularAssemblerContainer extends UpgradeableContainer implements
public void detectAndSendChanges() {
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 detectAndSendChanges() {
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);
@@ -291,9 +291,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;
}
@@ -395,7 +395,7 @@ public class PatternTermContainer extends MEMonitorableContainer
@Override
public void detectAndSendChanges() {
super.detectAndSendChanges();
if (Platform.isServer()) {
if (isServer()) {
if (this.isCraftingMode() != this.getPatternTerminal().isCraftingRecipe()) {
this.setCraftingMode(this.getPatternTerminal().isCraftingRecipe());
this.updateOrderOfOutputSlots();
@@ -417,7 +417,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 IContainerListener listener : this.listeners) {
for (final Slot slot : this.inventorySlots) {
if (slot instanceof OptionalFakeSlot || slot instanceof FakeCraftingMatrixSlot) {
@@ -431,7 +431,7 @@ public class PatternTermContainer extends MEMonitorableContainer
this.detectAndSendChanges();
}
if (s == this.craftSlot && Platform.isClient()) {
if (s == this.craftSlot && isClient()) {
this.getAndUpdateOutput();
}
}
@@ -23,17 +23,14 @@ import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.network.PacketBuffer;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
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,60 +40,53 @@ public class PriorityContainer extends AEBaseContainer {
PriorityContainer::new, IPriorityHost.class, SecurityPermissions.BUILD);
public static PriorityContainer fromNetwork(int windowId, PlayerInventory inv, PacketBuffer 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;
@OnlyIn(Dist.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, (TileEntity) (te instanceof TileEntity ? te : null),
(IPart) (te instanceof IPart ? te : null));
this.priHost = te;
this.priorityValue = te.getPriority();
}
@OnlyIn(Dist.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 detectAndSendChanges() {
super.detectAndSendChanges();
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;
}
}
@@ -38,7 +38,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 {
@@ -148,7 +147,7 @@ public class QuartzKnifeContainer extends AEBaseContainer {
}
private void makePlate() {
if (Platform.isServer()) {
if (isServer()) {
if (!this.getItemHandler().extractItem(0, 1, false).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.SpatialIOPortTileEntity;
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 SpatialIOPortTileEntity 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 detectAndSendChanges() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.delay++;
if (this.delay > 15 && this.network != null) {
this.delay = 0;
@@ -44,7 +44,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;
@@ -124,7 +123,7 @@ public class StorageBusContainer extends UpgradeableContainer {
public void detectAndSendChanges() {
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));
@@ -51,7 +51,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 {
@@ -199,7 +198,7 @@ public class UpgradeableContainer extends AEBaseContainer implements IOptionalSl
public void detectAndSendChanges() {
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.VibrationChamberTileEntity;
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 detectAndSendChanges() {
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.detectAndSendChanges();
if (!this.wirelessTerminalGUIObject.rangeCheck()) {
if (Platform.isServer() && this.isValidContainer()) {
if (isServer() && this.isValidContainer()) {
this.getPlayerInv().player.sendMessage(PlayerMessages.OutOfRange.get(), Util.DUMMY_UUID);
}
+27
View File
@@ -31,6 +31,8 @@ import java.util.Set;
import java.util.function.DoubleSupplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.base.Strings;
import org.apache.commons.lang3.tuple.Pair;
@@ -393,6 +395,15 @@ public final class AEConfig {
return this.meteoriteDimensionWhitelist;
}
@Nullable
public String getImprovedFluidTag() {
return Strings.emptyToNull(COMMON.improvedFluidTag.get());
}
public float getImprovedFluidMultiplier() {
return COMMON.improvedFluidMultiplier.get().floatValue();
}
// Setters keep visibility as low as possible.
private static class ClientConfig {
@@ -507,6 +518,11 @@ public final class AEConfig {
public final ConfigValue<Integer> condenserMatterBallsPower;
public final ConfigValue<Integer> condenserSingularityPower;
// In-World Purification
// Settings for improved speed depending on fluid the crystal is in
public final ConfigValue<String> improvedFluidTag;
public final ConfigValue<Double> improvedFluidMultiplier;
public final Map<TickRates, ConfigValue<Integer>> tickRateMin = new HashMap<>();
public final Map<TickRates, ConfigValue<Integer>> tickRateMax = new HashMap<>();
@@ -617,6 +633,17 @@ public final class AEConfig {
tickRateMax.put(tickRate, builder.define(tickRate.name() + "Max", tickRate.getDefaultMax()));
}
builder.pop();
builder.comment("Settings for in-world purification of crystals.").push("inWorldPurification");
improvedFluidTag = builder.comment(
"A fluid tag that identifies fluids that improve crystal purification speed. Does not affect purification with water/lava.")
.define("improvedFluidTag", "");
improvedFluidMultiplier = builder
.comment("The speed multiplier to use when the crystals are submerged in the improved fluid.")
.defineInRange("improvedFluidMultiplier", 2.0, 1.0, 10.0);
builder.pop();
}
}
@@ -20,7 +20,9 @@ package appeng.core.api.definitions;
import static appeng.block.AEBaseBlock.defaultProps;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.DispenserBlock;
import net.minecraft.block.SlabBlock;
import net.minecraft.block.SoundType;
@@ -28,6 +30,7 @@ import net.minecraft.block.StairsBlock;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.ToolType;
@@ -262,18 +265,20 @@ 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(defaultProps(Material.GLASS).notSolid()))
.rendering(new BlockRenderingCustomizer() {
@Override
@OnlyIn(Dist.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderType.getCutout());
}
}).build();
AbstractBlock.IExtendedPositionPredicate<EntityType<?>> neverAllowSpawn = (p1, p2, p3, p4) -> false;
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS).block("quartz_glass", () -> {
return new QuartzGlassBlock(defaultProps(Material.GLASS).notSolid().setAllowsSpawn(neverAllowSpawn));
}).rendering(new BlockRenderingCustomizer() {
@Override
@OnlyIn(Dist.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderType.getCutout());
}
}).build();
this.quartzVibrantGlass = deco
.block("quartz_vibrant_glass",
() -> new QuartzLampBlock(defaultProps(Material.GLASS).setLightLevel(b -> 15).notSolid()))
() -> new QuartzLampBlock(defaultProps(Material.GLASS).setLightLevel(b -> 15).notSolid()
.setAllowsSpawn(neverAllowSpawn)))
.addFeatures(AEFeature.DECORATIVE_LIGHTS, AEFeature.QUARTZ_GLASS)
.rendering(new BlockRenderingCustomizer() {
@Override
@@ -22,12 +22,12 @@ import java.util.HashMap;
import java.util.Map;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
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 {
@@ -35,18 +35,19 @@ public final class LocatableRegistry implements ILocatableRegistry {
public LocatableRegistry() {
this.set = new HashMap<>();
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(this::updateLocatable);
}
@SubscribeEvent
public void updateLocatable(final LocatableEventAnnounce e) {
private void updateLocatable(final LocatableEventAnnounce e) {
if (Platform.isClient()) {
return; // IGNORE!
}
if (e.change == LocatableEvent.REGISTER) {
AELog.debug("Registering locatable %s: %s", e.target.getLocatableSerial(), e.target);
this.set.put(e.target.getLocatableSerial(), e.target);
} else if (e.change == LocatableEvent.UNREGISTER) {
AELog.debug("Unregistering locatable %s: %s", e.target.getLocatableSerial(), e.target);
this.set.remove(e.target.getLocatableSerial());
}
}
@@ -29,7 +29,6 @@ import appeng.core.sync.packets.BlockTransitionEffectPacket;
import appeng.core.sync.packets.ClickPacket;
import appeng.core.sync.packets.CompassRequestPacket;
import appeng.core.sync.packets.CompassResponsePacket;
import appeng.core.sync.packets.CompressedNBTPacket;
import appeng.core.sync.packets.ConfigButtonPacket;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.core.sync.packets.CraftRequestPacket;
@@ -39,6 +38,7 @@ import appeng.core.sync.packets.ItemTransitionEffectPacket;
import appeng.core.sync.packets.JEIRecipePacket;
import appeng.core.sync.packets.LightningPacket;
import appeng.core.sync.packets.MEFluidInventoryUpdatePacket;
import appeng.core.sync.packets.MEInterfaceUpdatePacket;
import appeng.core.sync.packets.MEInventoryUpdatePacket;
import appeng.core.sync.packets.MatterCannonPacket;
import appeng.core.sync.packets.MockExplosionPacket;
@@ -101,7 +101,7 @@ public class BasePacketHandler {
PACKET_ASSEMBLER_ANIMATION(AssemblerAnimationPacket.class, AssemblerAnimationPacket::new),
PACKET_COMPRESSED_NBT(CompressedNBTPacket.class, CompressedNBTPacket::new),
PACKET_ME_INTERFACE_UPDATE(MEInterfaceUpdatePacket.class, MEInterfaceUpdatePacket::new),
PACKET_PAINTED_ENTITY(PaintedEntityPacket.class, PaintedEntityPacket::new),
@@ -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;
@@ -98,12 +98,9 @@ public class ConfigValuePacket extends BasePacket {
final ItemStack is = player.getHeldItem(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();
@@ -118,13 +115,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")) {
@@ -192,9 +189,7 @@ public class ConfigValuePacket extends BasePacket {
public void clientPacketData(final INetworkInfo network, final PlayerEntity player) {
final Container c = player.openContainer;
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 = Minecraft.getInstance().currentScreen;
@@ -19,13 +19,9 @@
package appeng.core.sync.packets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -41,7 +37,6 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.network.NetworkDirection;
import appeng.api.storage.data.IAEFluidStack;
import appeng.core.AELog;
import appeng.core.sync.BasePacket;
import appeng.core.sync.network.INetworkInfo;
import appeng.fluids.client.gui.FluidTerminalScreen;
@@ -55,8 +50,6 @@ import appeng.fluids.util.AEFluidStack;
public class MEFluidInventoryUpdatePacket extends BasePacket {
private static final int UNCOMPRESSED_PACKET_BYTE_LIMIT = 16 * 1024 * 1024;
private static final int OPERATION_BYTE_LIMIT = 2 * 1024;
private static final int TEMP_BUFFER_SIZE = 1024;
private static final int STREAM_MASK = 0xff;
// input.
@Nullable
@@ -66,47 +59,18 @@ public class MEFluidInventoryUpdatePacket extends BasePacket {
@Nullable
private final PacketBuffer data;
@Nullable
private final GZIPOutputStream compressFrame;
private int writtenBytes = 0;
private boolean empty = true;
public MEFluidInventoryUpdatePacket(final PacketBuffer stream) {
this.data = null;
this.compressFrame = null;
this.list = new LinkedList<>();
this.ref = stream.readByte();
try (final GZIPInputStream gzReader = new GZIPInputStream(new InputStream() {
@Override
public int read() {
if (stream.readableBytes() <= 0) {
return -1;
}
return stream.readByte() & STREAM_MASK;
}
})) {
final PacketBuffer uncompressed = new PacketBuffer(Unpooled.buffer(stream.readableBytes()));
final byte[] tmp = new byte[TEMP_BUFFER_SIZE];
while (gzReader.available() != 0) {
final int bytes = gzReader.read(tmp);
if (bytes > 0) {
uncompressed.writeBytes(tmp, 0, bytes);
}
}
while (uncompressed.readableBytes() > 0) {
this.list.add(AEFluidStack.fromPacket(uncompressed));
}
} catch (IOException e) {
throw new RuntimeException("Failed to decompress packet.", e);
while (stream.readableBytes() > 0) {
this.list.add(AEFluidStack.fromPacket(stream));
}
this.empty = this.list.isEmpty();
}
@@ -121,14 +85,6 @@ public class MEFluidInventoryUpdatePacket extends BasePacket {
this.data = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
this.data.writeInt(this.getPacketID());
this.data.writeByte(this.ref);
this.compressFrame = new GZIPOutputStream(new OutputStream() {
@Override
public void write(final int value) {
MEFluidInventoryUpdatePacket.this.data.writeByte(value);
}
});
this.list = null;
}
@@ -145,28 +101,19 @@ public class MEFluidInventoryUpdatePacket extends BasePacket {
@Nullable
@Override
public IPacket<?> toPacket(NetworkDirection direction) {
try {
this.compressFrame.close();
this.configureWrite(this.data);
return super.toPacket(direction);
} catch (final IOException e) {
AELog.debug(e);
}
return null;
this.configureWrite(this.data);
return super.toPacket(direction);
}
public void appendFluid(final IAEFluidStack fs) throws IOException, BufferOverflowException {
final PacketBuffer tmp = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
fs.writeToPacket(tmp);
this.compressFrame.flush();
if (this.writtenBytes + tmp.readableBytes() > UNCOMPRESSED_PACKET_BYTE_LIMIT) {
throw new BufferOverflowException();
} else {
this.writtenBytes += tmp.readableBytes();
this.compressFrame.write(tmp.array(), 0, tmp.readableBytes());
this.data.writeBytes(tmp.array(), 0, tmp.readableBytes());
this.empty = false;
}
}
@@ -18,13 +18,7 @@
package appeng.core.sync.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import io.netty.buffer.Unpooled;
@@ -32,7 +26,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@@ -41,55 +34,24 @@ import appeng.client.gui.implementations.InterfaceTerminalScreen;
import appeng.core.sync.BasePacket;
import appeng.core.sync.network.INetworkInfo;
//TODO, this is pointless, NBT is already compressed when written to a PacketBuffer.
public class CompressedNBTPacket extends BasePacket {
public class MEInterfaceUpdatePacket extends BasePacket {
// input.
private final CompoundNBT in;
// output...
private final PacketBuffer data;
private final GZIPOutputStream compressFrame;
public CompressedNBTPacket(final PacketBuffer stream) {
public MEInterfaceUpdatePacket(final PacketBuffer stream) {
this.data = null;
this.compressFrame = null;
try (DataInputStream inStream = new DataInputStream(new GZIPInputStream(new InputStream() {
@Override
public int read() {
if (stream.readableBytes() <= 0) {
return -1;
}
return stream.readByte() & 0xff;
}
}))) {
this.in = CompressedStreamTools.read(inStream);
} catch (IOException e) {
throw new RuntimeException("Failed to decompress packet.", e);
}
this.in = stream.readCompoundTag();
}
// api
public CompressedNBTPacket(final CompoundNBT din) throws IOException {
public MEInterfaceUpdatePacket(final CompoundNBT din) throws IOException {
this.in = null;
this.data = new PacketBuffer(Unpooled.buffer(2048));
this.data.writeInt(this.getPacketID());
this.in = din;
this.compressFrame = new GZIPOutputStream(new OutputStream() {
@Override
public void write(final int value) {
CompressedNBTPacket.this.data.writeByte(value);
}
});
CompressedStreamTools.write(din, new DataOutputStream(this.compressFrame));
this.compressFrame.close();
this.data.writeCompoundTag(din);
this.configureWrite(this.data);
}
@@ -19,12 +19,10 @@
package appeng.core.sync.packets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -45,7 +43,6 @@ import appeng.client.gui.implementations.CraftConfirmScreen;
import appeng.client.gui.implementations.CraftingCPUScreen;
import appeng.client.gui.implementations.MEMonitorableScreen;
import appeng.client.gui.implementations.NetworkStatusScreen;
import appeng.core.AELog;
import appeng.core.sync.BasePacket;
import appeng.core.sync.network.INetworkInfo;
import appeng.util.item.AEItemStack;
@@ -53,8 +50,6 @@ import appeng.util.item.AEItemStack;
public class MEInventoryUpdatePacket extends BasePacket {
private static final int UNCOMPRESSED_PACKET_BYTE_LIMIT = 16 * 1024 * 1024;
private static final int OPERATION_BYTE_LIMIT = 2 * 1024;
private static final int TEMP_BUFFER_SIZE = 1024;
private static final int STREAM_MASK = 0xff;
// input.
@Nullable
@@ -64,48 +59,18 @@ public class MEInventoryUpdatePacket extends BasePacket {
@Nullable
private final PacketBuffer data;
@Nullable
private final GZIPOutputStream compressFrame;
private int writtenBytes = 0;
private boolean empty = true;
public MEInventoryUpdatePacket(final PacketBuffer stream) {
this.data = null;
this.compressFrame = null;
this.list = new ArrayList<>();
this.ref = stream.readByte();
// int originalBytes = stream.readableBytes();
try (GZIPInputStream gzReader = new GZIPInputStream(new InputStream() {
@Override
public int read() {
if (stream.readableBytes() <= 0) {
return -1;
}
return stream.readByte() & STREAM_MASK;
}
})) {
final PacketBuffer uncompressed = new PacketBuffer(Unpooled.buffer(stream.readableBytes()));
final byte[] tmp = new byte[TEMP_BUFFER_SIZE];
while (gzReader.available() != 0) {
final int bytes = gzReader.read(tmp);
if (bytes > 0) {
uncompressed.writeBytes(tmp, 0, bytes);
}
}
while (uncompressed.readableBytes() > 0) {
this.list.add(AEItemStack.fromPacket(uncompressed));
}
} catch (IOException e) {
throw new RuntimeException("Failed to decompress packet.", e);
while (stream.readableBytes() > 0) {
this.list.add(AEItemStack.fromPacket(stream));
}
this.empty = this.list.isEmpty();
}
@@ -120,14 +85,6 @@ public class MEInventoryUpdatePacket extends BasePacket {
this.data = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
this.data.writeInt(this.getPacketID());
this.data.writeByte(this.ref);
this.compressFrame = new GZIPOutputStream(new OutputStream() {
@Override
public void write(final int value) {
MEInventoryUpdatePacket.this.data.writeByte(value);
}
});
this.list = null;
}
@@ -156,28 +113,19 @@ public class MEInventoryUpdatePacket extends BasePacket {
@Nullable
@Override
public IPacket<?> toPacket(NetworkDirection direction) {
try {
this.compressFrame.close();
this.configureWrite(this.data);
return super.toPacket(direction);
} catch (final IOException e) {
AELog.debug(e);
}
return null;
this.configureWrite(this.data);
return super.toPacket(direction);
}
public void appendItem(final IAEItemStack is) throws IOException, BufferOverflowException {
final PacketBuffer tmp = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
is.writeToPacket(tmp);
this.compressFrame.flush();
if (this.writtenBytes + tmp.readableBytes() > UNCOMPRESSED_PACKET_BYTE_LIMIT) {
throw new BufferOverflowException();
} else {
this.writtenBytes += tmp.readableBytes();
this.compressFrame.write(tmp.array(), 0, tmp.readableBytes());
this.data.writeBytes(tmp.array(), 0, tmp.readableBytes());
this.empty = false;
}
}
@@ -20,11 +20,8 @@ package appeng.decorative.solid;
import net.minecraft.block.AbstractGlassBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.material.Material;
import net.minecraft.util.Direction;
import appeng.helpers.AEMaterials;
public class QuartzGlassBlock extends AbstractGlassBlock {
public QuartzGlassBlock(Properties 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;
}
@@ -29,7 +29,6 @@ import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ToolType;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import appeng.block.AEBaseBlock;
import appeng.core.worlddata.WorldData;
@@ -43,11 +42,10 @@ public class SkyStoneBlock extends AEBaseBlock {
super(props);
this.type = type;
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(this::breakFaster);
}
@SubscribeEvent
public void breakFaster(final PlayerEvent.BreakSpeed event) {
private void breakFaster(final PlayerEvent.BreakSpeed event) {
if (event.getState().getBlock() == this && event.getPlayer() != null) {
final ItemStack is = event.getPlayer().getItemStackFromSlot(EquipmentSlotType.MAINHAND);
int level = -1;
@@ -19,13 +19,14 @@
package appeng.entity;
import net.minecraft.block.BlockState;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import appeng.api.features.AEFeature;
@@ -35,12 +36,27 @@ import appeng.client.EffectType;
import appeng.core.AEConfig;
import appeng.core.AppEng;
import appeng.items.misc.CrystalSeedItem;
import appeng.util.Platform;
public final 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) {
@@ -56,126 +72,140 @@ public final class GrowingCrystalEntity extends AEBaseItemEntity {
public void tick() {
super.tick();
final ItemStack is = this.getItem();
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.getItem();
final Item gc = is.getItem();
final int x = MathHelper.floor(this.getPosX());
final int y = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int z = MathHelper.floor(this.getPosZ());
if (gc instanceof IGrowableCrystal) // if it changes this just stops being an issue...
{
final int j = MathHelper.floor(this.getPosX());
final int i = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int k = MathHelper.floor(this.getPosZ());
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.isRemote()) {
// 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.proxy.spawnEffect(EffectType.Vibrant, this.world, this.getPosX(), this.getPosY() + 0.2,
this.getPosZ(), 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.proxy.spawnEffect(EffectType.Vibrant, this.world, this.getPosX(), this.getPosY() + 0.2,
this.getPosZ(), 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.setItem(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.setItem(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 TileEntity te = this.world.getTileEntity(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.func_239622_a_(pos, direction))) {
count++;
}
}
return count;
}
private boolean isPoweredAccelerator(BlockPos pos) {
final TileEntity te = this.world.getTileEntity(pos);
return te instanceof ICrystalGrowthAccelerator && ((ICrystalGrowthAccelerator) te).isPowered();
}
// Don't let seeds "float" on water surface
@Override
protected void applyFloatMotion() {
ItemStack item = getItem();
// 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) {
Vector3d v = this.getMotion();
// 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.setMotion(v.x * 0.99, v.y + yAccel, v.z * 0.99);
return;
}
super.applyFloatMotion();
@@ -37,15 +37,18 @@ public class FacadeContainer implements IFacadeContainer {
private final int facades = 6;
private final CableBusStorage storage;
private final Runnable changeCallback;
public FacadeContainer(final CableBusStorage cbs) {
public FacadeContainer(final CableBusStorage cbs, Runnable changeCallback) {
this.storage = cbs;
this.changeCallback = changeCallback;
}
@Override
public boolean addFacade(final IFacadePart a) {
if (this.getFacade(a.getSide()) == null) {
this.storage.setFacade(a.getSide().ordinal(), a);
this.notifyChange();
return true;
}
return false;
@@ -56,6 +59,7 @@ public class FacadeContainer implements IFacadeContainer {
if (side != null && side != AEPartLocation.INTERNAL) {
if (this.storage.getFacade(side.ordinal()) != null) {
this.storage.setFacade(side.ordinal(), null);
this.notifyChange();
if (host != null) {
host.markForUpdate();
}
@@ -84,6 +88,7 @@ public class FacadeContainer implements IFacadeContainer {
for (int x = 0; x < this.facades; x++) {
this.storage.setFacade(x, newFacades[x]);
}
this.notifyChange();
}
@Override
@@ -175,4 +180,9 @@ public class FacadeContainer implements IFacadeContainer {
}
return true;
}
private void notifyChange() {
this.changeCallback.run();
}
}
@@ -13,8 +13,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;
@@ -31,17 +29,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);
container.setTextField(this.level);
this.level.addButtons(children::add, this::addButton);
this.level.setValue(container.getReportingValue());
this.level.setOnChange(this::saveReportingValue);
this.level.setOnConfirm(this::closeScreen);
this.changeFocus(true);
final int y = 40;
final int x = 80 + 57;
this.guiSlots.add(new FluidSlotWidget(this.container.getFluidConfigInventory(), 0, 0, x, y));
}
private void saveReportingValue() {
this.level.getLongValue().ifPresent(container::setReportingValue);
}
@Override
protected void addButtons() {
this.redstoneMode = new ServerSettingToggleButton<>(this.guiLeft - 18, this.guiTop + 28,
@@ -82,8 +87,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)));
}
}
@@ -71,7 +71,7 @@ public abstract class FluidConfigurableContainer extends UpgradeableContainer im
@Override
protected void standardDetectAndSendChanges() {
if (Platform.isServer()) {
if (isServer()) {
this.getSyncHelper().sendDiff(this.listeners);
// clear out config items that are no longer valid (eg capacity upgrade removed)
@@ -81,7 +81,7 @@ public class FluidInterfaceContainer extends FluidConfigurableContainer {
public void detectAndSendChanges() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.tankSync.sendDiff(this.listeners);
}
@@ -4,16 +4,14 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
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;
@@ -25,34 +23,41 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
FluidLevelEmitterContainer::new, FluidLevelEmitterPart.class, SecurityPermissions.BUILD);
public static FluidLevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketBuffer 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;
@OnlyIn(Dist.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;
}
@OnlyIn(Dist.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 +79,7 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
public void detectAndSendChanges() {
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 +87,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();
@@ -128,7 +128,7 @@ public class FluidStorageBusContainer extends FluidConfigurableContainer {
public void detectAndSendChanges() {
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));
@@ -119,7 +119,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));
@@ -191,7 +191,7 @@ public class FluidTerminalContainer extends AEBaseContainer
}
private void queueInventory(final IContainerListener 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();
@@ -216,14 +216,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;
}
@@ -246,7 +246,7 @@ public class FluidTerminalContainer extends AEBaseContainer
@Override
public void detectAndSendChanges() {
if (Platform.isServer()) {
if (isServer()) {
if (this.monitor != this.terminal
.getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class))) {
this.setValidContainer(false);
@@ -96,16 +96,15 @@ public class FluidImportBusPart extends SharedFluidBusPart {
}
final TileEntity te = this.getConnectedTE();
LazyOptional<IFluidHandler> fhOpt = LazyOptional.empty();
if (te != null) {
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite());
}
if (fhOpt.isPresent()) {
try {
final IFluidHandler fh = fhOpt.orElse(null);
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory(this.getChannel());
LazyOptional<IFluidHandler> fhOpt = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
this.getSide().getFacing().getOpposite());
if (fhOpt.isPresent()) {
try {
final IFluidHandler fh = fhOpt.orElseThrow(() -> new IllegalStateException());
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory(this.getChannel());
if (fh != null) {
final FluidStack fluidStack = fh.drain(this.calculateAmountToSend(), FluidAction.SIMULATE);
if (this.filterEnabled() && !this.isInFilter(fluidStack)) {
@@ -128,12 +127,11 @@ public class FluidImportBusPart extends SharedFluidBusPart {
}
return TickRateModulation.IDLE;
} catch (GridAccessException e) {
// skip
}
} catch (GridAccessException e) {
e.printStackTrace();
}
}
return TickRateModulation.SLEEP;
}
@@ -13,6 +13,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;
@@ -31,13 +39,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));
@@ -59,7 +67,7 @@ public class AEFluidInventory implements IAEFluidTank {
if (slot >= 0 && slot < this.getSlots()) {
return this.fluids[slot];
}
return null;
return EMPTY_AE_FLUIDSTACK;
}
@Override
@@ -78,12 +86,12 @@ public class AEFluidInventory implements IAEFluidTank {
if (tank < 0 || tank >= fluids.length) {
return FluidStack.EMPTY;
}
return fluids[tank] == null ? FluidStack.EMPTY : fluids[tank].getFluidStack();
return fluids[tank] == EMPTY_AE_FLUIDSTACK ? FluidStack.EMPTY : fluids[tank].getFluidStack();
}
@Override
public int getTankCapacity(int tank) {
return Math.min(this.capacity, Integer.MAX_VALUE);
return this.capacity;
}
@Override
@@ -98,20 +106,20 @@ 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();
}
amountToStore = Math.min(amountToStore, resource.getAmount());
if (doFill) {
if (fluid == null) {
if (fluid == EMPTY_AE_FLUIDSTACK) {
this.setFluidInSlot(slot, AEFluidStack.fromFluidStack(resource));
} else {
fluid.setStackSize(fluid.getStackSize() + amountToStore);
@@ -141,14 +149,14 @@ public class AEFluidInventory implements IAEFluidTank {
int amountToStore = this.capacity;
if (fluid != null) {
if (fluid != EMPTY_AE_FLUIDSTACK) {
amountToStore -= fluid.getStackSize();
}
amountToStore = Math.min(amountToStore, resource.getAmount());
if (action == FluidAction.EXECUTE) {
if (fluid == null) {
if (fluid == EMPTY_AE_FLUIDSTACK) {
this.setFluidInSlot(slot, AEFluidStack.fromFluidStack(resource));
} else {
fluid.setStackSize(fluid.getStackSize() + amountToStore);
@@ -170,7 +178,7 @@ public class AEFluidInventory implements IAEFluidTank {
FluidStack totalDrained = FluidStack.EMPTY;
for (int slot = 0; slot < this.getSlots(); ++slot) {
FluidStack drain = this.drain(slot, resource, action == FluidAction.EXECUTE);
if (drain != null) {
if (!drain.isEmpty()) {
if (totalDrained.isEmpty()) {
totalDrained = drain;
} else {
@@ -205,7 +213,7 @@ public class AEFluidInventory implements IAEFluidTank {
FluidStack copy = totalDrained.copy();
copy.setAmount(toDrain);
FluidStack drain = this.drain(slot, copy, action == FluidAction.EXECUTE);
if (drain != null) {
if (!drain.isEmpty()) {
totalDrained.setAmount(totalDrained.getAmount() + drain.getAmount());
toDrain -= drain.getAmount();
}
@@ -220,7 +228,7 @@ public class AEFluidInventory implements IAEFluidTank {
private int indexOfFluid(FluidStack resource) {
for (int slot = 0; slot < fluids.length; slot++) {
if (fluids[slot] != null && fluids[slot].getFluidStack().isFluidEqual(resource)) {
if (fluids[slot] != EMPTY_AE_FLUIDSTACK && fluids[slot].getFluidStack().isFluidEqual(resource)) {
return slot;
}
}
@@ -229,7 +237,7 @@ public class AEFluidInventory implements IAEFluidTank {
private int indexOfEmptySlot() {
for (int slot = 0; slot < fluids.length; slot++) {
if (fluids[slot] == null) {
if (fluids[slot] == EMPTY_AE_FLUIDSTACK) {
return slot;
}
}
@@ -238,16 +246,16 @@ public class AEFluidInventory implements IAEFluidTank {
public FluidStack drain(final int slot, final FluidStack 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 FluidStack.EMPTY;
}
return this.drain(slot, resource.getAmount(), doDrain);
}
public FluidStack 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 FluidStack.EMPTY;
}
int drained = maxDrain;
@@ -259,7 +267,7 @@ public class AEFluidInventory implements IAEFluidTank {
if (doDrain) {
fluid.setStackSize(fluid.getStackSize() - drained);
if (fluid.getStackSize() <= 0) {
this.fluids[slot] = null;
this.fluids[slot] = EMPTY_AE_FLUIDSTACK;
}
this.onContentChanged(slot);
}
@@ -277,7 +285,7 @@ public class AEFluidInventory implements IAEFluidTank {
try {
final CompoundNBT c = new CompoundNBT();
if (this.fluids[x] != null) {
if (this.fluids[x] != EMPTY_AE_FLUIDSTACK) {
this.fluids[x].writeToNBT(c);
}
@@ -24,15 +24,19 @@ import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.BlockState;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.Entity;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ITag;
import net.minecraft.util.IItemProvider;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
@@ -41,6 +45,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import appeng.api.implementations.items.IGrowableCrystal;
import appeng.core.AEConfig;
import appeng.core.localization.ButtonToolTips;
import appeng.entity.GrowingCrystalEntity;
import appeng.items.AEBaseItem;
@@ -94,8 +99,24 @@ public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal {
}
@Override
public float getMultiplier(final Block blk, final Material mat) {
return 0.5f;
public float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos) {
// Check for the improved fluid tag and return the improved multiplier
String improvedFluidTagName = AEConfig.instance().getImprovedFluidTag();
if (improvedFluidTagName != null) {
ITag<Fluid> tag = FluidTags.getCollection().get(new ResourceLocation(improvedFluidTagName));
if (tag != null && state.getFluidState().isTagged(tag)) {
return AEConfig.instance().getImprovedFluidMultiplier();
}
}
// Check for the normal supported fluid
if (world != null && world.func_234923_W_() == World.field_234919_h_) {
// In the nether, use Lava as the "normal" fluid
return state.getFluidState().isTagged(FluidTags.LAVA) ? 1 : 0;
} else {
return state.getFluidState().isTagged(FluidTags.WATER) ? 1 : 0;
}
}
@Override
@@ -324,21 +324,21 @@ public class ColorApplicatorItem extends AEBasePoweredItem
return w.setBlockState(pos, newState);
}
if (blk instanceof CableBusBlock && p != null) {
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
}
TileEntity be = w.getTileEntity(pos);
if (be instanceof IColorableTile) {
IColorableTile ct = (IColorableTile) be;
AEColor c = ct.getColor();
if (c != newColor) {
ct.recolourBlock(side, newColor, null);
ct.recolourBlock(side, newColor, p);
return true;
}
return false;
}
if (blk instanceof CableBusBlock && p != null) {
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
}
return false;
}
+2 -1
View File
@@ -327,8 +327,9 @@ public class GridNode implements IGridNode, IPathItem {
@Override
public void setPlayerID(final int playerID) {
if (playerID >= 0) {
if (playerID >= 0 && this.playerID != playerID) {
this.playerID = playerID;
gridProxy.onGridNotification(GridNotification.OWNER_CHANGED);
}
}
+14 -2
View File
@@ -81,8 +81,16 @@ public class SecurityCache implements ISecurityGrid {
private void updateSecurityKey() {
final long lastCode = this.securityKey;
/**
* Placing a security station will propagate the security station's owner to all
* connected grid nodes to prevent the network from not reforming due to
* different owners later.
*/
int newOwner = -1;
if (this.securityProvider.size() == 1) {
this.securityKey = this.securityProvider.get(0).getSecurityKey();
ISecurityProvider securityProvider = this.securityProvider.get(0);
this.securityKey = securityProvider.getSecurityKey();
newOwner = securityProvider.getOwner();
} else {
this.securityKey = -1;
}
@@ -90,7 +98,11 @@ public class SecurityCache implements ISecurityGrid {
if (lastCode != this.securityKey) {
this.getGrid().postEvent(new MENetworkSecurityChange());
for (final IGridNode n : this.getGrid().getNodes()) {
((GridNode) n).setLastSecurityKey(this.securityKey);
GridNode gridNode = (GridNode) n;
gridNode.setLastSecurityKey(this.securityKey);
if (gridNode.getPlayerID() != newOwner) {
gridNode.setPlayerID(newOwner);
}
}
}
}
@@ -22,7 +22,6 @@ import java.util.Iterator;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
@@ -159,12 +158,16 @@ public class QuantumCluster implements ILocatable, IAECluster {
if (qc != null) {
final World theWorld = qc.center.getWorld();
if (!qc.isDestroyed) {
ChunkPos cPos = new ChunkPos(qc.center.getPos());
if (theWorld.getChunkProvider().isChunkLoaded(cPos)) {
// In future versions, we might actually want to delay the entire registration
// until the center
// tile begins ticking normally.
if (theWorld.isBlockLoaded(qc.center.getPos())) {
final World cur = theWorld.getServer().getWorld(theWorld.func_234923_W_());
final TileEntity te = theWorld.getTileEntity(qc.center.getPos());
return te != qc.center || theWorld != cur;
} else {
AELog.warn("Found a registered QNB with serial %s whose chunk seems to be unloaded: %s", qe, qc);
}
}
}
@@ -275,4 +278,17 @@ public class QuantumCluster implements ILocatable, IAECluster {
private void setRing(final QuantumBridgeTileEntity[] ring) {
this.Ring = ring;
}
@Override
public String toString() {
if (center == null) {
return "QuantumCluster{no-center}";
}
World world = center.getWorld();
BlockPos pos = center.getPos();
return "QuantumCluster{" + world + "," + pos + "}";
}
}
@@ -167,7 +167,6 @@ public class AENetworkProxy implements IGridBlock {
* short cut!
*
* @return grid of node
*
* @throws GridAccessException of node or grid is null
*/
public IGrid getGrid() throws GridAccessException {
@@ -280,6 +279,11 @@ public class AENetworkProxy implements IGridBlock {
@Override
public void onGridNotification(final GridNotification notification) {
if (notification == GridNotification.OWNER_CHANGED) {
gp.saveChanges();
return;
}
if (this.gp instanceof CablePart) {
((CablePart) this.gp).markForUpdate();
}
@@ -28,4 +28,7 @@ public interface IGridProxyable extends IGridHost {
DimensionalCoord getLocation();
void gridChanged();
void saveChanges();
}
@@ -41,7 +41,6 @@ import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
@@ -82,7 +81,6 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
private final EnumSet<LayerFlags> myLayerFlags = EnumSet.noneOf(LayerFlags.class);
private YesNo hasRedstone = YesNo.UNDECIDED;
private IPartHost tcb;
// TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not.
private boolean requiresDynamicRender = false;
private boolean inWorld = false;
// Cached collision shape for living entities
@@ -125,7 +123,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
@Override
public IFacadeContainer getFacadeContainer() {
return new FacadeContainer(this);
return new FacadeContainer(this, this::invalidateShapes);
}
@Override
@@ -1096,11 +1094,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
}
}
VoxelShape shape = VoxelShapes.empty();
for (final AxisAlignedBB bx : boxes) {
shape = VoxelShapes.or(shape, VoxelShapes.create(bx));
}
return shape;
return VoxelShapeCache.get(boxes);
}
private void invalidateShapes() {
@@ -0,0 +1,81 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.parts;
import java.util.List;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
/**
* While creation of a {@link VoxelShape} with
* {@link VoxelShapes#create(AxisAlignedBB)} is fast enough, combining voxel
* shapes with {@link VoxelShapes#or(VoxelShape, VoxelShape)} or any other
* combination method, as well as {@link VoxelShape#simplify()} are <b>extremely
* slow</b>. For example: Creating a VoxelShape for a list of 5 bounding boxes
* 10,000 times takes about 1.7 seconds.
*
* <p>
* To reduce the impact of this on cables, we introduce a global voxel shape
* cache so that cables can share their combined voxel shapes better.
*/
final class VoxelShapeCache {
// Why using a List here should not make much of a difference vs. using a Set:
// The part's bounding box depends on the side it is attached to, and the sides
// are iterated over in a fixed order, meaning the order of bounding boxes
// should
// be the same for a same set of parts.
private static final LoadingCache<List<AxisAlignedBB>, VoxelShape> CACHE = CacheBuilder.newBuilder()//
.maximumSize(10000L)//
.build(new CacheLoader<List<AxisAlignedBB>, VoxelShape>() {
@Override
public VoxelShape load(List<AxisAlignedBB> key) {
return create(key);
}
});
private VoxelShapeCache() {
}
public static VoxelShape get(List<AxisAlignedBB> boxes) {
return CACHE.getUnchecked(boxes);
}
private static VoxelShape create(List<AxisAlignedBB> boxes) {
if (boxes.isEmpty()) {
return VoxelShapes.empty();
}
int i = 0;
VoxelShape shape = VoxelShapes.create(boxes.get(i));
for (; i < boxes.size(); i++) {
AxisAlignedBB box = boxes.get(i);
shape = VoxelShapes.combine(shape, VoxelShapes.create(box), IBooleanFunction.OR);
}
return shape.simplify();
}
}
@@ -39,10 +39,13 @@ import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.model.data.IModelData;
import net.minecraftforge.items.IItemHandler;
@@ -253,9 +256,11 @@ public class AEBaseTileEntity extends TileEntity implements IOrientable, ICommon
if (this.renderFragment > 0) {
this.renderFragment |= 1;
} else {
// Clearing the cached model-data is always harmless regardless of status
this.requestModelDataUpdate();
// TODO: Optimize Network Load
if (this.world != null) {
this.requestModelDataUpdate();
if (this.world != null && !this.isRemoved() && !notLoaded()) {
boolean alreadyUpdated = false;
// Let the block update it's own state with our internal state changes
@@ -304,6 +309,7 @@ public class AEBaseTileEntity extends TileEntity implements IOrientable, ICommon
this.up = inUp;
this.markForUpdate();
Platform.notifyBlocksOfNeighbors(this.world, this.pos);
this.saveChanges();
}
public void onPlacement(BlockItemUseContext context) {
@@ -367,7 +373,6 @@ public class AEBaseTileEntity extends TileEntity implements IOrientable, ICommon
* null means nothing to store...
*
* @param from source of settings
*
* @return compound of source
*/
public CompoundNBT downloadSettings(final SettingsFrom from) {
@@ -456,4 +461,15 @@ public class AEBaseTileEntity extends TileEntity implements IOrientable, ICommon
return new AEModelData(up, forward);
}
/**
* AE Tile Entities will generally confine themselves to rendering within the
* bounding block. Forge however would retrieve the collision box here, which is
* very expensive.
*/
@OnlyIn(Dist.CLIENT)
@Override
public AxisAlignedBB getRenderBoundingBox() {
return new AxisAlignedBB(pos, pos.add(1, 1, 1));
}
}
@@ -78,7 +78,7 @@ public class CraftingStorageTileEntity extends CraftingTileEntity {
return 0;
}
final AbstractCraftingUnitBlock unit = (AbstractCraftingUnitBlock) this.world.getBlockState(this.pos)
final AbstractCraftingUnitBlock<?> unit = (AbstractCraftingUnitBlock<?>) this.world.getBlockState(this.pos)
.getBlock();
switch (unit.type) {
default:
@@ -80,6 +80,10 @@ public abstract class AENetworkInvTileEntity extends AEBaseInvTileEntity impleme
@Override
public void remove() {
super.remove();
// if the cached block state is not current, the following method may call
// markForUpdate
// and cause the block state to be changed back to non-air
updateContainingBlockInfo();
this.getProxy().remove();
}
@@ -125,7 +125,7 @@ public final class AEItemStack extends AEStack<IAEItemStack> implements IAEItemS
buffer.writeBoolean(this.isCraftable());
buffer.writeVarLong(this.getStackSize());
buffer.writeVarLong(this.getCountRequestable());
buffer.writeItemStack(getDefinition());
buffer.writeItemStack(getDefinition(), false);
}
@Override
@@ -29,6 +29,7 @@ import net.minecraft.nbt.CompoundNBT;
import appeng.api.config.FuzzyMode;
final class AESharedItemStack implements Comparable<AESharedItemStack> {
private final ItemStack itemStack;
private final int itemId;
private final int itemDamage;
@@ -97,7 +98,8 @@ final class AESharedItemStack implements Comparable<AESharedItemStack> {
return damageValue;
}
return 0;
return System.identityHashCode(this.getDefinition().getTag())
- System.identityHashCode(b.getDefinition().getTag());
}
private int makeHashCode() {
@@ -183,4 +185,5 @@ final class AESharedItemStack implements Comparable<AESharedItemStack> {
}
}
}
@@ -289,6 +289,8 @@
"gui.appliedenergistics2.With": "with",
"gui.appliedenergistics2.Yellow": "Yellow",
"gui.appliedenergistics2.Yes": "Yes",
"gui.appliedenergistics2.validation.InvalidNumber": "Please enter a number",
"gui.appliedenergistics2.validation.NumberLessThanMinValue": "Please enter a number greater than or equal to %d",
"gui.appliedenergistics2.inWorldCraftingPresses": "Crafting Presses are found in the center of meteorites which can be found in around the world, they can be located by using a meteorite compass.",
"gui.appliedenergistics2.inWorldFluix": "Drop 1 Charged Certus Quartz + 1 Nether Quartz + Redstone Dust into a puddle next to one another and wait a moment to receive 2 Fluix Crystals.",
"gui.appliedenergistics2.inWorldPurificationCertus": "Drop a Certus Quartz Seed made from Certus Quartz Dust and Sand into a puddle of water; to make the process faster add Crystal Growth Accelerators.",
@@ -1,3 +1 @@
{
"parent": "appliedenergistics2:block/cable_bus"
}
{}

Some files were not shown because too many files have changed in this diff Show More