mirror of
https://github.com/XevianLight/Aphelion.git
synced 2026-05-11 01:50:56 +01:00
Compare commits
21 Commits
oxygen-sys
...
2496e0cdd5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2496e0cdd5 | ||
|
|
0f4b98a912 | ||
|
|
018886768e | ||
|
|
bc8bb4ee05 | ||
|
|
ff08a51540 | ||
|
|
c0daaf2cfa | ||
|
|
af1efb5c57 | ||
|
|
9d186a0dd5 | ||
|
|
3557dcdb17 | ||
|
|
bf09330426 | ||
|
|
3b5d10f414 | ||
|
|
331da7c78f | ||
|
|
74731444ea | ||
|
|
df344034a6 | ||
|
|
84000f31fd | ||
|
|
012985441f | ||
|
|
cc93d2fb42 | ||
|
|
2f0c499fdf | ||
|
|
9b030d8c9d | ||
|
|
b012528247 | ||
|
|
f3bd3f891a |
@@ -1 +1,2 @@
|
|||||||
// 1.21.1 2026-01-11T15:05:33.587044 Tags for minecraft:fluid mod id aphelion
|
// 1.21.1 2026-03-14T19:22:23.3145075 Tags for minecraft:fluid mod id aphelion
|
||||||
|
36b33555f1ae6c80989afdd9d986ec0883959f49 data/minecraft/tags/fluid/rocket_fuel.json
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// 1.21.1 2026-01-28T08:47:15.3186366 Loot Tables
|
// 1.21.1 2026-02-07T22:29:56.7198656 Loot Tables
|
||||||
69d8318ddba171526d1fabb87d9d93548ed8598e data/aphelion/loot_table/blocks/arc_furnace_casing.json
|
69d8318ddba171526d1fabb87d9d93548ed8598e data/aphelion/loot_table/blocks/arc_furnace_casing.json
|
||||||
05f08985e601d30116f67e2f07b48b03b40cdca6 data/aphelion/loot_table/blocks/block_steel.json
|
05f08985e601d30116f67e2f07b48b03b40cdca6 data/aphelion/loot_table/blocks/block_steel.json
|
||||||
ff43a9c3741faf10b1e156a7a74d5cfb035cc118 data/aphelion/loot_table/blocks/dimension_changer.json
|
ff43a9c3741faf10b1e156a7a74d5cfb035cc118 data/aphelion/loot_table/blocks/dimension_changer.json
|
||||||
b63130d9c10485676303d729807b6fcaac080294 data/aphelion/loot_table/blocks/electric_arc_furnace.json
|
b63130d9c10485676303d729807b6fcaac080294 data/aphelion/loot_table/blocks/electric_arc_furnace.json
|
||||||
|
b9cfe672ead8e2673a7b2f5c4cec831e7e8e7040 data/aphelion/loot_table/blocks/launch_pad.json
|
||||||
afb6519a03415b8e0d5bafc9fadb70905a398046 data/aphelion/loot_table/blocks/oxygen_test_block.json
|
afb6519a03415b8e0d5bafc9fadb70905a398046 data/aphelion/loot_table/blocks/oxygen_test_block.json
|
||||||
|
87dfbcce4a96ea0ee6e9fc7908aa77d42d1904e5 data/aphelion/loot_table/blocks/rocket_assembler.json
|
||||||
|
9e269f103ac1ce517c1a089b8ca5474af085bd66 data/aphelion/loot_table/blocks/rocket_seat.json
|
||||||
1ab50c99e9f478840b9d003fd56ebdcab12fbbce data/aphelion/loot_table/blocks/test_block.json
|
1ab50c99e9f478840b9d003fd56ebdcab12fbbce data/aphelion/loot_table/blocks/test_block.json
|
||||||
7d8eeb99a1bc942a6e2cf292b21fd4534062b5ab data/aphelion/loot_table/blocks/vacuum_arc_furnace_controller.json
|
7d8eeb99a1bc942a6e2cf292b21fd4534062b5ab data/aphelion/loot_table/blocks/vacuum_arc_furnace_controller.json
|
||||||
797bf9839d79e08b4832c9eaf3cb303b0471ed0c data/aphelion/loot_table/blocks/vaf_dummy_block.json
|
797bf9839d79e08b4832c9eaf3cb303b0471ed0c data/aphelion/loot_table/blocks/vaf_dummy_block.json
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
// 1.21.1 2026-01-23T22:32:50.7214756 Tags for minecraft:block mod id aphelion
|
// 1.21.1 2026-02-07T23:23:26.8270373 Tags for minecraft:block mod id aphelion
|
||||||
|
46f0160a007d32a06624ad98f25e8a1a8d01bb08 data/aphelion/tags/block/launch_pad.json
|
||||||
|
883d37ed36ecbde487d467ddb26b34082067aa09 data/aphelion/tags/block/rocket_seat.json
|
||||||
058c56a0c17204ed5d9cadaffae84292b4752213 data/c/tags/block/storage_blocks.json
|
058c56a0c17204ed5d9cadaffae84292b4752213 data/c/tags/block/storage_blocks.json
|
||||||
058c56a0c17204ed5d9cadaffae84292b4752213 data/c/tags/block/storage_blocks/steel.json
|
058c56a0c17204ed5d9cadaffae84292b4752213 data/c/tags/block/storage_blocks/steel.json
|
||||||
7d420216f15b8f78d2a3b298f9bb773a9e5f79c3 data/minecraft/tags/block/mineable/pickaxe.json
|
7d420216f15b8f78d2a3b298f9bb773a9e5f79c3 data/minecraft/tags/block/mineable/pickaxe.json
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// 1.21.1 2026-01-28T08:47:15.3176368 Block States: aphelion
|
// 1.21.1 2026-02-07T22:29:56.7183649 Block States: aphelion
|
||||||
851ff42f7b21dec86107c8e0cefb3934ae4ebc08 assets/aphelion/blockstates/block_steel.json
|
851ff42f7b21dec86107c8e0cefb3934ae4ebc08 assets/aphelion/blockstates/block_steel.json
|
||||||
30b9c0efd7aaadb5412d98e4568f98b3632adbb9 assets/aphelion/blockstates/dimension_changer.json
|
30b9c0efd7aaadb5412d98e4568f98b3632adbb9 assets/aphelion/blockstates/dimension_changer.json
|
||||||
cb4287104006c80c8396b290ab5258df65d62cef assets/aphelion/blockstates/electric_arc_furnace.json
|
cb4287104006c80c8396b290ab5258df65d62cef assets/aphelion/blockstates/electric_arc_furnace.json
|
||||||
@@ -14,5 +14,6 @@ cdc831b0f1c462be64825fd34bd446e5b95afac6 assets/aphelion/models/item/arc_furnace
|
|||||||
db0ec473a016ce05c258cde18a217d47a9ea8324 assets/aphelion/models/item/dimension_changer.json
|
db0ec473a016ce05c258cde18a217d47a9ea8324 assets/aphelion/models/item/dimension_changer.json
|
||||||
279080c06ada87f54fd0a7b885b256dbe25a946a assets/aphelion/models/item/electric_arc_furnace.json
|
279080c06ada87f54fd0a7b885b256dbe25a946a assets/aphelion/models/item/electric_arc_furnace.json
|
||||||
24cf60e70f7d9450b0e70cf017662e80971bae17 assets/aphelion/models/item/oxygen_test_block.json
|
24cf60e70f7d9450b0e70cf017662e80971bae17 assets/aphelion/models/item/oxygen_test_block.json
|
||||||
|
ab6884315b8f6c99666a87f673b059b5659ff13d assets/aphelion/models/item/rocket_assembler.json
|
||||||
74418ef1cf678e72e7534924274688ef5a68af0e assets/aphelion/models/item/test_block.json
|
74418ef1cf678e72e7534924274688ef5a68af0e assets/aphelion/models/item/test_block.json
|
||||||
88ca3602517e99f7feaed57eddfc96965a25761c assets/aphelion/models/item/vacuum_arc_furnace_controller.json
|
88ca3602517e99f7feaed57eddfc96965a25761c assets/aphelion/models/item/vacuum_arc_furnace_controller.json
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "aphelion:block/rocket_assembler"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"bonus_rolls": 0.0,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "minecraft:survives_explosion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "aphelion:launch_pad"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rolls": 1.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"random_sequence": "aphelion:blocks/launch_pad"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"bonus_rolls": 0.0,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "minecraft:survives_explosion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "aphelion:rocket_assembler"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rolls": 1.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"random_sequence": "aphelion:blocks/rocket_assembler"
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"type": "minecraft:block",
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"bonus_rolls": 0.0,
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"condition": "minecraft:survives_explosion"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type": "minecraft:item",
|
||||||
|
"name": "aphelion:rocket_seat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rolls": 1.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"random_sequence": "aphelion:blocks/rocket_seat"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"aphelion:launch_pad"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"aphelion:rocket_seat"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"values": [
|
||||||
|
"aphelion:rocket_fuel"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,16 +1,26 @@
|
|||||||
package net.xevianlight.aphelion;
|
package net.xevianlight.aphelion;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.neoforged.api.distmarker.Dist;
|
import net.neoforged.api.distmarker.Dist;
|
||||||
import net.neoforged.fml.common.EventBusSubscriber;
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
|
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
|
||||||
|
import net.neoforged.neoforge.client.event.ClientTickEvent;
|
||||||
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
|
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
|
||||||
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
|
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
|
||||||
import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent;
|
import net.neoforged.neoforge.client.extensions.common.RegisterClientExtensionsEvent;
|
||||||
import net.neoforged.neoforge.event.AddReloadListenerEvent;
|
import net.neoforged.neoforge.event.AddReloadListenerEvent;
|
||||||
|
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
import net.xevianlight.aphelion.block.dummy.renderer.MultiblockDummyRenderer;
|
import net.xevianlight.aphelion.block.dummy.renderer.MultiblockDummyRenderer;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.renderer.OxygenTestRenderer;
|
import net.xevianlight.aphelion.block.entity.custom.renderer.OxygenTestRenderer;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.renderer.RocketAssemblerBlockEntityRenderer;
|
||||||
import net.xevianlight.aphelion.client.AphelionConfig;
|
import net.xevianlight.aphelion.client.AphelionConfig;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.EnvironmentSavedData;
|
||||||
|
import net.xevianlight.aphelion.network.packet.PartitionPayload;
|
||||||
import net.xevianlight.aphelion.planet.AphelionPlanetJSONLoader;
|
import net.xevianlight.aphelion.planet.AphelionPlanetJSONLoader;
|
||||||
import net.xevianlight.aphelion.core.init.*;
|
import net.xevianlight.aphelion.core.init.*;
|
||||||
import net.xevianlight.aphelion.fluid.BaseFluidType;
|
import net.xevianlight.aphelion.fluid.BaseFluidType;
|
||||||
@@ -119,20 +129,22 @@ public class Aphelion {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onClientSetup(FMLClientSetupEvent event) {
|
public static void onClientSetup(FMLClientSetupEvent event) {
|
||||||
event.enqueueWork(() -> {
|
event.enqueueWork(() -> {
|
||||||
// ItemBlockRenderTypes.setRenderLayer(ModFluids.SOURCE_OIL_FLUID.get(), RenderType.translucent());
|
ItemBlockRenderTypes.setRenderLayer(ModFluids.ROCKET_FUEL.get(), RenderType.translucent());
|
||||||
// ItemBlockRenderTypes.setRenderLayer(ModFluids.FLOWING_OIL_FLUID.get(), RenderType.translucent());
|
ItemBlockRenderTypes.setRenderLayer(ModFluids.FLOWING_ROCKET_FUEL.get(), RenderType.translucent());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onClientExtensions(RegisterClientExtensionsEvent event) {
|
public static void onClientExtensions(RegisterClientExtensionsEvent event) {
|
||||||
event.registerFluidType(((BaseFluidType) ModFluidTypes.OIL_FLUID_TYPE.get()).getClientFluidTypeExtensions(), ModFluidTypes.OIL_FLUID_TYPE.get());
|
event.registerFluidType(((BaseFluidType) ModFluidTypes.OIL_FLUID_TYPE.get()).getClientFluidTypeExtensions(), ModFluidTypes.OIL_FLUID_TYPE.get());
|
||||||
|
event.registerFluidType(((BaseFluidType) ModFluidTypes.ROCKET_FUEL_FLUID_TYPE.get()).getClientFluidTypeExtensions(), ModFluidTypes.ROCKET_FUEL_FLUID_TYPE.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void registerBER(EntityRenderersEvent.RegisterRenderers event) {
|
public static void registerBER(EntityRenderersEvent.RegisterRenderers event) {
|
||||||
event.registerBlockEntityRenderer(ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), MultiblockDummyRenderer::new);
|
event.registerBlockEntityRenderer(ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), MultiblockDummyRenderer::new);
|
||||||
event.registerBlockEntityRenderer(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), OxygenTestRenderer::new);
|
event.registerBlockEntityRenderer(ModBlockEntities.ROCKET_ASSEMBLER_BLOCK_ENTITY.get(), RocketAssemblerBlockEntityRenderer::new);
|
||||||
|
// event.registerBlockEntityRenderer(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), OxygenTestRenderer::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
@@ -146,5 +158,11 @@ public class Aphelion {
|
|||||||
public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) {
|
public static void registerRenderers(EntityRenderersEvent.RegisterRenderers event) {
|
||||||
event.registerEntityRenderer(ModEntities.ROCKET.get(), RocketRenderer::new);
|
event.registerEntityRenderer(ModEntities.ROCKET.get(), RocketRenderer::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onClientTick(ClientTickEvent.Post e) {
|
||||||
|
if (!Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) return;
|
||||||
|
EnvironmentSavedData.refreshFromIntegratedServerIfNeeded(Minecraft.getInstance(), 64, 50000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BaseRocketContainer;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BaseRocketFuelTank;
|
||||||
|
|
||||||
|
public class BasicRocketContainer extends BaseRocketContainer {
|
||||||
|
public BasicRocketContainer(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSlotCapacity() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BaseRocketFuelTank;
|
||||||
|
|
||||||
|
public class BasicRocketFuelTank extends BaseRocketFuelTank {
|
||||||
|
public BasicRocketFuelTank(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFuelCapacity() {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,9 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
|||||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.neoforged.neoforge.items.ItemStackHandler;
|
import net.neoforged.neoforge.items.ItemStackHandler;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BasicEntityBlock;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
||||||
@@ -34,14 +37,14 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ElectricArcFurnace extends BaseEntityBlock {
|
public class ElectricArcFurnace extends BasicHorizontalEntityBlock {
|
||||||
|
|
||||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||||
public static final BooleanProperty LIT = BlockStateProperties.LIT;
|
public static final BooleanProperty LIT = BlockStateProperties.LIT;
|
||||||
public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED;
|
public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED;
|
||||||
|
|
||||||
public ElectricArcFurnace(Properties properties) {
|
public ElectricArcFurnace(Properties properties) {
|
||||||
super(properties);
|
super(properties, true);
|
||||||
this.registerDefaultState(this.getStateDefinition().any()
|
this.registerDefaultState(this.getStateDefinition().any()
|
||||||
.setValue(FORMED, false));
|
.setValue(FORMED, false));
|
||||||
}
|
}
|
||||||
@@ -75,7 +78,7 @@ public class ElectricArcFurnace extends BaseEntityBlock {
|
|||||||
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) {
|
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) {
|
||||||
|
|
||||||
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
|
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
|
||||||
serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos);
|
serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos);
|
||||||
}
|
}
|
||||||
return InteractionResult.sidedSuccess(level.isClientSide);
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
||||||
}
|
}
|
||||||
@@ -164,25 +167,6 @@ public class ElectricArcFurnace extends BaseEntityBlock {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
|
|
||||||
if (level.isClientSide) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return createTickerHelper(blockEntityType, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), (level1, blockPos, blockState, electricArcFurnaceEntity) -> electricArcFurnaceEntity.tick(level1, blockPos, blockState));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BlockState rotate(BlockState state, Rotation rotation) {
|
|
||||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BlockState mirror(BlockState state, Mirror mirror) {
|
|
||||||
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(FORMED, false);
|
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(FORMED, false);
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModDimensions;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import net.xevianlight.aphelion.util.ModTags;
|
||||||
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class LaunchPad extends Block {
|
||||||
|
|
||||||
|
public static final BooleanProperty NORTH = BlockStateProperties.NORTH;
|
||||||
|
public static final BooleanProperty EAST = BlockStateProperties.EAST;
|
||||||
|
public static final BooleanProperty SOUTH = BlockStateProperties.SOUTH;
|
||||||
|
public static final BooleanProperty WEST = BlockStateProperties.WEST;
|
||||||
|
|
||||||
|
public LaunchPad(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
this.registerDefaultState(this.stateDefinition.any()
|
||||||
|
.setValue(NORTH, false)
|
||||||
|
.setValue(EAST, false)
|
||||||
|
.setValue(SOUTH, false)
|
||||||
|
.setValue(WEST, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(NORTH, EAST, SOUTH, WEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPad(BlockState st) {
|
||||||
|
return st.is(ModTags.Blocks.LAUNCH_PAD); // <- make a tag, recommended
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState withConnections(LevelAccessor level, BlockPos pos, BlockState state) {
|
||||||
|
return state
|
||||||
|
.setValue(NORTH, isPad(level.getBlockState(pos.north())))
|
||||||
|
.setValue(EAST, isPad(level.getBlockState(pos.east())))
|
||||||
|
.setValue(SOUTH, isPad(level.getBlockState(pos.south())))
|
||||||
|
.setValue(WEST, isPad(level.getBlockState(pos.west())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties getProperties() {
|
||||||
|
return Properties
|
||||||
|
.of()
|
||||||
|
.sound(SoundType.STONE)
|
||||||
|
.destroyTime(2f)
|
||||||
|
.explosionResistance(10f)
|
||||||
|
.requiresCorrectToolForDrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item.Properties getItemProperties() {
|
||||||
|
return new Item.Properties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
||||||
|
return withConnections(ctx.getLevel(), ctx.getClickedPos(), this.defaultBlockState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull BlockState updateShape(@NotNull BlockState state, Direction dir, @NotNull BlockState neighborState,
|
||||||
|
@NotNull LevelAccessor level, @NotNull BlockPos pos, @NotNull BlockPos neighborPos) {
|
||||||
|
if (dir.getAxis().isHorizontal()) {
|
||||||
|
return withConnections(level, pos, state);
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +1,53 @@
|
|||||||
package net.xevianlight.aphelion.block.custom;
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BasicEntityBlock;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
|
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class OxygenTestBlock extends Block implements EntityBlock {
|
public class OxygenTestBlock extends BasicEntityBlock {
|
||||||
|
|
||||||
public OxygenTestBlock(Properties properties) {
|
public OxygenTestBlock(Properties properties) {
|
||||||
super(properties);
|
super(properties, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final MapCodec<OxygenTestBlock> CODEC = simpleCodec(OxygenTestBlock::new);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull MapCodec<? extends BaseEntityBlock> codec() {
|
||||||
|
return CODEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
|
public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||||
return new OxygenTestBlockEntity(blockPos, blockState);
|
return new OxygenTestBlockEntity(blockPos, blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Properties getProperties() {
|
public static Properties getProperties() {
|
||||||
return Properties.of();
|
return Properties.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public RenderShape getRenderShape(@NotNull BlockState pState) {
|
||||||
|
return RenderShape.MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove(BlockState state, @NotNull Level level, @NotNull BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
|
if (state.getBlock() != newState.getBlock()) {
|
||||||
|
BlockEntity blockEntity= level.getBlockEntity(pos);
|
||||||
|
if (blockEntity instanceof OxygenTestBlockEntity oxygenTestBlockEntity) {
|
||||||
|
oxygenTestBlockEntity.removeEnclosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onRemove(state, level, pos, newState, movedByPiston);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.InteractionResult;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.*;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.RocketAssemblerBlockEntity;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModDimensions;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import net.xevianlight.aphelion.entites.vehicles.RocketEntity;
|
||||||
|
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class RocketAssembler extends BasicHorizontalEntityBlock {
|
||||||
|
|
||||||
|
public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED;
|
||||||
|
|
||||||
|
public RocketAssembler(Properties properties) {
|
||||||
|
super(properties, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final MapCodec<RocketAssembler> CODEC = simpleCodec(RocketAssembler::new);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull MapCodec<? extends BaseEntityBlock> codec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties getProperties() {
|
||||||
|
return Properties
|
||||||
|
.of()
|
||||||
|
.sound(SoundType.METAL)
|
||||||
|
.destroyTime(2f)
|
||||||
|
.explosionResistance(10f)
|
||||||
|
.requiresCorrectToolForDrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||||
|
return new RocketAssemblerBlockEntity(blockPos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(FORMED, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(FORMED);
|
||||||
|
super.createBlockStateDefinition(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
|
||||||
|
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof RocketAssemblerBlockEntity rocketAssemblerBlockEntity) {
|
||||||
|
RocketEntity rocket = rocketAssemblerBlockEntity.assemble();
|
||||||
|
}
|
||||||
|
return InteractionResult.sidedSuccess(level.isClientSide());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
|
||||||
|
public class RocketSeat extends Block {
|
||||||
|
public RocketSeat(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties getProperties() {
|
||||||
|
return Properties
|
||||||
|
.of()
|
||||||
|
.sound(SoundType.STONE)
|
||||||
|
.destroyTime(2f)
|
||||||
|
.explosionResistance(10f)
|
||||||
|
.requiresCorrectToolForDrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item.Properties getItemProperties() {
|
||||||
|
return new Item.Properties();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.StationFlightComputerBlockEntity;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class StationFlightComputerBlock extends BasicHorizontalEntityBlock {
|
||||||
|
|
||||||
|
public static final MapCodec<StationFlightComputerBlock> CODEC = simpleCodec(StationFlightComputerBlock::new);
|
||||||
|
|
||||||
|
public StationFlightComputerBlock(Properties properties) {
|
||||||
|
super(properties, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull MapCodec<? extends BaseEntityBlock> codec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||||
|
return new StationFlightComputerBlockEntity(blockPos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove(BlockState state, @NotNull Level level, @NotNull BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
|
if (level.getBlockEntity(pos) instanceof StationFlightComputerBlockEntity computerBE) {
|
||||||
|
PartitionData data = computerBE.getData();
|
||||||
|
if (data != null) {
|
||||||
|
data.setTraveling(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.ItemInteractionResult;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||||
|
import net.neoforged.neoforge.common.SoundActions;
|
||||||
|
import net.neoforged.neoforge.fluids.FluidStack;
|
||||||
|
import net.neoforged.neoforge.fluids.FluidType;
|
||||||
|
import net.neoforged.neoforge.fluids.FluidUtil;
|
||||||
|
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.StationEngineBlock;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.StationRocketEngineBlockEntity;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class StationRocketEngineBlock extends StationEngineBlock {
|
||||||
|
public StationRocketEngineBlock(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||||
|
return new StationRocketEngineBlockEntity(blockPos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ItemInteractionResult useItemOn(
|
||||||
|
ItemStack stack,
|
||||||
|
BlockState state,
|
||||||
|
Level level,
|
||||||
|
BlockPos pos,
|
||||||
|
Player player,
|
||||||
|
InteractionHand hand,
|
||||||
|
BlockHitResult hit) {
|
||||||
|
|
||||||
|
// Only intercept on client if holding a fluid container, otherwise let block placement through
|
||||||
|
if (level.isClientSide) {
|
||||||
|
return FluidUtil.getFluidHandler(stack).isPresent()
|
||||||
|
? ItemInteractionResult.SUCCESS
|
||||||
|
: ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockEntity be = level.getBlockEntity(pos);
|
||||||
|
if (!(be instanceof StationRocketEngineBlockEntity engineBE)) {
|
||||||
|
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
IFluidHandler tankHandler = level.getCapability(
|
||||||
|
Capabilities.FluidHandler.BLOCK, pos, state, be, null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tankHandler == null) {
|
||||||
|
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidStack fluidBeforeInteraction = FluidUtil.getFluidContained(player.getItemInHand(hand))
|
||||||
|
.orElse(FluidStack.EMPTY);
|
||||||
|
|
||||||
|
boolean success = FluidUtil.interactWithFluidHandler(player, hand, tankHandler);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
FluidStack tankFluid = tankHandler.getFluidInTank(0);
|
||||||
|
FluidStack relevantFluid = fluidBeforeInteraction.isEmpty() ? tankFluid : fluidBeforeInteraction;
|
||||||
|
|
||||||
|
Aphelion.LOGGER.info("fluidBeforeInteraction: {}", fluidBeforeInteraction);
|
||||||
|
Aphelion.LOGGER.info("tankFluid: {}", tankFluid);
|
||||||
|
Aphelion.LOGGER.info("relevantFluid: {}", relevantFluid);
|
||||||
|
|
||||||
|
if (!relevantFluid.isEmpty()) {
|
||||||
|
FluidType fluidType = relevantFluid.getFluid().getFluidType();
|
||||||
|
SoundEvent sound = fluidBeforeInteraction.isEmpty()
|
||||||
|
? fluidType.getSound(SoundActions.BUCKET_FILL)
|
||||||
|
: fluidType.getSound(SoundActions.BUCKET_EMPTY);
|
||||||
|
|
||||||
|
Aphelion.LOGGER.info("fluidType: {}", fluidType);
|
||||||
|
Aphelion.LOGGER.info("sound: {}", sound);
|
||||||
|
|
||||||
|
if (sound != null) {
|
||||||
|
Aphelion.LOGGER.info("Playing sound!");
|
||||||
|
level.playSound(null, pos, sound, SoundSource.BLOCKS, 1.0F, 1.0F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ItemInteractionResult.CONSUME;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,10 +14,10 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.*;
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
@@ -25,25 +25,25 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
|||||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.neoforged.neoforge.items.ItemStackHandler;
|
import net.neoforged.neoforge.items.ItemStackHandler;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity;
|
import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
|
||||||
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
||||||
import net.xevianlight.aphelion.util.IMultiblockController;
|
import net.xevianlight.aphelion.util.IMultiblockController;
|
||||||
import net.xevianlight.aphelion.util.MultiblockHelper;
|
import net.xevianlight.aphelion.util.MultiblockHelper;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class VacuumArcFurnaceController extends BaseEntityBlock {
|
public class VacuumArcFurnaceController extends BasicHorizontalEntityBlock {
|
||||||
|
|
||||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||||
public static final BooleanProperty LIT = BlockStateProperties.LIT;
|
public static final BooleanProperty LIT = BlockStateProperties.LIT;
|
||||||
public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED;
|
public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED;
|
||||||
|
|
||||||
public VacuumArcFurnaceController(Properties properties) {
|
public VacuumArcFurnaceController(Properties properties) {
|
||||||
super(properties);
|
super(properties, true);
|
||||||
this.registerDefaultState(this.getStateDefinition().any()
|
this.registerDefaultState(this.getStateDefinition().any()
|
||||||
.setValue(FACING, Direction.NORTH)
|
.setValue(FACING, Direction.NORTH)
|
||||||
.setValue(LIT, false)
|
.setValue(LIT, false)
|
||||||
@@ -77,7 +77,7 @@ public class VacuumArcFurnaceController extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) {
|
public @NotNull InteractionResult useWithoutItem(@NotNull BlockState state, Level level, @NotNull BlockPos pos, @NotNull Player player, @NotNull BlockHitResult result) {
|
||||||
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) {
|
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) {
|
||||||
if (vacuumArcFurnaceEntity.isFormed())
|
if (vacuumArcFurnaceEntity.isFormed())
|
||||||
serverPlayer.openMenu(new SimpleMenuProvider(vacuumArcFurnaceEntity, Component.literal("Vacuum Arc Furnace")), pos);
|
serverPlayer.openMenu(new SimpleMenuProvider(vacuumArcFurnaceEntity, Component.literal("Vacuum Arc Furnace")), pos);
|
||||||
@@ -88,21 +88,16 @@ public class VacuumArcFurnaceController extends BaseEntityBlock {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
|
public @Nullable BlockEntity newBlockEntity(@NotNull BlockPos blockPos, @NotNull BlockState blockState) {
|
||||||
return new VacuumArcFurnaceControllerEntity(blockPos, blockState);
|
return new VacuumArcFurnaceControllerEntity(blockPos, blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RenderShape getRenderShape(BlockState pState) {
|
protected void onRemove(BlockState state, Level level, @NotNull BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
return RenderShape.MODEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
|
|
||||||
if (!level.isClientSide && state.getBlock() != newState.getBlock()) {
|
if (!level.isClientSide && state.getBlock() != newState.getBlock()) {
|
||||||
BlockEntity blockEntity= level.getBlockEntity(pos);
|
BlockEntity blockEntity= level.getBlockEntity(pos);
|
||||||
if (blockEntity instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) {
|
if (blockEntity instanceof VacuumArcFurnaceControllerEntity vacuumArcFurnaceEntity) {
|
||||||
if(state.getValue(FORMED)) MultiblockHelper.unformForRemoval(level, state, pos, vacuumArcFurnaceEntity.SHAPE, AphelionBlockStateProperties.FORMED);
|
if(state.getValue(FORMED)) MultiblockHelper.unformForRemoval(level, state, pos, VacuumArcFurnaceControllerEntity.SHAPE, AphelionBlockStateProperties.FORMED);
|
||||||
vacuumArcFurnaceEntity.drops();
|
vacuumArcFurnaceEntity.drops();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +105,7 @@ public class VacuumArcFurnaceController extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
|
protected void onPlace(BlockState state, Level level, @NotNull BlockPos pos, @NotNull BlockState oldState, boolean movedByPiston) {
|
||||||
super.onPlace(state, level, pos, oldState, movedByPiston);
|
super.onPlace(state, level, pos, oldState, movedByPiston);
|
||||||
if (!level.isClientSide() && oldState.getBlock() != state.getBlock()) {
|
if (!level.isClientSide() && oldState.getBlock() != state.getBlock()) {
|
||||||
BlockEntity blockEntity= level.getBlockEntity(pos);
|
BlockEntity blockEntity= level.getBlockEntity(pos);
|
||||||
@@ -120,7 +115,7 @@ public class VacuumArcFurnaceController extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getRedstoneSignalFromItemHandler(@javax.annotation.Nullable ItemStackHandler itemStackHandler, List<Integer> slots) {
|
public static int getRedstoneSignalFromItemHandler(@Nullable ItemStackHandler itemStackHandler, List<Integer> slots) {
|
||||||
if (itemStackHandler == null) {
|
if (itemStackHandler == null) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -141,22 +136,22 @@ public class VacuumArcFurnaceController extends BaseEntityBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isSignalSource(BlockState state) {
|
protected boolean isSignalSource(@NotNull BlockState state) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {
|
protected int getSignal(@NotNull BlockState state, @NotNull BlockGetter level, @NotNull BlockPos pos, @NotNull Direction direction) {
|
||||||
return super.getSignal(state, level, pos, direction);
|
return super.getSignal(state, level, pos, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean hasAnalogOutputSignal(BlockState state) {
|
protected boolean hasAnalogOutputSignal(@NotNull BlockState state) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
|
protected int getAnalogOutputSignal(@NotNull BlockState blockState, Level level, @NotNull BlockPos pos) {
|
||||||
List<Integer> slots = new ArrayList<>();
|
List<Integer> slots = new ArrayList<>();
|
||||||
slots.add(VacuumArcFurnaceControllerEntity.INPUT_SLOT);
|
slots.add(VacuumArcFurnaceControllerEntity.INPUT_SLOT);
|
||||||
slots.add(VacuumArcFurnaceControllerEntity.SECONDARY_INPUT_SLOT);
|
slots.add(VacuumArcFurnaceControllerEntity.SECONDARY_INPUT_SLOT);
|
||||||
@@ -169,25 +164,6 @@ public class VacuumArcFurnaceController extends BaseEntityBlock {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
|
|
||||||
if (level.isClientSide) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return createTickerHelper(blockEntityType, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), (level1, blockPos, blockState, vacuumArcFurnaceEntity) -> vacuumArcFurnaceEntity.tick(level1, blockPos, blockState));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BlockState rotate(BlockState state, Rotation rotation) {
|
|
||||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BlockState mirror(BlockState state, Mirror mirror) {
|
|
||||||
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(FORMED, false);
|
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false).setValue(FORMED, false);
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
|
||||||
|
public class BaseRocketContainer extends Block implements IRocketInventoryUpgrade {
|
||||||
|
public BaseRocketContainer(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties getProperties() {
|
||||||
|
return Properties
|
||||||
|
.of()
|
||||||
|
.sound(SoundType.METAL)
|
||||||
|
.destroyTime(2f)
|
||||||
|
.explosionResistance(10f)
|
||||||
|
.requiresCorrectToolForDrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item.Properties getItemProperties() {
|
||||||
|
return new Item.Properties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSlotCapacity() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
|
||||||
|
public class BaseRocketFuelTank extends Block implements IRocketFuelUpgrade {
|
||||||
|
public BaseRocketFuelTank(Properties properties) {
|
||||||
|
super(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties getProperties() {
|
||||||
|
return Properties
|
||||||
|
.of()
|
||||||
|
.sound(SoundType.METAL)
|
||||||
|
.destroyTime(2f)
|
||||||
|
.explosionResistance(10f)
|
||||||
|
.requiresCorrectToolForDrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item.Properties getItemProperties() {
|
||||||
|
return new Item.Properties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFuelCapacity() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public abstract class BasicEntityBlock extends BaseEntityBlock {
|
||||||
|
|
||||||
|
private final boolean shouldTick;
|
||||||
|
private BlockEntityType<?> entity;
|
||||||
|
|
||||||
|
protected BasicEntityBlock(Properties properties, boolean shouldTick) {
|
||||||
|
super(properties);
|
||||||
|
this.shouldTick = shouldTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull RenderShape getRenderShape(@NotNull BlockState state) {
|
||||||
|
return RenderShape.MODEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Properties getProperties() {
|
||||||
|
return Properties.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Item.Properties getItemProperties() {
|
||||||
|
return new Item.Properties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(@NotNull Level level, @NotNull BlockState state, @NotNull BlockEntityType<T> blockEntityType) {
|
||||||
|
return !shouldTick ? null : (entityLevel, pos, blockState, blockEntity) -> {
|
||||||
|
if (blockEntity instanceof TickableBlockEntity tickable) {
|
||||||
|
if (!tickable.isInitialized()) tickable.firstTick(entityLevel, blockState, pos);
|
||||||
|
|
||||||
|
long time = entityLevel.getGameTime() - pos.hashCode();
|
||||||
|
tickable.tick(entityLevel, time, blockState, pos);
|
||||||
|
|
||||||
|
if (entityLevel.isClientSide()) {
|
||||||
|
tickable.clientTick((ClientLevel) entityLevel, time, blockState, pos);
|
||||||
|
} else {
|
||||||
|
tickable.serverTick((ServerLevel) entityLevel, time, blockState, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRemove(BlockState state, @NotNull Level level, @NotNull BlockPos pos, BlockState newState, boolean movedByPiston) {
|
||||||
|
if (state.getBlock() != newState.getBlock()) {
|
||||||
|
if (level.getBlockEntity(pos) instanceof TickableBlockEntity tickable) {
|
||||||
|
tickable.onRemoved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onRemove(state, level, pos, newState, movedByPiston);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public abstract class BasicHorizontalEntityBlock extends BasicEntityBlock {
|
||||||
|
|
||||||
|
protected BasicHorizontalEntityBlock(Properties properties, boolean shouldTick) {
|
||||||
|
super(properties, shouldTick);
|
||||||
|
this.registerDefaultState(this.getStateDefinition().any());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull BlockState rotate(BlockState state, Rotation rotation) {
|
||||||
|
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull BlockState mirror(BlockState state, Mirror mirror) {
|
||||||
|
return state.rotate(mirror.getRotation(state.getValue(FACING)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
|
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(FACING);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for blocks which should provide energy storage capacity to a rocket.
|
||||||
|
* <p>Note that blocks implementing this interface should not store energy themselves.
|
||||||
|
* Rockets determine their energy capacity from the sum of these blocks installed on them.</p>
|
||||||
|
* <p>Keep in mind that {@code TileEntity} blocks cannot be included in a {@code RocketStructure}.</p>
|
||||||
|
*/
|
||||||
|
public interface IRocketEnergyUpgrade {
|
||||||
|
/**
|
||||||
|
* Used to determine how much FE of energy storage a rocket receives from having this block is installed.
|
||||||
|
*/
|
||||||
|
int getEnergyCapacity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to determine how much FE transfer rate bonus a rocket receives from having this block is installed. This is added onto the base rockets energy transfer limit.
|
||||||
|
*/
|
||||||
|
default int getMaxTransferBonus() {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for blocks which should provide fluid storage capacity to a rocket.
|
||||||
|
* <p>Note that blocks implementing this interface should not store fluids themselves.
|
||||||
|
* Rockets determine their fluid container capacity from the sum of these blocks installed on them.</p>
|
||||||
|
* <p>Keep in mind that {@code TileEntity} blocks cannot be included in a {@code RocketStructure}.</p>
|
||||||
|
*/
|
||||||
|
public interface IRocketFluidUpgrade {
|
||||||
|
/**
|
||||||
|
* Used to determine how many millibuckets of fluid storage a rocket receives from having this block is installed.
|
||||||
|
*/
|
||||||
|
int getFluidCapacity();
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import net.neoforged.neoforge.fluids.capability.templates.FluidTank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for blocks which should provide fuel storage capacity to a rocket.
|
||||||
|
* <p>Note that blocks implementing this interface should not store fuel themselves.
|
||||||
|
* Rockets determine their fuel container capacity from the sum of these blocks installed on them.</p>
|
||||||
|
* <p>Keep in mind that {@code TileEntity} blocks cannot be included in a {@code RocketStructure}.</p>
|
||||||
|
*/
|
||||||
|
public interface IRocketFuelUpgrade {
|
||||||
|
/**
|
||||||
|
* Used to determine how many millibuckets of fuel storage a rocket receives from having this block is installed.
|
||||||
|
*/
|
||||||
|
int getFuelCapacity();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for blocks which should provide item slots to a rockets inventory.
|
||||||
|
* <p>Note that blocks implementing this interface should not store items themselves.
|
||||||
|
* Rockets determine their inventory slot count from the sum of these blocks installed on them.</p>
|
||||||
|
* <p>Keep in mind that {@code TileEntity} blocks cannot be included in a {@code RocketStructure}.</p>
|
||||||
|
*/
|
||||||
|
public interface IRocketInventoryUpgrade {
|
||||||
|
/**
|
||||||
|
* Used to determine how many inventory slots a rocket receives from having this block installed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int getSlotCapacity();
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.BaseEntityBlock;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class StationEngineBlock extends BasicEntityBlock {
|
||||||
|
|
||||||
|
public static final MapCodec<StationEngineBlock> CODEC = simpleCodec(StationEngineBlock::new);
|
||||||
|
|
||||||
|
protected StationEngineBlock(Properties properties) {
|
||||||
|
super(properties, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MapCodec<? extends BaseEntityBlock> codec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package net.xevianlight.aphelion.block.custom.base;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
public interface TickableBlockEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on both the client and server once per tick.
|
||||||
|
*
|
||||||
|
* <p>This is intended for logic that is common to both sides. Side-specific logic
|
||||||
|
* should go in {@link #clientTick(ClientLevel, long, BlockState, BlockPos)} or
|
||||||
|
* {@link #serverTick(ServerLevel, long, BlockState, BlockPos)}.</p>
|
||||||
|
*
|
||||||
|
* @param level the current level
|
||||||
|
* @param time a deterministic per-position tick time (see ticker implementation)
|
||||||
|
* @param state the current block state
|
||||||
|
* @param pos the world position of the block entity
|
||||||
|
*/
|
||||||
|
default void tick (Level level, long time, BlockState state, BlockPos pos) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on the client only once per tick.
|
||||||
|
*
|
||||||
|
* <p>Use this for client-side visual updates, particles, sounds, animation state,
|
||||||
|
* or other logic that must not run on the logical server.</p>
|
||||||
|
*
|
||||||
|
* @param level the client level
|
||||||
|
* @param time a deterministic per-position tick time (see ticker implementation)
|
||||||
|
* @param state the current block state
|
||||||
|
* @param pos the world position of the block entity
|
||||||
|
*/
|
||||||
|
void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs on the server only once per tick.
|
||||||
|
*
|
||||||
|
* <p>Use this for authoritative game logic such as inventory processing, energy
|
||||||
|
* generation/consumption, entity spawning, saving state, and network sync triggers.</p>
|
||||||
|
*
|
||||||
|
* @param level the server level
|
||||||
|
* @param time a deterministic per-position tick time (see ticker implementation)
|
||||||
|
* @param state the current block state
|
||||||
|
* @param pos the world position of the block entity
|
||||||
|
*/
|
||||||
|
void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this object has completed its initialization logic.
|
||||||
|
*
|
||||||
|
* <p>If this method returns {@code false}, {@link #firstTick(Level, BlockState, BlockPos)}
|
||||||
|
* will be invoked at the start of each tick on both the client and server until
|
||||||
|
* initialization is complete.</p>
|
||||||
|
*
|
||||||
|
* <p>Implementations should return {@code true} once initialization has finished
|
||||||
|
* to prevent {@code firstTick} from running again.</p>
|
||||||
|
*
|
||||||
|
* <p>Returns {@code true} if not implemented.</p>
|
||||||
|
*
|
||||||
|
* @return {@code true} if initialization has completed, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
default boolean isInitialized() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs initialization logic for this object.
|
||||||
|
*
|
||||||
|
* <p>This method is called at the start of each tick on both the client and server
|
||||||
|
* whenever {@link #isInitialized()} returns {@code false}. It will continue to be
|
||||||
|
* invoked every tick until initialization is complete.</p>
|
||||||
|
*
|
||||||
|
* <p>Implementations should perform any required setup and ensure that
|
||||||
|
* {@code isInitialized()} returns {@code true} afterward.</p>
|
||||||
|
*
|
||||||
|
* <p>Will never run if {@link #isInitialized()} is not implemented.</p>
|
||||||
|
*
|
||||||
|
* @param level the level the block entity exists in
|
||||||
|
* @param state the current block state
|
||||||
|
* @param pos the world position of the block entity
|
||||||
|
*/
|
||||||
|
void firstTick(Level level, BlockState state, BlockPos pos);
|
||||||
|
|
||||||
|
default void onRemoved() {}
|
||||||
|
}
|
||||||
@@ -142,7 +142,7 @@ public class BaseMultiblockDummyBlockEntity extends BlockEntity implements IMult
|
|||||||
// Force rerender on client
|
// Force rerender on client
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
|
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
|
||||||
requestModelDataUpdate(); // if you rely on model data
|
requestModelDataUpdate(); // if you rely on model partitionData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ public class BaseMultiblockDummyBlockEntity extends BlockEntity implements IMult
|
|||||||
setChanged();
|
setChanged();
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
|
level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
|
||||||
requestModelDataUpdate(); // only if you use model data
|
requestModelDataUpdate(); // only if you use model partitionData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.xevianlight.aphelion.block.entity.custom;
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
@@ -8,6 +9,7 @@ import net.minecraft.network.chat.Component;
|
|||||||
import net.minecraft.network.protocol.Packet;
|
import net.minecraft.network.protocol.Packet;
|
||||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.Containers;
|
import net.minecraft.world.Containers;
|
||||||
import net.minecraft.world.MenuProvider;
|
import net.minecraft.world.MenuProvider;
|
||||||
import net.minecraft.world.SimpleContainer;
|
import net.minecraft.world.SimpleContainer;
|
||||||
@@ -25,6 +27,7 @@ import net.neoforged.neoforge.energy.IEnergyStorage;
|
|||||||
import net.neoforged.neoforge.items.IItemHandler;
|
import net.neoforged.neoforge.items.IItemHandler;
|
||||||
import net.neoforged.neoforge.items.ItemStackHandler;
|
import net.neoforged.neoforge.items.ItemStackHandler;
|
||||||
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
|
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
|
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlocks;
|
import net.xevianlight.aphelion.core.init.ModBlocks;
|
||||||
@@ -39,7 +42,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvider {
|
public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvider, TickableBlockEntity, IArcFurnaceLike {
|
||||||
|
|
||||||
private final int SIZE = 4;
|
private final int SIZE = 4;
|
||||||
private int ENERGY_CAPACITY = 64000;
|
private int ENERGY_CAPACITY = 64000;
|
||||||
@@ -101,10 +104,8 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
// }
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
public void tick(Level level, BlockPos pos, BlockState blockState) {
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
if (!blockState.getValue(AphelionBlockStateProperties.FORMED))
|
|
||||||
return;
|
|
||||||
|
|
||||||
chargeFromItem();
|
chargeFromItem();
|
||||||
|
|
||||||
@@ -114,8 +115,8 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
// Recipe detected! We have enough energy to process
|
// Recipe detected! We have enough energy to process
|
||||||
progress++;
|
progress++;
|
||||||
useEnergy();
|
useEnergy();
|
||||||
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true));
|
level.setBlockAndUpdate(pos, state.setValue(ElectricArcFurnace.LIT, true));
|
||||||
setChanged(level, pos, blockState);
|
setChanged(level, pos, state);
|
||||||
|
|
||||||
if (hasCraftingFinished()) {
|
if (hasCraftingFinished()) {
|
||||||
outputBlastingResult(INPUT_SLOT, OUTPUT_SLOT);
|
outputBlastingResult(INPUT_SLOT, OUTPUT_SLOT);
|
||||||
@@ -123,14 +124,14 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
}
|
}
|
||||||
} else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
|
} else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
|
||||||
// Recipe detected but we ran out of power
|
// Recipe detected but we ran out of power
|
||||||
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, state.setValue(ElectricArcFurnace.LIT, false));
|
||||||
setChanged(level, pos, blockState);
|
setChanged(level, pos, state);
|
||||||
progress = progress > 0 ? progress - 1 : 0;
|
progress = progress > 0 ? progress - 1 : 0;
|
||||||
} else {
|
} else {
|
||||||
// Invalid recipe
|
// Invalid recipe
|
||||||
resetProgress();
|
resetProgress();
|
||||||
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, state.setValue(ElectricArcFurnace.LIT, false));
|
||||||
setChanged(level, pos, blockState);
|
setChanged(level, pos, state);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Secondary slot is NOT empty, try alloying recipes
|
// Secondary slot is NOT empty, try alloying recipes
|
||||||
@@ -139,8 +140,8 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
// Alloy recipe detected! We have enough energy to process
|
// Alloy recipe detected! We have enough energy to process
|
||||||
progress++;
|
progress++;
|
||||||
useEnergy();
|
useEnergy();
|
||||||
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, true));
|
level.setBlockAndUpdate(pos, state.setValue(ElectricArcFurnace.LIT, true));
|
||||||
setChanged(level, pos, blockState);
|
setChanged(level, pos, state);
|
||||||
|
|
||||||
if (hasCraftingFinished()) {
|
if (hasCraftingFinished()) {
|
||||||
outputAlloyingResult(INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT);
|
outputAlloyingResult(INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT);
|
||||||
@@ -148,19 +149,17 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Recipe detected but we ran out of power
|
// Recipe detected but we ran out of power
|
||||||
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, state.setValue(ElectricArcFurnace.LIT, false));
|
||||||
setChanged(level, pos, blockState);
|
setChanged(level, pos, state);
|
||||||
progress = progress > 0 ? progress - 1 : 0;
|
progress = progress > 0 ? progress - 1 : 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Invalid recipe
|
// Invalid recipe
|
||||||
resetProgress();
|
resetProgress();
|
||||||
level.setBlockAndUpdate(pos, blockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, state.setValue(ElectricArcFurnace.LIT, false));
|
||||||
setChanged(level, pos, blockState);
|
setChanged(level, pos, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void chargeFromItem() {
|
private void chargeFromItem() {
|
||||||
@@ -310,6 +309,11 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
|
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IEnergyStorage getTrueEnergyStorage() {
|
||||||
|
return ENERGY_STORAGE;
|
||||||
|
}
|
||||||
|
|
||||||
private final IItemHandler fullHandler = new SidedSlotHandler(inventory, new int[]{INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT, ENERGY_SLOT}, true, true);
|
private final IItemHandler fullHandler = new SidedSlotHandler(inventory, new int[]{INPUT_SLOT, SECONDARY_INPUT_SLOT, OUTPUT_SLOT, ENERGY_SLOT}, true, true);
|
||||||
private final IItemHandler emptyJeiHandler = new SidedSlotHandler(inventory, new int[]{}, false, false);
|
private final IItemHandler emptyJeiHandler = new SidedSlotHandler(inventory, new int[]{}, false, false);
|
||||||
private final IItemHandler inputHandler = new SidedSlotHandler(inventory, new int[]{0,1}, true, true);
|
private final IItemHandler inputHandler = new SidedSlotHandler(inventory, new int[]{0,1}, true, true);
|
||||||
@@ -345,6 +349,11 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
return inventory;
|
return inventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModEnergyStorage getEnergy() {
|
||||||
|
return ENERGY_STORAGE;
|
||||||
|
}
|
||||||
|
|
||||||
public void drops() {
|
public void drops() {
|
||||||
SimpleContainer inv = new SimpleContainer(inventory.getSlots());
|
SimpleContainer inv = new SimpleContainer(inventory.getSlots());
|
||||||
for(int i = 0; i < inventory.getSlots(); i++) {
|
for(int i = 0; i < inventory.getSlots(); i++) {
|
||||||
@@ -408,4 +417,14 @@ public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvide
|
|||||||
new MultiblockHelper.ShapePart(new BlockPos(3,0,3), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)),
|
new MultiblockHelper.ShapePart(new BlockPos(3,0,3), s -> s.is(ModBlocks.ARC_FURNACE_CASING_BLOCK)),
|
||||||
// new MultiblockHelper.ShapePart(new BlockPos(-1,0,0), s -> s.is(Blocks.AMETHYST_BLOCK))
|
// new MultiblockHelper.ShapePart(new BlockPos(-1,0,0), s -> s.is(Blocks.AMETHYST_BLOCK))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstTick(Level level, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.inventory.ContainerData;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.neoforged.neoforge.energy.IEnergyStorage;
|
||||||
|
import net.neoforged.neoforge.items.ItemStackHandler;
|
||||||
|
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
|
||||||
|
|
||||||
|
|
||||||
|
public interface IArcFurnaceLike {
|
||||||
|
ItemStackHandler getInventory();
|
||||||
|
ModEnergyStorage getEnergy();
|
||||||
|
|
||||||
|
void sendUpdate();
|
||||||
|
|
||||||
|
BlockState getBlockState();
|
||||||
|
|
||||||
|
IEnergyStorage getTrueEnergyStorage();
|
||||||
|
}
|
||||||
@@ -1,29 +1,38 @@
|
|||||||
package net.xevianlight.aphelion.block.entity.custom;
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.core.Vec3i;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import static net.xevianlight.aphelion.Aphelion.LOGGER;
|
import static net.xevianlight.aphelion.Aphelion.LOGGER;
|
||||||
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.EnvironmentSavedData;
|
||||||
|
import net.xevianlight.aphelion.util.FloodFill3D;
|
||||||
import org.openjdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
|
import org.openjdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class OxygenTestBlockEntity extends BlockEntity {
|
public class OxygenTestBlockEntity extends BlockEntity implements TickableBlockEntity {
|
||||||
|
|
||||||
|
|
||||||
public OxygenTestBlockEntity(BlockPos pos, BlockState blockState) {
|
public OxygenTestBlockEntity(BlockPos pos, BlockState blockState) {
|
||||||
super(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), pos, blockState);
|
super(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), pos, blockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return this.isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
private LevelChunk lastChunk;
|
private LevelChunk lastChunk;
|
||||||
private int lastChunkX = Integer.MIN_VALUE, lastChunkZ = Integer.MIN_VALUE;
|
private int lastChunkX = Integer.MIN_VALUE, lastChunkZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
@@ -43,11 +52,25 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
return level != null && fastBlockState(pos).isAir();
|
return level != null && fastBlockState(pos).isAir();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int MAX_RANGE = 100;
|
public static final int MAX_RANGE = 16;
|
||||||
public boolean isInRange(BlockPos pos1, BlockPos pos2) {
|
public boolean isInRange(BlockPos pos1, BlockPos pos2) {
|
||||||
return Math.abs(pos1.getX() - pos2.getX()) + Math.abs(pos1.getY() - pos2.getY()) + Math.abs(pos1.getZ() - pos2.getZ()) <= MAX_RANGE;
|
return Math.abs(pos1.getX() - pos2.getX()) + Math.abs(pos1.getY() - pos2.getY()) + Math.abs(pos1.getZ() - pos2.getZ()) <= MAX_RANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeEnclosed() {
|
||||||
|
if (enclosedCache != null) {
|
||||||
|
var singleplayerServer = Minecraft.getInstance().getSingleplayerServer();
|
||||||
|
if (singleplayerServer != null) {
|
||||||
|
var serverLevel = singleplayerServer.getLevel(level.dimension());
|
||||||
|
if (serverLevel != null) {
|
||||||
|
var savedData = EnvironmentSavedData.get(serverLevel);
|
||||||
|
savedData.resetOxygen(serverLevel, enclosedCache);
|
||||||
|
enclosedCache = Set.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 256*256*256 grid of booleans
|
/// 256*256*256 grid of booleans
|
||||||
private class BigBoolGrid {
|
private class BigBoolGrid {
|
||||||
int bitsSize;
|
int bitsSize;
|
||||||
@@ -94,7 +117,7 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
// wrapping order is X->Z->Y, so we go along the X axis,
|
// wrapping order is X->Z->Y, so we go along the X axis,
|
||||||
// wrap to the Z axis to make a square, and once the square is full
|
// wrap to the Z axis to make a square, and once the square is full
|
||||||
// we step up once along the Y axis.
|
// we step up once along the Y axis.
|
||||||
int bitPos = inX & 7; // bottom 4 bits of X is bit pos
|
int bitPos = inX & 15; // bottom 4 bits of X is bit pos
|
||||||
int bit = (1 << bitPos);
|
int bit = (1 << bitPos);
|
||||||
|
|
||||||
// First (bitsSize-4) bits are for X, next (bitsSize) bits are for Z, next (bitsSize) bits are for Y
|
// First (bitsSize-4) bits are for X, next (bitsSize) bits are for Z, next (bitsSize) bits are for Y
|
||||||
@@ -106,22 +129,29 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BlockPos> enclosedCache;
|
private Set<BlockPos> enclosedCache;
|
||||||
public List<BlockPos> getEnclosedBlocks() {
|
|
||||||
if (level == null) return List.of();
|
|
||||||
if (enclosedCache != null) return enclosedCache;
|
public Set<BlockPos> getEnclosedCache() {
|
||||||
|
return enclosedCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<BlockPos> getEnclosedBlocks() {
|
||||||
|
if (level == null) return Set.of();
|
||||||
|
// if (enclosedCache != null) return enclosedCache;
|
||||||
|
|
||||||
long start = System.nanoTime();
|
long start = System.nanoTime();
|
||||||
List<BlockPos> enclosedBlocks = new ArrayList<>();
|
Set<BlockPos> enclosedBlocks = FloodFill3D.run(level, getBlockPos(), 6000, FloodFill3D.TEST_FULL_SEAL, true);
|
||||||
// make this bitch BIIIIIG
|
|
||||||
BigBoolGrid seen = new BigBoolGrid(8, this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ());
|
|
||||||
|
|
||||||
// It's... a reasonable assumption that we won't have to include more blocks at once than the area of a sphere?
|
// // make this bitch BIIIIIG
|
||||||
// maybe a bit more, IDK how exactly it scales to blocks.
|
// BigBoolGrid seen = new BigBoolGrid(8, this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ());
|
||||||
Stack<BlockPos> stack = new Stack<>();
|
//
|
||||||
Stack<Integer> radiusStack = new Stack<>();
|
// // It's... a reasonable assumption that we won't have to include more blocks at once than the area of a sphere?
|
||||||
|
// // maybe a bit more, IDK how exactly it scales to blocks.
|
||||||
stack.add(this.getBlockPos());
|
// Stack<BlockPos> stack = new Stack<>();
|
||||||
|
// Stack<Integer> radiusStack = new Stack<>();
|
||||||
|
//
|
||||||
|
// stack.add(this.getBlockPos());
|
||||||
|
|
||||||
// Do flood fill out from this block
|
// Do flood fill out from this block
|
||||||
// Push on the top of the stack (newest), pop from the bottom of the stack (oldest).
|
// Push on the top of the stack (newest), pop from the bottom of the stack (oldest).
|
||||||
@@ -130,21 +160,48 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
// and you'd see that every pos of layer 1 is together, then layer 2, then layer 3...
|
// and you'd see that every pos of layer 1 is together, then layer 2, then layer 3...
|
||||||
|
|
||||||
|
|
||||||
BlockPos ourPos = getBlockPos();
|
// BlockPos ourPos = getBlockPos();
|
||||||
while (!stack.isEmpty()) {
|
// while (!stack.isEmpty()) {
|
||||||
BlockPos spreadFromPos = stack.pop();
|
// BlockPos spreadFromPos = stack.pop();
|
||||||
for (Direction d : Direction.values()) {
|
// for (Direction d : Direction.values()) {
|
||||||
BlockPos relativePos = spreadFromPos.relative(d);
|
// BlockPos relativePos = spreadFromPos.relative(d);
|
||||||
|
//
|
||||||
|
// if (isInRange(relativePos, ourPos) && canSpreadTo(relativePos)) {
|
||||||
|
// // seen.add runs seen.contains under the hood,
|
||||||
|
// // + this is the most expensive operation.
|
||||||
|
// // should save a lot of time!
|
||||||
|
// if (seen.add(relativePos.getX(), relativePos.getY(), relativePos.getZ())) {
|
||||||
|
// enclosedBlocks.add(relativePos);
|
||||||
|
// stack.add(relativePos);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (isInRange(relativePos, ourPos) && canSpreadTo(relativePos)) {
|
var singleplayerServer = Minecraft.getInstance().getSingleplayerServer();
|
||||||
// seen.add runs seen.contains under the hood,
|
if (singleplayerServer != null) {
|
||||||
// + this is the most expensive operation.
|
var serverLevel = singleplayerServer.getLevel(level.dimension());
|
||||||
// should save a lot of time!
|
if (serverLevel != null) {
|
||||||
if (seen.add(relativePos.getX(), relativePos.getY(), relativePos.getZ())) {
|
|
||||||
enclosedBlocks.add(relativePos);
|
// Build a set of longs for the newly computed blocks (order-independent)
|
||||||
stack.add(relativePos);
|
boolean changed = isChanged(enclosedBlocks);
|
||||||
|
// boolean changed = false;
|
||||||
|
if (changed) {
|
||||||
|
var savedData = EnvironmentSavedData.get(serverLevel);
|
||||||
|
|
||||||
|
// Revert old affected area back to defaults
|
||||||
|
if (enclosedCache != null) {
|
||||||
|
savedData.resetOxygen(serverLevel, enclosedCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply oxygen to new affected area
|
||||||
|
savedData.setOxygen(serverLevel, enclosedBlocks, true);
|
||||||
|
|
||||||
|
LOGGER.info("Saved data for {} blocks to leveldata", enclosedBlocks.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the cache no matter what (so next compare is correct)
|
||||||
|
enclosedCache = enclosedBlocks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
long durationNanos = System.nanoTime() - start;
|
long durationNanos = System.nanoTime() - start;
|
||||||
@@ -152,12 +209,62 @@ public class OxygenTestBlockEntity extends BlockEntity {
|
|||||||
LOGGER.info("Flood fill completed in {}µs, {} blocks at {}µs/block", durationMicros, enclosedBlocks.size(), durationMicros / enclosedBlocks.size());
|
LOGGER.info("Flood fill completed in {}µs, {} blocks at {}µs/block", durationMicros, enclosedBlocks.size(), durationMicros / enclosedBlocks.size());
|
||||||
|
|
||||||
enclosedCache = enclosedBlocks;
|
enclosedCache = enclosedBlocks;
|
||||||
return enclosedBlocks;
|
|
||||||
|
return enclosedCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isChanged(Set<BlockPos> enclosedBlocks) {
|
||||||
|
LongOpenHashSet newSet = new LongOpenHashSet(enclosedBlocks.size());
|
||||||
|
for (BlockPos p : enclosedBlocks) {
|
||||||
|
newSet.add(p.asLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a set of longs for the cached blocks (if any)
|
||||||
|
LongOpenHashSet oldSet = null;
|
||||||
|
if (enclosedCache != null) {
|
||||||
|
oldSet = new LongOpenHashSet(enclosedCache.size());
|
||||||
|
for (BlockPos p : enclosedCache) {
|
||||||
|
oldSet.add(p.asLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only save if the set of affected blocks has changed
|
||||||
|
boolean changed = (oldSet == null) || !oldSet.equals(newSet);
|
||||||
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void helper() {
|
private void helper() {
|
||||||
var myVar = new BlockPos(1, 1, 1).hashCode();
|
var myVar = new BlockPos(1, 1, 1).hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ticks = 0;
|
||||||
|
int refreshAfter = 20;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState blockState, BlockPos pos) {
|
||||||
|
ticks++;
|
||||||
|
if (ticks >= refreshAfter) {
|
||||||
|
getEnclosedBlocks();
|
||||||
|
ticks = 0;
|
||||||
|
|
||||||
|
// UNCOMMENT FOR DEBUG ONLY!!! EXTREMELY TPS INTENSIVE!!!
|
||||||
|
// EnvironmentSavedData.refreshFromIntegratedServerIfNeeded(Minecraft.getInstance(), 64, 10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(Level entityLevel, long time, BlockState blockState, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstTick(Level level, BlockState state, BlockPos pos) {
|
||||||
|
this.isInitialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,476 @@
|
|||||||
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.Connection;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModBlocks;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModDimensions;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import net.xevianlight.aphelion.entites.vehicles.RocketEntity;
|
||||||
|
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
||||||
|
import net.xevianlight.aphelion.util.ModTags;
|
||||||
|
import net.xevianlight.aphelion.util.RocketStructure;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RocketAssemblerBlockEntity extends BlockEntity implements TickableBlockEntity {
|
||||||
|
|
||||||
|
Direction facing;
|
||||||
|
BlockPos padScanStart = BlockPos.ZERO;
|
||||||
|
private PadInfo padBounds;
|
||||||
|
|
||||||
|
private @Nullable PartitionData data;
|
||||||
|
|
||||||
|
public @Nullable PadInfo getPadBounds() {
|
||||||
|
return padBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized;
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RocketAssemblerBlockEntity(BlockPos pos, BlockState blockState) {
|
||||||
|
super(ModBlockEntities.ROCKET_ASSEMBLER_BLOCK_ENTITY.get(), pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PadInfo(BlockPos min, BlockPos max) {
|
||||||
|
public int getVolume() {
|
||||||
|
int dx = max.getX() - min.getX() + 1;
|
||||||
|
int dy = max.getY() - min.getY() + 1;
|
||||||
|
int dz = max.getZ() - min.getZ() + 1;
|
||||||
|
|
||||||
|
return dx * dy * dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getCenter() {
|
||||||
|
int centerX = (min.getX() + max.getX()) / 2;
|
||||||
|
int centerZ = (min.getZ() + max.getZ()) / 2;
|
||||||
|
|
||||||
|
// bottom Y level
|
||||||
|
int y = min.getY();
|
||||||
|
|
||||||
|
return new BlockPos(centerX, y, centerZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Block TOWER_BLOCK = ModBlocks.BLOCK_STEEL.get();
|
||||||
|
public BlockPos towerBasePos;
|
||||||
|
|
||||||
|
private boolean connected(BlockState state, Direction dir) {
|
||||||
|
return switch (dir) {
|
||||||
|
case NORTH -> state.getValue(BlockStateProperties.NORTH);
|
||||||
|
case SOUTH -> state.getValue(BlockStateProperties.SOUTH);
|
||||||
|
case EAST -> state.getValue(BlockStateProperties.EAST);
|
||||||
|
case WEST -> state.getValue(BlockStateProperties.WEST);
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable PadInfo getPlatformViaFill() {
|
||||||
|
if (level == null) return null;
|
||||||
|
|
||||||
|
BlockPos start = this.padScanStart;
|
||||||
|
if (!isPad(level.getBlockState(start))) return null;
|
||||||
|
|
||||||
|
|
||||||
|
final int y = start.getY();
|
||||||
|
int towerHeight = 0;
|
||||||
|
|
||||||
|
ArrayDeque<BlockPos> queue = new ArrayDeque<>();
|
||||||
|
LongOpenHashSet visited = new LongOpenHashSet();
|
||||||
|
|
||||||
|
queue.add(start);
|
||||||
|
visited.add(start.asLong());
|
||||||
|
|
||||||
|
final Direction[] CARDINALS = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST};
|
||||||
|
// When the $#$# are we going to have a rocket larger than 32x32... don't...
|
||||||
|
final int MAX_PAD_BLOCKS = 1024;
|
||||||
|
|
||||||
|
boolean towerFound = false;
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
BlockPos p = queue.removeFirst();
|
||||||
|
BlockState s = level.getBlockState(p);
|
||||||
|
|
||||||
|
// We trust block states entirely. If a block state says it has a pad in a direction and is wrong, something else has failed. To minimize level checks and have this run as fast as possible, I won't compare the block
|
||||||
|
// if (!isPad(s)) continue;
|
||||||
|
|
||||||
|
for (Direction d : CARDINALS) {
|
||||||
|
// Only keep going if the block claims to have a neighbor in that direction
|
||||||
|
BlockPos n = p.relative(d);
|
||||||
|
|
||||||
|
if (!connected(s, d)) {
|
||||||
|
if (isTower(level.getBlockState(n))) {
|
||||||
|
if (!towerFound) {
|
||||||
|
towerBasePos = n;
|
||||||
|
towerFound = true;
|
||||||
|
} else if (!n.equals(towerBasePos)) {
|
||||||
|
Aphelion.LOGGER.warn("Multiple towers found, rocket pad invalid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited.contains(n.asLong())) continue; // skip if we've already seen this block
|
||||||
|
|
||||||
|
visited.add(n.asLong());
|
||||||
|
queue.addLast(n);
|
||||||
|
|
||||||
|
if (visited.size() > MAX_PAD_BLOCKS) return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pads missing a tower should be invalid
|
||||||
|
if (!towerFound || towerBasePos == null) return null;
|
||||||
|
towerHeight = getTowerHeight(level, towerBasePos);
|
||||||
|
if (towerHeight <= 0) return null;
|
||||||
|
|
||||||
|
int minX = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE;
|
||||||
|
int minZ = Integer.MAX_VALUE, maxZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
for (Long p : visited) {
|
||||||
|
minX = Math.min(minX, BlockPos.of(p).getX());
|
||||||
|
maxX = Math.max(maxX, BlockPos.of(p).getX());
|
||||||
|
minZ = Math.min(minZ, BlockPos.of(p).getZ());
|
||||||
|
maxZ = Math.max(maxZ, BlockPos.of(p).getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = (maxX - minX) + 1;
|
||||||
|
int length = (maxZ - minZ) + 1;
|
||||||
|
|
||||||
|
// SQUARE AND SOLID??? The math works out here that this will only be true if the number of blocks found matches the side lengths squared. We don't need to check for holes manually!
|
||||||
|
if (visited.size() != length * width || length != width) return null;
|
||||||
|
|
||||||
|
return new PadInfo(new BlockPos(minX, y + 1, minZ), new BlockPos(maxX, y + towerHeight, maxZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getTowerHeight(@NotNull Level level, @NotNull BlockPos base) {
|
||||||
|
int h = 0;
|
||||||
|
BlockPos p = base.above();
|
||||||
|
|
||||||
|
while (isTower(level.getBlockState(p))) {
|
||||||
|
h++;
|
||||||
|
p = p.above();
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockPos seatPos;
|
||||||
|
|
||||||
|
public @Nullable RocketStructure scan() {
|
||||||
|
if (level == null) return null;
|
||||||
|
|
||||||
|
seatPos = null;
|
||||||
|
|
||||||
|
PadInfo bounds = padBounds;
|
||||||
|
if (bounds == null) return null;
|
||||||
|
|
||||||
|
|
||||||
|
BlockPos min = bounds.min();
|
||||||
|
BlockPos max = bounds.max();
|
||||||
|
|
||||||
|
// Find seat, every rocket must have a seat and all blocks in the rocket must be attached to it
|
||||||
|
for (int y = min.getY(); y <= max.getY(); y++) {
|
||||||
|
for (int x = min.getX(); x <= max.getX(); x++) {
|
||||||
|
for (int z = min.getZ(); z <= max.getZ(); z++) {
|
||||||
|
BlockPos p = new BlockPos(x, y, z);
|
||||||
|
BlockState st = level.getBlockState(p);
|
||||||
|
|
||||||
|
if (!st.is(ModTags.Blocks.ROCKET_SEAT)) continue;
|
||||||
|
|
||||||
|
if (seatPos != null && !seatPos.equals(p)) {
|
||||||
|
Aphelion.LOGGER.warn("Rocket scan failed: multiple seats found");
|
||||||
|
seatPos = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
seatPos = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seatPos == null) {
|
||||||
|
Aphelion.LOGGER.warn("Rocket scan failed: no seat found");
|
||||||
|
seatPos = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Direction[] DIRS = Direction.values();
|
||||||
|
|
||||||
|
ArrayDeque<BlockPos> queue = new ArrayDeque<>();
|
||||||
|
LongOpenHashSet visited = new LongOpenHashSet();
|
||||||
|
|
||||||
|
queue.add(seatPos);
|
||||||
|
visited.add(seatPos.asLong());
|
||||||
|
|
||||||
|
RocketStructure structure = new RocketStructure(s -> {});
|
||||||
|
structure.addSeatOffset(0,0,0);
|
||||||
|
|
||||||
|
final int MAX_ROCKET_BLOCKS = 1000;
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
BlockPos p = queue.removeFirst();
|
||||||
|
BlockState st = level.getBlockState(p);
|
||||||
|
|
||||||
|
if (!within(bounds, p)) continue;
|
||||||
|
|
||||||
|
// Any block cases we should IGNORE
|
||||||
|
if (st.isAir()) continue; // ignore air
|
||||||
|
if (isPad(st)) continue; // ignore the pad
|
||||||
|
if (isTower(st)) continue; // ignore the tower
|
||||||
|
if (p.equals(this.worldPosition)) continue; // ignore the assembler
|
||||||
|
|
||||||
|
// Reject block entities
|
||||||
|
if (st.hasBlockEntity() || level.getBlockEntity(p) != null) {
|
||||||
|
Aphelion.LOGGER.warn("Rocket scan failed: found block entity at {}", p);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dx = p.getX() - seatPos.getX();
|
||||||
|
int dy = p.getY() - seatPos.getY();
|
||||||
|
int dz = p.getZ() - seatPos.getZ();
|
||||||
|
|
||||||
|
if (!fitsSignedByte(dx) || !fitsSignedByte(dy) || !fitsSignedByte(dz)) {
|
||||||
|
Aphelion.LOGGER.warn("Rocket scan failed: structure too large to pack at {} (dx={},dy={},dz={})", p, dx, dy, dz);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All checks succeeded, add the block to the rocket
|
||||||
|
structure.add(dx, dy, dz, st);
|
||||||
|
|
||||||
|
// Explore neighbors
|
||||||
|
for (Direction d : DIRS) {
|
||||||
|
BlockPos n = p.relative(d);
|
||||||
|
|
||||||
|
if (!within(bounds, n)) continue; // Skip neighbor outside of rocket assembler bounds
|
||||||
|
|
||||||
|
long key = n.asLong();
|
||||||
|
if (visited.contains(key)) continue; // Skip visited blocks
|
||||||
|
|
||||||
|
BlockState ns = level.getBlockState(n);
|
||||||
|
|
||||||
|
if (ns.isAir()) continue;
|
||||||
|
if (isPad(ns)) continue;
|
||||||
|
if (isTower(ns)) continue;
|
||||||
|
if (n.equals(this.worldPosition)) continue;
|
||||||
|
|
||||||
|
visited.add(key);
|
||||||
|
queue.addLast(n);
|
||||||
|
|
||||||
|
if (visited.size() >= MAX_ROCKET_BLOCKS) {
|
||||||
|
Aphelion.LOGGER.warn("Rocket scan failed: exceeded max blocks ({})", MAX_ROCKET_BLOCKS);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (structure.size() == 0) return null;
|
||||||
|
return structure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable RocketEntity assemble() {
|
||||||
|
if (level == null) return null;
|
||||||
|
RocketStructure structure = scan();
|
||||||
|
RocketEntity rocket = null;
|
||||||
|
|
||||||
|
var rockets = getRocketsInPad();
|
||||||
|
if (rockets.size() == 1) rocket = rockets.getFirst();
|
||||||
|
if (rocket != null)
|
||||||
|
rocket.disassemble();
|
||||||
|
|
||||||
|
if (structure != null && seatPos != null) {
|
||||||
|
RocketStructure.clearCaptured(level, seatPos, structure);
|
||||||
|
rocket = RocketEntity.spawnRocket(level, seatPos, structure);
|
||||||
|
Aphelion.LOGGER.info("Spawn rocket result: {}", rocket);
|
||||||
|
}
|
||||||
|
return rocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean within(PadInfo pad, BlockPos p) {
|
||||||
|
BlockPos min = pad.min;
|
||||||
|
BlockPos max = pad.max;
|
||||||
|
|
||||||
|
return p.getX() >= min.getX() && p.getX() <= max.getX()
|
||||||
|
&& p.getY() >= min.getY() && p.getY() <= max.getY()
|
||||||
|
&& p.getZ() >= min.getZ() && p.getZ() <= max.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean fitsSignedByte(int v) {
|
||||||
|
return v >= -128 && v <= 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
PadInfo newBounds = getPlatformViaFill();
|
||||||
|
setPadBoundsAndSync(newBounds);
|
||||||
|
|
||||||
|
boolean formed = newBounds != null;
|
||||||
|
if (state.getValue(AphelionBlockStateProperties.FORMED) != formed) {
|
||||||
|
level.setBlockAndUpdate(pos, state.setValue(AphelionBlockStateProperties.FORMED, formed));
|
||||||
|
if (data != null) {
|
||||||
|
if (formed) {
|
||||||
|
data.addLandingPadController(pos);
|
||||||
|
} else {
|
||||||
|
data.removeLandingPadController(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstTick(Level level, BlockState state, BlockPos pos) {
|
||||||
|
if (level.isClientSide()) return;
|
||||||
|
|
||||||
|
facing = getBlockState().getValue(BlockStateProperties.HORIZONTAL_FACING);
|
||||||
|
padScanStart = getBlockPos().mutable().below().relative(facing.getOpposite());
|
||||||
|
|
||||||
|
if (level instanceof ServerLevel serverLevel) {
|
||||||
|
if (serverLevel.dimension() == ModDimensions.SPACE) {
|
||||||
|
data = SpacePartitionSavedData.get(serverLevel).getDataForBlockPos(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoved() {
|
||||||
|
if (data == null) return;
|
||||||
|
data.removeLandingPadController(worldPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPad(BlockState s) {
|
||||||
|
return s.is(ModTags.Blocks.LAUNCH_PAD); // or s.getBlock() == ModBlocks.PAD.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTower(BlockState s) {
|
||||||
|
return s.is(TOWER_BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull List<RocketEntity> getRocketsInPad() {
|
||||||
|
if (level == null || padBounds == null) return List.of();
|
||||||
|
|
||||||
|
AABB padBox = new AABB(
|
||||||
|
padBounds.min().getX(),
|
||||||
|
padBounds.min().getY(),
|
||||||
|
padBounds.min().getZ(),
|
||||||
|
padBounds.max().getX() + 1,
|
||||||
|
padBounds.max().getY() + 1,
|
||||||
|
padBounds.max().getZ() + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
var rockets = new ArrayList<>(level.getEntitiesOfClass(RocketEntity.class, padBox.inflate(0.2)));
|
||||||
|
|
||||||
|
List<RocketEntity> found = new java.util.ArrayList<>(List.of());
|
||||||
|
|
||||||
|
for (RocketEntity rocket : rockets) {
|
||||||
|
AABB rocketBox = rocket.getBoundingBox();
|
||||||
|
if (!isFullyInside(padBox, rocketBox)) continue;
|
||||||
|
|
||||||
|
found.add(rocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isFullyInside(AABB outer, AABB inner) {
|
||||||
|
return inner.minX >= outer.minX && inner.maxX <= outer.maxX
|
||||||
|
&& inner.minY >= outer.minY && inner.maxY <= outer.maxY
|
||||||
|
&& inner.minZ >= outer.minZ && inner.maxZ <= outer.maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
|
||||||
|
super.saveAdditional(tag, registries);
|
||||||
|
|
||||||
|
PadInfo pad = this.padBounds;
|
||||||
|
if (pad != null) {
|
||||||
|
tag.putLong("PadMin", padBounds.min().asLong());
|
||||||
|
tag.putLong("PadMax", padBounds.max().asLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
|
||||||
|
super.loadAdditional(tag, registries);
|
||||||
|
|
||||||
|
if (tag.contains("PadMin") && tag.contains("PadMax")) {
|
||||||
|
BlockPos min = BlockPos.of(tag.getLong("PadMin"));
|
||||||
|
BlockPos max = BlockPos.of(tag.getLong("PadMax"));
|
||||||
|
this.padBounds = new PadInfo(min, max);
|
||||||
|
} else {
|
||||||
|
this.padBounds = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull CompoundTag getUpdateTag(HolderLookup.@NotNull Provider registries) {
|
||||||
|
CompoundTag tag = super.getUpdateTag(registries);
|
||||||
|
saveAdditional(tag, registries);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleUpdateTag(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
|
||||||
|
loadAdditional(tag, registries);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setPadBoundsAndSync(@Nullable PadInfo newBounds) {
|
||||||
|
if (java.util.Objects.equals(this.padBounds, newBounds)) return;
|
||||||
|
|
||||||
|
this.padBounds = newBounds;
|
||||||
|
setChanged(); // marks BE dirty for saving
|
||||||
|
|
||||||
|
if (level instanceof ServerLevel server) {
|
||||||
|
BlockState state = getBlockState();
|
||||||
|
server.sendBlockUpdated(worldPosition, state, state, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||||
|
// sends the tag from getUpdateTag()
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDataPacket(@NotNull Connection net, ClientboundBlockEntityDataPacket pkt, HolderLookup.@NotNull Provider registries) {
|
||||||
|
// apply the received tag on client
|
||||||
|
CompoundTag tag = pkt.getTag();
|
||||||
|
if (tag != null) {
|
||||||
|
this.loadAdditional(tag, registries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModDimensions;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class StationFlightComputerBlockEntity extends BlockEntity implements TickableBlockEntity {
|
||||||
|
public StationFlightComputerBlockEntity(BlockPos pos, BlockState blockState) {
|
||||||
|
super(ModBlockEntities.STATION_FLIGHT_COMPUTER_BLOCK_ENTITY.get(), pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PartitionData data;
|
||||||
|
private boolean isInitialized = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
if (data == null) return;
|
||||||
|
data.setTraveling(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable PartitionData getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstTick(Level level, BlockState state, BlockPos pos) {
|
||||||
|
if (level.isClientSide()) return;
|
||||||
|
if (level instanceof ServerLevel serverLevel) {
|
||||||
|
if (serverLevel.dimension() == ModDimensions.SPACE) {
|
||||||
|
data = SpacePartitionSavedData.get(serverLevel).getDataForBlockPos(pos);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean setTraveling(boolean value) {
|
||||||
|
if (data == null) return false;
|
||||||
|
data.setTraveling(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return isInitialized;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.network.protocol.Packet;
|
||||||
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
|
||||||
|
import net.neoforged.neoforge.fluids.capability.templates.FluidTank;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.base.StationEngineBlockEntity;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModFluidTags;
|
||||||
|
|
||||||
|
public class StationRocketEngineBlockEntity extends StationEngineBlockEntity {
|
||||||
|
|
||||||
|
/// Seconds to travel 1 AU
|
||||||
|
private final double SECONDS_PER_AU = 60;
|
||||||
|
/// AU per tick
|
||||||
|
private final double SPEED = 1/(SECONDS_PER_AU*20);
|
||||||
|
/// Fuel consumption per tick in millibuckets
|
||||||
|
private static final int FUEL_CONSUMPTION = 10;
|
||||||
|
|
||||||
|
private FluidTank tank = new FluidTank(
|
||||||
|
2000,
|
||||||
|
fluidStack -> fluidStack.is(ModFluidTags.ROCKET_FUEL)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getTravelSpeed() {
|
||||||
|
return SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFluidHandler getFluidStorage(Direction direction) {
|
||||||
|
return tank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StationRocketEngineBlockEntity(BlockPos pos, BlockState blockState) {
|
||||||
|
super(ModBlockEntities.STATION_ROCKET_ENGINE_BLOCK_ENTITY.get(), pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
super.serverTick(level, time, state, pos);
|
||||||
|
burn();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void burn() {
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (data.getDestination() != null && data.isTraveling()) {
|
||||||
|
if (!tank.isEmpty() && tank.getFluid().is(ModFluidTags.ROCKET_FUEL) && tank.getFluidAmount() >= FUEL_CONSUMPTION) { // has enough fuel?
|
||||||
|
if (data.travel(getTravelSpeed()))
|
||||||
|
tank.drain(FUEL_CONSUMPTION, IFluidHandler.FluidAction.EXECUTE);
|
||||||
|
} else {
|
||||||
|
// not enough fuel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
|
super.saveAdditional(tag, registries);
|
||||||
|
tag.put("fluid", tank.writeToNBT(registries, new CompoundTag()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||||
|
super.loadAdditional(tag, registries);
|
||||||
|
if (tag.contains("fluid")) {
|
||||||
|
tank.readFromNBT(registries, tag.getCompound("fluid"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
|
||||||
|
CompoundTag tag = new CompoundTag();
|
||||||
|
saveAdditional(tag, registries);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.xevianlight.aphelion.block.entity.custom;
|
package net.xevianlight.aphelion.block.entity.custom;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
@@ -8,6 +9,7 @@ import net.minecraft.network.chat.Component;
|
|||||||
import net.minecraft.network.protocol.Packet;
|
import net.minecraft.network.protocol.Packet;
|
||||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.Containers;
|
import net.minecraft.world.Containers;
|
||||||
import net.minecraft.world.MenuProvider;
|
import net.minecraft.world.MenuProvider;
|
||||||
import net.minecraft.world.SimpleContainer;
|
import net.minecraft.world.SimpleContainer;
|
||||||
@@ -28,14 +30,14 @@ import net.neoforged.neoforge.capabilities.Capabilities;
|
|||||||
import net.neoforged.neoforge.energy.IEnergyStorage;
|
import net.neoforged.neoforge.energy.IEnergyStorage;
|
||||||
import net.neoforged.neoforge.items.IItemHandler;
|
import net.neoforged.neoforge.items.IItemHandler;
|
||||||
import net.neoforged.neoforge.items.ItemStackHandler;
|
import net.neoforged.neoforge.items.ItemStackHandler;
|
||||||
import net.xevianlight.aphelion.block.custom.ElectricArcFurnace;
|
import net.xevianlight.aphelion.block.custom.VacuumArcFurnaceController;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
|
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlocks;
|
import net.xevianlight.aphelion.core.init.ModBlocks;
|
||||||
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe;
|
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipe;
|
||||||
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipeInput;
|
import net.xevianlight.aphelion.recipe.ElectricArcFurnaceRecipeInput;
|
||||||
import net.xevianlight.aphelion.recipe.ModRecipes;
|
import net.xevianlight.aphelion.recipe.ModRecipes;
|
||||||
import net.xevianlight.aphelion.screen.VacuumArcFurnaceMenu;
|
|
||||||
import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu;
|
import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu;
|
||||||
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
import net.xevianlight.aphelion.util.AphelionBlockStateProperties;
|
||||||
import net.xevianlight.aphelion.util.IMultiblockController;
|
import net.xevianlight.aphelion.util.IMultiblockController;
|
||||||
@@ -45,16 +47,16 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class VacuumArcFurnaceControllerEntity extends BlockEntity implements MenuProvider, IMultiblockController {
|
public class VacuumArcFurnaceControllerEntity extends BlockEntity implements MenuProvider, IMultiblockController, TickableBlockEntity, IArcFurnaceLike {
|
||||||
|
|
||||||
private final int SIZE = 4;
|
private final int SIZE = 4;
|
||||||
private int ENERGY_CAPACITY = 64000;
|
private final int ENERGY_CAPACITY = 64000;
|
||||||
private int MAX_TRANSFER = 320;
|
private final int MAX_TRANSFER = 320;
|
||||||
private int progress = 0;
|
private int progress = 0;
|
||||||
private int maxProgress = 100;
|
private int maxProgress = 100;
|
||||||
private final int DEFAULT_MAX_PROGRESS = 100;
|
private final int DEFAULT_MAX_PROGRESS = 100;
|
||||||
private final ContainerData data;
|
private final ContainerData data;
|
||||||
private int MACHINE_ENERGY_COST = 20;
|
private final int MACHINE_ENERGY_COST = 20;
|
||||||
|
|
||||||
public static final int INPUT_SLOT = 0;
|
public static final int INPUT_SLOT = 0;
|
||||||
public static final int SECONDARY_INPUT_SLOT = 1;
|
public static final int SECONDARY_INPUT_SLOT = 1;
|
||||||
@@ -99,7 +101,7 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
@Override
|
@Override
|
||||||
protected void onContentsChanged(int slot) {
|
protected void onContentsChanged(int slot) {
|
||||||
setChanged();
|
setChanged();
|
||||||
if(!level.isClientSide()) {
|
if (level != null && !level.isClientSide()) {
|
||||||
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
|
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,7 +116,8 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
// }
|
// }
|
||||||
};
|
};
|
||||||
|
|
||||||
public void tick(Level level, BlockPos pos, BlockState blockState) {
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
dirty = false;
|
dirty = false;
|
||||||
@@ -124,7 +127,7 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
|
|
||||||
BlockState newBlockState = level.getBlockState(pos);
|
BlockState newBlockState = level.getBlockState(pos);
|
||||||
|
|
||||||
if (!blockState.getValue(AphelionBlockStateProperties.FORMED))
|
if (!state.getValue(AphelionBlockStateProperties.FORMED))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
chargeFromItem();
|
chargeFromItem();
|
||||||
@@ -135,7 +138,7 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
// Recipe detected! We have enough energy to process
|
// Recipe detected! We have enough energy to process
|
||||||
progress++;
|
progress++;
|
||||||
useEnergy();
|
useEnergy();
|
||||||
level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, true));
|
level.setBlockAndUpdate(pos, newBlockState.setValue(VacuumArcFurnaceController.LIT, true));
|
||||||
setChanged(level, pos, newBlockState);
|
setChanged(level, pos, newBlockState);
|
||||||
|
|
||||||
if (hasCraftingFinished()) {
|
if (hasCraftingFinished()) {
|
||||||
@@ -144,13 +147,13 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
}
|
}
|
||||||
} else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
|
} else if (hasFurnaceRecipe(INPUT_SLOT) && !hasEnoughEnergyToCraft(MACHINE_ENERGY_COST)) {
|
||||||
// Recipe detected but we ran out of power
|
// Recipe detected but we ran out of power
|
||||||
level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, newBlockState.setValue(VacuumArcFurnaceController.LIT, false));
|
||||||
setChanged(level, pos, newBlockState);
|
setChanged(level, pos, newBlockState);
|
||||||
progress = progress > 0 ? progress - 1 : 0;
|
progress = progress > 0 ? progress - 1 : 0;
|
||||||
} else {
|
} else {
|
||||||
// Invalid recipe
|
// Invalid recipe
|
||||||
resetProgress();
|
resetProgress();
|
||||||
level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, newBlockState.setValue(VacuumArcFurnaceController.LIT, false));
|
||||||
setChanged(level, pos, newBlockState);
|
setChanged(level, pos, newBlockState);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -160,7 +163,7 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
// Alloy recipe detected! We have enough energy to process
|
// Alloy recipe detected! We have enough energy to process
|
||||||
progress++;
|
progress++;
|
||||||
useEnergy();
|
useEnergy();
|
||||||
level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, true));
|
level.setBlockAndUpdate(pos, newBlockState.setValue(VacuumArcFurnaceController.LIT, true));
|
||||||
setChanged(level, pos, newBlockState);
|
setChanged(level, pos, newBlockState);
|
||||||
|
|
||||||
if (hasCraftingFinished()) {
|
if (hasCraftingFinished()) {
|
||||||
@@ -169,14 +172,14 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Recipe detected but we ran out of power
|
// Recipe detected but we ran out of power
|
||||||
level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, newBlockState.setValue(VacuumArcFurnaceController.LIT, false));
|
||||||
setChanged(level, pos, newBlockState);
|
setChanged(level, pos, newBlockState);
|
||||||
progress = progress > 0 ? progress - 1 : 0;
|
progress = progress > 0 ? progress - 1 : 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Invalid recipe
|
// Invalid recipe
|
||||||
resetProgress();
|
resetProgress();
|
||||||
level.setBlockAndUpdate(pos, newBlockState.setValue(ElectricArcFurnace.LIT, false));
|
level.setBlockAndUpdate(pos, newBlockState.setValue(VacuumArcFurnaceController.LIT, false));
|
||||||
setChanged(level, pos, newBlockState);
|
setChanged(level, pos, newBlockState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +357,8 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
return isFormed() ? ENERGY_STORAGE : null;
|
return isFormed() ? ENERGY_STORAGE : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnergyStorage getTrueEnergyStorage(@Nullable Direction direction) {
|
@Override
|
||||||
|
public IEnergyStorage getTrueEnergyStorage() {
|
||||||
return this.ENERGY_STORAGE;
|
return this.ENERGY_STORAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,7 +415,7 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
|
public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
|
||||||
if (isFormed())
|
if (isFormed())
|
||||||
return new VacuumArcFurnaceMenu(i, inventory, this, this.data);
|
return new ElectricArcFurnaceMenu(i, inventory, this, this.data);
|
||||||
return new ElectricArcFurnaceMenu(i, inventory, this, this.data);
|
return new ElectricArcFurnaceMenu(i, inventory, this, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,4 +531,14 @@ public class VacuumArcFurnaceControllerEntity extends BlockEntity implements Men
|
|||||||
public void markDirty() {
|
public void markDirty() {
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstTick(Level level, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package net.xevianlight.aphelion.block.entity.custom.base;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.TickableBlockEntity;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModDimensions;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public abstract class StationEngineBlockEntity extends BlockEntity implements TickableBlockEntity {
|
||||||
|
|
||||||
|
private boolean isInitialized = false;
|
||||||
|
protected @Nullable PartitionData data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The travel speed in AU/tick.
|
||||||
|
*/
|
||||||
|
public abstract double getTravelSpeed();
|
||||||
|
|
||||||
|
protected StationEngineBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState blockState) {
|
||||||
|
super(type, pos, blockState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clientTick(ClientLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return isInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void firstTick(Level level, BlockState state, BlockPos pos) {
|
||||||
|
if (level.isClientSide()) return;
|
||||||
|
if (level instanceof ServerLevel serverLevel) {
|
||||||
|
if (serverLevel.dimension() == ModDimensions.SPACE) {
|
||||||
|
data = SpacePartitionSavedData.get(serverLevel).getDataForBlockPos(pos);
|
||||||
|
data.addEngine(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
|
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
@@ -23,7 +24,10 @@ public class OxygenTestRenderer implements BlockEntityRenderer<OxygenTestBlockEn
|
|||||||
public OxygenTestRenderer (BlockEntityRendererProvider.Context context) {}
|
public OxygenTestRenderer (BlockEntityRendererProvider.Context context) {}
|
||||||
|
|
||||||
private List<BlockPos> toBlockPositions(OxygenTestBlockEntity be) {
|
private List<BlockPos> toBlockPositions(OxygenTestBlockEntity be) {
|
||||||
return be.getEnclosedBlocks();
|
// cache = be.getEnclosedBlocks();
|
||||||
|
// if (cache != null)
|
||||||
|
// return cache;
|
||||||
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -31,6 +35,8 @@ public class OxygenTestRenderer implements BlockEntityRenderer<OxygenTestBlockEn
|
|||||||
return AABB.ofSize(blockEntity.getBlockPos().getCenter(), OxygenTestBlockEntity.MAX_RANGE*2, OxygenTestBlockEntity.MAX_RANGE*2, OxygenTestBlockEntity.MAX_RANGE*2);
|
return AABB.ofSize(blockEntity.getBlockPos().getCenter(), OxygenTestBlockEntity.MAX_RANGE*2, OxygenTestBlockEntity.MAX_RANGE*2, OxygenTestBlockEntity.MAX_RANGE*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<BlockPos> cache;
|
||||||
|
|
||||||
private Set<BlockPos> relativePositionsCache;
|
private Set<BlockPos> relativePositionsCache;
|
||||||
@Override
|
@Override
|
||||||
// If in debug mode, renders a model made from the blocks
|
// If in debug mode, renders a model made from the blocks
|
||||||
@@ -42,7 +48,7 @@ public class OxygenTestRenderer implements BlockEntityRenderer<OxygenTestBlockEn
|
|||||||
// Renderers are relative to our block pos, so transform all points to be relative to block pos as well
|
// Renderers are relative to our block pos, so transform all points to be relative to block pos as well
|
||||||
List<BlockPos> positionsToRender = toBlockPositions(be);
|
List<BlockPos> positionsToRender = toBlockPositions(be);
|
||||||
BlockPos originPos = be.getBlockPos();
|
BlockPos originPos = be.getBlockPos();
|
||||||
if (true) return;
|
// if (true) return;
|
||||||
|
|
||||||
Set<BlockPos> relativePositions;
|
Set<BlockPos> relativePositions;
|
||||||
if (relativePositionsCache != null) relativePositions = relativePositionsCache;
|
if (relativePositionsCache != null) relativePositions = relativePositionsCache;
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package net.xevianlight.aphelion.block.entity.custom.renderer;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.LevelRenderer;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderStateShard;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.RocketAssemblerBlockEntity;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class RocketAssemblerBlockEntityRenderer implements BlockEntityRenderer<RocketAssemblerBlockEntity> {
|
||||||
|
|
||||||
|
public RocketAssemblerBlockEntityRenderer (BlockEntityRendererProvider.Context context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AABB getRenderBoundingBox(RocketAssemblerBlockEntity blockEntity) {
|
||||||
|
// If we don't know bounds yet, fall back to default BE culling.
|
||||||
|
RocketAssemblerBlockEntity.PadInfo pad = blockEntity.getPadBounds();
|
||||||
|
if (pad == null) {
|
||||||
|
return BlockEntityRenderer.super.getRenderBoundingBox(blockEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos min = pad.min();
|
||||||
|
BlockPos max = pad.max();
|
||||||
|
|
||||||
|
// Expand slightly to avoid edge precision culling
|
||||||
|
return new AABB(
|
||||||
|
min.getX(), min.getY(), min.getZ(),
|
||||||
|
max.getX() + 1, max.getY() + 1, max.getZ() + 1
|
||||||
|
).inflate(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final RenderType CENTER_FACE = RenderType.create(
|
||||||
|
"aphelion_center_face",
|
||||||
|
DefaultVertexFormat.POSITION_COLOR,
|
||||||
|
VertexFormat.Mode.QUADS,
|
||||||
|
256,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
RenderType.CompositeState.builder()
|
||||||
|
.setShaderState(RenderStateShard.POSITION_COLOR_SHADER)
|
||||||
|
.setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY)
|
||||||
|
.setCullState(RenderStateShard.NO_CULL)
|
||||||
|
.setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST)
|
||||||
|
.createCompositeState(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(@NotNull RocketAssemblerBlockEntity be, float v, @NotNull PoseStack poseStack, @NotNull MultiBufferSource multiBufferSource, int i, int i1) {
|
||||||
|
// if (!Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) return;
|
||||||
|
|
||||||
|
if (be.getPadBounds() == null) return;
|
||||||
|
BlockPos min = be.getPadBounds().min();
|
||||||
|
BlockPos max = be.getPadBounds().max();
|
||||||
|
|
||||||
|
AABB box = new AABB(
|
||||||
|
min.getX(), min.getY(), min.getZ(),
|
||||||
|
max.getX() + 1, max.getY() + 1, max.getZ() + 1
|
||||||
|
);
|
||||||
|
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(-be.getBlockPos().getX(), -be.getBlockPos().getY(), -be.getBlockPos().getZ());
|
||||||
|
|
||||||
|
VertexConsumer lineVc = multiBufferSource.getBuffer(RenderType.lines());
|
||||||
|
LevelRenderer.renderLineBox(poseStack, lineVc, box, 0f, 1f, 0f, 1f);
|
||||||
|
|
||||||
|
VertexConsumer faceVc = multiBufferSource.getBuffer(CENTER_FACE);
|
||||||
|
|
||||||
|
BlockPos center = be.getPadBounds().getCenter();
|
||||||
|
float y = center.getY() + 0.01f; // avoid z-fighting
|
||||||
|
|
||||||
|
LevelRenderer.renderFace(
|
||||||
|
poseStack, faceVc, Direction.UP,
|
||||||
|
center.getX(), y, center.getZ(),
|
||||||
|
center.getX() + 1, y, center.getZ() + 1,
|
||||||
|
1f, 0f, 0f, 0.5f
|
||||||
|
);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,5 +33,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void onEnergyChanged();
|
public abstract void onEnergyChanged();
|
||||||
|
|
||||||
|
public void setCapacity(int capacity) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.xevianlight.aphelion.client;
|
|||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
import net.neoforged.fml.common.EventBusSubscriber;
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
@@ -11,8 +12,9 @@ import net.xevianlight.aphelion.Aphelion;
|
|||||||
import net.xevianlight.aphelion.client.dimension.DimensionRenderer;
|
import net.xevianlight.aphelion.client.dimension.DimensionRenderer;
|
||||||
import net.xevianlight.aphelion.client.dimension.DimensionRendererCache;
|
import net.xevianlight.aphelion.client.dimension.DimensionRendererCache;
|
||||||
import net.xevianlight.aphelion.client.dimension.SpaceSkyEffects;
|
import net.xevianlight.aphelion.client.dimension.SpaceSkyEffects;
|
||||||
import net.xevianlight.aphelion.core.space.SpacePartitionSavedData;
|
import net.xevianlight.aphelion.core.saveddata.EnvironmentSavedData;
|
||||||
import net.xevianlight.aphelion.util.SpacePartitionHelper;
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
|
|
||||||
@EventBusSubscriber(modid = Aphelion.MOD_ID, value = Dist.CLIENT)
|
@EventBusSubscriber(modid = Aphelion.MOD_ID, value = Dist.CLIENT)
|
||||||
public class AphelionDebugOverlay {
|
public class AphelionDebugOverlay {
|
||||||
@@ -41,14 +43,28 @@ public class AphelionDebugOverlay {
|
|||||||
+ ", thickFog=" + r.hasThickFog()
|
+ ", thickFog=" + r.hasThickFog()
|
||||||
+ ", fog=" + r.hasFog());
|
+ ", fog=" + r.hasFog());
|
||||||
|
|
||||||
int x = SpacePartitionHelper.get(Math.floor(mc.player.position().x));
|
int x = SpacePartition.get(Math.floor(mc.player.position().x));
|
||||||
int z = SpacePartitionHelper.get(Math.floor(mc.player.position().z));
|
int z = SpacePartition.get(Math.floor(mc.player.position().z));
|
||||||
|
|
||||||
// Left side of F3
|
// Left side of F3
|
||||||
event.getLeft().add("");
|
event.getLeft().add("");
|
||||||
event.getLeft().add("Aphelion:");
|
event.getLeft().add("Aphelion:");
|
||||||
event.getLeft().add(" Orbit: " + orbitId);
|
event.getLeft().add(" Orbit: " + PartitionClientState.lastData().getOrbit());
|
||||||
// event.getLeft().add(" Sky: " + rendererSummary);
|
// event.getLeft().add(" Sky: " + rendererSummary);
|
||||||
event.getLeft().add(" Station: " + x + " " + z + " ID: " + SpacePartitionSavedData.pack(x,z));
|
event.getLeft().add(" Station: " + x + " " + z + " ID: " + SpacePartitionSavedData.pack(x,z));
|
||||||
|
event.getLeft().add(" Station Destination: " + PartitionClientState.lastData().getDestination());
|
||||||
|
event.getLeft().add(" Station Owner: " + PartitionClientState.lastData().getOwner());
|
||||||
|
event.getLeft().add(" Station Engines: " + PartitionClientState.lastData().getEngines().toArray().length);
|
||||||
|
event.getLeft().add(" Station Landing Pads: " + PartitionClientState.lastData().getLandingPadContollersAsArray().length);
|
||||||
|
event.getLeft().add(" Station Traveling: " + PartitionClientState.lastData().isTraveling());
|
||||||
|
event.getLeft().add(" Station Trip Distance AU: " + PartitionClientState.lastData().getTripDistanceAU());
|
||||||
|
event.getLeft().add(" Station Distance Traveled AU: " + PartitionClientState.lastData().getDistanceTraveledAU());
|
||||||
|
var server = mc.getSingleplayerServer();
|
||||||
|
ServerLevel singlePlayerLevel;
|
||||||
|
if (server != null) {
|
||||||
|
singlePlayerLevel = server.getLevel(mc.level.dimension());
|
||||||
|
if (singlePlayerLevel != null)
|
||||||
|
event.getLeft().add(" Oxygen: " + EnvironmentSavedData.get(singlePlayerLevel).hasOxygen(singlePlayerLevel, mc.player.blockPosition().mutable().above()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package net.xevianlight.aphelion.client;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
|
||||||
|
public final class ClientOxygenCache {
|
||||||
|
public static final LongOpenHashSet OXYGEN = new LongOpenHashSet();
|
||||||
|
public static BlockPos lastCenter = BlockPos.ZERO;
|
||||||
|
public static long lastUpdateGameTime = -1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package net.xevianlight.aphelion.client;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderStateShard;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.neoforged.api.distmarker.Dist;
|
||||||
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
|
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
@EventBusSubscriber(modid = Aphelion.MOD_ID, value = Dist.CLIENT)
|
||||||
|
public final class OxygenDebugRender {
|
||||||
|
|
||||||
|
// Untextured translucent quads (POSITION_COLOR only)
|
||||||
|
private static final RenderType OXYGEN_FILL = RenderType.create(
|
||||||
|
"aphelion_oxygen_fill",
|
||||||
|
DefaultVertexFormat.POSITION_COLOR,
|
||||||
|
VertexFormat.Mode.QUADS,
|
||||||
|
256,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
RenderType.CompositeState.builder()
|
||||||
|
.setShaderState(RenderStateShard.POSITION_COLOR_SHADER)
|
||||||
|
.setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY)
|
||||||
|
.setCullState(RenderStateShard.NO_CULL)
|
||||||
|
.setDepthTestState(RenderStateShard.LEQUAL_DEPTH_TEST)
|
||||||
|
.setWriteMaskState(RenderStateShard.COLOR_DEPTH_WRITE)
|
||||||
|
.createCompositeState(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onRenderLevel(RenderLevelStageEvent event) {
|
||||||
|
// One stage only (pick one that exists and looks good)
|
||||||
|
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) return;
|
||||||
|
|
||||||
|
Minecraft mc = Minecraft.getInstance();
|
||||||
|
if (mc.level == null || mc.player == null) return;
|
||||||
|
if (!mc.gui.getDebugOverlay().showDebugScreen()) return;
|
||||||
|
|
||||||
|
PoseStack poseStack = event.getPoseStack();
|
||||||
|
var cam = mc.gameRenderer.getMainCamera();
|
||||||
|
var camPos = cam.getPosition();
|
||||||
|
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(-camPos.x, -camPos.y, -camPos.z);
|
||||||
|
|
||||||
|
MultiBufferSource.BufferSource bufferSource = mc.renderBuffers().bufferSource();
|
||||||
|
VertexConsumer vc = bufferSource.getBuffer(OXYGEN_FILL);
|
||||||
|
|
||||||
|
// Render surface faces only (fast + pretty)
|
||||||
|
for (long l : ClientOxygenCache.OXYGEN) {
|
||||||
|
BlockPos p = BlockPos.of(l);
|
||||||
|
drawSurfaceFaces(poseStack, vc, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
poseStack.popPose();
|
||||||
|
bufferSource.endBatch(OXYGEN_FILL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void drawSurfaceFaces(PoseStack poseStack, VertexConsumer vc, BlockPos p) {
|
||||||
|
// Neighbor checks: only render faces exposed to non-oxygen
|
||||||
|
boolean up = ClientOxygenCache.OXYGEN.contains(p.above().asLong());
|
||||||
|
boolean down = ClientOxygenCache.OXYGEN.contains(p.below().asLong());
|
||||||
|
boolean north = ClientOxygenCache.OXYGEN.contains(p.north().asLong());
|
||||||
|
boolean south = ClientOxygenCache.OXYGEN.contains(p.south().asLong());
|
||||||
|
boolean east = ClientOxygenCache.OXYGEN.contains(p.east().asLong());
|
||||||
|
boolean west = ClientOxygenCache.OXYGEN.contains(p.west().asLong());
|
||||||
|
|
||||||
|
if (up && down && north && south && east && west) return;
|
||||||
|
|
||||||
|
final float eps = 0.0025f;
|
||||||
|
float x0 = p.getX() + eps;
|
||||||
|
float y0 = p.getY() + eps;
|
||||||
|
float z0 = p.getZ() + eps;
|
||||||
|
float x1 = p.getX() + 1 - eps;
|
||||||
|
float y1 = p.getY() + 1 - eps;
|
||||||
|
float z1 = p.getZ() + 1 - eps;
|
||||||
|
|
||||||
|
// Color (ARGB-ish but as floats)
|
||||||
|
float r = 0.2f, g = 0.8f, b = 1.0f, a = 0.18f;
|
||||||
|
|
||||||
|
Matrix4f mat = poseStack.last().pose();
|
||||||
|
|
||||||
|
// IMPORTANT: vertex winding should be consistent (counter-clockwise)
|
||||||
|
if (!up) quad(mat, vc, x0,y1,z0, x1,y1,z0, x1,y1,z1, x0,y1,z1, r,g,b,a);
|
||||||
|
if (!down) quad(mat, vc, x0,y0,z1, x1,y0,z1, x1,y0,z0, x0,y0,z0, r,g,b,a);
|
||||||
|
|
||||||
|
if (!north) quad(mat, vc, x1,y0,z0, x0,y0,z0, x0,y1,z0, x1,y1,z0, r,g,b,a);
|
||||||
|
if (!south) quad(mat, vc, x0,y0,z1, x1,y0,z1, x1,y1,z1, x0,y1,z1, r,g,b,a);
|
||||||
|
|
||||||
|
if (!east) quad(mat, vc, x1,y0,z1, x1,y0,z0, x1,y1,z0, x1,y1,z1, r,g,b,a);
|
||||||
|
if (!west) quad(mat, vc, x0,y0,z0, x0,y0,z1, x0,y1,z1, x0,y1,z0, r,g,b,a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void quad(
|
||||||
|
Matrix4f mat, VertexConsumer vc,
|
||||||
|
float x0, float y0, float z0,
|
||||||
|
float x1, float y1, float z1,
|
||||||
|
float x2, float y2, float z2,
|
||||||
|
float x3, float y3, float z3,
|
||||||
|
float r, float g, float b, float a
|
||||||
|
) {
|
||||||
|
// POSITION_COLOR format: ONLY position + color.
|
||||||
|
vc.addVertex(mat, x0, y0, z0).setColor(r, g, b, a);
|
||||||
|
vc.addVertex(mat, x1, y1, z1).setColor(r, g, b, a);
|
||||||
|
vc.addVertex(mat, x2, y2, z2).setColor(r, g, b, a);
|
||||||
|
vc.addVertex(mat, x3, y3, z3).setColor(r, g, b, a);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,29 @@
|
|||||||
package net.xevianlight.aphelion.client;
|
package net.xevianlight.aphelion.client;
|
||||||
|
|
||||||
import net.xevianlight.aphelion.network.packet.PartitionData;
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import net.xevianlight.aphelion.network.packet.PartitionPayload;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class PartitionClientState {
|
public final class PartitionClientState {
|
||||||
private static volatile PartitionData last = null;
|
private static volatile PartitionPayload last = null;
|
||||||
|
|
||||||
public static void set(PartitionData d) { last = d; }
|
public static void set(PartitionPayload d) { last = d; }
|
||||||
|
|
||||||
public static Optional<PartitionData> get() {
|
public static Optional<PartitionPayload> get() {
|
||||||
return Optional.ofNullable(last);
|
return Optional.ofNullable(last);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String idOrUnknown() {
|
public static String idOrUnknown() {
|
||||||
return last != null ? last.id() : "unknown";
|
String orbit = String.valueOf(last.partitionData().getOrbit());
|
||||||
|
if (orbit == null) {
|
||||||
|
return "aphleion:orbit/default";
|
||||||
|
}
|
||||||
|
return last != null ? orbit : "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PartitionData lastData() {
|
||||||
|
return last.partitionData();
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// public static int pxOr(int fallback) {
|
// public static int pxOr(int fallback) {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.client.PartitionClientState;
|
import net.xevianlight.aphelion.client.PartitionClientState;
|
||||||
import net.xevianlight.aphelion.util.SpacePartitionHelper;
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ public class DimensionSkyEffects extends DimensionSpecialEffects {
|
|||||||
|
|
||||||
public static ResourceLocation orbitForPos(Vec3 pos) {
|
public static ResourceLocation orbitForPos(Vec3 pos) {
|
||||||
|
|
||||||
int x = SpacePartitionHelper.get(pos.x);
|
int x = SpacePartition.get(pos.x);
|
||||||
int z = SpacePartitionHelper.get(pos.z);
|
int z = SpacePartition.get(pos.z);
|
||||||
|
|
||||||
Minecraft mc = Minecraft.getInstance();
|
Minecraft mc = Minecraft.getInstance();
|
||||||
if (mc.level == null) return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
if (mc.level == null) return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
||||||
@@ -71,7 +71,7 @@ public class DimensionSkyEffects extends DimensionSpecialEffects {
|
|||||||
// int py = PartitionClientState.pyOr(0);
|
// int py = PartitionClientState.pyOr(0);
|
||||||
var data = ResourceLocation.parse(PartitionClientState.idOrUnknown());
|
var data = ResourceLocation.parse(PartitionClientState.idOrUnknown());
|
||||||
|
|
||||||
// var data = SpacePartitionSavedData.get(serverLevel).getOrbitForPartition((int) x, (int) z);
|
// var partitionData = SpacePartitionSavedData.get(serverLevel).getOrbitForPartition((int) x, (int) z);
|
||||||
if (data != null) return data;
|
if (data != null) return data;
|
||||||
|
|
||||||
return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
||||||
|
|||||||
@@ -5,13 +5,10 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.DimensionSpecialEffects;
|
import net.minecraft.client.renderer.DimensionSpecialEffects;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.client.PartitionClientState;
|
import net.xevianlight.aphelion.client.PartitionClientState;
|
||||||
import net.xevianlight.aphelion.core.space.SpacePartitionSavedData;
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
import net.xevianlight.aphelion.util.SpacePartitionHelper;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
@@ -29,7 +26,7 @@ public class SpaceSkyEffects extends DimensionSpecialEffects {
|
|||||||
return ResourceLocation.withDefaultNamespace("overworld");
|
return ResourceLocation.withDefaultNamespace("overworld");
|
||||||
}
|
}
|
||||||
if (effectsId.equals(Aphelion.id("space"))) {
|
if (effectsId.equals(Aphelion.id("space"))) {
|
||||||
return SpaceSkyEffects.orbitForPos(camera.getPosition()); // or inline this logic
|
return orbitForPos(camera.getPosition()); // or inline this logic
|
||||||
}
|
}
|
||||||
return effectsId;
|
return effectsId;
|
||||||
}
|
}
|
||||||
@@ -70,20 +67,18 @@ public class SpaceSkyEffects extends DimensionSpecialEffects {
|
|||||||
|
|
||||||
public static ResourceLocation orbitForPos(Vec3 pos) {
|
public static ResourceLocation orbitForPos(Vec3 pos) {
|
||||||
|
|
||||||
int x = SpacePartitionHelper.get(pos.x);
|
int x = SpacePartition.get(pos.x);
|
||||||
int z = SpacePartitionHelper.get(pos.z);
|
int z = SpacePartition.get(pos.z);
|
||||||
|
|
||||||
Minecraft mc = Minecraft.getInstance();
|
Minecraft mc = Minecraft.getInstance();
|
||||||
if (mc.level == null) return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
if (mc.level == null) return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
||||||
|
|
||||||
// int px = PartitionClientState.pxOr(0);
|
// int px = PartitionClientState.pxOr(0);
|
||||||
// int py = PartitionClientState.pyOr(0);
|
// int py = PartitionClientState.pyOr(0);
|
||||||
var data = ResourceLocation.parse(PartitionClientState.idOrUnknown());
|
|
||||||
|
|
||||||
// var data = SpacePartitionSavedData.get(serverLevel).getOrbitForPartition((int) x, (int) z);
|
// var partitionData = SpacePartitionSavedData.get(serverLevel).getOrbitForPartition((int) x, (int) z);
|
||||||
if (data != null) return data;
|
return ResourceLocation.parse(PartitionClientState.idOrUnknown());
|
||||||
|
|
||||||
return ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "orbit/default");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.xevianlight.aphelion.commands;
|
package net.xevianlight.aphelion.commands;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
import com.mojang.brigadier.arguments.LongArgumentType;
|
import com.mojang.brigadier.arguments.LongArgumentType;
|
||||||
@@ -15,20 +16,22 @@ import net.minecraft.nbt.CompoundTag;
|
|||||||
import net.minecraft.network.chat.*;
|
import net.minecraft.network.chat.*;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ColumnPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.RelativeMovement;
|
import net.minecraft.world.entity.RelativeMovement;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.core.space.SpacePartitionSavedData;
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
import net.xevianlight.aphelion.entites.vehicles.RocketEntity;
|
import net.xevianlight.aphelion.entites.vehicles.RocketEntity;
|
||||||
import net.xevianlight.aphelion.util.RocketStructure;
|
import net.xevianlight.aphelion.util.RocketStructure;
|
||||||
import net.xevianlight.aphelion.util.SpacePartitionHelper;
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class AphelionCommand {
|
public class AphelionCommand {
|
||||||
|
|
||||||
@@ -41,8 +44,8 @@ public class AphelionCommand {
|
|||||||
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
||||||
.then(Commands.argument("orbit", ResourceLocationArgument.id())
|
.then(Commands.argument("orbit", ResourceLocationArgument.id())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
int x = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
int x = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
||||||
int z = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
int z = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
||||||
ResourceLocation orbit = ResourceLocationArgument.getId(context, "orbit");
|
ResourceLocation orbit = ResourceLocationArgument.getId(context, "orbit");
|
||||||
|
|
||||||
ServerLevel level = context.getSource().getLevel();
|
ServerLevel level = context.getSource().getLevel();
|
||||||
@@ -79,8 +82,8 @@ public class AphelionCommand {
|
|||||||
.then(Commands.literal("get")
|
.then(Commands.literal("get")
|
||||||
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
int x = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
int x = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
||||||
int z = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
int z = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
||||||
|
|
||||||
ServerLevel level = context.getSource().getLevel();
|
ServerLevel level = context.getSource().getLevel();
|
||||||
ResourceLocation orbit = SpacePartitionSavedData.get(level).getOrbitForPartition(x, z);
|
ResourceLocation orbit = SpacePartitionSavedData.get(level).getOrbitForPartition(x, z);
|
||||||
@@ -104,8 +107,8 @@ public class AphelionCommand {
|
|||||||
.then(Commands.literal("clear")
|
.then(Commands.literal("clear")
|
||||||
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
int x = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
int x = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
||||||
int z = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
int z = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
||||||
|
|
||||||
ServerLevel level = context.getSource().getLevel();
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
|
||||||
@@ -142,8 +145,8 @@ public class AphelionCommand {
|
|||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
ServerLevel level = context.getSource().getLevel();
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
|
||||||
int x = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context,"pos").x());
|
int x = SpacePartition.get(ColumnPosArgument.getColumnPos(context,"pos").x());
|
||||||
int z = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context,"pos").z());
|
int z = SpacePartition.get(ColumnPosArgument.getColumnPos(context,"pos").z());
|
||||||
|
|
||||||
long key = SpacePartitionSavedData.pack(x,z);
|
long key = SpacePartitionSavedData.pack(x,z);
|
||||||
|
|
||||||
@@ -189,7 +192,7 @@ public class AphelionCommand {
|
|||||||
int x = ColumnPosArgument.getColumnPos(context, "pos").x();
|
int x = ColumnPosArgument.getColumnPos(context, "pos").x();
|
||||||
int z = ColumnPosArgument.getColumnPos(context, "pos").z();
|
int z = ColumnPosArgument.getColumnPos(context, "pos").z();
|
||||||
|
|
||||||
String stationCoord = SpacePartitionHelper.get(x) + " " + SpacePartitionHelper.get(z);
|
String stationCoord = SpacePartition.get(x) + " " + SpacePartition.get(z);
|
||||||
|
|
||||||
Component clickableOutput = getClickablePos(stationCoord, ChatFormatting.GREEN);
|
Component clickableOutput = getClickablePos(stationCoord, ChatFormatting.GREEN);
|
||||||
|
|
||||||
@@ -211,8 +214,8 @@ public class AphelionCommand {
|
|||||||
double x = (double) IntegerArgumentType.getInteger(context, "x");
|
double x = (double) IntegerArgumentType.getInteger(context, "x");
|
||||||
double z = (double) IntegerArgumentType.getInteger(context, "z");
|
double z = (double) IntegerArgumentType.getInteger(context, "z");
|
||||||
|
|
||||||
int destX = (int) Math.floor(x * SpacePartitionHelper.SIZE) + (SpacePartitionHelper.SIZE / 2);
|
int destX = (int) Math.floor(x * SpacePartition.SIZE) + (SpacePartition.SIZE / 2);
|
||||||
int destZ = (int) Math.floor(z * SpacePartitionHelper.SIZE) + (SpacePartitionHelper.SIZE / 2);
|
int destZ = (int) Math.floor(z * SpacePartition.SIZE) + (SpacePartition.SIZE / 2);
|
||||||
|
|
||||||
String stationCoord = x + ", " + z;
|
String stationCoord = x + ", " + z;
|
||||||
|
|
||||||
@@ -244,6 +247,90 @@ public class AphelionCommand {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.then(Commands.literal("destination")
|
||||||
|
.then(Commands.literal("set")
|
||||||
|
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
||||||
|
.then(Commands.argument("id", ResourceLocationArgument.id())
|
||||||
|
.executes(context -> {
|
||||||
|
int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
||||||
|
int pz = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
||||||
|
ResourceLocation orbit = ResourceLocationArgument.getId(context, "id");
|
||||||
|
|
||||||
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz);
|
||||||
|
if (data == null) {
|
||||||
|
context.getSource().sendFailure(Component.translatable("command.aphelion.station.invalid"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
data.setDestination(orbit);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(Commands.literal("owner")
|
||||||
|
.then(Commands.literal("get")
|
||||||
|
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
||||||
|
.executes(context -> {
|
||||||
|
int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
||||||
|
int pz = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
||||||
|
|
||||||
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz);
|
||||||
|
var cache = level.getServer().getProfileCache();
|
||||||
|
if (data == null) {
|
||||||
|
context.getSource().sendFailure(Component.translatable("command.aphelion.station.invalid"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (cache == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
UUID uuid = data.getOwner();
|
||||||
|
if (uuid == null) {
|
||||||
|
context.getSource().sendSuccess(() -> Component.translatable("command.aphelion.station.owner.unset"), true);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = cache.get(uuid).map(GameProfile::getName).orElse(null);
|
||||||
|
context.getSource().sendSuccess(() -> Component.translatable("command.aphelion.station.owner.get", px, pz, name), true);
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(Commands.literal("set")
|
||||||
|
.then(Commands.argument("pos", ColumnPosArgument.columnPos())
|
||||||
|
.then(Commands.argument("player", GameProfileArgument.gameProfile())
|
||||||
|
.executes(context -> {
|
||||||
|
int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
|
||||||
|
int pz = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
|
||||||
|
|
||||||
|
ServerLevel level = context.getSource().getLevel();
|
||||||
|
PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz);
|
||||||
|
if (data == null) {
|
||||||
|
context.getSource().sendFailure(Component.translatable("command.aphelion.station.invalid"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Collection<GameProfile> profiles =
|
||||||
|
GameProfileArgument.getGameProfiles(context, "player");
|
||||||
|
|
||||||
|
if (profiles.size() != 1) {
|
||||||
|
context.getSource().sendFailure(Component.translatable("command.aphelion.player.invalid"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameProfile profile = profiles.iterator().next();
|
||||||
|
UUID uuid = profile.getId();
|
||||||
|
|
||||||
|
data.setOwner(uuid);
|
||||||
|
context.getSource().sendSuccess(() -> Component.translatable("command.aphelion.station.owner.set.success", px, pz, profile.getName()), true);
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.then(Commands.literal("planet")
|
.then(Commands.literal("planet")
|
||||||
.then(Commands.literal("tp")
|
.then(Commands.literal("tp")
|
||||||
@@ -455,6 +542,22 @@ public class AphelionCommand {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.then(Commands.literal("disassemble")
|
||||||
|
.executes(context -> {
|
||||||
|
Entity entity = EntityArgument.getEntity(context, "entity");
|
||||||
|
|
||||||
|
if (entity instanceof RocketEntity rocket) {
|
||||||
|
if (rocket.disassemble()) {
|
||||||
|
context.getSource().sendSuccess(() -> Component.translatable("command.aphelion.rocket.disassemble.success"), true);
|
||||||
|
} else {
|
||||||
|
context.getSource().sendFailure(Component.translatable("command.aphelion.rocket.disassemble.failure"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.getSource().sendFailure(Component.translatable("command.aphelion.rocket.entity_invalid"));
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
package net.xevianlight.aphelion.core;
|
|
||||||
|
|
||||||
import net.minecraft.client.KeyMapping;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class KeyVariables {
|
|
||||||
|
|
||||||
public static final Map<UUID, Boolean> KEY_UP = new HashMap<>();
|
|
||||||
public static final Map<UUID, Boolean> KEY_DOWN = new HashMap<>();
|
|
||||||
public static final Map<UUID, Boolean> KEY_RIGHT = new HashMap<>();
|
|
||||||
public static final Map<UUID, Boolean> KEY_LEFT = new HashMap<>();
|
|
||||||
public static final Map<UUID, Boolean> KEY_JUMP = new HashMap<>();
|
|
||||||
public static final Map<UUID, Boolean> KEY_TABLET = new HashMap<>();
|
|
||||||
|
|
||||||
public static boolean isHoldingUp(Player player) {
|
|
||||||
return player != null && KEY_UP.getOrDefault(player.getUUID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isHoldingDown(Player player) {
|
|
||||||
return player != null && KEY_DOWN.getOrDefault(player.getUUID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isHoldingRight(Player player) {
|
|
||||||
return player != null && KEY_RIGHT.getOrDefault(player.getUUID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isHoldingLeft(Player player) {
|
|
||||||
return player != null && KEY_LEFT.getOrDefault(player.getUUID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isHoldingJump(Player player) {
|
|
||||||
return player != null && KEY_JUMP.getOrDefault(player.getUUID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean getHoldingTabletPress(Player player) {
|
|
||||||
return player != null && KEY_TABLET.getOrDefault(player.getUUID(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Map<KeyMapping, String> getKey(Minecraft minecraft) {
|
|
||||||
Map<KeyMapping, String> key = new HashMap<>();
|
|
||||||
key.put(minecraft.options.keyUp, "key_up");
|
|
||||||
key.put(minecraft.options.keyDown, "key_down");
|
|
||||||
key.put(minecraft.options.keyRight, "key_right");
|
|
||||||
key.put(minecraft.options.keyLeft, "key_left");
|
|
||||||
key.put(minecraft.options.keyJump, "key_jump");
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setKeyVariable(String key, UUID uuid, Boolean bool) {
|
|
||||||
switch (key) {
|
|
||||||
case "key_up":
|
|
||||||
KEY_UP.put(uuid, bool);
|
|
||||||
case "key_down":
|
|
||||||
KEY_DOWN.put(uuid, bool);
|
|
||||||
case "key_right":
|
|
||||||
KEY_RIGHT.put(uuid, bool);
|
|
||||||
case "key_left":
|
|
||||||
KEY_LEFT.put(uuid, bool);
|
|
||||||
case "key_jump":
|
|
||||||
KEY_JUMP.put(uuid, bool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ import net.minecraft.core.registries.BuiltInRegistries;
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.StationRocketEngineBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity;
|
import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.*;
|
import net.xevianlight.aphelion.block.entity.custom.*;
|
||||||
|
|
||||||
@@ -46,4 +47,19 @@ public class ModBlockEntities {
|
|||||||
BLOCK_ENTITIES.register("oxygen_test_block_entity", () -> BlockEntityType.Builder.of(
|
BLOCK_ENTITIES.register("oxygen_test_block_entity", () -> BlockEntityType.Builder.of(
|
||||||
OxygenTestBlockEntity::new, ModBlocks.OXYGEN_TEST_BLOCK.get()).build(null)
|
OxygenTestBlockEntity::new, ModBlocks.OXYGEN_TEST_BLOCK.get()).build(null)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public static final Supplier<BlockEntityType<RocketAssemblerBlockEntity>> ROCKET_ASSEMBLER_BLOCK_ENTITY =
|
||||||
|
BLOCK_ENTITIES.register("rocket_assembler_block_entity", () -> BlockEntityType.Builder.of(
|
||||||
|
RocketAssemblerBlockEntity::new, ModBlocks.ROCKET_ASSEMBLER.get()).build(null)
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final Supplier<BlockEntityType<StationRocketEngineBlockEntity>> STATION_ROCKET_ENGINE_BLOCK_ENTITY =
|
||||||
|
BLOCK_ENTITIES.register("station_rocket_engine_block_entity", () -> BlockEntityType.Builder.of(
|
||||||
|
StationRocketEngineBlockEntity::new, ModBlocks.STATION_ROCKET_ENGINE.get()).build(null)
|
||||||
|
);
|
||||||
|
|
||||||
|
public static final Supplier<BlockEntityType<StationFlightComputerBlockEntity>> STATION_FLIGHT_COMPUTER_BLOCK_ENTITY =
|
||||||
|
BLOCK_ENTITIES.register("station_flight_computer_block_entity", () -> BlockEntityType.Builder.of(
|
||||||
|
StationFlightComputerBlockEntity::new, ModBlocks.STATION_FLIGHT_COMPUTER_BLOCK.get()).build(null)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,19 @@ import net.xevianlight.aphelion.block.dummy.VAFMultiblockDummyBlock;
|
|||||||
public class ModBlocks {
|
public class ModBlocks {
|
||||||
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Aphelion.MOD_ID);
|
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Aphelion.MOD_ID);
|
||||||
|
|
||||||
public static final DeferredBlock<Block> TEST_BLOCK = BLOCKS.register("test_block", () -> new TestBlock(TestBlock.getProperties()));
|
public static final DeferredBlock<Block> TEST_BLOCK = BLOCKS.register("test_block", () -> new TestBlock(TestBlock.getProperties()));
|
||||||
public static final DeferredBlock<Block> BLOCK_STEEL = BLOCKS.register("block_steel", () -> new BlockSteel(BlockSteel.getProperties()));
|
public static final DeferredBlock<Block> BLOCK_STEEL = BLOCKS.register("block_steel", () -> new BlockSteel(BlockSteel.getProperties()));
|
||||||
public static final DeferredBlock<Block> DIMENSION_CHANGER = BLOCKS.register("dimension_changer", () -> new DimensionChangerBlock(DimensionChangerBlock.getProperties()));
|
public static final DeferredBlock<Block> LAUNCH_PAD = BLOCKS.register("launch_pad", () -> new LaunchPad(LaunchPad.getProperties()));
|
||||||
public static final DeferredBlock<Block> ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties()));
|
public static final DeferredBlock<Block> DIMENSION_CHANGER = BLOCKS.register("dimension_changer", () -> new DimensionChangerBlock(DimensionChangerBlock.getProperties()));
|
||||||
public static final DeferredBlock<Block> ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties()));
|
public static final DeferredBlock<Block> ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties()));
|
||||||
public static final DeferredBlock<Block> VACUUM_ARC_FURNACE_CONTROLLER = BLOCKS.register("vacuum_arc_furnace_controller", () -> new VacuumArcFurnaceController(VacuumArcFurnaceController.getProperties()));
|
public static final DeferredBlock<Block> ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties()));
|
||||||
public static final DeferredBlock<Block> VAF_MULTIBLOCK_DUMMY_BLOCK = BLOCKS.register("vaf_dummy_block", () -> new VAFMultiblockDummyBlock(VAFMultiblockDummyBlock.getProperties()));
|
public static final DeferredBlock<Block> VACUUM_ARC_FURNACE_CONTROLLER = BLOCKS.register("vacuum_arc_furnace_controller", () -> new VacuumArcFurnaceController(VacuumArcFurnaceController.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> VAF_MULTIBLOCK_DUMMY_BLOCK = BLOCKS.register("vaf_dummy_block", () -> new VAFMultiblockDummyBlock(VAFMultiblockDummyBlock.getProperties()));
|
||||||
public static final DeferredBlock<Block> OXYGEN_TEST_BLOCK = BLOCKS.register("oxygen_test_block", () -> new OxygenTestBlock(OxygenTestBlock.getProperties()));
|
public static final DeferredBlock<Block> OXYGEN_TEST_BLOCK = BLOCKS.register("oxygen_test_block", () -> new OxygenTestBlock(OxygenTestBlock.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> ROCKET_ASSEMBLER = BLOCKS.register("rocket_assembler", () -> new RocketAssembler(RocketAssembler.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> ROCKET_SEAT = BLOCKS.register("rocket_seat", () -> new RocketSeat(RocketSeat.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> STATION_ROCKET_ENGINE = BLOCKS.register("station_rocket_engine", () -> new StationRocketEngineBlock(StationRocketEngineBlock.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> BASIC_ROCKET_FUEL_TANK = BLOCKS.register("basic_rocket_fuel_tank", () -> new BasicRocketFuelTank(BasicRocketFuelTank.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> BASIC_ROCKET_CONTAINER = BLOCKS.register("basic_rocket_container", () -> new BasicRocketContainer(BasicRocketContainer.getProperties()));
|
||||||
|
public static final DeferredBlock<Block> STATION_FLIGHT_COMPUTER_BLOCK = BLOCKS.register("station_flight_computer", () -> new StationFlightComputerBlock(StationFlightComputerBlock.getProperties()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public class ModCreativeTabs {
|
|||||||
output.accept(ModItems.INGOT_NEODYMIUM);
|
output.accept(ModItems.INGOT_NEODYMIUM);
|
||||||
output.accept(ModItems.INGOT_IRIDIUM);
|
output.accept(ModItems.INGOT_IRIDIUM);
|
||||||
output.accept(ModFluids.OIL_BUCKET);
|
output.accept(ModFluids.OIL_BUCKET);
|
||||||
|
output.accept(ModFluids.ROCKET_FUEL_BUCKET);
|
||||||
output.accept(ModItems.MUSIC_DISC_BIT_SHIFT);
|
output.accept(ModItems.MUSIC_DISC_BIT_SHIFT);
|
||||||
}).build());
|
}).build());
|
||||||
|
|
||||||
@@ -41,5 +42,7 @@ public class ModCreativeTabs {
|
|||||||
output.accept(ModItems.BLOCK_STEEL);
|
output.accept(ModItems.BLOCK_STEEL);
|
||||||
output.accept(ModItems.ARC_FURNACE_CASING_BLOCK);
|
output.accept(ModItems.ARC_FURNACE_CASING_BLOCK);
|
||||||
output.accept(ModItems.VACUUM_ARC_FURNACE_CONTROLLER);
|
output.accept(ModItems.VACUUM_ARC_FURNACE_CONTROLLER);
|
||||||
|
output.accept(ModItems.LAUNCH_PAD);
|
||||||
|
output.accept(ModItems.ROCKET_ASSEMBLER);
|
||||||
}).build());
|
}).build());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package net.xevianlight.aphelion.core.init;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
|
import net.minecraft.world.damagesource.DamageType;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
|
||||||
|
public class ModDamageSources {
|
||||||
|
|
||||||
|
// Relatively sure this is right
|
||||||
|
public static final ResourceKey<DamageType> OXYGEN = ResourceKey.create(Registries.DAMAGE_TYPE, ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "oxygen"));
|
||||||
|
|
||||||
|
public static DamageSource create(Level level, ResourceKey<DamageType> key) {
|
||||||
|
return new DamageSource(level.registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package net.xevianlight.aphelion.core.init;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
|
||||||
|
public final class ModDimensions {
|
||||||
|
public static final ResourceKey<Level> SPACE = ResourceKey.create(Registries.DIMENSION, Aphelion.id("space"));
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net.xevianlight.aphelion.core.init;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.level.material.Fluid;
|
||||||
|
|
||||||
|
public class ModFluidTags {
|
||||||
|
public static final TagKey<Fluid> ROCKET_FUEL = create("rocket_fuel");
|
||||||
|
|
||||||
|
private static TagKey<Fluid> create(String name) {
|
||||||
|
return TagKey.create(Registries.FLUID, ResourceLocation.withDefaultNamespace(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TagKey<Fluid> create(ResourceLocation name) {
|
||||||
|
return TagKey.create(Registries.FLUID, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import net.neoforged.neoforge.registries.DeferredItem;
|
|||||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.block.custom.*;
|
import net.xevianlight.aphelion.block.custom.*;
|
||||||
import net.xevianlight.aphelion.block.dummy.VAFMultiblockDummyBlock;
|
|
||||||
import net.xevianlight.aphelion.item.*;
|
import net.xevianlight.aphelion.item.*;
|
||||||
|
|
||||||
public class ModItems {
|
public class ModItems {
|
||||||
@@ -36,5 +35,11 @@ public static final DeferredItem<Item> MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu
|
|||||||
public static final DeferredItem<BlockItem> ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties()));
|
public static final DeferredItem<BlockItem> ARC_FURNACE_CASING_BLOCK = ITEMS.register("arc_furnace_casing", () -> new BlockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK.get(), ArcFurnaceCasingBlock.getItemProperties()));
|
||||||
public static final DeferredItem<BlockItem> VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties()));
|
public static final DeferredItem<BlockItem> VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties()));
|
||||||
public static final DeferredItem<BlockItem> OXYGEN_TEST_BLOCK = ITEMS.register("oxygen_test_block", () -> new BlockItem(ModBlocks.OXYGEN_TEST_BLOCK.get(), new Item.Properties()));
|
public static final DeferredItem<BlockItem> OXYGEN_TEST_BLOCK = ITEMS.register("oxygen_test_block", () -> new BlockItem(ModBlocks.OXYGEN_TEST_BLOCK.get(), new Item.Properties()));
|
||||||
|
public static final DeferredItem<BlockItem> LAUNCH_PAD = ITEMS.register("launch_pad", () -> new BlockItem(ModBlocks.LAUNCH_PAD.get(), LaunchPad.getItemProperties()));
|
||||||
|
public static final DeferredItem<BlockItem> ROCKET_ASSEMBLER = ITEMS.register("rocket_assembler", () -> new BlockItem(ModBlocks.ROCKET_ASSEMBLER.get(), RocketAssembler.getItemProperties()));
|
||||||
|
public static final DeferredItem<BlockItem> ROCKET_SEAT = ITEMS.register("rocket_seat", () -> new BlockItem(ModBlocks.ROCKET_SEAT.get(), RocketSeat.getItemProperties()));
|
||||||
|
public static final DeferredItem<BlockItem> STATION_ROCKET_ENGINE = ITEMS.register("station_rocket_engine", () -> new BlockItem(ModBlocks.STATION_ROCKET_ENGINE.get(), StationRocketEngineBlock.getItemProperties()));
|
||||||
|
public static final DeferredItem<BlockItem> BASIC_ROCKET_FUEL_TANK = ITEMS.register("basic_rocket_fuel_tank", () -> new BlockItem(ModBlocks.BASIC_ROCKET_FUEL_TANK.get(), BasicRocketFuelTank.getItemProperties()));
|
||||||
|
public static final DeferredItem<BlockItem> BASIC_ROCKET_CONTAINER = ITEMS.register("basic_rocket_container", () -> new BlockItem(ModBlocks.BASIC_ROCKET_CONTAINER.get(), BasicRocketContainer.getItemProperties()));
|
||||||
// public static final DeferredItem<BlockItem> VAF_MULTIBLOCK_DUMMY_BLOCK = ITEMS.register("vaf_multiblock_dummy_block", () -> new BlockItem(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), VAFMultiblockDummyBlock.getItemProperties()));
|
// public static final DeferredItem<BlockItem> VAF_MULTIBLOCK_DUMMY_BLOCK = ITEMS.register("vaf_multiblock_dummy_block", () -> new BlockItem(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), VAFMultiblockDummyBlock.getItemProperties()));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public class ModSounds {
|
|||||||
public static final Supplier<SoundEvent> BIT_SHIFT = registerSoundEvent("bit_shift");
|
public static final Supplier<SoundEvent> BIT_SHIFT = registerSoundEvent("bit_shift");
|
||||||
public static final ResourceKey<JukeboxSong> BIT_SHIFT_KEY = createSong("bit_shift");
|
public static final ResourceKey<JukeboxSong> BIT_SHIFT_KEY = createSong("bit_shift");
|
||||||
|
|
||||||
|
public static final Supplier<SoundEvent> ROCKET_ENGINE = registerSoundEvent("rocket_engine");
|
||||||
|
|
||||||
private static Supplier<SoundEvent> registerSoundEvent(String name) {
|
private static Supplier<SoundEvent> registerSoundEvent(String name) {
|
||||||
ResourceLocation id = ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, name);
|
ResourceLocation id = ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, name);
|
||||||
return SOUND_EVENTS.register(name, () -> SoundEvent.createVariableRangeEvent(id));
|
return SOUND_EVENTS.register(name, () -> SoundEvent.createVariableRangeEvent(id));
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
package net.xevianlight.aphelion.core.saveddata;
|
||||||
|
|
||||||
|
import com.jcraft.jorbis.Block;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.saveddata.SavedData;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.client.ClientOxygenCache;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.EnvironmentData;
|
||||||
|
import net.xevianlight.aphelion.planet.Planet;
|
||||||
|
import net.xevianlight.aphelion.planet.PlanetCache;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern:
|
||||||
|
* - World-level SavedData
|
||||||
|
* - Outer map keyed by section (chunkX, sectionY, chunkZ) packed into a long
|
||||||
|
* - Inner map keyed by localIndex (0..4095) -> packed int env value
|
||||||
|
*
|
||||||
|
* Sparse by design: blocks not present in the inner map are implicitly "default environment".
|
||||||
|
*/
|
||||||
|
public class EnvironmentSavedData extends SavedData {
|
||||||
|
|
||||||
|
private final Long2IntOpenHashMap envData = new Long2IntOpenHashMap();
|
||||||
|
|
||||||
|
private static final String NAME = "aphelion_environment";
|
||||||
|
|
||||||
|
public static EnvironmentSavedData create() {
|
||||||
|
return new EnvironmentSavedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public CompoundTag save(@NotNull CompoundTag tag, @NotNull HolderLookup.Provider provider) {
|
||||||
|
int size = envData.size();
|
||||||
|
long[] positions = new long[size];
|
||||||
|
int[] data = new int[size];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (var e : envData.long2IntEntrySet()) {
|
||||||
|
positions[i] = e.getLongKey();
|
||||||
|
data[i] = e.getIntValue();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.putLongArray("Position", positions);
|
||||||
|
tag.putIntArray("Value", data);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnvironmentSavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider) {
|
||||||
|
EnvironmentSavedData data = create();
|
||||||
|
|
||||||
|
if (!tag.contains("Position", Tag.TAG_LONG_ARRAY) || !tag.contains("Value", Tag.TAG_INT_ARRAY)) { return data; }
|
||||||
|
|
||||||
|
long[] positions = tag.getLongArray("Position");
|
||||||
|
int[] values = tag.getIntArray("Value");
|
||||||
|
|
||||||
|
int length = Math.min(positions.length, values.length);
|
||||||
|
|
||||||
|
data.envData.ensureCapacity(length);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
data.envData.put(positions[i], values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnvironmentData getDataForPosition(Level level, BlockPos pos) {
|
||||||
|
Planet planet = PlanetCache.getByDimensionOrNull(level.dimension());
|
||||||
|
int packedDefault;
|
||||||
|
|
||||||
|
if (planet == null) {
|
||||||
|
packedDefault = EnvironmentData.DEFAULT_PACKED;
|
||||||
|
} else {
|
||||||
|
EnvironmentData planetData = new EnvironmentData(planet.oxygen(), EnvironmentData.DEFAULT_TEMPERATURE, (short) planet.gravity());
|
||||||
|
packedDefault = planetData.pack();
|
||||||
|
}
|
||||||
|
int packed = envData.getOrDefault(pos.asLong(),packedDefault);
|
||||||
|
return EnvironmentData.unpack(packed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataForPosition(Level level, BlockPos pos, EnvironmentData data) {
|
||||||
|
putOrRemove(level, pos.asLong(), data.pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOxygen(Level level, BlockPos pos) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
return data.hasOxygen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOxygen(Level level, BlockPos pos, boolean value) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
// Aphelion.LOGGER.info("Set oxygen for {} to {}", pos, value);
|
||||||
|
putOrRemove(level, pos.asLong(), data.withOxygen(value).pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOxygen(Level level, Collection<BlockPos> positions, boolean value) {
|
||||||
|
for (BlockPos pos : positions) {
|
||||||
|
setOxygen(level, pos, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetOxygen(Level level, BlockPos pos) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
putOrRemove(level, pos.asLong(), data.withOxygen(defaultData(level).hasOxygen()).pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetOxygen(Level level, Collection<BlockPos> positions) {
|
||||||
|
for (BlockPos pos : positions) {
|
||||||
|
resetOxygen(level, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getGravity(Level level, BlockPos pos) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
return data.getGravity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGravity(Level level, BlockPos pos, float value) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
putOrRemove(level, pos.asLong(), data.withGravity(value).pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGravity(Level level, Collection<BlockPos> positions, float value) {
|
||||||
|
for (BlockPos pos : positions) {
|
||||||
|
setGravity(level, pos, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetGravity(Level level, BlockPos pos) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
putOrRemove(level, pos.asLong(), data.withGravity(defaultData(level).getGravity()).pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getTemperature(Level level, BlockPos pos) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
return data.getTemperature();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemperature(Level level, BlockPos pos, short value) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
putOrRemove(level, pos.asLong(), data.withTemperature(value).pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemperature(Level level, Collection<BlockPos> positions, short value) {
|
||||||
|
for (BlockPos pos : positions) {
|
||||||
|
setTemperature(level, pos, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetTemperature(Level level, BlockPos pos) {
|
||||||
|
var data = getDataForPosition(level, pos);
|
||||||
|
putOrRemove(level, pos.asLong(), data.withTemperature((defaultData(level).getTemperature())).pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putOrRemove(Level level, long key, int packed) {
|
||||||
|
if (packed == defaultPacked(level)) {
|
||||||
|
envData.remove(key);
|
||||||
|
} else {
|
||||||
|
envData.put(key, packed);
|
||||||
|
}
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int defaultPacked(Level level) {
|
||||||
|
Planet planet = PlanetCache.getByDimensionOrNull(level.dimension());
|
||||||
|
if (planet == null) return EnvironmentData.DEFAULT_PACKED;
|
||||||
|
|
||||||
|
// NOTE: adjust gravity/temperature defaults to whatever your data model intends
|
||||||
|
EnvironmentData planetData = new EnvironmentData(
|
||||||
|
planet.oxygen(),
|
||||||
|
EnvironmentData.DEFAULT_TEMPERATURE,
|
||||||
|
(short) planet.gravity()
|
||||||
|
);
|
||||||
|
return planetData.pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EnvironmentData defaultData(Level level) {
|
||||||
|
return EnvironmentData.unpack(defaultPacked(level));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnvironmentSavedData get(ServerLevel level) {
|
||||||
|
return level.getDataStorage().computeIfAbsent(
|
||||||
|
new Factory<>(EnvironmentSavedData::create, EnvironmentSavedData::load),
|
||||||
|
NAME
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void refreshFromIntegratedServerIfNeeded(Minecraft mc, int radius, int maxBlocks) {
|
||||||
|
if (mc.level == null || mc.player == null) return;
|
||||||
|
|
||||||
|
long gameTime = mc.level.getGameTime();
|
||||||
|
if (ClientOxygenCache.lastUpdateGameTime != -1 && gameTime - ClientOxygenCache.lastUpdateGameTime < 20) return; // every 1s
|
||||||
|
|
||||||
|
BlockPos center = mc.player.blockPosition();
|
||||||
|
if (center.distManhattan(ClientOxygenCache.lastCenter) < 1) return; // don’t refresh if player barely moved
|
||||||
|
|
||||||
|
var server = mc.getSingleplayerServer();
|
||||||
|
if (server == null) return;
|
||||||
|
|
||||||
|
// IMPORTANT: execute on server thread
|
||||||
|
server.execute(() -> {
|
||||||
|
var serverLevel = server.getLevel(mc.level.dimension());
|
||||||
|
if (serverLevel == null) return;
|
||||||
|
|
||||||
|
EnvironmentSavedData env = EnvironmentSavedData.get(serverLevel);
|
||||||
|
|
||||||
|
LongOpenHashSet found = new LongOpenHashSet();
|
||||||
|
|
||||||
|
int r = radius;
|
||||||
|
int scanned = 0;
|
||||||
|
|
||||||
|
// Scan a cube-ish region
|
||||||
|
for (int dy = -r; dy <= r; dy++) {
|
||||||
|
for (int dz = -r; dz <= r; dz++) {
|
||||||
|
for (int dx = -r; dx <= r; dx++) {
|
||||||
|
if (found.size() >= maxBlocks) break;
|
||||||
|
|
||||||
|
BlockPos p = center.offset(dx, dy, dz);
|
||||||
|
|
||||||
|
// optional: skip non-air or skip solid blocks (visual preference)
|
||||||
|
// if (!serverLevel.getBlockState(p).isAir()) continue;
|
||||||
|
|
||||||
|
if (env.hasOxygen(serverLevel, p)) {
|
||||||
|
found.add(p.asLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
scanned++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy results back to client thread safely
|
||||||
|
mc.execute(() -> {
|
||||||
|
ClientOxygenCache.OXYGEN.clear();
|
||||||
|
ClientOxygenCache.OXYGEN.addAll(found);
|
||||||
|
ClientOxygenCache.lastCenter = center;
|
||||||
|
ClientOxygenCache.lastUpdateGameTime = gameTime;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package net.xevianlight.aphelion.core.saveddata;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.saveddata.SavedData;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.GravityData;
|
||||||
|
import net.xevianlight.aphelion.planet.Planet;
|
||||||
|
import net.xevianlight.aphelion.planet.PlanetCache;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern:
|
||||||
|
* - World-level SavedData
|
||||||
|
* - Outer map keyed by section (chunkX, sectionY, chunkZ) packed into a long
|
||||||
|
* - Inner map keyed by localIndex (0..4095) -> packed int env value
|
||||||
|
*
|
||||||
|
* Sparse by design: blocks not present in the inner map are implicitly "default environment".
|
||||||
|
*/
|
||||||
|
public class GravitySavedData extends SavedData {
|
||||||
|
|
||||||
|
private final Long2IntOpenHashMap gravityData = new Long2IntOpenHashMap();
|
||||||
|
|
||||||
|
private static final String NAME = "aphelion_gravity";
|
||||||
|
|
||||||
|
public static GravitySavedData create() {
|
||||||
|
return new GravitySavedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public CompoundTag save(@NotNull CompoundTag tag, @NotNull HolderLookup.Provider provider) {
|
||||||
|
int size = gravityData.size();
|
||||||
|
long[] positions = new long[size];
|
||||||
|
int[] data = new int[size];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (var e : gravityData.long2IntEntrySet()) {
|
||||||
|
positions[i] = e.getLongKey();
|
||||||
|
data[i] = e.getIntValue();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.putLongArray("Position", positions);
|
||||||
|
tag.putIntArray("Value", data);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GravitySavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider) {
|
||||||
|
GravitySavedData data = create();
|
||||||
|
|
||||||
|
if (!tag.contains("Position", Tag.TAG_LONG_ARRAY) || !tag.contains("Value", Tag.TAG_INT_ARRAY)) { return data; }
|
||||||
|
|
||||||
|
long[] positions = tag.getLongArray("Position");
|
||||||
|
int[] values = tag.getIntArray("Value");
|
||||||
|
|
||||||
|
int length = Math.min(positions.length, values.length);
|
||||||
|
|
||||||
|
data.gravityData.ensureCapacity(length);
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
data.gravityData.put(positions[i], values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int ABSENT = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
public @Nullable GravityData getGravityRegionData (Level level, BlockPos center) {
|
||||||
|
GravityData data = GravityData.unpack(gravityData.getOrDefault(center.asLong(), ABSENT));
|
||||||
|
return data.pack() == ABSENT ? null : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGravityRegion (BlockPos pos) {
|
||||||
|
gravityData.remove(pos.asLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGravityRegion (BlockPos pos, GravityData data) {
|
||||||
|
gravityData.put(pos.asLong(), data.pack());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cumulative sum of the acceleration for all gravity regions that overlap this block position
|
||||||
|
*/
|
||||||
|
public float getGravitySum (BlockPos pos) {
|
||||||
|
float sum = 0;
|
||||||
|
|
||||||
|
List<GravityData> regions = getGravityRegions(pos);
|
||||||
|
|
||||||
|
for (var e : regions) {
|
||||||
|
sum += e.getGravity();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the strongest acceleration among all gravity regions that overlap this block position
|
||||||
|
*/
|
||||||
|
public float getGravityMax (BlockPos pos) {
|
||||||
|
float max = 0;
|
||||||
|
|
||||||
|
List<GravityData> regions = getGravityRegions(pos);
|
||||||
|
|
||||||
|
for (var e : regions) {
|
||||||
|
var accel = e.getGravity();
|
||||||
|
if (accel > max) max = accel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all gravity data objects overlapping this position. NOTE: does not contain position of the regions
|
||||||
|
*/
|
||||||
|
public List<GravityData> getGravityRegions (BlockPos pos) {
|
||||||
|
List<GravityData> regions = new ArrayList<>();
|
||||||
|
for (var entry : gravityData.long2IntEntrySet()) {
|
||||||
|
GravityData data = GravityData.unpack(entry.getIntValue());
|
||||||
|
BlockPos center = BlockPos.of(entry.getLongKey());
|
||||||
|
|
||||||
|
if (contains(pos, center, data.getRadius())) {
|
||||||
|
regions.add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean contains(BlockPos pos, BlockPos center, float radius) {
|
||||||
|
float distanceSquared = ((center.getX() - pos.getX()) * (center.getX() - pos.getX())) + ((center.getY() - pos.getY()) * (center.getY() - pos.getY())) + ((center.getZ() - pos.getZ()) * (center.getZ() - pos.getZ()));
|
||||||
|
return distanceSquared <= radius * radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float defaultGravity (Level level) {
|
||||||
|
Planet planet = PlanetCache.getByDimensionOrNull(level.dimension());
|
||||||
|
if (planet == null) return GravityData.DEFAULT_GRAVITY;
|
||||||
|
return planet.gravity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GravitySavedData get(ServerLevel level) {
|
||||||
|
return level.getDataStorage().computeIfAbsent(
|
||||||
|
new Factory<>(GravitySavedData::create, GravitySavedData::load),
|
||||||
|
NAME
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
package net.xevianlight.aphelion.core.saveddata;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.saveddata.SavedData;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class SpacePartitionSavedData extends SavedData {
|
||||||
|
|
||||||
|
private static final String NAME = "aphelion_station_partitions";
|
||||||
|
|
||||||
|
private final Long2ObjectMap<PartitionData> map = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
public static SpacePartitionSavedData create() {
|
||||||
|
return new SpacePartitionSavedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpacePartitionSavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider) {
|
||||||
|
SpacePartitionSavedData data = create();
|
||||||
|
|
||||||
|
ListTag entries = tag.getList("Entries", CompoundTag.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
|
CompoundTag e = entries.getCompound(i);
|
||||||
|
|
||||||
|
long key = e.getLong("Key");
|
||||||
|
|
||||||
|
ResourceLocation orbitRL = null;
|
||||||
|
if (e.contains("Orbit", CompoundTag.TAG_STRING)) {
|
||||||
|
orbitRL = ResourceLocation.tryParse(e.getString("Orbit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
PartitionData pd = new PartitionData(orbitRL);
|
||||||
|
|
||||||
|
// Destination (optional)
|
||||||
|
if (e.contains("Destination", CompoundTag.TAG_STRING)) {
|
||||||
|
ResourceLocation destRL = ResourceLocation.tryParse(e.getString("Destination"));
|
||||||
|
pd.setDestination(destRL); // ok if null (parse fail)
|
||||||
|
} else {
|
||||||
|
pd.setDestination(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traveling (optional; default false)
|
||||||
|
if (e.contains("Traveling", CompoundTag.TAG_BYTE)) {
|
||||||
|
pd.setTraveling(e.getBoolean("Traveling"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distances (optional; default 0.0)
|
||||||
|
if (e.contains("DistanceTraveled", CompoundTag.TAG_DOUBLE)) {
|
||||||
|
pd.setDistanceTraveledAU(e.getDouble("DistanceTraveled"));
|
||||||
|
}
|
||||||
|
if (e.contains("DistanceToDest", CompoundTag.TAG_DOUBLE)) {
|
||||||
|
pd.setTripDistanceAU(e.getDouble("DistanceToDest"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.hasUUID("Owner")) {
|
||||||
|
pd.setOwner(e.getUUID("Owner"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.contains("Generated", CompoundTag.TAG_BYTE)) {
|
||||||
|
pd.setGenerated(e.getBoolean("Generated"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.contains("LandingPads", CompoundTag.TAG_LONG_ARRAY)) {
|
||||||
|
pd.setLandingPadContollersFromArray(e.getLongArray("LandingPads"));
|
||||||
|
}
|
||||||
|
|
||||||
|
data.map.put(key, pd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull CompoundTag save(CompoundTag tag, HolderLookup.@NotNull Provider registries) {
|
||||||
|
ListTag entries = new ListTag();
|
||||||
|
|
||||||
|
map.long2ObjectEntrySet().forEach(entry -> {
|
||||||
|
long key = entry.getLongKey();
|
||||||
|
PartitionData pd = entry.getValue();
|
||||||
|
|
||||||
|
CompoundTag e = new CompoundTag();
|
||||||
|
e.putLong("Key", key);
|
||||||
|
|
||||||
|
// Orbit
|
||||||
|
if (pd.getOrbit() != null) {
|
||||||
|
e.putString("Orbit", pd.getOrbit().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destination (only if present)
|
||||||
|
if (pd.getDestination() != null) {
|
||||||
|
e.putString("Destination", pd.getDestination().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traveling + distances
|
||||||
|
e.putBoolean("Traveling", pd.isTraveling());
|
||||||
|
|
||||||
|
e.putDouble("DistanceTraveled", pd.getDistanceTraveledAU());
|
||||||
|
e.putDouble("DistanceToDest", pd.recalculateTripDistAU());
|
||||||
|
|
||||||
|
if (pd.getOwner() != null) {
|
||||||
|
e.putUUID("Owner", pd.getOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
e.putBoolean("Generated", pd.isGenerated());
|
||||||
|
|
||||||
|
e.putLongArray("LandingPads", pd.getLandingPadContollersAsArray());
|
||||||
|
|
||||||
|
entries.add(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
tag.put("Entries", entries);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public @Nullable ResourceLocation getOrbitForPartition(int px, int pz) {
|
||||||
|
PartitionData data = map.get(pack(px, pz));
|
||||||
|
if (data == null) return null;
|
||||||
|
return map.get(pack(px, pz)).getOrbit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrbitForPartition(int px, int pz, ResourceLocation orbit) {
|
||||||
|
long key = pack(px, pz);
|
||||||
|
PartitionData prev = map.get(key);
|
||||||
|
PartitionData newData = new PartitionData(prev);
|
||||||
|
newData.setOrbit(orbit);
|
||||||
|
if (!newData.equals(prev)) {
|
||||||
|
map.put(key, newData);
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean clearOrbitForPartition(int px, int pz) {
|
||||||
|
long key = pack(px, pz);
|
||||||
|
PartitionData removed = map.remove(key);
|
||||||
|
if (removed != null) {
|
||||||
|
setDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void clearAllOrbits() {
|
||||||
|
if (!map.isEmpty()) {
|
||||||
|
map.clear();
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link PartitionData} stored at the given partition indices.
|
||||||
|
*
|
||||||
|
* <p>The returned {@code PartitionData} is mutable. Any modifications to the returned
|
||||||
|
* object will be persisted to the server.</p>
|
||||||
|
*
|
||||||
|
* <p>If no {@code PartitionData} exists at the specified indices, a new instance is
|
||||||
|
* created using {@code aphelion:orbit/default}, stored in the server cache, and returned.</p>
|
||||||
|
*
|
||||||
|
* @param px the partition X index
|
||||||
|
* @param pz the partition Z index
|
||||||
|
* @return the {@code PartitionData} associated with the specified partition indices
|
||||||
|
*/
|
||||||
|
public @NotNull PartitionData getData(int px, int pz) {
|
||||||
|
long key = pack(px, pz);
|
||||||
|
PartitionData data = map.get(key);
|
||||||
|
if (data == null) {
|
||||||
|
|
||||||
|
// pick a sensible default orbit, or null if you truly allow it
|
||||||
|
data = new PartitionData(Aphelion.id("orbit/default"));
|
||||||
|
map.put(key, data);
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link PartitionData} for the partition containing the given world position.
|
||||||
|
*
|
||||||
|
* <p>The returned {@code PartitionData} is mutable. Any modifications to the returned
|
||||||
|
* object will be persisted to the server.</p>
|
||||||
|
*
|
||||||
|
* <p>If no {@code PartitionData} exists for the partition containing the specified
|
||||||
|
* position, a new instance is created using {@code aphelion:orbit/default},
|
||||||
|
* stored in the server cache, and returned.</p>
|
||||||
|
*
|
||||||
|
* @param x the world X coordinate
|
||||||
|
* @param z the world Z coordinate
|
||||||
|
* @return the {@code PartitionData} associated with the partition containing the position
|
||||||
|
*/
|
||||||
|
public @NotNull PartitionData getDataForPos(int x, int z) {
|
||||||
|
int px = SpacePartition.get(x);
|
||||||
|
int pz = SpacePartition.get(z);
|
||||||
|
long key = pack(px, pz);
|
||||||
|
PartitionData data = map.get(key);
|
||||||
|
if (data == null) {
|
||||||
|
|
||||||
|
data = new PartitionData(Aphelion.id("orbit/default"));
|
||||||
|
map.put(key, data);
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link PartitionData} for the partition containing the given block position.
|
||||||
|
*
|
||||||
|
* <p>The returned {@code PartitionData} is mutable. Any modifications to the returned
|
||||||
|
* object will be persisted to the server.</p>
|
||||||
|
*
|
||||||
|
* <p>If no {@code PartitionData} exists for the partition containing the specified
|
||||||
|
* position, a new instance is created using {@code aphelion:orbit/default},
|
||||||
|
* stored in the server cache, and returned.</p>
|
||||||
|
*
|
||||||
|
* @param pos the world block position
|
||||||
|
* @return the {@code PartitionData} associated with the partition containing the position
|
||||||
|
*/
|
||||||
|
public @NotNull PartitionData getDataForBlockPos(BlockPos pos) {
|
||||||
|
int px = SpacePartition.get(pos.getX());
|
||||||
|
int pz = SpacePartition.get(pos.getZ());
|
||||||
|
long key = pack(px, pz);
|
||||||
|
PartitionData data = map.get(key);
|
||||||
|
if (data == null) {
|
||||||
|
|
||||||
|
data = new PartitionData(Aphelion.id("orbit/default"));
|
||||||
|
map.put(key, data);
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void overwriteAllExistingOrbits(ResourceLocation orbit) {
|
||||||
|
if (map.isEmpty()) return;
|
||||||
|
|
||||||
|
boolean changed = false;
|
||||||
|
for (var entry : map.long2ObjectEntrySet()) {
|
||||||
|
if(!orbit.equals(entry.getValue().getOrbit())) {
|
||||||
|
entry.getValue().setOrbit(orbit);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpacePartitionSavedData get(ServerLevel level) {
|
||||||
|
return level.getDataStorage().computeIfAbsent(
|
||||||
|
new Factory<>(SpacePartitionSavedData::create, SpacePartitionSavedData::load),
|
||||||
|
NAME
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long pack(int px, int pz) {
|
||||||
|
return (((long) px) << 32) | (pz & 0xffffffffL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int unpackX(long key) {
|
||||||
|
return (int)(key >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int unpackZ(long key) {
|
||||||
|
return (int)key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package net.xevianlight.aphelion.core.saveddata.types;
|
||||||
|
|
||||||
|
public record EnvironmentData (boolean oxygen, short temperature, float gravity){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static final boolean DEFAULT_OXYGEN = true;
|
||||||
|
public static final short DEFAULT_TEMPERATURE = (short) 294.2611; // 70F
|
||||||
|
public static final float DEFAULT_GRAVITY = 9.80665f; // 1G
|
||||||
|
|
||||||
|
public static final int DEFAULT_PACKED = new EnvironmentData(DEFAULT_OXYGEN, DEFAULT_TEMPERATURE, DEFAULT_GRAVITY).pack();
|
||||||
|
|
||||||
|
/* We can pack all of this into an int value per block position.
|
||||||
|
* If we have to store partitionData for an entire chunk section (16^3), this amounts to 16kB per section.
|
||||||
|
* 1000 sections touched is 16MB
|
||||||
|
* This is acceptable for partitionData that will only exist where it is not equal to the default values
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static final int OXYGEN_BITS = 1; // Boolean. Do we have oxygen or no?
|
||||||
|
private static final int TEMPERATURE_BITS = Short.SIZE; // 16 bits should suffice for temperature, gives 0k to 65536k range, more than enough
|
||||||
|
private static final int GRAVITY_BITS = 15; // Leftover bits can be assigned to gravity, 32768 values
|
||||||
|
|
||||||
|
private static final float GRAVITY_PRECISION = 100.0f; // 2 decimal precision
|
||||||
|
|
||||||
|
private static final int OXYGEN_BIT = 0;
|
||||||
|
private static final int TEMPERATURE_BIT = OXYGEN_BIT + OXYGEN_BITS; // next 16 bits
|
||||||
|
private static final int GRAVITY_BIT = TEMPERATURE_BIT + TEMPERATURE_BITS; // next 15 bits
|
||||||
|
|
||||||
|
|
||||||
|
public EnvironmentData(boolean oxygen, short temperature, float gravity) {
|
||||||
|
this.oxygen = oxygen;
|
||||||
|
this.temperature = temperature;
|
||||||
|
this.gravity = gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnvironmentData withOxygen(boolean newOxygen) {
|
||||||
|
return new EnvironmentData(newOxygen, this.temperature, this.gravity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnvironmentData withTemperature(short newTemperature) {
|
||||||
|
return new EnvironmentData(this.oxygen, newTemperature, this.gravity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public EnvironmentData withGravity(float newGravity) {
|
||||||
|
return new EnvironmentData(this.oxygen, this.temperature, newGravity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int pack() {
|
||||||
|
int packedData = 0;
|
||||||
|
|
||||||
|
packedData |= (this.oxygen ? 1 : 0) << OXYGEN_BIT;
|
||||||
|
packedData |= (this.temperature & ((1 << TEMPERATURE_BITS) - 1)) << TEMPERATURE_BIT;
|
||||||
|
packedData |= (int) (this.gravity * GRAVITY_PRECISION) << GRAVITY_BIT;
|
||||||
|
|
||||||
|
return packedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnvironmentData unpack(int packedData) {
|
||||||
|
boolean oxygen = ((packedData >> OXYGEN_BIT) & 1) == 1;
|
||||||
|
short temperature = (short) ((packedData >> TEMPERATURE_BIT) & ((1 << TEMPERATURE_BITS) - 1));
|
||||||
|
float gravity = ((packedData >> GRAVITY_BIT) & ((1 << GRAVITY_BITS) - 1)) / GRAVITY_PRECISION;
|
||||||
|
|
||||||
|
return new EnvironmentData(oxygen, temperature, gravity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) return true;
|
||||||
|
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||||
|
var that = (EnvironmentData) obj;
|
||||||
|
return this.oxygen == that.oxygen &&
|
||||||
|
this.temperature == that.temperature &&
|
||||||
|
Float.floatToIntBits(this.gravity) == Float.floatToIntBits(that.gravity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EnvironmentData[" +
|
||||||
|
"oxygen=" + oxygen + ", " +
|
||||||
|
"temperature=" + temperature + ", " +
|
||||||
|
"gravity=" + gravity + ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOxygen() {
|
||||||
|
return oxygen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getTemperature() {
|
||||||
|
return temperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getGravity() {
|
||||||
|
return gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package net.xevianlight.aphelion.core.saveddata.types;
|
||||||
|
|
||||||
|
// A record seems more useful here since this class is really only to help us interpret these values as they are written to the disk as an integer.
|
||||||
|
// We never store GravityData for any reason other than to test its values or to pack it into an integer.
|
||||||
|
public record GravityData (float gravity, float radius) {
|
||||||
|
|
||||||
|
public static final float DEFAULT_GRAVITY = 32.00f; // Vanilla acceleration
|
||||||
|
public static final float GRAVITY_PRECISION = 100.0f;
|
||||||
|
public static final float RADIUS_PRECISION = 100.0f;
|
||||||
|
public static final float MAX_RADIUS = Short.MAX_VALUE / RADIUS_PRECISION;
|
||||||
|
public static final float MAX_GRAVITY = Short.MAX_VALUE / GRAVITY_PRECISION;
|
||||||
|
|
||||||
|
public GravityData {
|
||||||
|
gravity = Math.clamp(gravity, 0f, MAX_GRAVITY);
|
||||||
|
radius = Math.clamp(radius, 0f, MAX_RADIUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int pack() {
|
||||||
|
int g = Math.round(gravity * GRAVITY_PRECISION) & 0xFFFF;
|
||||||
|
int r = Math.round(radius * RADIUS_PRECISION) & 0xFFFF;
|
||||||
|
|
||||||
|
return g | (r << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getGravity() {
|
||||||
|
return gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getRadius() {
|
||||||
|
return radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GravityData unpack(int packed) {
|
||||||
|
float gravity = (packed & 0xFFFF) / GRAVITY_PRECISION;
|
||||||
|
float radius = ((packed >>> 16) & 0xFFFF) / RADIUS_PRECISION;
|
||||||
|
|
||||||
|
return new GravityData(gravity, radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,341 @@
|
|||||||
|
package net.xevianlight.aphelion.core.saveddata.types;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.UUIDUtil;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.xevianlight.aphelion.planet.PlanetCache;
|
||||||
|
import net.xevianlight.aphelion.util.BigCodec;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class PartitionData {
|
||||||
|
public static final int MAX_PADS = 64;
|
||||||
|
private static final StreamCodec<ByteBuf, List<BlockPos>> BLOCKPOS_LIST_CODEC = BlockPos.STREAM_CODEC.apply(ByteBufCodecs.list(MAX_PADS));
|
||||||
|
|
||||||
|
@Nullable private ResourceLocation orbit;
|
||||||
|
@Nullable private ResourceLocation destination;
|
||||||
|
private boolean traveling;
|
||||||
|
/// How far we've already gone
|
||||||
|
private double distanceTraveledAU;
|
||||||
|
/// Total trip distance, from start to finish
|
||||||
|
private double tripDistanceAU;
|
||||||
|
private boolean generated;
|
||||||
|
private UUID owner;
|
||||||
|
private List<BlockPos> landingPadControllers;
|
||||||
|
private List<BlockPos> engines;
|
||||||
|
private double currentOrbitDistanceAU;
|
||||||
|
|
||||||
|
public PartitionData() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public PartitionData(@Nullable ResourceLocation orbit) {
|
||||||
|
this.orbit = orbit;
|
||||||
|
this.destination = null;
|
||||||
|
this.traveling = false;
|
||||||
|
this.distanceTraveledAU = 0;
|
||||||
|
this.tripDistanceAU = 0;
|
||||||
|
this.generated = false;
|
||||||
|
this.owner = null;
|
||||||
|
this.landingPadControllers = List.of();
|
||||||
|
this.engines = new ArrayList<>(List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
public PartitionData(PartitionData other) {
|
||||||
|
this.orbit = other.orbit;
|
||||||
|
this.destination = other.destination;
|
||||||
|
this.traveling = other.traveling;
|
||||||
|
this.distanceTraveledAU = other.distanceTraveledAU;
|
||||||
|
this.tripDistanceAU = other.tripDistanceAU;
|
||||||
|
this.generated = other.generated;
|
||||||
|
this.owner = other.owner;
|
||||||
|
this.landingPadControllers = other.landingPadControllers;
|
||||||
|
this.engines = other.engines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PartitionData> STREAM_CODEC =
|
||||||
|
BigCodec.composite(
|
||||||
|
// orbit is nullable -> optional codec
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
d -> Optional.ofNullable(d.getOrbit()),
|
||||||
|
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
d -> Optional.ofNullable(d.getDestination()),
|
||||||
|
|
||||||
|
ByteBufCodecs.BOOL,
|
||||||
|
PartitionData::isTraveling,
|
||||||
|
|
||||||
|
// doubles -> DOUBLE codec
|
||||||
|
ByteBufCodecs.DOUBLE,
|
||||||
|
PartitionData::getDistanceTraveledAU,
|
||||||
|
|
||||||
|
ByteBufCodecs.DOUBLE,
|
||||||
|
PartitionData::recalculateTripDistAU,
|
||||||
|
|
||||||
|
ByteBufCodecs.optional(UUIDUtil.STREAM_CODEC),
|
||||||
|
d -> Optional.ofNullable(d.getOwner()),
|
||||||
|
|
||||||
|
ByteBufCodecs.BOOL,
|
||||||
|
PartitionData::isGenerated,
|
||||||
|
|
||||||
|
BLOCKPOS_LIST_CODEC,
|
||||||
|
PartitionData::getLandingPadControllers,
|
||||||
|
|
||||||
|
BLOCKPOS_LIST_CODEC,
|
||||||
|
PartitionData::getEngines,
|
||||||
|
|
||||||
|
(orbitOpt, destOpt, traveling, distTraveled, distToDest, ownerOpt, generated, controllers, engines) -> {
|
||||||
|
PartitionData data = new PartitionData(orbitOpt.orElse(null));
|
||||||
|
data.destination = destOpt.orElse(null);
|
||||||
|
data.traveling = traveling;
|
||||||
|
data.distanceTraveledAU = distTraveled;
|
||||||
|
data.tripDistanceAU = distToDest;
|
||||||
|
data.owner = ownerOpt.orElse(null);
|
||||||
|
data.generated = generated;
|
||||||
|
data.landingPadControllers = controllers;
|
||||||
|
data.engines = engines;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
public @Nullable ResourceLocation getOrbit() {
|
||||||
|
return this.orbit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrbit(@Nullable ResourceLocation orbit) {
|
||||||
|
this.orbit = orbit;
|
||||||
|
recalculateTripDistAU();
|
||||||
|
distanceTraveledAU = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ResourceLocation getDestination() {
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDestination(@Nullable ResourceLocation destination) {
|
||||||
|
this.destination = destination;
|
||||||
|
recalculateTripDistAU();
|
||||||
|
distanceTraveledAU = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTraveling() {
|
||||||
|
return traveling;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTraveling(boolean traveling) {
|
||||||
|
this.traveling = traveling;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDistanceTraveledAU() {
|
||||||
|
return distanceTraveledAU;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistanceTraveledAU(double distanceTraveledAU) {
|
||||||
|
this.distanceTraveledAU = distanceTraveledAU;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double recalculateTripDistAU() {
|
||||||
|
var currentPlanet = PlanetCache.getByOrbitOrNull(orbit);
|
||||||
|
if (currentPlanet == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var destPlanet = PlanetCache.getOrDefault(destination);
|
||||||
|
|
||||||
|
var dist = destPlanet.orbitDistance() - currentPlanet.orbitDistance();
|
||||||
|
this.tripDistanceAU = dist;
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTripDistanceAU() {
|
||||||
|
return tripDistanceAU;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTripDistanceAU(double tripDistanceAU) {
|
||||||
|
this.tripDistanceAU = tripDistanceAU;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances travel progress by the specified distance in AU.
|
||||||
|
*
|
||||||
|
* <p>This increases {@code distanceTraveledAU} by the given amount and clamps
|
||||||
|
* the result so it never exceeds {@code tripDistanceAU}.</p>
|
||||||
|
*
|
||||||
|
* <p>If the requested distance would overshoot the destination, the traveled
|
||||||
|
* distance is set to exactly {@code tripDistanceAU}.</p>
|
||||||
|
*
|
||||||
|
* @param distance the distance to advance in astronomical units (AU)
|
||||||
|
* @return {@code true} when we arrive at our destination, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
public boolean travel(double distance) {
|
||||||
|
if (distanceTraveledAU + distance > tripDistanceAU) {
|
||||||
|
distanceTraveledAU = tripDistanceAU;
|
||||||
|
var destinationPlanet = PlanetCache.getOrNull(destination);
|
||||||
|
if (destinationPlanet != null) {
|
||||||
|
setOrbit(destinationPlanet.orbit().location());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
distanceTraveledAU += distance;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGenerated() {
|
||||||
|
return generated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGenerated(boolean generated) {
|
||||||
|
this.generated = generated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable UUID getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(@Nullable UUID owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the world positions of all landing pad controllers tracked
|
||||||
|
* by this partition.
|
||||||
|
*
|
||||||
|
* <p>This method returns only the stored {@link BlockPos} locations of known
|
||||||
|
* landing pad controllers, not the controller instances or block entities
|
||||||
|
* themselves. To interact with a controller, retrieve its block entity from
|
||||||
|
* the world using the returned positions.</p>
|
||||||
|
*
|
||||||
|
* <p>The returned list is a defensive copy and may be modified without affecting
|
||||||
|
* the underlying partition data. To persist changes, use
|
||||||
|
* {@code setLandingPadControllers(...)}.</p>
|
||||||
|
*
|
||||||
|
* @return a mutable copy of the landing pad controller positions known to this partition
|
||||||
|
*/
|
||||||
|
public List<BlockPos> getLandingPadControllers() {
|
||||||
|
return new ArrayList<>(landingPadControllers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLandingPadControllers(List<BlockPos> landingPadControllers) {
|
||||||
|
this.landingPadControllers = landingPadControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a landing pad controller at the specified world position.
|
||||||
|
*
|
||||||
|
* <p>If a controller does not already exist at the given position, it is added
|
||||||
|
* to the internal collection and the method returns {@code true}. If a controller
|
||||||
|
* is already present at that position, no changes are made.</p>
|
||||||
|
*
|
||||||
|
* @param pos the world position of the landing pad controller to add
|
||||||
|
* @return {@code true} if the controller was added, {@code false} if it already existed
|
||||||
|
*/
|
||||||
|
public boolean addLandingPadController(BlockPos pos) {
|
||||||
|
if (!landingPadControllers.contains(pos)) {
|
||||||
|
landingPadControllers.add(pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the landing pad controller at the specified world position.
|
||||||
|
*
|
||||||
|
* <p>If a controller exists at the given position, it is removed from the
|
||||||
|
* internal collection and the method returns {@code true}. If no controller
|
||||||
|
* is present at that position, no changes are made.</p>
|
||||||
|
*
|
||||||
|
* @param pos the world position of the landing pad controller to remove
|
||||||
|
* @return {@code true} if a controller was removed, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
public boolean removeLandingPadController(BlockPos pos) {
|
||||||
|
return landingPadControllers.remove(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a defensive copy of the world positions of all engines tracked by this partition.
|
||||||
|
*
|
||||||
|
* <p>This method returns only the stored {@link BlockPos} locations of known engines,
|
||||||
|
* not the engine instances or their corresponding block entities. To interact with an
|
||||||
|
* engine, retrieve the block entity from the world using the returned positions.</p>
|
||||||
|
*
|
||||||
|
* <p>It is not guaranteed that an engine exists at every returned position. If changes
|
||||||
|
* fail to synchronize with this partition, the stored data may become inaccurate.
|
||||||
|
* Always verify that the block entity at a given position is an engine before use.</p>
|
||||||
|
*
|
||||||
|
* <p>The returned list is a defensive copy and may be modified without affecting the
|
||||||
|
* underlying partition data. To persist changes, use {@code setEngines(...)}.</p>
|
||||||
|
*
|
||||||
|
* @return a mutable list containing the tracked engine positions
|
||||||
|
*/
|
||||||
|
public List<BlockPos> getEngines() {
|
||||||
|
return new ArrayList<>(engines);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEngines(List<BlockPos> engines) {
|
||||||
|
this.engines = engines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an engine at the specified world position.
|
||||||
|
*
|
||||||
|
* <p>If an engine does not already exist at the given position, it is added
|
||||||
|
* to the internal collection and the method returns {@code true}. If an engine
|
||||||
|
* is already present at that position, no changes are made.</p>
|
||||||
|
*
|
||||||
|
* @param pos the world position of the engine to add
|
||||||
|
* @return {@code true} if the engine was added, {@code false} if it already existed
|
||||||
|
*/
|
||||||
|
public boolean addEngine(BlockPos pos) {
|
||||||
|
if (!engines.contains(pos)) {
|
||||||
|
engines.add(pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeEngine(BlockPos pos) {
|
||||||
|
return engines.remove(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||||
|
|
||||||
|
PartitionData that = (PartitionData) obj;
|
||||||
|
|
||||||
|
return Objects.equals(this.orbit, that.orbit)
|
||||||
|
&& Objects.equals(this.destination, that.destination)
|
||||||
|
&& this.traveling == that.traveling
|
||||||
|
&& Double.compare(this.distanceTraveledAU, that.distanceTraveledAU) == 0
|
||||||
|
&& Double.compare(this.tripDistanceAU, that.tripDistanceAU) == 0
|
||||||
|
&& this.generated == that.generated
|
||||||
|
&& Objects.equals(this.owner, that.owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getLandingPadContollersAsArray() {
|
||||||
|
long[] out = new long[landingPadControllers.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (BlockPos pos : landingPadControllers) {
|
||||||
|
out[i] = pos.asLong();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLandingPadContollersFromArray(long[] in) {
|
||||||
|
List<BlockPos> newList = new java.util.ArrayList<>(List.of());
|
||||||
|
int i = 0;
|
||||||
|
for (Long packedPos : in) {
|
||||||
|
newList.add(BlockPos.of(packedPos));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
setLandingPadControllers(newList);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package net.xevianlight.aphelion.core.space;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.core.HolderLookup;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.saveddata.SavedData;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class SpacePartitionSavedData extends SavedData {
|
|
||||||
|
|
||||||
private static final String NAME = "aphelion_station_partitions";
|
|
||||||
|
|
||||||
private final Long2ObjectMap<ResourceLocation> map = new Long2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
public static SpacePartitionSavedData create() {
|
|
||||||
return new SpacePartitionSavedData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpacePartitionSavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider) {
|
|
||||||
SpacePartitionSavedData data = create();
|
|
||||||
|
|
||||||
ListTag entires = tag.getList("Entries", CompoundTag.TAG_COMPOUND);
|
|
||||||
for (int i = 0; i < entires.size(); i++) {
|
|
||||||
CompoundTag e = entires.getCompound(i);
|
|
||||||
long key = e.getLong("Key");
|
|
||||||
String orbit = e.getString("Orbit"); // "aphelion/mars"
|
|
||||||
ResourceLocation orbitRL = ResourceLocation.tryParse(orbit);
|
|
||||||
if (orbitRL != null)
|
|
||||||
data.map.put(key, orbitRL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) {
|
|
||||||
ListTag entries = new ListTag();
|
|
||||||
|
|
||||||
map.long2ObjectEntrySet().forEach(entry -> {
|
|
||||||
CompoundTag e = new CompoundTag();
|
|
||||||
e.putLong("Key", entry.getLongKey());
|
|
||||||
e.putString("Orbit", entry.getValue().toString());
|
|
||||||
entries.add(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
tag.put("Entries", entries);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable ResourceLocation getOrbitForPartition(int px, int pz) {
|
|
||||||
return map.get(pack(px, pz));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOrbitForPartition(int px, int pz, ResourceLocation orbit) {
|
|
||||||
long key = pack(px, pz);
|
|
||||||
ResourceLocation prev = map.get(key);
|
|
||||||
if (!orbit.equals(prev)) {
|
|
||||||
map.put(key, orbit);
|
|
||||||
setDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean clearOrbitForPartition(int px, int pz) {
|
|
||||||
long key = pack(px, pz);
|
|
||||||
ResourceLocation removed = map.remove(key);
|
|
||||||
if (removed != null) {
|
|
||||||
setDirty();;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearAllOrbits() {
|
|
||||||
if (!map.isEmpty()) {
|
|
||||||
map.clear();
|
|
||||||
setDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void overwriteAllExistingOrbits(ResourceLocation orbit) {
|
|
||||||
if (map.isEmpty()) return;
|
|
||||||
|
|
||||||
boolean changed = false;
|
|
||||||
for (var entry : map.long2ObjectEntrySet()) {
|
|
||||||
if(!orbit.equals(entry.getValue())) {
|
|
||||||
entry.setValue(orbit);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) setDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpacePartitionSavedData get(ServerLevel level) {
|
|
||||||
return level.getDataStorage().computeIfAbsent(
|
|
||||||
new Factory<>(SpacePartitionSavedData::create, SpacePartitionSavedData::load),
|
|
||||||
NAME
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long pack(int px, int pz) {
|
|
||||||
return (((long) px) << 32) | (pz & 0xffffffffL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int unpackX(long key) {
|
|
||||||
return (int)(key >> 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int unpackZ(long key) {
|
|
||||||
return (int)key;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -25,6 +25,9 @@ public class ModBlockLootTableProvider extends BlockLootSubProvider {
|
|||||||
dropSelf(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get());
|
dropSelf(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get());
|
||||||
dropSelf(ModBlocks.OXYGEN_TEST_BLOCK.get());
|
dropSelf(ModBlocks.OXYGEN_TEST_BLOCK.get());
|
||||||
dropOther(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), ItemStack.EMPTY.getItem());
|
dropOther(ModBlocks.VAF_MULTIBLOCK_DUMMY_BLOCK.get(), ItemStack.EMPTY.getItem());
|
||||||
|
dropSelf(ModBlocks.LAUNCH_PAD.get());
|
||||||
|
dropSelf(ModBlocks.ROCKET_ASSEMBLER.get());
|
||||||
|
dropSelf(ModBlocks.ROCKET_SEAT.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -25,8 +25,18 @@ public class ModBlockStateProvider extends BlockStateProvider {
|
|||||||
blockItem(ModBlocks.ELECTRIC_ARC_FURNACE);
|
blockItem(ModBlocks.ELECTRIC_ARC_FURNACE);
|
||||||
blockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER);
|
blockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER);
|
||||||
|
|
||||||
|
// horizontalBlock(ModBlocks.ROCKET_ASSEMBLER_BLOCK.get(), models().orientable("aphelion:rocket_assembler_block",
|
||||||
|
// modLoc("block/test_block"),
|
||||||
|
// mcLoc("block/furnace_front"),
|
||||||
|
// modLoc("block/test_block")));
|
||||||
|
blockItem(ModBlocks.ROCKET_ASSEMBLER);
|
||||||
|
|
||||||
blockWithItem(ModBlocks.BLOCK_STEEL);
|
blockWithItem(ModBlocks.BLOCK_STEEL);
|
||||||
blockWithItem(ModBlocks.DIMENSION_CHANGER);
|
blockWithItem(ModBlocks.DIMENSION_CHANGER);
|
||||||
|
|
||||||
|
// this is already defined ourselves
|
||||||
|
// blockItem(ModBlocks.LAUNCH_PAD);
|
||||||
|
|
||||||
blockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK);
|
blockItem(ModBlocks.ARC_FURNACE_CASING_BLOCK);
|
||||||
blockWithItem(ModBlocks.OXYGEN_TEST_BLOCK);
|
blockWithItem(ModBlocks.OXYGEN_TEST_BLOCK);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,12 @@ public class ModBlockTagProvider extends BlockTagsProvider {
|
|||||||
|
|
||||||
tag(ModTags.Blocks.STORAGE_BLOCKS)
|
tag(ModTags.Blocks.STORAGE_BLOCKS)
|
||||||
.add(ModBlocks.BLOCK_STEEL.get());
|
.add(ModBlocks.BLOCK_STEEL.get());
|
||||||
|
|
||||||
|
tag(ModTags.Blocks.LAUNCH_PAD)
|
||||||
|
.add(ModBlocks.LAUNCH_PAD.get());
|
||||||
|
|
||||||
|
tag(ModTags.Blocks.ROCKET_SEAT)
|
||||||
|
.add(ModBlocks.ROCKET_SEAT.get());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import net.minecraft.data.tags.FluidTagsProvider;
|
|||||||
import net.minecraft.tags.FluidTags;
|
import net.minecraft.tags.FluidTags;
|
||||||
import net.neoforged.neoforge.common.data.ExistingFileHelper;
|
import net.neoforged.neoforge.common.data.ExistingFileHelper;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModFluidTags;
|
||||||
import net.xevianlight.aphelion.fluid.ModFluids;
|
import net.xevianlight.aphelion.fluid.ModFluids;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -22,5 +23,7 @@ public class ModFluidTagsProvider extends FluidTagsProvider {
|
|||||||
// tag(FluidTags.LAVA)
|
// tag(FluidTags.LAVA)
|
||||||
// .add(ModFluids.SOURCE_OIL_FLUID.get())
|
// .add(ModFluids.SOURCE_OIL_FLUID.get())
|
||||||
// .add(ModFluids.FLOWING_OIL_FLUID.get());
|
// .add(ModFluids.FLOWING_OIL_FLUID.get());
|
||||||
|
tag(ModFluidTags.ROCKET_FUEL)
|
||||||
|
.add(ModFluids.ROCKET_FUEL.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package net.xevianlight.aphelion.entites.vehicles;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance;
|
||||||
|
import net.minecraft.client.resources.sounds.SoundInstance;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModSounds;
|
||||||
|
|
||||||
|
public class RocketEngineSound extends AbstractTickableSoundInstance {
|
||||||
|
private final RocketEntity rocket;
|
||||||
|
|
||||||
|
public RocketEngineSound (RocketEntity rocket) {
|
||||||
|
super(ModSounds.ROCKET_ENGINE.get(), SoundSource.AMBIENT, SoundInstance.createUnseededRandom());
|
||||||
|
this.rocket = rocket;
|
||||||
|
|
||||||
|
this.looping = true;
|
||||||
|
this.delay = 0;
|
||||||
|
this.volume = 1;
|
||||||
|
this.pitch = 1.0f;
|
||||||
|
|
||||||
|
this.x = rocket.getX();
|
||||||
|
this.y = rocket.getY();
|
||||||
|
this.z = rocket.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (rocket.isRemoved() || rocket.getPhase() != RocketEntity.FlightPhase.ASCEND) {
|
||||||
|
this.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// follow entity
|
||||||
|
this.x = rocket.getX();
|
||||||
|
this.y = rocket.getY();
|
||||||
|
this.z = rocket.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void killSound() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package net.xevianlight.aphelion.entites.vehicles;
|
package net.xevianlight.aphelion.entites.vehicles;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
@@ -10,16 +12,18 @@ import net.minecraft.network.syncher.EntityDataSerializers;
|
|||||||
import net.minecraft.network.syncher.SynchedEntityData;
|
import net.minecraft.network.syncher.SynchedEntityData;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ColumnPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.tags.FluidTags;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.damagesource.DamageSource;
|
import net.minecraft.world.damagesource.DamageSource;
|
||||||
import net.minecraft.world.entity.*;
|
import net.minecraft.world.entity.*;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.entity.vehicle.VehicleEntity;
|
import net.minecraft.world.entity.vehicle.VehicleEntity;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
@@ -27,16 +31,20 @@ import net.minecraft.world.level.portal.DimensionTransition;
|
|||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.neoforged.neoforge.entity.IEntityWithComplexSpawn;
|
import net.neoforged.neoforge.entity.IEntityWithComplexSpawn;
|
||||||
|
import net.neoforged.neoforge.fluids.FluidStack;
|
||||||
import net.neoforged.neoforge.fluids.FluidType;
|
import net.neoforged.neoforge.fluids.FluidType;
|
||||||
|
import net.neoforged.neoforge.fluids.capability.templates.FluidTank;
|
||||||
|
import net.neoforged.neoforge.items.ItemStackHandler;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.core.KeyVariables;
|
import net.xevianlight.aphelion.block.entity.energy.ModEnergyStorage;
|
||||||
import net.xevianlight.aphelion.core.init.ModEntities;
|
import net.xevianlight.aphelion.core.init.ModEntities;
|
||||||
|
import net.xevianlight.aphelion.fluid.ModFluids;
|
||||||
import net.xevianlight.aphelion.util.RocketStructure;
|
import net.xevianlight.aphelion.util.RocketStructure;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpawn {
|
public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpawn {
|
||||||
|
|
||||||
@@ -54,11 +62,10 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
private double landingPosZ;
|
private double landingPosZ;
|
||||||
|
|
||||||
private static final double TELEPORT_Y = 600.0;
|
private static final double TELEPORT_Y = 600.0;
|
||||||
private static final double ASCEND_ACCEL = 0.05;
|
private static final double ASCEND_ACCEL = 0.0125;
|
||||||
private static final double DESCEND_SPEED = 2;
|
private static final double DESCEND_SPEED = 1;
|
||||||
|
|
||||||
private double yVel = 0.0;
|
private double yVel = 0.0;
|
||||||
private boolean jumpWasDown = false;
|
|
||||||
|
|
||||||
private static final EntityDataAccessor<Byte> FLIGHT_PHASE =
|
private static final EntityDataAccessor<Byte> FLIGHT_PHASE =
|
||||||
SynchedEntityData.defineId(RocketEntity.class, EntityDataSerializers.BYTE);
|
SynchedEntityData.defineId(RocketEntity.class, EntityDataSerializers.BYTE);
|
||||||
@@ -66,6 +73,42 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
private static final EntityDataAccessor<CompoundTag> STRUCTURE_TAG =
|
private static final EntityDataAccessor<CompoundTag> STRUCTURE_TAG =
|
||||||
SynchedEntityData.defineId(RocketEntity.class, EntityDataSerializers.COMPOUND_TAG);
|
SynchedEntityData.defineId(RocketEntity.class, EntityDataSerializers.COMPOUND_TAG);
|
||||||
|
|
||||||
|
private final FluidTank FUEL_TANK = newFuelTank(0);
|
||||||
|
private final FluidTank FLUID_STORAGE = newFluidTank(0);
|
||||||
|
private final ModEnergyStorage ENERGY_STORAGE = newEnergyStorage(0, 0);
|
||||||
|
private final ItemStackHandler INVENTORY = new ItemStackHandler(0);
|
||||||
|
|
||||||
|
public ItemStackHandler getInventory() {
|
||||||
|
return INVENTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull FluidTank newFluidTank(int capacity) {
|
||||||
|
return new FluidTank(capacity) {
|
||||||
|
@Override
|
||||||
|
public boolean isFluidValid(@NotNull FluidStack stack) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull FluidTank newFuelTank(int capacity) {
|
||||||
|
return new FluidTank(capacity) {
|
||||||
|
@Override
|
||||||
|
public boolean isFluidValid(@NotNull FluidStack stack) {
|
||||||
|
return stack.is(FluidTags.WATER);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ModEnergyStorage newEnergyStorage(int capacity, int transfer) {
|
||||||
|
return new ModEnergyStorage(capacity, transfer) {
|
||||||
|
@Override
|
||||||
|
public void onEnergyChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static RocketEntity spawnRocket(Level level, BlockPos pos, RocketStructure structure) {
|
public static RocketEntity spawnRocket(Level level, BlockPos pos, RocketStructure structure) {
|
||||||
if (level.isClientSide) return null;
|
if (level.isClientSide) return null;
|
||||||
|
|
||||||
@@ -79,12 +122,38 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
0.0f
|
0.0f
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// rocket.FUEL_TANK.setFluid(new FluidStack(ModFluids.OIL.get(), 1000));
|
||||||
|
// rocket.INVENTORY.setSize(rocket.INVENTORY.getSlots() + 1);
|
||||||
|
// rocket.INVENTORY.insertItem(0, new ItemStack(Items.DIAMOND, 1), false);
|
||||||
|
|
||||||
|
// Fully initialize structure and containers
|
||||||
rocket.setStructure(structure);
|
rocket.setStructure(structure);
|
||||||
|
|
||||||
|
if (rocket.INVENTORY.getSlots() > 0)
|
||||||
|
rocket.INVENTORY.insertItem(0, new ItemStack(Items.DIAMOND), false);
|
||||||
|
if (rocket.FUEL_TANK.getCapacity() != 0)
|
||||||
|
rocket.FUEL_TANK.setFluid(new FluidStack(ModFluids.OIL.get(), 1000));
|
||||||
|
if (rocket.FLUID_STORAGE.getCapacity() != 0)
|
||||||
|
rocket.FLUID_STORAGE.setFluid(FluidStack.EMPTY);
|
||||||
|
|
||||||
|
|
||||||
level.addFreshEntity(rocket);
|
level.addFreshEntity(rocket);
|
||||||
|
|
||||||
return rocket;
|
return rocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void recalculateCapacitiesFromStructure() {
|
||||||
|
int inv = RocketStructure.calculateInventoryCapacity(structure);
|
||||||
|
int fuelCap = RocketStructure.calculateFuelCapacity(structure);
|
||||||
|
int fluidCap = RocketStructure.calculateFluidCapacity(structure);
|
||||||
|
int energyCap = RocketStructure.calculateEnergyCapacity(structure);
|
||||||
|
|
||||||
|
INVENTORY.setSize(inv);
|
||||||
|
FUEL_TANK.setCapacity(fuelCap);
|
||||||
|
FLUID_STORAGE.setCapacity(fluidCap);
|
||||||
|
ENERGY_STORAGE.setCapacity(energyCap);
|
||||||
|
}
|
||||||
|
|
||||||
public void launchTo(ResourceKey<Level> dim, @Nullable BlockPos pos) {
|
public void launchTo(ResourceKey<Level> dim, @Nullable BlockPos pos) {
|
||||||
if (level().isClientSide) return;
|
if (level().isClientSide) return;
|
||||||
|
|
||||||
@@ -101,24 +170,15 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
setPhase(FlightPhase.PREPARE);
|
setPhase(FlightPhase.PREPARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getFirstPlayerPassenger() {
|
|
||||||
if (!this.getPassengers().isEmpty()) {
|
|
||||||
for (int i = 0; i < this.getPassengers().size(); i++) {
|
|
||||||
if (this.getPassengers().get(i) instanceof Player player) {
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
|
||||||
if (!level().isClientSide) {
|
if (!level().isClientSide) {
|
||||||
|
|
||||||
|
if (INVENTORY.getSlots() > 0)
|
||||||
|
INVENTORY.insertItem(0, new ItemStack(Items.DIAMOND, 1), false);
|
||||||
|
|
||||||
switch (getPhase()) {
|
switch (getPhase()) {
|
||||||
case IDLE, LANDED -> tickIdle();
|
case IDLE, LANDED -> tickIdle();
|
||||||
case PREPARE -> tickPrepare();
|
case PREPARE -> tickPrepare();
|
||||||
@@ -134,11 +194,13 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
}
|
}
|
||||||
case DESCEND -> tickDescend();
|
case DESCEND -> tickDescend();
|
||||||
}
|
}
|
||||||
// Simple upward movement
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
move(MoverType.SELF, getDeltaMovement());
|
// Catch ANY edge cases which may cause a crash trying to move an entity as its chunk is unloading
|
||||||
|
if (!this.isRemoved() && this.isAlive() && level().hasChunkAt(blockPosition())) {
|
||||||
|
move(MoverType.SELF, getDeltaMovement());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tickIdle() {
|
private void tickIdle() {
|
||||||
@@ -146,11 +208,6 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tickPrepare() {
|
private void tickPrepare() {
|
||||||
// if (targetDim == this.level().dimension()) {
|
|
||||||
// setPhase(FlightPhase.IDLE);
|
|
||||||
// Aphelion.LOGGER.info("Target dimension matches current dimension");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
setPhase(FlightPhase.ASCEND);
|
setPhase(FlightPhase.ASCEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,10 +246,6 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
landingPosZ = getZ();
|
landingPosZ = getZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute landing Y in destination
|
|
||||||
int hx = (int) Math.floor(landingPosX);
|
|
||||||
int hz = (int) Math.floor(landingPosZ);
|
|
||||||
|
|
||||||
double arrivalY = TELEPORT_Y;
|
double arrivalY = TELEPORT_Y;
|
||||||
|
|
||||||
var passengers = List.copyOf(getPassengers());
|
var passengers = List.copyOf(getPassengers());
|
||||||
@@ -248,6 +301,7 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
if (isOnGround()) {
|
if (isOnGround()) {
|
||||||
resetDeltaMovement();
|
resetDeltaMovement();
|
||||||
setPhase(FlightPhase.LANDED);
|
setPhase(FlightPhase.LANDED);
|
||||||
|
yVel = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDeltaMovement(0, -DESCEND_SPEED, 0);
|
setDeltaMovement(0, -DESCEND_SPEED, 0);
|
||||||
@@ -303,12 +357,13 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
this.refreshDimensions();
|
this.refreshDimensions();
|
||||||
this.setBoundingBox(this.makeBoundingBox());
|
this.setBoundingBox(this.makeBoundingBox());
|
||||||
|
|
||||||
// prevent any internal “snap” from sticking
|
|
||||||
this.moveTo(x, y, z, yRot, xRot);
|
this.moveTo(x, y, z, yRot, xRot);
|
||||||
|
|
||||||
if (!level().isClientSide) {
|
if (!level().isClientSide) {
|
||||||
this.entityData.set(STRUCTURE_TAG, this.structure.save());
|
this.entityData.set(STRUCTURE_TAG, this.structure.save());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recalculateCapacitiesFromStructure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -326,34 +381,35 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RocketEngineSound ascendLoopSound;
|
||||||
|
|
||||||
private void handleClientFlightPhaseChange(FlightPhase phase) {
|
private void handleClientFlightPhaseChange(FlightPhase phase) {
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
case IDLE -> {
|
case IDLE, PREPARE, TRANSIT, DESCEND, LANDED -> {
|
||||||
// var x = 0;
|
Aphelion.LOGGER.info("Rocket state updated to {}", phase);
|
||||||
}
|
if (ascendLoopSound != null) {
|
||||||
case PREPARE -> {
|
ascendLoopSound.killSound();
|
||||||
// var x = 1;
|
ascendLoopSound = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case ASCEND -> {
|
case ASCEND -> {
|
||||||
// var x = 2;
|
if (ascendLoopSound == null || ascendLoopSound.isStopped()) {
|
||||||
}
|
ascendLoopSound = new RocketEngineSound(this);
|
||||||
case TRANSIT -> {
|
Minecraft.getInstance().getSoundManager().play(ascendLoopSound);
|
||||||
// var x = 3;
|
}
|
||||||
}
|
|
||||||
case DESCEND -> {
|
|
||||||
// var x = 4;
|
|
||||||
}
|
|
||||||
case LANDED -> {
|
|
||||||
// var x = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void readAdditionalSaveData(CompoundTag tag) {
|
protected void readAdditionalSaveData(CompoundTag tag) {
|
||||||
|
HolderLookup.Provider registries = level().registryAccess();
|
||||||
|
|
||||||
if (tag.contains("RocketStructure")) {
|
if (tag.contains("RocketStructure")) {
|
||||||
CompoundTag rocketTag = tag.getCompound("RocketStructure");
|
CompoundTag rocketTag = tag.getCompound("RocketStructure");
|
||||||
structure.load(rocketTag);
|
structure.load(rocketTag);
|
||||||
|
// recalculateCapacitiesFromStructure();
|
||||||
|
|
||||||
// Immediately apply correct bbox on load (server + client)
|
// Immediately apply correct bbox on load (server + client)
|
||||||
double x = getX(), y = getY(), z = getZ();
|
double x = getX(), y = getY(), z = getZ();
|
||||||
@@ -382,14 +438,27 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
|
|
||||||
landingPosX = tag.getDouble("LandingX");
|
landingPosX = tag.getDouble("LandingX");
|
||||||
landingPosZ = tag.getDouble("LandingZ");
|
landingPosZ = tag.getDouble("LandingZ");
|
||||||
|
yVel = tag.getDouble("yVelocity");
|
||||||
|
|
||||||
if (tag.contains("FlightPhase", Tag.TAG_BYTE)) {
|
if (tag.contains("FlightPhase", Tag.TAG_BYTE)) {
|
||||||
setPhase(FlightPhase.values()[tag.getByte("FlightPhase")]);
|
setPhase(FlightPhase.values()[tag.getByte("FlightPhase")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tag.contains("Inventory", CompoundTag.TAG_COMPOUND)){
|
||||||
|
INVENTORY.deserializeNBT(registries, tag.getCompound("Inventory"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FUEL_TANK.readFromNBT(registries, tag);
|
||||||
|
FLUID_STORAGE.readFromNBT(registries, tag);
|
||||||
|
|
||||||
|
if (tag.contains("Energy", CompoundTag.TAG_COMPOUND)) {
|
||||||
|
ENERGY_STORAGE.deserializeNBT(registries, tag.getCompound("Energy"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addAdditionalSaveData(CompoundTag tag) {
|
protected void addAdditionalSaveData(CompoundTag tag) {
|
||||||
|
HolderLookup.Provider registries = level().registryAccess();
|
||||||
tag.put("RocketStructure", structure.save());
|
tag.put("RocketStructure", structure.save());
|
||||||
if (targetDim != null)
|
if (targetDim != null)
|
||||||
tag.putString("TargetDim", targetDim.location().toString());
|
tag.putString("TargetDim", targetDim.location().toString());
|
||||||
@@ -399,9 +468,14 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
|
|
||||||
tag.putDouble("LandingX", landingPosX);
|
tag.putDouble("LandingX", landingPosX);
|
||||||
tag.putDouble("LandingZ", landingPosZ);
|
tag.putDouble("LandingZ", landingPosZ);
|
||||||
|
tag.putDouble("yVelocity", yVel);
|
||||||
|
tag.put("Inventory", INVENTORY.serializeNBT(registries));
|
||||||
|
tag = FUEL_TANK.writeToNBT(registries, tag);
|
||||||
|
tag = FLUID_STORAGE.writeToNBT(registries, tag);
|
||||||
|
tag.put("Energy", ENERGY_STORAGE.serializeNBT(registries));
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockPos getTargetPos() {
|
public @Nullable BlockPos getTargetPos() {
|
||||||
return targetPos;
|
return targetPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,15 +534,24 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
return EntityDimensions.scalable(w, h).withEyeHeight(Math.max(0.1f, h - 0.2f));
|
return EntityDimensions.scalable(w, h).withEyeHeight(Math.max(0.1f, h - 0.2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vec3 getSeatWorldPos(int seatIndex) {
|
||||||
|
int packed = structure.packedSeatAt(seatIndex);
|
||||||
|
|
||||||
|
int dx = RocketStructure.unpackX(packed);
|
||||||
|
int dy = RocketStructure.unpackY(packed);
|
||||||
|
int dz = RocketStructure.unpackZ(packed);
|
||||||
|
|
||||||
|
return this.position().add(dx, dy, dz);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void positionRider(@NotNull Entity passenger, @NotNull MoveFunction moveFunction) {
|
public void positionRider(@NotNull Entity passenger, @NotNull MoveFunction move) {
|
||||||
if (!this.hasPassenger(passenger)) return;
|
if (!this.hasPassenger(passenger)) return;
|
||||||
|
|
||||||
// Choose a stable seat position relative to the rocket.
|
Vec3 seat = getSeatWorldPos(0); // primary seat
|
||||||
// Example: centered, and 1.5 blocks above your base Y.
|
move.accept(passenger, seat.x, seat.y, seat.z);
|
||||||
Vec3 seat = new Vec3(this.getX(), this.getY() + structure.computeExtents().toLocalAABB().getYsize() / 2, this.getZ());
|
|
||||||
|
|
||||||
moveFunction.accept(passenger, seat.x, seat.y, seat.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -487,7 +570,7 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
|
|
||||||
public void applyStructureTag(CompoundTag structureTag) {
|
public void applyStructureTag(CompoundTag structureTag) {
|
||||||
this.structure.load(structureTag);
|
this.structure.load(structureTag);
|
||||||
this.refreshDimensions(); // if your hitbox/eye depends on structure
|
this.refreshDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AABB computeWorldAABBFromStructure() {
|
private AABB computeWorldAABBFromStructure() {
|
||||||
@@ -520,4 +603,72 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
|
|||||||
return InteractionResult.sidedSuccess(level().isClientSide);
|
return InteractionResult.sidedSuccess(level().isClientSide);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean disassemble() {
|
||||||
|
if (level().isClientSide) return false;
|
||||||
|
if (!(level() instanceof ServerLevel server)) return false;
|
||||||
|
Aphelion.LOGGER.info("Disassemble called for rocket: " + getId());
|
||||||
|
|
||||||
|
// In rare instances we can disassemble a rocket AFTER it has been killed.
|
||||||
|
// This usually only happens if another class instance still has a reference to this object stored and calls rocketEntity.disassemble().
|
||||||
|
// This SHOULD fix that
|
||||||
|
if (!this.isAlive()) return false; // dead
|
||||||
|
if (this.isRemoved()) return false; // discarded / removed
|
||||||
|
|
||||||
|
ejectPassengers();
|
||||||
|
|
||||||
|
BlockPos origin = BlockPos.containing(getX(), getY(), getZ());
|
||||||
|
|
||||||
|
// Place blocks
|
||||||
|
for (int i = 0; i < structure.size(); i++) {
|
||||||
|
int packed = structure.packedPosAt(i);
|
||||||
|
|
||||||
|
int dx = RocketStructure.unpackX(packed);
|
||||||
|
int dy = RocketStructure.unpackY(packed);
|
||||||
|
int dz = RocketStructure.unpackZ(packed);
|
||||||
|
|
||||||
|
BlockPos wp = origin.offset(dx, dy, dz);
|
||||||
|
var stateToPlace = structure.stateAt(i);
|
||||||
|
|
||||||
|
// Don't place air (shouldn't exist in structure anyway, but safe)
|
||||||
|
if (stateToPlace.isAir()) continue;
|
||||||
|
|
||||||
|
// Safety: don't overwrite existing blocks
|
||||||
|
if (!server.getBlockState(wp).isAir()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.setBlock(wp, stateToPlace, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the rocket entity
|
||||||
|
discard(); // preferred over kill() for "just remove this entity"
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dropItemStackHandler(ServerLevel level, Vec3 pos, ItemStackHandler handler) {
|
||||||
|
for (int i = 0; i < handler.getSlots(); i++) {
|
||||||
|
ItemStack stack = handler.getStackInSlot(i);
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
ItemStack toDrop = stack.copy();
|
||||||
|
handler.setStackInSlot(i, ItemStack.EMPTY);
|
||||||
|
|
||||||
|
ItemEntity ent = new ItemEntity(level, pos.x, pos.y, pos.z, toDrop);
|
||||||
|
level.addFreshEntity(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kill() {
|
||||||
|
if (!level().isClientSide())
|
||||||
|
dropItemStackHandler((ServerLevel) level(), position(), INVENTORY);
|
||||||
|
super.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemovedFromLevel() {
|
||||||
|
if (!level().isClientSide())
|
||||||
|
dropItemStackHandler((ServerLevel) level(), position(), INVENTORY);
|
||||||
|
super.onRemovedFromLevel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,19 +5,19 @@ import net.neoforged.fml.common.EventBusSubscriber;
|
|||||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||||
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
|
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
|
||||||
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
|
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
|
||||||
import net.neoforged.neoforge.network.handlers.ClientPayloadHandler;
|
|
||||||
import net.neoforged.neoforge.network.registration.HandlerThread;
|
import net.neoforged.neoforge.network.registration.HandlerThread;
|
||||||
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
|
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.StationRocketEngineBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.dummy.entity.BaseMultiblockDummyBlockEntity;
|
import net.xevianlight.aphelion.block.dummy.entity.BaseMultiblockDummyBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.dummy.entity.VAFMultiblockDummyBlockEntity;
|
|
||||||
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity;
|
import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity;
|
import net.xevianlight.aphelion.block.entity.custom.VacuumArcFurnaceControllerEntity;
|
||||||
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
import net.xevianlight.aphelion.core.init.ModBlockEntities;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModEntities;
|
||||||
import net.xevianlight.aphelion.network.RocketPayloadHandlers;
|
import net.xevianlight.aphelion.network.RocketPayloadHandlers;
|
||||||
import net.xevianlight.aphelion.network.ServerPayloadHandler;
|
import net.xevianlight.aphelion.network.PartitionPayloadHandler;
|
||||||
import net.xevianlight.aphelion.network.packet.PartitionData;
|
import net.xevianlight.aphelion.network.packet.PartitionPayload;
|
||||||
import net.xevianlight.aphelion.network.packet.RocketLaunchPayload;
|
import net.xevianlight.aphelion.network.packet.RocketLaunchPayload;
|
||||||
|
|
||||||
@EventBusSubscriber(modid = Aphelion.MOD_ID)
|
@EventBusSubscriber(modid = Aphelion.MOD_ID)
|
||||||
@@ -25,13 +25,20 @@ public class ModBusEvents {
|
|||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void registerCapabilities(RegisterCapabilitiesEvent event) {
|
public static void registerCapabilities(RegisterCapabilitiesEvent event) {
|
||||||
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.TEST_BLOCK_ENTITY.get(), TestBlockEntity::getItemHandler);
|
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.TEST_BLOCK_ENTITY.get(), TestBlockEntity::getItemHandler);
|
||||||
|
|
||||||
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getItemHandler);
|
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getItemHandler);
|
||||||
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getEnergyStorage);
|
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getEnergyStorage);
|
||||||
|
|
||||||
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getItemHandler);
|
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getItemHandler);
|
||||||
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getEnergyStorage);
|
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getEnergyStorage);
|
||||||
// event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), VAFMultiblockDummyBlockEntity::getItemHandler);
|
// event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), VAFMultiblockDummyBlockEntity::getItemHandler);
|
||||||
|
|
||||||
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getItemHandler);
|
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getItemHandler);
|
||||||
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getEnergyStorage);
|
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getEnergyStorage);
|
||||||
|
|
||||||
|
event.registerEntity(Capabilities.ItemHandler.ENTITY, ModEntities.ROCKET.get(), (rocket, ctx) -> rocket.getInventory());
|
||||||
|
|
||||||
|
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, ModBlockEntities.STATION_ROCKET_ENGINE_BLOCK_ENTITY.get(), StationRocketEngineBlockEntity::getFluidStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
@@ -40,9 +47,9 @@ public class ModBusEvents {
|
|||||||
.executesOn(HandlerThread.MAIN);
|
.executesOn(HandlerThread.MAIN);
|
||||||
|
|
||||||
registrar.playToClient(
|
registrar.playToClient(
|
||||||
PartitionData.TYPE,
|
PartitionPayload.TYPE,
|
||||||
PartitionData.STREAM_CODEC,
|
PartitionPayload.STREAM_CODEC,
|
||||||
ServerPayloadHandler::handleDataOnMain
|
PartitionPayloadHandler::handleDataOnMain
|
||||||
);
|
);
|
||||||
|
|
||||||
registrar.playToServer(
|
registrar.playToServer(
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import net.minecraft.client.Camera;
|
|||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.FogRenderer;
|
import net.minecraft.client.renderer.FogRenderer;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions;
|
import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions;
|
||||||
|
import net.neoforged.neoforge.common.SoundAction;
|
||||||
|
import net.neoforged.neoforge.common.SoundActions;
|
||||||
import net.neoforged.neoforge.fluids.FluidType;
|
import net.neoforged.neoforge.fluids.FluidType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -53,6 +57,17 @@ public class BaseFluidType extends FluidType {
|
|||||||
this.fogEnd = fogEnd;
|
this.fogEnd = fogEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable SoundEvent getSound(SoundAction action) {
|
||||||
|
if (action == SoundActions.BUCKET_FILL) {
|
||||||
|
return SoundEvents.BUCKET_FILL;
|
||||||
|
}
|
||||||
|
if (action == SoundActions.BUCKET_EMPTY) {
|
||||||
|
return SoundEvents.BUCKET_EMPTY;
|
||||||
|
}
|
||||||
|
return super.getSound(action);
|
||||||
|
}
|
||||||
|
|
||||||
public IClientFluidTypeExtensions getClientFluidTypeExtensions() {
|
public IClientFluidTypeExtensions getClientFluidTypeExtensions() {
|
||||||
return new IClientFluidTypeExtensions() {
|
return new IClientFluidTypeExtensions() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,23 +8,51 @@ import net.neoforged.neoforge.registries.NeoForgeRegistries;
|
|||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class ModFluidTypes {
|
public class ModFluidTypes {
|
||||||
public static final ResourceLocation WATER_STILL_RL = ResourceLocation.parse("block/water_still");
|
public static final ResourceLocation WATER_STILL_RL =
|
||||||
public static final ResourceLocation WATER_FLOWING_RL = ResourceLocation.parse("block/water_flow");
|
ResourceLocation.fromNamespaceAndPath("minecraft", "block/water_still");
|
||||||
public static final ResourceLocation WATER_OVERLAY_RL = ResourceLocation.parse("block/water_overlay");
|
|
||||||
|
public static final ResourceLocation WATER_FLOWING_RL =
|
||||||
|
ResourceLocation.fromNamespaceAndPath("minecraft", "block/water_flow");
|
||||||
|
|
||||||
|
public static final ResourceLocation WATER_OVERLAY_RL =
|
||||||
|
ResourceLocation.fromNamespaceAndPath("minecraft", "block/water_overlay");
|
||||||
|
|
||||||
|
static final Color oilColor = new Color(10, 10, 10, 255);
|
||||||
|
static final Color rocketFuelColor = new Color(73, 59, 28, 255);
|
||||||
|
|
||||||
public static final DeferredRegister<FluidType> FLUID_TYPES =
|
public static final DeferredRegister<FluidType> FLUID_TYPES =
|
||||||
DeferredRegister.create(NeoForgeRegistries.Keys.FLUID_TYPES, Aphelion.MOD_ID);
|
DeferredRegister.create(NeoForgeRegistries.Keys.FLUID_TYPES, Aphelion.MOD_ID);
|
||||||
|
|
||||||
public static final Supplier<FluidType> OIL_FLUID_TYPE = registerFluidType("oil",
|
public static final Supplier<FluidType> OIL_FLUID_TYPE = registerFluidType("oil",
|
||||||
new BaseFluidType(WATER_STILL_RL, WATER_FLOWING_RL, WATER_OVERLAY_RL, 0xFF101010,
|
new BaseFluidType(
|
||||||
new Vector3f(10f / 255f, 10f / 255f, 10f / 255f),
|
WATER_STILL_RL,
|
||||||
FluidType.Properties.create().canDrown(true), 0f, 2f
|
WATER_FLOWING_RL,
|
||||||
|
WATER_OVERLAY_RL,
|
||||||
|
oilColor.getRGB(),
|
||||||
|
colToVec(oilColor),
|
||||||
|
FluidType.Properties.create().canDrown(true),
|
||||||
|
0f,
|
||||||
|
0.5f
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
public static final Supplier<FluidType> ROCKET_FUEL_FLUID_TYPE = registerFluidType("rocket_fuel",
|
||||||
|
new BaseFluidType(
|
||||||
|
WATER_STILL_RL,
|
||||||
|
WATER_FLOWING_RL,
|
||||||
|
WATER_OVERLAY_RL,
|
||||||
|
rocketFuelColor.getRGB(),
|
||||||
|
colToVec(rocketFuelColor),
|
||||||
|
FluidType.Properties.create().canDrown(true),
|
||||||
|
0f,
|
||||||
|
2f)
|
||||||
|
);
|
||||||
|
|
||||||
private static Supplier<FluidType> registerFluidType(String name, FluidType fluidType) {
|
private static Supplier<FluidType> registerFluidType(String name, FluidType fluidType) {
|
||||||
return FLUID_TYPES.register(name, () -> fluidType);
|
return FLUID_TYPES.register(name, () -> fluidType);
|
||||||
}
|
}
|
||||||
@@ -32,4 +60,8 @@ public class ModFluidTypes {
|
|||||||
public static void register(IEventBus eventBus) {
|
public static void register(IEventBus eventBus) {
|
||||||
FLUID_TYPES.register(eventBus);
|
FLUID_TYPES.register(eventBus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Vector3f colToVec (Color color) {
|
||||||
|
return new Vector3f(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,19 +24,35 @@ public class ModFluids {
|
|||||||
public static final DeferredRegister<Fluid> FLUIDS =
|
public static final DeferredRegister<Fluid> FLUIDS =
|
||||||
DeferredRegister.create(BuiltInRegistries.FLUID, Aphelion.MOD_ID);
|
DeferredRegister.create(BuiltInRegistries.FLUID, Aphelion.MOD_ID);
|
||||||
|
|
||||||
public static final Supplier<FlowingFluid> SOURCE_OIL_FLUID = FLUIDS.register("oil",
|
public static final Supplier<FlowingFluid> ROCKET_FUEL = FLUIDS.register("rocket_fuel",
|
||||||
|
() -> new BaseFlowingFluid.Source(ModFluids.ROCKET_FUEL_PROPERTIES));
|
||||||
|
public static final Supplier<FlowingFluid> FLOWING_ROCKET_FUEL = FLUIDS.register("flowing_rocket_fuel",
|
||||||
|
() -> new BaseFlowingFluid.Flowing(ModFluids.ROCKET_FUEL_PROPERTIES));
|
||||||
|
|
||||||
|
public static final DeferredBlock<LiquidBlock> ROCKET_FUEL_BLOCK = ModBlocks.BLOCKS.register("rocket_fuel",
|
||||||
|
() -> new LiquidBlock(ModFluids.ROCKET_FUEL.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.WATER).noLootTable()));
|
||||||
|
|
||||||
|
public static final DeferredItem<Item> ROCKET_FUEL_BUCKET = ModItems.ITEMS.registerItem("rocket_fuel_bucket",
|
||||||
|
properties -> new BucketItem(ModFluids.ROCKET_FUEL.get(), properties.stacksTo(1).craftRemainder(Items.BUCKET)));
|
||||||
|
|
||||||
|
public static final BaseFlowingFluid.Properties ROCKET_FUEL_PROPERTIES = new BaseFlowingFluid.Properties(
|
||||||
|
ModFluidTypes.ROCKET_FUEL_FLUID_TYPE, ROCKET_FUEL, FLOWING_ROCKET_FUEL)
|
||||||
|
.slopeFindDistance(2).levelDecreasePerBlock(2).tickRate(10)
|
||||||
|
.block(ModFluids.ROCKET_FUEL_BLOCK).bucket(ModFluids.ROCKET_FUEL_BUCKET);
|
||||||
|
|
||||||
|
public static final Supplier<FlowingFluid> OIL = FLUIDS.register("oil",
|
||||||
() -> new BaseFlowingFluid.Source(ModFluids.OIL_PROPERTIES));
|
() -> new BaseFlowingFluid.Source(ModFluids.OIL_PROPERTIES));
|
||||||
public static final Supplier<FlowingFluid> FLOWING_OIL_FLUID = FLUIDS.register("flowing_oil",
|
public static final Supplier<FlowingFluid> FLOWING_OIL = FLUIDS.register("flowing_oil",
|
||||||
() -> new BaseFlowingFluid.Flowing(ModFluids.OIL_PROPERTIES));
|
() -> new BaseFlowingFluid.Flowing(ModFluids.OIL_PROPERTIES));
|
||||||
|
|
||||||
public static final DeferredBlock<LiquidBlock> OIL_BLOCK = ModBlocks.BLOCKS.register("oil",
|
public static final DeferredBlock<LiquidBlock> OIL_BLOCK = ModBlocks.BLOCKS.register("oil",
|
||||||
() -> new LiquidBlock(ModFluids.SOURCE_OIL_FLUID.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.WATER).noLootTable()));
|
() -> new LiquidBlock(ModFluids.OIL.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.WATER).noLootTable()));
|
||||||
|
|
||||||
public static final DeferredItem<Item> OIL_BUCKET = ModItems.ITEMS.registerItem("oil_bucket",
|
public static final DeferredItem<Item> OIL_BUCKET = ModItems.ITEMS.registerItem("oil_bucket",
|
||||||
properties -> new BucketItem(ModFluids.SOURCE_OIL_FLUID.get(), properties.stacksTo(1).craftRemainder(Items.BUCKET)));
|
properties -> new BucketItem(ModFluids.OIL.get(), properties.stacksTo(1).craftRemainder(Items.BUCKET)));
|
||||||
|
|
||||||
public static final BaseFlowingFluid.Properties OIL_PROPERTIES = new BaseFlowingFluid.Properties(
|
public static final BaseFlowingFluid.Properties OIL_PROPERTIES = new BaseFlowingFluid.Properties(
|
||||||
ModFluidTypes.OIL_FLUID_TYPE, SOURCE_OIL_FLUID, FLOWING_OIL_FLUID)
|
ModFluidTypes.OIL_FLUID_TYPE, OIL, FLOWING_OIL)
|
||||||
.slopeFindDistance(2).levelDecreasePerBlock(2).tickRate(10)
|
.slopeFindDistance(2).levelDecreasePerBlock(2).tickRate(10)
|
||||||
.block(ModFluids.OIL_BLOCK).bucket(ModFluids.OIL_BUCKET);
|
.block(ModFluids.OIL_BLOCK).bucket(ModFluids.OIL_BUCKET);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package net.xevianlight.aphelion.mixins.common;
|
||||||
|
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.xevianlight.aphelion.systems.OxygenService;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
//
|
||||||
|
@Mixin(LivingEntity.class)
|
||||||
|
public abstract class LivingEntityMixin extends Entity {
|
||||||
|
|
||||||
|
public LivingEntityMixin(EntityType<?> type, Level level) { super(type, level); }
|
||||||
|
|
||||||
|
@Inject(method= "tick", at = @At("TAIL"))
|
||||||
|
public void aphelion$tick(CallbackInfo ci) {
|
||||||
|
if ((level() instanceof ServerLevel level)) {
|
||||||
|
LivingEntity entity = (LivingEntity) (Object) this;
|
||||||
|
|
||||||
|
// Oxygen system
|
||||||
|
OxygenService.entityTick(level, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ public final class KeyNetwork {
|
|||||||
|
|
||||||
// consumeClick makes it fire once per press, not every tick held
|
// consumeClick makes it fire once per press, not every tick held
|
||||||
if (AphelionClient.ROCKET_LAUNCH_KEY.consumeClick() && mc.player.getVehicle() instanceof RocketEntity rocket) {
|
if (AphelionClient.ROCKET_LAUNCH_KEY.consumeClick() && mc.player.getVehicle() instanceof RocketEntity rocket) {
|
||||||
|
|
||||||
|
// Send a packet to the server telling it to try launching the rocket matching this id. The packet only contains the rocketId as an integer.
|
||||||
PacketDistributor.sendToServer(new RocketLaunchPayload(rocket.getId()));
|
PacketDistributor.sendToServer(new RocketLaunchPayload(rocket.getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package net.xevianlight.aphelion.network;
|
|||||||
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
import net.neoforged.neoforge.network.handling.IPayloadContext;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.client.PartitionClientState;
|
import net.xevianlight.aphelion.client.PartitionClientState;
|
||||||
import net.xevianlight.aphelion.network.packet.PartitionData;
|
import net.xevianlight.aphelion.network.packet.PartitionPayload;
|
||||||
|
|
||||||
// Handle packets TO the client FROM the server
|
// Handle packets TO the client FROM the server
|
||||||
public class ServerPayloadHandler {
|
public class PartitionPayloadHandler {
|
||||||
|
|
||||||
public static void handleDataOnMain(PartitionData data, IPayloadContext context) {
|
public static void handleDataOnMain(PartitionPayload data, IPayloadContext context) {
|
||||||
|
// Set our local partition state to the packet we just received.
|
||||||
PartitionClientState.set(data);
|
PartitionClientState.set(data);
|
||||||
Aphelion.LOGGER.info("Partition packet received! id={}", data.id());
|
Aphelion.LOGGER.info("Partition packet received! id={}", data.partitionData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,33 +4,21 @@ package net.xevianlight.aphelion.network;
|
|||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
import net.neoforged.fml.common.EventBusSubscriber;
|
import net.neoforged.fml.common.EventBusSubscriber;
|
||||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
|
||||||
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
import net.neoforged.neoforge.event.tick.ServerTickEvent;
|
||||||
import net.neoforged.neoforge.network.PacketDistributor;
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.core.space.SpacePartitionSavedData;
|
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
|
||||||
import net.xevianlight.aphelion.network.packet.PartitionData;
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
import net.xevianlight.aphelion.util.SpacePartitionHelper;
|
import net.xevianlight.aphelion.network.packet.PartitionPayload;
|
||||||
|
import net.xevianlight.aphelion.util.SpacePartition;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@EventBusSubscriber(modid = Aphelion.MOD_ID)
|
@EventBusSubscriber(modid = Aphelion.MOD_ID)
|
||||||
public final class PartitionSync {
|
public final class PartitionSync {
|
||||||
|
|
||||||
// send once right after join (safe: delayed to next server tick)
|
// Stora all packets we send to all players in a map so we can look it up later
|
||||||
private static final Set<UUID> PENDING_JOIN_SEND = new HashSet<>();
|
private static final java.util.Map<UUID, PartitionPayload> LAST_SENT = new java.util.HashMap<>();
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void onLogin(PlayerEvent.PlayerLoggedInEvent e) {
|
|
||||||
if (e.getEntity() instanceof ServerPlayer sp) {
|
|
||||||
PENDING_JOIN_SEND.add(sp.getUUID());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final java.util.Map<UUID, PartitionData> LAST_SENT = new java.util.HashMap<>();
|
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onServerTick(ServerTickEvent.Post e) {
|
public static void onServerTick(ServerTickEvent.Post e) {
|
||||||
@@ -39,24 +27,29 @@ public final class PartitionSync {
|
|||||||
// Aphelion.LOGGER.info("WORKS!!!");
|
// Aphelion.LOGGER.info("WORKS!!!");
|
||||||
|
|
||||||
for (ServerPlayer sp : server.getPlayerList().getPlayers()) {
|
for (ServerPlayer sp : server.getPlayerList().getPlayers()) {
|
||||||
PartitionData now = computePartitionFor(sp); // your logic
|
|
||||||
PartitionData prev = LAST_SENT.get(sp.getUUID());
|
|
||||||
|
|
||||||
|
// Prepare a new packet and compare it with the last one we sent the player
|
||||||
|
PartitionPayload now = computePartitionFor(sp);
|
||||||
|
PartitionPayload prev = LAST_SENT.get(sp.getUUID());
|
||||||
|
|
||||||
|
// If it is different, send them the new one
|
||||||
if (prev == null || !prev.equals(now)) {
|
if (prev == null || !prev.equals(now)) {
|
||||||
PacketDistributor.sendToPlayer(sp, now);
|
PacketDistributor.sendToPlayer(sp, now);
|
||||||
|
// Store this packet for later
|
||||||
LAST_SENT.put(sp.getUUID(), now);
|
LAST_SENT.put(sp.getUUID(), now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PartitionData computePartitionFor(ServerPlayer sp) {
|
private static PartitionPayload computePartitionFor(ServerPlayer sp) {
|
||||||
// Example: convert player position to partition coords
|
int px = (int)Math.floor(sp.getX() / SpacePartition.SIZE);
|
||||||
int px = (int)Math.floor(sp.getX() / SpacePartitionHelper.SIZE);
|
int pz = (int)Math.floor(sp.getZ() / SpacePartition.SIZE);
|
||||||
int pz = (int)Math.floor(sp.getZ() / SpacePartitionHelper.SIZE);
|
|
||||||
|
|
||||||
var orbit = SpacePartitionSavedData.get(sp.serverLevel()).getOrbitForPartition(px, pz);
|
PartitionData live = SpacePartitionSavedData.get(sp.serverLevel()).getData(px, pz);
|
||||||
String orbitId = (orbit != null) ? orbit.toString() : "aphelion:orbit/default";
|
|
||||||
|
|
||||||
return new PartitionData(orbitId);
|
// snapshot so mutations later don’t affect cached payloads
|
||||||
|
PartitionData snapshot = (live == null) ? null : new PartitionData(live);
|
||||||
|
|
||||||
|
return new PartitionPayload(snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package net.xevianlight.aphelion.network.packet;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import net.minecraft.network.codec.ByteBufCodecs;
|
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
|
||||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
|
||||||
|
|
||||||
public record PartitionData (String id) implements CustomPacketPayload {
|
|
||||||
public static final Type<PartitionData> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "partition_data"));
|
|
||||||
|
|
||||||
public static final StreamCodec<ByteBuf, PartitionData> STREAM_CODEC = StreamCodec.composite(
|
|
||||||
ByteBufCodecs.STRING_UTF8,
|
|
||||||
PartitionData::id,
|
|
||||||
|
|
||||||
PartitionData::new
|
|
||||||
);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type<? extends CustomPacketPayload> type() {
|
|
||||||
return TYPE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package net.xevianlight.aphelion.network.packet;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record PartitionPayload(PartitionData partitionData) implements CustomPacketPayload {
|
||||||
|
public static final Type<PartitionPayload> TYPE =
|
||||||
|
new Type<>(ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "partition_data"));
|
||||||
|
|
||||||
|
public static final StreamCodec<ByteBuf, PartitionPayload> STREAM_CODEC =
|
||||||
|
StreamCodec.composite(
|
||||||
|
PartitionData.STREAM_CODEC,
|
||||||
|
PartitionPayload::partitionData,
|
||||||
|
PartitionPayload::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
PartitionPayload that = (PartitionPayload) o;
|
||||||
|
return partitionData.equals(that.partitionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(partitionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
|
|||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.util.profiling.ProfilerFiller;
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.client.dimension.DimensionRenderer;
|
|
||||||
import net.xevianlight.aphelion.util.Constants;
|
import net.xevianlight.aphelion.util.Constants;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|||||||
12
src/main/java/net/xevianlight/aphelion/planet/Orbit.java
Normal file
12
src/main/java/net/xevianlight/aphelion/planet/Orbit.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package net.xevianlight.aphelion.planet;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
|
||||||
|
public record Orbit(
|
||||||
|
int temp
|
||||||
|
) {
|
||||||
|
public static final Codec<Orbit> CODEC = RecordCodecBuilder.create(inst -> inst.group(
|
||||||
|
Codec.INT.fieldOf("temp").forGetter(Orbit::temp)
|
||||||
|
).apply(inst, Orbit::new));
|
||||||
|
}
|
||||||
@@ -7,15 +7,21 @@ import net.minecraft.resources.ResourceKey;
|
|||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.xevianlight.aphelion.util.registries.ModRegistries;
|
import net.xevianlight.aphelion.util.registries.ModRegistries;
|
||||||
|
|
||||||
public record Planet(
|
public record Planet (
|
||||||
ResourceKey<Level> dimension,
|
ResourceKey<Level> dimension,
|
||||||
|
ResourceKey<Orbit> orbit,
|
||||||
double orbitDistance,
|
double orbitDistance,
|
||||||
ResourceKey<StarSystem> system
|
ResourceKey<StarSystem> system,
|
||||||
|
boolean oxygen,
|
||||||
|
float gravity
|
||||||
) {
|
) {
|
||||||
public static final Codec<Planet> CODEC = RecordCodecBuilder.create(inst -> inst.group(
|
public static final Codec<Planet> CODEC = RecordCodecBuilder.create(inst -> inst.group(
|
||||||
ResourceKey.codec(Registries.DIMENSION).fieldOf("dimension").forGetter(Planet::dimension),
|
ResourceKey.codec(Registries.DIMENSION).fieldOf("dimension").forGetter(Planet::dimension),
|
||||||
Codec.DOUBLE.fieldOf("orbit_distance").forGetter(Planet::orbitDistance),
|
ResourceKey.codec(ModRegistries.ORBIT).fieldOf("orbit").forGetter(Planet::orbit),
|
||||||
ResourceKey.codec(ModRegistries.STAR_SYSTEM).fieldOf("star_system").forGetter(Planet::system)
|
Codec.DOUBLE.fieldOf("orbit_distance").forGetter(Planet::orbitDistance),
|
||||||
|
ResourceKey.codec(ModRegistries.STAR_SYSTEM).fieldOf("star_system").forGetter(Planet::system),
|
||||||
|
Codec.BOOL.fieldOf("oxygen").forGetter(Planet::oxygen),
|
||||||
|
Codec.FLOAT.fieldOf("gravity").forGetter(Planet::gravity)
|
||||||
|
|
||||||
).apply(inst, Planet::new));
|
).apply(inst, Planet::new));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
import net.xevianlight.aphelion.util.registries.ModRegistries;
|
import net.xevianlight.aphelion.util.registries.ModRegistries;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -17,8 +18,11 @@ public final class PlanetCache {
|
|||||||
|
|
||||||
public static final Planet DEFAULT = new Planet(
|
public static final Planet DEFAULT = new Planet(
|
||||||
ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("overworld")),
|
ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("overworld")),
|
||||||
|
ResourceKey.create(ModRegistries.ORBIT, Aphelion.id("orbit/overworld")),
|
||||||
1,
|
1,
|
||||||
ResourceKey.create(ModRegistries.STAR_SYSTEM, Aphelion.id("sol"))
|
ResourceKey.create(ModRegistries.STAR_SYSTEM, Aphelion.id("sol")),
|
||||||
|
true,
|
||||||
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
public static void registerPlanets(Map<ResourceLocation, Planet> planets) {
|
public static void registerPlanets(Map<ResourceLocation, Planet> planets) {
|
||||||
@@ -46,6 +50,17 @@ public final class PlanetCache {
|
|||||||
return PLANETS.getOrDefault(id, DEFAULT);
|
return PLANETS.getOrDefault(id, DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @Nullable Planet getOrNull(ResourceLocation id) {
|
||||||
|
return PLANETS.getOrDefault(id, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable Planet getByOrbitOrNull(ResourceLocation id) {
|
||||||
|
return PLANETS.values().stream()
|
||||||
|
.filter(planet -> planet.orbit().location().equals(id))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
public static Planet getByDimensionOrNull(ResourceKey<Level> dimension) {
|
public static Planet getByDimensionOrNull(ResourceKey<Level> dimension) {
|
||||||
ResourceLocation planetId = PLANET_BY_DIMENSION.get(dimension);
|
ResourceLocation planetId = PLANET_BY_DIMENSION.get(dimension);
|
||||||
return planetId == null ? null : PLANETS.get(planetId);
|
return planetId == null ? null : PLANETS.get(planetId);
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ public record StarSystem(
|
|||||||
int temp
|
int temp
|
||||||
) {
|
) {
|
||||||
public static final Codec<StarSystem> CODEC = RecordCodecBuilder.create(inst -> inst.group(
|
public static final Codec<StarSystem> CODEC = RecordCodecBuilder.create(inst -> inst.group(
|
||||||
Codec.INT.fieldOf("dimension").forGetter(StarSystem::temp)
|
Codec.INT.fieldOf("temp").forGetter(StarSystem::temp)
|
||||||
).apply(inst, StarSystem::new));
|
).apply(inst, StarSystem::new));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,14 @@ import net.minecraft.world.level.Level;
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.neoforged.neoforge.items.SlotItemHandler;
|
import net.neoforged.neoforge.items.SlotItemHandler;
|
||||||
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
|
||||||
|
import net.xevianlight.aphelion.block.entity.custom.IArcFurnaceLike;
|
||||||
import net.xevianlight.aphelion.util.EnergyItemSlot;
|
import net.xevianlight.aphelion.util.EnergyItemSlot;
|
||||||
import net.xevianlight.aphelion.util.ExtractOnlySlot;
|
import net.xevianlight.aphelion.util.ExtractOnlySlot;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class ElectricArcFurnaceMenu extends AbstractContainerMenu {
|
public class ElectricArcFurnaceMenu extends AbstractContainerMenu {
|
||||||
|
|
||||||
public final ElectricArcFurnaceEntity blockEntity;
|
public final IArcFurnaceLike blockEntity;
|
||||||
private final Level level;
|
private final Level level;
|
||||||
private final ContainerData data;
|
private final ContainerData data;
|
||||||
|
|
||||||
@@ -28,17 +29,17 @@ public class ElectricArcFurnaceMenu extends AbstractContainerMenu {
|
|||||||
|
|
||||||
public ElectricArcFurnaceMenu(int i, Inventory inventory, BlockEntity blockEntity, ContainerData data) {
|
public ElectricArcFurnaceMenu(int i, Inventory inventory, BlockEntity blockEntity, ContainerData data) {
|
||||||
super(ModMenuTypes.ELECTRIC_ARC_FURNACE_MENU.get(), i);
|
super(ModMenuTypes.ELECTRIC_ARC_FURNACE_MENU.get(), i);
|
||||||
this.blockEntity = ((ElectricArcFurnaceEntity) blockEntity);
|
this.blockEntity = ((IArcFurnaceLike) blockEntity);
|
||||||
this.level = inventory.player.level();
|
this.level = inventory.player.level();
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
||||||
addPlayerInventory(inventory);
|
addPlayerInventory(inventory);
|
||||||
addPlayerHotbar(inventory);
|
addPlayerHotbar(inventory);
|
||||||
|
|
||||||
this.addSlot(new EnergyItemSlot(this.blockEntity.inventory, ElectricArcFurnaceEntity.ENERGY_SLOT, 8, 54));
|
this.addSlot(new EnergyItemSlot(this.blockEntity.getInventory(), ElectricArcFurnaceEntity.ENERGY_SLOT, 8, 54));
|
||||||
this.addSlot(new SlotItemHandler(this.blockEntity.inventory, ElectricArcFurnaceEntity.INPUT_SLOT, 63, 35));
|
this.addSlot(new SlotItemHandler(this.blockEntity.getInventory(), ElectricArcFurnaceEntity.INPUT_SLOT, 63, 35));
|
||||||
this.addSlot(new SlotItemHandler(this.blockEntity.inventory, ElectricArcFurnaceEntity.SECONDARY_INPUT_SLOT, 40, 35));
|
this.addSlot(new SlotItemHandler(this.blockEntity.getInventory(), ElectricArcFurnaceEntity.SECONDARY_INPUT_SLOT, 40, 35));
|
||||||
this.addSlot(new ExtractOnlySlot(this.blockEntity.inventory, ElectricArcFurnaceEntity.OUTPUT_SLOT, 125, 35));
|
this.addSlot(new ExtractOnlySlot(this.blockEntity.getInventory(), ElectricArcFurnaceEntity.OUTPUT_SLOT, 125, 35));
|
||||||
|
|
||||||
addDataSlots(data);
|
addDataSlots(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ public class ElectricArcFurnaceScreen extends AbstractContainerScreen<ElectricAr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void renderEnergyBar(GuiGraphics guiGraphics, int x, int y) {
|
private void renderEnergyBar(GuiGraphics guiGraphics, int x, int y) {
|
||||||
int stored = menu.blockEntity.getTrueEnergyStorage(null).getEnergyStored();
|
int stored = menu.blockEntity.getTrueEnergyStorage().getEnergyStored();
|
||||||
int max = menu.blockEntity.getTrueEnergyStorage(null).getMaxEnergyStored();
|
int max = menu.blockEntity.getTrueEnergyStorage().getMaxEnergyStored();
|
||||||
|
|
||||||
int h = (max <= 0) ? 0 : (int) Math.round((stored / (double) max) * 42.0);
|
int h = (max <= 0) ? 0 : (int) Math.round((stored / (double) max) * 42.0);
|
||||||
h = Math.max(0, Math.min(42, h));
|
h = Math.max(0, Math.min(42, h));
|
||||||
@@ -79,7 +79,7 @@ public class ElectricArcFurnaceScreen extends AbstractContainerScreen<ElectricAr
|
|||||||
|
|
||||||
private void assignEnergyInfoArea() {
|
private void assignEnergyInfoArea() {
|
||||||
energyInfoArea = new EnergyDisplayTooltipArea(((width - imageWidth) / 2) + 9,
|
energyInfoArea = new EnergyDisplayTooltipArea(((width - imageWidth) / 2) + 9,
|
||||||
((height - imageHeight) / 2 ) + 9, menu.blockEntity.getTrueEnergyStorage(null), 14, 42);
|
((height - imageHeight) / 2 ) + 9, menu.blockEntity.getTrueEnergyStorage(), 14, 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderEnergyAreaTooltip(GuiGraphics guiGraphics, int pMouseX, int pMouseY, int x, int y) {
|
private void renderEnergyAreaTooltip(GuiGraphics guiGraphics, int pMouseX, int pMouseY, int x, int y) {
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ public class VacuumArcFurnaceScreen extends AbstractContainerScreen<VacuumArcFur
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void renderEnergyBar(GuiGraphics guiGraphics, int x, int y) {
|
private void renderEnergyBar(GuiGraphics guiGraphics, int x, int y) {
|
||||||
int stored = menu.blockEntity.getTrueEnergyStorage(null).getEnergyStored();
|
int stored = menu.blockEntity.getTrueEnergyStorage().getEnergyStored();
|
||||||
int max = menu.blockEntity.getTrueEnergyStorage(null).getMaxEnergyStored();
|
int max = menu.blockEntity.getTrueEnergyStorage().getMaxEnergyStored();
|
||||||
|
|
||||||
int h = (max <= 0) ? 0 : (int) Math.round((stored / (double) max) * 42.0);
|
int h = (max <= 0) ? 0 : (int) Math.round((stored / (double) max) * 42.0);
|
||||||
h = Math.max(0, Math.min(42, h));
|
h = Math.max(0, Math.min(42, h));
|
||||||
@@ -79,7 +79,7 @@ public class VacuumArcFurnaceScreen extends AbstractContainerScreen<VacuumArcFur
|
|||||||
|
|
||||||
private void assignEnergyInfoArea() {
|
private void assignEnergyInfoArea() {
|
||||||
energyInfoArea = new EnergyDisplayTooltipArea(((width - imageWidth) / 2) + 9,
|
energyInfoArea = new EnergyDisplayTooltipArea(((width - imageWidth) / 2) + 9,
|
||||||
((height - imageHeight) / 2 ) + 9, menu.blockEntity.getTrueEnergyStorage(null), 14, 42);
|
((height - imageHeight) / 2 ) + 9, menu.blockEntity.getTrueEnergyStorage(), 14, 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderEnergyAreaTooltip(GuiGraphics guiGraphics, int pMouseX, int pMouseY, int x, int y) {
|
private void renderEnergyAreaTooltip(GuiGraphics guiGraphics, int pMouseX, int pMouseY, int x, int y) {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package net.xevianlight.aphelion.systems;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.core.init.ModDamageSources;
|
||||||
|
import net.xevianlight.aphelion.core.saveddata.EnvironmentSavedData;
|
||||||
|
|
||||||
|
public class OxygenService {
|
||||||
|
public static boolean hasOxygen(Level level, BlockPos pos) {
|
||||||
|
if (level.isClientSide) {
|
||||||
|
// We can't pull oxygen data from the client side, so just, uhh, don't!
|
||||||
|
Aphelion.LOGGER.warn("Tried to get server oxygen data from client side!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean positionHasOxygen = EnvironmentSavedData.get((ServerLevel) level).hasOxygen(level, pos);
|
||||||
|
|
||||||
|
return positionHasOxygen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasOxygen(Entity entity) {
|
||||||
|
// Not sure if this is at the entity's feet, head, or the middle... research later
|
||||||
|
// Blockpos is from entity's feet ~Xev
|
||||||
|
BlockPos entityBlockPos = BlockPos.containing(entity.getX(), entity.getY(), entity.getZ());
|
||||||
|
return hasOxygen(entity.level(), entityBlockPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int OXYGEN_DAMAGE_TICK_AMT = 2;
|
||||||
|
public static int OXYGEN_DAMAGE_TICK_FREQ = 20;
|
||||||
|
/// Called by LivingEntity.entityTick mixin
|
||||||
|
public static void entityTick(ServerLevel level, LivingEntity entity) {
|
||||||
|
if (entity.tickCount % OXYGEN_DAMAGE_TICK_FREQ != 0) return;
|
||||||
|
if (hasOxygen(entity)) return;
|
||||||
|
entity.hurt(ModDamageSources.create(level, ModDamageSources.OXYGEN), OXYGEN_DAMAGE_TICK_AMT);
|
||||||
|
}
|
||||||
|
}
|
||||||
500
src/main/java/net/xevianlight/aphelion/util/BigCodec.java
Normal file
500
src/main/java/net/xevianlight/aphelion/util/BigCodec.java
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
package net.xevianlight.aphelion.util;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.*;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since mojang only wanted to implement codecs up to 6 types, here's all the codecs 7-16. You're welcome.
|
||||||
|
* @param <B>
|
||||||
|
* @param <V>
|
||||||
|
*/
|
||||||
|
public interface BigCodec<B, V> extends StreamCodec<B, V> {
|
||||||
|
|
||||||
|
// ---------- 7 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final Function7<T1, T2, T3, T4, T5, T6, T7, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<B, C>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 8 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final Function8<T1, T2, T3, T4, T5, T6, T7, T8, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<B, C>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 9 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
Function9<T1, T2, T3, T4, T5, T6, T7, T8, T9, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 10 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
Function10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 11 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
final StreamCodec<? super B, T11> c11, final Function<C, T11> g11,
|
||||||
|
Function11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
T11 t11 = (T11)c11.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
c11.encode(b, g11.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 12 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
final StreamCodec<? super B, T11> c11, final Function<C, T11> g11,
|
||||||
|
final StreamCodec<? super B, T12> c12, final Function<C, T12> g12,
|
||||||
|
Function12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
T11 t11 = (T11)c11.decode(b);
|
||||||
|
T12 t12 = (T12)c12.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
c11.encode(b, g11.apply(v));
|
||||||
|
c12.encode(b, g12.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 13 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
final StreamCodec<? super B, T11> c11, final Function<C, T11> g11,
|
||||||
|
final StreamCodec<? super B, T12> c12, final Function<C, T12> g12,
|
||||||
|
final StreamCodec<? super B, T13> c13, final Function<C, T13> g13,
|
||||||
|
Function13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
T11 t11 = (T11)c11.decode(b);
|
||||||
|
T12 t12 = (T12)c12.decode(b);
|
||||||
|
T13 t13 = (T13)c13.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
c11.encode(b, g11.apply(v));
|
||||||
|
c12.encode(b, g12.apply(v));
|
||||||
|
c13.encode(b, g13.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 14 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
final StreamCodec<? super B, T11> c11, final Function<C, T11> g11,
|
||||||
|
final StreamCodec<? super B, T12> c12, final Function<C, T12> g12,
|
||||||
|
final StreamCodec<? super B, T13> c13, final Function<C, T13> g13,
|
||||||
|
final StreamCodec<? super B, T14> c14, final Function<C, T14> g14,
|
||||||
|
Function14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
T11 t11 = (T11)c11.decode(b);
|
||||||
|
T12 t12 = (T12)c12.decode(b);
|
||||||
|
T13 t13 = (T13)c13.decode(b);
|
||||||
|
T14 t14 = (T14)c14.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
c11.encode(b, g11.apply(v));
|
||||||
|
c12.encode(b, g12.apply(v));
|
||||||
|
c13.encode(b, g13.apply(v));
|
||||||
|
c14.encode(b, g14.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 15 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
final StreamCodec<? super B, T11> c11, final Function<C, T11> g11,
|
||||||
|
final StreamCodec<? super B, T12> c12, final Function<C, T12> g12,
|
||||||
|
final StreamCodec<? super B, T13> c13, final Function<C, T13> g13,
|
||||||
|
final StreamCodec<? super B, T14> c14, final Function<C, T14> g14,
|
||||||
|
final StreamCodec<? super B, T15> c15, final Function<C, T15> g15,
|
||||||
|
Function15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
T11 t11 = (T11)c11.decode(b);
|
||||||
|
T12 t12 = (T12)c12.decode(b);
|
||||||
|
T13 t13 = (T13)c13.decode(b);
|
||||||
|
T14 t14 = (T14)c14.decode(b);
|
||||||
|
T15 t15 = (T15)c15.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
c11.encode(b, g11.apply(v));
|
||||||
|
c12.encode(b, g12.apply(v));
|
||||||
|
c13.encode(b, g13.apply(v));
|
||||||
|
c14.encode(b, g14.apply(v));
|
||||||
|
c15.encode(b, g15.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 16 ----------
|
||||||
|
static <B, C, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> StreamCodec<B, C> composite(
|
||||||
|
final StreamCodec<? super B, T1> c1, final Function<C, T1> g1,
|
||||||
|
final StreamCodec<? super B, T2> c2, final Function<C, T2> g2,
|
||||||
|
final StreamCodec<? super B, T3> c3, final Function<C, T3> g3,
|
||||||
|
final StreamCodec<? super B, T4> c4, final Function<C, T4> g4,
|
||||||
|
final StreamCodec<? super B, T5> c5, final Function<C, T5> g5,
|
||||||
|
final StreamCodec<? super B, T6> c6, final Function<C, T6> g6,
|
||||||
|
final StreamCodec<? super B, T7> c7, final Function<C, T7> g7,
|
||||||
|
final StreamCodec<? super B, T8> c8, final Function<C, T8> g8,
|
||||||
|
final StreamCodec<? super B, T9> c9, final Function<C, T9> g9,
|
||||||
|
final StreamCodec<? super B, T10> c10, final Function<C, T10> g10,
|
||||||
|
final StreamCodec<? super B, T11> c11, final Function<C, T11> g11,
|
||||||
|
final StreamCodec<? super B, T12> c12, final Function<C, T12> g12,
|
||||||
|
final StreamCodec<? super B, T13> c13, final Function<C, T13> g13,
|
||||||
|
final StreamCodec<? super B, T14> c14, final Function<C, T14> g14,
|
||||||
|
final StreamCodec<? super B, T15> c15, final Function<C, T15> g15,
|
||||||
|
final StreamCodec<? super B, T16> c16, final Function<C, T16> g16,
|
||||||
|
Function16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, C> factory
|
||||||
|
) {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
public @NotNull C decode(@NotNull B b) {
|
||||||
|
T1 t1 = (T1)c1.decode(b);
|
||||||
|
T2 t2 = (T2)c2.decode(b);
|
||||||
|
T3 t3 = (T3)c3.decode(b);
|
||||||
|
T4 t4 = (T4)c4.decode(b);
|
||||||
|
T5 t5 = (T5)c5.decode(b);
|
||||||
|
T6 t6 = (T6)c6.decode(b);
|
||||||
|
T7 t7 = (T7)c7.decode(b);
|
||||||
|
T8 t8 = (T8)c8.decode(b);
|
||||||
|
T9 t9 = (T9)c9.decode(b);
|
||||||
|
T10 t10 = (T10)c10.decode(b);
|
||||||
|
T11 t11 = (T11)c11.decode(b);
|
||||||
|
T12 t12 = (T12)c12.decode(b);
|
||||||
|
T13 t13 = (T13)c13.decode(b);
|
||||||
|
T14 t14 = (T14)c14.decode(b);
|
||||||
|
T15 t15 = (T15)c15.decode(b);
|
||||||
|
T16 t16 = (T16)c16.decode(b);
|
||||||
|
return (C)factory.apply(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void encode(@NotNull B b, @NotNull C v) {
|
||||||
|
c1.encode(b, g1.apply(v));
|
||||||
|
c2.encode(b, g2.apply(v));
|
||||||
|
c3.encode(b, g3.apply(v));
|
||||||
|
c4.encode(b, g4.apply(v));
|
||||||
|
c5.encode(b, g5.apply(v));
|
||||||
|
c6.encode(b, g6.apply(v));
|
||||||
|
c7.encode(b, g7.apply(v));
|
||||||
|
c8.encode(b, g8.apply(v));
|
||||||
|
c9.encode(b, g9.apply(v));
|
||||||
|
c10.encode(b, g10.apply(v));
|
||||||
|
c11.encode(b, g11.apply(v));
|
||||||
|
c12.encode(b, g12.apply(v));
|
||||||
|
c13.encode(b, g13.apply(v));
|
||||||
|
c14.encode(b, g14.apply(v));
|
||||||
|
c15.encode(b, g15.apply(v));
|
||||||
|
c16.encode(b, g16.apply(v));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
114
src/main/java/net/xevianlight/aphelion/util/FloodFill3D.java
Normal file
114
src/main/java/net/xevianlight/aphelion/util/FloodFill3D.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package net.xevianlight.aphelion.util;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public final class FloodFill3D {
|
||||||
|
|
||||||
|
private static final Direction[] DIRECTIONS = Direction.values();
|
||||||
|
|
||||||
|
public static final SolidBlockPredicate TEST_FULL_SEAL = (level, pos, state, positions, queue, direction) -> {
|
||||||
|
if (state.isAir()) return true;
|
||||||
|
if (state.is(ModTags.Blocks.PASSES_FLOOD_FILL)) return true;
|
||||||
|
if (state.is(ModTags.Blocks.BLOCKS_FLOOD_FILL)) return false;
|
||||||
|
if (state.isCollisionShapeFullBlock(level, pos)) return false;
|
||||||
|
|
||||||
|
VoxelShape collisionShape = state.getCollisionShape(level, pos);
|
||||||
|
|
||||||
|
if (collisionShape.isEmpty()) return true;
|
||||||
|
if (!isSideSolid(collisionShape, direction)) return true;
|
||||||
|
if (!isFaceSturdy(collisionShape, direction) && !isFaceSturdy(collisionShape, direction.getOpposite())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the other directions to find a potential path for the partial block.
|
||||||
|
for (Direction dir : DIRECTIONS) {
|
||||||
|
if (dir.getAxis() == direction.getAxis()) continue;
|
||||||
|
var adjacentPos = pos.relative(dir);
|
||||||
|
var adjacentState = level.getBlockState(adjacentPos);
|
||||||
|
if (adjacentState.isAir()) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
positions.add(pos.asLong());
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Set<BlockPos> run(Level level, BlockPos start, int limit, SolidBlockPredicate predicate, boolean retainOrder) {
|
||||||
|
level.getProfiler().push("aphelion-floodfill");
|
||||||
|
|
||||||
|
LongSet positions = retainOrder ? new LongLinkedOpenHashSet(limit) : new LongOpenHashSet(limit);
|
||||||
|
LongArrayFIFOQueue queue = new LongArrayFIFOQueue(limit);
|
||||||
|
queue.enqueue(start.asLong());
|
||||||
|
|
||||||
|
while (!queue.isEmpty() && positions.size() < limit) {
|
||||||
|
long packedPos = queue.dequeueLong();
|
||||||
|
if (positions.contains(packedPos)) continue;
|
||||||
|
positions.add(packedPos);
|
||||||
|
|
||||||
|
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(BlockPos.getX(packedPos), BlockPos.getY(packedPos), BlockPos.getZ(packedPos));
|
||||||
|
for (Direction direction : DIRECTIONS) {
|
||||||
|
pos.set(packedPos);
|
||||||
|
pos.move(direction);
|
||||||
|
BlockState state = level.getBlockState(pos);
|
||||||
|
if (!predicate.test(level, pos, state, positions, queue, direction)) continue;
|
||||||
|
queue.enqueue(pos.asLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<BlockPos> result = retainOrder ? new LinkedHashSet<>(positions.size()) : new HashSet<>(positions.size());
|
||||||
|
for (long pos : positions) {
|
||||||
|
result.add(BlockPos.of(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
level.getProfiler().pop();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSideSolid(VoxelShape collisionShape, Direction dir) {
|
||||||
|
return switch (dir.getAxis()) {
|
||||||
|
case X -> isAxisCovered(collisionShape, Direction.Axis.Y, Direction.Axis.Z);
|
||||||
|
case Y -> isAxisCovered(collisionShape, Direction.Axis.X, Direction.Axis.Z);
|
||||||
|
case Z -> isAxisCovered(collisionShape, Direction.Axis.X, Direction.Axis.Y);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAxisCovered(VoxelShape shape, Direction.Axis axis1, Direction.Axis axis2) {
|
||||||
|
return shape.min(axis1) <= 0 && shape.max(axis1) >= 1 && shape.min(axis2) <= 0 && shape.max(axis2) >= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isFaceSturdy(VoxelShape collisionShape, Direction dir) {
|
||||||
|
VoxelShape faceShape = collisionShape.getFaceShape(dir);
|
||||||
|
if (faceShape.isEmpty()) return true;
|
||||||
|
var aabbs = faceShape.toAabbs();
|
||||||
|
if (aabbs.isEmpty()) return true;
|
||||||
|
return checkBounds(aabbs.get(0), dir.getAxis());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkBounds(AABB bounds, Direction.Axis axis) {
|
||||||
|
return switch (axis) {
|
||||||
|
case X -> bounds.minY <= 0 && bounds.maxY >= 1 && bounds.minZ <= 0 && bounds.maxZ >= 1;
|
||||||
|
case Y -> bounds.minX <= 0 && bounds.maxX >= 1 && bounds.minZ <= 0 && bounds.maxZ >= 1;
|
||||||
|
case Z -> bounds.minX <= 0 && bounds.maxX >= 1 && bounds.minY <= 0 && bounds.maxY >= 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SolidBlockPredicate {
|
||||||
|
|
||||||
|
boolean test(Level level, BlockPos pos, BlockState state, LongSet positions, LongArrayFIFOQueue queue, Direction direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,11 @@ public class ModTags {
|
|||||||
public static final TagKey<Block> STORAGE_BLOCKS = commonTag("storage_blocks");
|
public static final TagKey<Block> STORAGE_BLOCKS = commonTag("storage_blocks");
|
||||||
public static final TagKey<Block> STORAGE_BLOCKS_STEEL = commonTag("storage_blocks/steel");
|
public static final TagKey<Block> STORAGE_BLOCKS_STEEL = commonTag("storage_blocks/steel");
|
||||||
|
|
||||||
|
public static final TagKey<Block> LAUNCH_PAD = createTag("launch_pad");
|
||||||
|
public static final TagKey<Block> PASSES_FLOOD_FILL = createTag("passes_flood_fill");
|
||||||
|
public static final TagKey<Block> BLOCKS_FLOOD_FILL = createTag("blocks_flood_fill");
|
||||||
|
public static final TagKey<Block> ROCKET_SEAT = createTag("rocket_seat");
|
||||||
|
|
||||||
private static TagKey<Block> commonTag(String name) {
|
private static TagKey<Block> commonTag(String name) {
|
||||||
return BlockTags.create(ResourceLocation.fromNamespaceAndPath("c", name));
|
return BlockTags.create(ResourceLocation.fromNamespaceAndPath("c", name));
|
||||||
}
|
}
|
||||||
@@ -25,7 +30,6 @@ public class ModTags {
|
|||||||
public static class Items {
|
public static class Items {
|
||||||
public static final TagKey<Item> TEST_TAG = createTag("test_tag");
|
public static final TagKey<Item> TEST_TAG = createTag("test_tag");
|
||||||
public static final TagKey<Item> INGOTS = commonTag("ingots");
|
public static final TagKey<Item> INGOTS = commonTag("ingots");
|
||||||
|
|
||||||
public static final TagKey<Item> STORAGE_BLOCKS = commonTag("storage_blocks");
|
public static final TagKey<Item> STORAGE_BLOCKS = commonTag("storage_blocks");
|
||||||
public static final TagKey<Item> STORAGE_BLOCKS_STEEL = commonTag("storage_blocks/steel");
|
public static final TagKey<Item> STORAGE_BLOCKS_STEEL = commonTag("storage_blocks/steel");
|
||||||
public static final TagKey<Item> INGOT_ALUMINUM = commonTag("ingots/aluminum");
|
public static final TagKey<Item> INGOT_ALUMINUM = commonTag("ingots/aluminum");
|
||||||
|
|||||||
@@ -2,10 +2,20 @@ package net.xevianlight.aphelion.util;
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.IRocketEnergyUpgrade;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.IRocketFluidUpgrade;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.IRocketFuelUpgrade;
|
||||||
|
import net.xevianlight.aphelion.block.custom.base.IRocketInventoryUpgrade;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -17,6 +27,8 @@ public final class RocketStructure {
|
|||||||
private final IntList packedPositions = new IntArrayList();
|
private final IntList packedPositions = new IntArrayList();
|
||||||
private final IntList paletteIndices = new IntArrayList();
|
private final IntList paletteIndices = new IntArrayList();
|
||||||
|
|
||||||
|
private final IntList seatOffsets = new IntArrayList();
|
||||||
|
|
||||||
public RocketStructure(Builder builder) {
|
public RocketStructure(Builder builder) {
|
||||||
builder.build(this);
|
builder.build(this);
|
||||||
}
|
}
|
||||||
@@ -32,6 +44,7 @@ public final class RocketStructure {
|
|||||||
palette.clear();
|
palette.clear();
|
||||||
packedPositions.clear();
|
packedPositions.clear();
|
||||||
paletteIndices.clear();
|
paletteIndices.clear();
|
||||||
|
seatOffsets.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(int x, int y, int z, BlockState state) {
|
public void add(int x, int y, int z, BlockState state) {
|
||||||
@@ -67,6 +80,8 @@ public final class RocketStructure {
|
|||||||
tag.put("pos", posArr);
|
tag.put("pos", posArr);
|
||||||
tag.put("idx", idxArr);
|
tag.put("idx", idxArr);
|
||||||
|
|
||||||
|
tag.put("seats", new IntArrayTag(seatOffsets.toIntArray()));
|
||||||
|
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +105,11 @@ public final class RocketStructure {
|
|||||||
packedPositions.add(pos[i]);
|
packedPositions.add(pos[i]);
|
||||||
paletteIndices.add(idx[i]);
|
paletteIndices.add(idx[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tag.contains("seats", Tag.TAG_INT_ARRAY)) {
|
||||||
|
int[] seats = tag.getIntArray("seats");
|
||||||
|
for (int s : seats) seatOffsets.add(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int packPos (int x, int y, int z) {
|
public static int packPos (int x, int y, int z) {
|
||||||
@@ -135,4 +155,119 @@ public final class RocketStructure {
|
|||||||
|
|
||||||
return new Extents(minX, minY, minZ, maxX, maxY, maxZ);
|
return new Extents(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void clearCaptured(Level level, BlockPos origin, RocketStructure struct) {
|
||||||
|
final int flags = Block.UPDATE_CLIENTS;
|
||||||
|
|
||||||
|
// Pass 1: remove blocks which implement DOUBLE_BLOCK_HALF like doors to try and prevent duplication.
|
||||||
|
for (int i = 0; i < struct.size(); i++) {
|
||||||
|
int packed = struct.packedPosAt(i);
|
||||||
|
BlockPos wp = origin.offset(
|
||||||
|
RocketStructure.unpackX(packed),
|
||||||
|
RocketStructure.unpackY(packed),
|
||||||
|
RocketStructure.unpackZ(packed)
|
||||||
|
);
|
||||||
|
|
||||||
|
BlockState st = level.getBlockState(wp);
|
||||||
|
if (st.isAir()) continue;
|
||||||
|
|
||||||
|
if (st.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) {
|
||||||
|
DoubleBlockHalf half = st.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF);
|
||||||
|
BlockPos bottom = (half == DoubleBlockHalf.LOWER) ? wp : null;
|
||||||
|
|
||||||
|
// Break the BOTTOM block to stop potential dupes, as it seems that is the "master" block for doors.
|
||||||
|
// If you set the top block to air, the bottom one breaks a moment later and drops.
|
||||||
|
// If this doesn't work I declare it NOT MY FAULT!
|
||||||
|
// DoubleBlockHalf blocks should have a way to delete the entire thing at once god damnit
|
||||||
|
if (bottom != null && !level.getBlockState(bottom).isAir()) {
|
||||||
|
level.setBlock(bottom, Blocks.AIR.defaultBlockState(), flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2: remove likely-attached blocks first. This should stop duplication of torches/buttons/whatever else may break due to its supporting block being broken
|
||||||
|
for (int i = 0; i < struct.size(); i++) {
|
||||||
|
int packed = struct.packedPosAt(i);
|
||||||
|
BlockPos wp = origin.offset(unpackX(packed), unpackY(packed), unpackZ(packed));
|
||||||
|
BlockState st = level.getBlockState(wp);
|
||||||
|
if (st.isAir()) continue;
|
||||||
|
|
||||||
|
// Heuristic: if it isn't a full collision cube, it's often "attached" (buttons, torches, etc.)
|
||||||
|
if (!st.isCollisionShapeFullBlock(level, wp)) {
|
||||||
|
level.setBlock(wp, Blocks.AIR.defaultBlockState(), flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 3: remove the rest
|
||||||
|
for (int i = 0; i < struct.size(); i++) {
|
||||||
|
int packed = struct.packedPosAt(i);
|
||||||
|
BlockPos wp = origin.offset(unpackX(packed), unpackY(packed), unpackZ(packed));
|
||||||
|
if (!level.getBlockState(wp).isAir()) {
|
||||||
|
level.setBlock(wp, Blocks.AIR.defaultBlockState(), flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int seatCount() { return seatOffsets.size(); }
|
||||||
|
public int packedSeatAt(int i) { return seatOffsets.getInt(i); }
|
||||||
|
|
||||||
|
public void addSeatOffset(int dx, int dy, int dz) {
|
||||||
|
seatOffsets.add(packPos(dx, dy, dz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int calculateInventoryCapacity(RocketStructure structure) {
|
||||||
|
int totalSlots = 0;
|
||||||
|
for (int i = 0; i < structure.size(); i++) {
|
||||||
|
BlockState st = structure.stateAt(i);
|
||||||
|
Block block = st.getBlock();
|
||||||
|
|
||||||
|
if (block instanceof IRocketInventoryUpgrade upgrade) {
|
||||||
|
int slots = upgrade.getSlotCapacity();
|
||||||
|
if (slots > 0) totalSlots += slots;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalSlots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int calculateFuelCapacity(RocketStructure structure) {
|
||||||
|
int totalMB = 0;
|
||||||
|
for (int i = 0; i < structure.size(); i++) {
|
||||||
|
BlockState st = structure.stateAt(i);
|
||||||
|
Block block = st.getBlock();
|
||||||
|
|
||||||
|
if (block instanceof IRocketFuelUpgrade upgrade) {
|
||||||
|
int mb = upgrade.getFuelCapacity();
|
||||||
|
if (mb > 0) totalMB += mb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalMB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int calculateFluidCapacity(RocketStructure structure) {
|
||||||
|
int totalMB = 0;
|
||||||
|
for (int i = 0; i < structure.size(); i++) {
|
||||||
|
BlockState st = structure.stateAt(i);
|
||||||
|
Block block = st.getBlock();
|
||||||
|
|
||||||
|
if (block instanceof IRocketFluidUpgrade upgrade) {
|
||||||
|
int mb = upgrade.getFluidCapacity();
|
||||||
|
if (mb > 0) totalMB += mb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalMB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int calculateEnergyCapacity(RocketStructure structure) {
|
||||||
|
int totalFE = 0;
|
||||||
|
for (int i = 0; i < structure.size(); i++) {
|
||||||
|
BlockState st = structure.stateAt(i);
|
||||||
|
Block block = st.getBlock();
|
||||||
|
|
||||||
|
if (block instanceof IRocketEnergyUpgrade upgrade) {
|
||||||
|
int fe = upgrade.getEnergyCapacity();
|
||||||
|
if (fe > 0) totalFE += fe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalFE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package net.xevianlight.aphelion.util;
|
package net.xevianlight.aphelion.util;
|
||||||
|
|
||||||
public class SpacePartitionHelper {
|
public class SpacePartition {
|
||||||
|
|
||||||
public static final int SIZE = 16;
|
public static final int SIZE = 16;
|
||||||
|
|
||||||
189
src/main/java/net/xevianlight/aphelion/util/TechnoFloodFill.java
Normal file
189
src/main/java/net/xevianlight/aphelion/util/TechnoFloodFill.java
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
package net.xevianlight.aphelion.util;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standalone flood-fill utility.
|
||||||
|
* - Traverses blocks starting from origin
|
||||||
|
* - Visits only positions allowed by Passable predicate
|
||||||
|
* - Bounded by maxRange (Manhattan distance)
|
||||||
|
* - Returns all visited positions (excluding origin by default)
|
||||||
|
*/
|
||||||
|
public final class TechnoFloodFill {
|
||||||
|
|
||||||
|
private TechnoFloodFill() {}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Passable {
|
||||||
|
boolean test(Level level, BlockPos pos, BlockState state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Convenience predicate: treat air as passable. */
|
||||||
|
public static final Passable AIR_ONLY = (level, pos, state) -> state.isAir();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a bounded flood fill.
|
||||||
|
*
|
||||||
|
* @param level the world
|
||||||
|
* @param origin starting position (typically BE position)
|
||||||
|
* @param maxRange Manhattan range limit (|dx|+|dy|+|dz|)
|
||||||
|
* @param passable which blocks can be entered/added
|
||||||
|
* @param includeOrigin whether to include origin in the returned list
|
||||||
|
*/
|
||||||
|
public static List<BlockPos> run(Level level, BlockPos origin, int maxRange, Passable passable, boolean includeOrigin) {
|
||||||
|
if (level == null) return List.of();
|
||||||
|
|
||||||
|
// Choose a grid size big enough to cover maxRange in all axes.
|
||||||
|
// We need coordinates within [-maxRange, +maxRange] around origin.
|
||||||
|
// So size must be >= (2*maxRange + 1). Next power-of-two for cheap indexing.
|
||||||
|
int needed = 2 * maxRange + 1;
|
||||||
|
int sizePow2 = nextPow2(needed);
|
||||||
|
int bits = Integer.numberOfTrailingZeros(sizePow2); // since pow2
|
||||||
|
BigVisitedGrid seen = new BigVisitedGrid(bits, origin.getX(), origin.getY(), origin.getZ());
|
||||||
|
|
||||||
|
// Chunk-cached blockstate fetch (no BE state needed)
|
||||||
|
ChunkCache chunkCache = new ChunkCache(level);
|
||||||
|
|
||||||
|
List<BlockPos> out = new ArrayList<>();
|
||||||
|
Deque<BlockPos> stack = new ArrayDeque<>();
|
||||||
|
|
||||||
|
if (includeOrigin) {
|
||||||
|
out.add(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark origin visited so we don't bounce back into it.
|
||||||
|
seen.add(origin.getX(), origin.getY(), origin.getZ());
|
||||||
|
stack.push(origin);
|
||||||
|
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
BlockPos from = stack.pop();
|
||||||
|
|
||||||
|
for (Direction d : Direction.values()) {
|
||||||
|
BlockPos next = from.relative(d);
|
||||||
|
|
||||||
|
if (!inRangeManhattan(origin, next, maxRange)) continue;
|
||||||
|
|
||||||
|
// visited check first: cheapest early-out
|
||||||
|
if (!seen.add(next.getX(), next.getY(), next.getZ())) continue;
|
||||||
|
|
||||||
|
BlockState st = chunkCache.getBlockState(next);
|
||||||
|
if (!passable.test(level, next, st)) continue;
|
||||||
|
|
||||||
|
out.add(next);
|
||||||
|
stack.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean inRangeManhattan(BlockPos a, BlockPos b, int max) {
|
||||||
|
int dx = Math.abs(a.getX() - b.getX());
|
||||||
|
int dy = Math.abs(a.getY() - b.getY());
|
||||||
|
int dz = Math.abs(a.getZ() - b.getZ());
|
||||||
|
return dx + dy + dz <= max;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int nextPow2(int x) {
|
||||||
|
// smallest power of 2 >= x, with a minimum of 8
|
||||||
|
int v = 1;
|
||||||
|
while (v < x) v <<= 1;
|
||||||
|
return Math.max(v, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple chunk cache for repeated reads in flood fill.
|
||||||
|
*/
|
||||||
|
private static final class ChunkCache {
|
||||||
|
private final Level level;
|
||||||
|
private LevelChunk lastChunk;
|
||||||
|
private int lastCx = Integer.MIN_VALUE;
|
||||||
|
private int lastCz = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
private ChunkCache(Level level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockState getBlockState(BlockPos pos) {
|
||||||
|
int cx = pos.getX() >> 4;
|
||||||
|
int cz = pos.getZ() >> 4;
|
||||||
|
|
||||||
|
if (cx != lastCx || cz != lastCz || lastChunk == null) {
|
||||||
|
lastChunk = level.getChunk(cx, cz); // may load/generate; okay for your current usage
|
||||||
|
lastCx = cx;
|
||||||
|
lastCz = cz;
|
||||||
|
}
|
||||||
|
return lastChunk.getBlockState(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packed visited grid centered on an origin.
|
||||||
|
*
|
||||||
|
* Uses an int[] with 32 bits per word:
|
||||||
|
* - size = 2^bits (must be power-of-two)
|
||||||
|
* - Word index layout: ((y * size) + z) * wordsPerRow + (x / 32)
|
||||||
|
* - Bit inside the word: (x % 32)
|
||||||
|
*/
|
||||||
|
private static final class BigVisitedGrid {
|
||||||
|
private final int bits;
|
||||||
|
private final int size;
|
||||||
|
private final int wordsPerRow;
|
||||||
|
private final int[] words;
|
||||||
|
private final int xOff, yOff, zOff;
|
||||||
|
|
||||||
|
BigVisitedGrid(int bits, int xOrigin, int yOrigin, int zOrigin) {
|
||||||
|
if (bits < 3) throw new IllegalArgumentException("Grid too small (bits=" + bits + ")");
|
||||||
|
if (bits > 12) throw new IllegalArgumentException("Grid too large (bits=" + bits + ")");
|
||||||
|
|
||||||
|
this.bits = bits;
|
||||||
|
this.size = 1 << bits;
|
||||||
|
|
||||||
|
// Center origin at middle of grid
|
||||||
|
this.xOff = -xOrigin + (size / 2);
|
||||||
|
this.yOff = -yOrigin + (size / 2);
|
||||||
|
this.zOff = -zOrigin + (size / 2);
|
||||||
|
|
||||||
|
if ((size & 31) != 0) {
|
||||||
|
// to keep wordsPerRow integer
|
||||||
|
throw new IllegalArgumentException("Grid size must be divisible by 32, got " + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wordsPerRow = size >>> 5; // size / 32
|
||||||
|
int totalWords = wordsPerRow * size * size; // (y,z) rows * x-words
|
||||||
|
this.words = new int[totalWords];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if it was NOT previously visited and is now marked visited
|
||||||
|
*/
|
||||||
|
boolean add(int x, int y, int z) {
|
||||||
|
int inX = x + xOff;
|
||||||
|
int inY = y + yOff;
|
||||||
|
int inZ = z + zOff;
|
||||||
|
|
||||||
|
// Bounds check (fast and safe)
|
||||||
|
if ((inX | inY | inZ) < 0 || inX >= size || inY >= size || inZ >= size) {
|
||||||
|
return false; // out of grid => treat as "already seen" to prevent expansion
|
||||||
|
}
|
||||||
|
|
||||||
|
int wordIndex = ((inY * size) + inZ) * wordsPerRow + (inX >>> 5);
|
||||||
|
int bit = 1 << (inX & 31);
|
||||||
|
|
||||||
|
int prev = words[wordIndex];
|
||||||
|
if ((prev & bit) != 0) return false;
|
||||||
|
|
||||||
|
words[wordIndex] = prev | bit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,14 @@ package net.xevianlight.aphelion.util.registries;
|
|||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.xevianlight.aphelion.Aphelion;
|
import net.xevianlight.aphelion.Aphelion;
|
||||||
|
import net.xevianlight.aphelion.planet.Orbit;
|
||||||
import net.xevianlight.aphelion.planet.Planet;
|
import net.xevianlight.aphelion.planet.Planet;
|
||||||
import net.xevianlight.aphelion.planet.StarSystem;
|
import net.xevianlight.aphelion.planet.StarSystem;
|
||||||
|
|
||||||
public class ModRegistries {
|
public class ModRegistries {
|
||||||
public static final ResourceKey<Registry<StarSystem>> STAR_SYSTEM = createRegistryKey("star_system");
|
public static final ResourceKey<Registry<StarSystem>> STAR_SYSTEM = createRegistryKey("star_system");
|
||||||
public static final ResourceKey<Registry<Planet>> PLANET = createRegistryKey("planet");
|
public static final ResourceKey<Registry<Planet>> PLANET = createRegistryKey("planet");
|
||||||
|
public static final ResourceKey<Registry<Orbit>> ORBIT = createRegistryKey("orbit");
|
||||||
|
|
||||||
private static <T> ResourceKey<Registry<T>> createRegistryKey(String name) {
|
private static <T> ResourceKey<Registry<T>> createRegistryKey(String name) {
|
||||||
return ResourceKey.createRegistryKey(Aphelion.id(name));
|
return ResourceKey.createRegistryKey(Aphelion.id(name));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods.
|
# This is an example neoforge.mods.toml file. It contains the partitionData relating to the loading mods.
|
||||||
# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
|
# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
|
||||||
# The overall format is standard TOML format, v0.5.0.
|
# The overall format is standard TOML format, v0.5.0.
|
||||||
# Note that there are a couple of TOML lists in this file.
|
# Note that there are a couple of TOML lists in this file.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user