10 Commits

Author SHA1 Message Date
XevianLight
2496e0cdd5 Basic StationRocketEngineBlock functionality. StationFlightComputerBlock simply sets traveling to true. Rocket crash fixes, 2026-03-28 19:56:15 -06:00
XevianLight
0f4b98a912 More stuff for stations. Cleanup. 2026-02-22 00:36:48 -07:00
XevianLight
018886768e Rockets now support inventories. Fixed pad underside textures. 2026-02-08 22:23:29 -07:00
XevianLight
bc8bb4ee05 PartitionData stuff 2026-02-08 17:03:11 -07:00
XevianLight
ff08a51540 RocketEntity.disassemble and dupe fixes. Fixed VAF menu not appearing. 2026-02-08 02:01:14 -07:00
XevianLight
c0daaf2cfa RocketAssembler now creates rockets! 2026-02-08 00:00:51 -07:00
XevianLight
af1efb5c57 Rocket assembler block bounds calculation and debug renderer 2026-02-07 16:38:46 -07:00
XevianLight
9d186a0dd5 Things n stuff 2026-02-04 22:01:46 -07:00
XevianLight
3557dcdb17 Converted EnvironmentData and GravityData to records cause we never need to modify them 2026-02-04 19:13:15 -07:00
XevianLight
bf09330426 Create test.txt 2026-02-04 11:35:27 -07:00
120 changed files with 3066 additions and 1421 deletions

View File

@@ -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

View File

@@ -1,12 +1,12 @@
// 1.21.1 2026-02-06T10:36:07.5023829 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
086c97543700ccc2f815da4e6c29f11159766889 data/aphelion/loot_table/blocks/gravity_test_block.json
b9cfe672ead8e2673a7b2f5c4cec831e7e8e7040 data/aphelion/loot_table/blocks/launch_pad.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
2c6748b2cfb5b78e0cc95a2f3583d4e50cb4c964 data/aphelion/loot_table/blocks/rocket_assemblerblock.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

View File

@@ -1,5 +1,6 @@
// 1.21.1 2026-01-26T19:04:46.4981336 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 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

View File

@@ -1,21 +1,19 @@
// 1.21.1 2026-02-06T10:36:07.5013847 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
1975ab6c73f76642f8b6cfed00e0739b4ea5775f assets/aphelion/blockstates/gravity_test_block.json
28131a570d3666b7f323de4ad8a69e52ceec92e2 assets/aphelion/blockstates/oxygen_test_block.json 28131a570d3666b7f323de4ad8a69e52ceec92e2 assets/aphelion/blockstates/oxygen_test_block.json
b86c50fddcf6c8c6c19cb748529239d5962a3ede assets/aphelion/blockstates/test_block.json b86c50fddcf6c8c6c19cb748529239d5962a3ede assets/aphelion/blockstates/test_block.json
a810b97f4dace35d026f28d96cb9c47c93600d75 assets/aphelion/models/block/block_steel.json a810b97f4dace35d026f28d96cb9c47c93600d75 assets/aphelion/models/block/block_steel.json
2d3592b7ab7132908709243e97540151e0fb762e assets/aphelion/models/block/dimension_changer.json 2d3592b7ab7132908709243e97540151e0fb762e assets/aphelion/models/block/dimension_changer.json
5f7e8674070f31a63875b5d6147153bfa0eef61a assets/aphelion/models/block/electric_arc_furnace.json 5f7e8674070f31a63875b5d6147153bfa0eef61a assets/aphelion/models/block/electric_arc_furnace.json
ad49034f318c6bc14a4844cf598c6999296aa8a0 assets/aphelion/models/block/gravity_test_block.json
746f23f150a01524ad03cbd1eb822bfbb7cf453b assets/aphelion/models/block/oxygen_test_block.json 746f23f150a01524ad03cbd1eb822bfbb7cf453b assets/aphelion/models/block/oxygen_test_block.json
e0971228b4a1c4bc9dbab58a7dacdc3ae6037e02 assets/aphelion/models/block/test_block.json e0971228b4a1c4bc9dbab58a7dacdc3ae6037e02 assets/aphelion/models/block/test_block.json
cdc831b0f1c462be64825fd34bd446e5b95afac6 assets/aphelion/models/item/arc_furnace_casing.json cdc831b0f1c462be64825fd34bd446e5b95afac6 assets/aphelion/models/item/arc_furnace_casing.json
3599f9037eb2f66de1765318b97ab564c3eae92f assets/aphelion/models/item/block_steel.json 3599f9037eb2f66de1765318b97ab564c3eae92f assets/aphelion/models/item/block_steel.json
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
8de1f7597e1fa82c5603a88040ad935aa1cb9b29 assets/aphelion/models/item/gravity_test_block.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

View File

@@ -1,7 +0,0 @@
{
"variants": {
"": {
"model": "aphelion:block/gravity_test_block"
}
}
}

View File

@@ -1,6 +0,0 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "aphelion:block/gravity_test_block"
}
}

View File

@@ -1,3 +0,0 @@
{
"parent": "aphelion:block/gravity_test_block"
}

View File

@@ -0,0 +1,3 @@
{
"parent": "aphelion:block/rocket_assembler"
}

View File

@@ -11,11 +11,11 @@
"entries": [ "entries": [
{ {
"type": "minecraft:item", "type": "minecraft:item",
"name": "aphelion:gravity_test_block" "name": "aphelion:rocket_assembler"
} }
], ],
"rolls": 1.0 "rolls": 1.0
} }
], ],
"random_sequence": "aphelion:blocks/gravity_test_block" "random_sequence": "aphelion:blocks/rocket_assembler"
} }

View File

@@ -11,11 +11,11 @@
"entries": [ "entries": [
{ {
"type": "minecraft:item", "type": "minecraft:item",
"name": "minecraft:air" "name": "aphelion:rocket_seat"
} }
], ],
"rolls": 1.0 "rolls": 1.0
} }
], ],
"random_sequence": "aphelion:blocks/rocket_assemblerblock" "random_sequence": "aphelion:blocks/rocket_seat"
} }

View File

@@ -0,0 +1,5 @@
{
"values": [
"aphelion:rocket_seat"
]
}

View File

@@ -0,0 +1,5 @@
{
"values": [
"aphelion:rocket_fuel"
]
}

View File

@@ -1,6 +1,8 @@
package net.xevianlight.aphelion; package net.xevianlight.aphelion;
import net.minecraft.client.Minecraft; 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.minecraft.server.level.ServerPlayer;
import net.neoforged.api.distmarker.Dist; import net.neoforged.api.distmarker.Dist;
@@ -15,6 +17,7 @@ import net.neoforged.neoforge.event.tick.ServerTickEvent;
import net.neoforged.neoforge.network.PacketDistributor; 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.core.saveddata.EnvironmentSavedData;
import net.xevianlight.aphelion.network.packet.PartitionPayload; import net.xevianlight.aphelion.network.packet.PartitionPayload;
@@ -24,7 +27,10 @@ import net.xevianlight.aphelion.fluid.BaseFluidType;
import net.xevianlight.aphelion.fluid.ModFluidTypes; import net.xevianlight.aphelion.fluid.ModFluidTypes;
import net.xevianlight.aphelion.fluid.ModFluids; import net.xevianlight.aphelion.fluid.ModFluids;
import net.xevianlight.aphelion.recipe.ModRecipes; import net.xevianlight.aphelion.recipe.ModRecipes;
import net.xevianlight.aphelion.screen.*; import net.xevianlight.aphelion.screen.ElectricArcFurnaceScreen;
import net.xevianlight.aphelion.screen.ModMenuTypes;
import net.xevianlight.aphelion.screen.TestBlockScreen;
import net.xevianlight.aphelion.screen.VacuumArcFurnaceScreen;
import org.slf4j.Logger; import org.slf4j.Logger;
import net.xevianlight.aphelion.entites.vehicles.RocketRenderer; import net.xevianlight.aphelion.entites.vehicles.RocketRenderer;
@@ -123,19 +129,21 @@ 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.ROCKET_ASSEMBLER_BLOCK_ENTITY.get(), RocketAssemblerBlockEntityRenderer::new);
// event.registerBlockEntityRenderer(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), OxygenTestRenderer::new); // event.registerBlockEntityRenderer(ModBlockEntities.OXYGEN_TEST_BLOCK_ENTITY.get(), OxygenTestRenderer::new);
} }
@@ -144,7 +152,6 @@ public class Aphelion {
event.register(ModMenuTypes.TEST_BLOCK_MENU.get(), TestBlockScreen::new); event.register(ModMenuTypes.TEST_BLOCK_MENU.get(), TestBlockScreen::new);
event.register(ModMenuTypes.ELECTRIC_ARC_FURNACE_MENU.get(), ElectricArcFurnaceScreen::new); event.register(ModMenuTypes.ELECTRIC_ARC_FURNACE_MENU.get(), ElectricArcFurnaceScreen::new);
event.register(ModMenuTypes.VACUUM_ARC_FURNACE_MENU.get(), VacuumArcFurnaceScreen::new); event.register(ModMenuTypes.VACUUM_ARC_FURNACE_MENU.get(), VacuumArcFurnaceScreen::new);
event.register(ModMenuTypes.GRAVITY_TEST_BLOCK_MENU.get(), GravityTestBlockScreen::new);
} }
@SubscribeEvent @SubscribeEvent

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
} }
@@ -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);

View File

@@ -1,57 +0,0 @@
package net.xevianlight.aphelion.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.SimpleMenuProvider;
import net.minecraft.world.entity.player.Player;
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.minecraft.world.phys.BlockHitResult;
import net.xevianlight.aphelion.block.custom.base.BasicEntityBlock;
import net.xevianlight.aphelion.block.entity.custom.GravityTestBlockEntity;
import net.xevianlight.aphelion.block.entity.custom.OxygenTestBlockEntity;
import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GravityTestBlock extends BasicEntityBlock {
public GravityTestBlock(Properties properties) {super(properties, true);}
@Override
protected MapCodec<? extends BaseEntityBlock> codec() {return null;}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new GravityTestBlockEntity(blockPos, blockState);
}
public static Properties getProperties() {return Properties.of();}
@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 GravityTestBlockEntity BE) {
BE.removeGravityArea();
}
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
@Override
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) {
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof GravityTestBlockEntity testBlockEntity) {
serverPlayer.openMenu(new SimpleMenuProvider(testBlockEntity, Component.literal("Gravity Test Block")), pos);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
}

View File

@@ -2,8 +2,12 @@ package net.xevianlight.aphelion.block.custom;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; 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.Item;
import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
@@ -11,7 +15,12 @@ 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;
import net.minecraft.world.level.block.state.properties.BooleanProperty; 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.ModTags;
import net.xevianlight.aphelion.util.SpacePartition;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class LaunchPad extends Block { public class LaunchPad extends Block {
@@ -73,5 +82,4 @@ public class LaunchPad extends Block {
} }
return state; return state;
} }
} }

View File

@@ -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());
}
}

View File

@@ -1,44 +0,0 @@
package net.xevianlight.aphelion.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.Item;
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.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock;
import net.xevianlight.aphelion.block.entity.custom.RocketAssemblerBlockEntity;
import org.jetbrains.annotations.Nullable;
public class RocketAssemblerBlock extends BasicHorizontalEntityBlock {
public RocketAssemblerBlock(Properties properties) {
super(properties, true);
}
public static final MapCodec<RocketAssemblerBlock> CODEC = simpleCodec(RocketAssemblerBlock::new);
@Override
protected 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(BlockPos blockPos, BlockState blockState) {
return new RocketAssemblerBlockEntity(blockPos, blockState);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -29,6 +29,10 @@ public abstract class BasicEntityBlock extends BaseEntityBlock {
return RenderShape.MODEL; return RenderShape.MODEL;
} }
public static Properties getProperties() {
return Properties.of();
}
public static Item.Properties getItemProperties() { public static Item.Properties getItemProperties() {
return new Item.Properties(); return new Item.Properties();
} }
@@ -37,14 +41,16 @@ public abstract class BasicEntityBlock extends BaseEntityBlock {
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(@NotNull Level level, @NotNull BlockState state, @NotNull BlockEntityType<T> blockEntityType) { 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) -> { return !shouldTick ? null : (entityLevel, pos, blockState, blockEntity) -> {
if (blockEntity instanceof TickableBlockEntity tickable) { if (blockEntity instanceof TickableBlockEntity tickable) {
long time = level.getGameTime() - pos.hashCode(); if (!tickable.isInitialized()) tickable.firstTick(entityLevel, blockState, pos);
long time = entityLevel.getGameTime() - pos.hashCode();
tickable.tick(entityLevel, time, blockState, pos); tickable.tick(entityLevel, time, blockState, pos);
if (level.isClientSide()) {
tickable.clientTick((ClientLevel) level, time, state, pos); if (entityLevel.isClientSide()) {
tickable.clientTick((ClientLevel) entityLevel, time, blockState, pos);
} else { } else {
tickable.serverTick((ServerLevel) level, time, state, pos); tickable.serverTick((ServerLevel) entityLevel, time, blockState, pos);
} }
if (!tickable.isInitialized()) tickable.firstTick(level, state, pos);
} }
}; };
} }

View File

@@ -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;
};
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -7,16 +7,81 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
public interface TickableBlockEntity { public interface TickableBlockEntity {
void tick(Level entityLevel, long time, BlockState blockState, BlockPos pos);
/**
* 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); 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); 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() { default boolean isInitialized() {
return true; 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); void firstTick(Level level, BlockState state, BlockPos pos);
default void onRemoved() {} default void onRemoved() {}

View File

@@ -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) {
}
} }

View File

@@ -1,119 +0,0 @@
package net.xevianlight.aphelion.block.entity.custom;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
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.neoforged.neoforge.items.ItemStackHandler;
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.saveddata.GravitySavedData;
import net.xevianlight.aphelion.screen.ElectricArcFurnaceMenu;
import net.xevianlight.aphelion.screen.GravityTestBlockMenu;
import net.xevianlight.aphelion.systems.GravityService;
import org.jetbrains.annotations.Nullable;
public class GravityTestBlockEntity extends BlockEntity implements TickableBlockEntity, MenuProvider {
public GravityTestBlockEntity(BlockPos pos, BlockState blockState) {super(ModBlockEntities.GRAVITY_TEST_BLOCK_ENTITY.get(), pos, blockState);}
public int areaSize = 5;
public float gravityStrength = 5f;
private boolean isInitialized = false;
public final ItemStackHandler inventory = new ItemStackHandler(0);
@Override
protected void saveAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
pTag.putInt("areaSize", areaSize);
pTag.putFloat("gravityStrength", gravityStrength);
super.saveAdditional(pTag, pRegistries);
}
@Override
protected void loadAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
areaSize = pTag.getInt("areaSize");
gravityStrength = pTag.getFloat("gravityStrength");
super.loadAdditional(pTag, pRegistries);
}
public void setRadius(float r) {
areaSize = (int) r;
}
public void setStrength(float s) {
gravityStrength = s;
}
public void addGravityArea() {
Level level = getLevel();
if (level != null && level.isClientSide()) return;
BlockPos pos = this.getBlockPos();
GravityService.setGravityArea((ServerLevel) level, pos, gravityStrength, areaSize);
}
public void removeGravityArea() {
Level level = getLevel();
if (level != null && level.isClientSide()) return;
BlockPos pos = this.getBlockPos();
GravityService.removeGravityArea((ServerLevel) level, pos);
}
@Override
public void tick(Level entityLevel, long time, BlockState blockState, BlockPos pos) {}
@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) { addGravityArea(); isInitialized = true; }
@Override
public @Nullable AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new GravityTestBlockMenu(i, inventory, this);
}
@Override
public Component getDisplayName() {
return null;
}
public void sendUpdate() {
removeGravityArea();
addGravityArea();
}
@Override
public ClientboundBlockEntityDataPacket getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return this.saveCustomOnly(registries);
}
}

View File

@@ -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();
}

View File

@@ -1,19 +1,53 @@
package net.xevianlight.aphelion.block.entity.custom; package net.xevianlight.aphelion.block.entity.custom;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.client.multiplayer.ClientLevel; 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.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.server.level.ServerLevel;
import net.minecraft.world.level.Level; 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.entity.BlockEntity;
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.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.block.custom.base.TickableBlockEntity;
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.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 net.xevianlight.aphelion.util.RocketStructure;
import org.apache.commons.lang3.NotImplementedException; 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 { 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; public boolean isInitialized;
@Override @Override
public boolean isInitialized() { public boolean isInitialized() {
@@ -24,23 +58,270 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
super(ModBlockEntities.ROCKET_ASSEMBLER_BLOCK_ENTITY.get(), pos, blockState); super(ModBlockEntities.ROCKET_ASSEMBLER_BLOCK_ENTITY.get(), pos, blockState);
} }
public void tick(Level level1, BlockPos blockPos, BlockState 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 boolean getPlatform() { public BlockPos getCenter() {
// TODO int centerX = (min.getX() + max.getX()) / 2;
throw new NotImplementedException(); int centerZ = (min.getZ() + max.getZ()) / 2;
// bottom Y level
int y = min.getY();
return new BlockPos(centerX, y, centerZ);
}
} }
public RocketStructure scan() { private static final Block TOWER_BLOCK = ModBlocks.BLOCK_STEEL.get();
// TODO public BlockPos towerBasePos;
throw new NotImplementedException();
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;
};
} }
@Override public @Nullable PadInfo getPlatformViaFill() {
public void tick(Level entityLevel, long time, BlockState blockState, BlockPos pos) { 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 @Override
@@ -50,11 +331,146 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
@Override @Override
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) { 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 @Override
public void firstTick(Level level, BlockState state, BlockPos pos) { 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; 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);
}
}
} }

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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) {
}
} }

View File

@@ -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;
}
}

View File

@@ -42,8 +42,6 @@ public class OxygenTestRenderer implements BlockEntityRenderer<OxygenTestBlockEn
// If in debug mode, renders a model made from the blocks // If in debug mode, renders a model made from the blocks
// that are currently returned by toBlockPositions(OxygenTestBlockEntity). // that are currently returned by toBlockPositions(OxygenTestBlockEntity).
public void render(OxygenTestBlockEntity be, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) { public void render(OxygenTestBlockEntity be, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
if (true) return; // i think this is all deprecated now
// This bit's debug only, folks! // This bit's debug only, folks!
if (!Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) return; if (!Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) return;

View File

@@ -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();
}
}

View File

@@ -33,5 +33,9 @@
} }
public abstract void onEnergyChanged(); public abstract void onEnergyChanged();
public void setCapacity(int capacity) {
this.capacity = capacity;
}
} }

View File

@@ -3,7 +3,6 @@ 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.server.level.ServerLevel;
import net.minecraft.world.level.Level;
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;
@@ -15,7 +14,7 @@ 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.saveddata.EnvironmentSavedData; import net.xevianlight.aphelion.core.saveddata.EnvironmentSavedData;
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData; import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
import net.xevianlight.aphelion.util.SpacePartitionHelper; 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 {
@@ -44,16 +43,22 @@ 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 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(); var server = mc.getSingleplayerServer();
ServerLevel singlePlayerLevel; ServerLevel singlePlayerLevel;
if (server != null) { if (server != null) {

View File

@@ -1,179 +0,0 @@
package net.xevianlight.aphelion.client;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.core.BlockPos;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import java.util.Collection;
import java.util.Set;
public class DebugRenderUtils {
/// Utilities for dealing with longs instead of BlockPos
/// I am not actually sure if this is faster or not. it probably is
/// 99% sure that this breaks in edge cases
public static class LongPos {
private static final int PACKED_X_LENGTH = 26;
private static final int PACKED_Y_LENGTH = 12;
private static final int PACKED_Z_LENGTH = 26;
private static final long PACKED_X_MASK = 67108863L;
private static final long PACKED_Y_MASK = 4095L;
private static final long PACKED_Z_MASK = 67108863L;
private static final int Y_OFFSET = 0;
private static final int Z_OFFSET = 12;
private static final int X_OFFSET = 38;
private static final long Y_MAGIC = PACKED_Y_MASK << Y_OFFSET;
private static final long Z_MAGIC = PACKED_Z_MASK << Z_OFFSET;
private static final long X_MAGIC = PACKED_X_MASK << X_OFFSET;
// no safeguard for the maximum values, but those are
public static long above(long pos) {
long y = (((pos & Y_MAGIC) >> Y_OFFSET) + 1) << Y_OFFSET;
return (pos & (~Y_MAGIC)) | (y & Y_MAGIC);
}
public static long below(long pos) {
long y = (((pos & Y_MAGIC) >> Y_OFFSET) - 1) << Y_OFFSET;
return (pos & (~Y_MAGIC)) | (y & Y_MAGIC);
}
public static long north(long pos) {
long z = (((pos & Z_MAGIC) >> Z_OFFSET) - 1) << Z_OFFSET;
return (pos & (~Z_MAGIC)) | (z & Z_MAGIC);
}
public static long south(long pos) {
long z = (((pos & Z_MAGIC) >> Z_OFFSET) + 1) << Z_OFFSET;
return (pos & (~Z_MAGIC)) | (z & Z_MAGIC);
}
public static long east(long pos) {
long x = (((pos & X_MAGIC) >> X_OFFSET) + 1) << X_OFFSET;
return (pos & (~X_MAGIC)) | (x & X_MAGIC);
}
public static long west(long pos) {
long x = (((pos & X_MAGIC) >> X_OFFSET) - 1) << X_OFFSET;
return (pos & (~X_MAGIC)) | (x & X_MAGIC);
}
public static int getX(long pos) {
return BlockPos.getX(pos);
}
public static int getY(long pos) {
return BlockPos.getY(pos);
}
public static int getZ(long pos) {
return BlockPos.getZ(pos);
}
}
public static void drawSphere(PoseStack poseStack, VertexConsumer vc, Vector3f center, float radius) {
Matrix4f mat = poseStack.last().pose();
final int Y_SEGMENT_COUNT = 20;
for (int segmentY=0; segmentY < Y_SEGMENT_COUNT; segmentY++) {
double bottomAngle = (((double) segmentY / Y_SEGMENT_COUNT) - 0.5) * Math.PI;
double topAngle = (((double) (segmentY+1) / Y_SEGMENT_COUNT) - 0.5) * Math.PI;
float y0 = (float) Math.sin(bottomAngle) * radius + center.y();
float y1 = (float) Math.sin(topAngle) * radius + center.y();
float bottomRadius = (float) Math.cos(bottomAngle) * radius;
float topRadius = (float) Math.cos(topAngle) * radius;
final int POLAR_SEGMENT_COUNT = 20;
for (int segmentP = 0; segmentP < POLAR_SEGMENT_COUNT; segmentP++) {
// "left" and "right" As viewed from outside the sphere
double leftAngle = (((double) segmentP / POLAR_SEGMENT_COUNT) - 1) * 2 * Math.PI;
double rightAngle = (((double) (segmentP+1) / POLAR_SEGMENT_COUNT) - 1) * 2 * Math.PI;
// Points have to wind CCW, so 0->1->2->3 is CCW.
// 0 and 1 use y0, 2 and 3 use y1.
float x0, x1, x2, x3, z0, z1, z2, z3;
x0 = (float) Math.cos(leftAngle) * bottomRadius + center.x();
x1 = (float) Math.cos(rightAngle) * bottomRadius + center.x();
x2 = (float) Math.cos(rightAngle) * topRadius + center.x();
x3 = (float) Math.cos(leftAngle) * topRadius + center.x();
z0 = (float) Math.sin(leftAngle) * bottomRadius + center.z();
z1 = (float) Math.sin(rightAngle) * bottomRadius + center.z();
z2 = (float) Math.sin(rightAngle) * topRadius + center.z();
z3 = (float) Math.sin(leftAngle) * topRadius + center.z();
// Draw the sphere quad
quad(mat, vc,
x0, y0, z0,
x1, y0, z1,
x2, y1, z2,
x3, y1, z3,
0.7f, 0.2f, 0.2f, 0.5f);
}
}
}
// Sorry LOSERS, we only let FAST data structures in HERE.
public static void drawBlockArea(PoseStack poseStack, VertexConsumer vc, LongOpenHashSet blocks) {
for (long p : blocks) {
// Neighbor checks: only render faces exposed to non-oxygen
boolean up = blocks.contains(LongPos.above(p));
boolean down = blocks.contains(LongPos.below(p));
boolean north = blocks.contains(LongPos.north(p));
boolean south = blocks.contains(LongPos.south(p));
boolean east = blocks.contains(LongPos.east(p));
boolean west = blocks.contains(LongPos.west(p));
if (up && down && north && south && east && west) continue;
drawBlockFaces(poseStack, vc, p, up, down, north, south, east, west);
}
}
public static void drawBlockFaces(
PoseStack poseStack, VertexConsumer vc, long posAsLong,
boolean up, boolean down, boolean north, boolean south, boolean east, boolean west) {
final float eps = 0.0025f;
float x0 = LongPos.getX(posAsLong) + eps;
float y0 = LongPos.getY(posAsLong) + eps;
float z0 = LongPos.getZ(posAsLong) + eps;
float x1 = LongPos.getX(posAsLong) + 1 - eps;
float y1 = LongPos.getY(posAsLong) + 1 - eps;
float z1 = LongPos.getZ(posAsLong) + 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);
}
public 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);
}
}

View File

@@ -1,74 +0,0 @@
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 it.unimi.dsi.fastutil.longs.Long2IntMap;
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.client.server.IntegratedServer;
import net.minecraft.core.BlockPos;
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 net.xevianlight.aphelion.core.saveddata.GravitySavedData;
import net.xevianlight.aphelion.core.saveddata.types.GravityData;
@EventBusSubscriber(modid = Aphelion.MOD_ID, value = Dist.CLIENT)
public class GravityDebugRender {
// Untextured translucent quads (POSITION_COLOR only)
private static final RenderType GRAVITY_FILL = RenderType.create(
"aphelion_gravity_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);
// I'm lazy, so i'm just gonna make this work on a singleplayer server and call it a day :P
IntegratedServer singleplayer = mc.getSingleplayerServer();
if (singleplayer == null) return;
MultiBufferSource.BufferSource bufferSource = mc.renderBuffers().bufferSource();
VertexConsumer vc = bufferSource.getBuffer(GRAVITY_FILL);
for (Long2IntMap.Entry gravityEntry : GravitySavedData.get(singleplayer.getLevel(mc.level.dimension()))._debug_getGravityData().long2IntEntrySet()) {
GravityData d = GravityData.unpack(gravityEntry.getIntValue());
BlockPos p = BlockPos.of(gravityEntry.getLongKey());
DebugRenderUtils.drawSphere(poseStack, vc, p.getCenter().toVector3f(), d.getRadius());
}
poseStack.popPose();
bufferSource.endBatch(GRAVITY_FILL);
}
}

View File

@@ -17,8 +17,6 @@ import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.Aphelion;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import java.util.Collection;
@EventBusSubscriber(modid = Aphelion.MOD_ID, value = Dist.CLIENT) @EventBusSubscriber(modid = Aphelion.MOD_ID, value = Dist.CLIENT)
public final class OxygenDebugRender { public final class OxygenDebugRender {
@@ -58,9 +56,63 @@ public final class OxygenDebugRender {
MultiBufferSource.BufferSource bufferSource = mc.renderBuffers().bufferSource(); MultiBufferSource.BufferSource bufferSource = mc.renderBuffers().bufferSource();
VertexConsumer vc = bufferSource.getBuffer(OXYGEN_FILL); VertexConsumer vc = bufferSource.getBuffer(OXYGEN_FILL);
DebugRenderUtils.drawBlockArea(poseStack, vc, ClientOxygenCache.OXYGEN); // Render surface faces only (fast + pretty)
for (long l : ClientOxygenCache.OXYGEN) {
BlockPos p = BlockPos.of(l);
drawSurfaceFaces(poseStack, vc, p);
}
poseStack.popPose(); poseStack.popPose();
bufferSource.endBatch(OXYGEN_FILL); 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);
}
} }

View File

@@ -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");

View File

@@ -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;
@@ -67,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 partitionData = 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");
} }
} }

View File

@@ -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;
@@ -22,14 +23,15 @@ 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.saveddata.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.planet.Planet;
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 net.xevianlight.aphelion.util.registries.ModRegistries;
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 {
@@ -42,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();
@@ -80,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);
@@ -105,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();
@@ -143,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);
@@ -190,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);
@@ -212,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;
@@ -246,16 +248,21 @@ public class AphelionCommand {
) )
) )
.then(Commands.literal("destination") .then(Commands.literal("destination")
.then(Commands.literal("set").then( .then(Commands.literal("set")
Commands.argument("pos", ColumnPosArgument.columnPos()) .then(Commands.argument("pos", ColumnPosArgument.columnPos())
.then(Commands.argument("id", ResourceLocationArgument.id()) .then(Commands.argument("id", ResourceLocationArgument.id())
.executes(context -> { .executes(context -> {
int x = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x()); int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
int z = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").z()); int pz = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
ResourceLocation orbit = ResourceLocationArgument.getId(context, "id"); ResourceLocation orbit = ResourceLocationArgument.getId(context, "id");
ServerLevel level = context.getSource().getLevel(); ServerLevel level = context.getSource().getLevel();
SpacePartitionSavedData.get(level).getData(x,z).setDestination(orbit); 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; return 1;
}) })
@@ -263,6 +270,67 @@ public class AphelionCommand {
) )
) )
) )
.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")
@@ -474,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;
})
)
) )
) )

View File

@@ -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.*;
@@ -49,11 +50,16 @@ public class ModBlockEntities {
public static final Supplier<BlockEntityType<RocketAssemblerBlockEntity>> ROCKET_ASSEMBLER_BLOCK_ENTITY = public static final Supplier<BlockEntityType<RocketAssemblerBlockEntity>> ROCKET_ASSEMBLER_BLOCK_ENTITY =
BLOCK_ENTITIES.register("rocket_assembler_block_entity", () -> BlockEntityType.Builder.of( BLOCK_ENTITIES.register("rocket_assembler_block_entity", () -> BlockEntityType.Builder.of(
RocketAssemblerBlockEntity::new, ModBlocks.ROCKET_ASSEMBLER_BLOCK.get()).build(null) RocketAssemblerBlockEntity::new, ModBlocks.ROCKET_ASSEMBLER.get()).build(null)
); );
public static final Supplier<BlockEntityType<GravityTestBlockEntity>> GRAVITY_TEST_BLOCK_ENTITY = public static final Supplier<BlockEntityType<StationRocketEngineBlockEntity>> STATION_ROCKET_ENGINE_BLOCK_ENTITY =
BLOCK_ENTITIES.register("gravity_test_block_entity", () -> BlockEntityType.Builder.of( BLOCK_ENTITIES.register("station_rocket_engine_block_entity", () -> BlockEntityType.Builder.of(
GravityTestBlockEntity::new, ModBlocks.GRAVITY_TEST_BLOCK.get()).build(null) 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)
); );
} }

View File

@@ -6,7 +6,6 @@ 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.block.dummy.VAFMultiblockDummyBlock;
import net.xevianlight.aphelion.block.entity.custom.GravityTestBlockEntity;
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);
@@ -20,6 +19,10 @@ public class ModBlocks {
public static final DeferredBlock<Block> VACUUM_ARC_FURNACE_CONTROLLER = BLOCKS.register("vacuum_arc_furnace_controller", () -> new VacuumArcFurnaceController(VacuumArcFurnaceController.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> 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_BLOCK = BLOCKS.register("rocket_assemblerblock", () -> new RocketAssemblerBlock(RocketAssemblerBlock.getProperties())); public static final DeferredBlock<Block> ROCKET_ASSEMBLER = BLOCKS.register("rocket_assembler", () -> new RocketAssembler(RocketAssembler.getProperties()));
public static final DeferredBlock<Block> GRAVITY_TEST_BLOCK = BLOCKS.register("gravity_test_block", () -> new GravityTestBlock(GravityTestBlock.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()));
} }

View File

@@ -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());
@@ -42,5 +43,6 @@ public class ModCreativeTabs {
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.LAUNCH_PAD);
output.accept(ModItems.ROCKET_ASSEMBLER);
}).build()); }).build());
} }

View File

@@ -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"));
}

View File

@@ -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);
}
}

View File

@@ -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,7 +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> GRAVITY_TEST_BLOCK = ITEMS.register("gravity_test_block", () -> new BlockItem(ModBlocks.GRAVITY_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> 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()));
} }

View File

@@ -103,9 +103,8 @@ public class EnvironmentSavedData extends SavedData {
public void setOxygen(Level level, BlockPos pos, boolean value) { public void setOxygen(Level level, BlockPos pos, boolean value) {
var data = getDataForPosition(level, pos); var data = getDataForPosition(level, pos);
data.setOxygen(value);
// Aphelion.LOGGER.info("Set oxygen for {} to {}", pos, value); // Aphelion.LOGGER.info("Set oxygen for {} to {}", pos, value);
putOrRemove(level, pos.asLong(), data.pack()); putOrRemove(level, pos.asLong(), data.withOxygen(value).pack());
} }
public void setOxygen(Level level, Collection<BlockPos> positions, boolean value) { public void setOxygen(Level level, Collection<BlockPos> positions, boolean value) {
@@ -116,8 +115,7 @@ public class EnvironmentSavedData extends SavedData {
public void resetOxygen(Level level, BlockPos pos) { public void resetOxygen(Level level, BlockPos pos) {
var data = getDataForPosition(level, pos); var data = getDataForPosition(level, pos);
data.setOxygen(defaultData(level).hasOxygen()); putOrRemove(level, pos.asLong(), data.withOxygen(defaultData(level).hasOxygen()).pack());
putOrRemove(level, pos.asLong(), data.pack());
} }
public void resetOxygen(Level level, Collection<BlockPos> positions) { public void resetOxygen(Level level, Collection<BlockPos> positions) {
@@ -133,8 +131,7 @@ public class EnvironmentSavedData extends SavedData {
public void setGravity(Level level, BlockPos pos, float value) { public void setGravity(Level level, BlockPos pos, float value) {
var data = getDataForPosition(level, pos); var data = getDataForPosition(level, pos);
data.setGravity(value); putOrRemove(level, pos.asLong(), data.withGravity(value).pack());
putOrRemove(level, pos.asLong(), data.pack());
} }
public void setGravity(Level level, Collection<BlockPos> positions, float value) { public void setGravity(Level level, Collection<BlockPos> positions, float value) {
@@ -145,8 +142,7 @@ public class EnvironmentSavedData extends SavedData {
public void resetGravity(Level level, BlockPos pos) { public void resetGravity(Level level, BlockPos pos) {
var data = getDataForPosition(level, pos); var data = getDataForPosition(level, pos);
data.setGravity(defaultData(level).getGravity()); putOrRemove(level, pos.asLong(), data.withGravity(defaultData(level).getGravity()).pack());
putOrRemove(level, pos.asLong(), data.pack());
} }
public short getTemperature(Level level, BlockPos pos) { public short getTemperature(Level level, BlockPos pos) {
@@ -156,8 +152,7 @@ public class EnvironmentSavedData extends SavedData {
public void setTemperature(Level level, BlockPos pos, short value) { public void setTemperature(Level level, BlockPos pos, short value) {
var data = getDataForPosition(level, pos); var data = getDataForPosition(level, pos);
data.setTemperature(value); putOrRemove(level, pos.asLong(), data.withTemperature(value).pack());
putOrRemove(level, pos.asLong(), data.pack());
} }
public void setTemperature(Level level, Collection<BlockPos> positions, short value) { public void setTemperature(Level level, Collection<BlockPos> positions, short value) {
@@ -168,8 +163,7 @@ public class EnvironmentSavedData extends SavedData {
public void resetTemperature(Level level, BlockPos pos) { public void resetTemperature(Level level, BlockPos pos) {
var data = getDataForPosition(level, pos); var data = getDataForPosition(level, pos);
data.setTemperature(defaultData(level).getTemperature()); putOrRemove(level, pos.asLong(), data.withTemperature((defaultData(level).getTemperature())).pack());
putOrRemove(level, pos.asLong(), data.pack());
} }
private void putOrRemove(Level level, long key, int packed) { private void putOrRemove(Level level, long key, int packed) {

View File

@@ -1,8 +1,6 @@
package net.xevianlight.aphelion.core.saveddata; package net.xevianlight.aphelion.core.saveddata;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; 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.BlockPos;
import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@@ -10,8 +8,6 @@ import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.saveddata.SavedData; import net.minecraft.world.level.saveddata.SavedData;
import net.xevianlight.aphelion.client.ClientOxygenCache;
import net.xevianlight.aphelion.core.saveddata.types.EnvironmentData;
import net.xevianlight.aphelion.core.saveddata.types.GravityData; import net.xevianlight.aphelion.core.saveddata.types.GravityData;
import net.xevianlight.aphelion.planet.Planet; import net.xevianlight.aphelion.planet.Planet;
import net.xevianlight.aphelion.planet.PlanetCache; import net.xevianlight.aphelion.planet.PlanetCache;
@@ -19,7 +15,6 @@ 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.Collection;
import java.util.List; import java.util.List;
/** /**
@@ -32,18 +27,12 @@ import java.util.List;
*/ */
public class GravitySavedData extends SavedData { public class GravitySavedData extends SavedData {
private Level level;
private final Long2IntOpenHashMap gravityData = new Long2IntOpenHashMap(); private final Long2IntOpenHashMap gravityData = new Long2IntOpenHashMap();
private static final String NAME = "aphelion_gravity"; private static final String NAME = "aphelion_gravity";
public static GravitySavedData create(Level level) { public static GravitySavedData create() {
return new GravitySavedData(level); return new GravitySavedData();
}
public GravitySavedData(Level level) {
this.level = level;
} }
@Override @Override
@@ -66,18 +55,18 @@ public class GravitySavedData extends SavedData {
return tag; return tag;
} }
public static GravitySavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider, Level level) { public static GravitySavedData load(CompoundTag tag, HolderLookup.Provider lookupProvider) {
GravitySavedData data = new GravitySavedData(level); GravitySavedData data = create();
if (!tag.contains("Position", Tag.TAG_LONG_ARRAY) || !tag.contains("Value", Tag.TAG_INT_ARRAY)) { if (!tag.contains("Position", Tag.TAG_LONG_ARRAY) || !tag.contains("Value", Tag.TAG_INT_ARRAY)) { return data; }
return data;
}
long[] positions = tag.getLongArray("Position"); long[] positions = tag.getLongArray("Position");
int[] values = tag.getIntArray("Value"); int[] values = tag.getIntArray("Value");
int length = Math.min(positions.length, values.length); int length = Math.min(positions.length, values.length);
data.gravityData.ensureCapacity(length); data.gravityData.ensureCapacity(length);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
data.gravityData.put(positions[i], values[i]); data.gravityData.put(positions[i], values[i]);
} }
@@ -87,7 +76,7 @@ public class GravitySavedData extends SavedData {
private static final int ABSENT = Integer.MIN_VALUE; private static final int ABSENT = Integer.MIN_VALUE;
public @Nullable GravityData getGravityRegionData (BlockPos center) { public @Nullable GravityData getGravityRegionData (Level level, BlockPos center) {
GravityData data = GravityData.unpack(gravityData.getOrDefault(center.asLong(), ABSENT)); GravityData data = GravityData.unpack(gravityData.getOrDefault(center.asLong(), ABSENT));
return data.pack() == ABSENT ? null : data; return data.pack() == ABSENT ? null : data;
} }
@@ -100,9 +89,6 @@ public class GravitySavedData extends SavedData {
gravityData.put(pos.asLong(), data.pack()); gravityData.put(pos.asLong(), data.pack());
} }
public Long2IntOpenHashMap _debug_getGravityData() {
return gravityData;
}
/** /**
@@ -114,7 +100,7 @@ public class GravitySavedData extends SavedData {
List<GravityData> regions = getGravityRegions(pos); List<GravityData> regions = getGravityRegions(pos);
for (var e : regions) { for (var e : regions) {
sum += e.getAccel(); sum += e.getGravity();
} }
return sum; return sum;
@@ -124,19 +110,15 @@ public class GravitySavedData extends SavedData {
* Returns the strongest acceleration among all gravity regions that overlap this block position * Returns the strongest acceleration among all gravity regions that overlap this block position
*/ */
public float getGravityMax (BlockPos pos) { public float getGravityMax (BlockPos pos) {
float max = -1; float max = 0;
List<GravityData> regions = getGravityRegions(pos); List<GravityData> regions = getGravityRegions(pos);
for (var e : regions) { for (var e : regions) {
var accel = e.getAccel(); var accel = e.getGravity();
if (accel > max) max = accel; if (accel > max) max = accel;
} }
if (max == -1) {
max = defaultGravity(this.level);
}
return max; return max;
} }
@@ -171,10 +153,7 @@ public class GravitySavedData extends SavedData {
public static GravitySavedData get(ServerLevel level) { public static GravitySavedData get(ServerLevel level) {
return level.getDataStorage().computeIfAbsent( return level.getDataStorage().computeIfAbsent(
new Factory<>( new Factory<>(GravitySavedData::create, GravitySavedData::load),
() -> new GravitySavedData(level),
(tag, provider) -> GravitySavedData.load(tag, provider, level)
),
NAME NAME
); );
} }

View File

@@ -2,6 +2,7 @@ package net.xevianlight.aphelion.core.saveddata;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
@@ -10,6 +11,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.saveddata.SavedData; import net.minecraft.world.level.saveddata.SavedData;
import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.core.saveddata.types.PartitionData; import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
import net.xevianlight.aphelion.util.SpacePartition;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -54,10 +56,22 @@ public class SpacePartitionSavedData extends SavedData {
// Distances (optional; default 0.0) // Distances (optional; default 0.0)
if (e.contains("DistanceTraveled", CompoundTag.TAG_DOUBLE)) { if (e.contains("DistanceTraveled", CompoundTag.TAG_DOUBLE)) {
pd.setDistanceTraveled(e.getDouble("DistanceTraveled")); pd.setDistanceTraveledAU(e.getDouble("DistanceTraveled"));
} }
if (e.contains("DistanceToDest", CompoundTag.TAG_DOUBLE)) { if (e.contains("DistanceToDest", CompoundTag.TAG_DOUBLE)) {
pd.setDistanceToDest(e.getDouble("DistanceToDest")); 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); data.map.put(key, pd);
@@ -90,8 +104,16 @@ public class SpacePartitionSavedData extends SavedData {
// Traveling + distances // Traveling + distances
e.putBoolean("Traveling", pd.isTraveling()); e.putBoolean("Traveling", pd.isTraveling());
e.putDouble("DistanceTraveled", pd.getDistanceTraveled()); e.putDouble("DistanceTraveled", pd.getDistanceTraveledAU());
e.putDouble("DistanceToDest", pd.getDistanceToDest()); 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); entries.add(e);
}); });
@@ -137,10 +159,24 @@ public class SpacePartitionSavedData extends SavedData {
} }
} }
public @Nullable PartitionData getData(int px, int pz) { /**
* 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); long key = pack(px, pz);
PartitionData data = map.get(key); PartitionData data = map.get(key);
if (data == null) { if (data == null) {
// pick a sensible default orbit, or null if you truly allow it // pick a sensible default orbit, or null if you truly allow it
data = new PartitionData(Aphelion.id("orbit/default")); data = new PartitionData(Aphelion.id("orbit/default"));
map.put(key, data); map.put(key, data);
@@ -149,12 +185,67 @@ public class SpacePartitionSavedData extends SavedData {
return data; 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) { public void overwriteAllExistingOrbits(ResourceLocation orbit) {
if (map.isEmpty()) return; if (map.isEmpty()) return;
boolean changed = false; boolean changed = false;
for (var entry : map.long2ObjectEntrySet()) { for (var entry : map.long2ObjectEntrySet()) {
if(!orbit.equals(entry.getValue())) { if(!orbit.equals(entry.getValue().getOrbit())) {
entry.getValue().setOrbit(orbit); entry.getValue().setOrbit(orbit);
changed = true; changed = true;
} }

View File

@@ -1,6 +1,6 @@
package net.xevianlight.aphelion.core.saveddata.types; package net.xevianlight.aphelion.core.saveddata.types;
public final class EnvironmentData { public record EnvironmentData (boolean oxygen, short temperature, float gravity){
@@ -26,9 +26,6 @@ public final class EnvironmentData {
private static final int TEMPERATURE_BIT = OXYGEN_BIT + OXYGEN_BITS; // next 16 bits 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 private static final int GRAVITY_BIT = TEMPERATURE_BIT + TEMPERATURE_BITS; // next 15 bits
private boolean oxygen;
private short temperature;
private float gravity;
public EnvironmentData(boolean oxygen, short temperature, float gravity) { public EnvironmentData(boolean oxygen, short temperature, float gravity) {
this.oxygen = oxygen; this.oxygen = oxygen;
@@ -36,6 +33,19 @@ public final class EnvironmentData {
this.gravity = gravity; 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() { public int pack() {
int packedData = 0; int packedData = 0;
@@ -76,23 +86,12 @@ public final class EnvironmentData {
return oxygen; return oxygen;
} }
public void setOxygen(boolean oxygen) {
this.oxygen = oxygen;
}
public short getTemperature() { public short getTemperature() {
return temperature; return temperature;
} }
public void setTemperature(short temperature) {
this.temperature = temperature;
}
public float getGravity() { public float getGravity() {
return gravity; return gravity;
} }
public void setGravity(float gravity) {
this.gravity = gravity;
}
} }

View File

@@ -1,50 +1,39 @@
package net.xevianlight.aphelion.core.saveddata.types; package net.xevianlight.aphelion.core.saveddata.types;
public class GravityData { // 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.
private float accel; // We never store GravityData for any reason other than to test its values or to pack it into an integer.
private float radius; public record GravityData (float gravity, float radius) {
public static final float ONE_G = 9.80665f; public static final float DEFAULT_GRAVITY = 32.00f; // Vanilla acceleration
public static final float DEFAULT_GRAVITY = ONE_G; // 1G
public static final float GRAVITY_PRECISION = 100.0f; public static final float GRAVITY_PRECISION = 100.0f;
public static final float RADIUS_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_RADIUS = Short.MAX_VALUE / RADIUS_PRECISION;
public static final float MAX_GRAVITY = Short.MAX_VALUE / GRAVITY_PRECISION; public static final float MAX_GRAVITY = Short.MAX_VALUE / GRAVITY_PRECISION;
public GravityData(float accel, float radius) { public GravityData {
this.accel = accel; gravity = Math.clamp(gravity, 0f, MAX_GRAVITY);
this.radius = radius; radius = Math.clamp(radius, 0f, MAX_RADIUS);
} }
public int pack() { public int pack() {
int packed = 0; int g = Math.round(gravity * GRAVITY_PRECISION) & 0xFFFF;
int r = Math.round(radius * RADIUS_PRECISION) & 0xFFFF;
packed |= (int) (this.accel * GRAVITY_PRECISION); return g | (r << 16);
packed |= (int) (this.radius * RADIUS_PRECISION) << 16;
return packed;
} }
public float getAccel() { public float getGravity() {
return accel; return gravity;
}
public void setAccel(float accel) {
this.accel = accel;
} }
public float getRadius() { public float getRadius() {
return radius; return radius;
} }
public void setRadius(short radius) {
this.radius = radius;
}
public static GravityData unpack(int packed) { public static GravityData unpack(int packed) {
float accel = (packed & 0xFFFF) / GRAVITY_PRECISION; float gravity = (packed & 0xFFFF) / GRAVITY_PRECISION;
float radius = (packed >> 16) / RADIUS_PRECISION; float radius = ((packed >>> 16) & 0xFFFF) / RADIUS_PRECISION;
return new GravityData(accel, radius); return new GravityData(gravity, radius);
} }
} }

View File

@@ -1,39 +1,64 @@
package net.xevianlight.aphelion.core.saveddata.types; package net.xevianlight.aphelion.core.saveddata.types;
import io.netty.buffer.ByteBuf; 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.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.xevianlight.aphelion.planet.PlanetCache;
import net.xevianlight.aphelion.util.BigCodec;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Objects; import java.util.*;
import java.util.Optional;
public class PartitionData { 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 orbit;
@Nullable private ResourceLocation destination; @Nullable private ResourceLocation destination;
private boolean traveling; private boolean traveling;
private double distanceTraveled; /// How far we've already gone
private double distanceToDest; 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) { public PartitionData(@Nullable ResourceLocation orbit) {
this.orbit = orbit; this.orbit = orbit;
this.destination = null; this.destination = null;
this.traveling = false; this.traveling = false;
this.distanceTraveled = 0; this.distanceTraveledAU = 0;
this.distanceToDest = 0; this.tripDistanceAU = 0;
this.generated = false;
this.owner = null;
this.landingPadControllers = List.of();
this.engines = new ArrayList<>(List.of());
} }
public PartitionData(PartitionData other) { public PartitionData(PartitionData other) {
this.orbit = other.orbit; this.orbit = other.orbit;
this.destination = other.destination; this.destination = other.destination;
this.traveling = other.traveling; this.traveling = other.traveling;
this.distanceTraveled = other.distanceTraveled; this.distanceTraveledAU = other.distanceTraveledAU;
this.distanceToDest = other.distanceToDest; 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 = public static final StreamCodec<ByteBuf, PartitionData> STREAM_CODEC =
StreamCodec.composite( BigCodec.composite(
// orbit is nullable -> optional codec // orbit is nullable -> optional codec
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC), ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
d -> Optional.ofNullable(d.getOrbit()), d -> Optional.ofNullable(d.getOrbit()),
@@ -46,17 +71,33 @@ public class PartitionData {
// doubles -> DOUBLE codec // doubles -> DOUBLE codec
ByteBufCodecs.DOUBLE, ByteBufCodecs.DOUBLE,
PartitionData::getDistanceTraveled, PartitionData::getDistanceTraveledAU,
ByteBufCodecs.DOUBLE, ByteBufCodecs.DOUBLE,
PartitionData::getDistanceToDest, PartitionData::recalculateTripDistAU,
(orbitOpt, destOpt, traveling, distTraveled, distToDest) -> { 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)); PartitionData data = new PartitionData(orbitOpt.orElse(null));
data.destination = destOpt.orElse(null); data.destination = destOpt.orElse(null);
data.traveling = traveling; data.traveling = traveling;
data.distanceTraveled = distTraveled; data.distanceTraveledAU = distTraveled;
data.distanceToDest = distToDest; data.tripDistanceAU = distToDest;
data.owner = ownerOpt.orElse(null);
data.generated = generated;
data.landingPadControllers = controllers;
data.engines = engines;
return data; return data;
} }
); );
@@ -65,8 +106,10 @@ public class PartitionData {
return this.orbit; return this.orbit;
} }
public void setOrbit(ResourceLocation orbit) { public void setOrbit(@Nullable ResourceLocation orbit) {
this.orbit = orbit; this.orbit = orbit;
recalculateTripDistAU();
distanceTraveledAU = 0;
} }
public @Nullable ResourceLocation getDestination() { public @Nullable ResourceLocation getDestination() {
@@ -75,6 +118,8 @@ public class PartitionData {
public void setDestination(@Nullable ResourceLocation destination) { public void setDestination(@Nullable ResourceLocation destination) {
this.destination = destination; this.destination = destination;
recalculateTripDistAU();
distanceTraveledAU = 0;
} }
public boolean isTraveling() { public boolean isTraveling() {
@@ -85,24 +130,177 @@ public class PartitionData {
this.traveling = traveling; this.traveling = traveling;
} }
public double getDistanceTraveled() { public double getDistanceTraveledAU() {
return distanceTraveled; return distanceTraveledAU;
} }
public void setDistanceTraveled(double distanceTraveled) { public void setDistanceTraveledAU(double distanceTraveledAU) {
this.distanceTraveled = distanceTraveled; this.distanceTraveledAU = distanceTraveledAU;
} }
public double getDistanceToDest() { public double recalculateTripDistAU() {
return distanceToDest; var currentPlanet = PlanetCache.getByOrbitOrNull(orbit);
if (currentPlanet == null) {
return -1;
} }
public void setDistanceToDest(double distanceToDest) { var destPlanet = PlanetCache.getOrDefault(destination);
this.distanceToDest = distanceToDest;
var dist = destPlanet.orbitDistance() - currentPlanet.orbitDistance();
this.tripDistanceAU = dist;
return dist;
} }
public void travel(double distance) { public double getTripDistanceAU() {
distanceTraveled = Math.min( distanceTraveled + distance, distanceToDest); 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 @Override
@@ -115,7 +313,29 @@ public class PartitionData {
return Objects.equals(this.orbit, that.orbit) return Objects.equals(this.orbit, that.orbit)
&& Objects.equals(this.destination, that.destination) && Objects.equals(this.destination, that.destination)
&& this.traveling == that.traveling && this.traveling == that.traveling
&& Double.compare(this.distanceTraveled, that.distanceTraveled) == 0 && Double.compare(this.distanceTraveledAU, that.distanceTraveledAU) == 0
&& Double.compare(this.distanceToDest, that.distanceToDest) == 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);
} }
} }

View File

@@ -26,8 +26,8 @@ public class ModBlockLootTableProvider extends BlockLootSubProvider {
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.LAUNCH_PAD.get());
dropSelf(ModBlocks.GRAVITY_TEST_BLOCK.get()); dropSelf(ModBlocks.ROCKET_ASSEMBLER.get());
dropSelf(ModBlocks.ROCKET_ASSEMBLER_BLOCK.get()); dropSelf(ModBlocks.ROCKET_SEAT.get());
} }
@Override @Override

View File

@@ -25,6 +25,12 @@ 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);
@@ -32,9 +38,7 @@ public class ModBlockStateProvider extends BlockStateProvider {
// blockItem(ModBlocks.LAUNCH_PAD); // 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);
blockWithItem(ModBlocks.GRAVITY_TEST_BLOCK);
} }
private void blockWithItem(DeferredBlock<?> deferredBlock) { private void blockWithItem(DeferredBlock<?> deferredBlock) {

View File

@@ -44,5 +44,8 @@ public class ModBlockTagProvider extends BlockTagsProvider {
tag(ModTags.Blocks.LAUNCH_PAD) tag(ModTags.Blocks.LAUNCH_PAD)
.add(ModBlocks.LAUNCH_PAD.get()); .add(ModBlocks.LAUNCH_PAD.get());
tag(ModTags.Blocks.ROCKET_SEAT)
.add(ModBlocks.ROCKET_SEAT.get());
} }
} }

View File

@@ -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());
} }
} }

View File

@@ -2,6 +2,7 @@ package net.xevianlight.aphelion.entites.vehicles;
import net.minecraft.client.Minecraft; 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;
@@ -13,13 +14,16 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
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,14 +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.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.UUID;
public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpawn { public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpawn {
@@ -51,7 +61,7 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
private double landingPosX; private double landingPosX;
private double landingPosZ; private double landingPosZ;
private static final double TELEPORT_Y = 1000.0; private static final double TELEPORT_Y = 600.0;
private static final double ASCEND_ACCEL = 0.0125; private static final double ASCEND_ACCEL = 0.0125;
private static final double DESCEND_SPEED = 1; private static final double DESCEND_SPEED = 1;
@@ -63,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;
@@ -76,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;
@@ -104,6 +176,9 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
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();
@@ -119,12 +194,14 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
} }
case DESCEND -> tickDescend(); case DESCEND -> tickDescend();
} }
// Simple upward movement
} }
// 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()); move(MoverType.SELF, getDeltaMovement());
} }
}
private void tickIdle() { private void tickIdle() {
resetDeltaMovement(); resetDeltaMovement();
@@ -224,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);
@@ -279,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
@@ -325,9 +404,12 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
@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();
@@ -361,10 +443,22 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
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());
@@ -375,6 +469,10 @@ 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.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 @Nullable BlockPos getTargetPos() { public @Nullable BlockPos getTargetPos() {
@@ -436,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
@@ -463,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() {
@@ -496,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();
}
} }

View File

@@ -8,32 +8,37 @@ import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
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.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.network.ClientPlayerStateUpdateHandler; import net.xevianlight.aphelion.core.init.ModEntities;
import net.xevianlight.aphelion.network.RocketPayloadHandlers; import net.xevianlight.aphelion.network.RocketPayloadHandlers;
import net.xevianlight.aphelion.network.PartitionPayloadHandler; import net.xevianlight.aphelion.network.PartitionPayloadHandler;
import net.xevianlight.aphelion.network.UpdateGravityTestBlockHandler;
import net.xevianlight.aphelion.network.packet.ClientPlayerStateUpdatePacket;
import net.xevianlight.aphelion.network.packet.PartitionPayload; import net.xevianlight.aphelion.network.packet.PartitionPayload;
import net.xevianlight.aphelion.network.packet.RocketLaunchPayload; import net.xevianlight.aphelion.network.packet.RocketLaunchPayload;
import net.xevianlight.aphelion.network.packet.UpdateGravityTestBlockPacket;
@EventBusSubscriber(modid = Aphelion.MOD_ID) @EventBusSubscriber(modid = Aphelion.MOD_ID)
public class ModBusEvents { 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
@@ -53,16 +58,5 @@ public class ModBusEvents {
RocketPayloadHandlers::handleRocketLaunch RocketPayloadHandlers::handleRocketLaunch
); );
registrar.playToClient(
ClientPlayerStateUpdatePacket.TYPE,
ClientPlayerStateUpdatePacket.STREAM_CODEC,
ClientPlayerStateUpdateHandler::handleDataOnMain
);
registrar.playToServer(
UpdateGravityTestBlockPacket.TYPE,
UpdateGravityTestBlockPacket.STREAM_CODEC,
UpdateGravityTestBlockHandler::handleDataOnMain
);
} }
} }

View File

@@ -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

View File

@@ -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);
}
} }

View File

@@ -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);

View File

@@ -1,67 +0,0 @@
package net.xevianlight.aphelion.mixins.common;
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.BlockPos;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.state.BlockState;
import net.neoforged.neoforge.attachment.AttachmentHolder;
import net.xevianlight.aphelion.systems.GravityService;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import org.spongepowered.asm.util.Locals;
import java.io.ObjectOutputStream;
@Mixin(Entity.class)
public abstract class EntityMixin extends AttachmentHolder {
@Shadow
public float fallDistance;
// @At("RETURN") injects at all points IMMEDIATELY before a "return" opcode.
// Immediately before means IMMEDIATELY BEFORE,
// so even in a method like:
//
// return gravity;
//
// What's ACTUALLY happening is:
//
// $value = gravity;
// return $value;
//
// So, when we inject, we get:
//
// $value = gravity;
// [RETURN INJECT POINT]
// return $value;
//
// so we get to edit $value.
@Inject(method = "getGravity", at = @At("RETURN"), cancellable = true)
public void aphelion$getGravity(CallbackInfoReturnable<Double> cir) {
cir.setReturnValue(cir.getReturnValue() * GravityService.getGravityFactor((Entity) (Object) this));
}
// Should only break if the code that increases fall distance is moved outside Entity$checkFallDamage.
@WrapMethod(method = "checkFallDamage")
public void aphelion$checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos, Operation<Void> original) {
float prevFallDistance = this.fallDistance;
original.call(y, onGround, state, pos);
float newFallDistance = this.fallDistance;
if (newFallDistance > prevFallDistance && newFallDistance > 0) {
float fallDistanceGained = newFallDistance - prevFallDistance;
this.fallDistance = prevFallDistance + (fallDistanceGained * Math.min(GravityService.getGravityFactor((Entity) (Object) this), 1));
}
}
}

View File

@@ -5,8 +5,6 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.xevianlight.aphelion.systems.GravityService;
import net.xevianlight.aphelion.systems.OxygenService; import net.xevianlight.aphelion.systems.OxygenService;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -28,10 +26,4 @@ public abstract class LivingEntityMixin extends Entity {
OxygenService.entityTick(level, entity); OxygenService.entityTick(level, entity);
} }
} }
@Inject(method = "travel", at = @At("HEAD"))
public void aphelion$travel(Vec3 travelVector, CallbackInfo ci) {
// temporarily disabled in favor of aphelion$getGravity mixin
//if (this.isControlledByLocalInstance()) GravityService.onEntityTravel(level(), (LivingEntity) (Object) this);
}
} }

View File

@@ -1,42 +0,0 @@
package net.xevianlight.aphelion.network;
import net.minecraft.world.entity.player.Player;
import net.xevianlight.aphelion.core.saveddata.types.GravityData;
import net.xevianlight.aphelion.systems.GravityService;
import net.xevianlight.aphelion.systems.OxygenService;
/// Read-only player state object; updated by a server packet every so often
public record ClientPlayerState(boolean oxygen, float gravity, float temperature) {
// Default player state
private static ClientPlayerState localData = new ClientPlayerState(true, GravityData.DEFAULT_GRAVITY * 0.5f, 50f);
public static void updateState(ClientPlayerState newData) {
onStateUpdate(localData, newData);
localData = newData;
}
public static ClientPlayerState getServerStateOf(Player player) {
return new ClientPlayerState(OxygenService.hasOxygen(player), GravityService.getGravityAccel(player), 50f);
}
/// For things like playing SFX, VFX, etc. etc.
public static void onStateUpdate(ClientPlayerState oldData, ClientPlayerState newData) {
// TODO: add sfx
if (!oldData.oxygen() && newData.oxygen()) {
// On oxygen gained
}
if (oldData.oxygen() && !newData.oxygen()) {
// On oxygen removed
}
if (newData.gravity() - 0.25f > oldData.gravity()) {
// On gravity increased by > 0.25
}
if (oldData.gravity() - 0.25f > newData.gravity()) {
// On gravity decreased by > 0.25
}
}
public static ClientPlayerState getLocalData() {
return localData;
}
}

View File

@@ -1,11 +0,0 @@
package net.xevianlight.aphelion.network;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import net.xevianlight.aphelion.network.packet.ClientPlayerStateUpdatePacket;
public class ClientPlayerStateUpdateHandler {
public static void handleDataOnMain(ClientPlayerStateUpdatePacket packet, IPayloadContext context) {
context.enqueueWork(() -> ClientPlayerState.updateState(new ClientPlayerState(packet.oxygen(), packet.gravity(), packet.temp())));
}
}

View File

@@ -2,16 +2,12 @@ package net.xevianlight.aphelion.network;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
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.client.event.ClientTickEvent; import net.neoforged.neoforge.client.event.ClientTickEvent;
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.entites.vehicles.RocketEntity; import net.xevianlight.aphelion.entites.vehicles.RocketEntity;
import net.xevianlight.aphelion.network.packet.ClientPlayerStateUpdatePacket;
import net.xevianlight.aphelion.network.packet.RocketLaunchPayload; import net.xevianlight.aphelion.network.packet.RocketLaunchPayload;
import net.xevianlight.aphelion.client.AphelionClient; import net.xevianlight.aphelion.client.AphelionClient;
@@ -31,17 +27,4 @@ public final class KeyNetwork {
PacketDistributor.sendToServer(new RocketLaunchPayload(rocket.getId())); PacketDistributor.sendToServer(new RocketLaunchPayload(rocket.getId()));
} }
} }
@SubscribeEvent
public static void onServerTick(ServerTickEvent.Post event) {
int FREQ = 4;
for (ServerPlayer p : event.getServer().getPlayerList().getPlayers()) {
if (p.tickCount % FREQ == 0) {
ClientPlayerState state = ClientPlayerState.getServerStateOf(p);
PacketDistributor.sendToPlayer(p, new ClientPlayerStateUpdatePacket(state.oxygen(), state.gravity(), state.temperature()));
}
}
}
} }

View File

@@ -10,7 +10,7 @@ import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData; import net.xevianlight.aphelion.core.saveddata.SpacePartitionSavedData;
import net.xevianlight.aphelion.core.saveddata.types.PartitionData; import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
import net.xevianlight.aphelion.network.packet.PartitionPayload; import net.xevianlight.aphelion.network.packet.PartitionPayload;
import net.xevianlight.aphelion.util.SpacePartitionHelper; import net.xevianlight.aphelion.util.SpacePartition;
import java.util.UUID; import java.util.UUID;
@@ -42,8 +42,8 @@ public final class PartitionSync {
} }
private static PartitionPayload computePartitionFor(ServerPlayer sp) { private static PartitionPayload computePartitionFor(ServerPlayer sp) {
int px = (int)Math.floor(sp.getX() / SpacePartitionHelper.SIZE); int px = (int)Math.floor(sp.getX() / SpacePartition.SIZE);
int pz = (int)Math.floor(sp.getZ() / SpacePartitionHelper.SIZE); int pz = (int)Math.floor(sp.getZ() / SpacePartition.SIZE);
PartitionData live = SpacePartitionSavedData.get(sp.serverLevel()).getData(px, pz); PartitionData live = SpacePartitionSavedData.get(sp.serverLevel()).getData(px, pz);

View File

@@ -1,27 +0,0 @@
package net.xevianlight.aphelion.network;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import net.xevianlight.aphelion.block.entity.custom.GravityTestBlockEntity;
import net.xevianlight.aphelion.network.packet.UpdateGravityTestBlockPacket;
public class UpdateGravityTestBlockHandler {
public static void handleDataOnMain(UpdateGravityTestBlockPacket packet, IPayloadContext context) {
context.enqueueWork(() -> {
BlockPos pos = packet.pos();
float radius = packet.radius();
float strength = packet.strength();
Level level = context.player().level();
if (level.getBlockEntity(pos) instanceof GravityTestBlockEntity blockEntity) {
blockEntity.setRadius(radius);
blockEntity.setStrength(strength);
blockEntity.sendUpdate();
level.sendBlockUpdated(pos, level.getBlockState(pos), level.getBlockState(pos), Block.UPDATE_ALL);
}
});
}
}

View File

@@ -1,29 +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 ClientPlayerStateUpdatePacket(boolean oxygen, float gravity, float temp) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<ClientPlayerStateUpdatePacket> TYPE = new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "player_state_update"));
public static final StreamCodec<ByteBuf, ClientPlayerStateUpdatePacket> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL,
ClientPlayerStateUpdatePacket::oxygen,
ByteBufCodecs.FLOAT,
ClientPlayerStateUpdatePacket::gravity,
ByteBufCodecs.FLOAT,
ClientPlayerStateUpdatePacket::temp,
ClientPlayerStateUpdatePacket::new
);
@Override
public CustomPacketPayload.Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@@ -6,6 +6,7 @@ import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.core.saveddata.types.PartitionData; import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
import org.jetbrains.annotations.NotNull;
import java.util.Objects; import java.util.Objects;
@@ -21,7 +22,7 @@ public record PartitionPayload(PartitionData partitionData) implements CustomPac
); );
@Override @Override
public Type<? extends CustomPacketPayload> type() { public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE; return TYPE;
} }

View File

@@ -1,30 +0,0 @@
package net.xevianlight.aphelion.network.packet;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.BlockPos;
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 UpdateGravityTestBlockPacket(BlockPos pos, float radius, float strength) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<UpdateGravityTestBlockPacket> TYPE =
new CustomPacketPayload.Type<>(ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "update_oxygen_test_block"));
public static final StreamCodec<ByteBuf, UpdateGravityTestBlockPacket> STREAM_CODEC = StreamCodec.composite(
BlockPos.STREAM_CODEC,
UpdateGravityTestBlockPacket::pos,
ByteBufCodecs.FLOAT,
UpdateGravityTestBlockPacket::radius,
ByteBufCodecs.FLOAT,
UpdateGravityTestBlockPacket::strength,
UpdateGravityTestBlockPacket::new
);
@Override
public CustomPacketPayload.Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@@ -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;

View 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));
}

View File

@@ -9,6 +9,7 @@ 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, boolean oxygen,
@@ -16,6 +17,7 @@ public record Planet(
) { ) {
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),
ResourceKey.codec(ModRegistries.ORBIT).fieldOf("orbit").forGetter(Planet::orbit),
Codec.DOUBLE.fieldOf("orbit_distance").forGetter(Planet::orbitDistance), Codec.DOUBLE.fieldOf("orbit_distance").forGetter(Planet::orbitDistance),
ResourceKey.codec(ModRegistries.STAR_SYSTEM).fieldOf("star_system").forGetter(Planet::system), ResourceKey.codec(ModRegistries.STAR_SYSTEM).fieldOf("star_system").forGetter(Planet::system),
Codec.BOOL.fieldOf("oxygen").forGetter(Planet::oxygen), Codec.BOOL.fieldOf("oxygen").forGetter(Planet::oxygen),

View File

@@ -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,6 +18,7 @@ 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, true,
@@ -48,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);

View File

@@ -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);
} }

View File

@@ -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) {

View File

@@ -1,110 +0,0 @@
package net.xevianlight.aphelion.screen;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.BrewingStandMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.neoforged.neoforge.items.SlotItemHandler;
import net.xevianlight.aphelion.block.entity.custom.GravityTestBlockEntity;
import net.xevianlight.aphelion.block.entity.custom.TestBlockEntity;
import org.jetbrains.annotations.NotNull;
public class GravityTestBlockMenu extends AbstractContainerMenu {
public final GravityTestBlockEntity blockEntity;
private final Level level;
public GravityTestBlockMenu(int i, Inventory inventory, FriendlyByteBuf extraData) {
this(i, inventory, inventory.player.level().getBlockEntity(extraData.readBlockPos()));
}
public GravityTestBlockMenu(int i, Inventory inventory, BlockEntity blockEntity) {
super(ModMenuTypes.GRAVITY_TEST_BLOCK_MENU.get(), i);
this.blockEntity = ((GravityTestBlockEntity) blockEntity);
this.level = inventory.player.level();
// Init stuff
addPlayerInventory(inventory);
addPlayerHotbar(inventory);
}
@Override
public boolean stillValid(Player player) {
return true;
}
// CREDIT GOES TO: diesieben07 | https://github.com/diesieben07/SevenCommons
// must assign a slot number to each of the slots used by the GUI.
// For this container, we can see both the tile inventory's slots as well as the player inventory slots and the hotbar.
// Each time we add a Slot to the container, it automatically increases the slotIndex, which means
// 0 - 8 = hotbar slots (which will map to the InventoryPlayer slot numbers 0 - 8)
// 9 - 35 = player inventory slots (which map to the InventoryPlayer slot numbers 9 - 35)
// 36 - 44 = TileInventory slots, which map to our TileEntity slot numbers 0 - 8)
private static final int HOTBAR_SLOT_COUNT = 9;
private static final int PLAYER_INVENTORY_ROW_COUNT = 3;
private static final int PLAYER_INVENTORY_COLUMN_COUNT = 9;
private static final int PLAYER_INVENTORY_SLOT_COUNT = PLAYER_INVENTORY_COLUMN_COUNT * PLAYER_INVENTORY_ROW_COUNT;
private static final int VANILLA_SLOT_COUNT = HOTBAR_SLOT_COUNT + PLAYER_INVENTORY_SLOT_COUNT;
private static final int VANILLA_FIRST_SLOT_INDEX = 0;
private static final int TE_INVENTORY_FIRST_SLOT_INDEX = VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT;
// THIS YOU HAVE TO DEFINE!
private static final int TE_INVENTORY_SLOT_COUNT = 0; // must be the number of slots you have!
@Override
public @NotNull ItemStack quickMoveStack(Player playerIn, int pIndex) {
Slot sourceSlot = slots.get(pIndex);
if (sourceSlot == null || !sourceSlot.hasItem()) return ItemStack.EMPTY; //EMPTY_ITEM
ItemStack sourceStack = sourceSlot.getItem();
ItemStack copyOfSourceStack = sourceStack.copy();
// Check if the slot clicked is one of the vanilla container slots
if (pIndex < VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT) {
// This is a vanilla container slot so merge the stack into the tile inventory
if (!moveItemStackTo(sourceStack, TE_INVENTORY_FIRST_SLOT_INDEX, TE_INVENTORY_FIRST_SLOT_INDEX
+ TE_INVENTORY_SLOT_COUNT, false)) {
blockEntity.sendUpdate();
return ItemStack.EMPTY; // EMPTY_ITEM
}
} else if (pIndex < TE_INVENTORY_FIRST_SLOT_INDEX + TE_INVENTORY_SLOT_COUNT) {
// This is a TE slot so merge the stack into the players inventory
if (!moveItemStackTo(sourceStack, VANILLA_FIRST_SLOT_INDEX, VANILLA_FIRST_SLOT_INDEX + VANILLA_SLOT_COUNT, false)) {
blockEntity.sendUpdate();
return ItemStack.EMPTY;
}
} else {
System.out.println("Invalid slotIndex:" + pIndex);
return ItemStack.EMPTY;
}
// If stack size == 0 (the entire stack was moved) set slot contents to null
if (sourceStack.getCount() == 0) {
sourceSlot.set(ItemStack.EMPTY);
blockEntity.sendUpdate();
} else {
blockEntity.sendUpdate();
sourceSlot.setChanged();
}
sourceSlot.onTake(playerIn, sourceStack);
blockEntity.sendUpdate();
return copyOfSourceStack;
}
private void addPlayerInventory(Inventory playerInventory) {
for (int i = 0; i < 3; ++i) {
for (int l = 0; l < 9; ++l) {
this.addSlot(new Slot(playerInventory, l + i * 9 + 9, 8 + l * 18, 84 + i * 18));
}
}
}
private void addPlayerHotbar(Inventory playerInventory) {
for (int i = 0; i < 9; ++i) {
this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142));
}
}
}

View File

@@ -1,94 +0,0 @@
package net.xevianlight.aphelion.screen;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.StringWidget;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.gui.screens.inventory.FurnaceScreen;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Inventory;
import net.neoforged.neoforge.network.PacketDistributor;
import net.xevianlight.aphelion.Aphelion;
import net.xevianlight.aphelion.network.packet.UpdateGravityTestBlockPacket;
public class GravityTestBlockScreen extends AbstractContainerScreen<GravityTestBlockMenu> {
private static final ResourceLocation GUI_TEXTURE =
ResourceLocation.fromNamespaceAndPath(Aphelion.MOD_ID, "textures/gui/gravity_test_block/gui.png");
public GravityTestBlockScreen(GravityTestBlockMenu menu, Inventory playerInventory, Component title) {
super(menu, playerInventory, title);
}
private StringWidget gravityWidget;
private StringWidget rangeWidget;
@Override
protected void init() {
super.init();
// Gets rid of labels
this.inventoryLabelY = 73;
this.titleLabelY = 5;
// Increase Gravity
this.addRenderableWidget(Button.builder(Component.literal("+"), (button) -> {
PacketDistributor.sendToServer(new UpdateGravityTestBlockPacket(menu.blockEntity.getBlockPos(), menu.blockEntity.areaSize, menu.blockEntity.gravityStrength + 0.1f));
}).bounds(this.leftPos + 7, this.topPos + 30, 9, 9).build());
// Decrease Gravity
this.addRenderableWidget(Button.builder(Component.literal("-"), (button) -> {
PacketDistributor.sendToServer(new UpdateGravityTestBlockPacket(menu.blockEntity.getBlockPos(), menu.blockEntity.areaSize, menu.blockEntity.gravityStrength - 0.1f));
}).bounds(this.leftPos + 19, this.topPos + 30, 9, 9).build());
// Increase Radius
this.addRenderableWidget(Button.builder(Component.literal("+"), (button) -> {
PacketDistributor.sendToServer(new UpdateGravityTestBlockPacket(menu.blockEntity.getBlockPos(), menu.blockEntity.areaSize + 1, menu.blockEntity.gravityStrength));
}).bounds(this.leftPos + 135, this.topPos + 32, 9, 9).build());
// Decrease Radius
this.addRenderableWidget(Button.builder(Component.literal("-"), (button) -> {
PacketDistributor.sendToServer(new UpdateGravityTestBlockPacket(menu.blockEntity.getBlockPos(), menu.blockEntity.areaSize - 1, menu.blockEntity.gravityStrength));
}).bounds(this.leftPos + 147, this.topPos + 32, 9, 9).build());
// Current Gravity
gravityWidget = new StringWidget(
this.leftPos + 11,
this.topPos+46,
26,
9,
Component.literal("" + menu.blockEntity.gravityStrength),
this.font);
this.addRenderableWidget(gravityWidget);
// Current Radius
rangeWidget = new StringWidget(
this.leftPos + 139,
this.topPos+46,
26,
9,
Component.literal("" + menu.blockEntity.areaSize),
this.font);
this.addRenderableWidget(rangeWidget);
}
@Override
protected void renderBg(GuiGraphics pGuiGraphics, float pPartialTick, int pMouseX, int pMouseY) {
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
RenderSystem.setShaderTexture(0, GUI_TEXTURE);
int x = (width - imageWidth) / 2;
int y = (height - imageHeight) / 2;
pGuiGraphics.blit(GUI_TEXTURE, x, y, 0, 0, imageWidth, imageHeight);
}
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) {
renderBackground(guiGraphics, mouseX, mouseY, delta);
super.render(guiGraphics, mouseX, mouseY, delta);
renderTooltip(guiGraphics, mouseX, mouseY);
gravityWidget.setMessage(Component.literal(String.format("%.1f", menu.blockEntity.gravityStrength)));
rangeWidget.setMessage(Component.literal("" + menu.blockEntity.areaSize));
}
}

View File

@@ -10,8 +10,6 @@ import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister; import net.neoforged.neoforge.registries.DeferredRegister;
import net.xevianlight.aphelion.Aphelion; import net.xevianlight.aphelion.Aphelion;
import java.awt.*;
public class ModMenuTypes { public class ModMenuTypes {
public static final DeferredRegister<MenuType<?>> MENUS = public static final DeferredRegister<MenuType<?>> MENUS =
DeferredRegister.create(Registries.MENU, Aphelion.MOD_ID); DeferredRegister.create(Registries.MENU, Aphelion.MOD_ID);
@@ -25,9 +23,6 @@ public class ModMenuTypes {
public static DeferredHolder<MenuType<?>,MenuType<VacuumArcFurnaceMenu>> VACUUM_ARC_FURNACE_MENU = public static DeferredHolder<MenuType<?>,MenuType<VacuumArcFurnaceMenu>> VACUUM_ARC_FURNACE_MENU =
registerMenuType("vacuum_arc_furnace_menu", VacuumArcFurnaceMenu::new); registerMenuType("vacuum_arc_furnace_menu", VacuumArcFurnaceMenu::new);
public static DeferredHolder<MenuType<?>,MenuType<GravityTestBlockMenu>> GRAVITY_TEST_BLOCK_MENU =
registerMenuType("gravity_test_block_menu", GravityTestBlockMenu::new);
private static <T extends AbstractContainerMenu>DeferredHolder<MenuType<?>, MenuType<T>> registerMenuType(String name, private static <T extends AbstractContainerMenu>DeferredHolder<MenuType<?>, MenuType<T>> registerMenuType(String name,
IContainerFactory<T> factory) { IContainerFactory<T> factory) {
return MENUS.register(name, () -> IMenuTypeExtension.create(factory)); return MENUS.register(name, () -> IMenuTypeExtension.create(factory));

View File

@@ -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) {

View File

@@ -1,79 +0,0 @@
package net.xevianlight.aphelion.systems;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.xevianlight.aphelion.network.ClientPlayerState;
import net.xevianlight.aphelion.core.saveddata.GravitySavedData;
import net.xevianlight.aphelion.core.saveddata.types.GravityData;
public class GravityService {
public static void setGravityArea(ServerLevel level, BlockPos pos, float accel, int radius) {
GravitySavedData.get(level).setGravityRegion(pos, new GravityData(accel, radius));
}
public static void removeGravityArea(ServerLevel level, BlockPos pos) {
GravitySavedData.get(level).removeGravityRegion(pos);
}
public static float getGravityAccel(Level level, BlockPos pos) {
if (level.isClientSide) {
// Pull from the client data b/c we can't access the server's saved data
return ClientPlayerState.getLocalData().gravity();
}
// TODO: maybe change this based on how stuff pans out
var gravity = GravitySavedData.get((ServerLevel) level).getGravityMax(pos);
return gravity;
}
public static float getGravityAccel(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 getGravityAccel(entity.level(), entityBlockPos);
}
public static float getGravityFactor(Entity entity) {
float gravityAccelReal = getGravityAccel(entity);
// How many times normal gravity you're experiencing.
// "normal gravity" varies across different entities. Thankfully, minecraft slaps a "protected" status
// on LivingEntity.getGravity(), so i graciously get to go fuck myself and not care.
// Players are 0.08 units/second/travel() of gravity (from what i've gathered)
return gravityAccelReal / GravityData.ONE_G;
}
/// Called by LivingEntity$travel mixin
public static void onEntityTravel(Level level, LivingEntity entity) {
if (
entity.isFallFlying() || entity.isInLiquid() ||
entity.isUnderWater() ||
entity.hasEffect(MobEffects.SLOW_FALLING)
) return;
float gravityAccelReal = getGravityAccel(entity);
// How many times normal gravity you're experiencing.
// "normal gravity" varies across different entities. Thankfully, minecraft slaps a "protected" status
// on LivingEntity.getGravity(), so i graciously get to go fuck myself and not care.
// Players are 0.08 units/second/travel() of gravity (from what i've gathered)
float gravityFactor = gravityAccelReal / GravityData.ONE_G;
// NOTE: this might cause certain entities to fly into the stratosphere at ultra low gravity,
// seeing as this isn't the same for all entities.
// Thankfully, though, this should have no effect on anything at default gravity.
float baseGameGravityAccel = 0.08f;
float translatedAccel = baseGameGravityAccel * gravityFactor;
Vec3 currentVelocity = entity.getDeltaMovement();
// add baseGameGravity to cancel normal gravity, then subtract the new gravity
if (translatedAccel > 0) entity.setDeltaMovement(currentVelocity.x(), currentVelocity.y() + (baseGameGravityAccel - translatedAccel), currentVelocity.z());
else entity.setDeltaMovement(currentVelocity.x(), currentVelocity.y() + baseGameGravityAccel, currentVelocity.z());
}
}

View 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));
}
};
}
}

View File

@@ -47,7 +47,7 @@ public final class FloodFill3D {
}; };
public static Set<BlockPos> run(Level level, BlockPos start, int limit, SolidBlockPredicate predicate, boolean retainOrder) { public static Set<BlockPos> run(Level level, BlockPos start, int limit, SolidBlockPredicate predicate, boolean retainOrder) {
level.getProfiler().push("adastra-floodfill"); level.getProfiler().push("aphelion-floodfill");
LongSet positions = retainOrder ? new LongLinkedOpenHashSet(limit) : new LongOpenHashSet(limit); LongSet positions = retainOrder ? new LongLinkedOpenHashSet(limit) : new LongOpenHashSet(limit);
LongArrayFIFOQueue queue = new LongArrayFIFOQueue(limit); LongArrayFIFOQueue queue = new LongArrayFIFOQueue(limit);

View File

@@ -20,6 +20,7 @@ public class ModTags {
public static final TagKey<Block> LAUNCH_PAD = createTag("launch_pad"); 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> PASSES_FLOOD_FILL = createTag("passes_flood_fill");
public static final TagKey<Block> BLOCKS_FLOOD_FILL = createTag("blocks_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));

View File

@@ -5,9 +5,17 @@ import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.world.level.Level; 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;
@@ -19,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);
} }
@@ -34,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) {
@@ -69,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;
} }
@@ -92,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) {
@@ -138,36 +156,118 @@ public final class RocketStructure {
return new Extents(minX, minY, minZ, maxX, maxY, maxZ); return new Extents(minX, minY, minZ, maxX, maxY, maxZ);
} }
public static RocketStructure capture(Level level, BlockPos origin, int rx, int ry, int rz) {
return new RocketStructure(s -> {
for (int dy = -ry; dy <= ry; dy++) {
for (int dx = -rx; dx <= rx; dx++) {
for (int dz = -rz; dz <= rz; dz++) {
BlockPos p = origin.offset(dx, dy, dz);
BlockState st = level.getBlockState(p);
// Skip air and unbreakables/forbidden blocks as you like
if (st.isAir()) continue;
// Optional: ignore the assembler block itself
// if (p.equals(origin)) continue;
s.add(dx, dy, dz, st);
}
}
}
});
}
public static void clearCaptured(Level level, BlockPos origin, RocketStructure struct) { 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++) { for (int i = 0; i < struct.size(); i++) {
int packed = struct.packedPosAt(i); int packed = struct.packedPosAt(i);
int dx = RocketStructure.unpackX(packed); BlockPos wp = origin.offset(
int dy = RocketStructure.unpackY(packed); RocketStructure.unpackX(packed),
int dz = RocketStructure.unpackZ(packed); RocketStructure.unpackY(packed),
RocketStructure.unpackZ(packed)
);
BlockPos wp = origin.offset(dx, dy, dz); BlockState st = level.getBlockState(wp);
level.setBlock(wp, Blocks.AIR.defaultBlockState(), 3); 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;
}
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -4,7 +4,6 @@
"package": "net.xevianlight.aphelion.mixins", "package": "net.xevianlight.aphelion.mixins",
"compatibilityLevel": "JAVA_21", "compatibilityLevel": "JAVA_21",
"mixins": [ "mixins": [
"common.EntityMixin",
"common.LivingEntityMixin" "common.LivingEntityMixin"
], ],
"client": [ "client": [

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