Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 53f7046f93 | |||
| 44544c598f | |||
| bcee2ec427 | |||
| 9e41d543f5 | |||
| 81cc0b804d | |||
| e1fc9908b3 | |||
| 9ed1535477 | |||
| 1ef345d4a4 | |||
| ff7d1b9719 | |||
| 88c9370e4f | |||
| f2aae3ef0f | |||
| 12739f1e66 | |||
| 81d2170ada | |||
| 0f469e9df6 | |||
| 0f34f09b32 | |||
| 8e54a8fed9 | |||
| d9bb81a00f | |||
| 39e038d916 |
+2
-2
@@ -11,13 +11,13 @@ artifact_basename=appliedenergistics2
|
|||||||
minecraft_release=1.16
|
minecraft_release=1.16
|
||||||
minecraft_version=1.16.2
|
minecraft_version=1.16.2
|
||||||
mcp_mappings=20200723-1.16.1
|
mcp_mappings=20200723-1.16.1
|
||||||
forge_version=33.0.22
|
forge_version=33.0.42
|
||||||
|
|
||||||
#########################################################
|
#########################################################
|
||||||
# Provided APIs #
|
# Provided APIs #
|
||||||
#########################################################
|
#########################################################
|
||||||
jei_minecraft_version=1.16.2
|
jei_minecraft_version=1.16.2
|
||||||
jei_version=7.1.1.15
|
jei_version=7.3.2.25
|
||||||
top_version=3.0.3-beta-6
|
top_version=3.0.3-beta-6
|
||||||
hwyla_version=1.10.8-B72_1.15.2
|
hwyla_version=1.10.8-B72_1.15.2
|
||||||
ctm_version=MC1.15.2-1.1.0.9
|
ctm_version=MC1.15.2-1.1.0.9
|
||||||
|
|||||||
@@ -23,13 +23,23 @@
|
|||||||
|
|
||||||
package appeng.api.implementations.items;
|
package appeng.api.implementations.items;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import javax.annotation.Nullable;
|
||||||
import net.minecraft.block.material.Material;
|
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public interface IGrowableCrystal {
|
public interface IGrowableCrystal {
|
||||||
|
|
||||||
ItemStack triggerGrowth(ItemStack is);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package appeng.client.render.cablebus;
|
package appeng.client.render.cablebus;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
@@ -51,12 +52,21 @@ import net.minecraftforge.client.model.data.IModelData;
|
|||||||
import appeng.api.parts.IPartModel;
|
import appeng.api.parts.IPartModel;
|
||||||
import appeng.api.util.AECableType;
|
import appeng.api.util.AECableType;
|
||||||
import appeng.api.util.AEColor;
|
import appeng.api.util.AEColor;
|
||||||
|
import appeng.metrics.Metrics;
|
||||||
|
|
||||||
public class CableBusBakedModel implements IBakedModel {
|
public class CableBusBakedModel implements IBakedModel {
|
||||||
|
|
||||||
// The number of quads overall that will be cached
|
// The number of quads overall that will be cached
|
||||||
private static final int CACHE_QUAD_COUNT = 5000;
|
private static final int CACHE_QUAD_COUNT = 5000;
|
||||||
|
|
||||||
|
// Keep a weak-ref to the last created cache for reporting metrics
|
||||||
|
private static WeakReference<LoadingCache<CableBusRenderState, List<BakedQuad>>> cacheForMetrics = new WeakReference<>(
|
||||||
|
null);
|
||||||
|
|
||||||
|
static {
|
||||||
|
Metrics.cache("cable_bus_model_cache", () -> cacheForMetrics.get());
|
||||||
|
}
|
||||||
|
|
||||||
private final LoadingCache<CableBusRenderState, List<BakedQuad>> cableModelCache;
|
private final LoadingCache<CableBusRenderState, List<BakedQuad>> cableModelCache;
|
||||||
|
|
||||||
private final CableBuilder cableBuilder;
|
private final CableBuilder cableBuilder;
|
||||||
@@ -74,6 +84,7 @@ public class CableBusBakedModel implements IBakedModel {
|
|||||||
this.partModels = partModels;
|
this.partModels = partModels;
|
||||||
this.particleTexture = particleTexture;
|
this.particleTexture = particleTexture;
|
||||||
this.cableModelCache = CacheBuilder.newBuilder()//
|
this.cableModelCache = CacheBuilder.newBuilder()//
|
||||||
|
.recordStats()//
|
||||||
.maximumWeight(CACHE_QUAD_COUNT)//
|
.maximumWeight(CACHE_QUAD_COUNT)//
|
||||||
.weigher((Weigher<CableBusRenderState, List<BakedQuad>>) (key, value) -> value.size())//
|
.weigher((Weigher<CableBusRenderState, List<BakedQuad>>) (key, value) -> value.size())//
|
||||||
.build(new CacheLoader<CableBusRenderState, List<BakedQuad>>() {
|
.build(new CacheLoader<CableBusRenderState, List<BakedQuad>>() {
|
||||||
@@ -84,6 +95,7 @@ public class CableBusBakedModel implements IBakedModel {
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
cacheForMetrics = new WeakReference<>(cableModelCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import java.util.Set;
|
|||||||
import java.util.function.DoubleSupplier;
|
import java.util.function.DoubleSupplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
@@ -393,6 +395,19 @@ public final class AEConfig {
|
|||||||
return this.meteoriteDimensionWhitelist;
|
return this.meteoriteDimensionWhitelist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getImprovedFluidTag() {
|
||||||
|
return Strings.emptyToNull(COMMON.improvedFluidTag.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getImprovedFluidMultiplier() {
|
||||||
|
return COMMON.improvedFluidMultiplier.get().floatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrometheusMetricsServerPort() {
|
||||||
|
return COMMON.prometheusMetricsServer.get();
|
||||||
|
}
|
||||||
|
|
||||||
// Setters keep visibility as low as possible.
|
// Setters keep visibility as low as possible.
|
||||||
|
|
||||||
private static class ClientConfig {
|
private static class ClientConfig {
|
||||||
@@ -463,6 +478,7 @@ public final class AEConfig {
|
|||||||
public final BooleanValue removeCrashingItemsOnLoad;
|
public final BooleanValue removeCrashingItemsOnLoad;
|
||||||
public final ConfigValue<Integer> formationPlaneEntityLimit;
|
public final ConfigValue<Integer> formationPlaneEntityLimit;
|
||||||
public final ConfigValue<Integer> craftingCalculationTimePerTick;
|
public final ConfigValue<Integer> craftingCalculationTimePerTick;
|
||||||
|
public final ConfigValue<Integer> prometheusMetricsServer;
|
||||||
|
|
||||||
// Spatial IO/Dimension
|
// Spatial IO/Dimension
|
||||||
public final ConfigValue<Double> spatialPowerExponent;
|
public final ConfigValue<Double> spatialPowerExponent;
|
||||||
@@ -507,6 +523,11 @@ public final class AEConfig {
|
|||||||
public final ConfigValue<Integer> condenserMatterBallsPower;
|
public final ConfigValue<Integer> condenserMatterBallsPower;
|
||||||
public final ConfigValue<Integer> condenserSingularityPower;
|
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>> tickRateMin = new HashMap<>();
|
||||||
public final Map<TickRates, ConfigValue<Integer>> tickRateMax = new HashMap<>();
|
public final Map<TickRates, ConfigValue<Integer>> tickRateMax = new HashMap<>();
|
||||||
|
|
||||||
@@ -542,6 +563,12 @@ public final class AEConfig {
|
|||||||
.define("removeCrashingItemsOnLoad", false);
|
.define("removeCrashingItemsOnLoad", false);
|
||||||
builder.pop();
|
builder.pop();
|
||||||
|
|
||||||
|
builder.push("metrics");
|
||||||
|
prometheusMetricsServer = builder.comment(
|
||||||
|
"Enables the Prometheus Metrics exporting endpoint on 127.0.0.1 on this port number. 0 to disable.")
|
||||||
|
.define("prometheusMetricsServer", 0);
|
||||||
|
builder.pop();
|
||||||
|
|
||||||
builder.push("automation");
|
builder.push("automation");
|
||||||
formationPlaneEntityLimit = builder.comment("TODO").define("formationPlaneEntityLimit", 128);
|
formationPlaneEntityLimit = builder.comment("TODO").define("formationPlaneEntityLimit", 128);
|
||||||
builder.pop();
|
builder.pop();
|
||||||
@@ -617,6 +644,17 @@ public final class AEConfig {
|
|||||||
tickRateMax.put(tickRate, builder.define(tickRate.name() + "Max", tickRate.getDefaultMax()));
|
tickRateMax.put(tickRate, builder.define(tickRate.name() + "Max", tickRate.getDefaultMax()));
|
||||||
}
|
}
|
||||||
builder.pop();
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import appeng.entity.TinyTNTPrimedEntity;
|
|||||||
import appeng.entity.TinyTNTPrimedRenderer;
|
import appeng.entity.TinyTNTPrimedRenderer;
|
||||||
import appeng.hooks.TickHandler;
|
import appeng.hooks.TickHandler;
|
||||||
import appeng.integration.Integrations;
|
import appeng.integration.Integrations;
|
||||||
|
import appeng.metrics.endpoint.PrometheusEndpoint;
|
||||||
import appeng.parts.PartPlacement;
|
import appeng.parts.PartPlacement;
|
||||||
import appeng.server.ServerHelper;
|
import appeng.server.ServerHelper;
|
||||||
|
|
||||||
@@ -90,6 +91,8 @@ public final class AppEng {
|
|||||||
}
|
}
|
||||||
INSTANCE = this;
|
INSTANCE = this;
|
||||||
|
|
||||||
|
startMetricsServer();
|
||||||
|
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, AEConfig.CLIENT_SPEC);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, AEConfig.CLIENT_SPEC);
|
||||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, AEConfig.COMMON_SPEC);
|
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, AEConfig.COMMON_SPEC);
|
||||||
|
|
||||||
@@ -148,6 +151,20 @@ public final class AppEng {
|
|||||||
registerNetworkHandler();
|
registerNetworkHandler();
|
||||||
|
|
||||||
AddonLoader.loadAddons(Api.INSTANCE);
|
AddonLoader.loadAddons(Api.INSTANCE);
|
||||||
|
|
||||||
|
startMetricsServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startMetricsServer() {
|
||||||
|
int prometheusPort = AEConfig.instance().getPrometheusMetricsServerPort();
|
||||||
|
if (prometheusPort != 0) {
|
||||||
|
try {
|
||||||
|
AELog.info("Starting Prometheus Metrics Server on Port %s", prometheusPort);
|
||||||
|
new PrometheusEndpoint("localhost", prometheusPort);
|
||||||
|
} catch (Exception e) {
|
||||||
|
AELog.warn("Failed to start Prometheus Metrics-Server: %s", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
import net.minecraft.advancements.CriteriaTriggers;
|
import net.minecraft.advancements.CriteriaTriggers;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.ScreenManager;
|
import net.minecraft.client.gui.ScreenManager;
|
||||||
import net.minecraft.client.particle.ParticleManager;
|
import net.minecraft.client.particle.ParticleManager;
|
||||||
@@ -33,17 +32,7 @@ import net.minecraft.item.crafting.IRecipeSerializer;
|
|||||||
import net.minecraft.particles.ParticleType;
|
import net.minecraft.particles.ParticleType;
|
||||||
import net.minecraft.tileentity.TileEntityType;
|
import net.minecraft.tileentity.TileEntityType;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraft.util.registry.Registry;
|
|
||||||
import net.minecraft.util.registry.WorldGenRegistries;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.biome.Biome;
|
|
||||||
import net.minecraft.world.gen.GenerationStage;
|
|
||||||
import net.minecraft.world.gen.feature.Feature;
|
|
||||||
import net.minecraft.world.gen.feature.IFeatureConfig;
|
|
||||||
import net.minecraft.world.gen.feature.OreFeatureConfig;
|
|
||||||
import net.minecraft.world.gen.feature.structure.Structure;
|
|
||||||
import net.minecraft.world.gen.placement.IPlacementConfig;
|
|
||||||
import net.minecraft.world.gen.placement.Placement;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.client.event.ColorHandlerEvent;
|
import net.minecraftforge.client.event.ColorHandlerEvent;
|
||||||
@@ -62,14 +51,12 @@ import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
|
|||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||||
import net.minecraftforge.fml.network.IContainerFactory;
|
import net.minecraftforge.fml.network.IContainerFactory;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
|
||||||
import net.minecraftforge.registries.IForgeRegistry;
|
import net.minecraftforge.registries.IForgeRegistry;
|
||||||
|
|
||||||
import appeng.api.config.Upgrades;
|
import appeng.api.config.Upgrades;
|
||||||
import appeng.api.definitions.IBlocks;
|
import appeng.api.definitions.IBlocks;
|
||||||
import appeng.api.definitions.IItems;
|
import appeng.api.definitions.IItems;
|
||||||
import appeng.api.definitions.IParts;
|
import appeng.api.definitions.IParts;
|
||||||
import appeng.api.features.AEFeature;
|
|
||||||
import appeng.api.features.IRegistryContainer;
|
import appeng.api.features.IRegistryContainer;
|
||||||
import appeng.api.features.IWirelessTermHandler;
|
import appeng.api.features.IWirelessTermHandler;
|
||||||
import appeng.api.features.IWorldGen;
|
import appeng.api.features.IWorldGen;
|
||||||
@@ -219,15 +206,8 @@ import appeng.recipes.game.FacadeRecipe;
|
|||||||
import appeng.recipes.handlers.GrinderRecipeSerializer;
|
import appeng.recipes.handlers.GrinderRecipeSerializer;
|
||||||
import appeng.recipes.handlers.InscriberRecipeSerializer;
|
import appeng.recipes.handlers.InscriberRecipeSerializer;
|
||||||
import appeng.server.AECommand;
|
import appeng.server.AECommand;
|
||||||
import appeng.spatial.SpatialStorageBiome;
|
|
||||||
import appeng.spatial.SpatialStorageChunkGenerator;
|
|
||||||
import appeng.spatial.SpatialStorageDimensionIds;
|
|
||||||
import appeng.tile.AEBaseTileEntity;
|
import appeng.tile.AEBaseTileEntity;
|
||||||
import appeng.tile.crafting.MolecularAssemblerRenderer;
|
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 {
|
final class Registration {
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ package appeng.core.api.definitions;
|
|||||||
|
|
||||||
import static appeng.block.AEBaseBlock.defaultProps;
|
import static appeng.block.AEBaseBlock.defaultProps;
|
||||||
|
|
||||||
|
import net.minecraft.block.AbstractBlock;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.block.DispenserBlock;
|
import net.minecraft.block.DispenserBlock;
|
||||||
import net.minecraft.block.SlabBlock;
|
import net.minecraft.block.SlabBlock;
|
||||||
import net.minecraft.block.SoundType;
|
import net.minecraft.block.SoundType;
|
||||||
@@ -28,6 +30,7 @@ import net.minecraft.block.StairsBlock;
|
|||||||
import net.minecraft.block.material.Material;
|
import net.minecraft.block.material.Material;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.entity.EntityClassification;
|
import net.minecraft.entity.EntityClassification;
|
||||||
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.common.ToolType;
|
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))
|
this.chiseledQuartzBlock = deco.block("chiseled_quartz_block", () -> new AEDecorativeBlock(QUARTZ_PROPERTIES))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS)
|
AbstractBlock.IExtendedPositionPredicate<EntityType<?>> neverAllowSpawn = (p1, p2, p3, p4) -> false;
|
||||||
.block("quartz_glass", () -> new QuartzGlassBlock(defaultProps(Material.GLASS).notSolid()))
|
this.quartzGlass = registry.features(AEFeature.QUARTZ_GLASS).block("quartz_glass", () -> {
|
||||||
.rendering(new BlockRenderingCustomizer() {
|
return new QuartzGlassBlock(defaultProps(Material.GLASS).notSolid().setAllowsSpawn(neverAllowSpawn));
|
||||||
@Override
|
}).rendering(new BlockRenderingCustomizer() {
|
||||||
@OnlyIn(Dist.CLIENT)
|
@Override
|
||||||
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
|
@OnlyIn(Dist.CLIENT)
|
||||||
rendering.renderType(RenderType.getCutout());
|
public void customize(IBlockRendering rendering, IItemRendering itemRendering) {
|
||||||
}
|
rendering.renderType(RenderType.getCutout());
|
||||||
}).build();
|
}
|
||||||
|
}).build();
|
||||||
this.quartzVibrantGlass = deco
|
this.quartzVibrantGlass = deco
|
||||||
.block("quartz_vibrant_glass",
|
.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)
|
.addFeatures(AEFeature.DECORATIVE_LIGHTS, AEFeature.QUARTZ_GLASS)
|
||||||
.rendering(new BlockRenderingCustomizer() {
|
.rendering(new BlockRenderingCustomizer() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ public final class ApiItems implements IItems {
|
|||||||
|
|
||||||
GrowingCrystalEntity.TYPE = registry
|
GrowingCrystalEntity.TYPE = registry
|
||||||
.<GrowingCrystalEntity>entity("growing_crystal", GrowingCrystalEntity::new, EntityClassification.MISC)
|
.<GrowingCrystalEntity>entity("growing_crystal", GrowingCrystalEntity::new, EntityClassification.MISC)
|
||||||
.customize(builder -> builder.size(0.25F, 0.25F)).build();
|
.customize(builder -> builder.size(0.25F, 0.4F)).build();
|
||||||
|
|
||||||
// rv1
|
// rv1
|
||||||
this.encodedPattern = registry.item("encoded_pattern", EncodedPatternItem::new)
|
this.encodedPattern = registry.item("encoded_pattern", EncodedPatternItem::new)
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
|
|
||||||
import appeng.api.events.LocatableEventAnnounce;
|
import appeng.api.events.LocatableEventAnnounce;
|
||||||
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
|
import appeng.api.events.LocatableEventAnnounce.LocatableEvent;
|
||||||
import appeng.api.features.ILocatable;
|
import appeng.api.features.ILocatable;
|
||||||
import appeng.api.features.ILocatableRegistry;
|
import appeng.api.features.ILocatableRegistry;
|
||||||
|
import appeng.core.AELog;
|
||||||
import appeng.util.Platform;
|
import appeng.util.Platform;
|
||||||
|
|
||||||
public final class LocatableRegistry implements ILocatableRegistry {
|
public final class LocatableRegistry implements ILocatableRegistry {
|
||||||
@@ -35,18 +35,19 @@ public final class LocatableRegistry implements ILocatableRegistry {
|
|||||||
|
|
||||||
public LocatableRegistry() {
|
public LocatableRegistry() {
|
||||||
this.set = new HashMap<>();
|
this.set = new HashMap<>();
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
MinecraftForge.EVENT_BUS.addListener(this::updateLocatable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
private void updateLocatable(final LocatableEventAnnounce e) {
|
||||||
public void updateLocatable(final LocatableEventAnnounce e) {
|
|
||||||
if (Platform.isClient()) {
|
if (Platform.isClient()) {
|
||||||
return; // IGNORE!
|
return; // IGNORE!
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.change == LocatableEvent.REGISTER) {
|
if (e.change == LocatableEvent.REGISTER) {
|
||||||
|
AELog.debug("Registering locatable %s: %s", e.target.getLocatableSerial(), e.target);
|
||||||
this.set.put(e.target.getLocatableSerial(), e.target);
|
this.set.put(e.target.getLocatableSerial(), e.target);
|
||||||
} else if (e.change == LocatableEvent.UNREGISTER) {
|
} else if (e.change == LocatableEvent.UNREGISTER) {
|
||||||
|
AELog.debug("Unregistering locatable %s: %s", e.target.getLocatableSerial(), e.target);
|
||||||
this.set.remove(e.target.getLocatableSerial());
|
this.set.remove(e.target.getLocatableSerial());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,15 +18,27 @@
|
|||||||
|
|
||||||
package appeng.core.sync.packets;
|
package appeng.core.sync.packets;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||||
import net.minecraft.inventory.container.Container;
|
import net.minecraft.inventory.container.Container;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.item.crafting.IRecipe;
|
||||||
import net.minecraft.nbt.ListNBT;
|
import net.minecraft.item.crafting.IRecipeSerializer;
|
||||||
|
import net.minecraft.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.item.crafting.ShapedRecipe;
|
||||||
import net.minecraft.network.PacketBuffer;
|
import net.minecraft.network.PacketBuffer;
|
||||||
|
import net.minecraft.util.NonNullList;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraftforge.common.crafting.IShapedRecipe;
|
||||||
import net.minecraftforge.items.IItemHandler;
|
import net.minecraftforge.items.IItemHandler;
|
||||||
|
|
||||||
import appeng.api.config.Actionable;
|
import appeng.api.config.Actionable;
|
||||||
@@ -40,8 +52,10 @@ import appeng.api.networking.storage.IStorageGrid;
|
|||||||
import appeng.api.storage.IMEMonitor;
|
import appeng.api.storage.IMEMonitor;
|
||||||
import appeng.api.storage.channels.IItemStorageChannel;
|
import appeng.api.storage.channels.IItemStorageChannel;
|
||||||
import appeng.api.storage.data.IAEItemStack;
|
import appeng.api.storage.data.IAEItemStack;
|
||||||
|
import appeng.container.implementations.PatternTermContainer;
|
||||||
import appeng.core.Api;
|
import appeng.core.Api;
|
||||||
import appeng.core.sync.BasePacket;
|
import appeng.core.sync.BasePacket;
|
||||||
|
import appeng.core.sync.BasePacketHandler;
|
||||||
import appeng.core.sync.network.INetworkInfo;
|
import appeng.core.sync.network.INetworkInfo;
|
||||||
import appeng.helpers.IContainerCraftingPacket;
|
import appeng.helpers.IContainerCraftingPacket;
|
||||||
import appeng.items.storage.ViewCellItem;
|
import appeng.items.storage.ViewCellItem;
|
||||||
@@ -54,152 +68,289 @@ import appeng.util.prioritylist.IPartitionList;
|
|||||||
|
|
||||||
public class JEIRecipePacket extends BasePacket {
|
public class JEIRecipePacket extends BasePacket {
|
||||||
|
|
||||||
private ItemStack[][] recipe;
|
/**
|
||||||
|
* Transmit only a recipe ID.
|
||||||
|
*/
|
||||||
|
private static final int INLINE_RECIPE_NONE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmit the information about the recipe we actually need. This is
|
||||||
|
* explicitly limited since this is untrusted client->server info.
|
||||||
|
*/
|
||||||
|
private static final int INLINE_RECIPE_SHAPED = 2;
|
||||||
|
|
||||||
|
private ResourceLocation recipeId;
|
||||||
|
/**
|
||||||
|
* This is optional, in case the client already knows it could not resolve the
|
||||||
|
* recipe id.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private IRecipe<?> recipe;
|
||||||
|
private boolean crafting;
|
||||||
|
|
||||||
public JEIRecipePacket(final PacketBuffer stream) {
|
public JEIRecipePacket(final PacketBuffer stream) {
|
||||||
final CompoundNBT comp = stream.readCompoundTag();
|
this.crafting = stream.readBoolean();
|
||||||
if (comp != null) {
|
final String id = stream.readString(Short.MAX_VALUE);
|
||||||
this.recipe = new ItemStack[9][];
|
this.recipeId = new ResourceLocation(id);
|
||||||
for (int x = 0; x < this.recipe.length; x++) {
|
|
||||||
final ListNBT list = comp.getList("#" + x, 10);
|
|
||||||
if (list.size() > 0) {
|
|
||||||
this.recipe[x] = new ItemStack[list.size()];
|
|
||||||
for (int y = 0; y < list.size(); y++) {
|
|
||||||
this.recipe[x][y] = ItemStack.read(list.getCompound(y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// api
|
int inlineRecipeType = stream.readVarInt();
|
||||||
public JEIRecipePacket(final CompoundNBT recipe) {
|
switch (inlineRecipeType) {
|
||||||
final PacketBuffer data = new PacketBuffer(Unpooled.buffer());
|
case INLINE_RECIPE_NONE:
|
||||||
|
break;
|
||||||
data.writeInt(this.getPacketID());
|
case INLINE_RECIPE_SHAPED:
|
||||||
|
recipe = IRecipeSerializer.CRAFTING_SHAPED.read(this.recipeId, stream);
|
||||||
data.writeCompoundTag(recipe);
|
break;
|
||||||
|
default:
|
||||||
this.configureWrite(data);
|
throw new IllegalArgumentException("Invalid inline recipe type.");
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serverPacketData(final INetworkInfo manager, final PlayerEntity player) {
|
|
||||||
final ServerPlayerEntity pmp = (ServerPlayerEntity) player;
|
|
||||||
final Container con = pmp.openContainer;
|
|
||||||
|
|
||||||
if (!(con instanceof IContainerCraftingPacket)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
|
|
||||||
final IGridNode node = cct.getNetworkNode();
|
|
||||||
|
|
||||||
if (node == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final IGrid grid = node.getGrid();
|
|
||||||
if (grid == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final IStorageGrid inv = grid.getCache(IStorageGrid.class);
|
|
||||||
final IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
|
|
||||||
final ISecurityGrid security = grid.getCache(ISecurityGrid.class);
|
|
||||||
final ICraftingGrid crafting = grid.getCache(ICraftingGrid.class);
|
|
||||||
final IItemHandler craftMatrix = cct.getInventoryByName("crafting");
|
|
||||||
final IItemHandler playerInventory = cct.getInventoryByName("player");
|
|
||||||
|
|
||||||
if (inv != null && this.recipe != null && security != null) {
|
|
||||||
final IMEMonitor<IAEItemStack> storage = inv
|
|
||||||
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class));
|
|
||||||
final IPartitionList<IAEItemStack> filter = ViewCellItem.createFilter(cct.getViewCells());
|
|
||||||
|
|
||||||
for (int x = 0; x < craftMatrix.getSlots(); x++) {
|
|
||||||
ItemStack currentItem = craftMatrix.getStackInSlot(x);
|
|
||||||
|
|
||||||
// prepare slots
|
|
||||||
if (!currentItem.isEmpty()) {
|
|
||||||
// already the correct item?
|
|
||||||
ItemStack newItem = this.canUseInSlot(x, currentItem);
|
|
||||||
|
|
||||||
// put away old item
|
|
||||||
if (newItem != currentItem && security.hasPermission(player, SecurityPermissions.INJECT)) {
|
|
||||||
final IAEItemStack in = AEItemStack.fromItemStack(currentItem);
|
|
||||||
final IAEItemStack out = cct.useRealItems()
|
|
||||||
? Platform.poweredInsert(energy, storage, in, cct.getActionSource())
|
|
||||||
: null;
|
|
||||||
if (out != null) {
|
|
||||||
currentItem = out.createItemStack();
|
|
||||||
} else {
|
|
||||||
currentItem = ItemStack.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentItem.isEmpty() && this.recipe[x] != null) {
|
|
||||||
// for each variant
|
|
||||||
for (int y = 0; y < this.recipe[x].length && currentItem.isEmpty(); y++) {
|
|
||||||
final IAEItemStack request = AEItemStack.fromItemStack(this.recipe[x][y]);
|
|
||||||
if (request != null) {
|
|
||||||
// try ae
|
|
||||||
if ((filter == null || filter.isListed(request))
|
|
||||||
&& security.hasPermission(player, SecurityPermissions.EXTRACT)) {
|
|
||||||
request.setStackSize(1);
|
|
||||||
IAEItemStack out;
|
|
||||||
|
|
||||||
if (cct.useRealItems()) {
|
|
||||||
out = Platform.poweredExtraction(energy, storage, request, cct.getActionSource());
|
|
||||||
} else {
|
|
||||||
// Query the crafting grid if there is a pattern providing the item
|
|
||||||
if (!crafting.getCraftingFor(request, null, 0, null).isEmpty()) {
|
|
||||||
out = request;
|
|
||||||
} else {
|
|
||||||
// Fall back using an existing item
|
|
||||||
out = storage.extractItems(request, Actionable.SIMULATE, cct.getActionSource());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out != null) {
|
|
||||||
currentItem = out.createItemStack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try inventory
|
|
||||||
if (currentItem.isEmpty()) {
|
|
||||||
AdaptorItemHandler ad = new AdaptorItemHandler(playerInventory);
|
|
||||||
|
|
||||||
if (cct.useRealItems()) {
|
|
||||||
currentItem = ad.removeItems(1, this.recipe[x][y], null);
|
|
||||||
} else {
|
|
||||||
currentItem = ad.simulateRemove(1, this.recipe[x][y], null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ItemHandlerUtil.setStackInSlot(craftMatrix, x, currentItem);
|
|
||||||
}
|
|
||||||
con.onCraftMatrixChanged(new WrapperInvItemHandler(craftMatrix));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Sends a recipe identified by the given recipe ID to the server for either
|
||||||
* @param slot
|
* filling a crafting grid or a pattern.
|
||||||
* @param is itemstack
|
|
||||||
* @return is if it can be used, else EMPTY
|
|
||||||
*/
|
*/
|
||||||
private ItemStack canUseInSlot(int slot, ItemStack is) {
|
public JEIRecipePacket(final ResourceLocation recipeId, final boolean crafting) {
|
||||||
if (this.recipe[slot] != null) {
|
PacketBuffer data = createCommonHeader(recipeId, crafting, INLINE_RECIPE_NONE);
|
||||||
for (ItemStack option : this.recipe[slot]) {
|
this.configureWrite(data);
|
||||||
if (is.isItemEqual(option)) {
|
}
|
||||||
return is;
|
|
||||||
|
/**
|
||||||
|
* Sends a recipe to the server for either filling a crafting grid or a pattern.
|
||||||
|
* <p>
|
||||||
|
* Prefer the id-based constructor above whereever possible.
|
||||||
|
*/
|
||||||
|
public JEIRecipePacket(final ShapedRecipe recipe, final boolean crafting) {
|
||||||
|
PacketBuffer data = createCommonHeader(recipe.getId(), crafting, INLINE_RECIPE_SHAPED);
|
||||||
|
IRecipeSerializer.CRAFTING_SHAPED.write(data, recipe);
|
||||||
|
this.configureWrite(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PacketBuffer createCommonHeader(ResourceLocation recipeId, boolean crafting, int inlineRecipeType) {
|
||||||
|
final PacketBuffer data = new PacketBuffer(Unpooled.buffer());
|
||||||
|
|
||||||
|
data.writeInt(this.getPacketID());
|
||||||
|
data.writeBoolean(crafting);
|
||||||
|
data.writeResourceLocation(recipeId);
|
||||||
|
data.writeVarInt(inlineRecipeType);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Servside handler for this packet.
|
||||||
|
* <p>
|
||||||
|
* Makes use of {@link Preconditions#checkArgument(boolean)} as the
|
||||||
|
* {@link BasePacketHandler} is catching them and in general these cases should
|
||||||
|
* never happen except in an error case and should be logged then.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void serverPacketData(final INetworkInfo manager, final PlayerEntity player) {
|
||||||
|
// Setup and verification
|
||||||
|
final ServerPlayerEntity pmp = (ServerPlayerEntity) player;
|
||||||
|
final Container con = pmp.openContainer;
|
||||||
|
Preconditions.checkArgument(con instanceof IContainerCraftingPacket);
|
||||||
|
|
||||||
|
IRecipe<?> recipe = player.getEntityWorld().getRecipeManager().getRecipe(this.recipeId).orElse(null);
|
||||||
|
if (recipe == null && this.recipe != null) {
|
||||||
|
// Certain recipes (i.e. AE2 facades) are represented in JEI as ShapedRecipe's,
|
||||||
|
// while in reality they
|
||||||
|
// are special recipes. Those recipes are sent across the wire...
|
||||||
|
recipe = this.recipe;
|
||||||
|
}
|
||||||
|
Preconditions.checkArgument(recipe != null);
|
||||||
|
|
||||||
|
final IContainerCraftingPacket cct = (IContainerCraftingPacket) con;
|
||||||
|
final IGridNode node = cct.getNetworkNode();
|
||||||
|
|
||||||
|
Preconditions.checkArgument(node != null);
|
||||||
|
|
||||||
|
final IGrid grid = node.getGrid();
|
||||||
|
Preconditions.checkArgument(grid != null);
|
||||||
|
|
||||||
|
final IStorageGrid inv = grid.getCache(IStorageGrid.class);
|
||||||
|
Preconditions.checkArgument(inv != null);
|
||||||
|
|
||||||
|
final ISecurityGrid security = grid.getCache(ISecurityGrid.class);
|
||||||
|
Preconditions.checkArgument(security != null);
|
||||||
|
|
||||||
|
final IEnergyGrid energy = grid.getCache(IEnergyGrid.class);
|
||||||
|
final ICraftingGrid crafting = grid.getCache(ICraftingGrid.class);
|
||||||
|
final IItemHandler craftMatrix = cct.getInventoryByName("crafting");
|
||||||
|
final IItemHandler playerInventory = cct.getInventoryByName("player");
|
||||||
|
|
||||||
|
final IMEMonitor<IAEItemStack> storage = inv
|
||||||
|
.getInventory(Api.instance().storage().getStorageChannel(IItemStorageChannel.class));
|
||||||
|
final IPartitionList<IAEItemStack> filter = ViewCellItem.createFilter(cct.getViewCells());
|
||||||
|
final NonNullList<Ingredient> ingredients = this.ensure3by3CraftingMatrix(recipe);
|
||||||
|
|
||||||
|
// Handle each slot
|
||||||
|
for (int x = 0; x < craftMatrix.getSlots(); x++) {
|
||||||
|
ItemStack currentItem = craftMatrix.getStackInSlot(x);
|
||||||
|
Ingredient ingredient = ingredients.get(x);
|
||||||
|
|
||||||
|
// prepare slots
|
||||||
|
if (!currentItem.isEmpty()) {
|
||||||
|
// already the correct item? True, skip everything else
|
||||||
|
ItemStack newItem = this.canUseInSlot(ingredient, currentItem);
|
||||||
|
|
||||||
|
// put away old item, if not correct
|
||||||
|
if (newItem != currentItem && security.hasPermission(player, SecurityPermissions.INJECT)) {
|
||||||
|
final IAEItemStack in = AEItemStack.fromItemStack(currentItem);
|
||||||
|
final IAEItemStack out = cct.useRealItems()
|
||||||
|
? Platform.poweredInsert(energy, storage, in, cct.getActionSource())
|
||||||
|
: null;
|
||||||
|
if (out != null) {
|
||||||
|
currentItem = out.createItemStack();
|
||||||
|
} else {
|
||||||
|
currentItem = ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find item or pattern from the network
|
||||||
|
if (currentItem.isEmpty() && security.hasPermission(player, SecurityPermissions.EXTRACT)) {
|
||||||
|
IAEItemStack out;
|
||||||
|
|
||||||
|
if (cct.useRealItems()) {
|
||||||
|
IAEItemStack request = findBestMatchingItemStack(ingredient, filter, storage, cct);
|
||||||
|
out = request != null
|
||||||
|
? Platform.poweredExtraction(energy, storage, request.setStackSize(1),
|
||||||
|
cct.getActionSource())
|
||||||
|
: null;
|
||||||
|
} else {
|
||||||
|
out = findBestMatchingPattern(ingredient, filter, crafting, storage, cct);
|
||||||
|
if (out == null) {
|
||||||
|
out = findBestMatchingItemStack(ingredient, filter, storage, cct);
|
||||||
|
}
|
||||||
|
if (out == null && ingredient.getMatchingStacks().length > 0) {
|
||||||
|
out = AEItemStack.fromItemStack(ingredient.getMatchingStacks()[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out != null) {
|
||||||
|
currentItem = out.createItemStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If still nothing, search the player inventory.
|
||||||
|
if (currentItem.isEmpty()) {
|
||||||
|
ItemStack[] matchingStacks = ingredient.getMatchingStacks();
|
||||||
|
for (ItemStack matchingStack : matchingStacks) {
|
||||||
|
if (currentItem.isEmpty()) {
|
||||||
|
AdaptorItemHandler ad = new AdaptorItemHandler(playerInventory);
|
||||||
|
|
||||||
|
if (cct.useRealItems()) {
|
||||||
|
currentItem = ad.removeItems(1, matchingStack, null);
|
||||||
|
} else {
|
||||||
|
currentItem = ad.simulateRemove(1, matchingStack, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemHandlerUtil.setStackInSlot(craftMatrix, x, currentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.crafting) {
|
||||||
|
this.handleProcessing(con, cct, recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
con.onCraftMatrixChanged(new WrapperInvItemHandler(craftMatrix));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand any recipe to a 3x3 matrix.
|
||||||
|
* <p>
|
||||||
|
* Will throw an {@link IllegalArgumentException} in case it has more than 9 or
|
||||||
|
* a shaped recipe is either wider or higher than 3. ingredients.
|
||||||
|
*/
|
||||||
|
private NonNullList<Ingredient> ensure3by3CraftingMatrix(IRecipe<?> recipe) {
|
||||||
|
NonNullList<Ingredient> ingredients = recipe.getIngredients();
|
||||||
|
NonNullList<Ingredient> expandedIngredients = NonNullList.withSize(9, Ingredient.EMPTY);
|
||||||
|
|
||||||
|
Preconditions.checkArgument(ingredients.size() <= 9);
|
||||||
|
|
||||||
|
// shaped recipes can be smaller than 3x3, expand to 3x3 to match the crafting
|
||||||
|
// matrix
|
||||||
|
if (recipe instanceof IShapedRecipe) {
|
||||||
|
IShapedRecipe<?> shapedRecipe = (IShapedRecipe<?>) recipe;
|
||||||
|
int width = shapedRecipe.getRecipeWidth();
|
||||||
|
int height = shapedRecipe.getRecipeHeight();
|
||||||
|
Preconditions.checkArgument(width <= 3 && height <= 3);
|
||||||
|
|
||||||
|
for (int h = 0; h < height; h++) {
|
||||||
|
for (int w = 0; w < width; w++) {
|
||||||
|
int source = w + h * width;
|
||||||
|
int target = w + h * 3;
|
||||||
|
Ingredient i = ingredients.get(source);
|
||||||
|
expandedIngredients.set(target, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ItemStack.EMPTY;
|
// Anything else should be a flat list
|
||||||
|
else {
|
||||||
|
for (int i = 0; i < ingredients.size(); i++) {
|
||||||
|
expandedIngredients.set(i, ingredients.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expandedIngredients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param is itemstack
|
||||||
|
* @return is if it can be used, else EMPTY
|
||||||
|
*/
|
||||||
|
private ItemStack canUseInSlot(Ingredient ingredient, ItemStack is) {
|
||||||
|
return Arrays.stream(ingredient.getMatchingStacks()).filter(p -> p.isItemEqual(is)).findFirst()
|
||||||
|
.orElse(ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first matching itemstack with the highest stored amount.
|
||||||
|
*/
|
||||||
|
private IAEItemStack findBestMatchingItemStack(Ingredient ingredients, IPartitionList<IAEItemStack> filter,
|
||||||
|
IMEMonitor<IAEItemStack> storage, IContainerCraftingPacket cct) {
|
||||||
|
return Arrays.stream(ingredients.getMatchingStacks()).map(AEItemStack::fromItemStack) //
|
||||||
|
.filter(r -> r != null && (filter == null || filter.isListed(r))) //
|
||||||
|
.map(s -> {
|
||||||
|
// Determine the stored count
|
||||||
|
IAEItemStack stored = storage.extractItems(s.copy().setStackSize(Long.MAX_VALUE),
|
||||||
|
Actionable.SIMULATE, cct.getActionSource());
|
||||||
|
return Pair.of(s, stored != null ? stored.getStackSize() : 0);
|
||||||
|
}).min((left, right) -> Long.compare(right.getSecond(), left.getSecond()))//
|
||||||
|
.map(Pair::getFirst).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This tries to find the first pattern matching the list of ingredients.
|
||||||
|
* <p>
|
||||||
|
* As additional condition, it sorts by the stored amount to return the one with
|
||||||
|
* the highest stored amount.
|
||||||
|
*/
|
||||||
|
private IAEItemStack findBestMatchingPattern(Ingredient ingredients, IPartitionList<IAEItemStack> filter,
|
||||||
|
ICraftingGrid crafting, IMEMonitor<IAEItemStack> storage, IContainerCraftingPacket cct) {
|
||||||
|
return Arrays.stream(ingredients.getMatchingStacks()).map(AEItemStack::fromItemStack)
|
||||||
|
.filter(r -> r != null && (filter == null || filter.isListed(r)))
|
||||||
|
.map(s -> s.setCraftable(!crafting.getCraftingFor(s, null, 0, null).isEmpty()))
|
||||||
|
.filter(IAEItemStack::isCraftable).map(s -> {
|
||||||
|
final IAEItemStack stored = storage.extractItems(s, Actionable.SIMULATE, cct.getActionSource());
|
||||||
|
return s.setStackSize(stored != null ? stored.getStackSize() : 0);
|
||||||
|
}).min((left, right) -> {
|
||||||
|
final int craftable = Boolean.compare(left.isCraftable(), right.isCraftable());
|
||||||
|
return craftable != 0 ? craftable : Long.compare(right.getStackSize(), left.getStackSize());
|
||||||
|
}).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleProcessing(Container con, IContainerCraftingPacket cct, IRecipe<?> recipe) {
|
||||||
|
if (con instanceof PatternTermContainer) {
|
||||||
|
PatternTermContainer patternTerm = (PatternTermContainer) con;
|
||||||
|
if (!patternTerm.craftingMode) {
|
||||||
|
final IItemHandler output = cct.getInventoryByName("output");
|
||||||
|
ItemHandlerUtil.setStackInSlot(output, 0, recipe.getRecipeOutput());
|
||||||
|
ItemHandlerUtil.setStackInSlot(output, 1, ItemStack.EMPTY);
|
||||||
|
ItemHandlerUtil.setStackInSlot(output, 2, ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,9 @@
|
|||||||
package appeng.core.sync.packets;
|
package appeng.core.sync.packets;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.BufferOverflowException;
|
import java.nio.BufferOverflowException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|||||||
@@ -157,4 +157,8 @@ final class StorageData extends WorldSavedData implements IWorldGridStorageData
|
|||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return storage.size();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,13 @@ import com.google.common.base.Preconditions;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
|
|
||||||
|
import appeng.metrics.Metrics;
|
||||||
import appeng.services.CompassService;
|
import appeng.services.CompassService;
|
||||||
import appeng.services.compass.CompassThreadFactory;
|
import appeng.services.compass.CompassThreadFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton access to anything related to world-based data.
|
* Singleton access to anything related to world-based data.
|
||||||
*
|
* <p>
|
||||||
* Data will change depending which world is loaded. Will probably not affect
|
* Data will change depending which world is loaded. Will probably not affect
|
||||||
* SMP at all since only one world is loaded, but SSP more, cause they play on
|
* SMP at all since only one world is loaded, but SSP more, cause they play on
|
||||||
* different worlds.
|
* different worlds.
|
||||||
@@ -53,8 +54,15 @@ public final class WorldData implements IWorldData {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static MinecraftServer server;
|
private static MinecraftServer server;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Metrics.gauge("grid_storage_count", () -> {
|
||||||
|
WorldData worldData = (WorldData) instance;
|
||||||
|
return worldData != null ? worldData.storageData.size() : 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private final IWorldPlayerData playerData;
|
private final IWorldPlayerData playerData;
|
||||||
private final IWorldGridStorageData storageData;
|
private final StorageData storageData;
|
||||||
private final IWorldCompassData compassData;
|
private final IWorldCompassData compassData;
|
||||||
|
|
||||||
private WorldData(@Nonnull final ServerWorld overworld) {
|
private WorldData(@Nonnull final ServerWorld overworld) {
|
||||||
@@ -80,7 +88,6 @@ public final class WorldData implements IWorldData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ae2 data related to a specific world
|
* @return ae2 data related to a specific world
|
||||||
*
|
|
||||||
* @deprecated do not use singletons which are dependent on specific world state
|
* @deprecated do not use singletons which are dependent on specific world state
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -102,7 +109,7 @@ public final class WorldData implements IWorldData {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires to start up from external from here
|
* Requires to start up from external from here
|
||||||
*
|
* <p>
|
||||||
* drawback of the singleton build style
|
* drawback of the singleton build style
|
||||||
*/
|
*/
|
||||||
public static void onServerStarting(MinecraftServer server) {
|
public static void onServerStarting(MinecraftServer server) {
|
||||||
|
|||||||
@@ -20,11 +20,8 @@ package appeng.decorative.solid;
|
|||||||
|
|
||||||
import net.minecraft.block.AbstractGlassBlock;
|
import net.minecraft.block.AbstractGlassBlock;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.material.Material;
|
|
||||||
import net.minecraft.util.Direction;
|
import net.minecraft.util.Direction;
|
||||||
|
|
||||||
import appeng.helpers.AEMaterials;
|
|
||||||
|
|
||||||
public class QuartzGlassBlock extends AbstractGlassBlock {
|
public class QuartzGlassBlock extends AbstractGlassBlock {
|
||||||
|
|
||||||
public QuartzGlassBlock(Properties props) {
|
public QuartzGlassBlock(Properties props) {
|
||||||
@@ -33,8 +30,7 @@ public class QuartzGlassBlock extends AbstractGlassBlock {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side) {
|
public boolean isSideInvisible(BlockState state, BlockState adjacentBlockState, Direction side) {
|
||||||
final Material mat = adjacentBlockState.getMaterial();
|
if (adjacentBlockState.getBlock() instanceof QuartzGlassBlock) {
|
||||||
if (mat == Material.GLASS || mat == AEMaterials.GLASS) {
|
|
||||||
if (adjacentBlockState.getRenderType() == state.getRenderType()) {
|
if (adjacentBlockState.getRenderType() == state.getRenderType()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import net.minecraft.world.server.ServerWorld;
|
|||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.common.ToolType;
|
import net.minecraftforge.common.ToolType;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
|
|
||||||
import appeng.block.AEBaseBlock;
|
import appeng.block.AEBaseBlock;
|
||||||
import appeng.core.worlddata.WorldData;
|
import appeng.core.worlddata.WorldData;
|
||||||
@@ -43,11 +42,10 @@ public class SkyStoneBlock extends AEBaseBlock {
|
|||||||
super(props);
|
super(props);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
MinecraftForge.EVENT_BUS.register(this);
|
MinecraftForge.EVENT_BUS.addListener(this::breakFaster);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
private void breakFaster(final PlayerEvent.BreakSpeed event) {
|
||||||
public void breakFaster(final PlayerEvent.BreakSpeed event) {
|
|
||||||
if (event.getState().getBlock() == this && event.getPlayer() != null) {
|
if (event.getState().getBlock() == this && event.getPlayer() != null) {
|
||||||
final ItemStack is = event.getPlayer().getItemStackFromSlot(EquipmentSlotType.MAINHAND);
|
final ItemStack is = event.getPlayer().getItemStackFromSlot(EquipmentSlotType.MAINHAND);
|
||||||
int level = -1;
|
int level = -1;
|
||||||
|
|||||||
@@ -19,13 +19,14 @@
|
|||||||
package appeng.entity;
|
package appeng.entity;
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.material.Material;
|
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
import net.minecraft.util.Direction;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
import appeng.api.features.AEFeature;
|
import appeng.api.features.AEFeature;
|
||||||
@@ -35,12 +36,27 @@ import appeng.client.EffectType;
|
|||||||
import appeng.core.AEConfig;
|
import appeng.core.AEConfig;
|
||||||
import appeng.core.AppEng;
|
import appeng.core.AppEng;
|
||||||
import appeng.items.misc.CrystalSeedItem;
|
import appeng.items.misc.CrystalSeedItem;
|
||||||
import appeng.util.Platform;
|
|
||||||
|
|
||||||
public final class GrowingCrystalEntity extends AEBaseItemEntity {
|
public final class GrowingCrystalEntity extends AEBaseItemEntity {
|
||||||
|
|
||||||
public static EntityType<GrowingCrystalEntity> TYPE;
|
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;
|
private int progress_1000 = 0;
|
||||||
|
|
||||||
public GrowingCrystalEntity(EntityType<? extends GrowingCrystalEntity> type, World world) {
|
public GrowingCrystalEntity(EntityType<? extends GrowingCrystalEntity> type, World world) {
|
||||||
@@ -56,126 +72,140 @@ public final class GrowingCrystalEntity extends AEBaseItemEntity {
|
|||||||
public void tick() {
|
public void tick() {
|
||||||
super.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)) {
|
if (!AEConfig.instance().isFeatureEnabled(AEFeature.IN_WORLD_PURIFICATION)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ItemStack is = this.getItem();
|
final int x = MathHelper.floor(this.getPosX());
|
||||||
final Item gc = is.getItem();
|
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...
|
BlockPos pos = new BlockPos(x, y, z);
|
||||||
{
|
final BlockState state = this.world.getBlockState(pos);
|
||||||
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());
|
|
||||||
|
|
||||||
final BlockState state = this.world.getBlockState(new BlockPos(j, i, k));
|
final float multiplier = cry.getMultiplier(state, world, pos);
|
||||||
final Material mat = state.getMaterial();
|
|
||||||
final IGrowableCrystal cry = (IGrowableCrystal) is.getItem();
|
|
||||||
|
|
||||||
final float multiplier = cry.getMultiplier(state.getBlock(), mat);
|
if (multiplier <= 0) {
|
||||||
final int speed = (int) Math.max(1, this.getSpeed(j, i, k) * multiplier);
|
// 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 (world.isRemote()) {
|
||||||
if (isClient) {
|
// On the client, we reuse the growth-tick-progress
|
||||||
this.progress_1000++;
|
// as a tick-counter for particle effects
|
||||||
} else {
|
int len = getTicksBetweenParticleEffects(progressPerTick);
|
||||||
this.progress_1000 += speed;
|
if (++this.progress_1000 >= len) {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.progress_1000 = 0;
|
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) {
|
if (this.progress_1000 >= 1000) {
|
||||||
int len = 40;
|
// 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) {
|
// If we did not use a while loop here, the fastest growth for a crystal
|
||||||
len = 20;
|
// 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,
|
||||||
if (speed > 90) {
|
// it should be possible to go faster.
|
||||||
len = 15;
|
do {
|
||||||
}
|
newItem = cry.triggerGrowth(newItem);
|
||||||
|
|
||||||
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) {
|
|
||||||
this.progress_1000 -= 1000;
|
this.progress_1000 -= 1000;
|
||||||
// We need to copy the stack or the change detection will not work and not sync
|
// We assume that if the item changes, the process is complete and we can break
|
||||||
// this new stack to the client
|
} while (this.progress_1000 >= 1000 && newItem.getItem() == is.getItem());
|
||||||
ItemStack newItem = cry.triggerGrowth(is.copy());
|
|
||||||
this.setItem(newItem);
|
this.setItem(newItem);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSpeed(final int x, final int y, final int z) {
|
private static int getTicksBetweenParticleEffects(int progressPerTick) {
|
||||||
final int per = 80;
|
if (progressPerTick > 500) {
|
||||||
final float mul = 0.3f;
|
return 1; // 20 times per second
|
||||||
|
} else if (progressPerTick > 360) {
|
||||||
int qty = 0;
|
return 3;
|
||||||
|
} else if (progressPerTick > 240) {
|
||||||
if (this.isAccelerated(x + 1, y, z)) {
|
return 7;
|
||||||
qty += per + qty * mul;
|
} 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();
|
return te instanceof ICrystalGrowthAccelerator && ((ICrystalGrowthAccelerator) te).isPowered();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't let seeds "float" on water surface
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyFloatMotion() {
|
protected void applyFloatMotion() {
|
||||||
ItemStack item = getItem();
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
super.applyFloatMotion();
|
super.applyFloatMotion();
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import net.minecraft.util.SoundEvents;
|
|||||||
import net.minecraft.util.math.AxisAlignedBB;
|
import net.minecraft.util.math.AxisAlignedBB;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.Explosion;
|
import net.minecraft.world.Explosion;
|
||||||
import net.minecraft.world.Explosion.Mode;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
|
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
|
||||||
import net.minecraftforge.fml.network.NetworkHooks;
|
import net.minecraftforge.fml.network.NetworkHooks;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import net.minecraft.item.ItemStack;
|
|||||||
import net.minecraft.util.text.ITextComponent;
|
import net.minecraft.util.text.ITextComponent;
|
||||||
import net.minecraft.util.text.StringTextComponent;
|
import net.minecraft.util.text.StringTextComponent;
|
||||||
import net.minecraft.util.text.TranslationTextComponent;
|
import net.minecraft.util.text.TranslationTextComponent;
|
||||||
import net.minecraftforge.common.util.LazyOptional;
|
|
||||||
import net.minecraftforge.fluids.FluidAttributes;
|
import net.minecraftforge.fluids.FluidAttributes;
|
||||||
import net.minecraftforge.fluids.FluidStack;
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
import net.minecraftforge.fluids.FluidUtil;
|
import net.minecraftforge.fluids.FluidUtil;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import net.minecraft.entity.player.PlayerInventory;
|
|||||||
import net.minecraft.inventory.container.ContainerType;
|
import net.minecraft.inventory.container.ContainerType;
|
||||||
import net.minecraft.inventory.container.IContainerListener;
|
import net.minecraft.inventory.container.IContainerListener;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraftforge.common.util.LazyOptional;
|
|
||||||
import net.minecraftforge.fluids.FluidStack;
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
import net.minecraftforge.fluids.FluidUtil;
|
import net.minecraftforge.fluids.FluidUtil;
|
||||||
|
|
||||||
|
|||||||
@@ -96,16 +96,15 @@ public class FluidImportBusPart extends SharedFluidBusPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final TileEntity te = this.getConnectedTE();
|
final TileEntity te = this.getConnectedTE();
|
||||||
LazyOptional<IFluidHandler> fhOpt = LazyOptional.empty();
|
|
||||||
if (te != null) {
|
if (te != null) {
|
||||||
te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite());
|
LazyOptional<IFluidHandler> fhOpt = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
|
||||||
}
|
this.getSide().getFacing().getOpposite());
|
||||||
if (fhOpt.isPresent()) {
|
|
||||||
try {
|
if (fhOpt.isPresent()) {
|
||||||
final IFluidHandler fh = fhOpt.orElse(null);
|
try {
|
||||||
final IMEMonitor<IAEFluidStack> inv = this.getProxy().getStorage().getInventory(this.getChannel());
|
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);
|
final FluidStack fluidStack = fh.drain(this.calculateAmountToSend(), FluidAction.SIMULATE);
|
||||||
|
|
||||||
if (this.filterEnabled() && !this.isInFilter(fluidStack)) {
|
if (this.filterEnabled() && !this.isInFilter(fluidStack)) {
|
||||||
@@ -128,12 +127,11 @@ public class FluidImportBusPart extends SharedFluidBusPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return TickRateModulation.IDLE;
|
return TickRateModulation.IDLE;
|
||||||
|
} catch (GridAccessException e) {
|
||||||
|
// skip
|
||||||
}
|
}
|
||||||
} catch (GridAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TickRateModulation.SLEEP;
|
return TickRateModulation.SLEEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import appeng.core.AppEng;
|
|||||||
import appeng.core.sync.packets.PaintedEntityPacket;
|
import appeng.core.sync.packets.PaintedEntityPacket;
|
||||||
import appeng.crafting.CraftingJob;
|
import appeng.crafting.CraftingJob;
|
||||||
import appeng.me.Grid;
|
import appeng.me.Grid;
|
||||||
|
import appeng.metrics.Metrics;
|
||||||
import appeng.tile.AEBaseTileEntity;
|
import appeng.tile.AEBaseTileEntity;
|
||||||
import appeng.util.IWorldCallable;
|
import appeng.util.IWorldCallable;
|
||||||
import appeng.util.Platform;
|
import appeng.util.Platform;
|
||||||
@@ -77,6 +78,11 @@ public class TickHandler {
|
|||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Metrics.gauge("ticking_network_count", () -> INSTANCE.server.networks.size());
|
||||||
|
Metrics.gauge("crafting_job_count", INSTANCE.craftingJobs::size);
|
||||||
|
}
|
||||||
|
|
||||||
public static void setup(IEventBus eventBus) {
|
public static void setup(IEventBus eventBus) {
|
||||||
eventBus.addListener(INSTANCE::onServerTick);
|
eventBus.addListener(INSTANCE::onServerTick);
|
||||||
eventBus.addListener(INSTANCE::onWorldTick);
|
eventBus.addListener(INSTANCE::onWorldTick);
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
package appeng.integration.abstraction;
|
package appeng.integration.abstraction;
|
||||||
|
|
||||||
|
import mezz.jei.api.runtime.IJeiRuntime;
|
||||||
|
|
||||||
import appeng.integration.IIntegrationModule;
|
import appeng.integration.IIntegrationModule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,6 +27,10 @@ import appeng.integration.IIntegrationModule;
|
|||||||
*/
|
*/
|
||||||
public interface IJEI extends IIntegrationModule {
|
public interface IJEI extends IIntegrationModule {
|
||||||
|
|
||||||
|
default IJeiRuntime getRuntime() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
default String getSearchText() {
|
default String getSearchText() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Applied Energistics 2.
|
||||||
|
* Copyright (c) 2020 Team Appliedenergistics, All rights reserved.
|
||||||
|
*
|
||||||
|
* Applied Energistics 2 is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Applied Energistics 2 is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package appeng.integration.modules.jei;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.item.crafting.IRecipe;
|
||||||
|
|
||||||
|
import mezz.jei.api.gui.IRecipeLayout;
|
||||||
|
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
||||||
|
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper;
|
||||||
|
|
||||||
|
import appeng.container.implementations.CraftingTermContainer;
|
||||||
|
|
||||||
|
public class CraftingRecipeTransferHandler extends RecipeTransferHandler<CraftingTermContainer> {
|
||||||
|
|
||||||
|
CraftingRecipeTransferHandler(Class<CraftingTermContainer> containerClass, IRecipeTransferHandlerHelper helper) {
|
||||||
|
super(containerClass, helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IRecipeTransferError doTransferRecipe(CraftingTermContainer container, IRecipe<?> recipe,
|
||||||
|
IRecipeLayout recipeLayout, PlayerEntity player, boolean maxTransfer) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCrafting() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -111,6 +111,8 @@ class FacadeRegistryPlugin implements IRecipeManagerPlugin {
|
|||||||
ingredients.set(7, Ingredient.fromStacks(cableAnchor));
|
ingredients.set(7, Ingredient.fromStacks(cableAnchor));
|
||||||
ingredients.set(4, Ingredient.fromStacks(textureItem));
|
ingredients.set(4, Ingredient.fromStacks(textureItem));
|
||||||
|
|
||||||
|
result.setCount(4);
|
||||||
|
|
||||||
return new ShapedRecipe(id, "", 3, 3, ingredients, result);
|
return new ShapedRecipe(id, "", 3, 3, ingredients, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,11 +82,15 @@ public class JEIPlugin implements IModPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
|
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
|
||||||
// Allow recipe transfer from JEI to crafting and pattern terminal
|
|
||||||
registration.addRecipeTransferHandler(new RecipeTransferHandler<>(CraftingTermContainer.class),
|
// Allow vanilla crafting recipe transfer from JEI to crafting terminal
|
||||||
VanillaRecipeCategoryUid.CRAFTING);
|
registration.addRecipeTransferHandler(
|
||||||
registration.addRecipeTransferHandler(new RecipeTransferHandler<>(PatternTermContainer.class),
|
new CraftingRecipeTransferHandler(CraftingTermContainer.class, registration.getTransferHelper()),
|
||||||
VanillaRecipeCategoryUid.CRAFTING);
|
VanillaRecipeCategoryUid.CRAFTING);
|
||||||
|
|
||||||
|
// Universal handler for processing to try and handle all IRecipe
|
||||||
|
registration.addUniversalRecipeTransferHandler(
|
||||||
|
new PatternRecipeTransferHandler(PatternTermContainer.class, registration.getTransferHelper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ class JeiRuntimeAdapter implements IJEI {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IJeiRuntime getRuntime() {
|
||||||
|
return runtime;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSearchText() {
|
public String getSearchText() {
|
||||||
return Strings.nullToEmpty(this.runtime.getIngredientFilter().getFilterText());
|
return Strings.nullToEmpty(this.runtime.getIngredientFilter().getFilterText());
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Applied Energistics 2.
|
||||||
|
* Copyright (c) 2020 Team Appliedenergistics, All rights reserved.
|
||||||
|
*
|
||||||
|
* Applied Energistics 2 is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Applied Energistics 2 is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package appeng.integration.modules.jei;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.I18n;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.item.crafting.IRecipe;
|
||||||
|
|
||||||
|
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
|
||||||
|
import mezz.jei.api.gui.IRecipeLayout;
|
||||||
|
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
||||||
|
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper;
|
||||||
|
|
||||||
|
import appeng.container.implementations.PatternTermContainer;
|
||||||
|
|
||||||
|
public class PatternRecipeTransferHandler extends RecipeTransferHandler<PatternTermContainer> {
|
||||||
|
|
||||||
|
PatternRecipeTransferHandler(Class<PatternTermContainer> containerClass, IRecipeTransferHandlerHelper helper) {
|
||||||
|
super(containerClass, helper);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IRecipeTransferError doTransferRecipe(PatternTermContainer container, IRecipe<?> recipe,
|
||||||
|
IRecipeLayout recipeLayout, PlayerEntity player, boolean maxTransfer) {
|
||||||
|
if (container.isCraftingMode()
|
||||||
|
&& recipeLayout.getRecipeCategory().getUid() != VanillaRecipeCategoryUid.CRAFTING) {
|
||||||
|
return this.helper
|
||||||
|
.createUserErrorWithTooltip(I18n.format("jei.appliedenergistics2.requires_processing_mode"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recipe.getRecipeOutput().isEmpty()) {
|
||||||
|
return this.helper.createUserErrorWithTooltip(I18n.format("jei.appliedenergistics2.no_output"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isCrafting() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,101 +18,128 @@
|
|||||||
|
|
||||||
package appeng.integration.modules.jei;
|
package appeng.integration.modules.jei;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import net.minecraft.client.resources.I18n;
|
||||||
|
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.inventory.container.Container;
|
import net.minecraft.inventory.container.Container;
|
||||||
import net.minecraft.inventory.container.Slot;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.item.crafting.IRecipe;
|
||||||
import net.minecraft.nbt.ListNBT;
|
import net.minecraft.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.item.crafting.ShapedRecipe;
|
||||||
|
import net.minecraft.item.crafting.ShapelessRecipe;
|
||||||
|
import net.minecraft.util.NonNullList;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
import mezz.jei.api.gui.IRecipeLayout;
|
import mezz.jei.api.gui.IRecipeLayout;
|
||||||
import mezz.jei.api.gui.ingredient.IGuiIngredient;
|
import mezz.jei.api.gui.ingredient.IGuiIngredient;
|
||||||
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
|
||||||
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
|
import mezz.jei.api.recipe.transfer.IRecipeTransferHandler;
|
||||||
|
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper;
|
||||||
|
|
||||||
import appeng.container.slot.CraftingMatrixSlot;
|
|
||||||
import appeng.container.slot.FakeCraftingMatrixSlot;
|
|
||||||
import appeng.core.sync.network.NetworkHandler;
|
import appeng.core.sync.network.NetworkHandler;
|
||||||
import appeng.core.sync.packets.JEIRecipePacket;
|
import appeng.core.sync.packets.JEIRecipePacket;
|
||||||
import appeng.util.Platform;
|
import appeng.helpers.IContainerCraftingPacket;
|
||||||
|
|
||||||
class RecipeTransferHandler<T extends Container> implements IRecipeTransferHandler<T> {
|
abstract class RecipeTransferHandler<T extends Container & IContainerCraftingPacket>
|
||||||
|
implements IRecipeTransferHandler<T> {
|
||||||
|
|
||||||
private final Class<T> containerClass;
|
private final Class<T> containerClass;
|
||||||
|
protected final IRecipeTransferHandlerHelper helper;
|
||||||
|
|
||||||
RecipeTransferHandler(Class<T> containerClass) {
|
RecipeTransferHandler(Class<T> containerClass, IRecipeTransferHandlerHelper helper) {
|
||||||
this.containerClass = containerClass;
|
this.containerClass = containerClass;
|
||||||
|
this.helper = helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<T> getContainerClass() {
|
public final Class<T> getContainerClass() {
|
||||||
return this.containerClass;
|
return this.containerClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public IRecipeTransferError transferRecipe(T container, IRecipeLayout recipeLayout, PlayerEntity player,
|
public final IRecipeTransferError transferRecipe(T container, Object recipe, IRecipeLayout recipeLayout,
|
||||||
boolean maxTransfer, boolean doTransfer) {
|
PlayerEntity player, boolean maxTransfer, boolean doTransfer) {
|
||||||
if (!doTransfer) {
|
if (!(recipe instanceof IRecipe)) {
|
||||||
return null;
|
return this.helper.createInternalError();
|
||||||
|
}
|
||||||
|
final IRecipe<?> irecipe = (IRecipe<?>) recipe;
|
||||||
|
final ResourceLocation recipeId = irecipe.getId();
|
||||||
|
|
||||||
|
if (recipeId == null) {
|
||||||
|
return this.helper.createUserErrorWithTooltip(I18n.format("jei.appliedenergistics2.missing_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Integer, ? extends IGuiIngredient<ItemStack>> ingredients = recipeLayout.getItemStacks()
|
// Check that the recipe can actually be looked up via the manager, i.e. our
|
||||||
.getGuiIngredients();
|
// facade recipes
|
||||||
|
// have an ID, but are never registered with the recipe manager.
|
||||||
final CompoundNBT recipe = new CompoundNBT();
|
boolean canSendReference = true;
|
||||||
|
if (!player.getEntityWorld().getRecipeManager().getRecipe(recipeId).isPresent()) {
|
||||||
int slotIndex = 0;
|
// Validate that the recipe is a shapeless or shapedrecipe, since we can
|
||||||
for (Map.Entry<Integer, ? extends IGuiIngredient<ItemStack>> ingredientEntry : ingredients.entrySet()) {
|
// serialize those
|
||||||
IGuiIngredient<ItemStack> ingredient = ingredientEntry.getValue();
|
if (!(recipe instanceof ShapedRecipe) && !(recipe instanceof ShapelessRecipe)) {
|
||||||
if (!ingredient.isInput()) {
|
return this.helper.createUserErrorWithTooltip(I18n.format("jei.appliedenergistics2.missing_id"));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
canSendReference = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (final Slot slot : container.inventorySlots) {
|
if (!irecipe.canFit(3, 3)) {
|
||||||
if (slot instanceof CraftingMatrixSlot || slot instanceof FakeCraftingMatrixSlot) {
|
return this.helper.createUserErrorWithTooltip(I18n.format("jei.appliedenergistics2.recipe_too_large"));
|
||||||
if (slot.getSlotIndex() == slotIndex) {
|
}
|
||||||
final ListNBT tags = new ListNBT();
|
|
||||||
final List<ItemStack> list = new ArrayList<>();
|
|
||||||
final ItemStack displayed = ingredient.getDisplayedIngredient();
|
|
||||||
|
|
||||||
// prefer currently displayed item
|
final IRecipeTransferError error = doTransferRecipe(container, irecipe, recipeLayout, player, maxTransfer);
|
||||||
if (displayed != null && !displayed.isEmpty()) {
|
|
||||||
list.add(displayed);
|
if (error != null) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doTransfer) {
|
||||||
|
if (canSendReference) {
|
||||||
|
NetworkHandler.instance().sendToServer(new JEIRecipePacket(recipeId, isCrafting()));
|
||||||
|
} else {
|
||||||
|
// To avoid earlier problems of too large packets being sent that crashed the
|
||||||
|
// client,
|
||||||
|
// as a fallback when the recipe ID could not be resolved, we'll just send the
|
||||||
|
// displayed
|
||||||
|
// items.
|
||||||
|
NonNullList<Ingredient> flatIngredients = NonNullList.withSize(9, Ingredient.EMPTY);
|
||||||
|
ItemStack output = ItemStack.EMPTY;
|
||||||
|
|
||||||
|
// Determine the first JEI slot that has an actual input, we'll use this to
|
||||||
|
// offset the
|
||||||
|
// crafting grid target slot
|
||||||
|
int firstInputSlot = recipeLayout.getItemStacks().getGuiIngredients().entrySet().stream()
|
||||||
|
.filter(e -> e.getValue().isInput()).mapToInt(Map.Entry::getKey).min().orElse(0);
|
||||||
|
|
||||||
|
// Now map the actual ingredients into the output/input
|
||||||
|
for (Map.Entry<Integer, ? extends IGuiIngredient<ItemStack>> entry : recipeLayout.getItemStacks()
|
||||||
|
.getGuiIngredients().entrySet()) {
|
||||||
|
IGuiIngredient<ItemStack> item = entry.getValue();
|
||||||
|
if (item.getDisplayedIngredient() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inputIndex = entry.getKey() - firstInputSlot;
|
||||||
|
if (item.isInput() && inputIndex < flatIngredients.size()) {
|
||||||
|
ItemStack displayedIngredient = item.getDisplayedIngredient();
|
||||||
|
if (displayedIngredient != null) {
|
||||||
|
flatIngredients.set(inputIndex, Ingredient.fromStacks(displayedIngredient));
|
||||||
}
|
}
|
||||||
|
} else if (!item.isInput() && output.isEmpty()) {
|
||||||
// prefer pure crystals.
|
output = item.getDisplayedIngredient();
|
||||||
for (ItemStack stack : ingredient.getAllIngredients()) {
|
|
||||||
if (Platform.isRecipePrioritized(stack)) {
|
|
||||||
list.add(0, stack);
|
|
||||||
} else {
|
|
||||||
list.add(stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final ItemStack is : list) {
|
|
||||||
final CompoundNBT tag = new CompoundNBT();
|
|
||||||
is.write(tag);
|
|
||||||
tags.add(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
recipe.put("#" + slot.getSlotIndex(), tags);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShapedRecipe fallbackRecipe = new ShapedRecipe(recipeId, "", 3, 3, flatIngredients, output);
|
||||||
|
NetworkHandler.instance().sendToServer(new JEIRecipePacket(fallbackRecipe, isCrafting()));
|
||||||
}
|
}
|
||||||
|
|
||||||
slotIndex++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkHandler.instance().sendToServer(new JEIRecipePacket(recipe));
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract IRecipeTransferError doTransferRecipe(T container, IRecipe<?> recipe, IRecipeLayout recipeLayout,
|
||||||
|
PlayerEntity player, boolean maxTransfer);
|
||||||
|
|
||||||
|
protected abstract boolean isCrafting();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,19 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.material.Material;
|
|
||||||
import net.minecraft.client.util.ITooltipFlag;
|
import net.minecraft.client.util.ITooltipFlag;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.fluid.Fluid;
|
||||||
import net.minecraft.item.ItemGroup;
|
import net.minecraft.item.ItemGroup;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
|
import net.minecraft.tags.FluidTags;
|
||||||
|
import net.minecraft.tags.ITag;
|
||||||
import net.minecraft.util.IItemProvider;
|
import net.minecraft.util.IItemProvider;
|
||||||
import net.minecraft.util.NonNullList;
|
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.math.MathHelper;
|
||||||
import net.minecraft.util.text.ITextComponent;
|
import net.minecraft.util.text.ITextComponent;
|
||||||
import net.minecraft.util.text.StringTextComponent;
|
import net.minecraft.util.text.StringTextComponent;
|
||||||
@@ -41,6 +45,7 @@ import net.minecraftforge.api.distmarker.Dist;
|
|||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
import appeng.api.implementations.items.IGrowableCrystal;
|
import appeng.api.implementations.items.IGrowableCrystal;
|
||||||
|
import appeng.core.AEConfig;
|
||||||
import appeng.core.localization.ButtonToolTips;
|
import appeng.core.localization.ButtonToolTips;
|
||||||
import appeng.entity.GrowingCrystalEntity;
|
import appeng.entity.GrowingCrystalEntity;
|
||||||
import appeng.items.AEBaseItem;
|
import appeng.items.AEBaseItem;
|
||||||
@@ -94,8 +99,24 @@ public class CrystalSeedItem extends AEBaseItem implements IGrowableCrystal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getMultiplier(final Block blk, final Material mat) {
|
public float getMultiplier(BlockState state, @Nullable World world, @Nullable BlockPos pos) {
|
||||||
return 0.5f;
|
|
||||||
|
// 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
|
@Override
|
||||||
|
|||||||
@@ -324,21 +324,21 @@ public class ColorApplicatorItem extends AEBasePoweredItem
|
|||||||
return w.setBlockState(pos, newState);
|
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);
|
TileEntity be = w.getTileEntity(pos);
|
||||||
if (be instanceof IColorableTile) {
|
if (be instanceof IColorableTile) {
|
||||||
IColorableTile ct = (IColorableTile) be;
|
IColorableTile ct = (IColorableTile) be;
|
||||||
AEColor c = ct.getColor();
|
AEColor c = ct.getColor();
|
||||||
if (c != newColor) {
|
if (c != newColor) {
|
||||||
ct.recolourBlock(side, newColor, null);
|
ct.recolourBlock(side, newColor, p);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blk instanceof CableBusBlock && p != null) {
|
|
||||||
return ((CableBusBlock) blk).recolorBlock(w, pos, side, newColor.dye, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraftforge.common.MinecraftForge;
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
import net.minecraftforge.event.world.WorldEvent;
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
@@ -159,12 +158,16 @@ public class QuantumCluster implements ILocatable, IAECluster {
|
|||||||
if (qc != null) {
|
if (qc != null) {
|
||||||
final World theWorld = qc.center.getWorld();
|
final World theWorld = qc.center.getWorld();
|
||||||
if (!qc.isDestroyed) {
|
if (!qc.isDestroyed) {
|
||||||
ChunkPos cPos = new ChunkPos(qc.center.getPos());
|
// In future versions, we might actually want to delay the entire registration
|
||||||
if (theWorld.getChunkProvider().isChunkLoaded(cPos)) {
|
// until the center
|
||||||
|
// tile begins ticking normally.
|
||||||
|
if (theWorld.isBlockLoaded(qc.center.getPos())) {
|
||||||
final World cur = theWorld.getServer().getWorld(theWorld.func_234923_W_());
|
final World cur = theWorld.getServer().getWorld(theWorld.func_234923_W_());
|
||||||
|
|
||||||
final TileEntity te = theWorld.getTileEntity(qc.center.getPos());
|
final TileEntity te = theWorld.getTileEntity(qc.center.getPos());
|
||||||
return te != qc.center || theWorld != cur;
|
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) {
|
private void setRing(final QuantumBridgeTileEntity[] ring) {
|
||||||
this.Ring = 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 + "}";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package appeng.metrics;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Metrics simplify reporting of metrics for a Guava {@link Cache}.
|
||||||
|
*/
|
||||||
|
public class CacheMetrics extends Metric {
|
||||||
|
|
||||||
|
private final Supplier<Cache<?, ?>> cacheSupplier;
|
||||||
|
|
||||||
|
// Pre-allocate these to avoid repeated concatenation during reporting
|
||||||
|
private final String sizeId;
|
||||||
|
private final String hitCountId;
|
||||||
|
private final String missCountId;
|
||||||
|
private final String loadSuccessCountId;
|
||||||
|
private final String loadExceptionCountId;
|
||||||
|
private final String totalLoadTimeId;
|
||||||
|
private final String evictionCountId;
|
||||||
|
|
||||||
|
public CacheMetrics(String id, Supplier<Cache<?, ?>> cacheSupplier) {
|
||||||
|
super(id);
|
||||||
|
this.cacheSupplier = cacheSupplier;
|
||||||
|
|
||||||
|
this.sizeId = id + ".size";
|
||||||
|
this.hitCountId = id + ".hit-count";
|
||||||
|
this.missCountId = id + ".miss-count";
|
||||||
|
this.loadSuccessCountId = id + ".load-success-count";
|
||||||
|
this.loadExceptionCountId = id + ".load-exception-count";
|
||||||
|
this.totalLoadTimeId = id + ".total-load-time";
|
||||||
|
this.evictionCountId = id + ".eviction-count";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(MetricVisitor visitor) {
|
||||||
|
Cache<?, ?> cache = this.cacheSupplier.get();
|
||||||
|
|
||||||
|
if (cache != null) {
|
||||||
|
visitor.visitGauge(sizeId, cache.size()); // Thread-safe
|
||||||
|
|
||||||
|
CacheStats stats = cache.stats(); // Thread-safe
|
||||||
|
visitor.visitGauge(hitCountId, stats.hitCount());
|
||||||
|
visitor.visitGauge(missCountId, stats.missCount());
|
||||||
|
visitor.visitGauge(loadSuccessCountId, stats.loadSuccessCount());
|
||||||
|
visitor.visitGauge(loadExceptionCountId, stats.loadExceptionCount());
|
||||||
|
visitor.visitGauge(totalLoadTimeId, stats.totalLoadTime());
|
||||||
|
visitor.visitGauge(evictionCountId, stats.evictionCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package appeng.metrics;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Gauge is a Metric that will simply read the current value of something when
|
||||||
|
* the metrics are being reported.
|
||||||
|
*/
|
||||||
|
class Gauge extends Metric {
|
||||||
|
|
||||||
|
private final Supplier<Number> valueSupplier;
|
||||||
|
|
||||||
|
public Gauge(String id, Supplier<Number> valueSupplier) {
|
||||||
|
super(id);
|
||||||
|
this.valueSupplier = valueSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<Number> getValueSupplier() {
|
||||||
|
return valueSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Number getValue() {
|
||||||
|
return valueSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(MetricVisitor visitor) {
|
||||||
|
visitor.visitGauge(getId(), getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package appeng.metrics;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
abstract class Metric {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public Metric(String id) {
|
||||||
|
this.id = Objects.requireNonNull(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o)
|
||||||
|
return true;
|
||||||
|
if (o == null || getClass() != o.getClass())
|
||||||
|
return false;
|
||||||
|
Metric metric = (Metric) o;
|
||||||
|
return id.equals(metric.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void accept(MetricVisitor visitor);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package appeng.metrics;
|
||||||
|
|
||||||
|
public interface MetricVisitor {
|
||||||
|
|
||||||
|
void visitGauge(String id, Number value);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package appeng.metrics;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.LongSupplier;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheStats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry to register Metrics.
|
||||||
|
*/
|
||||||
|
public final class Metrics {
|
||||||
|
|
||||||
|
private static final List<Metric> metrics = new ArrayList<>();
|
||||||
|
|
||||||
|
private Metrics() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a Gauge, which is a simple metric with no state that will be
|
||||||
|
* queried for its current value whenever metrics are being reported.
|
||||||
|
*/
|
||||||
|
public static synchronized void gauge(String id, Supplier<Number> valueSupplier) {
|
||||||
|
metrics.add(new Gauge(id, valueSupplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register several standard metrics for a Guava {@link Cache}.
|
||||||
|
*/
|
||||||
|
public static synchronized void cache(String name, Cache<?, ?> cache) {
|
||||||
|
metrics.add(new CacheMetrics(name, () -> cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register several standard metrics for a Guava {@link Cache} based on a
|
||||||
|
* supplier to support an underlying changing cache.
|
||||||
|
*/
|
||||||
|
public static synchronized void cache(String name, Supplier<Cache<?, ?>> cacheSupplier) {
|
||||||
|
metrics.add(new CacheMetrics(name, cacheSupplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void visit(MetricVisitor visitor) {
|
||||||
|
for (Metric metric : metrics) {
|
||||||
|
metric.accept(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package appeng.metrics.endpoint;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
|
||||||
|
import appeng.metrics.Metrics;
|
||||||
|
import appeng.metrics.reporter.PrometheusReporter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an embedded HTTP-Server that will expose the Metrics in a format
|
||||||
|
* compatible with Prometheus.
|
||||||
|
*/
|
||||||
|
public class PrometheusEndpoint implements AutoCloseable {
|
||||||
|
|
||||||
|
private final HttpServer server;
|
||||||
|
|
||||||
|
private final PrometheusReporter reporter = new PrometheusReporter();
|
||||||
|
|
||||||
|
public PrometheusEndpoint(String hostname, int port) {
|
||||||
|
try {
|
||||||
|
this.server = HttpServer.create();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to create metrics HTTP-server", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
InetSocketAddress address;
|
||||||
|
if (hostname.isEmpty()) {
|
||||||
|
address = new InetSocketAddress((InetAddress) null, port);
|
||||||
|
} else {
|
||||||
|
address = new InetSocketAddress(hostname, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default endpoint is /metrics in Prometheus
|
||||||
|
this.server.createContext("/metrics", this::handler);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.server.bind(address, 0);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to bind metrics HTTP server to " + address, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.server.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
this.server.stop(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void handler(HttpExchange exchange) throws IOException {
|
||||||
|
// Update metrics
|
||||||
|
reporter.reset();
|
||||||
|
Metrics.visit(reporter);
|
||||||
|
|
||||||
|
CharSequence report = getReport();
|
||||||
|
|
||||||
|
exchange.getResponseHeaders().add("Content-Type", "text/plain; version=0.0.4");
|
||||||
|
exchange.sendResponseHeaders(200, report.length());
|
||||||
|
|
||||||
|
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody(), StandardCharsets.US_ASCII)) {
|
||||||
|
writer.append(report);
|
||||||
|
}
|
||||||
|
exchange.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharSequence getReport() {
|
||||||
|
return reporter.getReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package appeng.metrics.reporter;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import appeng.metrics.MetricVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports Metrics as simple key-value pairs to a {@link PrintStream} (i.e.
|
||||||
|
* {@link System#out}).
|
||||||
|
*/
|
||||||
|
public class PrintStreamReporter implements MetricVisitor {
|
||||||
|
|
||||||
|
private final PrintStream out;
|
||||||
|
|
||||||
|
public PrintStreamReporter(PrintStream out) {
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitGauge(String id, Number value) {
|
||||||
|
out.println(id + '=' + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package appeng.metrics.reporter;
|
||||||
|
|
||||||
|
import appeng.metrics.MetricVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports the metrics in a format compatible with Prometheus.
|
||||||
|
* <p>
|
||||||
|
* See <a href=
|
||||||
|
* "https://prometheus.io/docs/instrumenting/exposition_formats/">Prometheus
|
||||||
|
* Docs</a>
|
||||||
|
*/
|
||||||
|
public class PrometheusReporter implements MetricVisitor {
|
||||||
|
|
||||||
|
private final StringBuilder response = new StringBuilder();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitGauge(String id, Number value) {
|
||||||
|
printType(id, "gauge");
|
||||||
|
appendId(id);
|
||||||
|
response.append(' ').append(value.toString()).append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printType(String id, String type) {
|
||||||
|
response.append("# TYPE ");
|
||||||
|
appendId(id);
|
||||||
|
response.append(' ').append(type).append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
response.setLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length() {
|
||||||
|
return response.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence getReport() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendId(String id) {
|
||||||
|
response.append("appeng_");
|
||||||
|
// Sanitize to [a-zA-Z0-9:_]
|
||||||
|
for (int i = 0; i < id.length(); i++) {
|
||||||
|
char ch = id.charAt(i);
|
||||||
|
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || ch == ':' || ch == '_') {
|
||||||
|
response.append(ch);
|
||||||
|
} else {
|
||||||
|
response.append('_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
import net.minecraft.world.biome.Biome;
|
import net.minecraft.world.biome.Biome;
|
||||||
import net.minecraft.world.gen.feature.StructureFeature;
|
|
||||||
import net.minecraft.world.gen.feature.structure.Structure;
|
import net.minecraft.world.gen.feature.structure.Structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import net.minecraft.util.Hand;
|
|||||||
import net.minecraft.util.math.AxisAlignedBB;
|
import net.minecraft.util.math.AxisAlignedBB;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.shapes.VoxelShape;
|
import net.minecraft.util.math.shapes.VoxelShape;
|
||||||
import net.minecraft.util.math.shapes.VoxelShapes;
|
|
||||||
import net.minecraft.util.math.vector.Vector3d;
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
import net.minecraft.world.IBlockReader;
|
import net.minecraft.world.IBlockReader;
|
||||||
import net.minecraft.world.World;
|
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 final EnumSet<LayerFlags> myLayerFlags = EnumSet.noneOf(LayerFlags.class);
|
||||||
private YesNo hasRedstone = YesNo.UNDECIDED;
|
private YesNo hasRedstone = YesNo.UNDECIDED;
|
||||||
private IPartHost tcb;
|
private IPartHost tcb;
|
||||||
// TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not.
|
|
||||||
private boolean requiresDynamicRender = false;
|
private boolean requiresDynamicRender = false;
|
||||||
private boolean inWorld = false;
|
private boolean inWorld = false;
|
||||||
// Cached collision shape for living entities
|
// Cached collision shape for living entities
|
||||||
@@ -1096,11 +1094,7 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelShape shape = VoxelShapes.empty();
|
return VoxelShapeCache.get(boxes);
|
||||||
for (final AxisAlignedBB bx : boxes) {
|
|
||||||
shape = VoxelShapes.or(shape, VoxelShapes.create(bx));
|
|
||||||
}
|
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateShapes() {
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -34,7 +34,6 @@ import net.minecraft.item.Items;
|
|||||||
import net.minecraft.tags.BlockTags;
|
import net.minecraft.tags.BlockTags;
|
||||||
import net.minecraft.tags.ITag;
|
import net.minecraft.tags.ITag;
|
||||||
import net.minecraft.tags.ItemTags;
|
import net.minecraft.tags.ItemTags;
|
||||||
import net.minecraft.tags.Tag;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
import net.minecraft.util.math.AxisAlignedBB;
|
import net.minecraft.util.math.AxisAlignedBB;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package appeng.server;
|
package appeng.server;
|
||||||
|
|
||||||
import appeng.server.subcommands.ChunkLogger;
|
import appeng.server.subcommands.ChunkLogger;
|
||||||
|
import appeng.server.subcommands.MetricsCommand;
|
||||||
import appeng.server.subcommands.SpatialStorageCommand;
|
import appeng.server.subcommands.SpatialStorageCommand;
|
||||||
import appeng.server.subcommands.Supporters;
|
import appeng.server.subcommands.Supporters;
|
||||||
import appeng.server.subcommands.TestMeteoritesCommand;
|
import appeng.server.subcommands.TestMeteoritesCommand;
|
||||||
@@ -27,7 +28,7 @@ import appeng.server.subcommands.TestOreGenCommand;
|
|||||||
public enum Commands {
|
public enum Commands {
|
||||||
Chunklogger(4, new ChunkLogger(), false), Supporters(0, new Supporters(), false),
|
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);
|
Spatial(4, new SpatialStorageCommand(), false), Metrics(4, new MetricsCommand(), false);
|
||||||
|
|
||||||
public final int level;
|
public final int level;
|
||||||
public final ISubCommand command;
|
public final ISubCommand command;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package appeng.server.subcommands;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.util.text.StringTextComponent;
|
||||||
|
|
||||||
|
import appeng.metrics.Metrics;
|
||||||
|
import appeng.metrics.reporter.PrintStreamReporter;
|
||||||
|
import appeng.server.ISubCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple way of printing out AE2's metrics on the server console.
|
||||||
|
*/
|
||||||
|
public class MetricsCommand implements ISubCommand {
|
||||||
|
@Override
|
||||||
|
public void call(MinecraftServer srv, CommandContext<CommandSource> ctx, CommandSource sender) {
|
||||||
|
System.out.println("-------- AE2 Metrics:");
|
||||||
|
Metrics.visit(new PrintStreamReporter(System.out));
|
||||||
|
System.out.println("-------- END");
|
||||||
|
|
||||||
|
sender.sendFeedback(new StringTextComponent("Metrics reported to server console..."), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -253,9 +253,11 @@ public class AEBaseTileEntity extends TileEntity implements IOrientable, ICommon
|
|||||||
if (this.renderFragment > 0) {
|
if (this.renderFragment > 0) {
|
||||||
this.renderFragment |= 1;
|
this.renderFragment |= 1;
|
||||||
} else {
|
} else {
|
||||||
|
// Clearing the cached model-data is always harmless regardless of status
|
||||||
|
this.requestModelDataUpdate();
|
||||||
|
|
||||||
// TODO: Optimize Network Load
|
// TODO: Optimize Network Load
|
||||||
if (this.world != null) {
|
if (this.world != null && !this.isRemoved() && !notLoaded()) {
|
||||||
this.requestModelDataUpdate();
|
|
||||||
|
|
||||||
boolean alreadyUpdated = false;
|
boolean alreadyUpdated = false;
|
||||||
// Let the block update it's own state with our internal state changes
|
// Let the block update it's own state with our internal state changes
|
||||||
@@ -368,7 +370,6 @@ public class AEBaseTileEntity extends TileEntity implements IOrientable, ICommon
|
|||||||
* null means nothing to store...
|
* null means nothing to store...
|
||||||
*
|
*
|
||||||
* @param from source of settings
|
* @param from source of settings
|
||||||
*
|
|
||||||
* @return compound of source
|
* @return compound of source
|
||||||
*/
|
*/
|
||||||
public CompoundNBT downloadSettings(final SettingsFrom from) {
|
public CompoundNBT downloadSettings(final SettingsFrom from) {
|
||||||
|
|||||||
@@ -705,5 +705,9 @@
|
|||||||
"waila.appliedenergistics2.P2POutput": "Linked (Output Side)",
|
"waila.appliedenergistics2.P2POutput": "Linked (Output Side)",
|
||||||
"waila.appliedenergistics2.P2PUnlinked": "Unlinked",
|
"waila.appliedenergistics2.P2PUnlinked": "Unlinked",
|
||||||
"waila.appliedenergistics2.Showing": "Showing",
|
"waila.appliedenergistics2.Showing": "Showing",
|
||||||
"waila.appliedenergistics2.Unlocked": "Unlocked"
|
"waila.appliedenergistics2.Unlocked": "Unlocked",
|
||||||
|
"jei.appliedenergistics2.missing_id": "Cannot identify recipe",
|
||||||
|
"jei.appliedenergistics2.recipe_too_large": "Recipe larger than 3x3",
|
||||||
|
"jei.appliedenergistics2.requires_processing_mode": "Requires processing mode",
|
||||||
|
"jei.appliedenergistics2.no_output": "Recipe has no output"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1 @@
|
|||||||
{
|
{}
|
||||||
"parent": "appliedenergistics2:block/cable_bus"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"display": {
|
"display": {
|
||||||
"icon": {
|
"icon": {
|
||||||
"item": "appliedenergistics2:facade"
|
"item": "appliedenergistics2:facade",
|
||||||
|
"nbt": "{item:\"minecraft:stone\"}"
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"translate": "achievement.ae2.Facade"
|
"translate": "achievement.ae2.Facade"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user