Compare commits

...

41 Commits

Author SHA1 Message Date
shartte d07937fa93 Render Performance Improvements (#4721)
* Return a fixed bounding box for frustrum culling of AE tile entity renderers to prevent Forge from re-retrieving the TE from the world and calculating the collision box (very costly for cables).

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

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

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

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

(cherry picked from commit e90dd2f9c6)
2020-09-01 01:07:28 +02:00
yueh 133ed1f09d Remove GZipStreams from packets. (#4664)
This should now be handled by the normal network manager (hopefully).
Also renamed the `CompressedNBTPacket` to `MEInterfaceUpdatePacket` as that is the only use case.
2020-08-31 23:09:28 +02:00
shartte db9ec2b070 Remove the use of SlotItemHandler since we are only faking an inventory-backed slot, (#4654)
we might as well use what we had used pre-2017, which is simply extending a normal Slot.
This should also fix #4652.
2020-08-29 22:03:35 +02:00
Sebastian Hartte 00d4c20d9f Strip pom.xml dependencies. 2020-08-26 11:39:03 +02:00
Sebastian Hartte 5efc085693 Fix tiles that have a "powered" block state from not being able to be broken when
they are connected to a powered grid.
2020-08-24 23:54:37 +02:00
Sebastian Hartte aff94cec8c Fix search box interactions in the interface terminal. 2020-08-24 22:33:04 +02:00
Sebastian Hartte 2184d03d0b Actually save the rotation when a tile is rotated. 2020-08-24 01:06:26 +02:00
Sebastian Hartte 44c0931c02 Fixes #4635: Actually remove requesters when removing requesters. 2020-08-23 20:47:05 +02:00
yueh 423ab81813 Added step to publish to modmaven (#4607)
(cherry picked from commit 7e1bfdb5ac)
2020-08-21 20:40:03 +02:00
shartte 4e42b17e81 Fixes #4590: Crystal seeds (other than certus) don't display their growth stages. (#4618) 2020-08-19 16:43:45 +02:00
shartte fc0f8eab56 Fixes #4611: Use of client-side only method for Tiny TNT explosion. (#4612) 2020-08-18 01:33:47 +02:00
shartte 41b7900706 Fixes #4598: Incorrectly rotated face for determining block lightlevel in ME Chest renderer. (#4599) (#4609)
(cherry picked from commit 954ce1442e)
2020-08-17 21:58:51 +02:00
shartte 7656157b0d Fixes #4602: Prevent encoding invalid patterns and handle corrupted patterns a little more gracefully. (#4608) 2020-08-17 21:49:37 +02:00
Sebastian Hartte 87c2c94bd4 Use minus sign for minus buttons in number widget. 2020-08-11 16:31:20 +02:00
yueh d591a1492c Fixes #4583: Confine fluix crafting into the same block (#4586)
Fluix crystals will now spawn within an area around the center of the block as well as some tuned down motion while still maintaining some randomness.
2020-08-10 11:30:04 +02:00
shartte d1ba26311b One Spatial Dimension (#4570)
* Ported the "one spatial dimension" concept from pre-1.15

* Apply lighting updates after moving chunks.
Mark world data as dirty when changing it.

* Consolidated naming of spatial storage world/dimension.
Use server light manager to update lighting info in chunk.

* Save last transition information,
and add ae2 spatial command to interact with spatial storage.

* Formatting fixes.

* Improved docs for the origin generation.

Inverted condition on when the x/z axis are flipped.
Also added a helper to map a plot to a region file

* Remove TransitionResult since it's only used in lieu of a boolean.
Rework transition to make the checks BEFORE allocating a new cell.

* Removed unused imports.

Co-authored-by: yueh <yueh@users.noreply.github.com>
2020-08-10 11:29:07 +02:00
shartte 8733c2c11f Fix Special Model Registration (#4577)
* Fixes #4575 by updating Forge (ModelRegistryEvent is a modbus event but was misflagged as not being one). And update Forge because their breaking change breaks our mod.

* Update gradle.properties

Co-authored-by: yueh <yueh@users.noreply.github.com>

* Update src/main/resources/META-INF/mods.toml

Co-authored-by: yueh <yueh@users.noreply.github.com>

Co-authored-by: yueh <yueh@users.noreply.github.com>
2020-08-10 11:25:26 +02:00
yueh 4230fc7325 Fixes #4576: Append the amount to each button (#4580) 2020-08-09 13:23:24 +02:00
shartte 484af8baf3 Fixes #4574 by referencing only non-pure fluix crystals in the fluix dust grinder recipe. (#4578) 2020-08-09 12:24:42 +02:00
shartte 9456f639d4 Simplify "unlit" quad loading and move to Mixins (#4569)
* Simplified the UV loader concept: We only need it to make quads "unlit" which means that the lighting system is not applied to them. They always render at their full brightness.

* More documentation and slight cleanups.
2020-08-09 01:27:19 +02:00
shartte fe753c540b Fixes #4571: When not used for displaying localized text, specify Locale.ROOT for toLowerCase/toUpperCase to avoid issues with locale-specific non-ASCII lowercase conversions for ASCII uppercase characters (i.e. turkish lowercase i for uppercase I). (#4573) 2020-08-08 14:56:51 +02:00
Sebastian Hartte 2fabab3a6a Fix AO lighting for faces not fully snapped to block edges. 2020-08-08 02:20:40 +02:00
shartte 6f39bd8fe8 Move the nether quartz dust tag to the forge namespace and use it instead of AE's internal one. (#4568) 2020-08-07 23:13:02 +02:00
yueh c7d3877129 Use the declared power drain for p2p tunnels (#4567) 2020-08-06 20:07:40 +02:00
179 changed files with 2285 additions and 3402 deletions
+9 -1
View File
@@ -55,7 +55,15 @@ jobs:
KEY_STORE_ALIAS: ${{ secrets.KEY_STORE_ALIAS }}
KEY_STORE_KEY_PASS: ${{ secrets.KEY_STORE_KEY_PASS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew publish --no-daemon --max-workers 1
run: ./gradlew publishMavenPublicationToGitHubPackagesRepository --no-daemon --max-workers 1
- name: Publish to modmaven
env:
KEY_STORE_PASS: ${{ secrets.KEY_STORE_PASS }}
KEY_STORE_ALIAS: ${{ secrets.KEY_STORE_ALIAS }}
KEY_STORE_KEY_PASS: ${{ secrets.KEY_STORE_KEY_PASS }}
MODMAVEN_USER: ${{ secrets.MODMAVEN_USER }}
MODMAVEN_PASSWORD: ${{ secrets.MODMAVEN_PASSWORD }}
run: ./gradlew publishMavenPublicationToModmavenRepository --no-daemon --max-workers 1
- name: Prepare artifact metadata
id: prepare_artifact_metadata
run: |
+15
View File
@@ -190,6 +190,13 @@ publishing {
groupId = project.group
artifactId = project.archivesBaseName
version = project.version
// ForgeGradle will generate wild dependency definitions, see https://github.com/MinecraftForge/ForgeGradle/issues/584
// Since we don't actually depend on anything, just remove the entire node.
pom.withXml {
asNode().remove(asNode().dependencies)
}
from components.java
artifact sourcesJar
artifact javadocJar
@@ -205,5 +212,13 @@ publishing {
name = "GitHubPackages"
url = "https://maven.pkg.github.com/AppliedEnergistics/Applied-Energistics-2"
}
maven {
credentials {
username System.getenv("MODMAVEN_USER")
password System.getenv("MODMAVEN_PASSWORD")
}
name = "modmaven"
url = "https://modmaven.k-4u.nl/artifactory/local-releases/"
}
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ artifact_basename=appliedenergistics2
minecraft_release=1.16
minecraft_version=1.16.1
mcp_mappings=20200723-1.16.1
forge_version=32.0.93
forge_version=32.0.108
#########################################################
# Provided APIs #
@@ -23,6 +23,8 @@
package appeng.api.config;
import java.util.Locale;
/**
* Represent the security systems basic permissions, these are not for
* anti-griefing, they are part of the mod as a gameplay feature.
@@ -58,7 +60,7 @@ public enum SecurityPermissions {
*/
SECURITY;
private final String translationKey = "gui.appliedenergistics2.security." + this.name().toLowerCase();
private final String translationKey = "gui.appliedenergistics2.security." + this.name().toLowerCase(Locale.ROOT);
public String getTranslatedName() {
return this.translationKey + ".name";
@@ -53,6 +53,8 @@ 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.
*/
@@ -73,6 +75,8 @@ public interface ICraftingHelper {
* operation by the ME system.
* @param allowSubstitutes Controls whether the ME system will allow the use of
* equivalent items to craft this recipe.
* @throws IllegalArgumentException If either in or out contain only empty
* ItemStacks.
*/
ItemStack encodeCraftingPattern(@Nullable ItemStack stack, ICraftingRecipe recipe, ItemStack[] in, ItemStack out,
boolean allowSubstitutes);
@@ -1,38 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 AlgorithmX2
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package appeng.api.implementations;
/**
* Defines the result of performing a transition from the world into a storage
* cell, if its possible, and what the energy usage is.
*/
public class TransitionResult {
public final boolean success;
public final double energyUsage;
public TransitionResult(final boolean success, final double power) {
this.success = success;
this.energyUsage = power;
}
}
@@ -23,13 +23,23 @@
package appeng.api.implementations.items;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import javax.annotation.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public interface IGrowableCrystal {
ItemStack triggerGrowth(ItemStack is);
float getMultiplier(Block blk, Material mat);
/**
* Gets the crystal growth multiplier based on the material it is submerged in.
*
* @param state The blockstate of the block the crystal is currently in.
* @return The multiplier for the growth tick progress. Zero if the crystal
* cannot grow in the current material.
*/
float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos);
}
@@ -25,11 +25,8 @@ package appeng.api.implementations.items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.RegistryKey;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import appeng.api.implementations.TransitionResult;
import appeng.api.util.WorldCoord;
/**
@@ -52,13 +49,13 @@ public interface ISpatialStorageCell {
int getMaxStoredDim(ItemStack is);
/**
* get the currently stored Dimension id.
* get the currently stored spatial storage plot id.
*
* @param is spatial storage cell
*
* @return dimension id or -1
* @return plot id or -1
*/
RegistryKey<World> getStoredDimension(ItemStack is);
int getAllocatedPlotId(ItemStack is);
/**
* Perform a spatial swap with the contents of the cell, and the world.
@@ -69,7 +66,7 @@ public interface ISpatialStorageCell {
* @param max max coord
* @param playerId owner of current grid or -1
*
* @return result of transition
* @return success of transition
*/
TransitionResult doSpatialTransition(ItemStack is, ServerWorld w, WorldCoord min, WorldCoord max, int playerId);
boolean doSpatialTransition(ItemStack is, ServerWorld w, WorldCoord min, WorldCoord max, int playerId);
}
@@ -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);
@@ -1,51 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2017, 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.api.storage;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
public interface ISpatialDimension {
ServerWorld getWorld(RegistryKey<World> cellDim);
@Nullable
RegistryKey<World> createNewCellDimension(BlockPos capacity);
void deleteCellDimension(RegistryKey<World> cellDim);
boolean isCellDimension(RegistryKey<World> cellDim);
BlockPos getCellDimensionOrigin(RegistryKey<World> cellDim);
BlockPos getCellDimensionSize(RegistryKey<World> cellDim);
/**
* Adds a user-facing tooltip that describes this dimension so that a player can
* keep storage cells apart.
*/
void addCellDimensionTooltip(RegistryKey<World> cellDim, List<ITextComponent> lines);
}
@@ -23,10 +23,8 @@ import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.item.ItemModelsProperties;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.World;
@@ -39,6 +39,8 @@ import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.DyeColor;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
@@ -82,8 +84,12 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusTileEntity> implement
private static final ICableBusContainer NULL_CABLE_BUS = new NullCableBusContainer();
private static final IntegerProperty LIGHT_LEVEL = IntegerProperty.create("light_level", 0, 15);
public CableBusBlock() {
super(defaultProps(AEMaterials.GLASS).notSolid().noDrops().variableOpacity());
super(defaultProps(AEMaterials.GLASS).notSolid().noDrops().variableOpacity()
.setLightLevel(state -> state.get(LIGHT_LEVEL)));
setDefaultState(getDefaultState().with(LIGHT_LEVEL, 0));
}
@Override
@@ -126,11 +132,9 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusTileEntity> implement
}
@Override
public int getLightValue(final BlockState state, final IBlockReader world, final BlockPos pos) {
if (state.getBlock() != this) {
return state.getBlock().getLightValue(state, world, pos);
}
return this.cb(world, pos).getLightValue();
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) {
super.fillStateContainer(builder);
builder.add(LIGHT_LEVEL);
}
@Override
@@ -386,4 +390,13 @@ public class CableBusBlock extends AEBaseTileBlock<CableBusTileEntity> implement
}
}
@Override
protected BlockState updateBlockStateFromTileEntity(BlockState currentState, CableBusTileEntity te) {
if (currentState.getBlock() != this) {
return currentState;
}
int lightLevel = te.getCableBus().getLightValue();
return super.updateBlockStateFromTileEntity(currentState, te).with(LIGHT_LEVEL, lightLevel);
}
}
@@ -18,6 +18,8 @@
package appeng.block.networking;
import java.util.Locale;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
@@ -51,7 +53,7 @@ public class WirelessBlock extends AEBaseTileBlock<WirelessTileEntity> {
@Override
public String getString() {
return this.name().toLowerCase();
return this.name().toLowerCase(Locale.ROOT);
}
}
@@ -30,14 +30,10 @@ import net.minecraft.block.DispenserBlock;
import net.minecraft.dispenser.IDispenseItemBehavior;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemModelsProperties;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import appeng.api.features.AEFeature;
import appeng.block.AEBaseBlockItemChargeable;
import appeng.bootstrap.components.IClientSetupComponent;
import appeng.bootstrap.components.IInitComponent;
import appeng.bootstrap.components.IItemRegistrationComponent;
import appeng.core.AEItemGroup;
+3 -1
View File
@@ -1,6 +1,8 @@
package appeng.client;
import java.util.Locale;
import org.lwjgl.glfw.GLFW;
public enum ActionKey {
@@ -13,7 +15,7 @@ public enum ActionKey {
}
public String getTranslationKey() {
return "key." + this.name().toLowerCase() + ".desc";
return "key." + this.name().toLowerCase(Locale.ROOT) + ".desc";
}
public int getDefaultKey() {
@@ -8,19 +8,18 @@ import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.items.SlotItemHandler;
/**
* A proxy for a slot that will always return an itemstack with size 1, if there
* is an item in the slot. Used to prevent the default item count from
* rendering.
*/
class Size1Slot extends SlotItemHandler {
class Size1Slot extends Slot {
private final SlotItemHandler delegate;
private final Slot delegate;
public Size1Slot(SlotItemHandler delegate) {
super(delegate.getItemHandler(), delegate.getSlotIndex(), delegate.xPos, delegate.yPos);
public Size1Slot(Slot delegate) {
super(delegate.inventory, delegate.getSlotIndex(), delegate.xPos, delegate.yPos);
this.delegate = delegate;
}
@@ -90,7 +90,9 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
this.searchField.setMaxStringLength(25);
this.searchField.setTextColor(0xFFFFFF);
this.searchField.setVisible(true);
this.searchField.setFocused2(true);
this.searchField.setResponder(str -> this.refreshList());
this.addListener(this.searchField);
this.changeFocus(true);
}
@Override
@@ -131,11 +133,8 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
@Override
public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) {
if (this.searchField.mouseClicked(xCoord, yCoord, btn)) {
if (btn == 1 && this.searchField.isMouseOver(xCoord, yCoord)) {
this.searchField.setText("");
this.refreshList();
}
if (btn == 1 && this.searchField.isMouseOver(xCoord, yCoord)) {
this.searchField.setText("");
return true;
}
@@ -173,11 +172,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
if (character == ' ' && this.searchField.getText().isEmpty()) {
return true;
}
if (this.searchField.charTyped(character, key)) {
this.refreshList();
return true;
}
return false;
return super.charTyped(character, key);
}
@Override
@@ -245,7 +240,7 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
/**
* Rebuilds the list of interfaces.
*
* <p>
* Respects a search term if present (ignores case) and adding only matching
* patterns.
*/
@@ -338,12 +333,11 @@ public class InterfaceTerminalScreen extends AEBaseScreen<InterfaceTerminalConta
/**
* Tries to retrieve a cache for a with search term as keyword.
*
* <p>
* If this cache should be empty, it will populate it with an earlier cache if
* available or at least the cache for the empty string.
*
* @param searchTerm the corresponding search
*
* @return a Set matching a superset of the search term
*/
private Set<Object> getCacheForSearchTerm(final String searchTerm) {
@@ -1,5 +1,7 @@
package appeng.client.gui.implementations;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.LongConsumer;
@@ -24,7 +26,7 @@ import appeng.core.AEConfig;
public class NumberEntryWidget implements ITickingWidget {
private static final ITextComponent PLUS = new StringTextComponent("+");
private static final ITextComponent MINUS = new StringTextComponent("+");
private static final ITextComponent MINUS = new StringTextComponent("-");
private final AEBaseScreen<?> parent;
@@ -33,14 +35,7 @@ public class NumberEntryWidget implements ITickingWidget {
private final NumberBox level;
private final NumberEntryType type;
private Button plus1;
private Button plus10;
private Button plus100;
private Button plus1000;
private Button minus1;
private Button minus10;
private Button minus100;
private Button minus1000;
private List<Button> buttons;
public NumberEntryWidget(AEBaseScreen<?> parent, int x, int y, int width, int height, NumberEntryType type,
LongConsumer changeListener) {
@@ -63,14 +58,7 @@ public class NumberEntryWidget implements ITickingWidget {
public void setActive(boolean active) {
this.level.setEnabled(active);
this.plus1.active = active;
this.plus10.active = active;
this.plus100.active = active;
this.plus1000.active = active;
this.minus1.active = active;
this.minus10.active = active;
this.minus100.active = active;
this.minus1000.active = active;
this.buttons.forEach(b -> b.active = active);
}
public void setTextFieldBounds(int x, int y, int width) {
@@ -93,18 +81,23 @@ public class NumberEntryWidget implements ITickingWidget {
int left = parent.getGuiLeft() + x;
int top = parent.getGuiTop() + y;
addButton.accept(this.plus1 = new Button(left, top, 22, 20, PLUS /* + a */, btn -> addQty(a)));
addButton.accept(this.plus10 = new Button(left + 28, top, 28, 20, PLUS /* + b */, btn -> addQty(b)));
addButton.accept(this.plus100 = new Button(left + 62, top, 32, 20, PLUS /* + c */, btn -> addQty(c)));
addButton.accept(this.plus1000 = new Button(left + 100, top, 38, 20, PLUS/* + d */, btn -> addQty(d)));
List<Button> buttons = new ArrayList<>(8);
buttons.add(new Button(left, top, 22, 20, makeLabel(PLUS, a), btn -> addQty(a)));
buttons.add(new Button(left + 28, top, 28, 20, makeLabel(PLUS, b), btn -> addQty(b)));
buttons.add(new Button(left + 62, top, 32, 20, makeLabel(PLUS, c), btn -> addQty(c)));
buttons.add(new Button(left + 100, top, 38, 20, makeLabel(PLUS, d), btn -> addQty(d)));
// Placing this here will give a sensible tab order
addChildren.accept(this.level);
addButton.accept(this.minus1 = new Button(left, top + 42, 22, 20, MINUS /* + a */, btn -> addQty(-a)));
addButton.accept(this.minus10 = new Button(left + 28, top + 42, 28, 20, MINUS /* + b */, btn -> addQty(-b)));
addButton.accept(this.minus100 = new Button(left + 62, top + 42, 32, 20, MINUS /* + c */, btn -> addQty(-c)));
addButton.accept(this.minus1000 = new Button(left + 100, top + 42, 38, 20, MINUS /* + d */, btn -> addQty(-d)));
buttons.add(new Button(left, top + 42, 22, 20, makeLabel(MINUS, a), btn -> addQty(-a)));
buttons.add(new Button(left + 28, top + 42, 28, 20, makeLabel(MINUS, b), btn -> addQty(-b)));
buttons.add(new Button(left + 62, top + 42, 32, 20, makeLabel(MINUS, c), btn -> addQty(-c)));
buttons.add(new Button(left + 100, top + 42, 38, 20, makeLabel(MINUS, d), btn -> addQty(-d)));
this.buttons = buttons;
this.buttons.forEach(addButton);
}
private void addQty(final long i) {
@@ -134,4 +127,8 @@ public class NumberEntryWidget implements ITickingWidget {
this.level.tick();
}
private ITextComponent makeLabel(ITextComponent prefix, int amount) {
return prefix.copyRaw().appendString(String.valueOf(amount));
}
}
@@ -41,11 +41,6 @@ import appeng.fluids.util.FluidSorters;
import appeng.util.Platform;
import appeng.util.prioritylist.IPartitionList;
/**
* @author BrockWS
* @version rv6 - 22/05/2018
* @since rv6 22/05/2018
*/
public class FluidRepo {
private final IItemList<IAEFluidStack> list = Api.instance().storage().getStorageChannel(IFluidStorageChannel.class)
.createList();
+11 -12
View File
@@ -21,23 +21,22 @@ package appeng.client.me;
import javax.annotation.Nonnull;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.SlotItemHandler;
import appeng.api.storage.data.IAEFluidStack;
import appeng.fluids.container.slots.IMEFluidSlot;
/**
* @author BrockWS
* @version rv6 - 22/05/2018
* @since rv6 22/05/2018
*/
public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
public class SlotFluidME extends Slot implements IMEFluidSlot {
private InternalFluidSlotME slot;
private static final IInventory EMPTY_INVENTORY = new Inventory(0);
private final InternalFluidSlotME slot;
public SlotFluidME(InternalFluidSlotME slot) {
super(null, 0, slot.getxPosition(), slot.getyPosition());
super(EMPTY_INVENTORY, 0, slot.getxPosition(), slot.getyPosition());
this.slot = slot;
}
@@ -50,7 +49,7 @@ public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
}
@Override
public boolean isItemValid(final ItemStack par1ItemStack) {
public boolean isItemValid(final ItemStack stack) {
return false;
}
@@ -69,7 +68,7 @@ public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
}
@Override
public void putStack(final ItemStack par1ItemStack) {
public void putStack(final ItemStack stack) {
}
@@ -85,7 +84,7 @@ public class SlotFluidME extends SlotItemHandler implements IMEFluidSlot {
}
@Override
public boolean canTakeStack(final PlayerEntity par1PlayerEntity) {
public boolean canTakeStack(final PlayerEntity player) {
return false;
}
}
+15 -11
View File
@@ -19,23 +19,27 @@
package appeng.client.me;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.SlotItemHandler;
import appeng.api.storage.data.IAEItemStack;
public class SlotME extends SlotItemHandler {
public class SlotME extends Slot {
private final InternalSlotME mySlot;
private static final IInventory EMPTY_INVENTORY = new Inventory(0);
public SlotME(final InternalSlotME me) {
super(null, 0, me.getxPosition(), me.getyPosition());
this.mySlot = me;
private final InternalSlotME slot;
public SlotME(final InternalSlotME slot) {
super(EMPTY_INVENTORY, 0, slot.getxPosition(), slot.getyPosition());
this.slot = slot;
}
public IAEItemStack getAEStack() {
if (this.mySlot.hasPower()) {
return this.mySlot.getAEStack();
if (this.slot.hasPower()) {
return this.slot.getAEStack();
}
return null;
}
@@ -47,15 +51,15 @@ public class SlotME extends SlotItemHandler {
@Override
public ItemStack getStack() {
if (this.mySlot.hasPower()) {
return this.mySlot.getStack();
if (this.slot.hasPower()) {
return this.slot.getStack();
}
return ItemStack.EMPTY;
}
@Override
public boolean getHasStack() {
if (this.mySlot.hasPower()) {
if (this.slot.hasPower()) {
return !this.getStack().isEmpty();
}
return false;
@@ -18,6 +18,8 @@
package appeng.client.render;
import java.util.Locale;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.util.Direction;
@@ -111,6 +113,6 @@ public enum FacingToRotation implements IStringSerializable {
@Override
public String getString() {
return name().toLowerCase();
return name().toLowerCase(Locale.ROOT);
}
}
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
import net.minecraft.client.renderer.model.BakedQuad;
@@ -100,7 +101,7 @@ class CableBuilder {
}
return new RenderMaterial(AtlasTexture.LOCATION_BLOCKS_TEXTURE,
new ResourceLocation(AppEng.MOD_ID, textureFolder + color.name().toLowerCase()));
new ResourceLocation(AppEng.MOD_ID, textureFolder + color.name().toLowerCase(Locale.ROOT)));
}
/**
@@ -21,7 +21,6 @@ package appeng.client.render.cablebus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -30,6 +29,11 @@ import java.util.Random;
import javax.annotation.Nullable;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.Weigher;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.model.BakedQuad;
@@ -50,7 +54,10 @@ import appeng.api.util.AEColor;
public class CableBusBakedModel implements IBakedModel {
private static final Map<CableBusRenderState, List<BakedQuad>> CABLE_MODEL_CACHE = new HashMap<>();
// The number of quads overall that will be cached
private static final int CACHE_QUAD_COUNT = 5000;
private final LoadingCache<CableBusRenderState, List<BakedQuad>> cableModelCache;
private final CableBuilder cableBuilder;
@@ -66,6 +73,17 @@ public class CableBusBakedModel implements IBakedModel {
this.facadeBuilder = facadeBuilder;
this.partModels = partModels;
this.particleTexture = particleTexture;
this.cableModelCache = CacheBuilder.newBuilder()//
.maximumWeight(CACHE_QUAD_COUNT)//
.weigher((Weigher<CableBusRenderState, List<BakedQuad>>) (key, value) -> value.size())//
.build(new CacheLoader<CableBusRenderState, List<BakedQuad>>() {
@Override
public List<BakedQuad> load(CableBusRenderState renderState) {
final List<BakedQuad> model = new ArrayList<>();
addCableQuads(renderState, model);
return model;
}
});
}
@Override
@@ -92,11 +110,7 @@ public class CableBusBakedModel implements IBakedModel {
if (layer == RenderType.getCutout()) {
// First, handle the cable at the center of the cable bus
final List<BakedQuad> cableModel = CABLE_MODEL_CACHE.computeIfAbsent(renderState, k -> {
final List<BakedQuad> model = new ArrayList<>();
this.addCableQuads(renderState, model);
return model;
});
final List<BakedQuad> cableModel = cableModelCache.getUnchecked(renderState);
quads.addAll(cableModel);
// Then handle attachments
@@ -341,8 +355,4 @@ public class CableBusBakedModel implements IBakedModel {
return ItemOverrideList.EMPTY;
}
public static void clearCache() {
CABLE_MODEL_CACHE.clear();
}
}
@@ -18,7 +18,6 @@ public class CableBusModelLoader implements IModelLoader<CableBusModel> {
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
CableBusBakedModel.clearCache();
}
@Override
@@ -19,6 +19,7 @@
package appeng.client.render.cablebus;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
@@ -74,7 +75,7 @@ public enum CableCoreType {
public RenderMaterial getTexture(AEColor color) {
return new RenderMaterial(AtlasTexture.LOCATION_BLOCKS_TEXTURE,
new ResourceLocation(AppEng.MOD_ID, this.textureFolder + "/" + color.name().toLowerCase()));
new ResourceLocation(AppEng.MOD_ID, this.textureFolder + "/" + color.name().toLowerCase(Locale.ROOT)));
}
}
@@ -18,6 +18,8 @@
package appeng.client.render.crafting;
import java.util.Locale;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -48,7 +50,7 @@ public class CraftingCubeModelLoader implements IModelLoader<CraftingCubeModel>
if (typeEl != null) {
String typeName = deserializationContext.deserialize(typeEl, String.class);
if (typeName != null) {
unitType = AbstractCraftingUnitBlock.CraftingUnitType.valueOf(typeName.toUpperCase());
unitType = AbstractCraftingUnitBlock.CraftingUnitType.valueOf(typeName.toUpperCase(Locale.ROOT));
}
}
if (unitType == null) {
@@ -49,7 +49,7 @@ public class EnergyParticleData implements IParticleData {
reader.expect(' ');
boolean forItem = reader.readBoolean();
reader.expect(' ');
AEPartLocation direction = AEPartLocation.valueOf(reader.readString().toUpperCase());
AEPartLocation direction = AEPartLocation.valueOf(reader.readString().toUpperCase(Locale.ROOT));
return new EnergyParticleData(forItem, direction);
}
@@ -74,7 +74,8 @@ public class EnergyParticleData implements IParticleData {
@Override
public String getParameters() {
return String.format(Locale.ROOT, "%s %s", forItem ? "true" : "false", direction.name().toLowerCase());
return String.format(Locale.ROOT, "%s %s", forItem ? "true" : "false",
direction.name().toLowerCase(Locale.ROOT));
}
}
@@ -34,8 +34,6 @@ import com.google.common.collect.ImmutableList;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
@@ -193,7 +191,7 @@ public class AutoRotatingBakedModel extends DelegateBakedModel {
vec.setX(vec.getX() + 0.5f);
vec.setY(vec.getY() + 0.5f);
vec.setZ(vec.getZ() + 0.5f);
return new float[] { vec.getX(), vec.getY(), vec.getZ() };
return new float[] { snap(vec.getX()), snap(vec.getY()), snap(vec.getZ()) };
case 4:
Vector4f vecc = new Vector4f(fs[0], fs[1], fs[2], fs[3]);
vecc.setX(vecc.getX() - 0.5f);
@@ -203,13 +201,32 @@ public class AutoRotatingBakedModel extends DelegateBakedModel {
vecc.setX(vecc.getX() + 0.5f);
vecc.setY(vecc.getY() + 0.5f);
vecc.setZ(vecc.getZ() + 0.5f);
return new float[] { vecc.getX(), vecc.getY(), vecc.getZ(), vecc.getW() };
return new float[] { snap(vecc.getX()), snap(vecc.getY()), snap(vecc.getZ()), snap(vecc.getW()) };
default:
return fs;
}
}
/**
* This is the same value used by Vanilla's AO lighter.
*/
private static final float EPS = 0.0001f;
/**
* This tries to snap the coordinate to the edges of the block frame, because
* Vanilla uses direct equals comparisons to 0 and 1 for checking if a face
* extends fully towards the edge. This is used primarily for AO calculations.
*/
private static float snap(float x) {
if (Math.abs(x) <= EPS) {
return 0f;
} else if (Math.abs(x - 1) <= EPS) {
return 1f;
}
return x;
}
private float[] transformNormal(float[] fs) {
if (this.face == null) {
switch (fs.length) {
@@ -220,7 +237,7 @@ public class AutoRotatingBakedModel extends DelegateBakedModel {
case 4:
Vector4f vec4 = new Vector4f(fs[0], fs[1], fs[2], fs[3]);
vec4.transform(this.f2r.getMat());
return new float[] { vec4.getX(), vec4.getY(), vec4.getZ(), 0 };
return new float[] { snap(vec4.getX()), snap(vec4.getY()), snap(vec4.getZ()), 0 };
default:
return fs;
@@ -1,210 +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>.
*/
package appeng.client.render.model;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.mojang.blaze3d.matrix.MatrixStack;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.BlockFaceUV;
import net.minecraft.client.renderer.model.BlockModel;
import net.minecraft.client.renderer.model.BlockPart;
import net.minecraft.client.renderer.model.BlockPartFace;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IModelTransform;
import net.minecraft.client.renderer.model.IUnbakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.model.ItemOverride;
import net.minecraft.client.renderer.model.ItemOverrideList;
import net.minecraft.client.renderer.model.ItemTransformVec3f;
import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.client.renderer.model.RenderMaterial;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.Direction;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.TransformationMatrix;
import net.minecraftforge.client.model.IModelBuilder;
import net.minecraftforge.client.model.IModelConfiguration;
import net.minecraftforge.client.model.IModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
import net.minecraftforge.common.model.TransformationHelper;
public class UVLModelLoader implements IModelLoader<UVLModelLoader.UVLModelWrapper> {
public static final UVLModelLoader INSTANCE = new UVLModelLoader();
@Override
public void onResourceManagerReload(IResourceManager resourceManager) {
}
@Override
public UVLModelWrapper read(JsonDeserializationContext deserializationContext, JsonObject modelContents) {
modelContents.remove("loader");
BlockModel blockModel = UVLSERIALIZER.fromJson(modelContents, BlockModel.class);
return new UVLModelWrapper(blockModel);
}
final Gson UVLSERIALIZER = (new GsonBuilder())
.registerTypeAdapter(BlockModel.class, new ModelLoaderRegistry.ExpandedBlockModelDeserializer())
.registerTypeAdapter(BlockPart.class, new BlockPart.Deserializer())
.registerTypeAdapter(BlockPartFace.class, new BlockPartFaceOverrideSerializer())
.registerTypeAdapter(BlockFaceUV.class, new BlockFaceUV.Deserializer())
.registerTypeAdapter(ItemTransformVec3f.class, new ItemTransformVec3f.Deserializer())
.registerTypeAdapter(ItemCameraTransforms.class, new ItemCameraTransforms.Deserializer())
.registerTypeAdapter(ItemOverride.class, new ItemOverride.Deserializer())
.registerTypeAdapter(TransformationMatrix.class, new TransformationHelper.Deserializer()).create();
private static class BlockPartFaceOverrideSerializer extends BlockPartFace.Deserializer {
@Override
public BlockPartFace deserialize(JsonElement p_deserialize_1_, Type p_deserialize_2_,
JsonDeserializationContext p_deserialize_3_) throws JsonParseException {
BlockPartFace blockFace = super.deserialize(p_deserialize_1_, p_deserialize_2_, p_deserialize_3_);
Pair<Float, Float> uvl = this.parseUVL(p_deserialize_1_.getAsJsonObject());
if (uvl != null) {
return new BlockPartFaceWithUVL(blockFace.cullFace, blockFace.tintIndex, blockFace.texture,
blockFace.blockFaceUV, uvl.getLeft(), uvl.getRight());
}
return blockFace;
}
protected Pair<Float, Float> parseUVL(JsonObject object) {
if (!object.has("uvlightmap")) {
return null;
}
object = object.get("uvlightmap").getAsJsonObject();
return new ImmutablePair<>(JSONUtils.getFloat(object, "sky", 0), JSONUtils.getFloat(object, "block", 0));
}
}
private static class BlockPartFaceWithUVL extends BlockPartFace {
private final float sky;
private final float block;
public BlockPartFaceWithUVL(@Nullable Direction cullFaceIn, int tintIndexIn, String textureIn,
BlockFaceUV blockFaceUVIn, float sky, float block) {
super(cullFaceIn, tintIndexIn, textureIn, blockFaceUVIn);
this.sky = sky;
this.block = block;
}
public float getSky() {
return sky;
}
public float getBlock() {
return block;
}
}
public static class UVLModelWrapper implements IModelGeometry<UVLModelWrapper> {
private final BlockModel parent;
public UVLModelWrapper(BlockModel parent) {
this.parent = parent;
}
@Override
public IBakedModel bake(IModelConfiguration owner, ModelBakery bakery,
Function<RenderMaterial, TextureAtlasSprite> spriteGetter, IModelTransform modelTransform,
ItemOverrideList overrides, ResourceLocation modelLocation) {
TextureAtlasSprite particle = spriteGetter.apply(owner.resolveTexture("particle"));
IModelBuilder<?> builder = IModelBuilder.of(owner, overrides, particle);
for (BlockPart blockpart : parent.getElements()) {
for (Direction direction : blockpart.mapFaces.keySet()) {
BlockPartFace blockpartface = blockpart.mapFaces.get(direction);
TextureAtlasSprite textureatlassprite1 = spriteGetter
.apply(owner.resolveTexture(blockpartface.texture));
BakedQuad quad = BlockModel.makeBakedQuad(blockpart, blockpartface, textureatlassprite1, direction,
modelTransform, modelLocation);
if (blockpartface instanceof BlockPartFaceWithUVL) {
BlockPartFaceWithUVL uvlFace = (BlockPartFaceWithUVL) blockpartface;
quad = applyPreBakedLighting(quad, textureatlassprite1, uvlFace.sky, uvlFace.block);
}
if (blockpartface.cullFace == null) {
builder.addGeneralQuad(quad);
} else {
builder.addFaceQuad(modelTransform.getRotation().rotateTransform(blockpartface.cullFace), quad);
}
}
}
return builder.build();
}
private BakedQuad applyPreBakedLighting(BakedQuad quad, TextureAtlasSprite sprite, float sky, float block) {
// FIXME: just piping through the quads and manipulating uv index 2 directly
// seems way easier than this
BakedQuadBuilder builder = new BakedQuadBuilder(sprite);
VertexLighterFlat trans = new VertexLighterFlat(Minecraft.getInstance().getBlockColors()) {
@Override
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z) {
lightmap[0] = block;
lightmap[1] = sky;
}
@Override
public void setQuadTint(int tint) {
// Tint requires a block state which we don't have at this point
}
};
MatrixStack identity = new MatrixStack();
trans.setTransform(identity.getLast());
trans.setParent(builder);
quad.pipe(trans);
builder.setQuadTint(quad.getTintIndex());
builder.setQuadOrientation(quad.getFace());
builder.setApplyDiffuseLighting(false);
return builder.build();
}
@Override
public Collection<RenderMaterial> getTextures(IModelConfiguration owner,
Function<ResourceLocation, IUnbakedModel> modelGetter,
Set<com.mojang.datafixers.util.Pair<String, String>> missingTextureErrors) {
return parent.getTextures(modelGetter, missingTextureErrors);
}
}
}
@@ -20,6 +20,7 @@ package appeng.client.render.spatial;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -59,7 +60,7 @@ public class SpatialPylonModel implements BasicUnbakedModel<SpatialPylonModel> {
private static RenderMaterial getTexturePath(SpatialPylonTextureType type) {
return new RenderMaterial(AtlasTexture.LOCATION_BLOCKS_TEXTURE,
new ResourceLocation(AppEng.MOD_ID, "block/spatial_pylon/" + type.name().toLowerCase()));
new ResourceLocation(AppEng.MOD_ID, "block/spatial_pylon/" + type.name().toLowerCase(Locale.ROOT)));
}
}
@@ -142,7 +142,7 @@ public class ChestTileEntityRenderer extends TileEntityRenderer<ChestTileEntity>
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, @Nonnull Random rand,
@Nonnull IModelData extraData) {
if (side != null) {
side = r.rotate(side); // This fixes the incorrect lightmap position
side = r.resultingRotate(side); // This fixes the incorrect lightmap position
}
List<BakedQuad> quads = new ArrayList<>(super.getQuads(state, side, rand, extraData));
@@ -42,7 +42,7 @@ import appeng.api.networking.security.IActionHost;
import appeng.container.AEBaseContainer;
import appeng.container.ContainerLocator;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.sync.packets.CompressedNBTPacket;
import appeng.core.sync.packets.MEInterfaceUpdatePacket;
import appeng.helpers.DualityInterface;
import appeng.helpers.IInterfaceHost;
import appeng.helpers.InventoryAction;
@@ -177,7 +177,7 @@ public final class InterfaceTerminalContainer extends AEBaseContainer {
if (!this.data.isEmpty()) {
try {
NetworkHandler.instance().sendTo(new CompressedNBTPacket(this.data),
NetworkHandler.instance().sendTo(new MEInterfaceUpdatePacket(this.data),
(ServerPlayerEntity) this.getPlayerInv().player);
} catch (final IOException e) {
// :P
@@ -261,13 +261,19 @@ public class PatternTermContainer extends MEMonitorableContainer
return new ItemStack[] { out };
}
} else {
boolean hasValue = false;
final ItemStack[] list = new ItemStack[3];
for (int i = 0; i < this.outputSlots.length; i++) {
final ItemStack out = this.outputSlots[i].getStack();
list[i] = out;
if (!out.isEmpty()) {
hasValue = true;
}
}
if (hasValue) {
return list;
}
return list;
}
return null;
+27
View File
@@ -31,6 +31,8 @@ import java.util.Set;
import java.util.function.DoubleSupplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.base.Strings;
import org.apache.commons.lang3.tuple.Pair;
@@ -393,6 +395,15 @@ public final class AEConfig {
return this.meteoriteDimensionWhitelist;
}
@Nullable
public String getImprovedFluidTag() {
return Strings.emptyToNull(COMMON.improvedFluidTag.get());
}
public float getImprovedFluidMultiplier() {
return COMMON.improvedFluidMultiplier.get().floatValue();
}
// Setters keep visibility as low as possible.
private static class ClientConfig {
@@ -507,6 +518,11 @@ public final class AEConfig {
public final ConfigValue<Integer> condenserMatterBallsPower;
public final ConfigValue<Integer> condenserSingularityPower;
// In-World Purification
// Settings for improved speed depending on fluid the crystal is in
public final ConfigValue<String> improvedFluidTag;
public final ConfigValue<Double> improvedFluidMultiplier;
public final Map<TickRates, ConfigValue<Integer>> tickRateMin = new HashMap<>();
public final Map<TickRates, ConfigValue<Integer>> tickRateMax = new HashMap<>();
@@ -617,6 +633,17 @@ public final class AEConfig {
tickRateMax.put(tickRate, builder.define(tickRate.name() + "Max", tickRate.getDefaultMax()));
}
builder.pop();
builder.comment("Settings for in-world purification of crystals.").push("inWorldPurification");
improvedFluidTag = builder.comment(
"A fluid tag that identifies fluids that improve crystal purification speed. Does not affect purification with water/lava.")
.define("improvedFluidTag", "");
improvedFluidMultiplier = builder
.comment("The speed multiplier to use when the crystals are submerged in the improved fluid.")
.defineInRange("improvedFluidMultiplier", 2.0, 1.0, 10.0);
builder.pop();
}
}
-56
View File
@@ -19,7 +19,6 @@
package appeng.core;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
@@ -40,8 +39,6 @@ import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.structure.Structure;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.DistExecutor;
@@ -56,29 +53,10 @@ import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import appeng.block.paint.PaintSplotchesModel;
import appeng.block.qnb.QnbFormedModel;
import appeng.bootstrap.components.IClientSetupComponent;
import appeng.bootstrap.components.IInitComponent;
import appeng.bootstrap.components.IPostInitComponent;
import appeng.capabilities.Capabilities;
import appeng.client.ClientHelper;
import appeng.client.render.DummyFluidItemModel;
import appeng.client.render.FacadeItemModel;
import appeng.client.render.SimpleModelLoader;
import appeng.client.render.cablebus.CableBusModelLoader;
import appeng.client.render.cablebus.P2PTunnelFrequencyModel;
import appeng.client.render.crafting.CraftingCubeModelLoader;
import appeng.client.render.crafting.EncodedPatternModelLoader;
import appeng.client.render.model.BiometricCardModel;
import appeng.client.render.model.ColorApplicatorModel;
import appeng.client.render.model.DriveModel;
import appeng.client.render.model.GlassModel;
import appeng.client.render.model.MemoryCardModel;
import appeng.client.render.model.SkyCompassModel;
import appeng.client.render.model.UVLModelLoader;
import appeng.client.render.spatial.SpatialPylonModel;
import appeng.core.features.registries.PartModels;
import appeng.core.stats.AdvancementTriggers;
import appeng.core.sync.network.NetworkHandler;
import appeng.core.worlddata.WorldData;
@@ -90,7 +68,6 @@ import appeng.entity.TinyTNTPrimedRenderer;
import appeng.hooks.TickHandler;
import appeng.integration.Integrations;
import appeng.parts.PartPlacement;
import appeng.parts.automation.PlaneModelLoader;
import appeng.server.ServerHelper;
@Mod(AppEng.MOD_ID)
@@ -181,39 +158,6 @@ public final class AppEng {
RenderingRegistry.registerEntityRenderingHandler(ChargedQuartzEntity.TYPE,
m -> new ItemRenderer(m, Minecraft.getInstance().getItemRenderer()));
// TODO: Do not use the internal API
final ApiDefinitions definitions = Api.INSTANCE.definitions();
definitions.getRegistry().getBootstrapComponents(IClientSetupComponent.class)
.forEachRemaining(IClientSetupComponent::setup);
addBuiltInModel("glass", GlassModel::new);
addBuiltInModel("sky_compass", SkyCompassModel::new);
addBuiltInModel("dummy_fluid_item", DummyFluidItemModel::new);
addBuiltInModel("memory_card", MemoryCardModel::new);
addBuiltInModel("biometric_card", BiometricCardModel::new);
addBuiltInModel("drive", DriveModel::new);
addBuiltInModel("color_applicator", ColorApplicatorModel::new);
addBuiltInModel("spatial_pylon", SpatialPylonModel::new);
addBuiltInModel("paint_splotches", PaintSplotchesModel::new);
addBuiltInModel("quantum_bridge_formed", QnbFormedModel::new);
addBuiltInModel("p2p_tunnel_frequency", P2PTunnelFrequencyModel::new);
addBuiltInModel("facade", FacadeItemModel::new);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "encoded_pattern"),
EncodedPatternModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "part_plane"),
PlaneModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "crafting_cube"),
CraftingCubeModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "uvlightmap"), UVLModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "cable_bus"),
new CableBusModelLoader((PartModels) Api.INSTANCE.registries().partModels()));
}
@OnlyIn(Dist.CLIENT)
private static <T extends IModelGeometry<T>> void addBuiltInModel(String id, Supplier<T> modelFactory) {
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, id),
new SimpleModelLoader<>(modelFactory));
}
@Nonnull
+65 -8
View File
@@ -18,6 +18,8 @@
package appeng.core;
import java.util.function.Supplier;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
@@ -50,7 +52,8 @@ import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.event.ParticleFactoryRegisterEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.client.model.geometry.IModelGeometry;
import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.IEventBus;
@@ -79,7 +82,10 @@ import appeng.api.networking.security.ISecurityGrid;
import appeng.api.networking.spatial.ISpatialCache;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.networking.ticking.ITickManager;
import appeng.block.paint.PaintSplotchesModel;
import appeng.block.qnb.QnbFormedModel;
import appeng.bootstrap.components.IBlockRegistrationComponent;
import appeng.bootstrap.components.IClientSetupComponent;
import appeng.bootstrap.components.IEntityRegistrationComponent;
import appeng.bootstrap.components.IItemColorRegistrationComponent;
import appeng.bootstrap.components.IItemRegistrationComponent;
@@ -118,6 +124,13 @@ import appeng.client.gui.implementations.UpgradeableScreen;
import appeng.client.gui.implementations.VibrationChamberScreen;
import appeng.client.gui.implementations.WirelessScreen;
import appeng.client.gui.implementations.WirelessTermScreen;
import appeng.client.render.DummyFluidItemModel;
import appeng.client.render.FacadeItemModel;
import appeng.client.render.SimpleModelLoader;
import appeng.client.render.cablebus.CableBusModelLoader;
import appeng.client.render.cablebus.P2PTunnelFrequencyModel;
import appeng.client.render.crafting.CraftingCubeModelLoader;
import appeng.client.render.crafting.EncodedPatternModelLoader;
import appeng.client.render.effects.ChargedOreFX;
import appeng.client.render.effects.CraftingFx;
import appeng.client.render.effects.EnergyFx;
@@ -126,6 +139,13 @@ import appeng.client.render.effects.LightningFX;
import appeng.client.render.effects.MatterCannonFX;
import appeng.client.render.effects.ParticleTypes;
import appeng.client.render.effects.VibrantFX;
import appeng.client.render.model.BiometricCardModel;
import appeng.client.render.model.ColorApplicatorModel;
import appeng.client.render.model.DriveModel;
import appeng.client.render.model.GlassModel;
import appeng.client.render.model.MemoryCardModel;
import appeng.client.render.model.SkyCompassModel;
import appeng.client.render.spatial.SpatialPylonModel;
import appeng.client.render.tesr.InscriberTESR;
import appeng.client.render.tesr.SkyChestTESR;
import appeng.container.AEBaseContainer;
@@ -194,20 +214,21 @@ import appeng.me.cache.SecurityCache;
import appeng.me.cache.SpatialPylonCache;
import appeng.me.cache.TickManagerCache;
import appeng.mixins.StructureAccessor;
import appeng.parts.automation.PlaneModelLoader;
import appeng.recipes.game.DisassembleRecipe;
import appeng.recipes.game.FacadeRecipe;
import appeng.recipes.handlers.GrinderRecipe;
import appeng.recipes.handlers.GrinderRecipeSerializer;
import appeng.recipes.handlers.InscriberRecipe;
import appeng.recipes.handlers.InscriberRecipeSerializer;
import appeng.server.AECommand;
import appeng.spatial.StorageCellBiome;
import appeng.spatial.StorageChunkGenerator;
import appeng.spatial.SpatialStorageBiome;
import appeng.spatial.SpatialStorageChunkGenerator;
import appeng.spatial.SpatialStorageDimensionIds;
import appeng.tile.AEBaseTileEntity;
import appeng.tile.crafting.MolecularAssemblerRenderer;
import appeng.worldgen.ChargedQuartzOreConfig;
import appeng.worldgen.ChargedQuartzOreFeature;
import appeng.worldgen.meteorite.MeteoriteStructure;
import appeng.worldgen.meteorite.MeteoriteStructurePiece;
final class Registration {
@@ -239,6 +260,39 @@ final class Registration {
@OnlyIn(Dist.CLIENT)
public void modelRegistryEvent(ModelRegistryEvent event) {
registerSpecialModels();
// TODO: Do not use the internal API
final ApiDefinitions definitions = Api.INSTANCE.definitions();
definitions.getRegistry().getBootstrapComponents(IClientSetupComponent.class)
.forEachRemaining(IClientSetupComponent::setup);
addBuiltInModel("glass", GlassModel::new);
addBuiltInModel("sky_compass", SkyCompassModel::new);
addBuiltInModel("dummy_fluid_item", DummyFluidItemModel::new);
addBuiltInModel("memory_card", MemoryCardModel::new);
addBuiltInModel("biometric_card", BiometricCardModel::new);
addBuiltInModel("drive", DriveModel::new);
addBuiltInModel("color_applicator", ColorApplicatorModel::new);
addBuiltInModel("spatial_pylon", SpatialPylonModel::new);
addBuiltInModel("paint_splotches", PaintSplotchesModel::new);
addBuiltInModel("quantum_bridge_formed", QnbFormedModel::new);
addBuiltInModel("p2p_tunnel_frequency", P2PTunnelFrequencyModel::new);
addBuiltInModel("facade", FacadeItemModel::new);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "encoded_pattern"),
EncodedPatternModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "part_plane"),
PlaneModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "crafting_cube"),
CraftingCubeModelLoader.INSTANCE);
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, "cable_bus"),
new CableBusModelLoader((PartModels) Api.INSTANCE.registries().partModels()));
}
@OnlyIn(Dist.CLIENT)
private static <T extends IModelGeometry<T>> void addBuiltInModel(String id, Supplier<T> modelFactory) {
ModelLoaderRegistry.registerLoader(new ResourceLocation(AppEng.MOD_ID, id),
new SimpleModelLoader<>(modelFactory));
}
/**
@@ -628,6 +682,8 @@ final class Registration {
new ResourceLocation(dimension));
}
MeteoriteStructurePiece.register();
ForgeRegistries.BIOMES.forEach(b -> {
addMeteoriteWorldGen(b);
addQuartzWorldGen(b);
@@ -687,11 +743,12 @@ final class Registration {
MeteoriteStructure.INSTANCE.setRegistryName(MeteoriteStructure.ID),
GenerationStage.Decoration.TOP_LAYER_MODIFICATION);
Registry.register(Registry.CHUNK_GENERATOR_CODEC, AppEng.makeId("storage"), StorageChunkGenerator.CODEC);
Registry.register(Registry.CHUNK_GENERATOR_CODEC, SpatialStorageDimensionIds.CHUNK_GENERATOR_ID,
SpatialStorageChunkGenerator.CODEC);
}
public void registerBiomes(RegistryEvent.Register<Biome> evt) {
evt.getRegistry().register(StorageCellBiome.INSTANCE.setRegistryName(AppEng.makeId("storage")));
evt.getRegistry().register(SpatialStorageBiome.INSTANCE.setRegistryName(SpatialStorageDimensionIds.BIOME_ID));
}
@OnlyIn(Dist.CLIENT)
@@ -726,7 +783,7 @@ final class Registration {
modEventBus.addListener(this::registerParticleFactories);
modEventBus.addListener(this::registerTextures);
MinecraftForge.EVENT_BUS.addListener(this::modelRegistryEvent);
modEventBus.addListener(this::modelRegistryEvent);
modEventBus.addListener(this::registerItemColors);
modEventBus.addListener(this::handleModelBake);
}
@@ -115,7 +115,12 @@ public class ApiCrafting implements ICraftingHelper {
// We use the shared itemstack for an identity lookup.
IAEItemStack ais = Api.instance().storage().getStorageChannel(IItemStorageChannel.class).createStack(is);
return new CraftingPatternDetails(ais, world);
try {
return new CraftingPatternDetails(ais, world);
} catch (IllegalStateException e) {
AELog.warn("Could not decode an invalid pattern %s: %s", is, e);
return null;
}
}
private boolean attemptRecovery(EncodedPatternItem patternItem, ItemStack itemStack, World world) {
@@ -20,7 +20,9 @@ package appeng.core.api.definitions;
import static appeng.block.AEBaseBlock.defaultProps;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.DispenserBlock;
import net.minecraft.block.SlabBlock;
import net.minecraft.block.SoundType;
@@ -28,6 +30,7 @@ import net.minecraft.block.StairsBlock;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.ToolType;
@@ -262,18 +265,20 @@ public final class ApiBlocks implements IBlocks {
this.chiseledQuartzBlock = deco.block("chiseled_quartz_block", () -> new AEDecorativeBlock(QUARTZ_PROPERTIES))
.build();
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS)
.block("quartz_glass", () -> new QuartzGlassBlock(defaultProps(Material.GLASS).notSolid()))
.rendering(new BlockRenderingCustomizer() {
@Override
@OnlyIn(Dist.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderType.getCutout());
}
}).build();
AbstractBlock.IExtendedPositionPredicate<EntityType<?>> neverAllowSpawn = (p1, p2, p3, p4) -> false;
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS).block("quartz_glass", () -> {
return new QuartzGlassBlock(defaultProps(Material.GLASS).notSolid().setAllowsSpawn(neverAllowSpawn));
}).rendering(new BlockRenderingCustomizer() {
@Override
@OnlyIn(Dist.CLIENT)
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
rendering.renderType(RenderType.getCutout());
}
}).build();
this.quartzVibrantGlass = deco
.block("quartz_vibrant_glass",
() -> new QuartzLampBlock(defaultProps(Material.GLASS).setLightLevel(b -> 15).notSolid()))
() -> new QuartzLampBlock(defaultProps(Material.GLASS).setLightLevel(b -> 15).notSolid()
.setAllowsSpawn(neverAllowSpawn)))
.addFeatures(AEFeature.DECORATIVE_LIGHTS, AEFeature.QUARTZ_GLASS)
.rendering(new BlockRenderingCustomizer() {
@Override
@@ -56,6 +56,7 @@ import appeng.hooks.BlockToolDispenseItemBehavior;
import appeng.hooks.MatterCannonDispenseItemBehavior;
import appeng.items.materials.MaterialType;
import appeng.items.misc.CrystalSeedItem;
import appeng.items.misc.CrystalSeedRendering;
import appeng.items.misc.EncodedPatternItem;
import appeng.items.misc.PaintBallItem;
import appeng.items.misc.PaintBallItemRendering;
@@ -299,24 +300,15 @@ public final class ApiItems implements IItems {
this.certusCrystalSeed = registry
.item("certus_crystal_seed",
props -> new CrystalSeedItem(props, materials.purifiedCertusQuartzCrystal().item()))
.bootstrap(item -> new IClientSetupComponent() {
@Override
@OnlyIn(Dist.CLIENT)
public void setup() {
// Expose the growth of the seed to the model system
ItemModelsProperties.func_239418_a_(item, new ResourceLocation("appliedenergistics2:growth"),
(is, w, p) -> CrystalSeedItem.getGrowthTicks(is)
/ (float) CrystalSeedItem.GROWTH_TICKS_REQUIRED);
}
}).features(AEFeature.CRYSTAL_SEEDS).build();
.bootstrap(CrystalSeedRendering::new).features(AEFeature.CRYSTAL_SEEDS).build();
this.fluixCrystalSeed = registry
.item("fluix_crystal_seed",
props -> new CrystalSeedItem(props, materials.purifiedFluixCrystal().item()))
.features(AEFeature.CRYSTAL_SEEDS).build();
.bootstrap(CrystalSeedRendering::new).features(AEFeature.CRYSTAL_SEEDS).build();
this.netherQuartzSeed = registry
.item("nether_quartz_seed",
props -> new CrystalSeedItem(props, materials.purifiedNetherQuartzCrystal().item()))
.features(AEFeature.CRYSTAL_SEEDS).build();
.bootstrap(CrystalSeedRendering::new).features(AEFeature.CRYSTAL_SEEDS).build();
GrowingCrystalEntity.TYPE = registry
.<GrowingCrystalEntity>entity("growing_crystal", GrowingCrystalEntity::new, EntityClassification.MISC)
@@ -22,12 +22,12 @@ import java.util.HashMap;
import java.util.Map;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import appeng.api.events.LocatableEventAnnounce;
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
import appeng.api.features.ILocatable;
import appeng.api.features.ILocatableRegistry;
import appeng.core.AELog;
import appeng.util.Platform;
public final class LocatableRegistry implements ILocatableRegistry {
@@ -35,18 +35,19 @@ public final class LocatableRegistry implements ILocatableRegistry {
public LocatableRegistry() {
this.set = new HashMap<>();
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(this::updateLocatable);
}
@SubscribeEvent
public void updateLocatable(final LocatableEventAnnounce e) {
private void updateLocatable(final LocatableEventAnnounce e) {
if (Platform.isClient()) {
return; // IGNORE!
}
if (e.change == LocatableEvent.REGISTER) {
AELog.debug("Registering locatable %s: %s", e.target.getLocatableSerial(), e.target);
this.set.put(e.target.getLocatableSerial(), e.target);
} else if (e.change == LocatableEvent.UNREGISTER) {
AELog.debug("Unregistering locatable %s: %s", e.target.getLocatableSerial(), e.target);
this.set.remove(e.target.getLocatableSerial());
}
}
@@ -29,7 +29,6 @@ import appeng.core.sync.packets.BlockTransitionEffectPacket;
import appeng.core.sync.packets.ClickPacket;
import appeng.core.sync.packets.CompassRequestPacket;
import appeng.core.sync.packets.CompassResponsePacket;
import appeng.core.sync.packets.CompressedNBTPacket;
import appeng.core.sync.packets.ConfigButtonPacket;
import appeng.core.sync.packets.ConfigValuePacket;
import appeng.core.sync.packets.CraftRequestPacket;
@@ -39,6 +38,7 @@ import appeng.core.sync.packets.ItemTransitionEffectPacket;
import appeng.core.sync.packets.JEIRecipePacket;
import appeng.core.sync.packets.LightningPacket;
import appeng.core.sync.packets.MEFluidInventoryUpdatePacket;
import appeng.core.sync.packets.MEInterfaceUpdatePacket;
import appeng.core.sync.packets.MEInventoryUpdatePacket;
import appeng.core.sync.packets.MatterCannonPacket;
import appeng.core.sync.packets.MockExplosionPacket;
@@ -101,7 +101,7 @@ public class BasePacketHandler {
PACKET_ASSEMBLER_ANIMATION(AssemblerAnimationPacket.class, AssemblerAnimationPacket::new),
PACKET_COMPRESSED_NBT(CompressedNBTPacket.class, CompressedNBTPacket::new),
PACKET_ME_INTERFACE_UPDATE(MEInterfaceUpdatePacket.class, MEInterfaceUpdatePacket::new),
PACKET_PAINTED_ENTITY(PaintedEntityPacket.class, PaintedEntityPacket::new),
@@ -19,13 +19,9 @@
package appeng.core.sync.packets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -41,7 +37,6 @@ import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.network.NetworkDirection;
import appeng.api.storage.data.IAEFluidStack;
import appeng.core.AELog;
import appeng.core.sync.BasePacket;
import appeng.core.sync.network.INetworkInfo;
import appeng.fluids.client.gui.FluidTerminalScreen;
@@ -55,8 +50,6 @@ import appeng.fluids.util.AEFluidStack;
public class MEFluidInventoryUpdatePacket extends BasePacket {
private static final int UNCOMPRESSED_PACKET_BYTE_LIMIT = 16 * 1024 * 1024;
private static final int OPERATION_BYTE_LIMIT = 2 * 1024;
private static final int TEMP_BUFFER_SIZE = 1024;
private static final int STREAM_MASK = 0xff;
// input.
@Nullable
@@ -66,47 +59,18 @@ public class MEFluidInventoryUpdatePacket extends BasePacket {
@Nullable
private final PacketBuffer data;
@Nullable
private final GZIPOutputStream compressFrame;
private int writtenBytes = 0;
private boolean empty = true;
public MEFluidInventoryUpdatePacket(final PacketBuffer stream) {
this.data = null;
this.compressFrame = null;
this.list = new LinkedList<>();
this.ref = stream.readByte();
try (final GZIPInputStream gzReader = new GZIPInputStream(new InputStream() {
@Override
public int read() {
if (stream.readableBytes() <= 0) {
return -1;
}
return stream.readByte() & STREAM_MASK;
}
})) {
final PacketBuffer uncompressed = new PacketBuffer(Unpooled.buffer(stream.readableBytes()));
final byte[] tmp = new byte[TEMP_BUFFER_SIZE];
while (gzReader.available() != 0) {
final int bytes = gzReader.read(tmp);
if (bytes > 0) {
uncompressed.writeBytes(tmp, 0, bytes);
}
}
while (uncompressed.readableBytes() > 0) {
this.list.add(AEFluidStack.fromPacket(uncompressed));
}
} catch (IOException e) {
throw new RuntimeException("Failed to decompress packet.", e);
while (stream.readableBytes() > 0) {
this.list.add(AEFluidStack.fromPacket(stream));
}
this.empty = this.list.isEmpty();
}
@@ -121,14 +85,6 @@ public class MEFluidInventoryUpdatePacket extends BasePacket {
this.data = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
this.data.writeInt(this.getPacketID());
this.data.writeByte(this.ref);
this.compressFrame = new GZIPOutputStream(new OutputStream() {
@Override
public void write(final int value) {
MEFluidInventoryUpdatePacket.this.data.writeByte(value);
}
});
this.list = null;
}
@@ -145,28 +101,19 @@ public class MEFluidInventoryUpdatePacket extends BasePacket {
@Nullable
@Override
public IPacket<?> toPacket(NetworkDirection direction) {
try {
this.compressFrame.close();
this.configureWrite(this.data);
return super.toPacket(direction);
} catch (final IOException e) {
AELog.debug(e);
}
return null;
this.configureWrite(this.data);
return super.toPacket(direction);
}
public void appendFluid(final IAEFluidStack fs) throws IOException, BufferOverflowException {
final PacketBuffer tmp = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
fs.writeToPacket(tmp);
this.compressFrame.flush();
if (this.writtenBytes + tmp.readableBytes() > UNCOMPRESSED_PACKET_BYTE_LIMIT) {
throw new BufferOverflowException();
} else {
this.writtenBytes += tmp.readableBytes();
this.compressFrame.write(tmp.array(), 0, tmp.readableBytes());
this.data.writeBytes(tmp.array(), 0, tmp.readableBytes());
this.empty = false;
}
}
@@ -18,13 +18,7 @@
package appeng.core.sync.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import io.netty.buffer.Unpooled;
@@ -32,7 +26,6 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@@ -41,55 +34,24 @@ import appeng.client.gui.implementations.InterfaceTerminalScreen;
import appeng.core.sync.BasePacket;
import appeng.core.sync.network.INetworkInfo;
//TODO, this is pointless, NBT is already compressed when written to a PacketBuffer.
public class CompressedNBTPacket extends BasePacket {
public class MEInterfaceUpdatePacket extends BasePacket {
// input.
private final CompoundNBT in;
// output...
private final PacketBuffer data;
private final GZIPOutputStream compressFrame;
public CompressedNBTPacket(final PacketBuffer stream) {
public MEInterfaceUpdatePacket(final PacketBuffer stream) {
this.data = null;
this.compressFrame = null;
try (DataInputStream inStream = new DataInputStream(new GZIPInputStream(new InputStream() {
@Override
public int read() {
if (stream.readableBytes() <= 0) {
return -1;
}
return stream.readByte() & 0xff;
}
}))) {
this.in = CompressedStreamTools.read(inStream);
} catch (IOException e) {
throw new RuntimeException("Failed to decompress packet.", e);
}
this.in = stream.readCompoundTag();
}
// api
public CompressedNBTPacket(final CompoundNBT din) throws IOException {
public MEInterfaceUpdatePacket(final CompoundNBT din) throws IOException {
this.in = null;
this.data = new PacketBuffer(Unpooled.buffer(2048));
this.data.writeInt(this.getPacketID());
this.in = din;
this.compressFrame = new GZIPOutputStream(new OutputStream() {
@Override
public void write(final int value) {
CompressedNBTPacket.this.data.writeByte(value);
}
});
CompressedStreamTools.write(din, new DataOutputStream(this.compressFrame));
this.compressFrame.close();
this.data.writeCompoundTag(din);
this.configureWrite(this.data);
}
@@ -19,12 +19,10 @@
package appeng.core.sync.packets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.annotation.Nullable;
@@ -45,7 +43,6 @@ import appeng.client.gui.implementations.CraftConfirmScreen;
import appeng.client.gui.implementations.CraftingCPUScreen;
import appeng.client.gui.implementations.MEMonitorableScreen;
import appeng.client.gui.implementations.NetworkStatusScreen;
import appeng.core.AELog;
import appeng.core.sync.BasePacket;
import appeng.core.sync.network.INetworkInfo;
import appeng.util.item.AEItemStack;
@@ -53,8 +50,6 @@ import appeng.util.item.AEItemStack;
public class MEInventoryUpdatePacket extends BasePacket {
private static final int UNCOMPRESSED_PACKET_BYTE_LIMIT = 16 * 1024 * 1024;
private static final int OPERATION_BYTE_LIMIT = 2 * 1024;
private static final int TEMP_BUFFER_SIZE = 1024;
private static final int STREAM_MASK = 0xff;
// input.
@Nullable
@@ -64,48 +59,18 @@ public class MEInventoryUpdatePacket extends BasePacket {
@Nullable
private final PacketBuffer data;
@Nullable
private final GZIPOutputStream compressFrame;
private int writtenBytes = 0;
private boolean empty = true;
public MEInventoryUpdatePacket(final PacketBuffer stream) {
this.data = null;
this.compressFrame = null;
this.list = new ArrayList<>();
this.ref = stream.readByte();
// int originalBytes = stream.readableBytes();
try (GZIPInputStream gzReader = new GZIPInputStream(new InputStream() {
@Override
public int read() {
if (stream.readableBytes() <= 0) {
return -1;
}
return stream.readByte() & STREAM_MASK;
}
})) {
final PacketBuffer uncompressed = new PacketBuffer(Unpooled.buffer(stream.readableBytes()));
final byte[] tmp = new byte[TEMP_BUFFER_SIZE];
while (gzReader.available() != 0) {
final int bytes = gzReader.read(tmp);
if (bytes > 0) {
uncompressed.writeBytes(tmp, 0, bytes);
}
}
while (uncompressed.readableBytes() > 0) {
this.list.add(AEItemStack.fromPacket(uncompressed));
}
} catch (IOException e) {
throw new RuntimeException("Failed to decompress packet.", e);
while (stream.readableBytes() > 0) {
this.list.add(AEItemStack.fromPacket(stream));
}
this.empty = this.list.isEmpty();
}
@@ -120,14 +85,6 @@ public class MEInventoryUpdatePacket extends BasePacket {
this.data = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
this.data.writeInt(this.getPacketID());
this.data.writeByte(this.ref);
this.compressFrame = new GZIPOutputStream(new OutputStream() {
@Override
public void write(final int value) {
MEInventoryUpdatePacket.this.data.writeByte(value);
}
});
this.list = null;
}
@@ -156,28 +113,19 @@ public class MEInventoryUpdatePacket extends BasePacket {
@Nullable
@Override
public IPacket<?> toPacket(NetworkDirection direction) {
try {
this.compressFrame.close();
this.configureWrite(this.data);
return super.toPacket(direction);
} catch (final IOException e) {
AELog.debug(e);
}
return null;
this.configureWrite(this.data);
return super.toPacket(direction);
}
public void appendItem(final IAEItemStack is) throws IOException, BufferOverflowException {
final PacketBuffer tmp = new PacketBuffer(Unpooled.buffer(OPERATION_BYTE_LIMIT));
is.writeToPacket(tmp);
this.compressFrame.flush();
if (this.writtenBytes + tmp.readableBytes() > UNCOMPRESSED_PACKET_BYTE_LIMIT) {
throw new BufferOverflowException();
} else {
this.writtenBytes += tmp.readableBytes();
this.compressFrame.write(tmp.array(), 0, tmp.readableBytes());
this.data.writeBytes(tmp.array(), 0, tmp.readableBytes());
this.empty = false;
}
}
@@ -20,11 +20,8 @@ package appeng.decorative.solid;
import net.minecraft.block.AbstractGlassBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.material.Material;
import net.minecraft.util.Direction;
import appeng.helpers.AEMaterials;
public class QuartzGlassBlock extends AbstractGlassBlock {
public QuartzGlassBlock(Properties props) {
@@ -33,8 +30,7 @@ public class QuartzGlassBlock extends AbstractGlassBlock {
@Override
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side) {
final Material mat = adjacentBlockState.getMaterial();
if (mat == Material.GLASS || mat == AEMaterials.GLASS) {
if (adjacentBlockState.getBlock() instanceof QuartzGlassBlock) {
if (adjacentBlockState.getRenderType() == state.getRenderType()) {
return true;
}
@@ -29,7 +29,6 @@ import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ToolType;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import appeng.block.AEBaseBlock;
import appeng.core.worlddata.WorldData;
@@ -43,11 +42,10 @@ public class SkyStoneBlock extends AEBaseBlock {
super(props);
this.type = type;
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(this::breakFaster);
}
@SubscribeEvent
public void breakFaster(final PlayerEvent.BreakSpeed event) {
private void breakFaster(final PlayerEvent.BreakSpeed event) {
if (event.getState().getBlock() == this && event.getPlayer() != null) {
final ItemStack is = event.getPlayer().getItemStackFromSlot(EquipmentSlotType.MAINHAND);
int level = -1;
@@ -19,6 +19,7 @@
package appeng.entity;
import java.util.List;
import java.util.Random;
import net.minecraft.block.BlockState;
import net.minecraft.block.material.Material;
@@ -42,6 +43,8 @@ import appeng.util.Platform;
public final class ChargedQuartzEntity extends AEBaseItemEntity {
private static final Random RANDOM = new Random();
public static EntityType<ChargedQuartzEntity> TYPE;
private int delay = 0;
@@ -135,9 +138,15 @@ public final class ChargedQuartzEntity extends AEBaseItemEntity {
}
materials.fluixCrystal().maybeStack(2).ifPresent(is -> {
final ItemEntity entity = new ItemEntity(this.world, this.getPosX(), this.getPosY(), this.getPosZ(),
is);
final double x = Math.floor(this.getPosX()) + .25d + RANDOM.nextDouble() * .5;
final double y = Math.floor(this.getPosY()) + .25d + RANDOM.nextDouble() * .5;
final double z = Math.floor(this.getPosZ()) + .25d + RANDOM.nextDouble() * .5;
final double xSpeed = RANDOM.nextDouble() * .25 - 0.125;
final double ySpeed = RANDOM.nextDouble() * .25 - 0.125;
final double zSpeed = RANDOM.nextDouble() * .25 - 0.125;
final ItemEntity entity = new ItemEntity(this.world, x, y, z, is);
entity.setMotion(xSpeed, ySpeed, zSpeed);
this.world.addEntity(entity);
});
@@ -19,13 +19,14 @@
package appeng.entity;
import net.minecraft.block.BlockState;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityType;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import appeng.api.features.AEFeature;
@@ -35,12 +36,27 @@ import appeng.client.EffectType;
import appeng.core.AEConfig;
import appeng.core.AppEng;
import appeng.items.misc.CrystalSeedItem;
import appeng.util.Platform;
public final class GrowingCrystalEntity extends AEBaseItemEntity {
public static EntityType<GrowingCrystalEntity> TYPE;
// Growth tick progress per tick by number of adjacent accelerators
// Expressed as 1/1000th of a growth tick, applied to progress_1000
// each time this entity ticks.
private static final int[] GROWTH_TICK_PROGRESS = { 1, // no accelerators
40, // 1 accelerator
92, // 2 accelerators
159, // 3 accelerators
247, // 4 accelerators
361, // 5 accelerators
509 // 6 accelerators
};
/**
* The accumulated progress towards a single growth tick of the crystal in
* 1/1000th of a growth tick.
*/
private int progress_1000 = 0;
public GrowingCrystalEntity(EntityType<? extends GrowingCrystalEntity> type, World world) {
@@ -56,126 +72,140 @@ public final class GrowingCrystalEntity extends AEBaseItemEntity {
public void tick() {
super.tick();
final ItemStack is = this.getItem();
final Item gc = is.getItem();
if (!(gc instanceof IGrowableCrystal)) {
return;
}
applyGrowthTick((IGrowableCrystal) gc, is);
}
private void applyGrowthTick(IGrowableCrystal cry, ItemStack is) {
if (!AEConfig.instance().isFeatureEnabled(AEFeature.IN_WORLD_PURIFICATION)) {
return;
}
final ItemStack is = this.getItem();
final Item gc = is.getItem();
final int x = MathHelper.floor(this.getPosX());
final int y = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int z = MathHelper.floor(this.getPosZ());
if (gc instanceof IGrowableCrystal) // if it changes this just stops being an issue...
{
final int j = MathHelper.floor(this.getPosX());
final int i = MathHelper.floor((this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0D);
final int k = MathHelper.floor(this.getPosZ());
BlockPos pos = new BlockPos(x, y, z);
final BlockState state = this.world.getBlockState(pos);
final BlockState state = this.world.getBlockState(new BlockPos(j, i, k));
final Material mat = state.getMaterial();
final IGrowableCrystal cry = (IGrowableCrystal) is.getItem();
final float multiplier = cry.getMultiplier(state, world, pos);
final float multiplier = cry.getMultiplier(state.getBlock(), mat);
final int speed = (int) Math.max(1, this.getSpeed(j, i, k) * multiplier);
if (multiplier <= 0) {
// Crystal is in unsuitable material, reset progress and quit
this.progress_1000 = 0;
return;
}
final boolean isClient = Platform.isClient();
final int progressPerTick = (int) Math.max(1, this.getSpeed(pos) * multiplier);
if (mat.isLiquid()) {
if (isClient) {
this.progress_1000++;
} else {
this.progress_1000 += speed;
}
} else {
if (world.isRemote()) {
// On the client, we reuse the growth-tick-progress
// as a tick-counter for particle effects
int len = getTicksBetweenParticleEffects(progressPerTick);
if (++this.progress_1000 >= len) {
this.progress_1000 = 0;
AppEng.proxy.spawnEffect(EffectType.Vibrant, this.world, this.getPosX(), this.getPosY() + 0.2,
this.getPosZ(), null);
}
} else {
this.progress_1000 += progressPerTick;
if (isClient) {
int len = 40;
if (this.progress_1000 >= 1000) {
// We need to copy the stack or the change detection will not work and not sync
// this new stack to the client
ItemStack newItem = is.copy();
if (speed > 2) {
len = 20;
}
if (speed > 90) {
len = 15;
}
if (speed > 150) {
len = 10;
}
if (speed > 240) {
len = 7;
}
if (speed > 360) {
len = 3;
}
if (speed > 500) {
len = 1;
}
if (this.progress_1000 >= len) {
this.progress_1000 = 0;
AppEng.proxy.spawnEffect(EffectType.Vibrant, this.world, this.getPosX(), this.getPosY() + 0.2,
this.getPosZ(), null);
}
} else {
if (this.progress_1000 > 1000) {
// If we did not use a while loop here, the fastest growth for a crystal
// would be limited to a minimum of 30 seconds (based on 600 required growth
// ticks).
// Should a crystal decide to use a high multiplier for a certain material,
// it should be possible to go faster.
do {
newItem = cry.triggerGrowth(newItem);
this.progress_1000 -= 1000;
// We need to copy the stack or the change detection will not work and not sync
// this new stack to the client
ItemStack newItem = cry.triggerGrowth(is.copy());
this.setItem(newItem);
}
// We assume that if the item changes, the process is complete and we can break
} while (this.progress_1000 >= 1000 && newItem.getItem() == is.getItem());
this.setItem(newItem);
}
}
}
private int getSpeed(final int x, final int y, final int z) {
final int per = 80;
final float mul = 0.3f;
int qty = 0;
if (this.isAccelerated(x + 1, y, z)) {
qty += per + qty * mul;
private static int getTicksBetweenParticleEffects(int progressPerTick) {
if (progressPerTick > 500) {
return 1; // 20 times per second
} else if (progressPerTick > 360) {
return 3;
} else if (progressPerTick > 240) {
return 7;
} else if (progressPerTick > 150) {
return 10;
} else if (progressPerTick > 90) {
return 15;
} else if (progressPerTick > 2) {
return 20;
} else {
return 40; // Every 2 seconds
}
if (this.isAccelerated(x, y + 1, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y, z + 1)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x - 1, y, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y - 1, z)) {
qty += per + qty * mul;
}
if (this.isAccelerated(x, y, z - 1)) {
qty += per + qty * mul;
}
return qty;
}
private boolean isAccelerated(final int x, final int y, final int z) {
final TileEntity te = this.world.getTileEntity(new BlockPos(x, y, z));
/**
* Gets the extra progress per tick in 1/1000th of a growth tick based on the
* surrounding accelerators.
*/
private int getSpeed(BlockPos pos) {
int acceleratorCount = getAcceleratorCount(pos);
if (acceleratorCount < 0) {
return GROWTH_TICK_PROGRESS[0];
} else if (acceleratorCount >= GROWTH_TICK_PROGRESS.length) {
return GROWTH_TICK_PROGRESS[GROWTH_TICK_PROGRESS.length - 1];
} else {
return GROWTH_TICK_PROGRESS[acceleratorCount];
}
}
private int getAcceleratorCount(BlockPos pos) {
int count = 0;
BlockPos.Mutable testPos = new BlockPos.Mutable();
for (Direction direction : Direction.values()) {
if (this.isPoweredAccelerator(testPos.func_239622_a_(pos, direction))) {
count++;
}
}
return count;
}
private boolean isPoweredAccelerator(BlockPos pos) {
final TileEntity te = this.world.getTileEntity(pos);
return te instanceof ICrystalGrowthAccelerator && ((ICrystalGrowthAccelerator) te).isPowered();
}
// Don't let seeds "float" on water surface
@Override
protected void applyFloatMotion() {
ItemStack item = getItem();
// Make ungrown seeds sink, and fully grown seeds bouyant allowing for
// automation based around dropping seeds between 5 CGAs, then catchiung
// them on their way up.
if (item.getItem() instanceof CrystalSeedItem) {
Vector3d v = this.getMotion();
// Apply a much smaller acceleration to make them slowly sink
double yAccel = this.hasNoGravity() ? 0 : -0.002;
// Apply the x/z slow-down, and the y acceleration
this.setMotion(v.x * 0.99, v.y + yAccel, v.z * 0.99);
return;
}
super.applyFloatMotion();
@@ -140,8 +140,8 @@ public final class TinyTNTPrimedEntity extends TNTEntity implements IEntityAddit
return;
}
final Explosion ex = new Explosion(this.world, this, this.getPosX(), this.getPosY(), this.getPosZ(), 0.2f,
false, Mode.BREAK);
final Explosion ex = new Explosion(this.world, this, null, null, this.getPosX(), this.getPosY(), this.getPosZ(),
0.2f, false, Explosion.Mode.BREAK);
final AxisAlignedBB area = new AxisAlignedBB(this.getPosX() - 1.5, this.getPosY() - 1.5f, this.getPosZ() - 1.5,
this.getPosX() + 1.5, this.getPosY() + 1.5, this.getPosZ() + 1.5);
final List<Entity> list = this.world.getEntitiesWithinAABBExcludingEntity(this, area);
@@ -37,15 +37,18 @@ public class FacadeContainer implements IFacadeContainer {
private final int facades = 6;
private final CableBusStorage storage;
private final Runnable changeCallback;
public FacadeContainer(final CableBusStorage cbs) {
public FacadeContainer(final CableBusStorage cbs, Runnable changeCallback) {
this.storage = cbs;
this.changeCallback = changeCallback;
}
@Override
public boolean addFacade(final IFacadePart a) {
if (this.getFacade(a.getSide()) == null) {
this.storage.setFacade(a.getSide().ordinal(), a);
this.notifyChange();
return true;
}
return false;
@@ -56,6 +59,7 @@ public class FacadeContainer implements IFacadeContainer {
if (side != null && side != AEPartLocation.INTERNAL) {
if (this.storage.getFacade(side.ordinal()) != null) {
this.storage.setFacade(side.ordinal(), null);
this.notifyChange();
if (host != null) {
host.markForUpdate();
}
@@ -84,6 +88,7 @@ public class FacadeContainer implements IFacadeContainer {
for (int x = 0; x < this.facades; x++) {
this.storage.setFacade(x, newFacades[x]);
}
this.notifyChange();
}
@Override
@@ -175,4 +180,9 @@ public class FacadeContainer implements IFacadeContainer {
}
return true;
}
private void notifyChange() {
this.changeCallback.run();
}
}
@@ -96,16 +96,15 @@ public class FluidImportBusPart extends SharedFluidBusPart {
}
final TileEntity te = this.getConnectedTE();
LazyOptional<IFluidHandler> fhOpt = LazyOptional.empty();
if (te != null) {
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite());
}
if (fhOpt.isPresent()) {
try {
final IFluidHandler fh = fhOpt.orElse(null);
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory(this.getChannel());
LazyOptional<IFluidHandler> fhOpt = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
this.getSide().getFacing().getOpposite());
if (fhOpt.isPresent()) {
try {
final IFluidHandler fh = fhOpt.orElseThrow(() -> new IllegalStateException());
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory(this.getChannel());
if (fh != null) {
final FluidStack fluidStack = fh.drain(this.calculateAmountToSend(), FluidAction.SIMULATE);
if (this.filterEnabled() && !this.isInFilter(fluidStack)) {
@@ -128,12 +127,11 @@ public class FluidImportBusPart extends SharedFluidBusPart {
}
return TickRateModulation.IDLE;
} catch (GridAccessException e) {
// skip
}
} catch (GridAccessException e) {
e.printStackTrace();
}
}
return TickRateModulation.SLEEP;
}
@@ -1,14 +0,0 @@
package appeng.hooks;
import net.minecraft.util.RegistryKey;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.server.ServerWorld;
public interface DynamicDimensions {
ServerWorld addWorld(RegistryKey<World> worldId, RegistryKey<DimensionType> dimensionTypeId,
ChunkGenerator chunkGenerator);
}
@@ -0,0 +1,127 @@
package appeng.hooks;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.BlockFaceUV;
import net.minecraft.client.renderer.model.BlockPartFace;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.util.Direction;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import appeng.core.AppEng;
import appeng.mixins.unlitquad.BakedQuadAccessor;
/**
* Implementation details of allowing quads to be defined as "unlit" in JSON
* models by specifying an "unlit" boolean property on the face.
*/
public class UnlitQuadHooks {
/**
* Offset into the vertex data (which is represented as integers).
*/
private static final int LIGHT_OFFSET = getLightOffset();
// Lightmap texture coordinate with full intensity light leading to no drop in
// brightness
private static final int UNLIT_LIGHT_UV = LightTexture.packLight(15, 15);
/**
* Thread-Local flag to indicate that an enhanced Applied Energistics model is
* currently being deserialized.
*/
private static final ThreadLocal<Boolean> ENABLE_UNLIT_EXTENSIONS = new ThreadLocal<>();
/**
* Notify the unlit model system that a specific model is about to be
* deserialized by {@link net.minecraft.client.renderer.model.ModelBakery}.
*/
public static void beginDeserializingModel(ResourceLocation location) {
String namespace = location.getNamespace();
if (namespace.equals(AppEng.MOD_ID)) {
ENABLE_UNLIT_EXTENSIONS.set(true);
}
}
/**
* Notify the unlit model system that deserialization of a model has ended.
*/
public static void endDeserializingModel() {
ENABLE_UNLIT_EXTENSIONS.set(false);
}
public static boolean isUnlitExtensionEnabled() {
Boolean b = ENABLE_UNLIT_EXTENSIONS.get();
return b != null && b;
}
public static BlockPartFace enhanceModelElementFace(BlockPartFace modelElement, JsonElement jsonElement) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (JSONUtils.getBoolean(jsonObject, "unlit", false)) {
return new UnlitBlockPartFace(modelElement.cullFace, modelElement.tintIndex, modelElement.texture,
modelElement.blockFaceUV);
}
return modelElement;
}
/**
* Creates a new quad from the given quad and pre-bakes it to not be affected by
* lighting (neither diffuse lighting nor the prebaked lightmap). This works on
* the assumption that Vanilla will not modify a quad's lightmap data if it's
* not zero.
*/
public static BakedQuad makeUnlit(BakedQuad quad) {
int[] vertexData = quad.getVertexData().clone();
int stride = DefaultVertexFormats.BLOCK.getIntegerSize();
// Set the pre-baked texture coords for the lightmap.
// Vanilla will not overwrite them if they are non-zero
for (int i = 0; i < 4; i++) {
vertexData[stride * i + LIGHT_OFFSET] = UNLIT_LIGHT_UV;
}
TextureAtlasSprite sprite = ((BakedQuadAccessor) quad).getSprite();
// Copy the quad to disable diffuse lighting
return new BakedQuad(vertexData, quad.getTintIndex(), quad.getFace(), sprite, false /* diffuse lighting */);
}
/**
* This subclass is used as a marker to indicate this face deserialized from
* JSON is supposed to be unlit, which translates to processing by
* {@link #makeUnlit(BakedQuad)}.
*/
public static class UnlitBlockPartFace extends BlockPartFace {
public UnlitBlockPartFace(Direction cullFaceIn, int tintIndexIn, String textureIn, BlockFaceUV blockFaceUVIn) {
super(cullFaceIn, tintIndexIn, textureIn, blockFaceUVIn);
}
}
/**
* Find the index in the BakedQuad vertex data (for vertex 0) where the lightmap
* coordinates are. Assumes the BLOCK vertex format.
*/
private static int getLightOffset() {
VertexFormat format = DefaultVertexFormats.BLOCK;
int offset = 0;
for (VertexFormatElement element : format.getElements()) {
// TEX_2SB is the lightmap vertex element
if (element == DefaultVertexFormats.TEX_2SB) {
if (element.getType() != VertexFormatElement.Type.SHORT) {
throw new UnsupportedOperationException("Expected light map format to be of type SHORT");
}
if (offset % 4 != 0) {
throw new UnsupportedOperationException("Expected light map offset to be 4-byte aligned");
}
return offset / 4;
}
offset += element.getSize();
}
throw new UnsupportedOperationException("Failed to find the lightmap index in the block vertex format");
}
}
@@ -37,7 +37,7 @@ public enum TheOneProbeText {
}
public String getUnlocalized() {
return this.root + '.' + this.name().toLowerCase(Locale.ENGLISH);
return this.root + '.' + this.name().toLowerCase(Locale.ROOT);
}
}
@@ -24,17 +24,19 @@ import javax.annotation.Nullable;
import com.google.common.base.Preconditions;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.BlockState;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.Entity;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemModelsProperties;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tags.FluidTags;
import net.minecraft.tags.ITag;
import net.minecraft.util.IItemProvider;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
@@ -43,6 +45,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import appeng.api.implementations.items.IGrowableCrystal;
import appeng.core.AEConfig;
import appeng.core.localization.ButtonToolTips;
import appeng.entity.GrowingCrystalEntity;
import appeng.items.AEBaseItem;
@@ -96,8 +99,24 @@ public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal {
}
@Override
public float getMultiplier(final Block blk, final Material mat) {
return 0.5f;
public float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos) {
// Check for the improved fluid tag and return the improved multiplier
String improvedFluidTagName = AEConfig.instance().getImprovedFluidTag();
if (improvedFluidTagName != null) {
ITag<Fluid> tag = FluidTags.getCollection().get(new ResourceLocation(improvedFluidTagName));
if (tag != null && state.getFluidState().isTagged(tag)) {
return AEConfig.instance().getImprovedFluidMultiplier();
}
}
// Check for the normal supported fluid
if (world != null && world.func_234923_W_() == World.field_234919_h_) {
// In the nether, use Lava as the "normal" fluid
return state.getFluidState().isTagged(FluidTags.LAVA) ? 1 : 0;
} else {
return state.getFluidState().isTagged(FluidTags.WATER) ? 1 : 0;
}
}
@Override
@@ -0,0 +1,29 @@
package appeng.items.misc;
import net.minecraft.item.Item;
import net.minecraft.item.ItemModelsProperties;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import appeng.bootstrap.components.IClientSetupComponent;
/**
* Exposes a predicate "growth", which is used in the item model to
* differentiate the growth stages.
*/
public class CrystalSeedRendering implements IClientSetupComponent {
private final Item item;
public CrystalSeedRendering(Item item) {
this.item = item;
}
@Override
@OnlyIn(Dist.CLIENT)
public void setup() {
// Expose the growth of the seed to the model system
ItemModelsProperties.func_239418_a_(item, new ResourceLocation("appliedenergistics2:growth"),
(is, w, p) -> CrystalSeedItem.getGrowthTicks(is) / (float) CrystalSeedItem.GROWTH_TICKS_REQUIRED);
}
}
@@ -307,14 +307,27 @@ public class EncodedPatternItem extends AEBaseItem {
final ListNBT tagIn = new ListNBT();
final ListNBT tagOut = new ListNBT();
boolean hasInput = false;
for (final ItemStack i : in) {
tagIn.add(createItemTag(i));
if (!i.isEmpty()) {
hasInput = true;
}
}
Preconditions.checkArgument(hasInput, "cannot encode a pattern that has no inputs.");
boolean hasNonEmptyOutput = false;
for (final ItemStack i : out) {
tagOut.add(createItemTag(i));
if (!i.isEmpty()) {
hasNonEmptyOutput = true;
}
}
// Patterns without any outputs are corrupt! Never encode such a pattern.
Preconditions.checkArgument(hasNonEmptyOutput, "cannot encode a pattern that has no output.");
encodedValue.put(EncodedPatternItem.NBT_INGREDIENTS, tagIn);
encodedValue.put(EncodedPatternItem.NBT_PRODUCTS, tagOut);
return encodedValue;
@@ -18,35 +18,40 @@
package appeng.items.storage;
import java.time.Instant;
import java.util.List;
import java.util.Locale;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants;
import appeng.api.implementations.TransitionResult;
import appeng.api.implementations.items.ISpatialStorageCell;
import appeng.api.storage.ISpatialDimension;
import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.core.localization.GuiText;
import appeng.items.AEBaseItem;
import appeng.spatial.SpatialDimensionManager;
import appeng.spatial.StorageHelper;
import appeng.spatial.SpatialStorageHelper;
import appeng.spatial.SpatialStoragePlot;
import appeng.spatial.SpatialStoragePlotManager;
import appeng.spatial.TransitionInfo;
public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorageCell {
private static final String TAG_DIMENSION_ID = "dimension_id";
private static final String TAG_PLOT_ID = "plot_id";
/**
* This is only stored in the itemstack to display in the tooltip on the
* client-side.
*/
private static final String TAG_PLOT_SIZE = "plot_size";
private final int maxRegion;
@@ -59,18 +64,22 @@ public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorag
@Override
public void addInformation(final ItemStack stack, final World world, final List<ITextComponent> lines,
final ITooltipFlag advancedTooltips) {
final RegistryKey<World> worldId = this.getStoredDimension(stack);
if (worldId == null) {
int plotId = this.getAllocatedPlotId(stack);
if (plotId == -1) {
lines.add(GuiText.Unformatted.text().deepCopy().mergeStyle(TextFormatting.ITALIC));
lines.add(GuiText.SpatialCapacity.text(maxRegion, maxRegion, maxRegion));
} else {
SpatialDimensionManager.INSTANCE.addCellDimensionTooltip(worldId, lines);
return;
}
if (advancedTooltips.isAdvanced()) {
if (worldId != null && worldId.func_240901_a_() != null) {
lines.add(new StringTextComponent("Dimension: " + worldId.func_240901_a_()));
}
// Add a serial number to allows players to keep different cells apart
// Try to make this a little more flavorful.
String serialNumber = String.format(Locale.ROOT, "SP-%04d", plotId);
lines.add(GuiText.SerialNumber.text(serialNumber));
CompoundNBT tag = stack.getTag();
if (tag != null && tag.contains(TAG_PLOT_SIZE, Constants.NBT.TAG_LONG)) {
BlockPos size = BlockPos.fromLong(tag.getLong(TAG_PLOT_SIZE));
lines.add(GuiText.StoredSize.text(size.getX(), size.getY(), size.getZ()));
}
}
@@ -85,70 +94,80 @@ public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorag
}
@Override
public RegistryKey<World> getStoredDimension(final ItemStack is) {
public int getAllocatedPlotId(final ItemStack is) {
final CompoundNBT c = is.getTag();
if (c != null && c.contains(TAG_DIMENSION_ID)) {
if (c != null && c.contains(TAG_PLOT_ID)) {
try {
ResourceLocation worldId = new ResourceLocation(c.getString(TAG_DIMENSION_ID));
return RegistryKey.func_240903_a_(Registry.WORLD_KEY, worldId);
int plotId = c.getInt(TAG_PLOT_ID);
if (SpatialStoragePlotManager.INSTANCE.getPlot(plotId) == null) {
return -1;
}
return plotId;
} catch (Exception e) {
AELog.warn("Failed to retrieve storage cell dimension.", e);
AELog.warn("Failed to retrieve spatial storage dimension: %s", e);
}
}
return null;
return -1;
}
@Override
public TransitionResult doSpatialTransition(final ItemStack is, final ServerWorld w, final WorldCoord min,
public boolean doSpatialTransition(final ItemStack is, final ServerWorld w, final WorldCoord min,
final WorldCoord max, int playerId) {
final int targetX = max.x - min.x - 1;
final int targetY = max.y - min.y - 1;
final int targetZ = max.z - min.z - 1;
final int maxSize = this.getMaxStoredDim(is);
if (targetX > maxSize && targetY > maxSize && targetZ > maxSize) {
AELog.info(
"Failing spatial transition because the transfer area (%dx%dx%d) exceeds the cell capacity (%s).",
targetX, targetY, targetZ, maxSize);
return false;
}
final BlockPos targetSize = new BlockPos(targetX, targetY, targetZ);
ISpatialDimension manager = SpatialDimensionManager.INSTANCE;
SpatialStoragePlotManager manager = SpatialStoragePlotManager.INSTANCE;
RegistryKey<World> worldId = this.getStoredDimension(is);
if (worldId == null || manager.getWorld(worldId) == null) {
worldId = manager.createNewCellDimension(targetSize);
SpatialStoragePlot plot = SpatialStoragePlotManager.INSTANCE.getPlot(this.getAllocatedPlotId(is));
if (plot != null) {
// Check that the existing plot has the right size
if (!plot.getSize().equals(targetSize)) {
AELog.info(
"Failing spatial transition because the transfer area (%dx%dx%d) does not match the spatial storage plot's size (%s).",
targetX, targetY, targetZ, plot.getSize());
return false;
}
} else {
// Otherwise allocate a new one
plot = manager.allocatePlot(targetSize, playerId);
}
if (worldId == null) {
// Failed to create the dimension
return new TransitionResult(false, 0);
}
// Store some information about this transition in the plot
TransitionInfo info = new TransitionInfo(w.func_234923_W_().func_240901_a_(), min.getBlockPos(),
max.getBlockPos(), Instant.now());
manager.setLastTransition(plot.getId(), info);
try {
if (manager.isCellDimension(worldId)) {
ServerWorld cellWorld = manager.getWorld(worldId);
ServerWorld cellWorld = manager.getWorld();
BlockPos scale = manager.getCellDimensionSize(worldId);
BlockPos offset = plot.getOrigin();
if (scale.equals(targetSize)) {
if (targetX <= maxSize && targetY <= maxSize && targetZ <= maxSize) {
BlockPos offset = manager.getCellDimensionOrigin(worldId);
this.setStoredDimension(is, plot.getId(), plot.getSize());
SpatialStorageHelper.getInstance().swapRegions(w, min.x + 1, min.y + 1, min.z + 1, cellWorld, offset.getX(),
offset.getY(), offset.getZ(), targetX - 1, targetY - 1, targetZ - 1);
this.setStoredDimension(is, worldId);
StorageHelper.getInstance().swapRegions(w, min.x + 1, min.y + 1, min.z + 1, cellWorld,
offset.getX(), offset.getY(), offset.getZ(), targetX - 1, targetY - 1, targetZ - 1);
return new TransitionResult(true, 0);
}
}
}
return new TransitionResult(false, 0);
return true;
} finally {
// clean up newly created dimensions that failed transfer
if (manager.isCellDimension(worldId) && this.getStoredDimension(is) == null) {
manager.deleteCellDimension(worldId);
if (this.getAllocatedPlotId(is) == -1) {
manager.freePlot(plot.getId(), true);
}
}
}
private void setStoredDimension(final ItemStack is, RegistryKey<World> worldId) {
public void setStoredDimension(final ItemStack is, int plotId, BlockPos size) {
final CompoundNBT c = is.getOrCreateTag();
c.putString(TAG_DIMENSION_ID, worldId.func_240901_a_().toString());
c.putInt(TAG_PLOT_ID, plotId);
c.putLong(TAG_PLOT_SIZE, size.toLong());
}
}
@@ -32,7 +32,6 @@ import net.minecraft.block.BlockState;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemModelsProperties;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.item.SnowballItem;
@@ -69,7 +68,6 @@ import appeng.block.networking.CableBusBlock;
import appeng.block.paint.PaintSplotchesBlock;
import appeng.core.AEConfig;
import appeng.core.Api;
import appeng.core.AppEng;
import appeng.core.localization.GuiText;
import appeng.helpers.IMouseWheelItem;
import appeng.hooks.IBlockTool;
@@ -326,21 +324,21 @@ public class ColorApplicatorItem extends AEBasePoweredItem
return w.setBlockState(pos, newState);
}
if (blk instanceof CableBusBlock && p != null) {
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
}
TileEntity be = w.getTileEntity(pos);
if (be instanceof IColorableTile) {
IColorableTile ct = (IColorableTile) be;
AEColor c = ct.getColor();
if (c != newColor) {
ct.recolourBlock(side, newColor, null);
ct.recolourBlock(side, newColor, p);
return true;
}
return false;
}
if (blk instanceof CableBusBlock && p != null) {
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
}
return false;
}
-1
View File
@@ -176,7 +176,6 @@ public class Grid implements IGrid {
}
gridNode.getGridProxy().gridChanged();
// postEventTo( gridNode, networkChanged );
}
@Override
+2 -1
View File
@@ -327,8 +327,9 @@ public class GridNode implements IGridNode, IPathItem {
@Override
public void setPlayerID(final int playerID) {
if (playerID >= 0) {
if (playerID >= 0 && this.playerID != playerID) {
this.playerID = playerID;
gridProxy.onGridNotification(GridNotification.OWNER_CHANGED);
}
}
+1 -1
View File
@@ -502,7 +502,7 @@ public class EnergyGridCache implements IEnergyGrid {
private void removeRequester(IAEPowerStorage requester) {
Preconditions.checkState(!ongoingInjectOperation,
"Cannot modify energy requesters while energy is being injected.");
this.requesters.add(requester);
this.requesters.remove(requester);
}
private void addProvider(IAEPowerStorage provider) {
+14 -2
View File
@@ -81,8 +81,16 @@ public class SecurityCache implements ISecurityGrid {
private void updateSecurityKey() {
final long lastCode = this.securityKey;
/**
* Placing a security station will propagate the security station's owner to all
* connected grid nodes to prevent the network from not reforming due to
* different owners later.
*/
int newOwner = -1;
if (this.securityProvider.size() == 1) {
this.securityKey = this.securityProvider.get(0).getSecurityKey();
ISecurityProvider securityProvider = this.securityProvider.get(0);
this.securityKey = securityProvider.getSecurityKey();
newOwner = securityProvider.getOwner();
} else {
this.securityKey = -1;
}
@@ -90,7 +98,11 @@ public class SecurityCache implements ISecurityGrid {
if (lastCode != this.securityKey) {
this.getGrid().postEvent(new MENetworkSecurityChange());
for (final IGridNode n : this.getGrid().getNodes()) {
((GridNode) n).setLastSecurityKey(this.securityKey);
GridNode gridNode = (GridNode) n;
gridNode.setLastSecurityKey(this.securityKey);
if (gridNode.getPlayerID() != newOwner) {
gridNode.setPlayerID(newOwner);
}
}
}
}
@@ -22,7 +22,6 @@ import java.util.Iterator;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
@@ -159,12 +158,16 @@ public class QuantumCluster implements ILocatable, IAECluster {
if (qc != null) {
final World theWorld = qc.center.getWorld();
if (!qc.isDestroyed) {
ChunkPos cPos = new ChunkPos(qc.center.getPos());
if (theWorld.getChunkProvider().isChunkLoaded(cPos)) {
// In future versions, we might actually want to delay the entire registration
// until the center
// tile begins ticking normally.
if (theWorld.isBlockLoaded(qc.center.getPos())) {
final World cur = theWorld.getServer().getWorld(theWorld.func_234923_W_());
final TileEntity te = theWorld.getTileEntity(qc.center.getPos());
return te != qc.center || theWorld != cur;
} else {
AELog.warn("Found a registered QNB with serial %s whose chunk seems to be unloaded: %s", qe, qc);
}
}
}
@@ -275,4 +278,17 @@ public class QuantumCluster implements ILocatable, IAECluster {
private void setRing(final QuantumBridgeTileEntity[] ring) {
this.Ring = ring;
}
@Override
public String toString() {
if (center == null) {
return "QuantumCluster{no-center}";
}
World world = center.getWorld();
BlockPos pos = center.getPos();
return "QuantumCluster{" + world + "," + pos + "}";
}
}
@@ -167,7 +167,6 @@ public class AENetworkProxy implements IGridBlock {
* short cut!
*
* @return grid of node
*
* @throws GridAccessException of node or grid is null
*/
public IGrid getGrid() throws GridAccessException {
@@ -280,6 +279,11 @@ public class AENetworkProxy implements IGridBlock {
@Override
public void onGridNotification(final GridNotification notification) {
if (notification == GridNotification.OWNER_CHANGED) {
gp.saveChanges();
return;
}
if (this.gp instanceof CablePart) {
((CablePart) this.gp).markForUpdate();
}
@@ -28,4 +28,7 @@ public interface IGridProxyable extends IGridHost {
DimensionalCoord getLocation();
void gridChanged();
void saveChanges();
}
@@ -1,105 +0,0 @@
package appeng.mixins;
import java.util.Map;
import java.util.concurrent.Executor;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.IDynamicRegistries;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.Dimension;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import net.minecraft.world.border.IBorderListener;
import net.minecraft.world.border.WorldBorder;
import net.minecraft.world.chunk.listener.IChunkStatusListener;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.settings.DimensionGeneratorSettings;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.DerivedWorldInfo;
import net.minecraft.world.storage.IServerConfiguration;
import net.minecraft.world.storage.IServerWorldInfo;
import net.minecraft.world.storage.SaveFormat;
import appeng.hooks.DynamicDimensions;
@Mixin(MinecraftServer.class)
public class MinecraftServerMixin implements DynamicDimensions {
@Shadow()
private IDynamicRegistries.Impl field_240767_f_;
@Shadow
private Map<RegistryKey<World>, ServerWorld> worlds;
@Shadow
private IServerConfiguration field_240768_i_;
@Shadow
private Executor backgroundExecutor;
@Shadow
private SaveFormat.LevelSave anvilConverterForAnvilFile;
public ServerWorld addWorld(RegistryKey<World> worldId, RegistryKey<DimensionType> dimensionTypeId,
ChunkGenerator chunkGenerator) {
Preconditions.checkArgument(!worlds.containsKey(worldId), "World with id %s already exists.",
worldId.func_240901_a_());
// Each worlds must have a corresponding dimension options, otherwise they will
// not be re-created on load
DimensionGeneratorSettings generatorOptions = this.field_240768_i_.getDimensionGeneratorSettings();
Preconditions.checkArgument(!generatorOptions.func_236224_e_().containsKey(worldId.func_240901_a_()),
"Dimension config with id %s already exists.", worldId.func_240901_a_());
DimensionType dimensionType = field_240767_f_.func_230520_a_().getValueForKey(dimensionTypeId);
Preconditions.checkArgument(dimensionType != null, "dimensionTypeId %s is not registered", dimensionTypeId);
// Create dimension options with the given world-id and chunk generator
Dimension dimensionOptions = new Dimension(
() -> field_240767_f_.func_230520_a_().getValueForKey(dimensionTypeId), chunkGenerator);
IServerWorldInfo serverWorldProperties = this.field_240768_i_.func_230407_G_();
// This sucks a little, but during startup, the server will use the same
// listener for every world,
// but it is ultimately only stored in the chunk storage. So we retrieve it from
// the overworld to
// mirror the original code as much as possible
ServerWorld overworld = worlds.get(ServerWorld.field_234918_g_);
Preconditions.checkState(overworld != null, "Overworld does not exist!");
IChunkStatusListener worldGenerationProgressListener = ((ThreadedAnvilChunkStorageAccessor) overworld
.getChunkProvider().chunkManager).getWorldGenerationProgressListener();
// Inherit seed + WorldBorder from overworld
long seed = overworld.getSeed();
WorldBorder worldBorder = overworld.getWorldBorder();
MinecraftServer self = (MinecraftServer) (Object) this;
DerivedWorldInfo unmodifiableLevelProperties = new DerivedWorldInfo(this.field_240768_i_,
serverWorldProperties);
ServerWorld world = new ServerWorld(self, this.backgroundExecutor, this.anvilConverterForAnvilFile,
unmodifiableLevelProperties, worldId, dimensionTypeId, dimensionType, worldGenerationProgressListener,
chunkGenerator, false, seed, ImmutableList.of(), false);
worldBorder.addListener(new IBorderListener.Impl(world.getWorldBorder()));
this.worlds.put(worldId, world);
// Adding it here will cause the world to be re-created on server restart
RegistryKey<Dimension> dimOptKey = RegistryKey.func_240903_a_(Registry.DIMENSION_KEY, worldId.func_240901_a_());
generatorOptions.func_236224_e_().register(dimOptKey, dimensionOptions);
generatorOptions.func_236224_e_().func_239662_d_(dimOptKey); // Otherwise it wont be saved
// Ensure the save properties are saved, or the world will potentially be lost
// on restart
this.anvilConverterForAnvilFile.func_237288_a_(this.field_240767_f_, this.field_240768_i_,
self.getPlayerList().getHostPlayerData());
return world;
}
}
@@ -0,0 +1,38 @@
package appeng.mixins.spatial;
import java.util.List;
import java.util.Map;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import net.minecraft.util.RegistryKey;
import net.minecraft.world.Dimension;
import appeng.spatial.SpatialStorageDimensionIds;
/**
* This Mixin tries to hide the screen that warns users about experimental
* features being used, but only if AE2's dimension is the only added dimension.
*/
@Mixin(Dimension.class)
public class DimensionOptionMixin {
/**
* This injects right after the method makes a local copy of all present
* dimensions, and modifies the list by removing our mod-provided dimension from
* it.
* <p>
* This means Vanilla will perform it's check whether it should display an
* experimental warning without considering our dimension.
*/
@ModifyVariable(method = "func_236060_a_", at = @At(value = "INVOKE_ASSIGN", ordinal = 0, target = "Lcom/google/common/collect/Lists;newArrayList(Ljava/lang/Iterable;)Ljava/util/ArrayList;", remap = false), allow = 1)
private static List<Map.Entry<RegistryKey<Dimension>, Dimension>> overrideExperimentalCheck(
List<Map.Entry<RegistryKey<Dimension>, Dimension>> dimensions) {
// this only removes our dimension from the check
dimensions.removeIf(e -> e.getKey() == SpatialStorageDimensionIds.DIMENSION_ID);
return dimensions;
}
}
@@ -1,4 +1,4 @@
package appeng.mixins;
package appeng.mixins.spatial;
import java.util.OptionalLong;
@@ -11,15 +11,20 @@ import net.minecraft.server.IDynamicRegistries;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.DimensionType;
import appeng.spatial.SpatialDimensionManager;
import appeng.spatial.SpatialStorageDimensionIds;
/**
* Adds the storage cell world dimension type as a built-in dimension type. This
* can be registered as a JSON file as well, but doing so will trigger an
* experimental feature warning when the world is being loaded.
*/
@Mixin(value = DimensionType.class)
public class DimensionTypeMixin {
@Inject(method = "func_236027_a_", at = @At("TAIL"))
private static void addRegistryDefaults(IDynamicRegistries.Impl registryTracker, CallbackInfoReturnable<?> cir) {
registryTracker.func_239774_a_(SpatialDimensionManager.STORAGE_DIMENSION_TYPE,
registryTracker.func_239774_a_(SpatialStorageDimensionIds.DIMENSION_TYPE_ID,
new DimensionType(OptionalLong.of(12000), false, false, false, false, false, false, true, false, false,
256, BlockTags.INFINIBURN_OVERWORLD.getName(), 1.0f));
@@ -1,4 +1,4 @@
package appeng.mixins;
package appeng.mixins.spatial;
import java.util.Optional;
@@ -11,8 +11,8 @@ import net.minecraft.client.world.DimensionRenderInfo;
import net.minecraft.util.RegistryKey;
import net.minecraft.world.DimensionType;
import appeng.spatial.SpatialDimensionManager;
import appeng.spatial.StorageSkyProperties;
import appeng.spatial.SpatialStorageDimensionIds;
import appeng.spatial.SpatialStorageSkyProperties;
@Mixin(DimensionRenderInfo.class)
public class SkyPropertiesMixin {
@@ -20,8 +20,8 @@ public class SkyPropertiesMixin {
@Inject(method = "func_239215_a_", at = @At("HEAD"), cancellable = true)
private static void byDimensionType(Optional<RegistryKey<DimensionType>> optional,
CallbackInfoReturnable<DimensionRenderInfo> ci) {
if (optional.orElse(null) == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
ci.setReturnValue(StorageSkyProperties.INSTANCE);
if (optional.orElse(null) == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
ci.setReturnValue(SpatialStorageSkyProperties.INSTANCE);
}
}
@@ -1,4 +1,4 @@
package appeng.mixins;
package appeng.mixins.spatial;
import com.mojang.blaze3d.matrix.MatrixStack;
@@ -12,7 +12,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.WorldRenderer;
import appeng.client.render.SpatialSkyRender;
import appeng.spatial.SpatialDimensionManager;
import appeng.spatial.SpatialStorageDimensionIds;
@Mixin(value = WorldRenderer.class)
public class SkyRenderMixin {
@@ -23,7 +23,7 @@ public class SkyRenderMixin {
@SuppressWarnings("ConstantConditions")
@Inject(method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V", at = @At("HEAD"), cancellable = true)
public void renderSky(MatrixStack matrices, float tickDelta, CallbackInfo ci) {
if (mc.world.func_234922_V_() == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
if (mc.world.func_234922_V_() == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
SpatialSkyRender.getInstance().render(matrices);
ci.cancel();
}
@@ -0,0 +1,15 @@
package appeng.mixins.unlitquad;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
@Mixin(BakedQuad.class)
public interface BakedQuadAccessor {
@Accessor
TextureAtlasSprite getSprite();
}
@@ -0,0 +1,37 @@
package appeng.mixins.unlitquad;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.BlockModel;
import net.minecraft.client.renderer.model.BlockPart;
import net.minecraft.client.renderer.model.BlockPartFace;
import net.minecraft.client.renderer.model.IModelTransform;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import appeng.hooks.UnlitQuadHooks;
/**
* This mixin hooks into conversion from {@link BlockPartFace} to
* {@link BakedQuad} to apply our unlit extensions if the block part face is an
* instance of our marker class
* {@link appeng.hooks.UnlitQuadHooks.UnlitBlockPartFace}.
*/
@Mixin(BlockModel.class)
public class BlockModelMixin {
@Inject(method = "bakeFace", at = @At("RETURN"), cancellable = true, require = 1, allow = 1)
private static void onBakeFace(BlockPart partIn, BlockPartFace partFaceIn, TextureAtlasSprite spriteIn,
Direction directionIn, IModelTransform transformIn, ResourceLocation locationIn,
CallbackInfoReturnable<BakedQuad> cri) {
if (partFaceIn instanceof UnlitQuadHooks.UnlitBlockPartFace) {
cri.setReturnValue(UnlitQuadHooks.makeUnlit(cri.getReturnValue()));
}
}
}
@@ -0,0 +1,35 @@
package appeng.mixins.unlitquad;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.renderer.model.BlockPartFace;
import appeng.hooks.UnlitQuadHooks;
/**
* This mixin will call the hook to deserialize the unlit property, but only if
* we are currently deserializing an AE2 model.
*/
@Mixin(BlockPartFace.Deserializer.class)
public class BlockPartFaceDeserializerMixin {
@Inject(method = "deserialize", at = @At("RETURN"), cancellable = true, allow = 1, remap = false)
public void onDeserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext,
CallbackInfoReturnable<BlockPartFace> cri) {
if (!UnlitQuadHooks.isUnlitExtensionEnabled()) {
return; // Not in a model that activated the deserializer
}
BlockPartFace modelElement = cri.getReturnValue();
cri.setReturnValue(UnlitQuadHooks.enhanceModelElementFace(modelElement, jsonElement));
}
}
@@ -0,0 +1,34 @@
package appeng.mixins.unlitquad;
import java.io.IOException;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.renderer.model.BlockModel;
import net.minecraft.client.renderer.model.ModelBakery;
import net.minecraft.util.ResourceLocation;
import appeng.hooks.UnlitQuadHooks;
/**
* The only job of this mixin is to only enable the unlit extensions if the
* model is whitelisted for it, which is decided in {@link UnlitQuadHooks}.
*/
@Mixin(ModelBakery.class)
public class ModelBakeryMixin {
@Inject(method = "loadModel", at = @At("HEAD"), allow = 1)
protected void onBeginLoadModel(ResourceLocation location, CallbackInfoReturnable<BlockModel> cri)
throws IOException {
UnlitQuadHooks.beginDeserializingModel(location);
}
@Inject(method = "loadModel", at = @At("RETURN"))
protected void onEndLoadModel(ResourceLocation location, CallbackInfoReturnable<BlockModel> cri) {
UnlitQuadHooks.endDeserializingModel();
}
}
@@ -41,7 +41,6 @@ import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
@@ -82,7 +81,6 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
private final EnumSet<LayerFlags> myLayerFlags = EnumSet.noneOf(LayerFlags.class);
private YesNo hasRedstone = YesNo.UNDECIDED;
private IPartHost tcb;
// TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not.
private boolean requiresDynamicRender = false;
private boolean inWorld = false;
// Cached collision shape for living entities
@@ -125,7 +123,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
@Override
public IFacadeContainer getFacadeContainer() {
return new FacadeContainer(this);
return new FacadeContainer(this, this::invalidateShapes);
}
@Override
@@ -1096,11 +1094,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
}
}
VoxelShape shape = VoxelShapes.empty();
for (final AxisAlignedBB bx : boxes) {
shape = VoxelShapes.or(shape, VoxelShapes.create(bx));
}
return shape;
return VoxelShapeCache.get(boxes);
}
private void invalidateShapes() {
@@ -0,0 +1,81 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.parts;
import java.util.List;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.shapes.IBooleanFunction;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
/**
* While creation of a {@link VoxelShape} with
* {@link VoxelShapes#create(AxisAlignedBB)} is fast enough, combining voxel
* shapes with {@link VoxelShapes#or(VoxelShape, VoxelShape)} or any other
* combination method, as well as {@link VoxelShape#simplify()} are <b>extremely
* slow</b>. For example: Creating a VoxelShape for a list of 5 bounding boxes
* 10,000 times takes about 1.7 seconds.
*
* <p>
* To reduce the impact of this on cables, we introduce a global voxel shape
* cache so that cables can share their combined voxel shapes better.
*/
final class VoxelShapeCache {
// Why using a List here should not make much of a difference vs. using a Set:
// The part's bounding box depends on the side it is attached to, and the sides
// are iterated over in a fixed order, meaning the order of bounding boxes
// should
// be the same for a same set of parts.
private static final LoadingCache<List<AxisAlignedBB>, VoxelShape> CACHE = CacheBuilder.newBuilder()//
.maximumSize(10000L)//
.build(new CacheLoader<List<AxisAlignedBB>, VoxelShape>() {
@Override
public VoxelShape load(List<AxisAlignedBB> key) {
return create(key);
}
});
private VoxelShapeCache() {
}
public static VoxelShape get(List<AxisAlignedBB> boxes) {
return CACHE.getUnchecked(boxes);
}
private static VoxelShape create(List<AxisAlignedBB> boxes) {
if (boxes.isEmpty()) {
return VoxelShapes.empty();
}
int i = 0;
VoxelShape shape = VoxelShapes.create(boxes.get(i));
for (; i < boxes.size(); i++) {
AxisAlignedBB box = boxes.get(i);
shape = VoxelShapes.combine(shape, VoxelShapes.create(box), IBooleanFunction.OR);
}
return shape.simplify();
}
}
@@ -28,15 +28,11 @@ import net.minecraft.block.BlockState;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.client.model.data.IModelData;
import appeng.api.parts.IPart;
import appeng.api.parts.IPartHost;
import appeng.api.parts.IPartModel;
import appeng.api.util.AEPartLocation;
import appeng.items.parts.PartModels;
public class IdentityAnnihilationPlanePart extends AnnihilationPlanePart {
@@ -45,6 +45,11 @@ public class FEP2PTunnelPart extends P2PTunnelPart<FEP2PTunnelPart> {
super(is);
}
@Override
protected float getPowerDrainPerTick() {
return 2.0f;
}
@PartModels
public static List<IPartModel> getModels() {
return MODELS.getModels();
@@ -54,7 +54,8 @@ public class FluidP2PTunnelPart extends P2PTunnelPart<FluidP2PTunnelPart> {
return MODELS.getModels();
}
public float getPowerDrainPerTick() {
@Override
protected float getPowerDrainPerTick() {
return 2.0f;
}
@@ -68,6 +68,11 @@ public class ItemP2PTunnelPart extends P2PTunnelPart<ItemP2PTunnelPart> implemen
super(is);
}
@Override
protected float getPowerDrainPerTick() {
return POWER_DRAIN;
}
@Override
public void onNeighborChanged(IBlockReader w, BlockPos pos, BlockPos neighbor) {
this.cachedInv = null;
@@ -235,10 +240,6 @@ public class ItemP2PTunnelPart extends P2PTunnelPart<ItemP2PTunnelPart> implemen
return this.getDestination().getSlotLimit(slot);
}
public float getPowerDrainPerTick() {
return POWER_DRAIN;
}
@Override
public IPartModel getStaticModels() {
return MODELS.getModel(this.isPowered(), this.isActive());
@@ -56,6 +56,10 @@ public class LightP2PTunnelPart extends P2PTunnelPart<LightP2PTunnelPart> implem
super(is);
}
protected float getPowerDrainPerTick() {
return 0.5f;
}
@Override
public void chanRender(final MENetworkChannelsChanged c) {
this.onTunnelNetworkChange();
@@ -186,10 +190,6 @@ public class LightP2PTunnelPart extends P2PTunnelPart<LightP2PTunnelPart> implem
return this.doWork() ? TickRateModulation.URGENT : TickRateModulation.SLOWER;
}
public float getPowerDrainPerTick() {
return 0.5f;
}
@Override
public IPartModel getStaticModels() {
return MODELS.getModel(this.isPowered(), this.isActive());
@@ -67,6 +67,11 @@ public class MEP2PTunnelPart extends P2PTunnelPart<MEP2PTunnelPart> implements I
this.outerProxy.setFlags(GridFlags.DENSE_CAPACITY, GridFlags.CANNOT_CARRY_COMPRESSED);
}
@Override
protected float getPowerDrainPerTick() {
return 2.0f;
}
@Override
public void readFromNBT(final CompoundNBT extra) {
super.readFromNBT(extra);
@@ -61,6 +61,11 @@ public abstract class P2PTunnelPart<T extends P2PTunnelPart> extends BasicStateP
public P2PTunnelPart(final ItemStack is) {
super(is);
this.getProxy().setIdlePowerUsage(this.getPowerDrainPerTick());
}
protected float getPowerDrainPerTick() {
return 1.0f;
}
public TunnelCollection<T> getCollection(final Collection<P2PTunnelPart> collection,
@@ -55,6 +55,11 @@ public class RedstoneP2PTunnelPart extends P2PTunnelPart<RedstoneP2PTunnelPart>
super(is);
}
@Override
protected float getPowerDrainPerTick() {
return 0.5f;
}
@MENetworkEventSubscribe
public void changeStateA(final MENetworkBootingStatusChange bs) {
this.setNetworkReady();
@@ -123,10 +128,6 @@ public class RedstoneP2PTunnelPart extends P2PTunnelPart<RedstoneP2PTunnelPart>
this.setNetworkReady();
}
public float getPowerDrainPerTick() {
return 0.5f;
}
@Override
public void onNeighborChanged(IBlockReader w, BlockPos pos, BlockPos neighbor) {
if (!this.isOutput()) {
@@ -20,7 +20,6 @@ import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistryEntry;
import appeng.core.AEConfig;
import appeng.core.AppEng;
import appeng.core.sync.BasePacket;
public class GrinderRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>>
@@ -14,7 +14,6 @@ import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistryEntry;
import appeng.api.features.InscriberProcessType;
import appeng.core.AppEng;
import appeng.core.sync.BasePacket;
public class InscriberRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>>
+3 -1
View File
@@ -20,6 +20,8 @@ package appeng.server;
import static net.minecraft.command.Commands.literal;
import java.util.Locale;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@@ -46,7 +48,7 @@ public final class AECommand {
private void add(LiteralArgumentBuilder<net.minecraft.command.CommandSource> builder, Commands subCommand) {
LiteralArgumentBuilder<CommandSource> subCommandBuilder = literal(subCommand.name().toLowerCase())
LiteralArgumentBuilder<CommandSource> subCommandBuilder = literal(subCommand.name().toLowerCase(Locale.ROOT))
.requires(src -> src.hasPermissionLevel(subCommand.level));
subCommand.command.addArguments(subCommandBuilder);
subCommandBuilder.executes(ctx -> {
+3 -1
View File
@@ -19,13 +19,15 @@
package appeng.server;
import appeng.server.subcommands.ChunkLogger;
import appeng.server.subcommands.SpatialStorageCommand;
import appeng.server.subcommands.Supporters;
import appeng.server.subcommands.TestMeteoritesCommand;
import appeng.server.subcommands.TestOreGenCommand;
public enum Commands {
Chunklogger(4, new ChunkLogger(), false), Supporters(0, new Supporters(), false),
TestOreGen(4, new TestOreGenCommand(), true), TestMeteorites(4, new TestMeteoritesCommand(), true);
TestOreGen(4, new TestOreGenCommand(), true), TestMeteorites(4, new TestMeteoritesCommand(), true),
Spatial(4, new SpatialStorageCommand(), false);
public final int level;
public final ISubCommand command;
@@ -0,0 +1,327 @@
package appeng.server.subcommands;
import static net.minecraft.command.Commands.literal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.function.UnaryOperator;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.CommandException;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.IFormattableTextComponent;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.event.ClickEvent;
import net.minecraft.util.text.event.HoverEvent;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import appeng.core.Api;
import appeng.core.worlddata.WorldData;
import appeng.items.storage.SpatialStorageCellItem;
import appeng.server.ISubCommand;
import appeng.spatial.SpatialStorageDimensionIds;
import appeng.spatial.SpatialStoragePlot;
import appeng.spatial.SpatialStoragePlotManager;
import appeng.spatial.TransitionInfo;
/**
* This admin command allows management of spatial storage plots.
*/
public class SpatialStorageCommand implements ISubCommand {
@Override
public void addArguments(LiteralArgumentBuilder<CommandSource> builder) {
// Shows info about a given plot or about the plot the player is currently in
builder.then(literal("info").executes(ctx -> {
showPlotInfo(ctx.getSource(), getCurrentPlot(ctx.getSource()));
return 1;
}).then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
showPlotInfo(ctx.getSource(), getPlot(plotId));
return 1;
})));
// Teleport into the plot
builder.then(literal("tp").then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
teleportToPlot(ctx.getSource(), plotId);
return 1;
})));
// Teleport from the current plot back to the source of its content, or do the
// same for a given plot id
builder.then(literal("tpback").executes(ctx -> {
teleportBack(ctx.getSource());
return 1;
}).then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
teleportBack(ctx.getSource(), getPlot(plotId));
return 1;
})));
// Creates a storage cell for the given plot id and gives it to the player
builder.then(
literal("givecell").then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
giveCell(ctx.getSource(), plotId);
return 1;
})));
}
/**
* If the player is currently within a spatial storage plot, teleports them back
* to the last source of transition.
*/
private void teleportBack(CommandSource source) {
if (source.getWorld().func_234923_W_() != SpatialStorageDimensionIds.WORLD_ID) {
throw new CommandException(new StringTextComponent("Must be within the spatial storage world."));
}
BlockPos playerPos = new BlockPos(source.getPos());
int x = playerPos.getX();
int z = playerPos.getZ();
// This is slow, but for an admin-command it's acceptable
for (SpatialStoragePlot plot : SpatialStoragePlotManager.INSTANCE.getPlots()) {
BlockPos origin = plot.getOrigin();
BlockPos size = plot.getSize();
if (x >= origin.getX() && x <= origin.getX() + size.getX() && z >= origin.getZ()
&& z <= origin.getZ() + size.getZ()) {
teleportBack(source, plot);
return;
}
}
throw new CommandException(ITextComponent.func_241827_a_("Couldn't find a plot for the current position."));
}
/**
* Teleports back from the given plot.
*/
private void teleportBack(CommandSource source, SpatialStoragePlot plot) {
TransitionInfo lastTransition = plot.getLastTransition();
if (lastTransition == null) {
throw new CommandException(
ITextComponent.func_241827_a_("This plot doesn't have a last known transition."));
}
String command = getTeleportCommand(lastTransition.getWorldId(), lastTransition.getMin().add(0, 1, 0));
runCommandFor(source, command);
}
/**
* Shows detailed information about a spatial storage plot.
*/
private static void showPlotInfo(CommandSource source, SpatialStoragePlot plot) {
sendKeyValuePair(source, "Plot ID", String.valueOf(plot.getId()));
// Show the owner of the spatial storage plot
int playerId = plot.getOwner();
if (playerId != -1) {
UUID profileId = WorldData.instance().playerData().getProfileId(playerId);
if (profileId == null) {
sendKeyValuePair(source, "Owner", "Unknown AE2 player (" + playerId + ")");
} else {
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
ServerPlayerEntity player = server.getPlayerList().getPlayerByUUID(profileId);
if (player == null) {
sendKeyValuePair(source, "Owner", "Unknown Minecraft profile (" + profileId + ")");
} else {
sendKeyValuePair(source, "Owner", player.getDisplayName());
}
}
} else {
sendKeyValuePair(source, "Owner", "Unknown");
}
sendKeyValuePair(source, "Size", formatBlockPos(plot.getSize(), "x"));
// Show the plot's origin and make it clickable to teleport directly to it
String teleportToPlotCommand = getTeleportCommand(SpatialStorageDimensionIds.WORLD_ID.func_240901_a_(),
plot.getOrigin());
sendKeyValuePair(source, "Origin", new StringTextComponent(formatBlockPos(plot.getOrigin(), ","))
.modifyStyle(makeCommandLink(teleportToPlotCommand, "Teleport into plot")));
sendKeyValuePair(source, "Region file:", plot.getRegionFilename());
// Show information about what was last transfered into the plot (with a
// clickable link to the source)
TransitionInfo lastTransition = plot.getLastTransition();
if (lastTransition != null) {
source.sendFeedback(new StringTextComponent("Last Transition:").mergeStyle(TextFormatting.UNDERLINE,
TextFormatting.BOLD), true);
String sourceWorldId = lastTransition.getWorldId().toString();
IFormattableTextComponent sourceLink = new StringTextComponent(
sourceWorldId + " - " + formatBlockPos(lastTransition.getMin(), ",") + " to "
+ formatBlockPos(lastTransition.getMax(), ","));
String tpCommand = getTeleportCommand(lastTransition.getWorldId(), lastTransition.getMin().add(0, 1, 0));
sourceLink.modifyStyle(makeCommandLink(tpCommand, "Click to teleport"));
sendKeyValuePair(source, "Source", sourceLink);
sendKeyValuePair(source, "When", lastTransition.getTimestamp().toString());
} else {
source.sendFeedback(new StringTextComponent("Last Transition unknown"), true);
}
}
private static void teleportToPlot(CommandSource source, int plotId) {
SpatialStoragePlot plot = getPlot(plotId);
String teleportCommand = getTeleportCommand(SpatialStorageDimensionIds.WORLD_ID.func_240901_a_(),
plot.getOrigin());
runCommandFor(source, teleportCommand);
}
private void giveCell(CommandSource source, int plotId) throws CommandSyntaxException {
ServerPlayerEntity player = source.asPlayer();
SpatialStoragePlot plot = getPlot(plotId);
ItemStack cell;
int longestSide = getLongestSide(plot.getSize());
if (longestSide <= 2) {
cell = Api.instance().definitions().items().spatialCell2().stack(1);
} else if (longestSide <= 16) {
cell = Api.instance().definitions().items().spatialCell16().stack(1);
} else {
cell = Api.instance().definitions().items().spatialCell128().stack(1);
}
if (!(cell.getItem() instanceof SpatialStorageCellItem)) {
throw new CommandException(
ITextComponent.func_241827_a_("Storage cell items don't implement the storage cell interface!"));
}
SpatialStorageCellItem spatialCellItem = (SpatialStorageCellItem) cell.getItem();
spatialCellItem.setStoredDimension(cell, plotId, plot.getSize());
player.addItemStackToInventory(cell);
}
private static int getLongestSide(BlockPos size) {
return Math.max(size.getX(), Math.max(size.getY(), size.getZ()));
}
@Override
public void call(MinecraftServer srv, CommandContext<CommandSource> ctx, CommandSource sender) {
List<SpatialStoragePlot> plots = new ArrayList<>(SpatialStoragePlotManager.INSTANCE.getPlots());
// Prints the least recently used plots
plots.sort(Comparator.comparing((SpatialStoragePlot plot) -> {
TransitionInfo lastTransition = plot.getLastTransition();
if (lastTransition != null) {
return lastTransition.getTimestamp();
} else {
return Instant.MIN;
}
}).reversed());
for (int i = 0; i < Math.min(5, plots.size()); i++) {
SpatialStoragePlot plot = plots.get(i);
String size = formatBlockPos(plot.getSize(), "x");
BlockPos originPos = plot.getOrigin();
String origin = formatBlockPos(originPos, ",");
ITextComponent infoLink = new StringTextComponent("Plot #" + plot.getId())
.modifyStyle(makeCommandLink("/ae2 spatial info " + plot.getId(), "Click to show details"));
ITextComponent tpLink = new StringTextComponent("Origin: " + origin)
.modifyStyle(makeCommandLink("/ae2 spatial tp " + plot.getId(), "Click to teleport into plot"));
IFormattableTextComponent message = new StringTextComponent("").append(infoLink)
.appendString(" Size: " + size + " ").append(tpLink);
sender.sendFeedback(message, true);
}
}
private static String formatBlockPos(BlockPos size, String separator) {
return size.getX() + separator + size.getY() + separator + size.getZ();
}
private static UnaryOperator<Style> makeCommandLink(String command, String tooltip) {
return style -> style.applyFormatting(TextFormatting.UNDERLINE)
.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command))
.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new StringTextComponent(tooltip)));
}
private static void runCommandFor(CommandSource source, String command) {
Commands commandManager = ServerLifecycleHooks.getCurrentServer().getCommandManager();
commandManager.handleCommand(source, command);
}
private static String getTeleportCommand(ResourceLocation worldId, BlockPos pos) {
return "/execute in " + worldId + " run tp @s " + pos.getX() + " " + (pos.getY() + 1) + " " + pos.getZ();
}
private static SpatialStoragePlot getPlot(int plotId) {
SpatialStoragePlot plot = SpatialStoragePlotManager.INSTANCE.getPlot(plotId);
if (plot == null) {
throw new CommandException(new StringTextComponent("Plot not found: " + plotId));
}
return plot;
}
private static void sendKeyValuePair(CommandSource source, String label, ITextComponent value) {
source.sendFeedback(
new StringTextComponent("")
.append(new StringTextComponent(label + ": ").mergeStyle(TextFormatting.BOLD)).append(value),
true);
}
private static void sendKeyValuePair(CommandSource source, String label, String value) {
sendKeyValuePair(source, label, new StringTextComponent(value));
}
/**
* Gets the spatial storage plot that the command source is currently in.
*/
private static SpatialStoragePlot getCurrentPlot(CommandSource source) {
if (source.getWorld().func_234923_W_() != SpatialStorageDimensionIds.WORLD_ID) {
throw new CommandException(new StringTextComponent("Must be within the spatial storage world."));
}
BlockPos playerPos = new BlockPos(source.getPos());
int x = playerPos.getX();
int z = playerPos.getZ();
// This is slow, but for an admin-command it's acceptable
for (SpatialStoragePlot plot : SpatialStoragePlotManager.INSTANCE.getPlots()) {
BlockPos origin = plot.getOrigin();
BlockPos size = plot.getSize();
if (x >= origin.getX() && x <= origin.getX() + size.getX() && z >= origin.getZ()
&& z <= origin.getZ() + size.getZ()) {
return plot;
}
}
throw new CommandException(ITextComponent.func_241827_a_("Couldn't find a plot for the current position."));
}
}
+14 -16
View File
@@ -34,9 +34,10 @@ import net.minecraft.world.ITickList;
import net.minecraft.world.NextTickListEntry;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.lighting.WorldLightManager;
import net.minecraft.world.server.ServerTickList;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.server.ServerWorldLightManager;
import appeng.api.movable.IMovableHandler;
import appeng.api.movable.IMovableRegistry;
@@ -300,12 +301,17 @@ public class CachedPlane {
private void updateChunks() {
WorldLightManager lightManager = world.getLightManager();
// update shit..
for (int x = 0; x < this.cx_size; x++) {
for (int z = 0; z < this.cz_size; z++) {
final Chunk c = this.myChunks[x][z];
// FIXME: Light shit
c.markDirty();
if (lightManager instanceof ServerWorldLightManager) {
ServerWorldLightManager serverLightManager = (ServerWorldLightManager) lightManager;
for (int x = 0; x < this.cx_size; x++) {
for (int z = 0; z < this.cz_size; z++) {
final Chunk c = this.myChunks[x][z];
serverLightManager.lightChunk(c, false);
c.markDirty();
}
}
}
@@ -317,17 +323,14 @@ public class CachedPlane {
WorldData.instance().compassData().service().updateArea(this.getWorld(), c);
// FIXME this was sending chunks to players...
SChunkDataPacket cdp = new SChunkDataPacket(c, verticalBits, false);
((ServerChunkProvider) world.getChunkProvider()).chunkManager.getTrackingPlayers(c.getPos(), false)
world.getChunkProvider().chunkManager.getTrackingPlayers(c.getPos(), false)
.forEach(spe -> spe.connection.sendPacket(cdp));
}
}
// FIXME check if this makes any sense at all to send changes to players asap
ServerChunkProvider serverChunkProvider = (ServerChunkProvider) world.getChunkProvider();
serverChunkProvider.tick(() -> false);
world.getChunkProvider().tick(() -> false);
}
List<WorldCoord> getUpdates() {
@@ -340,7 +343,6 @@ public class CachedPlane {
private static class BlockStorageData {
public BlockState state;
public int light;
}
private class Column {
@@ -373,8 +375,6 @@ public class CachedPlane {
final ChunkSection[] storage = this.c.getSections();
final ChunkSection extendedBlockStorage = storage[y >> 4];
extendedBlockStorage.setBlockState(this.x, y & 15, this.z, data.state);
// FIXME extendedBlockStorage.setBlockLight( this.x, y & 15, this.z, data.light
// );
}
private void fillData(final int y, BlockStorageData data) {
@@ -382,8 +382,6 @@ public class CachedPlane {
final ChunkSection extendedblockstorage = storage[y >> 4];
data.state = extendedblockstorage.getBlockState(this.x, y & 15, this.z);
// FIXME data.light = extendedblockstorage.getBlockLight( this.x, y & 15, this.z
// );
}
private boolean doNotSkip(final int y) {
@@ -1,70 +0,0 @@
package appeng.spatial;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.storage.WorldSavedData;
/**
* Helps with encoding and decoding the extra data we attach to each created
* storage dimension world as persistent state.
*/
public final class SpatialDimensionExtraData extends WorldSavedData {
/**
* ID of this data when it is attached to a world.
*/
public static final String ID = "ae2_spatial_info";
// Used to allow forward compatibility
private static final int CURRENT_FORMAT = 1;
private static final String TAG_FORMAT = "format";
private static final String TAG_SIZE = "size";
/**
* The storage size of this dimension. This is dicateted by the pylon structure
* size used to perform the first transfer into this dimension. Once it's set,
* it cannot be changed anymore.
*/
private BlockPos size = BlockPos.ZERO;
public SpatialDimensionExtraData() {
super(ID);
}
public SpatialDimensionExtraData(BlockPos size) {
super(ID);
this.size = size;
}
public BlockPos getSize() {
return size;
}
public void setSize(BlockPos size) {
this.size = size;
setDirty(true);
}
@Override
public void read(CompoundNBT tag) {
int version = tag.getInt(TAG_FORMAT);
if (version != CURRENT_FORMAT) {
// Currently no new format has been defined, as such anything but the current
// version is invalid
throw new IllegalStateException("Invalid AE2 spatial info version: " + version);
}
size = NBTUtil.readBlockPos(tag.getCompound(TAG_SIZE));
}
@Override
public CompoundNBT write(CompoundNBT tag) {
tag.putInt(TAG_FORMAT, CURRENT_FORMAT);
tag.put(TAG_SIZE, NBTUtil.writeBlockPos(size));
return tag;
}
}
@@ -1,201 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2017, 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.spatial;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import appeng.api.storage.ISpatialDimension;
import appeng.core.AELog;
import appeng.core.AppEng;
import appeng.core.localization.GuiText;
import appeng.hooks.DynamicDimensions;
public final class SpatialDimensionManager implements ISpatialDimension {
// A region file is 512x512 blocks (32x32 chunks),
// to avoid creating the 4 regions around 0,0,0,
// we move the origin to the middle of region 0,0
public static final BlockPos REGION_CENTER = new BlockPos(512 / 2, 64, 512 / 2);
public static final RegistryKey<DimensionType> STORAGE_DIMENSION_TYPE = RegistryKey
.func_240903_a_(Registry.DIMENSION_TYPE_KEY, AppEng.makeId("storage_cell"));
public static final ISpatialDimension INSTANCE = new SpatialDimensionManager();
private static final String DIM_ID_PREFIX = "spatial_";
private SpatialDimensionManager() {
}
@Override
public ServerWorld getWorld(RegistryKey<World> cellDim) {
MinecraftServer server = getServer();
return server.getWorld(cellDim);
}
@Override
public RegistryKey<World> createNewCellDimension(BlockPos size) {
ResourceLocation dimKey = findFreeDimensionId();
AELog.info("Allocating storage cell dimension '%s'", dimKey);
DynamicDimensions dynamicDimensions = (DynamicDimensions) getServer();
RegistryKey<World> worldId = RegistryKey.func_240903_a_(Registry.WORLD_KEY, dimKey);
ServerWorld world = dynamicDimensions.addWorld(worldId, STORAGE_DIMENSION_TYPE, StorageChunkGenerator.INSTANCE);
SpatialDimensionExtraData spatialExtraData = world.getSavedData().getOrCreate(SpatialDimensionExtraData::new,
SpatialDimensionExtraData.ID);
spatialExtraData.setSize(size);
return worldId;
}
/**
* Tries finding the next free storage cell dimension ID based on the currently
* registered storage cell dimensions.
*/
private ResourceLocation findFreeDimensionId() {
int maxId = 0;
for (RegistryKey<World> worldId : getServer().func_240770_D_()) {
ResourceLocation regName = worldId.func_240901_a_();
if (regName == null || !AppEng.MOD_ID.equals(regName.getNamespace())) {
continue;
}
String path = regName.getPath();
if (!path.startsWith(DIM_ID_PREFIX)) {
continue;
}
try {
String numericIdPart = path.substring(DIM_ID_PREFIX.length());
maxId = Math.max(Integer.parseUnsignedInt(numericIdPart), maxId);
} catch (NumberFormatException e) {
AELog.warn("Unparsable storage cell dimension id '%s'", path, e);
}
}
++maxId;
return AppEng.makeId(DIM_ID_PREFIX + maxId);
}
@Override
public void deleteCellDimension(RegistryKey<World> worldId) {
AELog.info("Unregistering storage cell dimension %s", worldId.func_240901_a_());
MinecraftServer server = getServer();
// FIXME FABRIC ServerWorld world = DimensionManager.getWorld(server, worldId, false, false);
// FIXME FABRIC if (world != null) {
// FIXME FABRIC DimensionManager.unloadWorld(world);
// FIXME FABRIC }
// FIXME FABRIC DimensionManager.unloadWorlds(server, true);
// FIXME FABRIC DimensionManager.unregisterDimension(worldId.getId());
throw new IllegalStateException();
}
@Override
public boolean isCellDimension(RegistryKey<World> worldId) {
ResourceLocation id = worldId.func_240901_a_();
if (!id.getNamespace().equals(AppEng.MOD_ID)) {
return false; // World belongs to a different mod
}
if (!id.getPath().startsWith(DIM_ID_PREFIX)) {
return false;
}
// Check that the world has the right dimension type
ServerWorld world = getServer().getWorld(worldId);
if (world == null) {
return false; // Non-existent world
}
return world.func_234922_V_().equals(STORAGE_DIMENSION_TYPE);
}
@Override
public BlockPos getCellDimensionOrigin(RegistryKey<World> worldId) {
return REGION_CENTER;
}
@Override
public BlockPos getCellDimensionSize(RegistryKey<World> worldId) {
SpatialDimensionExtraData extraData = getExtraData(worldId);
return extraData != null ? extraData.getSize() : BlockPos.ZERO;
}
@Override
public void addCellDimensionTooltip(RegistryKey<World> worldId, List<ITextComponent> lines) {
// Check if the cell dimension type is even registered
ResourceLocation registryName = worldId.func_240901_a_();
if (registryName == null || !AppEng.MOD_ID.equals(registryName.getNamespace())) {
return;
}
if (!registryName.getPath().startsWith(DIM_ID_PREFIX)) {
return;
}
// Add the actual stored size
BlockPos size = SpatialDimensionManager.INSTANCE.getCellDimensionSize(worldId);
lines.add(GuiText.StoredSize.text(size.getX(), size.getY(), size.getZ()));
// Add a serial number to allows players to keep different cells apart
int dimId;
try {
String numericIdPart = registryName.getPath().substring(DIM_ID_PREFIX.length());
dimId = Integer.parseUnsignedInt(numericIdPart);
} catch (NumberFormatException ignored) {
return;
}
// Try to make this a little more flavorful.
String serialNumber = String.format(Locale.ROOT, "SP-%04d", dimId);
lines.add(GuiText.SerialNumber.text(serialNumber));
}
@Nullable
private SpatialDimensionExtraData getExtraData(RegistryKey<World> worldId) {
ServerWorld world = getWorld(worldId);
if (world == null) {
return null;
}
return world.getSavedData().get(SpatialDimensionExtraData::new, SpatialDimensionExtraData.ID);
}
private static MinecraftServer getServer() {
return ServerLifecycleHooks.getCurrentServer();
}
}
@@ -25,11 +25,14 @@ import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public class StorageCellBiome extends Biome {
/**
* The single biome used within the spatial storage world.
*/
public class SpatialStorageBiome extends Biome {
public static final StorageCellBiome INSTANCE = new StorageCellBiome();
public static final SpatialStorageBiome INSTANCE = new SpatialStorageBiome();
public StorageCellBiome() {
public SpatialStorageBiome() {
super(new Biome.Builder()
.surfaceBuilder(
new ConfiguredSurfaceBuilder<>(SurfaceBuilder.NOPE, SurfaceBuilder.STONE_STONE_GRAVEL_CONFIG))
@@ -43,18 +43,21 @@ import net.minecraft.world.gen.settings.DimensionStructuresSettings;
import appeng.core.Api;
public class StorageChunkGenerator extends ChunkGenerator {
/**
* Chunk generator the spatial storage world.
*/
public class SpatialStorageChunkGenerator extends ChunkGenerator {
private final Blockreader columnSample;
public static final StorageChunkGenerator INSTANCE = new StorageChunkGenerator();
public static final SpatialStorageChunkGenerator INSTANCE = new SpatialStorageChunkGenerator();
public static final Codec<StorageChunkGenerator> CODEC = RecordCodecBuilder
public static final Codec<SpatialStorageChunkGenerator> CODEC = RecordCodecBuilder
.create((instance) -> instance.stable(INSTANCE));
private final BlockState defaultBlockState;
private StorageChunkGenerator() {
private SpatialStorageChunkGenerator() {
super(createBiomeProvider(), createSettings());
this.defaultBlockState = Api.instance().definitions().blocks().matrixFrame().block().getDefaultState();
@@ -71,7 +74,7 @@ public class StorageChunkGenerator extends ChunkGenerator {
}
private static BiomeProvider createBiomeProvider() {
return new SingleBiomeProvider(StorageCellBiome.INSTANCE);
return new SingleBiomeProvider(SpatialStorageBiome.INSTANCE);
}
private static DimensionStructuresSettings createSettings() {
@@ -0,0 +1,57 @@
package appeng.spatial;
import net.minecraft.util.RegistryKey;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.Dimension;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import appeng.core.AppEng;
import appeng.mixins.spatial.DimensionTypeMixin;
/**
* IDs for the spatial storage world related dimension objects.
*/
public final class SpatialStorageDimensionIds {
/**
* ID of the {@link DimensionType} used for the spatial storage world.
* <p>
* This is defined in {@link DimensionTypeMixin}.
*/
public static final RegistryKey<DimensionType> DIMENSION_TYPE_ID = RegistryKey
.func_240903_a_(Registry.DIMENSION_TYPE_KEY, AppEng.makeId("spatial_storage"));
/**
* ID of the {@link net.minecraft.world.gen.ChunkGenerator} used for the spatial
* storage world.
*/
public static final ResourceLocation CHUNK_GENERATOR_ID = AppEng.makeId("spatial_storage");
/**
* ID of the {@link net.minecraft.world.biome.Biome} used for the spatial
* storage world.
*/
public static final ResourceLocation BIOME_ID = AppEng.makeId("spatial_storage");
/**
* ID of the {@link Dimension} used for the spatial storage dimension.
* <p>
* This is defined in
* <code>data/minecraft/dimension/appliedenergistics2/spatial_storage.json</code>
*/
public static final RegistryKey<Dimension> DIMENSION_ID = RegistryKey.func_240903_a_(Registry.DIMENSION_KEY,
AppEng.makeId("spatial_storage"));
/**
* ID of the {@link World} that is instantiated from the dimension/dimension
* type.
*/
public static final RegistryKey<World> WORLD_ID = RegistryKey.func_240903_a_(Registry.WORLD_KEY,
AppEng.makeId("spatial_storage"));
private SpatialStorageDimensionIds() {
}
}
@@ -39,13 +39,13 @@ import appeng.api.util.WorldCoord;
import appeng.core.Api;
import appeng.core.AppEng;
public class StorageHelper {
public class SpatialStorageHelper {
private static StorageHelper instance;
private static SpatialStorageHelper instance;
public static StorageHelper getInstance() {
public static SpatialStorageHelper getInstance() {
if (instance == null) {
instance = new StorageHelper();
instance = new SpatialStorageHelper();
}
return instance;
}
@@ -64,7 +64,7 @@ public class StorageHelper {
try {
oldWorld = (ServerWorld) entity.world;
newWorld = (ServerWorld) link.dim;
newWorld = link.dim;
} catch (final Throwable e) {
return entity;
}
@@ -98,7 +98,7 @@ public class StorageHelper {
ChunkStatus.FULL, true);
if (entity instanceof ServerPlayerEntity
&& link.dim.func_234922_V_() == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
&& link.dim.func_234922_V_() == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
AppEng.instance().getAdvancementTriggers().getSpatialExplorer().trigger((ServerPlayerEntity) entity);
}
@@ -0,0 +1,189 @@
package appeng.spatial;
import java.util.Locale;
import javax.annotation.Nullable;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraftforge.common.util.Constants;
/**
* A plot inside the storage cell world that is assigned to a specific storage
* cell.
*/
public class SpatialStoragePlot {
private static final String TAG_ID = "id";
private static final String TAG_SIZE = "size";
private static final String TAG_OWNER = "owner";
private static final String TAG_LAST_TRANSITION = "last_transition";
private static final int REGION_SIZE = 512;
public static final int MAX_SIZE = 128;
/**
* Id of the plot.
*/
private final int id;
/**
* The storage size of this dimension. This is dictated by the pylon structure
* size used to perform the first transfer into this dimension. Once it's set,
* it cannot be changed anymore.
*/
private final BlockPos size;
/**
* AE2 player id of who primarily owned the network when the storage plot was
* allocated.
*/
private final int owner;
/**
* Information about the last transition into this plot.
*/
@Nullable
private TransitionInfo lastTransition;
public SpatialStoragePlot(int id, BlockPos size, int owner) {
this.id = id;
this.size = size;
this.owner = owner;
if (size.getX() < 1 || size.getY() < 1 || size.getZ() < 1) {
throw new IllegalArgumentException("Plot size " + size + " is smaller than minimum size.");
}
if (size.getX() > MAX_SIZE || size.getY() > MAX_SIZE || size.getZ() > MAX_SIZE) {
throw new IllegalArgumentException("Plot size " + size + " exceeds maximum size of " + MAX_SIZE);
}
}
public int getId() {
return id;
}
/**
* The size in blocks of the plot in the storage dimension.
*
* @see #getOrigin()
*/
public BlockPos getSize() {
return size;
}
/**
* Returns the AE player id of the owner of this plot, or -1 if unknown.
*
* @see appeng.core.worlddata.IWorldPlayerData
*/
public int getOwner() {
return owner;
}
/**
* Returns information about the last transition into this plot (if any).
*/
@Nullable
public TransitionInfo getLastTransition() {
return lastTransition;
}
void setLastTransition(TransitionInfo info) {
this.lastTransition = info;
}
/**
* The origin of this plot within the spatial storage dimension.
* <p>
* To map an integer to a specific position, it uses the following algorithm.
*
* The 2 least significant bits determine the sign for the x and z axis. Every
* other pack of 2 bits locate the plot within a quadrant of a increasing area
* by the bit position.
*
* The first 2 bits after the sign address a quadrant within 1024x1024 blocks
* (or 4 region files)
*
* Every further will continue to double both x and z values. E.g. 2048x2048 for
* the 3rd pack and 4096x4096 for the 4th.
*
* @see #getSize()
*/
public BlockPos getOrigin() {
int signBits = id & 0b11;
int offsetBits = id >> 2;
int offsetScale = 1;
int posx = REGION_SIZE / 2;
int posz = REGION_SIZE / 2;
// find quadrant
while (offsetBits != 0) {
posx += REGION_SIZE * offsetScale * (offsetBits & 0b01);
posz += REGION_SIZE * offsetScale * (offsetBits >> 1 & 0b01);
offsetBits >>= 2;
offsetScale <<= 1;
}
// mirror in one of 4 directions
// First flip the z axis on every increment and then every two the x axis
if ((signBits & 0b01) != 0) {
posz *= -1;
}
if ((signBits & 0b10) != 0) {
posx *= -1;
}
// offset from cell center
//
// This is to ensure a 128x128x128 block plot (or 8x8 chunks) is centered within
// the region.
// This provides 12 chunks around it, so the default chunk loading radius on
// servers of 10 will prevent adjacent regions to be generated. As well as a 24
// chunks to the next plot.
posx -= 64;
posz -= 64;
return new BlockPos(posx, 64, posz);
}
/**
* Returns the filename of the region in the world save containing this plot.
*/
public String getRegionFilename() {
BlockPos origin = this.getOrigin();
ChunkPos originChunk = new ChunkPos(origin);
return String.format(Locale.ROOT, "r.%d.%d.mca", originChunk.getRegionCoordX(), originChunk.getRegionCoordZ());
}
public CompoundNBT toTag() {
CompoundNBT tag = new CompoundNBT();
tag.putInt(TAG_ID, id);
tag.put(TAG_SIZE, NBTUtil.writeBlockPos(size));
tag.putInt(TAG_OWNER, owner);
if (lastTransition != null) {
tag.put(TAG_LAST_TRANSITION, lastTransition.toTag());
}
return tag;
}
public static SpatialStoragePlot fromTag(CompoundNBT tag) {
int id = tag.getInt(TAG_ID);
BlockPos size = NBTUtil.readBlockPos(tag.getCompound(TAG_SIZE));
int ownerId = tag.getInt(TAG_OWNER);
SpatialStoragePlot plot = new SpatialStoragePlot(id, size, ownerId);
if (tag.contains(TAG_LAST_TRANSITION, Constants.NBT.TAG_COMPOUND)) {
plot.lastTransition = TransitionInfo.fromTag(tag.getCompound(TAG_LAST_TRANSITION));
}
return plot;
}
}

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