Compare commits

...

69 Commits

Author SHA1 Message Date
Sebastian Hartte 5da5315a5b Post-merge fixes. Reformat. Optimize Imports. 2020-09-20 14:47:25 +02:00
Sebastian Hartte f3c44c64c2 Merge remote-tracking branch 'origin/master' into fabric/master
# Conflicts:
#	build.gradle
#	gradle.properties
#	src/api/java/appeng/api/util/DimensionalCoord.java
#	src/main/java/appeng/block/paint/PaintSplotchesBakedModel.java
#	src/main/java/appeng/bootstrap/BlockDefinitionBuilder.java
#	src/main/java/appeng/client/gui/AEBaseScreen.java
#	src/main/java/appeng/client/gui/implementations/CraftAmountScreen.java
#	src/main/java/appeng/client/gui/implementations/CraftConfirmScreen.java
#	src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java
#	src/main/java/appeng/client/gui/implementations/InterfaceScreen.java
#	src/main/java/appeng/client/gui/implementations/InterfaceTerminalScreen.java
#	src/main/java/appeng/client/gui/implementations/LevelEmitterScreen.java
#	src/main/java/appeng/client/gui/implementations/NumberEntryWidget.java
#	src/main/java/appeng/client/gui/implementations/PriorityScreen.java
#	src/main/java/appeng/client/gui/widgets/IconButton.java
#	src/main/java/appeng/client/gui/widgets/NumberBox.java
#	src/main/java/appeng/client/gui/widgets/SettingToggleButton.java
#	src/main/java/appeng/client/gui/widgets/TabButton.java
#	src/main/java/appeng/client/render/DelegateBakedModel.java
#	src/main/java/appeng/client/render/DummyFluidDispatcherBakedModel.java
#	src/main/java/appeng/client/render/FacadeBakedItemModel.java
#	src/main/java/appeng/client/render/FacadeDispatcherBakedModel.java
#	src/main/java/appeng/client/render/cablebus/P2PTunnelFrequencyBakedModel.java
#	src/main/java/appeng/client/render/crafting/EncodedPatternBakedModel.java
#	src/main/java/appeng/client/render/model/AutoRotatingBakedModel.java
#	src/main/java/appeng/client/render/model/BiometricCardBakedModel.java
#	src/main/java/appeng/client/render/model/ColorApplicatorBakedModel.java
#	src/main/java/appeng/client/render/model/GlassBakedModel.java
#	src/main/java/appeng/client/render/model/MemoryCardBakedModel.java
#	src/main/java/appeng/client/render/model/SkyCompassBakedModel.java
#	src/main/java/appeng/client/render/tesr/ChestTileEntityRenderer.java
#	src/main/java/appeng/container/AEBaseContainer.java
#	src/main/java/appeng/container/ContainerLocator.java
#	src/main/java/appeng/container/guisync/SyncData.java
#	src/main/java/appeng/container/implementations/CellWorkbenchContainer.java
#	src/main/java/appeng/container/implementations/CondenserContainer.java
#	src/main/java/appeng/container/implementations/ContainerHelper.java
#	src/main/java/appeng/container/implementations/CraftConfirmContainer.java
#	src/main/java/appeng/container/implementations/CraftingCPUContainer.java
#	src/main/java/appeng/container/implementations/CraftingCPURecord.java
#	src/main/java/appeng/container/implementations/CraftingStatusContainer.java
#	src/main/java/appeng/container/implementations/IOPortContainer.java
#	src/main/java/appeng/container/implementations/InscriberContainer.java
#	src/main/java/appeng/container/implementations/InterfaceTerminalContainer.java
#	src/main/java/appeng/container/implementations/LevelEmitterContainer.java
#	src/main/java/appeng/container/implementations/MEMonitorableContainer.java
#	src/main/java/appeng/container/implementations/MolecularAssemblerContainer.java
#	src/main/java/appeng/container/implementations/PatternTermContainer.java
#	src/main/java/appeng/container/implementations/PriorityContainer.java
#	src/main/java/appeng/container/implementations/QuartzKnifeContainer.java
#	src/main/java/appeng/container/implementations/SpatialIOPortContainer.java
#	src/main/java/appeng/container/implementations/VibrationChamberContainer.java
#	src/main/java/appeng/container/implementations/WirelessTermContainer.java
#	src/main/java/appeng/core/AEConfig.java
#	src/main/java/appeng/core/AppEngBootstrap.java
#	src/main/java/appeng/core/Registration.java
#	src/main/java/appeng/core/api/definitions/ApiItems.java
#	src/main/java/appeng/core/features/registries/WorldGenRegistry.java
#	src/main/java/appeng/core/sync/network/NetworkHandler.java
#	src/main/java/appeng/core/worlddata/WorldData.java
#	src/main/java/appeng/datagen/providers/loot/BlockDropProvider.java
#	src/main/java/appeng/debug/ChunkLoaderBlockEntity.java
#	src/main/java/appeng/debug/ReplicatorCardItem.java
#	src/main/java/appeng/entity/GrowingCrystalEntity.java
#	src/main/java/appeng/fluids/client/gui/FluidLevelEmitterScreen.java
#	src/main/java/appeng/fluids/client/gui/FluidTerminalScreen.java
#	src/main/java/appeng/fluids/container/FluidConfigurableContainer.java
#	src/main/java/appeng/fluids/container/FluidInterfaceContainer.java
#	src/main/java/appeng/fluids/container/FluidLevelEmitterContainer.java
#	src/main/java/appeng/fluids/container/FluidTerminalContainer.java
#	src/main/java/appeng/fluids/util/AEFluidInventory.java
#	src/main/java/appeng/items/misc/CrystalSeedItem.java
#	src/main/java/appeng/items/misc/CrystalSeedRendering.java
#	src/main/java/appeng/items/storage/SpatialStorageCellItem.java
#	src/main/java/appeng/me/cluster/implementations/QuantumCluster.java
#	src/main/java/appeng/mixins/feature/ConfiguredFeaturesAccessor.java
#	src/main/java/appeng/mixins/spatial/BiomesMixin.java
#	src/main/java/appeng/mixins/spatial/DimensionTypeMixin.java
#	src/main/java/appeng/mixins/spatial/EntityMixin.java
#	src/main/java/appeng/mixins/spatial/SkyRenderMixin.java
#	src/main/java/appeng/mixins/structure/BiomeAccessor.java
#	src/main/java/appeng/mixins/structure/GenerationSettingsAccessor.java
#	src/main/java/appeng/parts/automation/FormationPlanePart.java
#	src/main/java/appeng/parts/automation/PlaneBakedModel.java
#	src/main/java/appeng/server/subcommands/SpatialStorageCommand.java
#	src/main/java/appeng/server/subcommands/TestMeteoritesCommand.java
#	src/main/java/appeng/server/subcommands/TestOreGenCommand.java
#	src/main/java/appeng/spatial/SpatialStorageBiome.java
#	src/main/java/appeng/spatial/SpatialStorageChunkGenerator.java
#	src/main/java/appeng/spatial/SpatialStorageDimensionIds.java
#	src/main/java/appeng/spatial/SpatialStorageHelper.java
#	src/main/java/appeng/util/Platform.java
#	src/main/java/appeng/worldgen/BiomeModifier.java
#	src/main/java/appeng/worldgen/meteorite/MeteoriteStructureStart.java
#	src/main/java/appeng/worldgen/meteorite/fallout/FalloutCopy.java
#	src/main/resources/assets/appliedenergistics2/textures/guis/states.png
2020-09-20 14:11:22 +02:00
Sebastian Hartte b8f48407cd Fix tooltip rendering. 2020-09-20 01:06:53 +02:00
Sebastian Hartte 489b0f635e Merge remote-tracking branch 'origin/8.0.x-1.16.1' 2020-09-20 00:42:52 +02:00
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
Sebastian Hartte cacd824db3 Remove unused field indicating availability of client classes. 2020-09-19 18:20:11 +02:00
yueh 876bb52087 Update forge mappings to 20200916-1.16.2 (#4749) 2020-09-19 17:32:23 +02:00
Sebastian Hartte 1b4969c8e7 Formatting fixes. 2020-09-19 16:58:37 +02:00
Sebastian Hartte f2427ee347 Fixes #4747: The chunk unload event was triggering before the chunk was actually saved to disk, which means disconnecting grid connections at that point led to grid nodes not saving their data to the chunk. 2020-09-19 16:54:30 +02:00
Sebastian Hartte 4aa9c92a68 Fixes #4747: Server unload event was destroying grids before the associated nodes were saved, leading to player ids not being saved correctly when quitting the server. 2020-09-19 16:49:44 +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 b5a90357c0 Fix ME Chest attributes being exposed on the wrong side. 2020-09-19 13:59:09 +02:00
Sebastian Hartte 5be0adbc89 Fixed Mixins that were targeting the wrong class/methods. 2020-09-19 02:48:59 +02:00
Sebastian Hartte d8e5f4fed5 Fixed formatating after migrating mappings. 2020-09-19 02:41:53 +02:00
Sebastian Hartte 47dfc19b7c Renamed mixin after mapping update. 2020-09-19 02:40:51 +02:00
Sebastian Hartte 45f5851079 Updated versions, and mappings. 2020-09-19 02:40:35 +02:00
Sebastian Hartte 76efe6026d Add an indicator that shows the number of active crafting jobs. 2020-09-18 21:34:42 +02:00
yueh 4e03cbd356 Simply build all branches 2020-09-18 10:32:06 +02:00
Sebastian Hartte ce058b006c Fixes #4748: badly filtered inventory leads to crashes, also expose the ME storage again. 2020-09-18 03:11:33 +02:00
yueh 9005db6f1c Fix yaml syntax 2020-09-17 21:42:20 +02:00
yueh 58afe755b8 Build PRs for all release branches (#4746) 2020-09-17 21:37:53 +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
Sebastian Hartte 5aef82b9ef Optimized Imports 2020-09-16 21:35:47 +02:00
Sebastian Hartte 483f93f977 Post-merge fixes and item generator fix. 2020-09-16 20:12:38 +02:00
Sebastian Hartte 1759f92164 Prevent FG from force-exiting the daemon if runData is used. 2020-09-16 19:51:22 +02:00
Sebastian Hartte e50c13e570 Rolled back the memory change since it'll cause a daemon to launch even during a CI build. 2020-09-16 19:29:47 +02:00
Sebastian Hartte c0575e11b0 Merge remote-tracking branch 'origin/master' into fabric/master
# Conflicts:
#	build.gradle
#	gradle.properties
#	gradle/scripts/artifacts.gradle
#	gradle/scripts/curseforge.gradle
#	gradle/wrapper/gradle-wrapper.jar
#	gradle/wrapper/gradle-wrapper.properties
#	src/main/java/appeng/block/networking/CableBusBlock.java
#	src/main/java/appeng/client/gui/AEBaseScreen.java
#	src/main/java/appeng/client/gui/implementations/InterfaceTerminalScreen.java
#	src/main/java/appeng/client/gui/widgets/Scrollbar.java
#	src/main/java/appeng/core/sync/packets/JEIRecipePacket.java
#	src/main/java/appeng/tile/AEBaseBlockEntity.java
#	src/main/resources/META-INF/mods.toml
2020-09-16 18:57:15 +02:00
Sebastian Hartte 749dd35f88 Updated forge and increased memory for IDE-sync daemon. 2020-09-16 18:18:22 +02:00
Sebastian Hartte ca79a377bd Merge remote-tracking branch 'origin/8.0.x-1.16.1'
# Conflicts:
#	gradle/scripts/dependencies.gradle
2020-09-16 17:49:59 +02:00
Sebastian Hartte 475c8ff5a1 Adapt the same sorting logic used for items stored in the network for items obtained from patterns, which fixes items from patterns not being able to be shift-clicked into new patterns. 2020-09-16 17:44:49 +02:00
yueh 5b3ccf26d0 Merge all gradle files (#4732)
* Merge all gradle files

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

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

* Removed Sonarqube and JaCoCo plugins.

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

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

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

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

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

(cherry picked from commit b756d68be1)
2020-09-08 14:28:08 +02:00
Sebastian Hartte 0f469e9df6 Bumped Forge version. 2020-09-07 23:34:56 +02:00
Sebastian Hartte 0f34f09b32 Remove object-scanning subscriptions and remove unused test-file. 2020-09-06 20:59:37 +02:00
Sebastian Hartte 8e54a8fed9 Fixes a problem where tile entities would become unremovable (well, one more of these at least),
because they would reset their block state upon removal and loss of power.
2020-09-06 20:59:19 +02:00
Sebastian Hartte d9bb81a00f Fixes #4680: Facade advancement is showing an untextured facade. 2020-09-03 17:36:20 +02:00
yueh 39e038d916 Fixes #4683: Hopefully LazyOptional works as advertised (#4684) 2020-09-02 21:36:40 +02:00
138 changed files with 2689 additions and 2733 deletions
+2 -1
View File
@@ -2,7 +2,8 @@ name: 'Build PRs'
on:
pull_request:
branches: [ master ]
branches:
- '*'
jobs:
build:
+211 -81
View File
@@ -47,84 +47,6 @@ repositories {
}
}
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
compileJava {
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
options.deprecation = false
}
// ensure everything uses UTF-8 and not some random codepage chosen by gradle
compileJava.options.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
// Create version number
ext.pr = System.getenv('PR_NUMBER') ?: ""
if (ext.pr) {
version = version + "+pr." + ext.pr
}
ext.branch = System.getenv('BRANCH') ?: ""
if (ext.branch) {
version = version + "+branch." + ext.branch
}
ext.tag = System.getenv('TAG') ?: ""
if (ext.tag && ext.tag.startsWith("fabric/v")) {
version = ext.tag.substring("fabric/v".length())
// Validate that the rest is a semver version
if (version ==~ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) {
println("::set-env name=VERSION::${version}")
} else {
throw new GradleException("Invalid semver: $version")
}
}
// Maven group and artifact name
group = artifact_group
archivesBaseName = artifact_basename
sourceSets {
api
main {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
resources {
srcDir 'src/generated/resources'
}
}
datagen {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
test {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
}
siteexport {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
configurations {
apiCompile.extendsFrom(compileClasspath)
datagenCompile.extendsFrom(compileClasspath)
siteexportCompile.extendsFrom(compileClasspath)
siteexportRuntime.extendsFrom(runtimeClasspath)
}
apply from: 'gradle/scripts/spotless.gradle'
minecraft {
accessWidener "src/main/resources/appliedenergistics2.accesswidener"
}
dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
@@ -163,6 +85,87 @@ dependencies {
// unit test dependencies
testCompile "junit:junit:4.13"
}
group = artifact_group
archivesBaseName = artifact_basename
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
compileJava {
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_1_8
options.deprecation = false
}
// ensure everything uses UTF-8 and not some random codepage chosen by gradle
compileJava.options.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
///////////////////
// Version Number
ext.pr = System.getenv('PR_NUMBER') ?: ""
if (ext.pr) {
version = version + "+pr." + ext.pr
}
ext.branch = System.getenv('BRANCH') ?: ""
if (ext.branch) {
version = version + "+branch." + ext.branch
}
ext.tag = System.getenv('TAG') ?: ""
if (ext.tag && ext.tag.startsWith("fabric/v")) {
version = ext.tag.substring("fabric/v".length())
// Validate that the rest is a semver version
if (version ==~ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/) {
println("::set-env name=VERSION::${version}")
} else {
throw new GradleException("Invalid semver: $version")
}
}
ext.isAlpha = project.version.contains("alpha")
ext.isBeta = project.version.contains("beta")
sourceSets {
api
main {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
resources {
srcDir 'src/generated/resources'
}
}
datagen {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
test {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
}
siteexport {
compileClasspath += sourceSets.api.output
runtimeClasspath += sourceSets.api.output
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
}
configurations {
apiCompile.extendsFrom(compileClasspath)
datagenCompile.extendsFrom(compileClasspath)
siteexportCompile.extendsFrom(compileClasspath)
siteexportRuntime.extendsFrom(runtimeClasspath)
}
////////////////////
// Forge/Minecraft
minecraft {
accessWidener "src/main/resources/appliedenergistics2.accesswidener"
}
processResources {
inputs.property "version", project.version
@@ -177,10 +180,76 @@ processResources {
}
}
apply from: 'gradle/scripts/artifacts.gradle'
apply from: 'gradle/scripts/curseforge.gradle'
//////////////
// Aritfacts
processResources {
exclude '.cache'
}
// configure the maven publication
jar {
finalizedBy 'remapJar'
from sourceSets.main.output.classesDirs
from sourceSets.api.output.classesDirs
from sourceSets.main.output.resourcesDir
from sourceSets.api.output.resourcesDir
manifest {
attributes([
"Specification-Title": "Applied Energistics 2",
"Specification-Vendor": "TeamAppliedEnergistics",
"Specification-Version": "${project.version}",
"Implementation-Title": "${project.name}",
"Implementation-Version": "${project.version}",
"Implementation-Vendor" :"TeamAppliedEnergistics",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
"MixinConfigs": "appliedenergistics2.mixins.json"
])
}
}
task javadocs(type: Javadoc) {
classpath = sourceSets.main.compileClasspath
source = sourceSets.api.java
include "appeng/api/**"
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
options.charSet = 'UTF-8'
}
task javadocJar(type: Jar, dependsOn: javadocs) {
classifier = "javadoc"
from javadoc.destinationDir
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
from sourceSets.api.allSource
}
task apiJar(type: Jar) {
classifier = "api"
from sourceSets.api.output
include "appeng/api/**"
// TODO: when FG bug is fixed, remove allJava from the api jar.
// https://github.com/MinecraftForge/ForgeGradle/issues/369
// Gradle should be able to pull them from the -sources jar.
from sourceSets.api.allJava
}
artifacts {
archives javadocJar
archives sourcesJar
archives apiJar
}
//////////////////
// Maven publish
publishing {
if (!version.endsWith("-SNAPSHOT")) {
publications {
@@ -239,3 +308,64 @@ task runSiteExport(type: RunClientTask, dependsOn: downloadAssets, group: "ae2",
classpath sourceSets.main.output
classpath sourceSets.siteexport.output
}
/////////////
// Spotless
spotless {
java {
target 'src/*/java/appeng/**/*.java'
indentWithSpaces()
eclipse().configFile 'codeformat/codeformat.xml'
importOrderFile 'codeformat/ae2.importorder'
}
format 'json', {
target 'src/*/resources/**/*.json'
targetExclude 'src/generated/resources/**'
prettier().config(['parser': 'json'])
}
}
////////////////
// Curse Forge
if (System.getenv("CURSEFORGE") && !version.endsWith("-SNAPSHOT")) {
def cfReleaseType = "release"
if (ext.isAlpha) {
cfReleaseType = "alpha"
} else if (ext.isBeta) {
cfReleaseType = "beta"
}
curseforge {
apiKey = System.getenv("CURSEFORGE")
project {
id = project.curseforge_project
changelogType = "markdown"
changelog = System.getenv("CHANGELOG") ?: "Please visit our [releases](https://github.com/AppliedEnergistics/Applied-Energistics-2/releases) for a changelog"
releaseType = cfReleaseType
addGameVersion project.minecraft_version
addGameVersion "Fabric"
mainArtifact(remapJar.archiveFile) {
displayName = "${project.version} [FABRIC]"
}
}
options {
forgeGradleIntegration = false
}
afterEvaluate {
tasks.getByName("curseforge${project.curseforge_project}").dependsOn remapJar
}
}
}
////////////////
// IntelliJ Project Import
// The Mixin annotation process does not have an obfuscation source when running through the IntelliJ compiler,
// thus we have to prevent it from being activated as part of importing this Gradle project into IntelliJ.
if (System.getProperty("idea.sync.active") == "true") {
afterEvaluate {
tasks.withType(JavaCompile).all {
it.options.annotationProcessorPath = files()
}
}
}
+5 -10
View File
@@ -3,24 +3,19 @@ version=0.0.0-SNAPSHOT
artifact_group=appeng
artifact_basename=appliedenergistics2-fabric
kotlin.code.style=official
org.gradle.jvmargs=-Xmx2G
# Fabric Properties
# Check these on https://modmuss50.me/fabric.html
minecraft_version=1.16.2
yarn_mappings=1.16.2+build.6
loader_version=0.9.2+build.206
minecraft_version=1.16.3
yarn_mappings=1.16.3+build.11
loader_version=0.9.3+build.207
#Fabric api
fabric_version=0.20.1+build.401-1.16
fabric_version=0.21.0+build.407-1.16
loom_version=0.4-SNAPSHOT
# Kotlin
kotlin_version=1.3.61
fabric_kotlin_version=1.3.61+build.1
loom_version=0.5-SNAPSHOT
# Dependencies
libblockattributes_version=0.8.0
-78
View File
@@ -1,78 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
processResources {
exclude '.cache'
}
jar {
finalizedBy 'remapJar'
from sourceSets.main.output.classesDirs
from sourceSets.api.output.classesDirs
from sourceSets.main.output.resourcesDir
from sourceSets.api.output.resourcesDir
manifest {
attributes([
"Specification-Title": "Applied Energistics 2",
"Specification-Vendor": "TeamAppliedEnergistics",
"Specification-Version": "${project.version}",
"Implementation-Title": "${project.name}",
"Implementation-Version": "${project.version}",
"Implementation-Vendor" :"TeamAppliedEnergistics",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
])
}
}
task javadocs(type: Javadoc) {
classpath = sourceSets.main.compileClasspath
source = sourceSets.api.java
include "appeng/api/**"
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
options.charSet = 'UTF-8'
}
task javadocJar(type: Jar, dependsOn: javadocs) {
classifier = "javadoc"
from javadoc.destinationDir
}
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = "sources"
from sourceSets.main.allSource
from sourceSets.api.allSource
}
task apiJar(type: Jar) {
classifier = "api"
from sourceSets.api.output
include "appeng/api/**"
}
artifacts {
archives javadocJar
archives sourcesJar
archives apiJar
}
-50
View File
@@ -1,50 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, TeamAppliedEnergistics, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*g GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
def isAlpha = project.version.contains("alpha")
def isBeta = project.version.contains("beta")
def cfReleaseType = "release"
if (isAlpha) {
cfReleaseType = "alpha"
} else if (isBeta) {
cfReleaseType = "beta"
}
if (System.getenv("CURSEFORGE") && !version.endsWith("-SNAPSHOT")) {
curseforge {
apiKey = System.getenv("CURSEFORGE")
project {
id = project.curseforge_project
changelogType = "markdown"
changelog = System.getenv("CHANGELOG") ?: "Please visit our [releases](https://github.com/AppliedEnergistics/Applied-Energistics-2/releases) for a changelog"
releaseType = cfReleaseType
addGameVersion project.minecraft_version
addGameVersion "Fabric"
mainArtifact(remapJar.archiveFile) {
displayName = "${project.version} [FABRIC]"
}
}
options {
forgeGradleIntegration = false
}
afterEvaluate {
tasks.getByName("curseforge${project.curseforge_project}").dependsOn remapJar
}
}
}
-42
View File
@@ -1,42 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
repositories {
mavenLocal()
maven { // modmaven, maven proxy
name 'modmaven'
url "https://modmaven.k-4u.nl/"
}
}
dependencies {
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
// compile against provided APIs
compileOnly "mezz.jei:jei-${jei_minecraft_version}:${jei_version}:api"
compileOnly "mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}:api"
// Runtime, Mods
runtimeOnly fg.deobf("mezz.jei:jei-${jei_minecraft_version}:${jei_version}")
runtimeOnly fg.deobf("mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}")
//runtimeOnly fg.deobf("team.chisel.ctm:CTM:${ctm_version}")
// unit test dependencies
testCompile "junit:junit:4.13"
}
-32
View File
@@ -1,32 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*g GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
spotless {
java {
target 'src/*/java/appeng/**/*.java'
indentWithSpaces()
eclipse().configFile 'codeformat/codeformat.xml'
importOrderFile 'codeformat/ae2.importorder'
}
format 'json', {
target 'src/*/resources/**/*.json'
targetExclude 'src/generated/resources/**'
prettier().config(['parser': 'json'])
}
}
-1
View File
@@ -10,7 +10,6 @@ pluginManagement {
plugins {
id "fabric-loom" version loom_version
id "org.jetbrains.kotlin.jvm" version kotlin_version
}
}
@@ -53,10 +53,10 @@ public interface ICraftingHelper {
* @param stack If null, a new item will be created to hold the encoded pattern.
* Otherwise the given item must already contains an encoded
* pattern that will be overwritten.
* @throws IllegalArgumentException If either in or out contain only empty
* ItemStacks.
* @return A new encoded pattern, or the given stack with the pattern encoded in
* it.
* @throws IllegalArgumentException If either in or out contain only empty
* ItemStacks.
*/
ItemStack encodeProcessingPattern(@Nullable ItemStack stack, ItemStack[] in, ItemStack[] out);
@@ -23,13 +23,23 @@
package appeng.api.implementations.items;
import net.minecraft.block.Block;
import net.minecraft.block.Material;
import javax.annotation.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public interface IGrowableCrystal {
ItemStack triggerGrowth(ItemStack is);
float getMultiplier(Block blk, Material mat);
/**
* Gets the crystal growth multiplier based on the material it is submerged in.
*
* @param state The blockstate of the block the crystal is currently in.
* @return The multiplier for the growth tick progress. Zero if the crystal
* cannot grow in the current material.
*/
float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos);
}
@@ -28,4 +28,9 @@ public enum GridNotification {
* the visible connections for this node have changed, useful for cable.
*/
CONNECTIONS_CHANGED,
/**
* the owner of the grid node has changed, and the node needs to be re-saved
*/
OWNER_CHANGED
}
@@ -87,7 +87,8 @@ public interface IGridBlock {
AEColor getGridColor();
/**
* Notifies your IGridBlock that changes were made to your connections
* Called by the {@link IGridNode} to notify its {@link IGridBlock} about
* events.
*/
void onGridNotification(@Nonnull GridNotification notification);
@@ -86,7 +86,7 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusBlockEntity> implemen
public CableBusBlock() {
super(defaultProps(AEMaterials.GLASS).nonOpaque().dropsNothing().dynamicBounds()
.lightLevel(state -> state.get(LIGHT_LEVEL)));
.luminance(state -> state.get(LIGHT_LEVEL)));
setDefaultState(getDefaultState().with(LIGHT_LEVEL, 0));
}
@@ -38,11 +38,11 @@ import appeng.helpers.Splotch;
*/
class PaintSplotchesBakedModel implements BakedModel, FabricBakedModel {
private static final SpriteIdentifier TEXTURE_PAINT1 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_PAINT1 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/paint1"));
private static final SpriteIdentifier TEXTURE_PAINT2 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_PAINT2 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/paint2"));
private static final SpriteIdentifier TEXTURE_PAINT3 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_PAINT3 = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/paint3"));
private final Sprite[] textures;
@@ -36,18 +36,18 @@ import appeng.core.AppEng;
class QnbFormedBakedModel implements BakedModel, FabricBakedModel {
private static final SpriteIdentifier TEXTURE_LINK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_LINK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/quantum_link"));
private static final SpriteIdentifier TEXTURE_RING = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_RING = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/quantum_ring"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "block/quantum_ring_light"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "block/quantum_ring_light"));
private static final SpriteIdentifier TEXTURE_RING_LIGHT_CORNER = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEX, new Identifier(AppEng.MOD_ID, "block/quantum_ring_light_corner"));
private static final SpriteIdentifier TEXTURE_CABLE_GLASS = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
new Identifier(AppEng.MOD_ID, "part/cable/glass/transparent"));
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "block/quantum_ring_light_corner"));
private static final SpriteIdentifier TEXTURE_CABLE_GLASS = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "part/cable/glass/transparent"));
private static final SpriteIdentifier TEXTURE_COVERED_CABLE = new SpriteIdentifier(
SpriteAtlasTexture.BLOCK_ATLAS_TEX, new Identifier(AppEng.MOD_ID, "part/cable/covered/transparent"));
SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier(AppEng.MOD_ID, "part/cable/covered/transparent"));
private static final float DEFAULT_RENDER_MIN = 2.0f;
private static final float DEFAULT_RENDER_MAX = 14.0f;
@@ -106,6 +106,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
private Stopwatch dbl_clickTimer = Stopwatch.createStarted();
private ItemStack dbl_whichItem = ItemStack.EMPTY;
private Slot bl_clicked;
private boolean handlingRightClick;
protected final List<CustomSlotWidget> guiSlots = new ArrayList<>();
public AEBaseScreen(T container, PlayerInventory playerInventory, Text title) {
@@ -284,12 +285,18 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) {
this.drag_click.clear();
// Forward right-clicks as-if they were left-clicks
if (btn == 1) {
for (final Object o : this.buttons) {
final AbstractButtonWidget widget = (AbstractButtonWidget) o;
if (widget.isMouseOver(xCoord, yCoord)) {
return super.mouseClicked(xCoord, yCoord, 0);
handlingRightClick = true;
try {
for (final Object o : this.buttons) {
final AbstractButtonWidget widget = (AbstractButtonWidget) o;
if (widget.isMouseOver(xCoord, yCoord)) {
return super.mouseClicked(xCoord, yCoord, 0);
}
}
} finally {
handlingRightClick = false;
}
}
@@ -300,22 +307,36 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
}
}
if (this.getScrollBar() != null) {
this.getScrollBar().click(xCoord - this.x, yCoord - this.y);
// Forward left mouse button down events to the scrollbar
if (btn == 0 && this.getScrollBar() != null) {
if (this.getScrollBar().mouseDown(xCoord - this.x, yCoord - this.y)) {
return true;
}
}
return super.mouseClicked(xCoord, yCoord, btn);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double dragX, double dragY) {
public boolean mouseReleased(double mouseX, double mouseY, int button) {
// Forward left mouse button up events to the scrollbar
if (button == 0 && this.getScrollBar() != null) {
if (this.getScrollBar().mouseUp(mouseX - this.x, mouseY - this.y)) {
return true;
}
}
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int mouseButton, double dragX, double dragY) {
final Slot slot = this.getSlot((int) mouseX, (int) mouseY);
final ItemStack itemstack = getPlayer().inventory.getCursorStack();
if (this.getScrollBar() != null) {
// FIXME: Coordinate system of mouseX/mouseY is unclear
this.getScrollBar().click((int) mouseX - this.x, (int) mouseY - this.y);
this.getScrollBar().mouseDragged((int) mouseX - this.x, (int) mouseY - this.y);
}
if (slot instanceof FakeSlot && !itemstack.isEmpty()) {
@@ -622,15 +643,7 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
}
protected Text getGuiDisplayName(final Text in) {
return this.hasCustomInventoryName() ? new LiteralText(this.getInventoryName()) : in;
}
private boolean hasCustomInventoryName() {
return this.handler.getCustomName() != null;
}
private String getInventoryName() {
return this.handler.getCustomName();
return title.getString().isEmpty() ? in : title;
}
/**
@@ -798,6 +811,11 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
public void tick() {
super.tick();
if (this.getScrollBar() != null) {
this.getScrollBar().tick();
}
for (Element child : children) {
if (child instanceof ITickingWidget) {
((ITickingWidget) child).tick();
@@ -805,6 +823,14 @@ public abstract class AEBaseScreen<T extends AEBaseContainer> extends HandledScr
}
}
/**
* Returns true while the current event being handled is a click of the right
* mouse button.
*/
public boolean isHandlingRightClick() {
return handlingRightClick;
}
public List<Rectangle> getExclusionZones() {
return Lists.newArrayList(new Rectangle(x, y, backgroundWidth, backgroundHeight));
}
@@ -46,22 +46,28 @@ public class CraftAmountScreen extends AEBaseScreen<CraftAmountContainer> {
public void init() {
super.init();
this.amountToCraft = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.CRAFT_ITEM_COUNT, value -> {
});
this.amountToCraft = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.CRAFT_ITEM_COUNT);
this.amountToCraft.setValue(1);
this.amountToCraft.setTextFieldBounds(62, 57, 50);
this.amountToCraft.setMinValue(1);
this.amountToCraft.setHideValidationIcon(true);
this.amountToCraft.addButtons(children::add, this::addButton);
this.next = this
.addButton(new ButtonWidget(this.x + 128, this.y + 51, 38, 20, GuiText.Next.text(), this::confirm));
this.amountToCraft.setOnConfirm(() -> this.confirm(this.next));
subGui.addBackButton(this::addButton, 154, 0);
changeFocus(true);
}
private void confirm(ButtonWidget button) {
NetworkHandler.instance()
.sendToServer(new CraftRequestPacket((int) this.amountToCraft.getValue(), hasShiftDown()));
int amount = this.amountToCraft.getIntValue().orElse(0);
if (amount <= 0) {
return;
}
NetworkHandler.instance().sendToServer(new CraftRequestPacket(amount, hasShiftDown()));
}
@Override
@@ -77,23 +83,9 @@ public class CraftAmountScreen extends AEBaseScreen<CraftAmountContainer> {
this.bindTexture("guis/craft_amt.png");
drawTexture(matrices, offsetX, offsetY, 0, 0, this.backgroundWidth, this.backgroundHeight);
this.next.active = this.amountToCraft.getValue() > 0;
this.next.active = this.amountToCraft.getIntValue().orElse(0) > 0;
this.amountToCraft.render(matrices, offsetX, offsetY, partialTicks);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) {
if (keyCode == 28) {
this.next.onPress();
return true;
} else {
return super.keyPressed(keyCode, scanCode, p_keyPressed_3_);
}
}
protected String getBackground() {
return "guis/craftAmt.png";
}
}
@@ -25,6 +25,8 @@ import java.util.List;
import com.mojang.blaze3d.systems.RenderSystem;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
@@ -73,10 +75,6 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
this.setScrollBar(scrollbar);
}
boolean isAutoStart() {
return this.handler.isAutoStart();
}
@Override
public void init() {
super.init();
@@ -87,12 +85,14 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
this.addButton(this.start);
this.selectCPU = new ButtonWidget(this.x + (219 - 180) / 2, this.y + this.backgroundHeight - 68, 180, 20,
GuiText.CraftingCPU.withSuffix(": ").append(GuiText.Automatic.text()), btn -> selectNextCpu());
getNextCpuButtonLabel(), btn -> selectNextCpu());
this.selectCPU.active = false;
this.addButton(this.selectCPU);
addButton(new ButtonWidget(this.x + 6, this.y + this.backgroundHeight - 25, 50, 20, GuiText.Cancel.text(),
btn -> subGui.goBack()));
this.setScrollBar();
}
@Override
@@ -133,22 +133,22 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
}
private void updateCPUButtonText() {
Text btnTextText = GuiText.CraftingCPU.withSuffix(": ").append(GuiText.Automatic.text());
if (this.handler.getSelectedCpu() >= 0)// && status.selectedCpu < status.cpus.size() )
{
if (this.handler.getName() != null) {
final String name = this.handler.getName().asTruncatedString(20);
btnTextText = GuiText.CraftingCPU.withSuffix(": " + name);
} else {
btnTextText = GuiText.CraftingCPU.withSuffix(": #" + this.handler.getSelectedCpu());
}
}
this.selectCPU.setMessage(getNextCpuButtonLabel());
}
private Text getNextCpuButtonLabel() {
if (this.handler.hasNoCPU()) {
btnTextText = GuiText.NoCraftingCPUs.text();
return GuiText.NoCraftingCPUs.text();
}
this.selectCPU.setMessage(btnTextText);
Text cpuName;
if (this.handler.cpuName == null) {
cpuName = GuiText.Automatic.text();
} else {
cpuName = this.handler.cpuName;
}
return GuiText.CraftingCPU.withSuffix(": ").append(cpuName);
}
private boolean isSimulation() {
@@ -440,7 +440,7 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
@Override
public boolean keyPressed(int keyCode, int scanCode, int p_keyPressed_3_) {
if (!this.checkHotbarKeys(keyCode, scanCode)) {
if (keyCode == 28) {
if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
this.start();
return true;
}
@@ -449,7 +449,7 @@ public class CraftConfirmScreen extends AEBaseScreen<CraftConfirmContainer> {
}
private void selectNextCpu() {
final boolean backwards = getClient().mouse.wasRightButtonClicked();
final boolean backwards = isHandlingRightClick();
NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next"));
}
@@ -44,12 +44,12 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
super.init();
this.selectCPU = new ButtonWidget(this.x + 8, this.y + this.backgroundHeight - 25, 150, 20,
GuiText.CraftingCPU.withSuffix(": ").append(GuiText.NoCraftingCPUs.text()), btn -> selectNextCpu());
getNextCpuButtonLabel(), btn -> selectNextCpu());
this.addButton(this.selectCPU);
subGui.addBackButton(btn -> {
addButton(btn);
btn.setHideEdge(13);
btn.setHideEdge(true);
}, 213, -4);
}
@@ -60,23 +60,14 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
}
private void updateCPUButtonText() {
Text btnTextText = GuiText.NoCraftingJobs.text();
if (this.handler.selectedCpu >= 0)// && status.selectedCpu < status.cpus.size() )
{
if (this.handler.myName != null) {
final String name = this.handler.myName.asTruncatedString(20);
btnTextText = GuiText.CPUs.withSuffix(": " + name);
} else {
btnTextText = GuiText.CPUs.withSuffix(": #" + this.handler.selectedCpu);
}
}
this.selectCPU.setMessage(getNextCpuButtonLabel());
}
private Text getNextCpuButtonLabel() {
if (this.handler.noCPU) {
btnTextText = GuiText.NoCraftingJobs.text();
return GuiText.NoCraftingJobs.text();
}
this.selectCPU.setMessage(btnTextText);
return GuiText.CraftingCPU.withSuffix(": ").append(handler.cpuName);
}
@Override
@@ -84,9 +75,8 @@ public class CraftingStatusScreen extends CraftingCPUScreen<CraftingStatusContai
return in; // the cup name is on the button
}
// FIXME: Extract to separate class? Shared with GuiCraftConfirm
private void selectNextCpu() {
final boolean backwards = client.mouse.wasRightButtonClicked();
final boolean backwards = isHandlingRightClick();
NetworkHandler.instance().sendToServer(new ConfigValuePacket("Terminal.Cpu", backwards ? "Prev" : "Next"));
}
@@ -87,7 +87,7 @@ public class InterfaceScreen extends UpgradeableScreen<InterfaceContainer> {
}
private void selectNextInterfaceMode() {
final boolean backwards = getClient().mouse.wasRightButtonClicked();
final boolean backwards = isHandlingRightClick();
NetworkHandler.instance().sendToServer(new ConfigButtonPacket(Settings.INTERFACE_TERMINAL, backwards));
}
@@ -70,8 +70,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
public InterfaceTerminalScreen(InterfaceTerminalContainer container, PlayerInventory playerInventory, Text title) {
super(container, playerInventory, title);
final Scrollbar scrollbar = new Scrollbar();
this.setScrollBar(scrollbar);
this.setScrollBar(new Scrollbar().setLeft(175).setTop(18).setHeight(106));
this.backgroundWidth = 195;
this.backgroundHeight = 222;
}
@@ -80,10 +79,6 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
public void init() {
super.init();
this.getScrollBar().setLeft(175);
this.getScrollBar().setHeight(106);
this.getScrollBar().setTop(18);
this.searchField = new AETextField(this.textRenderer, this.x + 104, this.y + 4, 65, 12);
this.searchField.setHasBorder(false);
this.searchField.setMaxLength(25);
@@ -34,8 +34,6 @@ import appeng.client.gui.widgets.ServerSettingToggleButton;
import appeng.client.gui.widgets.SettingToggleButton;
import appeng.container.implementations.LevelEmitterContainer;
import appeng.core.localization.GuiText;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
public class LevelEmitterScreen extends UpgradeableScreen<LevelEmitterContainer> {
@@ -51,15 +49,18 @@ public class LevelEmitterScreen extends UpgradeableScreen<LevelEmitterContainer>
public void init() {
super.init();
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_ITEM_COUNT,
this::onLevelChange);
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_ITEM_COUNT);
this.level.setTextFieldBounds(25, 44, 75);
this.level.addButtons(children::add, this::addButton);
handler.setTextField(this.level);
this.level.setValue(handler.getReportingValue());
this.level.setOnChange(this::saveReportingValue);
this.level.setOnConfirm(this::onClose);
this.changeFocus(true);
}
private void onLevelChange(long level) {
NetworkHandler.instance().sendToServer(new ConfigValuePacket("LevelEmitter.Value", String.valueOf(level)));
private void saveReportingValue() {
this.level.getLongValue().ifPresent(handler::setReportingValue);
}
@Override
@@ -26,6 +26,7 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import me.shedaniel.math.Rectangle;
@@ -51,6 +52,7 @@ import appeng.client.gui.widgets.SettingToggleButton;
import appeng.client.gui.widgets.TabButton;
import appeng.client.me.InternalSlotME;
import appeng.client.me.ItemRepo;
import appeng.client.render.StackSizeRenderer;
import appeng.container.implementations.CraftingStatusContainer;
import appeng.container.implementations.MEMonitorableContainer;
import appeng.container.slot.AppEngSlot;
@@ -237,7 +239,7 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
if (this.viewCell || this instanceof WirelessTermScreen) {
this.craftingStatusBtn = this.addButton(new TabButton(this.x + 170, this.y - 4, 2 + 11 * 16,
GuiText.CraftingStatus.text(), this.itemRenderer, btn -> showCraftingStatus()));
this.craftingStatusBtn.setHideEdge(13);
this.craftingStatusBtn.setHideEdge(true);
}
this.isAutoFocus = SearchBoxMode.AUTOSEARCH == searchMode || SearchBoxMode.JEI_AUTOSEARCH == searchMode
@@ -283,6 +285,8 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
craftingGridOffsetX -= 25;
craftingGridOffsetY -= 6;
this.setScrollBar();
}
@Override
@@ -292,6 +296,16 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
this.currentMouseX = mouseX;
this.currentMouseY = mouseY;
// Show the number of active crafting jobs
if (this.craftingStatusBtn != null && handler.activeCraftingJobs != -1) {
// The stack size renderer expects a 16x16 slot, while the button is normally
// bigger
int x = this.craftingStatusBtn.x + (this.craftingStatusBtn.getWidth() - 16) / 2;
int y = this.craftingStatusBtn.y + (this.craftingStatusBtn.getHeight() - 16) / 2;
StackSizeRenderer.renderSizeLabel(textRenderer, x - this.x, y - this.y,
new LiteralText(String.valueOf(handler.activeCraftingJobs)));
}
}
@Override
@@ -356,6 +370,7 @@ public class MEMonitorableScreen<T extends MEMonitorableContainer> extends AEBas
if (this.searchField != null) {
this.searchField.render(matrices, mouseX, mouseY, partialTicks);
}
}
protected String getBackground() {
@@ -2,41 +2,58 @@ package appeng.client.gui.implementations;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import appeng.client.gui.AEBaseScreen;
import appeng.client.gui.NumberEntryType;
import appeng.client.gui.widgets.ITickingWidget;
import appeng.client.gui.widgets.NumberBox;
import appeng.client.gui.widgets.ConfirmableTextField;
import appeng.client.gui.widgets.ValidationIcon;
import appeng.core.AEConfig;
/**
* A utility widget that consists of a text-field to enter a number with
* attached buttons to increment/decrement the number in fixed intervals.
*/
public class NumberEntryWidget implements ITickingWidget {
public class NumberEntryWidget extends DrawableHelper {
private static final Text INVALID_NUMBER = new TranslatableText("gui.appliedenergistics2.validation.InvalidNumber");
private static final String NUMBER_LESS_THAN_MIN_VALUE = "gui.appliedenergistics2.validation.NumberLessThanMinValue";
private static final Text PLUS = Text.of("+");
private static final Text MINUS = Text.of("-");
private static final int TEXT_COLOR_ERROR = 0xFF1900;
private static final int TEXT_COLOR_NORMAL = 0xFFFFFF;
private final AEBaseScreen<?> parent;
private final int x;
private final int y;
private final NumberBox level;
private final ConfirmableTextField textField;
private final NumberEntryType type;
private List<ButtonWidget> buttons;
private long minValue;
private ValidationIcon validationIcon;
public NumberEntryWidget(AEBaseScreen<?> parent, int x, int y, int width, int height, NumberEntryType type,
LongConsumer changeListener) {
// Called when the value changes
private Runnable onChange;
// Called when the user presses enter while there's a valid number in the field
private Runnable onConfirm;
private boolean hideValidationIcon;
public NumberEntryWidget(AEBaseScreen<?> parent, int x, int y, int width, int height, NumberEntryType type) {
this.parent = parent;
this.x = x;
this.y = y;
@@ -45,27 +62,50 @@ public class NumberEntryWidget implements ITickingWidget {
TextRenderer font = parent.getClient().textRenderer;
int inputX = parent.getX() + x;
int inputY = parent.getY() + y;
this.level = new NumberBox(font, inputX, inputY, width, font.fontHeight, type.getInputType(), changeListener);
this.level.setHasBorder(false);
this.level.setMaxLength(16);
this.level.setEditableColor(0xFFFFFF);
this.level.setVisible(true);
parent.setInitialFocus(this.level);
this.textField = new ConfirmableTextField(font, inputX, inputY, width, font.fontHeight, LiteralText.EMPTY);
this.textField.setHasBorder(false);
this.textField.setMaxLength(16);
this.textField.setEditableColor(TEXT_COLOR_NORMAL);
this.textField.setVisible(true);
this.textField.setSelected(true);
parent.setInitialFocus(this.textField);
this.textField.setChangedListener(text -> {
validate();
if (onChange != null) {
this.onChange.run();
}
});
this.textField.setOnConfirm(() -> {
// Only confirm if it's actually valid
if (this.onConfirm != null && getLongValue().isPresent()) {
this.onConfirm.run();
}
});
validate();
}
public void setOnConfirm(Runnable callback) {
this.onConfirm = callback;
}
public void setOnChange(Runnable callback) {
this.onChange = callback;
}
public void setActive(boolean active) {
this.level.active = active;
this.textField.active = active;
this.buttons.forEach(b -> b.active = active);
}
public void setTextFieldBounds(int x, int y, int width) {
this.level.x = parent.getX() + x;
this.level.y = parent.getY() + y;
this.level.setWidth(width);
this.textField.x = parent.getX() + x;
this.textField.y = parent.getY() + y;
this.textField.setWidth(width);
}
public void setMinValue(long minValue) {
this.level.setMinValue(minValue);
this.minValue = minValue;
validate();
}
public void addButtons(Consumer<Element> addChildren, Consumer<ButtonWidget> addButton) {
@@ -78,54 +118,116 @@ public class NumberEntryWidget implements ITickingWidget {
int left = parent.getX() + x;
int top = parent.getY() + y;
List<ButtonWidget> buttons = new ArrayList<>(8);
List<ButtonWidget> buttons = new ArrayList<>(9);
buttons.add(new ButtonWidget(left, top, 22, 20, makeLabel(PLUS, a), btn -> addQty(a)));
buttons.add(new ButtonWidget(left + 28, top, 28, 20, makeLabel(PLUS, b), btn -> addQty(b)));
buttons.add(new ButtonWidget(left + 62, top, 32, 20, makeLabel(PLUS, c), btn -> addQty(c)));
buttons.add(new ButtonWidget(left + 100, top, 38, 20, makeLabel(PLUS, d), btn -> addQty(d)));
// Need to add these now for sensible tab-order
buttons.forEach(addButton);
// Placing this here will give a sensible tab order
addChildren.accept(this.level);
addChildren.accept(this.textField);
buttons.add(new ButtonWidget(left, top + 42, 22, 20, makeLabel(MINUS, a), btn -> addQty(-a)));
buttons.add(new ButtonWidget(left + 28, top + 42, 28, 20, makeLabel(MINUS, b), btn -> addQty(-b)));
buttons.add(new ButtonWidget(left + 62, top + 42, 32, 20, makeLabel(MINUS, c), btn -> addQty(-c)));
buttons.add(new ButtonWidget(left + 100, top + 42, 38, 20, makeLabel(MINUS, d), btn -> addQty(-d)));
// This element is not focusable
if (!hideValidationIcon) {
this.validationIcon = new ValidationIcon(left + 104, top + 27);
buttons.add(this.validationIcon);
}
// Add the rest to the tab order
buttons.subList(4, buttons.size()).forEach(addButton);
this.buttons = buttons;
this.buttons.forEach(addButton);
// we need to re-validate because the icon may now be present and needs it's
// initial state
this.validate();
}
private void addQty(final long i) {
long currentValue = this.level.getValue();
long minValue = this.level.getMinValue();
this.level.setText(String.valueOf(Math.max(minValue, currentValue + i)));
/**
* Returns the integer value currently in the text-field, if it is a valid
* number and is within the allowed min/max value.
*/
public OptionalInt getIntValue() {
String text = textField.getText().trim();
try {
int value = Integer.parseInt(text, 10);
if (value < minValue) {
return OptionalInt.empty();
}
return OptionalInt.of(value);
} catch (NumberFormatException ignored) {
return OptionalInt.empty();
}
}
public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) {
this.level.render(matrices, mouseX, mouseY, partialTicks);
}
public void setValue(long value, boolean skipNotify) {
this.level.setValue(value, skipNotify);
/**
* Returns the long value currently in the text-field, if it is a valid number
* and is within the allowed min/max value.
*/
public OptionalLong getLongValue() {
String text = textField.getText().trim();
try {
long value = Long.parseLong(text, 10);
if (value < minValue) {
return OptionalLong.empty();
}
return OptionalLong.of(value);
} catch (NumberFormatException ignored) {
return OptionalLong.empty();
}
}
public void setValue(long value) {
setValue(value, false);
this.textField.setText(String.valueOf(Math.max(minValue, value)));
this.textField.setCursorToEnd();
this.textField.setSelectionStart(0);
validate();
}
public long getValue() {
return level.getValue();
private void addQty(final long i) {
getLongValue().ifPresent(currentValue -> setValue(currentValue + i));
}
@Override
public void tick() {
this.level.tick();
public void render(MatrixStack matrices, int mouseX, int mouseY, float partialTicks) {
this.textField.render(matrices, mouseX, mouseY, partialTicks);
}
private void validate() {
List<Text> validationErrors = new ArrayList<>();
String text = textField.getText().trim();
try {
long value = Long.parseLong(text, 10);
if (value < minValue) {
validationErrors.add(new TranslatableText(NUMBER_LESS_THAN_MIN_VALUE, minValue));
}
} catch (NumberFormatException ignored) {
validationErrors.add(INVALID_NUMBER);
}
boolean valid = validationErrors.isEmpty();
this.textField.setEditableColor(valid ? TEXT_COLOR_NORMAL : TEXT_COLOR_ERROR);
if (this.validationIcon != null) {
this.validationIcon.setValid(valid);
this.validationIcon.setTooltip(validationErrors);
}
}
private Text makeLabel(Text prefix, int amount) {
return prefix.copy().append(String.valueOf(amount));
}
public void setHideValidationIcon(boolean hideValidationIcon) {
this.hideValidationIcon = hideValidationIcon;
}
}
@@ -18,6 +18,8 @@
package appeng.client.gui.implementations;
import java.util.OptionalInt;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.text.Text;
@@ -26,8 +28,6 @@ import appeng.client.gui.AEBaseScreen;
import appeng.client.gui.NumberEntryType;
import appeng.container.implementations.PriorityContainer;
import appeng.core.localization.GuiText;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
@@ -38,24 +38,38 @@ public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
public PriorityScreen(PriorityContainer container, PlayerInventory playerInventory, Text title) {
super(container, playerInventory, title);
this.subGui = new AESubScreen(this, container.getPriorityHost());
// This is the effective size of the background image
backgroundWidth = 175;
backgroundHeight = 128;
}
@Override
public void init() {
super.init();
this.priority = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.PRIORITY, this::onPriorityChange);
this.priority = new NumberEntryWidget(this, 20, 30, 138, 62, NumberEntryType.PRIORITY);
this.priority.setTextFieldBounds(62, 57, 50);
this.priority.setMinValue(Integer.MIN_VALUE);
handler.setTextField(this.priority);
this.priority.setValue(this.handler.getPriorityValue());
this.priority.addButtons(children::add, this::addButton);
this.subGui.addBackButton(this::addButton, 154, 0);
this.priority.setOnChange(this::savePriority);
this.priority.setOnConfirm(() -> {
savePriority();
this.subGui.goBack();
});
changeFocus(true);
}
private void onPriorityChange(long priority) {
NetworkHandler.instance()
.sendToServer(new ConfigValuePacket("PriorityHost.Priority", String.valueOf(priority)));
private void savePriority() {
OptionalInt priority = this.priority.getIntValue();
if (priority.isPresent()) {
handler.setPriority(priority.getAsInt());
}
}
@Override
@@ -66,13 +80,10 @@ public class PriorityScreen extends AEBaseScreen<PriorityContainer> {
@Override
public void drawBG(MatrixStack matrices, final int offsetX, final int offsetY, final int mouseX, final int mouseY,
float partialTicks) {
this.bindTexture(getBackground());
this.bindTexture("guis/priority.png");
drawTexture(matrices, offsetX, offsetY, 0, 0, this.backgroundWidth, this.backgroundHeight);
this.priority.render(matrices, mouseX, mouseY, partialTicks);
}
protected String getBackground() {
return "guis/priority.png";
}
}
@@ -48,9 +48,9 @@ public class WirelessScreen extends AEBaseScreen<WirelessContainer> {
this.textRenderer.draw(matrices, GuiText.inventory.text(), 8, this.backgroundHeight - 96 + 3, 4210752);
if (handler.getRange() > 0) {
final String firstMessage = GuiText.Range.text() + ": " + (handler.getRange() / 10.0) + " m";
final String secondMessage = GuiText.PowerUsageRate.text() + ": "
+ Platform.formatPowerLong(handler.getDrain(), true);
final Text firstMessage = GuiText.Range.withSuffix(": " + (handler.getRange() / 10.0) + " m");
final Text secondMessage = GuiText.PowerUsageRate
.withSuffix(": " + Platform.formatPowerLong(handler.getDrain(), true));
final int strWidth = Math.max(this.textRenderer.getWidth(firstMessage),
this.textRenderer.getWidth(secondMessage));
@@ -0,0 +1,51 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.gui.widgets;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
public class ConfirmableTextField extends TextFieldWidget {
private Runnable onConfirm;
public ConfirmableTextField(TextRenderer fontRenderer, int x, int y, int width, int height, Text text) {
super(fontRenderer, x, y, width, height, text);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if (isActive() && (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER)) {
if (this.onConfirm != null) {
this.onConfirm.run();
}
return true;
}
return super.keyPressed(keyCode, scanCode, modifiers);
}
public void setOnConfirm(Runnable onConfirm) {
this.onConfirm = onConfirm;
}
}
@@ -0,0 +1,96 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.gui.widgets;
import java.time.Duration;
/**
* This class can be used to implement repeating events such as holding down a
* button to fire an event repeatedly while the button is still being held, or
* repeatedly scrolling down a page, while the mouse is held down on the
* scrollbar.
*/
public class EventRepeater {
/**
* -1 if no repeat event is scheduled. Otherwise contains the
* {@link System#nanoTime()} at which the next event should occur.
*/
private long nextEventTime = -1;
private EventCallback eventCallback = null;
private final long eventDelay; // In nanoseconds
private final long eventInterval; // In nanoseconds
public EventRepeater(Duration delay, Duration interval) {
this.eventDelay = delay.toNanos();
this.eventInterval = interval.toNanos();
}
public void tick() {
if (this.eventCallback == null) {
return; // No event scheduled
}
// Use nanoTime here because it is monotonically increasing, while
// System.currentTimeMillis is not
long nanoTime = System.nanoTime();
if (nanoTime < this.nextEventTime) {
return; // Event time not reached
}
// Before triggering, recompute the next event, since
// the event callback itself may reschedule/cancel, and
// we should not overwrite that
this.nextEventTime = nanoTime + this.eventInterval;
this.eventCallback.trigger();
}
/**
* Schedule the given callback to be called after a given initial delay, and
* then after the given interval repeatedly.
*
* <p>
* Replaces any previously queued callback.
*/
public void repeat(EventCallback callback) {
long time = System.nanoTime();
this.eventCallback = callback;
this.nextEventTime = time + eventDelay;
}
public boolean isRepeating() {
return this.eventCallback != null;
}
/**
* Stop repeating the event.
*/
public void stop() {
this.eventCallback = null;
}
@FunctionalInterface
public interface EventCallback {
void trigger();
}
}
@@ -22,6 +22,7 @@ import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.sound.SoundManager;
import net.minecraft.client.texture.TextureManager;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.LiteralText;
@@ -33,6 +34,10 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
private boolean halfSize = false;
private boolean disableClickSound = false;
private boolean disableBackground = false;
public IconButton(final int x, final int y, PressAction onPress) {
super(x, y, 16, 16, LiteralText.EMPTY, onPress);
}
@@ -42,6 +47,13 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
this.active = vis;
}
@Override
public void playDownSound(SoundManager soundManager) {
if (!disableClickSound) {
super.playDownSound(soundManager);
}
}
@Override
public void renderButton(MatrixStack matrices, final int mouseX, final int mouseY, float partial) {
@@ -72,7 +84,9 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
final int uv_y = iconIndex / 16;
final int uv_x = iconIndex - uv_y * 16;
drawTexture(matrices, 0, 0, 256 - 16, 256 - 16, 16, 16);
if (!disableBackground) {
drawTexture(matrices, 0, 0, 256 - 16, 256 - 16, 16, 16);
}
drawTexture(matrices, 0, 0, uv_x * 16, uv_y * 16, 16, 16);
RenderSystem.popMatrix();
} else {
@@ -85,12 +99,18 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
final int uv_y = iconIndex / 16;
final int uv_x = iconIndex - uv_y * 16;
drawTexture(matrices, this.x, this.y, 256 - 16, 256 - 16, 16, 16);
if (!disableBackground) {
drawTexture(matrices, this.x, this.y, 256 - 16, 256 - 16, 16, 16);
}
drawTexture(matrices, this.x, this.y, uv_x * 16, uv_y * 16, 16, 16);
}
RenderSystem.enableDepthTest();
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
if (isHovered()) {
renderToolTip(matrices, mouseX, mouseY);
}
}
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
}
protected abstract int getIconIndex();
@@ -133,4 +153,20 @@ public abstract class IconButton extends ButtonWidget implements ITooltip {
this.halfSize = halfSize;
}
public boolean isDisableClickSound() {
return disableClickSound;
}
public void setDisableClickSound(boolean disableClickSound) {
this.disableClickSound = disableClickSound;
}
public boolean isDisableBackground() {
return disableBackground;
}
public void setDisableBackground(boolean disableBackground) {
this.disableBackground = disableBackground;
}
}
@@ -1,153 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.client.gui.widgets;
import java.util.function.LongConsumer;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.LiteralText;
public class NumberBox extends TextFieldWidget {
private final LongConsumer changeListener;
private long lastValue;
private long minValue = 0;
private long maxValue;
public NumberBox(final TextRenderer fontRenderer, final int x, final int y, final int width, final int height,
final Class<?> type, LongConsumer changeListener) {
super(fontRenderer, x, y, width, height, new LiteralText("0"));
this.setText("0");
setChangedListener(this::handleTextChanged);
this.lastValue = 0;
this.changeListener = changeListener;
if (type == int.class || type == Integer.class) {
maxValue = Integer.MAX_VALUE;
} else {
maxValue = Long.MAX_VALUE;
}
}
// NOTE: This method MUST NOT BE NAMED like a method in the superclass because
// of a ForgeGradle remapping bug
// since it'll not remap this method's name, but it will remap the name of the
// method reference above.
private void handleTextChanged(String text) {
if (text.isEmpty()) {
setText("0"); // Will call onTextChanged recursively
return;
}
boolean canBeNegative = canBeNegative();
if (canBeNegative && text.equals("-")) {
// Allow this as a special case to make typing in a negative number easier
return;
}
StringBuilder sanitized = new StringBuilder(text);
boolean encounteredNonZero = false;
for (int i = 0; i < sanitized.length(); i++) {
char ch = sanitized.charAt(i);
if (canBeNegative && i == 0 && ch == '-') {
continue; // Allow leading minus sign
}
if (ch >= '1' && ch <= '9') {
encounteredNonZero = true;
continue;
}
if (ch != '0' || !encounteredNonZero) {
sanitized.deleteCharAt(i--);
}
}
if (sanitized.length() == 0) {
sanitized.append('0');
}
String sanitizedStr = sanitized.toString();
if (!sanitizedStr.equals(text)) {
setText(sanitizedStr); // Will call onTextChanged recursively
return;
}
if (getValue() < minValue) {
setText(String.valueOf(minValue)); // Will call onTextChanged recursively
return;
}
if (getValue() > maxValue) {
setText(String.valueOf(maxValue)); // Will call onTextChanged recursively
return;
}
reportChange();
}
private void reportChange() {
long value = getValue();
if (value != lastValue) {
lastValue = value;
changeListener.accept(value);
}
}
public void setValue(long value, boolean skipNotify) {
// This check avoid changing the cursor position needlessly
if (value == this.getValue()) {
return;
}
if (skipNotify) {
lastValue = value;
}
setText(String.valueOf(value));
}
public long getValue() {
if (getText().equals("-") && canBeNegative()) {
return lastValue; // Allow this as a special case to type in a negative number more easily
}
return Long.parseLong(getText());
}
@Override
public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {
if (super.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_)) {
return true;
}
// Swallow key presses for numbers because they would otherwise trigger the
// hotbar swapping unintentionally
return isFocused() && p_keyPressed_1_ >= '0' && p_keyPressed_1_ <= '9';
}
public void setMinValue(long minValue) {
this.minValue = minValue;
}
public long getMinValue() {
return minValue;
}
private boolean canBeNegative() {
return minValue < 0;
}
}
@@ -18,18 +18,74 @@
package appeng.client.gui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import java.time.Duration;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.util.Rect2i;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import appeng.client.gui.AEBaseScreen;
public class Scrollbar implements IScrollSource {
/**
* Implements a vertical scrollbar using Vanilla's scrollbar handle texture from
* the creative tab.
* <p>
* It is expected that the background of the UI contains a pre-baked scrollbar
* track border, and that the exact rectangle of that track is set on this
* object via {@link #setLeft(int)}, {@link #setTop(int)} and
* {@link #setHeight(int)}. While the width of the track can also be set, the
* drawn handle will use vanilla's sprite width (see {@link #HANDLE_WIDTH}.
*/
public class Scrollbar extends DrawableHelper implements IScrollSource {
/**
* Width of the scrollbar handle sprite in the source texture.
*/
private static final int HANDLE_WIDTH = 12;
/**
* Height of the scrollbar handle sprite in the source texture.
*/
private static final int HANDLE_HEIGHT = 15;
/**
* Texture containing the scrollbar handle sprites.
*/
private static final Identifier TEXTURE = new Identifier("minecraft",
"textures/gui/container/creative_inventory/tabs.png");
/**
* Rectangle in the source texture that contains the sprite for an enabled
* handle.
*/
private static final Rect2i ENABLED = new Rect2i(232, 0, HANDLE_WIDTH, HANDLE_HEIGHT);
/**
* Rectangle in the source texture that contains the sprite for a disabled
* handle.
*/
private static final Rect2i DISABLED = new Rect2i(232 + HANDLE_WIDTH, 0, HANDLE_WIDTH, HANDLE_HEIGHT);
/**
* The screen x-coordinate of the scrollbar's inner track.
*/
private int displayX = 0;
/**
* The screen y-coordinate of the scrollbar's inner track.
*/
private int displayY = 0;
private int width = 12;
/**
* The inner width of the scrollbar track.
*/
private int width = HANDLE_WIDTH;
/**
* The inner height of the scrollbar track.
*/
private int height = 16;
private int pageSize = 1;
@@ -37,20 +93,58 @@ public class Scrollbar implements IScrollSource {
private int minScroll = 0;
private int currentScroll = 0;
private final DrawableHelper drawable = new DrawableHelper() {
};
/**
* True if the scrollbar's handle is currently being dragged.
*/
private boolean dragging;
/**
* The y-coordinate relative to the upper edge of the scrollbar handle, where
* the user pressed the mouse button to drag. While dragging, this is applied as
* an offset to the effective scrollbar position.
*/
private int dragYOffset;
private final EventRepeater eventRepeater = new EventRepeater(Duration.ofMillis(250), Duration.ofMillis(150));
/**
* Draws the handle of the scrollbar.
* <p>
* The GUI is assumed to already contain a prebaked scrollbar track in its
* background.
*/
public void draw(MatrixStack matrices, final AEBaseScreen<?> g) {
g.bindTexture("minecraft", "gui/container/creative_inventory/tabs.png");
RenderSystem.color4f(1.0f, 1.0f, 1.0f, 1.0f);
setZOffset(g.getZOffset());
drawable.setZOffset(g.getZOffset());
// Draw the track (nice for debugging)
// fill(matrices, displayX, displayY, this.displayX + width, this.displayY +
// height, 0xffff0000);
g.bindTexture(TEXTURE);
int yOffset;
Rect2i sourceRect;
if (this.getRange() == 0) {
drawable.drawTexture(matrices, this.displayX, this.displayY, 232 + this.width, 0, this.width, 15);
yOffset = 0;
sourceRect = DISABLED;
} else {
final int offset = (this.currentScroll - this.minScroll) * (this.height - 15) / this.getRange();
drawable.drawTexture(matrices, this.displayX, offset + this.displayY, 232, 0, this.width, 15);
yOffset = getHandleYOffset();
sourceRect = ENABLED;
}
drawTexture(matrices, this.displayX, this.displayY + yOffset, sourceRect.getX(), sourceRect.getY(),
sourceRect.getWidth(), sourceRect.getHeight());
}
/**
* Returns the y-position of the scrollbar handle in relation to the upper edge
* of the scrollbar's track.
*/
private int getHandleYOffset() {
if (getRange() == 0) {
return 0;
}
int availableHeight = this.height - HANDLE_HEIGHT;
return (this.currentScroll - this.minScroll) * availableHeight / this.getRange();
}
private int getRange() {
@@ -114,24 +208,96 @@ public class Scrollbar implements IScrollSource {
return this.currentScroll;
}
public void click(final double x, final double y) {
if (this.getRange() == 0) {
public boolean mouseDown(double x, double y) {
this.dragging = false;
// Clicks to the left or right of the scrollbar don't do anything
if (x < displayX || x >= displayX + width) {
return false;
}
// Clicks to the top or bottom don't do anything either
int relY = (int) Math.round(y - displayY);
if (relY < 0 || relY >= height) {
return false;
}
// Do nothing when there's no range, but swallow the event
if (getRange() == 0) {
return true;
}
int handleYOffset = getHandleYOffset();
if (relY < handleYOffset) {
// Clicks above the handle will page up, repeatedly
pageUp();
eventRepeater.repeat(this::pageUp);
} else if (relY < handleYOffset + HANDLE_HEIGHT) {
// Clicks on the handle will initiate dragging it
this.dragging = true;
this.dragYOffset = relY - handleYOffset;
} else {
// Clicks below the handle will page down, repeatedly
pageDown();
eventRepeater.repeat(this::pageDown);
}
return true;
}
public boolean mouseUp(double x, double y) {
this.dragging = false;
this.eventRepeater.stop();
return false;
}
public void mouseDragged(double x, double y) {
if (this.getRange() == 0 || !this.dragging || this.eventRepeater.isRepeating()) {
return;
}
if (x > this.displayX && x <= this.displayX + this.width) {
if (y > this.displayY && y <= this.displayY + this.height) {
this.currentScroll = (int) (y - this.displayY);
this.currentScroll = this.minScroll + ((this.currentScroll * 2 * this.getRange() / this.height));
this.currentScroll = (this.currentScroll + 1) >> 1;
this.applyRange();
}
}
// Compute the position of the mouse (adjusted for where it grabbed the handle,
// so as if it grabbed
// the upper edge of it) within the scrollable area of the track (minus the
// handle height).
double handleUpperEdgeY = y - this.displayY - this.dragYOffset;
double availableHeight = this.height - HANDLE_HEIGHT;
double position = MathHelper.clamp(handleUpperEdgeY / availableHeight, 0.0, 1.0);
this.currentScroll = this.minScroll + (int) Math.round(position * this.getRange());
this.applyRange();
}
public void wheel(double delta) {
// Do nothing when there's no range
if (getRange() == 0) {
return;
}
delta = Math.max(Math.min(-delta, 1), -1);
this.currentScroll += delta * this.pageSize;
this.applyRange();
}
/**
* Ticks the scrollbar for the purposes of input-repeats (since mouse-downs are
* not repeat-triggered), used to repeatedly page-up or page-down when the mouse
* is held in the area above or below the scrollbar handle.
*/
public void tick() {
this.eventRepeater.tick();
}
private void pageUp() {
this.currentScroll -= this.pageSize;
this.applyRange();
}
private void pageDown() {
this.currentScroll += this.pageSize;
this.applyRange();
}
}
@@ -25,6 +25,7 @@ import java.util.function.Predicate;
import java.util.regex.Pattern;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
@@ -47,6 +48,7 @@ import appeng.api.config.StorageFilter;
import appeng.api.config.TerminalStyle;
import appeng.api.config.ViewItems;
import appeng.api.config.YesNo;
import appeng.client.gui.AEBaseScreen;
import appeng.core.localization.ButtonToolTips;
import appeng.util.EnumCycler;
@@ -89,9 +91,11 @@ public class SettingToggleButton<T extends Enum<T>> extends IconButton {
registerApp(16 * 7, Settings.CONDENSER_OUTPUT, CondenserOutput.TRASH, ButtonToolTips.CondenserOutput,
ButtonToolTips.Trash);
registerApp(16 * 7 + 1, Settings.CONDENSER_OUTPUT, CondenserOutput.MATTER_BALLS,
ButtonToolTips.CondenserOutput, ButtonToolTips.MatterBalls);
ButtonToolTips.CondenserOutput,
ButtonToolTips.MatterBalls.text(CondenserOutput.MATTER_BALLS.requiredPower));
registerApp(16 * 7 + 2, Settings.CONDENSER_OUTPUT, CondenserOutput.SINGULARITY,
ButtonToolTips.CondenserOutput, ButtonToolTips.Singularity);
ButtonToolTips.CondenserOutput,
ButtonToolTips.Singularity.text(CondenserOutput.SINGULARITY.requiredPower));
registerApp(16 * 9 + 1, Settings.ACCESS, AccessRestriction.READ, ButtonToolTips.IOMode,
ButtonToolTips.Read);
@@ -230,7 +234,13 @@ public class SettingToggleButton<T extends Enum<T>> extends IconButton {
}
private void triggerPress() {
boolean backwards = MinecraftClient.getInstance().mouse.wasRightButtonClicked();
boolean backwards = false;
// This isn't great, but we don't get any information about right-clicks
// otherwise
Screen currentScreen = MinecraftClient.getInstance().currentScreen;
if (currentScreen instanceof AEBaseScreen) {
backwards = ((AEBaseScreen<?>) currentScreen).isHandlingRightClick();
}
onPress.handle(this, backwards);
}
@@ -31,7 +31,7 @@ import net.minecraft.util.Identifier;
public class TabButton extends ButtonWidget implements ITooltip {
public static final Identifier TEXTURE_STATES = new Identifier("appliedenergistics2", "textures/guis/states.png");
private final ItemRenderer itemRenderer;
private int hideEdge = 0;
private boolean hideEdge;
private int myIcon = -1;
private ItemStack myItem;
@@ -69,14 +69,17 @@ public class TabButton extends ButtonWidget implements ITooltip {
RenderSystem.enableAlphaTest();
int uv_x = (this.hideEdge > 0 ? 11 : 13);
// Selects the button border from the sprite-sheet, where each type occupies a
// 2x2 slot
int uv_x = 8 + (this.hideEdge ? 0 : 2);
int uv_y = 12 + (this.isFocused() ? 2 : 0);
final int offsetX = this.hideEdge > 0 ? 1 : 0;
final int offsetX = this.hideEdge ? 1 : 0;
drawTexture(matrices, this.x, this.y, uv_x * 16, 0, 25, 22);
drawTexture(matrices, this.x, this.y, uv_x * 16, uv_y * 16, 25, 22);
if (this.myIcon >= 0) {
final int uv_y = this.myIcon / 16;
uv_y = this.myIcon / 16;
uv_x = this.myIcon - uv_y * 16;
drawTexture(matrices, offsetX + this.x + 3, this.y + 3, uv_x * 16, uv_y * 16, 16, 16);
@@ -122,11 +125,11 @@ public class TabButton extends ButtonWidget implements ITooltip {
return this.visible;
}
public int getHideEdge() {
public boolean getHideEdge() {
return this.hideEdge;
}
public void setHideEdge(final int hideEdge) {
public void setHideEdge(final boolean hideEdge) {
this.hideEdge = hideEdge;
}
}
@@ -0,0 +1,64 @@
package appeng.client.gui.widgets;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
/**
* Displays a small icon that shows validation errors for some input control.
*/
public class ValidationIcon extends IconButton {
private final List<Text> tooltip = new ArrayList<>();
private boolean valid;
public ValidationIcon(int x, int y) {
super(x, y, btn -> {
});
setDisableBackground(true);
setDisableClickSound(true);
setHalfSize(true);
}
public void setValid(boolean valid) {
setVisibility(!valid);
if (valid) {
this.tooltip.clear();
}
}
public void setTooltip(List<Text> lines) {
this.tooltip.clear();
this.tooltip.addAll(lines);
}
@Override
protected int getIconIndex() {
return 16 * 8;
}
@Override
public boolean changeFocus(boolean flag) {
return false; // Cannot focus this element
}
@Override
public void renderToolTip(MatrixStack matrices, int mouseX, int mouseY) {
if (this.tooltip.isEmpty()) {
return;
}
MinecraftClient client = MinecraftClient.getInstance();
Screen screen = client.currentScreen;
if (screen == null) {
return;
}
screen.renderTooltip(matrices, this.tooltip, x, y);
}
}
@@ -104,7 +104,7 @@ class CableBuilder {
throw new IllegalStateException("Cable type " + cableType + " does not support connections.");
}
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, textureFolder + color.name().toLowerCase(Locale.ROOT)));
}
@@ -74,7 +74,7 @@ public enum CableCoreType {
}
public SpriteIdentifier getTexture(AEColor color) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, this.textureFolder + "/" + color.name().toLowerCase(Locale.ROOT)));
}
@@ -18,7 +18,7 @@ import appeng.core.AppEng;
public class P2PTunnelFrequencyModel implements BasicUnbakedModel {
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "part/p2p_tunnel_frequency"));
@Nullable
@@ -47,7 +47,8 @@ public class SmartCableTextures {
new Identifier(AppEng.MOD_ID, "part/cable/smart/channels_12"), //
new Identifier(AppEng.MOD_ID, "part/cable/smart/channels_13"), //
new Identifier(AppEng.MOD_ID, "part/cable/smart/channels_14")//
}).map(e -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, e)).toArray(SpriteIdentifier[]::new);
}).map(e -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, e))
.toArray(SpriteIdentifier[]::new);
// Textures used to display channels on smart cables. There's two sets of 5
// textures each, and
@@ -115,7 +115,7 @@ public class CraftingCubeModel implements BasicUnbakedModel {
}
private static SpriteIdentifier texture(String name) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/crafting/" + name));
}
}
@@ -25,7 +25,7 @@ import appeng.core.AppEng;
public class BiometricCardModel implements BasicUnbakedModel {
public static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/biometric_card_base");
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/biometric_card_hash"));
@Override
@@ -26,11 +26,11 @@ public class ColorApplicatorModel implements BasicUnbakedModel {
private static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/color_applicator_colored");
private static final SpriteIdentifier TEXTURE_DARK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_DARK = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_dark"));
private static final SpriteIdentifier TEXTURE_MEDIUM = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_MEDIUM = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_medium"));
private static final SpriteIdentifier TEXTURE_BRIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_BRIGHT = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/color_applicator_tip_bright"));
@Override
@@ -62,13 +62,13 @@ class GlassBakedModel implements BakedModel, FabricBakedModel {
private static final byte[][][] OFFSETS = generateOffsets();
// Alternating textures based on position
static final SpriteIdentifier TEXTURE_A = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_A = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_a"));
static final SpriteIdentifier TEXTURE_B = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_B = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_b"));
static final SpriteIdentifier TEXTURE_C = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_C = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_c"));
static final SpriteIdentifier TEXTURE_D = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
static final SpriteIdentifier TEXTURE_D = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier("appliedenergistics2:block/glass/quartz_glass_d"));
// Frame texture
@@ -81,7 +81,7 @@ class GlassBakedModel implements BakedModel, FabricBakedModel {
private static SpriteIdentifier[] generateTexturesFrame() {
return IntStream.range(1, 16).mapToObj(Integer::toBinaryString).map(s -> Strings.padStart(s, 4, '0'))
.map(s -> new Identifier("appliedenergistics2:block/glass/quartz_glass_frame" + s))
.map(rl -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX, rl))
.map(rl -> new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, rl))
.toArray(SpriteIdentifier[]::new);
}
@@ -25,7 +25,7 @@ import appeng.core.AppEng;
public class MemoryCardModel implements BasicUnbakedModel {
public static final Identifier MODEL_BASE = new Identifier(AppEng.MOD_ID, "item/memory_card_base");
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "item/memory_card_hash"));
@Override
@@ -59,7 +59,7 @@ public class SpatialPylonModel implements BasicUnbakedModel {
}
private static SpriteIdentifier getTexturePath(SpatialPylonTextureType type) {
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
return new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/spatial_pylon/" + type.name().toLowerCase(Locale.ROOT)));
}
@@ -38,7 +38,7 @@ public final class InscriberTESR extends BlockEntityRenderer<InscriberBlockEntit
private static final float ITEM_RENDER_SCALE = 1.0f / 1.2f;
private static final SpriteIdentifier TEXTURE_INSIDE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEX,
private static final SpriteIdentifier TEXTURE_INSIDE = new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE,
new Identifier(AppEng.MOD_ID, "block/inscriber_inside"));
public static final ImmutableList<SpriteIdentifier> SPRITES = ImmutableList.of(TEXTURE_INSIDE);
@@ -66,10 +66,8 @@ import appeng.container.slot.PlayerInvSlot;
import appeng.core.AELog;
import appeng.core.Api;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.core.sync.packets.InventoryActionPacket;
import appeng.core.sync.packets.TargetItemStackPacket;
import appeng.helpers.ICustomNameObject;
import appeng.helpers.InventoryAction;
import appeng.me.helpers.PlayerSource;
import appeng.mixins.ScreenHandlerListeners;
@@ -88,11 +86,9 @@ public abstract class AEBaseContainer extends ScreenHandler {
private final IGuiItemObject obj;
private final HashMap<Integer, SyncData> syncData = new HashMap<>();
private boolean isContainerValid = true;
private String customName;
private ContainerLocator locator;
private IMEInventoryHandler<IAEItemStack> cellInv;
private IEnergySource powerSrc;
private boolean sentCustomName;
private int ticksSinceCheck = 900;
private IAEItemStack clientRequestedTargetItem = null;
@@ -317,9 +313,7 @@ public abstract class AEBaseContainer extends ScreenHandler {
@Override
public void sendContentUpdates() {
this.sendCustomName();
if (Platform.isServer()) {
if (isServer()) {
if (this.tileEntity != null
&& this.tileEntity.getWorld().getBlockEntity(this.tileEntity.getPos()) != this.tileEntity) {
this.setValidContainer(false);
@@ -886,43 +880,6 @@ public abstract class AEBaseContainer extends ScreenHandler {
this.sendContentUpdates();
}
private void sendCustomName() {
// FIXME: Trash this, this is handled by NamedContainerProvider now
if (!this.sentCustomName) {
this.sentCustomName = true;
if (Platform.isServer()) {
ICustomNameObject name = null;
if (this.part instanceof ICustomNameObject) {
name = (ICustomNameObject) this.part;
}
if (this.tileEntity instanceof ICustomNameObject) {
name = (ICustomNameObject) this.tileEntity;
}
if (this.obj instanceof ICustomNameObject) {
name = (ICustomNameObject) this.obj;
}
if (this instanceof ICustomNameObject) {
name = (ICustomNameObject) this;
}
if (name != null) {
if (name.hasCustomInventoryName()) {
this.setCustomName(name.getCustomInventoryName().getString());
}
if (this.getCustomName() != null) {
NetworkHandler.instance().sendTo(new ConfigValuePacket("CustomName", this.getCustomName()),
(ServerPlayerEntity) this.getPlayerInventory().player);
}
}
}
}
}
public void swapSlotContents(final int slotA, final int slotB) {
final Slot a = this.getSlot(slotA);
final Slot b = this.getSlot(slotB);
@@ -1012,14 +969,6 @@ public abstract class AEBaseContainer extends ScreenHandler {
this.cellInv = cellInv;
}
public String getCustomName() {
return this.customName;
}
public void setCustomName(final String customName) {
this.customName = customName;
}
public PlayerInventory getPlayerInventory() {
return this.invPlayer;
}
@@ -1048,4 +997,18 @@ public abstract class AEBaseContainer extends ScreenHandler {
this.powerSrc = powerSrc;
}
/**
* Returns whether this container instance lives on the client.
*/
protected boolean isClient() {
return invPlayer.player.getEntityWorld().isClient();
}
/**
* Returns whether this container instance lives on the server.
*/
protected boolean isServer() {
return !isClient();
}
}
@@ -89,6 +89,8 @@ public class SyncData {
NetworkHandler.instance().sendTo(new ConfigValuePacket("SyncDat." + this.channel, json),
(ServerPlayerEntity) o);
}
this.clientVersion = val;
return;
}
// Types other than Text must be non-null
@@ -50,7 +50,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.tile.misc.CellWorkbenchBlockEntity;
import appeng.util.EnumCycler;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.inv.WrapperSupplierItemHandler;
import appeng.util.iterators.NullIterator;
@@ -143,7 +142,7 @@ public class CellWorkbenchContainer extends UpgradeableContainer {
@Override
public void sendContentUpdates() {
final ItemStack is = this.workBench.getInventoryByName("cell").getInvStack(0);
if (Platform.isServer()) {
if (isServer()) {
for (final ScreenHandlerListener listener : this.getListeners()) {
if (this.prevStack != is) {
// if the bars changed an item was probably made, so just send shit!
@@ -34,7 +34,6 @@ import appeng.container.interfaces.IProgressProvider;
import appeng.container.slot.OutputSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.tile.misc.CondenserBlockEntity;
import appeng.util.Platform;
public class CondenserContainer extends AEBaseContainer implements IProgressProvider {
@@ -76,13 +75,13 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv
@Override
public void sendContentUpdates() {
if (Platform.isServer()) {
if (isServer()) {
final double maxStorage = this.condenser.getStorage();
final double requiredEnergy = this.condenser.getRequiredPower();
this.requiredEnergy = requiredEnergy == 0 ? (int) maxStorage : (int) Math.min(requiredEnergy, maxStorage);
this.storedPower = (int) this.condenser.getStoredPower();
this.setOutput((CondenserOutput) this.condenser.getConfigManager().getSetting(Settings.CONDENSER_OUTPUT));
this.output = (CondenserOutput) this.condenser.getConfigManager().getSetting(Settings.CONDENSER_OUTPUT);
}
super.sendContentUpdates();
@@ -102,7 +101,4 @@ public class CondenserContainer extends AEBaseContainer implements IProgressProv
return this.output;
}
private void setOutput(final CondenserOutput output) {
this.output = output;
}
}
@@ -1,5 +1,7 @@
package appeng.container.implementations;
import java.util.function.Function;
import javax.annotation.Nullable;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
@@ -12,9 +14,7 @@ import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import appeng.api.config.SecurityPermissions;
import appeng.api.features.IWirelessTermHandler;
@@ -44,6 +44,8 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
private final SecurityPermissions requiredPermission;
private Function<I, Text> containerTitleStrategy = this::getDefaultContainerTitle;
public ContainerHelper(ContainerFactory<C, I> factory, Class<I> interfaceClass) {
this(factory, interfaceClass, null);
}
@@ -55,19 +57,47 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
this.factory = factory;
}
/**
* Specifies a custom strategy for obtaining a custom container name.
* <p>
* The stratgy should return {@link LiteralText#EMPTY} if there's no custom
* name.
*/
public ContainerHelper<C, I> withContainerTitle(Function<I, Text> containerTitleStrategy) {
this.containerTitleStrategy = containerTitleStrategy;
return this;
}
/**
* Opens a container that is based around a single block entity. The tile
* entity's position is encoded in the packet buffer.
*/
public C fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf packetBuf) {
return fromNetwork(windowId, inv, packetBuf, (accessObj, container, buffer) -> {
});
}
/**
* Same as {@link #open}, but allows or additional data to be read from the
* packet, and passed onto the container.
*/
public C fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf packetBuf,
InitialDataDeserializer<C, I> initialDataDeserializer) {
I host = getHostFromLocator(inv.player, ContainerLocator.read(packetBuf));
if (host != null) {
return factory.create(windowId, inv, host);
C container = factory.create(windowId, inv, host);
initialDataDeserializer.deserializeInitialData(host, container, packetBuf);
return container;
}
return null;
}
public boolean open(PlayerEntity player, ContainerLocator locator) {
return open(player, locator, (accessObj, buffer) -> {
});
}
public boolean open(PlayerEntity player, ContainerLocator locator, InitialDataSerializer<I> initialDataSerializer) {
if (!(player instanceof ServerPlayerEntity)) {
// Cannot open containers on the client or for non-players
// FIXME logging?
@@ -84,9 +114,9 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
return false;
}
Text title = findContainerTitle(player.world, locator, accessInterface);
Text title = containerTitleStrategy.apply(accessInterface);
player.openHandledScreen(new HandlerFactory(locator, title, accessInterface));
player.openHandledScreen(new HandlerFactory(locator, title, accessInterface, initialDataSerializer));
return true;
}
@@ -99,15 +129,20 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
private final Text title;
public HandlerFactory(ContainerLocator locator, Text title, I accessInterface) {
private final InitialDataSerializer<I> initialDataSerializer;
public HandlerFactory(ContainerLocator locator, Text title, I accessInterface,
InitialDataSerializer<I> initialDataSerializer) {
this.locator = locator;
this.title = title;
this.accessInterface = accessInterface;
this.initialDataSerializer = initialDataSerializer;
}
@Override
public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) {
locator.write(buf);
initialDataSerializer.serializeInitialData(accessInterface, buf);
}
@Override
@@ -127,28 +162,6 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
}
private Text findContainerTitle(World world, ContainerLocator locator, I accessInterface) {
if (accessInterface instanceof ICustomNameObject) {
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
if (customNameObject.hasCustomInventoryName()) {
return customNameObject.getCustomInventoryName();
}
}
// Use block name at position
// FIXME: this is not right, we'd need to check the part's item stack, or custom
// naming interface impl
// FIXME: Should move this up, because at this point, it's hard to know where
// the terminal host came from (part or tile)
if (locator.hasBlockPos()) {
return new TranslatableText(world.getBlockState(locator.getBlockPos()).getBlock().getTranslationKey());
}
return new LiteralText("Unknown");
}
private I getHostFromLocator(PlayerEntity player, ContainerLocator locator) {
if (locator.hasItemIndex()) {
return getHostFromPlayerInventory(player, locator);
@@ -225,6 +238,24 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
C create(int windowId, PlayerInventory playerInv, I accessObj);
}
/**
* Strategy used to serialize initial data for opening the container on the
* client-side into the packet that is sent to the client.
*/
@FunctionalInterface
public interface InitialDataSerializer<I> {
void serializeInitialData(I host, PacketByteBuf buffer);
}
/**
* Strategy used to deserialize initial data for opening the container on the
* client-side from the packet received by the server.
*/
@FunctionalInterface
public interface InitialDataDeserializer<C, I> {
void deserializeInitialData(I host, C container, PacketByteBuf buffer);
}
private boolean checkPermission(PlayerEntity player, Object accessInterface) {
if (requiredPermission != null) {
@@ -235,4 +266,15 @@ public final class ContainerHelper<C extends AEBaseContainer, I> {
}
private Text getDefaultContainerTitle(I accessInterface) {
if (accessInterface instanceof ICustomNameObject) {
ICustomNameObject customNameObject = (ICustomNameObject) accessInterface;
if (customNameObject.hasCustomInventoryName()) {
return customNameObject.getCustomInventoryName();
}
}
return LiteralText.EMPTY;
}
}
@@ -19,14 +19,8 @@
package appeng.container.implementations;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Future;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableSet;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
@@ -66,9 +60,8 @@ import appeng.me.helpers.PlayerSource;
import appeng.parts.reporting.CraftingTerminalPart;
import appeng.parts.reporting.PatternTerminalPart;
import appeng.parts.reporting.TerminalPart;
import appeng.util.Platform;
public class CraftConfirmContainer extends AEBaseContainer {
public class CraftConfirmContainer extends AEBaseContainer implements CraftingCPUCyclingContainer {
public static ScreenHandlerType<CraftConfirmContainer> TYPE;
@@ -83,98 +76,51 @@ public class CraftConfirmContainer extends AEBaseContainer {
return helper.open(player, locator);
}
private final ArrayList<CraftingCPURecord> cpus = new ArrayList<>();
private final CraftingCPUCycler cpuCycler;
private ICraftingCPU selectedCpu;
private Future<ICraftingJob> job;
private ICraftingJob result;
@GuiSync(0)
public long bytesUsed;
@GuiSync(1)
public long cpuBytesAvail;
@GuiSync(2)
public int cpuCoProcessors;
@GuiSync(3)
public boolean autoStart = false;
@GuiSync(4)
public boolean simulation = true;
@GuiSync(5)
public int selectedCpu = -1;
// Indicates whether any CPUs are available
@GuiSync(6)
public boolean noCPU = true;
// Properties of the currently selected crafting CPU, this can be null
// if no CPUs are available, or if an automatic one is selected
@GuiSync(1)
public long cpuBytesAvail;
@GuiSync(2)
public int cpuCoProcessors;
@GuiSync(7)
public Text myName;
public Text cpuName;
public CraftConfirmContainer(int id, PlayerInventory ip, ITerminalHost te) {
super(TYPE, id, ip, te);
this.cpuCycler = new CraftingCPUCycler(this::cpuMatches, this::onCPUSelectionChanged);
// A player can select no crafting CPU to use a suitable one automatically
this.cpuCycler.setAllowNoSelection(true);
}
public void cycleCpu(final boolean next) {
if (next) {
this.setSelectedCpu(this.getSelectedCpu() + 1);
} else {
this.setSelectedCpu(this.getSelectedCpu() - 1);
}
if (this.getSelectedCpu() < -1) {
this.setSelectedCpu(this.cpus.size() - 1);
} else if (this.getSelectedCpu() >= this.cpus.size()) {
this.setSelectedCpu(-1);
}
if (this.getSelectedCpu() == -1) {
this.setCpuAvailableBytes(0);
this.setCpuCoProcessors(0);
this.setName(null);
} else {
CraftingCPURecord cpu = this.cpus.get(this.getSelectedCpu());
this.setName(cpu.getName());
this.setCpuAvailableBytes(cpu.getSize());
this.setCpuCoProcessors(cpu.getProcessors());
}
@Override
public void cycleSelectedCPU(final boolean next) {
this.cpuCycler.cycleCpu(next);
}
@Override
public void sendContentUpdates() {
if (Platform.isClient()) {
if (isClient()) {
return;
}
final ICraftingGrid cc = this.getGrid().getCache(ICraftingGrid.class);
final ImmutableSet<ICraftingCPU> cpuSet = cc.getCpus();
int matches = 0;
boolean changed = false;
for (final ICraftingCPU c : cpuSet) {
boolean found = false;
for (final CraftingCPURecord ccr : this.cpus) {
if (ccr.getCpu() == c) {
found = true;
break;
}
}
final boolean matched = this.cpuMatches(c);
if (matched) {
matches++;
}
if (found == !matched) {
changed = true;
}
}
if (changed || this.cpus.size() != matches) {
this.cpus.clear();
for (final ICraftingCPU c : cpuSet) {
if (this.cpuMatches(c)) {
this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c));
}
}
this.sendCPUs();
}
this.setNoCPU(this.cpus.isEmpty());
this.cpuCycler.detectAndSendChanges(this.getGrid());
super.sendContentUpdates();
@@ -277,22 +223,6 @@ public class CraftConfirmContainer extends AEBaseContainer {
return c.getAvailableStorage() >= this.getUsedBytes() && !c.isBusy();
}
private void sendCPUs() {
Collections.sort(this.cpus);
if (this.getSelectedCpu() >= this.cpus.size()) {
this.setSelectedCpu(-1);
this.setCpuAvailableBytes(0);
this.setCpuCoProcessors(0);
this.setName(null);
} else if (this.getSelectedCpu() != -1) {
CraftingCPURecord cpu = this.cpus.get(this.getSelectedCpu());
this.setName(cpu.getName());
this.setCpuAvailableBytes(cpu.getSize());
this.setCpuCoProcessors(cpu.getProcessors());
}
}
public void startJob() {
ScreenHandlerType<?> originalGui = null;
@@ -315,9 +245,7 @@ public class CraftConfirmContainer extends AEBaseContainer {
if (this.result != null && !this.isSimulation()) {
final ICraftingGrid cc = this.getGrid().getCache(ICraftingGrid.class);
final ICraftingLink g = cc.submitJob(this.result, null,
this.getSelectedCpu() == -1 ? null : this.cpus.get(this.getSelectedCpu()).getCpu(), true,
this.getActionSrc());
final ICraftingLink g = cc.submitJob(this.result, null, this.selectedCpu, true, this.getActionSrc());
this.setAutoStart(false);
if (g != null && originalGui != null && this.getLocator() != null) {
ContainerOpener.openContainer(originalGui, getPlayerInventory().player, getLocator());
@@ -347,6 +275,22 @@ public class CraftConfirmContainer extends AEBaseContainer {
}
}
private void onCPUSelectionChanged(CraftingCPURecord cpuRecord, boolean cpusAvailable) {
noCPU = !cpusAvailable;
if (cpuRecord == null) {
cpuBytesAvail = 0;
cpuCoProcessors = 0;
cpuName = null;
selectedCpu = null;
} else {
cpuBytesAvail = cpuRecord.getSize();
cpuCoProcessors = cpuRecord.getProcessors();
cpuName = cpuRecord.getName();
selectedCpu = cpuRecord.getCpu();
}
}
public World getWorld() {
return this.getPlayerInv().player.world;
}
@@ -371,42 +315,18 @@ public class CraftConfirmContainer extends AEBaseContainer {
return this.cpuBytesAvail;
}
private void setCpuAvailableBytes(final long cpuBytesAvail) {
this.cpuBytesAvail = cpuBytesAvail;
}
public int getCpuCoProcessors() {
return this.cpuCoProcessors;
}
private void setCpuCoProcessors(final int cpuCoProcessors) {
this.cpuCoProcessors = cpuCoProcessors;
}
public int getSelectedCpu() {
return this.selectedCpu;
}
private void setSelectedCpu(final int selectedCpu) {
this.selectedCpu = selectedCpu;
}
public Text getName() {
return this.myName;
}
private void setName(@Nullable final Text myName) {
this.myName = myName;
return this.cpuName;
}
public boolean hasNoCPU() {
return this.noCPU;
}
private void setNoCPU(final boolean noCPU) {
this.noCPU = noCPU;
}
public boolean isSimulation() {
return this.simulation;
}
@@ -26,7 +26,7 @@ import net.minecraft.network.PacketByteBuf;
import net.minecraft.screen.ScreenHandlerListener;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.text.LiteralText;
import appeng.api.config.SecurityPermissions;
import appeng.api.networking.IGrid;
@@ -46,24 +46,28 @@ import appeng.core.Api;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.core.sync.packets.MEInventoryUpdatePacket;
import appeng.helpers.ICustomNameObject;
import appeng.me.cluster.implementations.CraftingCPUCluster;
import appeng.tile.crafting.CraftingBlockEntity;
import appeng.util.Platform;
public class CraftingCPUContainer extends AEBaseContainer
implements IMEMonitorHandlerReceiver<IAEItemStack>, ICustomNameObject {
public class CraftingCPUContainer extends AEBaseContainer implements IMEMonitorHandlerReceiver<IAEItemStack> {
public static ScreenHandlerType<CraftingCPUContainer> TYPE;
private static final ContainerHelper<CraftingCPUContainer, CraftingBlockEntity> helper = new ContainerHelper<>(
CraftingCPUContainer::new, CraftingBlockEntity.class, SecurityPermissions.CRAFT);
CraftingCPUContainer::new, CraftingBlockEntity.class, SecurityPermissions.CRAFT)
.withContainerTitle(craftingTileEntity -> {
// Use the cluster's custom name instead of the right-clicked block entities one
CraftingCPUCluster cluster = craftingTileEntity.getCluster();
if (cluster != null && cluster.getName() != null) {
return cluster.getName();
}
return LiteralText.EMPTY;
});
private final IItemList<IAEItemStack> list = Api.instance().storage().getStorageChannel(IItemStorageChannel.class)
.createList();
private IGrid network;
private final IGrid network;
private CraftingCPUCluster monitor = null;
private Text cpuName = null;
@GuiSync(0)
public long eta = -1;
@@ -77,14 +81,16 @@ public class CraftingCPUContainer extends AEBaseContainer
final IActionHost host = (IActionHost) (te instanceof IActionHost ? te : null);
if (host != null && host.getActionableNode() != null) {
this.setNetwork(host.getActionableNode().getGrid());
this.network = host.getActionableNode().getGrid();
} else {
this.network = null;
}
if (te instanceof CraftingBlockEntity) {
this.setCPU(((CraftingBlockEntity) te).getCluster());
}
if (this.getNetwork() == null && Platform.isServer()) {
if (this.getNetwork() == null && isServer()) {
this.setValidContainer(false);
}
}
@@ -114,15 +120,13 @@ public class CraftingCPUContainer extends AEBaseContainer
}
if (c instanceof CraftingCPUCluster) {
this.cpuName = c.getName();
this.setMonitor((CraftingCPUCluster) c);
this.monitor = (CraftingCPUCluster) c;
this.list.resetStatus();
this.getMonitor().getListOfItem(this.list, CraftingItemList.ALL);
this.getMonitor().addListener(this, null);
this.setEstimatedTime(0);
} else {
this.setMonitor(null);
this.cpuName = null;
this.monitor = null;
this.setEstimatedTime(-1);
}
}
@@ -153,7 +157,7 @@ public class CraftingCPUContainer extends AEBaseContainer
@Override
public void sendContentUpdates() {
if (Platform.isServer() && this.getMonitor() != null && !this.list.isEmpty()) {
if (isServer() && this.getMonitor() != null && !this.list.isEmpty()) {
try {
if (this.getEstimatedTime() >= 0) {
final long elapsedTime = this.getMonitor().getElapsedTime();
@@ -218,16 +222,6 @@ public class CraftingCPUContainer extends AEBaseContainer
}
@Override
public Text getCustomInventoryName() {
return this.cpuName;
}
@Override
public boolean hasCustomInventoryName() {
return this.cpuName != null;
}
public long getEstimatedTime() {
return this.eta;
}
@@ -240,15 +234,8 @@ public class CraftingCPUContainer extends AEBaseContainer
return this.monitor;
}
private void setMonitor(final CraftingCPUCluster monitor) {
this.monitor = monitor;
}
IGrid getNetwork() {
return this.network;
}
private void setNetwork(final IGrid network) {
this.network = network;
}
}
@@ -0,0 +1,131 @@
package appeng.container.implementations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import com.google.common.collect.ImmutableSet;
import net.minecraft.text.LiteralText;
import appeng.api.networking.IGrid;
import appeng.api.networking.crafting.ICraftingCPU;
import appeng.api.networking.crafting.ICraftingGrid;
/**
* Utility class for dialogs that can cycle through crafting CPUs
*/
class CraftingCPUCycler {
@FunctionalInterface
public interface ChangeListener {
void onChange(CraftingCPURecord selectedCpu, boolean cpusAvailable);
}
private final Predicate<ICraftingCPU> cpuFilter;
private final ChangeListener changeListener;
private final List<CraftingCPURecord> cpus = new ArrayList<>();
private int selectedCpu = -1;
private boolean initialDataSent = false;
private boolean allowNoSelection;
public CraftingCPUCycler(Predicate<ICraftingCPU> cpuFilter, ChangeListener changeListener) {
this.cpuFilter = cpuFilter;
this.changeListener = changeListener;
}
public void detectAndSendChanges(IGrid network) {
final ICraftingGrid cc = network.getCache(ICraftingGrid.class);
final ImmutableSet<ICraftingCPU> cpuSet = cc.getCpus();
int matches = 0;
boolean changed = !initialDataSent;
initialDataSent = true;
for (final ICraftingCPU c : cpuSet) {
boolean found = false;
for (final CraftingCPURecord ccr : this.cpus) {
if (ccr.getCpu() == c) {
found = true;
break;
}
}
final boolean matched = this.cpuFilter.test(c);
if (matched) {
matches++;
}
if (found == !matched) {
changed = true;
}
}
if (changed || this.cpus.size() != matches) {
this.cpus.clear();
for (final ICraftingCPU c : cpuSet) {
if (this.cpuFilter.test(c)) {
this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c));
}
}
// Sort and assign numeric IDs in case they have no names
Collections.sort(this.cpus);
for (int i = 0; i < this.cpus.size(); i++) {
CraftingCPURecord cpu = cpus.get(i);
if (cpu.getName() == null) {
cpu.setName(new LiteralText("#" + (i + 1)));
}
}
this.notifyListener();
}
}
public void cycleCpu(final boolean next) {
if (next) {
this.selectedCpu++;
} else {
this.selectedCpu--;
}
// If "no CPU" is a valid selection, then -1 is the first potential item
int lowerLimit = this.allowNoSelection ? -1 : 0;
if (this.selectedCpu < lowerLimit) {
this.selectedCpu = this.cpus.size() - 1;
} else if (this.selectedCpu >= this.cpus.size()) {
this.selectedCpu = lowerLimit;
}
this.notifyListener();
}
public boolean isAllowNoSelection() {
return allowNoSelection;
}
public void setAllowNoSelection(boolean allowNoSelection) {
this.allowNoSelection = allowNoSelection;
}
private void notifyListener() {
if (this.selectedCpu >= this.cpus.size()) {
this.selectedCpu = -1;
}
// Force the selected CPU to the first available CPU unless no-selection is
// explicitly allowed
if (!this.allowNoSelection && this.selectedCpu == -1 && !this.cpus.isEmpty()) {
this.selectedCpu = 0;
}
if (this.selectedCpu != -1) {
this.changeListener.onChange(this.cpus.get(this.selectedCpu), true);
} else {
this.changeListener.onChange(null, !this.cpus.isEmpty());
}
}
}
@@ -0,0 +1,12 @@
package appeng.container.implementations;
/**
* Implemented on screens that show information about a crafting CPU and allow
* the CPU to be cycled. Is triggered by receiving a config value packet with
* name <code>Terminal.Cpu</code>.
*/
public interface CraftingCPUCyclingContainer {
void cycleSelectedCPU(boolean forward);
}
@@ -25,16 +25,16 @@ import net.minecraft.text.Text;
import appeng.api.networking.crafting.ICraftingCPU;
public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
private final Text myName;
private final ICraftingCPU cpu;
private final long size;
private final int processors;
private Text name;
public CraftingCPURecord(final long size, final int coProcessors, final ICraftingCPU server) {
this.size = size;
this.processors = coProcessors;
this.cpu = server;
this.myName = server.getName();
this.name = server.getName();
}
@Override
@@ -50,10 +50,6 @@ public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
return this.cpu;
}
Text getName() {
return this.myName;
}
int getProcessors() {
return this.processors;
}
@@ -61,4 +57,13 @@ public class CraftingCPURecord implements Comparable<CraftingCPURecord> {
long getSize() {
return this.size;
}
public Text getName() {
return name;
}
public void setName(Text name) {
this.name = name;
}
}
@@ -18,12 +18,6 @@
package appeng.container.implementations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.ImmutableSet;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
@@ -31,14 +25,13 @@ import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.text.Text;
import appeng.api.config.SecurityPermissions;
import appeng.api.networking.IGrid;
import appeng.api.networking.crafting.ICraftingCPU;
import appeng.api.networking.crafting.ICraftingGrid;
import appeng.api.storage.ITerminalHost;
import appeng.container.ContainerLocator;
import appeng.container.guisync.GuiSync;
import appeng.util.Platform;
public class CraftingStatusContainer extends CraftingCPUContainer {
public class CraftingStatusContainer extends CraftingCPUContainer implements CraftingCPUCyclingContainer {
public static ScreenHandlerType<CraftingStatusContainer> TYPE;
@@ -53,13 +46,13 @@ public class CraftingStatusContainer extends CraftingCPUContainer {
return helper.open(player, locator);
}
private final List<CraftingCPURecord> cpus = new ArrayList<>();
@GuiSync(5)
public int selectedCpu = -1;
private final CraftingCPUCycler cpuCycler = new CraftingCPUCycler(this::cpuMatches, this::onCPUSelectionChanged);
@GuiSync(6)
public boolean noCPU = true;
@GuiSync(7)
public Text myName;
public Text cpuName;
public CraftingStatusContainer(int id, final PlayerInventory ip, final ITerminalHost te) {
super(TYPE, id, ip, te);
@@ -67,43 +60,9 @@ public class CraftingStatusContainer extends CraftingCPUContainer {
@Override
public void sendContentUpdates() {
if (Platform.isServer() && this.getNetwork() != null) {
final ICraftingGrid cc = this.getNetwork().getCache(ICraftingGrid.class);
final ImmutableSet<ICraftingCPU> cpuSet = cc.getCpus();
int matches = 0;
boolean changed = false;
for (final ICraftingCPU c : cpuSet) {
boolean found = false;
for (final CraftingCPURecord ccr : this.cpus) {
if (ccr.getCpu() == c) {
found = true;
}
}
final boolean matched = this.cpuMatches(c);
if (matched) {
matches++;
}
if (found == !matched) {
changed = true;
}
}
if (changed || this.cpus.size() != matches) {
this.cpus.clear();
for (final ICraftingCPU c : cpuSet) {
if (this.cpuMatches(c)) {
this.cpus.add(new CraftingCPURecord(c.getAvailableStorage(), c.getCoProcessors(), c));
}
}
this.sendCPUs();
}
this.noCPU = this.cpus.isEmpty();
IGrid network = this.getNetwork();
if (isServer() && network != null) {
cpuCycler.detectAndSendChanges(network);
}
super.sendContentUpdates();
@@ -113,52 +72,20 @@ public class CraftingStatusContainer extends CraftingCPUContainer {
return c.isBusy();
}
private void sendCPUs() {
Collections.sort(this.cpus);
if (this.selectedCpu >= this.cpus.size()) {
this.selectedCpu = -1;
this.myName = null;
} else if (this.selectedCpu != -1) {
this.myName = this.cpus.get(this.selectedCpu).getName();
}
if (this.selectedCpu == -1 && this.cpus.size() > 0) {
this.selectedCpu = 0;
}
if (this.selectedCpu != -1) {
if (this.cpus.get(this.selectedCpu).getCpu() != this.getMonitor()) {
this.setCPU(this.cpus.get(this.selectedCpu).getCpu());
}
private void onCPUSelectionChanged(CraftingCPURecord cpuRecord, boolean cpusAvailable) {
noCPU = !cpusAvailable;
if (cpuRecord == null) {
cpuName = null;
setCPU(null);
} else {
this.setCPU(null);
cpuName = cpuRecord.getName();
setCPU(cpuRecord.getCpu());
}
}
public void cycleCpu(final boolean next) {
if (next) {
this.selectedCpu++;
} else {
this.selectedCpu--;
}
if (this.selectedCpu < -1) {
this.selectedCpu = this.cpus.size() - 1;
} else if (this.selectedCpu >= this.cpus.size()) {
this.selectedCpu = -1;
}
if (this.selectedCpu == -1 && this.cpus.size() > 0) {
this.selectedCpu = 0;
}
if (this.selectedCpu == -1) {
this.myName = null;
this.setCPU(null);
} else {
this.myName = this.cpus.get(this.selectedCpu).getName();
this.setCPU(this.cpus.get(this.selectedCpu).getCpu());
}
@Override
public void cycleSelectedCPU(boolean forward) {
this.cpuCycler.cycleCpu(forward);
}
}
@@ -36,7 +36,6 @@ import appeng.container.slot.FakeTypeOnlySlot;
import appeng.container.slot.OptionalTypeOnlyFakeSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.parts.automation.FormationPlanePart;
import appeng.util.Platform;
public class FormationPlaneContainer extends UpgradeableContainer {
@@ -108,7 +107,7 @@ public class FormationPlaneContainer extends UpgradeableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
this.setPlaceMode((YesNo) this.getUpgradeable().getConfigManager().getSetting(Settings.PLACE_BLOCK));
}
@@ -35,7 +35,6 @@ import appeng.container.guisync.GuiSync;
import appeng.container.slot.OutputSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.tile.storage.IOPortBlockEntity;
import appeng.util.Platform;
public class IOPortContainer extends UpgradeableContainer {
@@ -112,7 +111,7 @@ public class IOPortContainer extends UpgradeableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.setOperationMode(
(OperationMode) this.getUpgradeable().getConfigManager().getSetting(Settings.OPERATION_MODE));
this.setFullMode(
@@ -36,7 +36,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.tile.misc.InscriberBlockEntity;
import appeng.tile.misc.InscriberRecipes;
import appeng.util.Platform;
/**
* @author AlgorithmX2
@@ -120,7 +119,7 @@ public class InscriberContainer extends UpgradeableContainer implements IProgres
public void sendContentUpdates() {
this.standardDetectAndSendChanges();
if (Platform.isServer()) {
if (isServer()) {
this.maxProcessingTime = this.ti.getMaxProcessingTime();
this.processingTime = this.ti.getProcessingTime();
}
@@ -55,7 +55,6 @@ import appeng.parts.reporting.InterfaceTerminalPart;
import appeng.tile.inventory.AppEngInternalInventory;
import appeng.tile.misc.InterfaceBlockEntity;
import appeng.util.InventoryAdaptor;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.inv.AdaptorFixedInv;
import appeng.util.inv.WrapperCursorItemHandler;
@@ -88,7 +87,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer {
public InterfaceTerminalContainer(int id, final PlayerInventory ip, final InterfaceTerminalPart anchor) {
super(TYPE, id, ip, anchor);
if (Platform.isServer()) {
if (isServer()) {
this.grid = anchor.getActionableNode().getGrid();
}
@@ -97,7 +96,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer {
@Override
public void sendContentUpdates() {
if (Platform.isClient()) {
if (isClient()) {
return;
}
@@ -18,8 +18,6 @@
package appeng.container.implementations;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
@@ -33,13 +31,13 @@ import appeng.api.config.RedstoneMode;
import appeng.api.config.SecurityPermissions;
import appeng.api.config.Settings;
import appeng.api.config.YesNo;
import appeng.client.gui.implementations.NumberEntryWidget;
import appeng.container.ContainerLocator;
import appeng.container.guisync.GuiSync;
import appeng.container.slot.FakeTypeOnlySlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.parts.automation.LevelEmitterPart;
import appeng.util.Platform;
public class LevelEmitterContainer extends UpgradeableContainer {
@@ -49,38 +47,46 @@ public class LevelEmitterContainer extends UpgradeableContainer {
LevelEmitterContainer::new, LevelEmitterPart.class, SecurityPermissions.BUILD);
public static LevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) {
return helper.fromNetwork(windowId, inv, buf);
return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> {
container.reportingValue = buffer.readVarLong();
});
}
public static boolean open(PlayerEntity player, ContainerLocator locator) {
return helper.open(player, locator);
return helper.open(player, locator, (host, buffer) -> {
buffer.writeVarLong(host.getReportingValue());
});
}
private final LevelEmitterPart lvlEmitter;
@Environment(EnvType.CLIENT)
private NumberEntryWidget textField;
@GuiSync(2)
public LevelType lvType;
@GuiSync(3)
public long EmitterValue = -1;
@GuiSync(4)
public YesNo cmType;
// Only synced once on container-open, and only used on client
private long reportingValue;
public LevelEmitterContainer(int id, final PlayerInventory ip, final LevelEmitterPart te) {
super(TYPE, id, ip, te);
this.lvlEmitter = te;
}
@Environment(EnvType.CLIENT)
public void setTextField(final NumberEntryWidget level) {
this.textField = level;
this.textField.setValue(this.EmitterValue);
public long getReportingValue() {
return reportingValue;
}
public void setLevel(final long l, final PlayerEntity player) {
this.lvlEmitter.setReportingValue(l);
this.EmitterValue = l;
public void setReportingValue(long reportingValue) {
if (isClient()) {
if (reportingValue != this.reportingValue) {
this.reportingValue = reportingValue;
NetworkHandler.instance()
.sendToServer(new ConfigValuePacket("LevelEmitter.Value", String.valueOf(reportingValue)));
}
} else {
this.lvlEmitter.setReportingValue(reportingValue);
}
}
@Override
@@ -116,7 +122,6 @@ public class LevelEmitterContainer extends UpgradeableContainer {
@Override
public int availableUpgrades() {
return 1;
}
@@ -124,8 +129,7 @@ public class LevelEmitterContainer extends UpgradeableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
this.EmitterValue = this.lvlEmitter.getReportingValue();
if (isServer()) {
this.setCraftingMode(
(YesNo) this.getUpgradeable().getConfigManager().getSetting(Settings.CRAFT_VIA_REDSTONE));
this.setLevelMode((LevelType) this.getUpgradeable().getConfigManager().getSetting(Settings.LEVEL_TYPE));
@@ -137,15 +141,6 @@ public class LevelEmitterContainer extends UpgradeableContainer {
this.standardDetectAndSendChanges();
}
@Override
public void onUpdate(final String field, final Object oldValue, final Object newValue) {
if (field.equals("EmitterValue")) {
if (this.textField != null) {
this.textField.setValue(this.EmitterValue);
}
}
}
@Override
public YesNo getCraftingMode() {
return this.cmType;
@@ -163,4 +158,5 @@ public class LevelEmitterContainer extends UpgradeableContainer {
private void setLevelMode(final LevelType lvType) {
this.lvType = lvType;
}
}
@@ -46,6 +46,8 @@ import appeng.api.implementations.tiles.IViewCellStorage;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridHost;
import appeng.api.networking.IGridNode;
import appeng.api.networking.crafting.ICraftingCPU;
import appeng.api.networking.crafting.ICraftingGrid;
import appeng.api.networking.energy.IEnergyGrid;
import appeng.api.networking.energy.IEnergySource;
import appeng.api.networking.security.IActionHost;
@@ -73,7 +75,6 @@ import appeng.core.sync.packets.MEInventoryUpdatePacket;
import appeng.me.helpers.ChannelPowerSrc;
import appeng.util.ConfigManager;
import appeng.util.IConfigManagerHost;
import appeng.util.Platform;
public class MEMonitorableContainer extends AEBaseContainer
implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver<IAEItemStack> {
@@ -101,6 +102,13 @@ public class MEMonitorableContainer extends AEBaseContainer
public boolean canAccessViewCells = false;
@GuiSync(98)
public boolean hasPower = false;
/**
* The number of active crafting jobs in the network. -1 means unknown and will
* hide the label on the screen.
*/
@GuiSync(100)
public int activeCraftingJobs = -1;
private IConfigManagerHost gui;
private IConfigManager serverCM;
private IGridNode networkNode;
@@ -122,7 +130,7 @@ public class MEMonitorableContainer extends AEBaseContainer
this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL);
this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING);
if (Platform.isServer()) {
if (isServer()) {
this.serverCM = monitorable.getConfigManager();
this.monitor = monitorable
@@ -183,12 +191,14 @@ public class MEMonitorableContainer extends AEBaseContainer
@Override
public void sendContentUpdates() {
if (Platform.isServer()) {
if (isServer()) {
if (this.monitor != this.host
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class))) {
this.setValidContainer(false);
}
this.updateActiveCraftingJobs();
for (final Settings set : this.serverCM.getSettings()) {
final Enum<?> sideLocal = this.serverCM.getSetting(set);
final Enum<?> sideRemote = this.clientCM.getSetting(set);
@@ -286,8 +296,38 @@ public class MEMonitorableContainer extends AEBaseContainer
this.queueInventory(c);
}
private void updateActiveCraftingJobs() {
IGridNode hostNode = networkNode;
if (hostNode == null) {
// Wireless terminals do not directly expose the target grid (even though they
// have one)
if (host instanceof IActionHost) {
hostNode = ((IActionHost) host).getActionableNode();
}
}
IGrid grid = null;
if (hostNode != null) {
grid = hostNode.getGrid();
}
if (grid == null) {
// No grid to query crafting jobs from
this.activeCraftingJobs = -1;
return;
}
int activeJobs = 0;
ICraftingGrid craftingGrid = grid.getCache(ICraftingGrid.class);
for (ICraftingCPU cpus : craftingGrid.getCpus()) {
if (cpus.isBusy()) {
activeJobs++;
}
}
this.activeCraftingJobs = activeJobs;
}
private void queueInventory(final ScreenHandlerListener c) {
if (Platform.isServer() && c instanceof PlayerEntity && this.monitor != null) {
if (isServer() && c instanceof PlayerEntity && this.monitor != null) {
try {
MEInventoryUpdatePacket piu = new MEInventoryUpdatePacket();
final IItemList<IAEItemStack> monitorCache = this.monitor.getStorageList();
@@ -356,7 +396,7 @@ public class MEMonitorableContainer extends AEBaseContainer
@Override
public IConfigManager getConfigManager() {
if (Platform.isServer()) {
if (isServer()) {
return this.serverCM;
}
return this.clientCM;
@@ -42,7 +42,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.items.misc.EncodedPatternItem;
import appeng.tile.crafting.MolecularAssemblerBlockEntity;
import appeng.util.Platform;
public class MolecularAssemblerContainer extends UpgradeableContainer implements IProgressProvider {
@@ -148,7 +147,7 @@ public class MolecularAssemblerContainer extends UpgradeableContainer implements
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.setRedStoneMode(
(RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_CONTROLLED));
}
@@ -43,7 +43,6 @@ import appeng.container.guisync.GuiSync;
import appeng.core.Api;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.MEInventoryUpdatePacket;
import appeng.util.Platform;
import appeng.util.item.AEItemStack;
public class NetworkStatusContainer extends AEBaseContainer {
@@ -83,7 +82,7 @@ public class NetworkStatusContainer extends AEBaseContainer {
}
}
if (this.network == null && Platform.isServer()) {
if (this.network == null && isServer()) {
this.setValidContainer(false);
}
}
@@ -100,7 +99,7 @@ public class NetworkStatusContainer extends AEBaseContainer {
@Override
public void sendContentUpdates() {
this.delay++;
if (Platform.isServer() && this.delay > 15 && this.network != null) {
if (isServer() && this.delay > 15 && this.network != null) {
this.delay = 0;
final IEnergyGrid eg = this.network.getCache(IEnergyGrid.class);
@@ -297,9 +297,9 @@ public class PatternTermContainer extends MEMonitorableContainer
@Override
public boolean isSlotEnabled(final int idx) {
if (idx == 1) {
return Platform.isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode();
return isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode();
} else if (idx == 2) {
return Platform.isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode();
return isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode();
} else {
return false;
}
@@ -401,7 +401,7 @@ public class PatternTermContainer extends MEMonitorableContainer
@Override
public void sendContentUpdates() {
super.sendContentUpdates();
if (Platform.isServer()) {
if (isServer()) {
if (this.isCraftingMode() != this.getPatternTerminal().isCraftingRecipe()) {
this.setCraftingMode(this.getPatternTerminal().isCraftingRecipe());
this.updateOrderOfOutputSlots();
@@ -423,7 +423,7 @@ public class PatternTermContainer extends MEMonitorableContainer
@Override
public void onSlotChange(final Slot s) {
if (s == this.patternSlotOUT && Platform.isServer()) {
if (s == this.patternSlotOUT && isServer()) {
for (final ScreenHandlerListener listener : this.getListeners()) {
for (int i = 0; i < this.slots.size(); i++) {
Slot slot = this.slots.get(i);
@@ -438,7 +438,7 @@ public class PatternTermContainer extends MEMonitorableContainer
this.sendContentUpdates();
}
if (s == this.craftSlot && Platform.isClient()) {
if (s == this.craftSlot && isClient()) {
this.getAndUpdateOutput();
}
}
@@ -18,8 +18,6 @@
package appeng.container.implementations;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
@@ -28,12 +26,11 @@ import net.minecraft.screen.ScreenHandlerType;
import appeng.api.config.SecurityPermissions;
import appeng.api.parts.IPart;
import appeng.client.gui.implementations.NumberEntryWidget;
import appeng.container.AEBaseContainer;
import appeng.container.ContainerLocator;
import appeng.container.guisync.GuiSync;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.helpers.IPriorityHost;
import appeng.util.Platform;
public class PriorityContainer extends AEBaseContainer {
@@ -43,59 +40,53 @@ public class PriorityContainer extends AEBaseContainer {
PriorityContainer::new, IPriorityHost.class, SecurityPermissions.BUILD);
public static PriorityContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) {
return helper.fromNetwork(windowId, inv, buf);
return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> {
container.priorityValue = buffer.readVarInt();
});
}
public static boolean open(PlayerEntity player, ContainerLocator locator) {
return helper.open(player, locator);
return helper.open(player, locator, (host, buffer) -> buffer.writeVarInt(host.getPriority()));
}
private final IPriorityHost priHost;
@Environment(EnvType.CLIENT)
private NumberEntryWidget textField;
@GuiSync(2)
public long PriorityValue = -1;
private int priorityValue;
public PriorityContainer(int id, final PlayerInventory ip, final IPriorityHost te) {
super(TYPE, id, ip, (BlockEntity) (te instanceof BlockEntity ? te : null),
(IPart) (te instanceof IPart ? te : null));
this.priHost = te;
this.priorityValue = te.getPriority();
}
@Environment(EnvType.CLIENT)
public void setTextField(final NumberEntryWidget level) {
this.textField = level;
this.textField.setValue(this.PriorityValue, true);
}
public void setPriority(final int newValue, final PlayerEntity player) {
this.priHost.setPriority(newValue);
this.PriorityValue = newValue;
public void setPriority(final int newValue) {
if (newValue != priorityValue) {
if (isClient()) {
// If for whatever reason the client enters the value first, do not update based
// on incoming server data
this.priorityValue = newValue;
NetworkHandler.instance()
.sendToServer(new ConfigValuePacket("PriorityHost.Priority", String.valueOf(newValue)));
} else {
this.priHost.setPriority(newValue);
this.priorityValue = newValue;
}
}
}
@Override
public void sendContentUpdates() {
super.sendContentUpdates();
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
this.PriorityValue = this.priHost.getPriority();
}
}
@Override
public void onUpdate(final String field, final Object oldValue, final Object newValue) {
if (field.equals("PriorityValue")) {
if (this.textField != null) {
this.textField.setValue(this.PriorityValue, true);
}
}
super.onUpdate(field, oldValue, newValue);
public int getPriorityValue() {
return priorityValue;
}
public IPriorityHost getPriorityHost() {
return this.priHost;
}
}
@@ -37,7 +37,6 @@ import appeng.core.Api;
import appeng.items.contents.QuartzKnifeObj;
import appeng.items.materials.MaterialItem;
import appeng.tile.inventory.AppEngInternalInventory;
import appeng.util.Platform;
public class QuartzKnifeContainer extends AEBaseContainer {
@@ -142,7 +141,7 @@ public class QuartzKnifeContainer extends AEBaseContainer {
}
private void makePlate() {
if (Platform.isServer()) {
if (isServer()) {
if (!inSlot.getSlot(0).extract(1).isEmpty()) {
final ItemStack item = QuartzKnifeContainer.this.toolInv.getItemStack();
final ItemStack before = item.copy();
@@ -35,7 +35,6 @@ import appeng.container.guisync.GuiSync;
import appeng.container.slot.OutputSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.tile.spatial.SpatialIOPortBlockEntity;
import appeng.util.Platform;
public class SpatialIOPortContainer extends AEBaseContainer {
@@ -65,7 +64,7 @@ public class SpatialIOPortContainer extends AEBaseContainer {
public SpatialIOPortContainer(int id, final PlayerInventory ip, final SpatialIOPortBlockEntity spatialIOPort) {
super(TYPE, id, ip, spatialIOPort, null);
if (Platform.isServer()) {
if (isServer()) {
this.network = spatialIOPort.getGridNode(AEPartLocation.INTERNAL).getGrid();
}
@@ -89,7 +88,7 @@ public class SpatialIOPortContainer extends AEBaseContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.delay++;
if (this.delay > 15 && this.network != null) {
this.delay = 0;
@@ -45,7 +45,6 @@ import appeng.container.slot.OptionalTypeOnlyFakeSlot;
import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.parts.misc.StorageBusPart;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.iterators.NullIterator;
@@ -125,7 +124,7 @@ public class StorageBusContainer extends UpgradeableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
this.setReadWriteMode(
(AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS));
@@ -52,7 +52,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.items.contents.NetworkToolViewer;
import appeng.items.tools.NetworkToolItem;
import appeng.parts.automation.ExportBusPart;
import appeng.util.Platform;
public class UpgradeableContainer extends AEBaseContainer implements IOptionalSlotHost {
@@ -200,7 +199,7 @@ public class UpgradeableContainer extends AEBaseContainer implements IOptionalSl
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
final IConfigManager cm = this.getUpgradeable().getConfigManager();
this.loadSettingsFromHost(cm);
}
@@ -29,7 +29,6 @@ import appeng.container.guisync.GuiSync;
import appeng.container.interfaces.IProgressProvider;
import appeng.container.slot.RestrictedInputSlot;
import appeng.tile.misc.VibrationChamberBlockEntity;
import appeng.util.Platform;
public class VibrationChamberContainer extends AEBaseContainer implements IProgressProvider {
@@ -65,7 +64,7 @@ public class VibrationChamberContainer extends AEBaseContainer implements IProgr
@Override
public void sendContentUpdates() {
if (Platform.isServer()) {
if (isServer()) {
this.remainingBurnTime = this.vibrationChamber.getMaxBurnTime() <= 0 ? 0
: (int) (100.0 * this.vibrationChamber.getBurnTime() / this.vibrationChamber.getMaxBurnTime());
this.burnSpeed = this.remainingBurnTime <= 0 ? 0 : this.vibrationChamber.getBurnSpeed();
@@ -28,7 +28,6 @@ import appeng.container.ContainerLocator;
import appeng.core.AEConfig;
import appeng.core.localization.PlayerMessages;
import appeng.helpers.WirelessTerminalGuiObject;
import appeng.util.Platform;
public class WirelessTermContainer extends MEPortableCellContainer {
@@ -57,7 +56,7 @@ public class WirelessTermContainer extends MEPortableCellContainer {
super.sendContentUpdates();
if (!this.wirelessTerminalGUIObject.rangeCheck()) {
if (Platform.isServer() && this.isValidContainer()) {
if (isServer() && this.isValidContainer()) {
this.getPlayerInv().player.sendSystemMessage(PlayerMessages.OutOfRange.get(), Util.NIL_UUID);
}
+28 -1
View File
@@ -30,6 +30,10 @@ import java.util.Map;
import java.util.function.DoubleSupplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.base.Strings;
import appeng.api.config.CondenserOutput;
import appeng.api.config.PowerMultiplier;
import appeng.api.config.PowerUnits;
@@ -46,6 +50,7 @@ import appeng.core.config.DoubleOption;
import appeng.core.config.EnumOption;
import appeng.core.config.IntegerOption;
import appeng.core.config.StringListOption;
import appeng.core.config.StringOption;
import appeng.core.settings.TickRates;
import appeng.util.EnumCycler;
@@ -398,7 +403,16 @@ public final class AEConfig {
return commonConfig.quartzOreBiomeBlacklist.get();
}
// Setters keep visibility as low as possible.
@Nullable
public String getImprovedFluidTag() {
return Strings.emptyToNull(commonConfig.improvedFluidTag.get());
}
public float getImprovedFluidMultiplier() {
return (float) commonConfig.improvedFluidMultiplier.get();
}
// Setters keep visibility as low as possible.
private static class ClientConfig {
@@ -510,6 +524,11 @@ public final class AEConfig {
public final IntegerOption condenserMatterBallsPower;
public final IntegerOption condenserSingularityPower;
// In-World Purification
// Settings for improved speed depending on fluid the crystal is in
public final StringOption improvedFluidTag;
public final DoubleOption improvedFluidMultiplier;
public final Map<TickRates, IntegerOption> tickRateMin = new HashMap<>();
public final Map<TickRates, IntegerOption> tickRateMax = new HashMap<>();
@@ -604,6 +623,14 @@ public final class AEConfig {
tickRateMin.put(tickRate, tickrates.addInt(tickRate.name() + "Min", tickRate.getDefaultMin()));
tickRateMax.put(tickRate, tickrates.addInt(tickRate.name() + "Max", tickRate.getDefaultMax()));
}
ConfigSection inWorldPurification = root.subsection("inWorldPurification",
"Settings for in-world purification of crystals.");
improvedFluidTag = inWorldPurification.addString("improvedFluidTag", "",
"A fluid tag that identifies fluids that improve crystal purification speed. Does not affect purification with water/lava.");
improvedFluidMultiplier = inWorldPurification.addDouble("improvedFluidMultiplier", 2.0, 1.0, 10.0,
"The speed multiplier to use when the crystals are submerged in the improved fluid.");
}
}
@@ -30,6 +30,7 @@ import net.minecraft.block.Material;
import net.minecraft.block.SlabBlock;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.sound.BlockSoundGroup;
@@ -267,17 +268,19 @@ public final class ApiBlocks implements IBlocks {
this.chiseledQuartzBlock = deco.block("chiseled_quartz_block", () -> new AEDecorativeBlock(QUARTZ_PROPERTIES))
.build();
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS)
.block("quartz_glass", () -> new QuartzGlassBlock(glassProps()))
.rendering(new BlockRenderingCustomizer() {
@Override
@Environment(EnvType.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderLayer.getCutout());
}
}).build();
AbstractBlock.TypedContextPredicate<EntityType<?>> neverAllowSpawn = (p1, p2, p3, p4) -> false;
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS).block("quartz_glass", () -> {
return new QuartzGlassBlock(glassProps().allowsSpawning(neverAllowSpawn));
}).rendering(new BlockRenderingCustomizer() {
@Override
@Environment(EnvType.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderLayer.getCutout());
}
}).build();
this.quartzVibrantGlass = deco
.block("quartz_vibrant_glass", () -> new QuartzLampBlock(glassProps().lightLevel(15)))
.block("quartz_vibrant_glass",
() -> new QuartzLampBlock(glassProps().lightLevel(15).allowsSpawning(neverAllowSpawn)))
.addFeatures(AEFeature.DECORATIVE_LIGHTS, AEFeature.QUARTZ_GLASS)
.rendering(new BlockRenderingCustomizer() {
@Override
@@ -318,7 +318,7 @@ public final class ApiItems implements IItems {
GrowingCrystalEntity.TYPE = registry
.<GrowingCrystalEntity>entity("growing_crystal", GrowingCrystalEntity::new, SpawnGroup.MISC)
.customize(builder -> builder.dimensions(EntityDimensions.fixed(0.25F, 0.25F))).build();
.customize(builder -> builder.dimensions(EntityDimensions.fixed(0.25F, 0.4F))).build();
// rv1
this.encodedPattern = registry.item("encoded_pattern", EncodedPatternItem::new)
@@ -58,6 +58,14 @@ public class ConfigSection {
return option;
}
public StringOption addString(String id, String defaultValue) {
return addString(id, defaultValue, comment);
}
public StringOption addString(String id, String defaultValue, String comment) {
return addOption(new StringOption(this, id, comment, defaultValue));
}
public IntegerOption addInt(String id, int defaultValue) {
return addInt(id, defaultValue, comment);
}
@@ -0,0 +1,63 @@
package appeng.core.config;
import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
public class StringOption extends BaseOption {
private final String defaultValue;
private String currentValue;
public StringOption(ConfigSection parent, String id, String comment, String defaultValue) {
super(parent, id, comment);
this.defaultValue = defaultValue;
this.currentValue = this.defaultValue;
}
public String get() {
return currentValue;
}
public void set(String value) {
Preconditions.checkNotNull(value);
if (value.equals(currentValue)) {
return;
}
currentValue = value;
parent.markDirty();
}
@Override
protected JsonElement write() {
return new JsonPrimitive(currentValue);
}
@Override
protected void read(JsonElement element) {
if (!element.isJsonPrimitive()) {
throw new ConfigValidationException(this, "Expected a JSON primitive: " + element);
}
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (!primitive.isString()) {
throw new ConfigValidationException(this, "Expected a JSON string, but found: " + primitive);
}
this.currentValue = primitive.getAsString();
}
@Override
public boolean isDifferentFromDefault() {
return !currentValue.equals(defaultValue);
}
@Override
public String getDefaultAsString() {
return defaultValue;
}
@Override
public String getCurrentValueAsString() {
return currentValue;
}
}
@@ -25,6 +25,7 @@ import appeng.api.events.LocatableEventAnnounce;
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
import appeng.api.features.ILocatable;
import appeng.api.features.ILocatableRegistry;
import appeng.core.AELog;
import appeng.util.Platform;
public final class LocatableRegistry implements ILocatableRegistry {
@@ -39,8 +40,10 @@ public final class LocatableRegistry implements ILocatableRegistry {
}
if (change == LocatableEvent.REGISTER) {
AELog.debug("Registering locatable %s: %s", target.getLocatableSerial(), target);
this.set.put(target.getLocatableSerial(), target);
} else if (change == LocatableEvent.UNREGISTER) {
AELog.debug("Unregistering locatable %s: %s", target.getLocatableSerial(), target);
this.set.remove(target.getLocatableSerial());
}
});
@@ -34,7 +34,7 @@ import appeng.core.sync.network.INetworkInfo;
public abstract class BasePacket {
/**
* Sadly {@link PacketBuffer#readString()} gets inlined by Proguard which means
* Sadly {@link PacketByteBuf#readString()} gets inlined by Proguard which means
* it's not available on the Server. This field has the default string length
* that is used for writeString, which then also should be used for readString
* when it has no special length requirements.
@@ -37,7 +37,7 @@ import appeng.container.AEBaseContainer;
import appeng.container.implementations.CellWorkbenchContainer;
import appeng.container.implementations.CraftConfirmContainer;
import appeng.container.implementations.CraftingCPUContainer;
import appeng.container.implementations.CraftingStatusContainer;
import appeng.container.implementations.CraftingCPUCyclingContainer;
import appeng.container.implementations.LevelEmitterContainer;
import appeng.container.implementations.NetworkToolContainer;
import appeng.container.implementations.PatternTermContainer;
@@ -97,12 +97,9 @@ public class ConfigValuePacket extends BasePacket {
final ItemStack is = player.getStackInHand(hand);
final IMouseWheelItem si = (IMouseWheelItem) is.getItem();
si.onWheel(is, this.Value.equals("WheelUp"));
} else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftingStatusContainer) {
final CraftingStatusContainer qk = (CraftingStatusContainer) c;
qk.cycleCpu(this.Value.equals("Next"));
} else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftConfirmContainer) {
final CraftConfirmContainer qk = (CraftConfirmContainer) c;
qk.cycleCpu(this.Value.equals("Next"));
} else if (this.Name.equals("Terminal.Cpu") && c instanceof CraftingCPUCyclingContainer) {
final CraftingCPUCyclingContainer qk = (CraftingCPUCyclingContainer) c;
qk.cycleSelectedCPU(this.Value.equals("Next"));
} else if (this.Name.equals("Terminal.Start") && c instanceof CraftConfirmContainer) {
final CraftConfirmContainer qk = (CraftConfirmContainer) c;
qk.startJob();
@@ -117,13 +114,13 @@ public class ConfigValuePacket extends BasePacket {
sc.toggleSetting(this.Value, player);
} else if (this.Name.equals("PriorityHost.Priority") && c instanceof PriorityContainer) {
final PriorityContainer pc = (PriorityContainer) c;
pc.setPriority(Integer.parseInt(this.Value), player);
pc.setPriority(Integer.parseInt(this.Value));
} else if (this.Name.equals("LevelEmitter.Value") && c instanceof LevelEmitterContainer) {
final LevelEmitterContainer lvc = (LevelEmitterContainer) c;
lvc.setLevel(Long.parseLong(this.Value), player);
lvc.setReportingValue(Long.parseLong(this.Value));
} else if (this.Name.equals("FluidLevelEmitter.Value") && c instanceof FluidLevelEmitterContainer) {
final FluidLevelEmitterContainer lvc = (FluidLevelEmitterContainer) c;
lvc.setLevel(Long.parseLong(this.Value), player);
lvc.setReportingValue(Long.parseLong(this.Value));
} else if (this.Name.startsWith("PatternTerminal.") && c instanceof PatternTermContainer) {
final PatternTermContainer cpt = (PatternTermContainer) c;
if (this.Name.equals("PatternTerminal.CraftMode")) {
@@ -191,9 +188,7 @@ public class ConfigValuePacket extends BasePacket {
public void clientPacketData(final INetworkInfo network, final PlayerEntity player) {
final ScreenHandler c = player.currentScreenHandler;
if (this.Name.equals("CustomName") && c instanceof AEBaseContainer) {
((AEBaseContainer) c).setCustomName(this.Value);
} else if (this.Name.startsWith("SyncDat.")) {
if (this.Name.startsWith("SyncDat.")) {
((AEBaseContainer) c).stringSync(Integer.parseInt(this.Name.substring(8)), this.Value);
} else if (this.Name.equals("CraftingStatus") && this.Value.equals("Clear")) {
final Screen gs = MinecraftClient.getInstance().currentScreen;
@@ -18,15 +18,27 @@
package appeng.core.sync.packets;
import java.util.Arrays;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import com.mojang.datafixers.util.Pair;
import io.netty.buffer.Unpooled;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import alexiil.mc.lib.attributes.item.FixedItemInv;
@@ -41,11 +53,14 @@ import appeng.api.networking.storage.IStorageGrid;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.channels.IItemStorageChannel;
import appeng.api.storage.data.IAEItemStack;
import appeng.container.implementations.PatternTermContainer;
import appeng.core.Api;
import appeng.core.sync.BasePacket;
import appeng.core.sync.BasePacketHandler;
import appeng.core.sync.network.INetworkInfo;
import appeng.helpers.IContainerCraftingPacket;
import appeng.items.storage.ViewCellItem;
import appeng.mixins.IngredientAccessor;
import appeng.util.Platform;
import appeng.util.helpers.ItemHandlerUtil;
import appeng.util.inv.AdaptorFixedInv;
@@ -55,151 +70,312 @@ import appeng.util.prioritylist.IPartitionList;
public class JEIRecipePacket extends BasePacket {
private ItemStack[][] recipe;
/**
* Transmit only a recipe ID.
*/
private static final int INLINE_RECIPE_NONE = 1;
/**
* Transmit the information about the recipe we actually need. This is
* explicitly limited since this is untrusted client->server info.
*/
private static final int INLINE_RECIPE_SHAPED = 2;
private Identifier recipeId;
/**
* This is optional, in case the client already knows it could not resolve the
* recipe id.
*/
@Nullable
private Recipe<?> recipe;
private boolean crafting;
public JEIRecipePacket(final PacketByteBuf stream) {
final CompoundTag comp = stream.readCompoundTag();
if (comp != null) {
this.recipe = new ItemStack[9][];
for (int x = 0; x < this.recipe.length; x++) {
final ListTag list = comp.getList("#" + x, 10);
if (list.size() > 0) {
this.recipe[x] = new ItemStack[list.size()];
for (int y = 0; y < list.size(); y++) {
this.recipe[x][y] = ItemStack.fromTag(list.getCompound(y));
}
}
}
}
}
this.crafting = stream.readBoolean();
final String id = stream.readString(Short.MAX_VALUE);
this.recipeId = new Identifier(id);
// api
public JEIRecipePacket(final CompoundTag recipe) {
final PacketByteBuf data = new PacketByteBuf(Unpooled.buffer());
data.writeInt(this.getPacketID());
data.writeCompoundTag(recipe);
this.configureWrite(data);
}
@Override
public void serverPacketData(final INetworkInfo manager, final PlayerEntity player) {
final ServerPlayerEntity pmp = (ServerPlayerEntity) player;
final ScreenHandler con = pmp.currentScreenHandler;
if (!(con instanceof IContainerCraftingPacket)) {
return;
}
final IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
final IGridNode node = cct.getNetworkNode();
if (node == null) {
return;
}
final IGrid grid = node.getGrid();
if (grid == null) {
return;
}
final IStorageGrid inv = grid.getCache(IStorageGrid.class);
final IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
final ISecurityGrid security = grid.getCache(ISecurityGrid.class);
final ICraftingGrid crafting = grid.getCache(ICraftingGrid.class);
final FixedItemInv craftMatrix = cct.getInventoryByName("crafting");
final FixedItemInv playerInventory = cct.getInventoryByName("player");
if (inv != null && this.recipe != null && security != null) {
final IMEMonitor<IAEItemStack> storage = inv
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class));
final IPartitionList<IAEItemStack> filter = ViewCellItem.createFilter(cct.getViewCells());
for (int x = 0; x < craftMatrix.getSlotCount(); x++) {
ItemStack currentItem = craftMatrix.getInvStack(x);
// prepare slots
if (!currentItem.isEmpty()) {
// already the correct item?
ItemStack newItem = this.canUseInSlot(x, currentItem);
// put away old item
if (newItem != currentItem && security.hasPermission(player, SecurityPermissions.INJECT)) {
final IAEItemStack in = AEItemStack.fromItemStack(currentItem);
final IAEItemStack out = cct.useRealItems()
? Platform.poweredInsert(energy, storage, in, cct.getActionSource())
: null;
if (out != null) {
currentItem = out.createItemStack();
} else {
currentItem = ItemStack.EMPTY;
}
}
}
if (currentItem.isEmpty() && this.recipe[x] != null) {
// for each variant
for (int y = 0; y < this.recipe[x].length && currentItem.isEmpty(); y++) {
final IAEItemStack request = AEItemStack.fromItemStack(this.recipe[x][y]);
if (request != null) {
// try ae
if ((filter == null || filter.isListed(request))
&& security.hasPermission(player, SecurityPermissions.EXTRACT)) {
request.setStackSize(1);
IAEItemStack out;
if (cct.useRealItems()) {
out = Platform.poweredExtraction(energy, storage, request, cct.getActionSource());
} else {
// Query the crafting grid if there is a pattern providing the item
if (!crafting.getCraftingFor(request, null, 0, null).isEmpty()) {
out = request;
} else {
// Fall back using an existing item
out = storage.extractItems(request, Actionable.SIMULATE, cct.getActionSource());
}
}
if (out != null) {
currentItem = out.createItemStack();
}
}
// try inventory
if (currentItem.isEmpty()) {
AdaptorFixedInv ad = new AdaptorFixedInv(playerInventory);
if (cct.useRealItems()) {
currentItem = ad.removeItems(1, this.recipe[x][y], null);
} else {
currentItem = ad.simulateRemove(1, this.recipe[x][y], null);
}
}
}
}
}
ItemHandlerUtil.setStackInSlot(craftMatrix, x, currentItem);
}
con.onContentChanged(new WrapperInvItemHandler(craftMatrix));
int inlineRecipeType = stream.readVarInt();
switch (inlineRecipeType) {
case INLINE_RECIPE_NONE:
break;
case INLINE_RECIPE_SHAPED:
recipe = RecipeSerializer.SHAPED.read(this.recipeId, stream);
break;
default:
throw new IllegalArgumentException("Invalid inline recipe type.");
}
}
/**
* @param slot
* @param is itemstack
* @return is if it can be used, else EMPTY
* Sends a recipe identified by the given recipe ID to the server for either
* filling a crafting grid or a pattern.
*/
private ItemStack canUseInSlot(int slot, ItemStack is) {
if (this.recipe[slot] != null) {
for (ItemStack option : this.recipe[slot]) {
if (is.isItemEqual(option)) {
return is;
public JEIRecipePacket(final Identifier recipeId, final boolean crafting) {
PacketByteBuf data = createCommonHeader(recipeId, crafting, INLINE_RECIPE_NONE);
this.configureWrite(data);
}
/**
* Sends a recipe to the server for either filling a crafting grid or a pattern.
* <p>
* Prefer the id-based constructor above whereever possible.
*/
public JEIRecipePacket(final ShapedRecipe recipe, final boolean crafting) {
PacketByteBuf data = createCommonHeader(recipe.getId(), crafting, INLINE_RECIPE_SHAPED);
RecipeSerializer.SHAPED.write(data, recipe);
this.configureWrite(data);
}
private PacketByteBuf createCommonHeader(Identifier recipeId, boolean crafting, int inlineRecipeType) {
final PacketByteBuf data = new PacketByteBuf(Unpooled.buffer());
data.writeInt(this.getPacketID());
data.writeBoolean(crafting);
data.writeIdentifier(recipeId);
data.writeVarInt(inlineRecipeType);
return data;
}
/**
* Servside handler for this packet.
* <p>
* Makes use of {@link Preconditions#checkArgument(boolean)} as the
* {@link BasePacketHandler} is catching them and in general these cases should
* never happen except in an error case and should be logged then.
*/
@Override
public void serverPacketData(final INetworkInfo manager, final PlayerEntity player) {
// Setup and verification
final ServerPlayerEntity pmp = (ServerPlayerEntity) player;
final ScreenHandler con = pmp.currentScreenHandler;
Preconditions.checkArgument(con instanceof IContainerCraftingPacket);
Recipe<?> recipe = player.getEntityWorld().getRecipeManager().get(this.recipeId).orElse(null);
if (recipe == null && this.recipe != null) {
// Certain recipes (i.e. AE2 facades) are represented in JEI as ShapedRecipe's,
// while in reality they
// are special recipes. Those recipes are sent across the wire...
recipe = this.recipe;
}
Preconditions.checkArgument(recipe != null);
final IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
final IGridNode node = cct.getNetworkNode();
Preconditions.checkArgument(node != null);
final IGrid grid = node.getGrid();
Preconditions.checkArgument(grid != null);
final IStorageGrid inv = grid.getCache(IStorageGrid.class);
Preconditions.checkArgument(inv != null);
final ISecurityGrid security = grid.getCache(ISecurityGrid.class);
Preconditions.checkArgument(security != null);
final IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
final ICraftingGrid crafting = grid.getCache(ICraftingGrid.class);
final FixedItemInv craftMatrix = cct.getInventoryByName("crafting");
final FixedItemInv playerInventory = cct.getInventoryByName("player");
final IMEMonitor<IAEItemStack> storage = inv
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class));
final IPartitionList<IAEItemStack> filter = ViewCellItem.createFilter(cct.getViewCells());
final DefaultedList<Ingredient> ingredients = this.ensure3by3CraftingMatrix(recipe);
// Handle each slot
for (int x = 0; x < craftMatrix.getSlotCount(); x++) {
ItemStack currentItem = craftMatrix.getInvStack(x);
Ingredient ingredient = ingredients.get(x);
// prepare slots
if (!currentItem.isEmpty()) {
// already the correct item? True, skip everything else
ItemStack newItem = this.canUseInSlot(ingredient, currentItem);
// put away old item, if not correct
if (newItem != currentItem && security.hasPermission(player, SecurityPermissions.INJECT)) {
final IAEItemStack in = AEItemStack.fromItemStack(currentItem);
final IAEItemStack out = cct.useRealItems()
? Platform.poweredInsert(energy, storage, in, cct.getActionSource())
: null;
if (out != null) {
currentItem = out.createItemStack();
} else {
currentItem = ItemStack.EMPTY;
}
}
}
// Find item or pattern from the network
if (currentItem.isEmpty() && security.hasPermission(player, SecurityPermissions.EXTRACT)) {
IAEItemStack out;
if (cct.useRealItems()) {
IAEItemStack request = findBestMatchingItemStack(ingredient, filter, storage, cct);
out = request != null
? Platform.poweredExtraction(energy, storage, request.setStackSize(1),
cct.getActionSource())
: null;
} else {
out = findBestMatchingPattern(ingredient, filter, crafting, storage, cct);
if (out == null) {
out = findBestMatchingItemStack(ingredient, filter, storage, cct);
}
if (out == null && getMatchingStacks(ingredient).length > 0) {
out = AEItemStack.fromItemStack(getMatchingStacks(ingredient)[0]);
}
}
if (out != null) {
currentItem = out.createItemStack();
}
}
// If still nothing, search the player inventory.
if (currentItem.isEmpty()) {
ItemStack[] matchingStacks = getMatchingStacks(ingredient);
for (ItemStack matchingStack : matchingStacks) {
if (currentItem.isEmpty()) {
AdaptorFixedInv ad = new AdaptorFixedInv(playerInventory);
if (cct.useRealItems()) {
currentItem = ad.removeItems(1, matchingStack, null);
} else {
currentItem = ad.simulateRemove(1, matchingStack, null);
}
}
}
}
ItemHandlerUtil.setStackInSlot(craftMatrix, x, currentItem);
}
if (!this.crafting) {
this.handleProcessing(con, cct, recipe);
}
con.onContentChanged(new WrapperInvItemHandler(craftMatrix));
}
@SuppressWarnings("ConstantConditions")
private static ItemStack[] getMatchingStacks(Ingredient ingredient) {
IngredientAccessor accessor = (IngredientAccessor) (Object) ingredient;
accessor.appeng_cacheMatchingStacks();
if (ingredient.isEmpty()) {
return new ItemStack[0];
}
ItemStack[] stacks = accessor.getMatchingStacks();
if (stacks.length == 1 && stacks[0].isEmpty()) {
return new ItemStack[0];
}
return stacks;
}
/**
* Expand any recipe to a 3x3 matrix.
* <p>
* Will throw an {@link IllegalArgumentException} in case it has more than 9 or
* a shaped recipe is either wider or higher than 3. ingredients.
*/
private DefaultedList<Ingredient> ensure3by3CraftingMatrix(Recipe<?> recipe) {
DefaultedList<Ingredient> ingredients = recipe.getPreviewInputs();
DefaultedList<Ingredient> expandedIngredients = DefaultedList.ofSize(9, Ingredient.EMPTY);
Preconditions.checkArgument(ingredients.size() <= 9);
// shaped recipes can be smaller than 3x3, expand to 3x3 to match the crafting
// matrix
if (recipe instanceof ShapedRecipe) {
ShapedRecipe shapedRecipe = (ShapedRecipe) recipe;
int width = shapedRecipe.getWidth();
int height = shapedRecipe.getHeight();
Preconditions.checkArgument(width <= 3 && height <= 3);
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
int source = w + h * width;
int target = w + h * 3;
Ingredient i = ingredients.get(source);
expandedIngredients.set(target, i);
}
}
}
return ItemStack.EMPTY;
// Anything else should be a flat list
else {
for (int i = 0; i < ingredients.size(); i++) {
expandedIngredients.set(i, ingredients.get(i));
}
}
return expandedIngredients;
}
/**
* @param is itemstack
* @return is if it can be used, else EMPTY
*/
private ItemStack canUseInSlot(Ingredient ingredient, ItemStack is) {
return Arrays.stream(getMatchingStacks(ingredient)).filter(p -> p.isItemEqual(is)).findFirst()
.orElse(ItemStack.EMPTY);
}
/**
* Finds the first matching itemstack with the highest stored amount.
*/
private IAEItemStack findBestMatchingItemStack(Ingredient ingredients, IPartitionList<IAEItemStack> filter,
IMEMonitor<IAEItemStack> storage, IContainerCraftingPacket cct) {
Stream<AEItemStack> stacks = Arrays.stream(getMatchingStacks(ingredients))//
.map(AEItemStack::fromItemStack) //
.filter(r -> r != null && (filter == null || filter.isListed(r)));
return getMostStored(stacks, storage, cct);
}
/**
* This tries to find the first pattern matching the list of ingredients.
* <p>
* As additional condition, it sorts by the stored amount to return the one with
* the highest stored amount.
*/
private IAEItemStack findBestMatchingPattern(Ingredient ingredients, IPartitionList<IAEItemStack> filter,
ICraftingGrid crafting, IMEMonitor<IAEItemStack> storage, IContainerCraftingPacket cct) {
Stream<IAEItemStack> stacks = Arrays.stream(getMatchingStacks(ingredients))//
.map(AEItemStack::fromItemStack)//
.filter(r -> r != null && (filter == null || filter.isListed(r)))//
.map(s -> s.setCraftable(!crafting.getCraftingFor(s, null, 0, null).isEmpty()))//
.filter(IAEItemStack::isCraftable);
return getMostStored(stacks, storage, cct);
}
/**
* From a stream of AE item stacks, pick the one with the highest available
* amount in the network. Returns null if the stream is empty.
*/
private static IAEItemStack getMostStored(Stream<? extends IAEItemStack> stacks, IMEMonitor<IAEItemStack> storage,
IContainerCraftingPacket cct) {
return stacks//
.map(s -> {
// Determine the stored count
IAEItemStack stored = storage.extractItems(s.copy().setStackSize(Long.MAX_VALUE),
Actionable.SIMULATE, cct.getActionSource());
return Pair.of(s, stored != null ? stored.getStackSize() : 0);
})//
.min((left, right) -> Long.compare(right.getSecond(), left.getSecond()))//
.map(Pair::getFirst)//
.orElse(null);
}
private void handleProcessing(ScreenHandler con, IContainerCraftingPacket cct, Recipe<?> recipe) {
if (con instanceof PatternTermContainer) {
PatternTermContainer patternTerm = (PatternTermContainer) con;
if (!patternTerm.craftingMode) {
final FixedItemInv output = cct.getInventoryByName("output");
ItemHandlerUtil.setStackInSlot(output, 0, recipe.getOutput());
ItemHandlerUtil.setStackInSlot(output, 1, ItemStack.EMPTY);
ItemHandlerUtil.setStackInSlot(output, 2, ItemStack.EMPTY);
}
}
}
}
@@ -19,11 +19,9 @@
package appeng.core.sync.packets;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -33,9 +33,9 @@ import net.minecraft.world.World;
import alexiil.mc.lib.attributes.AttributeList;
import alexiil.mc.lib.attributes.Simulation;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import alexiil.mc.lib.attributes.item.ItemExtractable;
import alexiil.mc.lib.attributes.item.filter.ExactItemStackFilter;
import alexiil.mc.lib.attributes.item.filter.ItemFilter;
import appeng.tile.AEBaseBlockEntity;
@@ -43,7 +43,7 @@ public class ItemGenBlockEntity extends AEBaseBlockEntity {
private static final Queue<ItemStack> POSSIBLE_ITEMS = new ArrayDeque<>();
private final ItemExtractable handler = new QueuedItemHandler();
private final QueuedItemHandler handler = new QueuedItemHandler();
public ItemGenBlockEntity(BlockEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
@@ -77,22 +77,24 @@ public class ItemGenBlockEntity extends AEBaseBlockEntity {
to.offer(handler);
}
class QueuedItemHandler implements ItemExtractable {
static class QueuedItemHandler implements FixedItemInv {
@Override
public ItemStack attemptExtraction(ItemFilter itemFilter, int maxAmount, Simulation simulation) {
// When item filter asks for a specific item, just return that
if (itemFilter instanceof ExactItemStackFilter) {
return ((ExactItemStackFilter) itemFilter).stack.copy();
}
public ItemExtractable getExtractable() {
return (itemFilter, i, simulation) -> {
// When item filter asks for a specific item, just return that
if (itemFilter instanceof ExactItemStackFilter) {
return ((ExactItemStackFilter) itemFilter).stack.copy();
}
final ItemStack is = POSSIBLE_ITEMS.peek();
final ItemStack is = POSSIBLE_ITEMS.peek();
if (is == null || !itemFilter.matches(is)) {
return ItemStack.EMPTY;
}
if (is == null || !itemFilter.matches(is)) {
return ItemStack.EMPTY;
}
return simulation == Simulation.SIMULATE ? is.copy() : this.getNextItem();
return simulation == Simulation.SIMULATE ? is.copy() : getNextItem();
};
}
private ItemStack getNextItem() {
@@ -101,7 +103,28 @@ public class ItemGenBlockEntity extends AEBaseBlockEntity {
POSSIBLE_ITEMS.add(is);
return is.copy();
}
}
;
@Override
public int getSlotCount() {
return 1;
}
@Override
public ItemStack getInvStack(int i) {
return POSSIBLE_ITEMS.peek().copy();
}
@Override
public boolean isItemValidForSlot(int i, ItemStack itemStack) {
return false;
}
@Override
public boolean setInvStack(int i, ItemStack itemStack, Simulation simulation) {
if (itemStack.isEmpty()) {
getNextItem(); // Go to next item
}
return itemStack.isEmpty();
}
}
}
@@ -20,11 +20,8 @@ package appeng.decorative.solid;
import net.minecraft.block.AbstractGlassBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.util.math.Direction;
import appeng.helpers.AEMaterials;
public class QuartzGlassBlock extends AbstractGlassBlock {
public QuartzGlassBlock(Settings props) {
@@ -33,8 +30,7 @@ public class QuartzGlassBlock extends AbstractGlassBlock {
@Override
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side) {
final Material mat = adjacentBlockState.getMaterial();
if (mat == Material.GLASS || mat == AEMaterials.GLASS) {
if (adjacentBlockState.getBlock() instanceof QuartzGlassBlock) {
if (adjacentBlockState.getRenderType() == state.getRenderType()) {
return true;
}
@@ -19,13 +19,14 @@
package appeng.entity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Material;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import appeng.api.features.AEFeature;
@@ -36,12 +37,28 @@ import appeng.core.AEConfig;
import appeng.core.AppEng;
import appeng.items.misc.CrystalSeedItem;
import appeng.mixins.ItemEntityAccessor;
import appeng.util.Platform;
public class GrowingCrystalEntity extends AEBaseItemEntity {
public static EntityType<GrowingCrystalEntity> TYPE;
// Growth tick progress per tick by number of adjacent accelerators
// Expressed as 1/1000th of a growth tick, applied to progress_1000
// each time this entity ticks.
private static final int[] GROWTH_TICK_PROGRESS = { //
1, // no accelerators
40, // 1 accelerator
92, // 2 accelerators
159, // 3 accelerators
247, // 4 accelerators
361, // 5 accelerators
509 // 6 accelerators
};
/**
* The accumulated progress towards a single growth tick of the crystal in
* 1/1000th of a growth tick.
*/
private int progress_1000 = 0;
public GrowingCrystalEntity(EntityType<? extends GrowingCrystalEntity> type, World world) {
@@ -62,126 +79,140 @@ public class GrowingCrystalEntity extends AEBaseItemEntity {
super.tick();
final ItemStack is = this.getStack();
final Item gc = is.getItem();
if (!(gc instanceof IGrowableCrystal)) {
return;
}
applyGrowthTick((IGrowableCrystal) gc, is);
}
private void applyGrowthTick(IGrowableCrystal cry, ItemStack is) {
if (!AEConfig.instance().isFeatureEnabled(AEFeature.IN_WORLD_PURIFICATION)) {
return;
}
final ItemStack is = this.getStack();
final Item gc = is.getItem();
final int x = MathHelper.floor(this.getX());
final int y = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int z = MathHelper.floor(this.getZ());
if (gc instanceof IGrowableCrystal) // if it changes this just stops being an issue...
{
final int j = MathHelper.floor(this.getX());
final int i = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int k = MathHelper.floor(this.getZ());
BlockPos pos = new BlockPos(x, y, z);
final BlockState state = this.world.getBlockState(pos);
final BlockState state = this.world.getBlockState(new BlockPos(j, i, k));
final Material mat = state.getMaterial();
final IGrowableCrystal cry = (IGrowableCrystal) is.getItem();
final float multiplier = cry.getMultiplier(state, world, pos);
final float multiplier = cry.getMultiplier(state.getBlock(), mat);
final int speed = (int) Math.max(1, this.getSpeed(j, i, k) * multiplier);
if (multiplier <= 0) {
// Crystal is in unsuitable material, reset progress and quit
this.progress_1000 = 0;
return;
}
final boolean isClient = Platform.isClient();
final int progressPerTick = (int) Math.max(1, this.getSpeed(pos) * multiplier);
if (mat.isLiquid()) {
if (isClient) {
this.progress_1000++;
} else {
this.progress_1000 += speed;
}
} else {
if (world.isClient()) {
// On the client, we reuse the growth-tick-progress
// as a tick-counter for particle effects
int len = getTicksBetweenParticleEffects(progressPerTick);
if (++this.progress_1000 >= len) {
this.progress_1000 = 0;
AppEng.instance().spawnEffect(EffectType.Vibrant, this.world, this.getX(), this.getY() + 0.2,
this.getZ(), null);
}
} else {
this.progress_1000 += progressPerTick;
if (isClient) {
int len = 40;
if (this.progress_1000 >= 1000) {
// We need to copy the stack or the change detection will not work and not sync
// this new stack to the client
ItemStack newItem = is.copy();
if (speed > 2) {
len = 20;
}
if (speed > 90) {
len = 15;
}
if (speed > 150) {
len = 10;
}
if (speed > 240) {
len = 7;
}
if (speed > 360) {
len = 3;
}
if (speed > 500) {
len = 1;
}
if (this.progress_1000 >= len) {
this.progress_1000 = 0;
AppEng.instance().spawnEffect(EffectType.Vibrant, this.world, this.getX(), this.getY() + 0.2,
this.getZ(), null);
}
} else {
if (this.progress_1000 > 1000) {
// If we did not use a while loop here, the fastest growth for a crystal
// would be limited to a minimum of 30 seconds (based on 600 required growth
// ticks).
// Should a crystal decide to use a high multiplier for a certain material,
// it should be possible to go faster.
do {
newItem = cry.triggerGrowth(newItem);
this.progress_1000 -= 1000;
// We need to copy the stack or the change detection will not work and not sync
// this new stack to the client
ItemStack newItem = cry.triggerGrowth(is.copy());
this.setStack(newItem);
}
// We assume that if the item changes, the process is complete and we can break
} while (this.progress_1000 >= 1000 && newItem.getItem() == is.getItem());
this.setStack(newItem);
}
}
}
private int getSpeed(final int x, final int y, final int z) {
final int per = 80;
final float mul = 0.3f;
int qty = 0;
if (this.isAccelerated(x + 1, y, z)) {
qty += per + qty * mul;
private static int getTicksBetweenParticleEffects(int progressPerTick) {
if (progressPerTick > 500) {
return 1; // 20 times per second
} else if (progressPerTick > 360) {
return 3;
} else if (progressPerTick > 240) {
return 7;
} else if (progressPerTick > 150) {
return 10;
} else if (progressPerTick > 90) {
return 15;
} else if (progressPerTick > 2) {
return 20;
} else {
return 40; // Every 2 seconds
}
if (this.isAccelerated(x, y + 1, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y, z + 1)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x - 1, y, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y - 1, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y, z - 1)) {
qty += per + qty * mul;
}
return qty;
}
private boolean isAccelerated(final int x, final int y, final int z) {
final BlockEntity te = this.world.getBlockEntity(new BlockPos(x, y, z));
/**
* Gets the extra progress per tick in 1/1000th of a growth tick based on the
* surrounding accelerators.
*/
private int getSpeed(BlockPos pos) {
int acceleratorCount = getAcceleratorCount(pos);
if (acceleratorCount < 0) {
return GROWTH_TICK_PROGRESS[0];
} else if (acceleratorCount >= GROWTH_TICK_PROGRESS.length) {
return GROWTH_TICK_PROGRESS[GROWTH_TICK_PROGRESS.length - 1];
} else {
return GROWTH_TICK_PROGRESS[acceleratorCount];
}
}
private int getAcceleratorCount(BlockPos pos) {
int count = 0;
BlockPos.Mutable testPos = new BlockPos.Mutable();
for (Direction direction : Direction.values()) {
if (this.isPoweredAccelerator(testPos.set(pos, direction))) {
count++;
}
}
return count;
}
private boolean isPoweredAccelerator(BlockPos pos) {
final BlockEntity te = this.world.getBlockEntity(pos);
return te instanceof ICrystalGrowthAccelerator && ((ICrystalGrowthAccelerator) te).isPowered();
}
// Don't let seeds "float" on water surface
@Override
public void applyBuoyancy() {
ItemStack item = getStack();
// Make ungrown seeds sink, and fully grown seeds bouyant allowing for
// automation based around dropping seeds between 5 CGAs, then catchiung
// them on their way up.
if (item.getItem() instanceof CrystalSeedItem) {
Vec3d v = this.getVelocity();
// Apply a much smaller acceleration to make them slowly sink
double yAccel = this.hasNoGravity() ? 0 : -0.002;
// Apply the x/z slow-down, and the y acceleration
this.setVelocity(v.x * 0.99, v.y + yAccel, v.z * 0.99);
return;
}
super.applyBuoyancy();
@@ -74,6 +74,6 @@ public class TinyTNTPrimedRenderer extends EntityRenderer<TinyTNTPrimedEntity> {
@Override
public Identifier getTexture(final TinyTNTPrimedEntity entity) {
return SpriteAtlasTexture.BLOCK_ATLAS_TEX;
return SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE;
}
}
@@ -11,8 +11,6 @@ import appeng.client.gui.implementations.NumberEntryWidget;
import appeng.client.gui.implementations.UpgradeableScreen;
import appeng.client.gui.widgets.ServerSettingToggleButton;
import appeng.core.localization.GuiText;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.fluids.client.gui.widgets.FluidSlotWidget;
import appeng.fluids.container.FluidLevelEmitterContainer;
@@ -28,17 +26,24 @@ public class FluidLevelEmitterScreen extends UpgradeableScreen<FluidLevelEmitter
public void init() {
super.init();
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_FLUID_VOLUME,
this::onLevelChange);
this.level = new NumberEntryWidget(this, 20, 17, 138, 62, NumberEntryType.LEVEL_FLUID_VOLUME);
this.level.setTextFieldBounds(25, 44, 75);
handler.setTextField(this.level);
this.level.addButtons(children::add, this::addButton);
this.level.setValue(handler.getReportingValue());
this.level.setOnChange(this::saveReportingValue);
this.level.setOnConfirm(this::onClose);
this.changeFocus(true);
final int y = 40;
final int x = 80 + 57;
this.guiSlots.add(new FluidSlotWidget(this.handler.getFluidConfigInventory(), 0, 0, x, y));
}
private void saveReportingValue() {
this.level.getLongValue().ifPresent(handler::setReportingValue);
}
@Override
protected void addButtons() {
this.redstoneMode = new ServerSettingToggleButton<>(this.x - 18, this.y + 28, Settings.REDSTONE_EMITTER,
@@ -79,8 +84,4 @@ public class FluidLevelEmitterScreen extends UpgradeableScreen<FluidLevelEmitter
protected void handleButtonVisibility() {
}
private void onLevelChange(long level) {
NetworkHandler.instance().sendToServer(new ConfigValuePacket("FluidLevelEmitter.Value", String.valueOf(level)));
}
}
@@ -21,7 +21,6 @@ import appeng.container.implementations.UpgradeableContainer;
import appeng.fluids.helper.FluidSyncHelper;
import appeng.fluids.util.AEFluidStack;
import appeng.fluids.util.IAEFluidTank;
import appeng.util.Platform;
public abstract class FluidConfigurableContainer extends UpgradeableContainer implements IFluidSyncContainer {
private FluidSyncHelper sync = null;
@@ -76,7 +75,7 @@ public abstract class FluidConfigurableContainer extends UpgradeableContainer im
@Override
protected void standardDetectAndSendChanges() {
if (Platform.isServer()) {
if (isServer()) {
this.getSyncHelper().sendDiff(this.getListeners());
// clear out config items that are no longer valid (eg capacity upgrade removed)
@@ -36,7 +36,6 @@ import appeng.fluids.helper.DualityFluidInterface;
import appeng.fluids.helper.FluidSyncHelper;
import appeng.fluids.helper.IFluidInterfaceHost;
import appeng.fluids.util.IAEFluidTank;
import appeng.util.Platform;
public class FluidInterfaceContainer extends FluidConfigurableContainer {
@@ -81,7 +80,7 @@ public class FluidInterfaceContainer extends FluidConfigurableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.tankSync.sendDiff(this.getListeners());
}
@@ -1,7 +1,5 @@
package appeng.fluids.container;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.network.PacketByteBuf;
@@ -10,13 +8,12 @@ import net.minecraft.screen.ScreenHandlerType;
import appeng.api.config.RedstoneMode;
import appeng.api.config.SecurityPermissions;
import appeng.api.config.Settings;
import appeng.client.gui.implementations.NumberEntryWidget;
import appeng.container.ContainerLocator;
import appeng.container.guisync.GuiSync;
import appeng.container.implementations.ContainerHelper;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.fluids.parts.FluidLevelEmitterPart;
import appeng.fluids.util.IAEFluidTank;
import appeng.util.Platform;
public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
public static ScreenHandlerType<FluidLevelEmitterContainer> TYPE;
@@ -25,34 +22,41 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
FluidLevelEmitterContainer::new, FluidLevelEmitterPart.class, SecurityPermissions.BUILD);
public static FluidLevelEmitterContainer fromNetwork(int windowId, PlayerInventory inv, PacketByteBuf buf) {
return helper.fromNetwork(windowId, inv, buf);
return helper.fromNetwork(windowId, inv, buf, (host, container, buffer) -> {
container.reportingValue = buffer.readVarLong();
});
}
public static boolean open(PlayerEntity player, ContainerLocator locator) {
return helper.open(player, locator);
return helper.open(player, locator, (host, buffer) -> {
buffer.writeVarLong(host.getReportingValue());
});
}
private final FluidLevelEmitterPart lvlEmitter;
@Environment(EnvType.CLIENT)
private NumberEntryWidget textField;
@GuiSync(3)
public long EmitterValue = -1;
// Only synced once on container-open, and only used on client
private long reportingValue;
public FluidLevelEmitterContainer(int id, final PlayerInventory ip, final FluidLevelEmitterPart te) {
super(TYPE, id, ip, te);
this.lvlEmitter = te;
}
@Environment(EnvType.CLIENT)
public void setTextField(final NumberEntryWidget level) {
this.textField = level;
this.textField.setValue(this.EmitterValue);
public long getReportingValue() {
return reportingValue;
}
public void setLevel(final long l, final PlayerEntity player) {
this.lvlEmitter.setReportingValue(l);
this.EmitterValue = l;
public void setReportingValue(long reportingValue) {
if (isClient()) {
if (reportingValue != this.reportingValue) {
this.reportingValue = reportingValue;
NetworkHandler.instance()
.sendToServer(new ConfigValuePacket("FluidLevelEmitter.Value", String.valueOf(reportingValue)));
}
} else {
this.lvlEmitter.setReportingValue(reportingValue);
}
}
@Override
@@ -74,8 +78,7 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
this.EmitterValue = this.lvlEmitter.getReportingValue();
if (isServer()) {
this.setRedStoneMode(
(RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_EMITTER));
}
@@ -83,15 +86,6 @@ public class FluidLevelEmitterContainer extends FluidConfigurableContainer {
this.standardDetectAndSendChanges();
}
@Override
public void onUpdate(final String field, final Object oldValue, final Object newValue) {
if (field.equals("EmitterValue")) {
if (this.textField != null) {
this.textField.setValue(this.EmitterValue);
}
}
}
@Override
public IAEFluidTank getFluidConfigInventory() {
return this.lvlEmitter.getConfig();
@@ -44,7 +44,6 @@ import appeng.container.slot.RestrictedInputSlot;
import appeng.core.Api;
import appeng.fluids.parts.FluidStorageBusPart;
import appeng.fluids.util.IAEFluidTank;
import appeng.util.Platform;
import appeng.util.iterators.NullIterator;
/**
@@ -129,7 +128,7 @@ public class FluidStorageBusContainer extends FluidConfigurableContainer {
public void sendContentUpdates() {
this.verifyPermissions(SecurityPermissions.BUILD, false);
if (Platform.isServer()) {
if (isServer()) {
this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
this.setReadWriteMode(
(AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS));
@@ -124,7 +124,7 @@ public class FluidTerminalContainer extends AEBaseContainer
this.clientCM.registerSetting(Settings.SORT_BY, SortOrder.NAME);
this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING);
this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL);
if (Platform.isServer()) {
if (isServer()) {
this.serverCM = terminal.getConfigManager();
this.monitor = terminal
.getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class));
@@ -196,7 +196,7 @@ public class FluidTerminalContainer extends AEBaseContainer
}
private void queueInventory(final ScreenHandlerListener c) {
if (Platform.isServer() && c instanceof PlayerEntity && this.monitor != null) {
if (isServer() && c instanceof PlayerEntity && this.monitor != null) {
try {
MEFluidInventoryUpdatePacket piu = new MEFluidInventoryUpdatePacket();
final IItemList<IAEFluidStack> monitorCache = this.monitor.getStorageList();
@@ -221,14 +221,14 @@ public class FluidTerminalContainer extends AEBaseContainer
@Override
public IConfigManager getConfigManager() {
if (Platform.isServer()) {
if (isServer()) {
return this.serverCM;
}
return this.clientCM;
}
public void setTargetStack(final IAEFluidStack stack) {
if (Platform.isClient()) {
if (isClient()) {
if (stack == null && this.clientRequestedTargetFluid == null) {
return;
}
@@ -251,7 +251,7 @@ public class FluidTerminalContainer extends AEBaseContainer
@Override
public void sendContentUpdates() {
if (Platform.isServer()) {
if (isServer()) {
if (this.monitor != this.terminal
.getInventory(Api.instance().storage().getStorageChannel(IFluidStorageChannel.class))) {
this.setValidContainer(false);
@@ -18,13 +18,14 @@
package appeng.fluids.helper;
import java.math.RoundingMode;
import java.util.Optional;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.nbt.CompoundTag;
import alexiil.mc.lib.attributes.AttributeList;
import alexiil.mc.lib.attributes.fluid.volume.FluidVolume;
import alexiil.mc.lib.attributes.fluid.amount.FluidAmount;
import alexiil.mc.lib.attributes.item.FixedItemInv;
import appeng.api.config.Actionable;
@@ -319,8 +320,8 @@ public class DualityFluidInterface
toStore.setStackSize(-toStore.getStackSize());
// make sure strange things didn't happen...
final FluidVolume canExtract = this.tanks.drain(slot, toStore.getFluidStack(), false);
if (canExtract.isEmpty() || canExtract.getAmount() != toStore.getStackSize()) {
final FluidAmount canExtract = this.tanks.drain(slot, toStore.getFluidStack(), false);
if (canExtract.isZero() || canExtract.asLong(1000, RoundingMode.DOWN) != toStore.getStackSize()) {
changed = true;
} else {
IAEFluidStack notStored = Platform.poweredInsert(src, dest, toStore, this.interfaceRequestSource);
@@ -329,8 +330,8 @@ public class DualityFluidInterface
if (toStore.getStackSize() > 0) {
// extract items!
changed = true;
final FluidVolume removed = this.tanks.drain(slot, toStore.getFluidStack(), true);
if (removed.isEmpty() || toStore.getStackSize() != removed.getAmount()) {
final FluidAmount removed = this.tanks.drain(slot, toStore.getFluidStack(), true);
if (removed.isZero() || toStore.getStackSize() != removed.asLong(1000, RoundingMode.DOWN)) {
throw new IllegalStateException("bad attempt at managing tanks. ( drain )");
}
}
@@ -141,7 +141,7 @@ public class FluidImportBusPart extends SharedFluidBusPart {
return TickRateModulation.IDLE;
} catch (GridAccessException e) {
e.printStackTrace();
// skip
}
}
@@ -19,6 +19,14 @@ import appeng.core.AELog;
import appeng.util.Platform;
public class AEFluidInventory implements IAEFluidTank {
/**
* While this may seem redundant, it helps since this class heavily mixes AE
* fluids stacks, which use null to represent "nothing", and Minecraft's
* FluidStack, which uses #isEmpty() to represent nothing.
*/
private static final IAEFluidStack EMPTY_AE_FLUIDSTACK = null;
private final IAEFluidStack[] fluids;
private final IAEFluidInventory handler;
private final int capacity;
@@ -48,13 +56,13 @@ public class AEFluidInventory implements IAEFluidTank {
public void setFluidInSlot(final int slot, final IAEFluidStack fluid) {
if (slot >= 0 && slot < this.getSlots()) {
if (Objects.equals(this.fluids[slot], fluid)) {
if (fluid != null && fluid.getStackSize() != this.fluids[slot].getStackSize()) {
if (fluid != EMPTY_AE_FLUIDSTACK && fluid.getStackSize() != this.fluids[slot].getStackSize()) {
this.fluids[slot].setStackSize(Math.min(fluid.getStackSize(), this.capacity));
this.onContentChanged(slot);
}
} else {
if (fluid == null) {
this.fluids[slot] = null;
if (fluid == EMPTY_AE_FLUIDSTACK) {
this.fluids[slot] = EMPTY_AE_FLUIDSTACK;
} else {
this.fluids[slot] = fluid.copy();
this.fluids[slot].setStackSize(Math.min(fluid.getStackSize(), this.capacity));
@@ -76,7 +84,7 @@ public class AEFluidInventory implements IAEFluidTank {
if (slot >= 0 && slot < this.getSlots()) {
return this.fluids[slot];
}
return null;
return EMPTY_AE_FLUIDSTACK;
}
@Override
@@ -99,7 +107,7 @@ public class AEFluidInventory implements IAEFluidTank {
if (tank < 0 || tank >= fluids.length) {
return FluidVolumeUtil.EMPTY;
}
return fluids[tank] == null ? FluidVolumeUtil.EMPTY : fluids[tank].getFluidStack();
return fluids[tank] == EMPTY_AE_FLUIDSTACK ? FluidVolumeUtil.EMPTY : fluids[tank].getFluidStack();
}
@Override
@@ -119,13 +127,13 @@ public class AEFluidInventory implements IAEFluidTank {
final IAEFluidStack fluid = this.fluids[slot];
if (fluid != null && !fluid.getFluidStack().equals(resource)) {
if (fluid != EMPTY_AE_FLUIDSTACK && !fluid.getFluidStack().equals(resource)) {
return 0;
}
int amountToStore = this.capacity;
if (fluid != null) {
if (fluid != EMPTY_AE_FLUIDSTACK) {
amountToStore -= fluid.getStackSize();
}
@@ -143,19 +151,19 @@ public class AEFluidInventory implements IAEFluidTank {
return amountToStore;
}
public FluidVolume drain(final int slot, final FluidVolume resource, final boolean doDrain) {
public FluidAmount drain(final int slot, final FluidVolume resource, final boolean doDrain) {
final IAEFluidStack fluid = this.fluids[slot];
if (resource.isEmpty() || fluid == null || !fluid.getFluidStack().equals(resource)) {
return null;
if (resource.isEmpty() || fluid == EMPTY_AE_FLUIDSTACK || !fluid.getFluidStack().equals(resource)) {
return FluidAmount.ZERO;
}
int toDrain = (int) resource.getAmount_F().asLong(1000, RoundingMode.DOWN);
return this.drain(slot, toDrain, doDrain);
}
public FluidVolume drain(final int slot, final int maxDrain, boolean doDrain) {
public FluidAmount drain(final int slot, final int maxDrain, boolean doDrain) {
final IAEFluidStack fluid = this.fluids[slot];
if (fluid == null || maxDrain <= 0) {
return null;
if (fluid == EMPTY_AE_FLUIDSTACK || maxDrain <= 0) {
return FluidAmount.ZERO;
}
int drained = maxDrain;
@@ -163,15 +171,15 @@ public class AEFluidInventory implements IAEFluidTank {
drained = (int) fluid.getStackSize();
}
FluidVolume stack = fluid.getFluidStack().withAmount(FluidAmount.of(drained, 1000));
FluidAmount amount = FluidAmount.of(drained, 1000);
if (doDrain) {
fluid.setStackSize(fluid.getStackSize() - drained);
if (fluid.getStackSize() <= 0) {
this.fluids[slot] = null;
this.fluids[slot] = EMPTY_AE_FLUIDSTACK;
}
this.onContentChanged(slot);
}
return stack;
return amount;
}
public void writeToNBT(final CompoundTag data, final String name) {
@@ -185,7 +193,7 @@ public class AEFluidInventory implements IAEFluidTank {
try {
final CompoundTag c = new CompoundTag();
if (this.fluids[x] != null) {
if (this.fluids[x] != EMPTY_AE_FLUIDSTACK) {
this.fluids[x].writeToNBT(c);
}
+7 -32
View File
@@ -35,8 +35,8 @@ import com.google.common.base.Stopwatch;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;
@@ -73,7 +73,7 @@ public class TickHandler {
ServerTickEvents.END_SERVER_TICK.register(this::onAfterServerTick);
ServerTickEvents.START_WORLD_TICK.register(this::onBeforeWorldTick);
ServerTickEvents.END_WORLD_TICK.register(this::onAfterWorldTick);
ServerLifecycleEvents.SERVER_STOPPING.register(this::onServerStopping);
ServerWorldEvents.UNLOAD.register(this::onUnloadWorld);
}
public static TickHandler instance() {
@@ -135,38 +135,13 @@ public class TickHandler {
this.getRepo().clear();
}
// FIXME FABRIC It does not look like worlds can ever unload in Fabric.
// FIXME FABRIC public void unloadWorld(final WorldEvent.Unload ev) {
// FIXME FABRIC if (Platform.isServer()) // for no there is no reason to care about this on the client...
// FIXME FABRIC {
// FIXME FABRIC final List<IGridNode> toDestroy = new ArrayList<>();
// FIXME FABRIC
// FIXME FABRIC this.getRepo().updateNetworks();
// FIXME FABRIC for (final Grid g : this.getRepo().networks) {
// FIXME FABRIC for (final IGridNode n : g.getNodes()) {
// FIXME FABRIC if (n.getWorld() == ev.getWorld()) {
// FIXME FABRIC toDestroy.add(n);
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC
// FIXME FABRIC for (final IGridNode n : toDestroy) {
// FIXME FABRIC n.destroy();
// FIXME FABRIC }
// FIXME FABRIC }
// FIXME FABRIC }
/**
* This is primarily useful for an integrated server being stopped, and can be
* fully replaced by the event above once world unload events hit.
*/
private void onServerStopping(MinecraftServer server) {
public void onUnloadWorld(MinecraftServer server, ServerWorld world) {
final List<IGridNode> toDestroy = new ArrayList<>();
this.server.updateNetworks();
for (final Grid g : this.server.networks) {
this.getRepo().updateNetworks();
for (final Grid g : this.getRepo().networks) {
for (final IGridNode n : g.getNodes()) {
WorldAccess nodeWorld = n.getWorld();
if (nodeWorld instanceof ServerWorld && ((ServerWorld) nodeWorld).getServer() == server) {
if (n.getWorld() == world) {
toDestroy.add(n);
}
}
@@ -0,0 +1,43 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020 Team Appliedenergistics, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.integration.modules.jei;
import me.shedaniel.rei.api.AutoTransferHandler;
import me.shedaniel.rei.api.RecipeDisplay;
import appeng.container.implementations.CraftingTermContainer;
public class CraftingRecipeTransferHandler extends RecipeTransferHandler<CraftingTermContainer> {
public CraftingRecipeTransferHandler(Class<CraftingTermContainer> containerClass) {
super(containerClass);
}
@Override
protected AutoTransferHandler.Result doTransferRecipe(CraftingTermContainer container, RecipeDisplay recipe,
AutoTransferHandler.Context context) {
return null;
}
@Override
protected boolean isCrafting() {
return true;
}
}
@@ -108,6 +108,8 @@ class FacadeRegistryGenerator implements LiveRecipeGenerator<DefaultShapedDispla
ingredients.set(7, Ingredient.ofStacks(cableAnchor));
ingredients.set(4, Ingredient.ofStacks(textureItem));
result.setCount(4);
return new DefaultShapedDisplay(new ShapedRecipe(id, "", 3, 3, ingredients, result));
}
@@ -0,0 +1,52 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020 Team Appliedenergistics, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.integration.modules.jei;
import me.shedaniel.rei.api.AutoTransferHandler;
import me.shedaniel.rei.api.RecipeDisplay;
import me.shedaniel.rei.plugin.DefaultPlugin;
import appeng.container.implementations.PatternTermContainer;
public class PatternRecipeTransferHandler extends RecipeTransferHandler<PatternTermContainer> {
PatternRecipeTransferHandler(Class<PatternTermContainer> containerClass) {
super(containerClass);
}
protected AutoTransferHandler.Result doTransferRecipe(PatternTermContainer container, RecipeDisplay recipe,
AutoTransferHandler.Context context) {
if (container.isCraftingMode() && recipe.getRecipeCategory() != DefaultPlugin.CRAFTING) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.requires_processing_mode");
}
if (recipe.getResultingEntries().isEmpty()) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.no_output");
}
return null;
}
@Override
protected boolean isCrafting() {
return false;
}
}
@@ -18,27 +18,27 @@
package appeng.integration.modules.jei;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import me.shedaniel.rei.api.AutoTransferHandler;
import me.shedaniel.rei.api.EntryStack;
import me.shedaniel.rei.api.RecipeDisplay;
import me.shedaniel.rei.api.TransferRecipeDisplay;
import appeng.container.slot.CraftingMatrixSlot;
import appeng.container.slot.FakeCraftingMatrixSlot;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.JEIRecipePacket;
import appeng.mixins.SlotMixin;
import appeng.util.Platform;
import appeng.helpers.IContainerCraftingPacket;
class RecipeTransferHandler<T extends ScreenHandler> implements AutoTransferHandler {
abstract class RecipeTransferHandler<T extends ScreenHandler & IContainerCraftingPacket>
implements AutoTransferHandler {
private final Class<T> containerClass;
@@ -47,67 +47,85 @@ class RecipeTransferHandler<T extends ScreenHandler> implements AutoTransferHand
}
@Override
public Result handle(Context context) {
if (!(context.getRecipe() instanceof TransferRecipeDisplay)) {
return Result.createNotApplicable();
public AutoTransferHandler.Result handle(AutoTransferHandler.Context context) {
RecipeDisplay recipe = context.getRecipe();
if (!containerClass.isInstance(context.getContainerScreen().getScreenHandler())) {
return AutoTransferHandler.Result.createNotApplicable();
}
ScreenHandler container = context.getContainerScreen().getScreenHandler();
T container = containerClass.cast(context.getContainerScreen().getScreenHandler());
if (!containerClass.isInstance(container)) {
return Result.createNotApplicable();
final Identifier recipeId = recipe.getRecipeLocation().orElse(null);
// Check that the recipe can actually be looked up via the manager, i.e. our
// facade recipes
// have an ID, but are never registered with the recipe manager.
boolean canSendReference = true;
if (recipeId == null || !context.getMinecraft().world.getRecipeManager().get(recipeId).isPresent()) {
canSendReference = false;
}
if (!context.isActuallyCrafting()) {
// This is just to check whether the button is enabled
return Result.createSuccessful();
if (recipe instanceof TransferRecipeDisplay) {
TransferRecipeDisplay trd = (TransferRecipeDisplay) recipe;
if (trd.getWidth() > 3 || trd.getHeight() > 3) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.recipe_too_large");
}
} else if (recipe.getInputEntries().size() > 9) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.recipe_too_large");
}
TransferRecipeDisplay transferRecipe = (TransferRecipeDisplay) context.getRecipe();
final AutoTransferHandler.Result error = doTransferRecipe(container, recipe, context);
final CompoundTag recipe = new CompoundTag();
if (error != null) {
return error;
}
List<List<EntryStack>> ingredients = transferRecipe.getInputEntries();
for (int i = 0; i < ingredients.size(); i++) {
List<EntryStack> ingredientEntry = ingredients.get(i);
// Vanilla will pack ingredients if the recipe is less than 3x3
int x = i % transferRecipe.getWidth();
int y = (i - x) / transferRecipe.getWidth();
int slotIndex = 3 * y + x;
for (final Slot slot : container.slots) {
if (slot instanceof CraftingMatrixSlot || slot instanceof FakeCraftingMatrixSlot) {
int containerSlotInvIdx = ((SlotMixin) slot).getIndex();
if (containerSlotInvIdx == slotIndex) {
final ListTag tags = new ListTag();
final List<ItemStack> list = new ArrayList<>();
// prefer pure crystals.
for (EntryStack stack : ingredientEntry) {
if (Platform.isRecipePrioritized(stack.getItemStack())) {
list.add(0, stack.getItemStack());
} else {
list.add(stack.getItemStack());
}
}
for (final ItemStack is : list) {
final CompoundTag tag = new CompoundTag();
is.toTag(tag);
tags.add(tag);
}
recipe.put("#" + containerSlotInvIdx, tags);
break;
if (context.isActuallyCrafting()) {
if (canSendReference) {
NetworkHandler.instance().sendToServer(new JEIRecipePacket(recipeId, isCrafting()));
} else {
// To avoid earlier problems of too large packets being sent that crashed the
// client,
// as a fallback when the recipe ID could not be resolved, we'll just send the
// displayed
// items.
DefaultedList<Ingredient> flatIngredients = DefaultedList.ofSize(9, Ingredient.EMPTY);
ItemStack output = null;
for (EntryStack entryStack : recipe.getResultingEntries().get(0)) {
if (entryStack.getType() == EntryStack.Type.ITEM) {
output = entryStack.getItemStack();
}
}
if (output == null || output.isEmpty()) {
return AutoTransferHandler.Result.createFailed("jei.appliedenergistics2.no_output");
}
// Now map the actual ingredients into the output/input
for (int i = 0; i < recipe.getInputEntries().size(); i++) {
List<EntryStack> inputEntry = recipe.getInputEntries().get(i);
if (inputEntry.isEmpty()) {
continue;
}
EntryStack first = inputEntry.get(0);
if (i < flatIngredients.size()) {
ItemStack displayedIngredient = first.getItemStack();
if (displayedIngredient != null) {
flatIngredients.set(i, Ingredient.ofStacks(Stream.of(displayedIngredient)));
}
}
}
ShapedRecipe fallbackRecipe = new ShapedRecipe(recipeId, "", 3, 3, flatIngredients, output);
NetworkHandler.instance().sendToServer(new JEIRecipePacket(fallbackRecipe, isCrafting()));
}
}
NetworkHandler.instance().sendToServer(new JEIRecipePacket(recipe));
return Result.createFailed(""); // this will return to the screen
return Result.createSuccessful().blocksFurtherHandling();
}
protected abstract AutoTransferHandler.Result doTransferRecipe(T container, RecipeDisplay recipe,
AutoTransferHandler.Context context);
protected abstract boolean isCrafting();
}

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