6 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
89 changed files with 2574 additions and 297 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,11 +1,12 @@
// 1.21.1 2026-02-04T21:03:31.3164337 Loot Tables // 1.21.1 2026-02-07T22:29:56.7198656 Loot Tables
69d8318ddba171526d1fabb87d9d93548ed8598e data/aphelion/loot_table/blocks/arc_furnace_casing.json 69d8318ddba171526d1fabb87d9d93548ed8598e data/aphelion/loot_table/blocks/arc_furnace_casing.json
05f08985e601d30116f67e2f07b48b03b40cdca6 data/aphelion/loot_table/blocks/block_steel.json 05f08985e601d30116f67e2f07b48b03b40cdca6 data/aphelion/loot_table/blocks/block_steel.json
ff43a9c3741faf10b1e156a7a74d5cfb035cc118 data/aphelion/loot_table/blocks/dimension_changer.json ff43a9c3741faf10b1e156a7a74d5cfb035cc118 data/aphelion/loot_table/blocks/dimension_changer.json
b63130d9c10485676303d729807b6fcaac080294 data/aphelion/loot_table/blocks/electric_arc_furnace.json b63130d9c10485676303d729807b6fcaac080294 data/aphelion/loot_table/blocks/electric_arc_furnace.json
b9cfe672ead8e2673a7b2f5c4cec831e7e8e7040 data/aphelion/loot_table/blocks/launch_pad.json 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
f3178154dadee30cc28f5ff23af45be98f2766cf data/aphelion/loot_table/blocks/rocket_assembler_block.json 87dfbcce4a96ea0ee6e9fc7908aa77d42d1904e5 data/aphelion/loot_table/blocks/rocket_assembler.json
9e269f103ac1ce517c1a089b8ca5474af085bd66 data/aphelion/loot_table/blocks/rocket_seat.json
1ab50c99e9f478840b9d003fd56ebdcab12fbbce data/aphelion/loot_table/blocks/test_block.json 1ab50c99e9f478840b9d003fd56ebdcab12fbbce data/aphelion/loot_table/blocks/test_block.json
7d8eeb99a1bc942a6e2cf292b21fd4534062b5ab data/aphelion/loot_table/blocks/vacuum_arc_furnace_controller.json 7d8eeb99a1bc942a6e2cf292b21fd4534062b5ab data/aphelion/loot_table/blocks/vacuum_arc_furnace_controller.json
797bf9839d79e08b4832c9eaf3cb303b0471ed0c data/aphelion/loot_table/blocks/vaf_dummy_block.json 797bf9839d79e08b4832c9eaf3cb303b0471ed0c data/aphelion/loot_table/blocks/vaf_dummy_block.json

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-04T21:03:31.3154339 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
28131a570d3666b7f323de4ad8a69e52ceec92e2 assets/aphelion/blockstates/oxygen_test_block.json 28131a570d3666b7f323de4ad8a69e52ceec92e2 assets/aphelion/blockstates/oxygen_test_block.json
85c7c0dab53d0219b315c822147a90ade9075844 assets/aphelion/blockstates/rocket_assembler_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
746f23f150a01524ad03cbd1eb822bfbb7cf453b assets/aphelion/models/block/oxygen_test_block.json 746f23f150a01524ad03cbd1eb822bfbb7cf453b assets/aphelion/models/block/oxygen_test_block.json
17eb7327e504f7a88028af804e046281d9719fdf assets/aphelion/models/block/rocket_assembler_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
24cf60e70f7d9450b0e70cf017662e80971bae17 assets/aphelion/models/item/oxygen_test_block.json 24cf60e70f7d9450b0e70cf017662e80971bae17 assets/aphelion/models/item/oxygen_test_block.json
dafa6e1a3cfd753e211ae94179a433c69e9d2a28 assets/aphelion/models/item/rocket_assembler_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

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

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
{
"type": "minecraft:block",
"pools": [
{
"bonus_rolls": 0.0,
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
],
"entries": [
{
"type": "minecraft:item",
"name": "aphelion:rocket_seat"
}
],
"rolls": 1.0
}
],
"random_sequence": "aphelion:blocks/rocket_seat"
}

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;
@@ -127,14 +129,15 @@ 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

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,7 @@ 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.BasicEntityBlock;
import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock; import net.xevianlight.aphelion.block.custom.base.BasicHorizontalEntityBlock;
import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity; import net.xevianlight.aphelion.block.entity.custom.ElectricArcFurnaceEntity;
@@ -77,7 +78,7 @@ public class ElectricArcFurnace extends BasicHorizontalEntityBlock {
public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) { public InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult result) {
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) { if (!level.isClientSide && player instanceof ServerPlayer serverPlayer && level.getBlockEntity(pos) instanceof ElectricArcFurnaceEntity electricArcFurnaceEntity) {
serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos); serverPlayer.openMenu(new SimpleMenuProvider(electricArcFurnaceEntity, Component.literal("Electric Arc Furnace")), pos);
} }
return InteractionResult.sidedSuccess(level.isClientSide); return InteractionResult.sidedSuccess(level.isClientSide);
} }

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

@@ -2,27 +2,37 @@ package net.xevianlight.aphelion.block.custom;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos; 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.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BooleanProperty; 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.custom.base.BasicHorizontalEntityBlock;
import net.xevianlight.aphelion.block.entity.custom.RocketAssemblerBlockEntity; 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 net.xevianlight.aphelion.util.AphelionBlockStateProperties;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class RocketAssemblerBlock extends BasicHorizontalEntityBlock { public class RocketAssembler extends BasicHorizontalEntityBlock {
public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED; public static final BooleanProperty FORMED = AphelionBlockStateProperties.FORMED;
public RocketAssemblerBlock(Properties properties) { public RocketAssembler(Properties properties) {
super(properties, true); super(properties, true);
} }
public static final MapCodec<RocketAssemblerBlock> CODEC = simpleCodec(RocketAssemblerBlock::new); public static final MapCodec<RocketAssembler> CODEC = simpleCodec(RocketAssembler::new);
@Override @Override
protected @NotNull MapCodec<? extends BaseEntityBlock> codec() { protected @NotNull MapCodec<? extends BaseEntityBlock> codec() {
@@ -53,4 +63,12 @@ public class RocketAssemblerBlock extends BasicHorizontalEntityBlock {
builder.add(FORMED); builder.add(FORMED);
super.createBlockStateDefinition(builder); 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

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

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

@@ -9,41 +9,78 @@ import net.minecraft.world.level.block.state.BlockState;
public interface TickableBlockEntity { public interface TickableBlockEntity {
/** /**
* Runs on both the client AND server. * Runs on both the client and server once per tick.
* @param level *
* @param time * <p>This is intended for logic that is common to both sides. Side-specific logic
* @param state * should go in {@link #clientTick(ClientLevel, long, BlockState, BlockPos)} or
* @param pos * {@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) {}; default void tick (Level level, long time, BlockState state, BlockPos pos) {};
/** /**
* Runs on the client only * Runs on the client only once per tick.
* @param level *
* @param time * <p>Use this for client-side visual updates, particles, sounds, animation state,
* @param state * or other logic that must not run on the logical server.</p>
* @param pos *
* @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 * Runs on the server only once per tick.
* @param level *
* @param time * <p>Use this for authoritative game logic such as inventory processing, energy
* @param state * generation/consumption, entity spawning, saving state, and network sync triggers.</p>
* @param pos *
* @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;
}; }
/** /**
* Runs on client AND server, once only. * Performs initialization logic for this object.
* @param level *
* @param state * <p>This method is called at the start of each tick on both the client and server
* @param pos * 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);

View File

@@ -42,7 +42,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
public class ElectricArcFurnaceEntity extends BlockEntity implements MenuProvider, TickableBlockEntity { 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;
@@ -309,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);
@@ -344,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++) {

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

@@ -16,10 +16,15 @@ 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.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.BlockStateProperties;
import net.minecraft.world.phys.AABB;
import net.xevianlight.aphelion.Aphelion; 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.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.AphelionBlockStateProperties;
import net.xevianlight.aphelion.util.ModTags; import net.xevianlight.aphelion.util.ModTags;
import net.xevianlight.aphelion.util.RocketStructure; import net.xevianlight.aphelion.util.RocketStructure;
@@ -28,6 +33,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque; 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 {
@@ -35,6 +42,8 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
BlockPos padScanStart = BlockPos.ZERO; BlockPos padScanStart = BlockPos.ZERO;
private PadInfo padBounds; private PadInfo padBounds;
private @Nullable PartitionData data;
public @Nullable PadInfo getPadBounds() { public @Nullable PadInfo getPadBounds() {
return padBounds; return padBounds;
} }
@@ -57,50 +66,21 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
return dx * dy * dz; return dx * dy * dz;
} }
}
private final Block TOWER_BLOCK = ModBlocks.BLOCK_STEEL.get(); public BlockPos getCenter() {
public BlockPos towerBasePos; int centerX = (min.getX() + max.getX()) / 2;
int centerZ = (min.getZ() + max.getZ()) / 2;
public @Nullable PadInfo getPlatform() { // bottom Y level
// TODO int y = min.getY();
int y = this.padScanStart.getY();
BlockPos start = this.padScanStart;
if (level == null) return null; return new BlockPos(centerX, y, centerZ);
if (!isPad(level.getBlockState(start))) return null;
int minX = start.getX();
while (isPad(level.getBlockState(new BlockPos(minX - 1, y, start.getZ())))) minX--;
// Find maxX by walking east
int maxX = start.getX();
while (isPad(level.getBlockState(new BlockPos(maxX + 1, y, start.getZ())))) maxX++;
// Find minZ by walking north
int minZ = start.getZ();
while (isPad(level.getBlockState(new BlockPos(start.getX(), y, minZ - 1)))) minZ--;
// Find maxZ by walking south
int maxZ = start.getZ();
while (isPad(level.getBlockState(new BlockPos(start.getX(), y, maxZ + 1)))) maxZ++;
int width = (maxX - minX) + 1;
int length = (maxZ - minZ) + 1;
// Must be square
if (width != length) return null;
// Verify the entire rectangle is filled with pad blocks
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
if (!isPad(level.getBlockState(new BlockPos(x, y, z)))) return null;
}
} }
return new PadInfo(new BlockPos(minX, y, minZ), new BlockPos(maxX, y, maxZ));
} }
private static final Block TOWER_BLOCK = ModBlocks.BLOCK_STEEL.get();
public BlockPos towerBasePos;
private boolean connected(BlockState state, Direction dir) { private boolean connected(BlockState state, Direction dir) {
return switch (dir) { return switch (dir) {
case NORTH -> state.getValue(BlockStateProperties.NORTH); case NORTH -> state.getValue(BlockStateProperties.NORTH);
@@ -111,7 +91,7 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
}; };
} }
public @Nullable PadInfo getPlatformFill() { public @Nullable PadInfo getPlatformViaFill() {
if (level == null) return null; if (level == null) return null;
BlockPos start = this.padScanStart; BlockPos start = this.padScanStart;
@@ -128,8 +108,8 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
visited.add(start.asLong()); visited.add(start.asLong());
final Direction[] CARDINALS = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST}; final Direction[] CARDINALS = {Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST};
// When the $#$# are we going to have a rocket larger than 64x64... don't... // When the $#$# are we going to have a rocket larger than 32x32... don't...
final int MAX_PAD_BLOCKS = 4096; final int MAX_PAD_BLOCKS = 1024;
boolean towerFound = false; boolean towerFound = false;
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
@@ -144,7 +124,7 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
BlockPos n = p.relative(d); BlockPos n = p.relative(d);
if (!connected(s, d)) { if (!connected(s, d)) {
if (level.getBlockState(n).is(TOWER_BLOCK)) { if (isTower(level.getBlockState(n))) {
if (!towerFound) { if (!towerFound) {
towerBasePos = n; towerBasePos = n;
towerFound = true; towerFound = true;
@@ -194,16 +174,154 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
int h = 0; int h = 0;
BlockPos p = base.above(); BlockPos p = base.above();
while (level.getBlockState(p).is(TOWER_BLOCK)) { while (isTower(level.getBlockState(p))) {
h++; h++;
p = p.above(); p = p.above();
} }
return h; return h;
} }
public RocketStructure scan() { private BlockPos seatPos;
// TODO
throw new NotImplementedException(); 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
@@ -213,27 +331,84 @@ 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 = getPlatformFill(); PadInfo newBounds = getPlatformViaFill();
setPadBoundsAndSync(newBounds); setPadBoundsAndSync(newBounds);
boolean formed = newBounds != null; boolean formed = newBounds != null;
if (state.getValue(AphelionBlockStateProperties.FORMED) != formed) { if (state.getValue(AphelionBlockStateProperties.FORMED) != formed) {
level.setBlockAndUpdate(pos, state.setValue(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); facing = getBlockState().getValue(BlockStateProperties.HORIZONTAL_FACING);
padScanStart = getBlockPos().mutable().below().relative(facing.getOpposite()); 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) { private static boolean isPad(BlockState s) {
return s.is(ModTags.Blocks.LAUNCH_PAD); // or s.getBlock() == ModBlocks.PAD.get() 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 @Override
protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) { protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
super.saveAdditional(tag, registries); super.saveAdditional(tag, registries);
@@ -296,4 +471,6 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
this.loadAdditional(tag, registries); 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

@@ -47,7 +47,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Optional; import java.util.Optional;
public class VacuumArcFurnaceControllerEntity extends BlockEntity implements MenuProvider, IMultiblockController, TickableBlockEntity { public class VacuumArcFurnaceControllerEntity extends BlockEntity implements MenuProvider, IMultiblockController, TickableBlockEntity, IArcFurnaceLike {
private final int SIZE = 4; private final int SIZE = 4;
private final int ENERGY_CAPACITY = 64000; private final int ENERGY_CAPACITY = 64000;
@@ -357,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;
} }

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

@@ -1,14 +1,18 @@
package net.xevianlight.aphelion.block.entity.custom.renderer; 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.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.xevianlight.aphelion.block.entity.custom.RocketAssemblerBlockEntity; import net.xevianlight.aphelion.block.entity.custom.RocketAssemblerBlockEntity;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -19,9 +23,42 @@ public class RocketAssemblerBlockEntityRenderer implements BlockEntityRenderer<R
} }
@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 @Override
public void render(@NotNull RocketAssemblerBlockEntity be, float v, @NotNull PoseStack poseStack, @NotNull MultiBufferSource multiBufferSource, int i, int i1) { 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 (!Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) return;
if (be.getPadBounds() == null) return; if (be.getPadBounds() == null) return;
BlockPos min = be.getPadBounds().min(); BlockPos min = be.getPadBounds().min();
@@ -35,10 +72,20 @@ public class RocketAssemblerBlockEntityRenderer implements BlockEntityRenderer<R
poseStack.pushPose(); poseStack.pushPose();
poseStack.translate(-be.getBlockPos().getX(), -be.getBlockPos().getY(), -be.getBlockPos().getZ()); poseStack.translate(-be.getBlockPos().getX(), -be.getBlockPos().getY(), -be.getBlockPos().getZ());
VertexConsumer vc = multiBufferSource.getBuffer(RenderType.lines()); VertexConsumer lineVc = multiBufferSource.getBuffer(RenderType.lines());
LevelRenderer.renderLineBox(poseStack, lineVc, box, 0f, 1f, 0f, 1f);
LevelRenderer.renderLineBox(poseStack, vc, box, 0.0f, 1.0f, 0.0f, 1.0f); 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(); 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

@@ -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,20 +248,86 @@ 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;
})
)
)
)
)
.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; 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;
})
)
) )
) )
) )
@@ -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,6 +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<StationRocketEngineBlockEntity>> STATION_ROCKET_ENGINE_BLOCK_ENTITY =
BLOCK_ENTITIES.register("station_rocket_engine_block_entity", () -> BlockEntityType.Builder.of(
StationRocketEngineBlockEntity::new, ModBlocks.STATION_ROCKET_ENGINE.get()).build(null)
);
public static final Supplier<BlockEntityType<StationFlightComputerBlockEntity>> STATION_FLIGHT_COMPUTER_BLOCK_ENTITY =
BLOCK_ENTITIES.register("station_flight_computer_block_entity", () -> BlockEntityType.Builder.of(
StationFlightComputerBlockEntity::new, ModBlocks.STATION_FLIGHT_COMPUTER_BLOCK.get()).build(null)
); );
} }

View File

@@ -10,14 +10,19 @@ import net.xevianlight.aphelion.block.dummy.VAFMultiblockDummyBlock;
public class ModBlocks { public class ModBlocks {
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Aphelion.MOD_ID); public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(Aphelion.MOD_ID);
public static final DeferredBlock<Block> TEST_BLOCK = BLOCKS.register("test_block", () -> new TestBlock(TestBlock.getProperties())); public static final DeferredBlock<Block> TEST_BLOCK = BLOCKS.register("test_block", () -> new TestBlock(TestBlock.getProperties()));
public static final DeferredBlock<Block> BLOCK_STEEL = BLOCKS.register("block_steel", () -> new BlockSteel(BlockSteel.getProperties())); public static final DeferredBlock<Block> BLOCK_STEEL = BLOCKS.register("block_steel", () -> new BlockSteel(BlockSteel.getProperties()));
public static final DeferredBlock<Block> LAUNCH_PAD = BLOCKS.register("launch_pad", () -> new LaunchPad(LaunchPad.getProperties())); public static final DeferredBlock<Block> LAUNCH_PAD = BLOCKS.register("launch_pad", () -> new LaunchPad(LaunchPad.getProperties()));
public static final DeferredBlock<Block> DIMENSION_CHANGER = BLOCKS.register("dimension_changer", () -> new DimensionChangerBlock(DimensionChangerBlock.getProperties())); public static final DeferredBlock<Block> DIMENSION_CHANGER = BLOCKS.register("dimension_changer", () -> new DimensionChangerBlock(DimensionChangerBlock.getProperties()));
public static final DeferredBlock<Block> ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties())); public static final DeferredBlock<Block> ELECTRIC_ARC_FURNACE = BLOCKS.register("electric_arc_furnace", () -> new ElectricArcFurnace(ElectricArcFurnace.getProperties()));
public static final DeferredBlock<Block> ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.getProperties())); public static final DeferredBlock<Block> ARC_FURNACE_CASING_BLOCK = BLOCKS.register("arc_furnace_casing", () -> new ArcFurnaceCasingBlock(ArcFurnaceCasingBlock.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> 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_assembler_block", () -> new RocketAssemblerBlock(RocketAssemblerBlock.getProperties())); public static final DeferredBlock<Block> ROCKET_ASSEMBLER = BLOCKS.register("rocket_assembler", () -> new RocketAssembler(RocketAssembler.getProperties()));
public static final DeferredBlock<Block> ROCKET_SEAT = BLOCKS.register("rocket_seat", () -> new RocketSeat(RocketSeat.getProperties()));
public static final DeferredBlock<Block> STATION_ROCKET_ENGINE = BLOCKS.register("station_rocket_engine", () -> new StationRocketEngineBlock(StationRocketEngineBlock.getProperties()));
public static final DeferredBlock<Block> BASIC_ROCKET_FUEL_TANK = BLOCKS.register("basic_rocket_fuel_tank", () -> new BasicRocketFuelTank(BasicRocketFuelTank.getProperties()));
public static final DeferredBlock<Block> BASIC_ROCKET_CONTAINER = BLOCKS.register("basic_rocket_container", () -> new BasicRocketContainer(BasicRocketContainer.getProperties()));
public static final DeferredBlock<Block> STATION_FLIGHT_COMPUTER_BLOCK = BLOCKS.register("station_flight_computer", () -> new StationFlightComputerBlock(StationFlightComputerBlock.getProperties()));
} }

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,6 +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_BLOCK); 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 {
@@ -37,6 +36,10 @@ public static final DeferredItem<Item> MUSIC_DISC_BIT_SHIFT = ITEMS.register("mu
public static final DeferredItem<BlockItem> VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties())); public static final DeferredItem<BlockItem> VACUUM_ARC_FURNACE_CONTROLLER = ITEMS.register("vacuum_arc_furnace_controller", () -> new BlockItem(ModBlocks.VACUUM_ARC_FURNACE_CONTROLLER.get(), VacuumArcFurnaceController.getItemProperties()));
public static final DeferredItem<BlockItem> OXYGEN_TEST_BLOCK = ITEMS.register("oxygen_test_block", () -> new BlockItem(ModBlocks.OXYGEN_TEST_BLOCK.get(), new Item.Properties())); public static final DeferredItem<BlockItem> OXYGEN_TEST_BLOCK = ITEMS.register("oxygen_test_block", () -> new BlockItem(ModBlocks.OXYGEN_TEST_BLOCK.get(), new Item.Properties()));
public static final DeferredItem<BlockItem> LAUNCH_PAD = ITEMS.register("launch_pad", () -> new BlockItem(ModBlocks.LAUNCH_PAD.get(), LaunchPad.getItemProperties())); public static final DeferredItem<BlockItem> LAUNCH_PAD = ITEMS.register("launch_pad", () -> new BlockItem(ModBlocks.LAUNCH_PAD.get(), LaunchPad.getItemProperties()));
public static final DeferredItem<BlockItem> ROCKET_ASSEMBLER_BLOCK = ITEMS.register("rocket_assembler_block", () -> new BlockItem(ModBlocks.ROCKET_ASSEMBLER_BLOCK.get(), RocketAssemblerBlock.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

@@ -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,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;
}
var destPlanet = PlanetCache.getOrDefault(destination);
var dist = destPlanet.orbitDistance() - currentPlanet.orbitDistance();
this.tripDistanceAU = dist;
return dist;
} }
public void setDistanceToDest(double distanceToDest) { public double getTripDistanceAU() {
this.distanceToDest = distanceToDest; return tripDistanceAU;
} }
public void travel(double distance) { public void setTripDistanceAU(double tripDistanceAU) {
distanceTraveled = Math.min( distanceTraveled + distance, distanceToDest); 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,7 +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.ROCKET_ASSEMBLER_BLOCK.get()); dropSelf(ModBlocks.ROCKET_ASSEMBLER.get());
dropSelf(ModBlocks.ROCKET_SEAT.get());
} }
@Override @Override

View File

@@ -29,7 +29,7 @@ public class ModBlockStateProvider extends BlockStateProvider {
// modLoc("block/test_block"), // modLoc("block/test_block"),
// mcLoc("block/furnace_front"), // mcLoc("block/furnace_front"),
// modLoc("block/test_block"))); // modLoc("block/test_block")));
blockItem(ModBlocks.ROCKET_ASSEMBLER_BLOCK); blockItem(ModBlocks.ROCKET_ASSEMBLER);
blockWithItem(ModBlocks.BLOCK_STEEL); blockWithItem(ModBlocks.BLOCK_STEEL);
blockWithItem(ModBlocks.DIMENSION_CHANGER); blockWithItem(ModBlocks.DIMENSION_CHANGER);

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,11 +194,13 @@ public class RocketEntity extends VehicleEntity implements IEntityWithComplexSpa
} }
case DESCEND -> tickDescend(); case DESCEND -> tickDescend();
} }
// Simple upward movement
} }
move(MoverType.SELF, getDeltaMovement()); // Catch ANY edge cases which may cause a crash trying to move an entity as its chunk is unloading
if (!this.isRemoved() && this.isAlive() && level().hasChunkAt(blockPosition())) {
move(MoverType.SELF, getDeltaMovement());
}
} }
private void tickIdle() { private void tickIdle() {
@@ -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,11 +8,13 @@ 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.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.packet.PartitionPayload; import net.xevianlight.aphelion.network.packet.PartitionPayload;
@@ -23,13 +25,20 @@ public class ModBusEvents {
@SubscribeEvent @SubscribeEvent
public static void registerCapabilities(RegisterCapabilitiesEvent event) { public static void registerCapabilities(RegisterCapabilitiesEvent event) {
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.TEST_BLOCK_ENTITY.get(), TestBlockEntity::getItemHandler); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.TEST_BLOCK_ENTITY.get(), TestBlockEntity::getItemHandler);
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getItemHandler); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getItemHandler);
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getEnergyStorage); event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.ELECTRIC_ARC_FURNACE_ENTITY.get(), ElectricArcFurnaceEntity::getEnergyStorage);
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getItemHandler); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getItemHandler);
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getEnergyStorage); event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VACUUM_ARC_FURNACE_ENTITY.get(), VacuumArcFurnaceControllerEntity::getEnergyStorage);
// event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), VAFMultiblockDummyBlockEntity::getItemHandler); // event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), VAFMultiblockDummyBlockEntity::getItemHandler);
event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getItemHandler); event.registerBlockEntity(Capabilities.ItemHandler.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getItemHandler);
event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getEnergyStorage); event.registerBlockEntity(Capabilities.EnergyStorage.BLOCK, ModBlockEntities.VAF_MULTIBLOCK_DUMMY_ENTITY.get(), BaseMultiblockDummyBlockEntity::getEnergyStorage);
event.registerEntity(Capabilities.ItemHandler.ENTITY, ModEntities.ROCKET.get(), (rocket, ctx) -> rocket.getInventory());
event.registerBlockEntity(Capabilities.FluidHandler.BLOCK, ModBlockEntities.STATION_ROCKET_ENGINE_BLOCK_ENTITY.get(), StationRocketEngineBlockEntity::getFluidStorage);
} }
@SubscribeEvent @SubscribeEvent

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

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

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

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

@@ -7,8 +7,9 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.xevianlight.aphelion.util.registries.ModRegistries; import net.xevianlight.aphelion.util.registries.ModRegistries;
public record Planet( public record Planet (
ResourceKey<Level> dimension, ResourceKey<Level> dimension,
ResourceKey<Orbit> orbit,
double orbitDistance, double orbitDistance,
ResourceKey<StarSystem> system, ResourceKey<StarSystem> system,
boolean oxygen, 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

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

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

@@ -0,0 +1,34 @@
{
"variants": {
"facing=east,formed=false": {
"model": "aphelion:block/rocket_assembler",
"y": 90
},
"facing=north,formed=false": {
"model": "aphelion:block/rocket_assembler"
},
"facing=south,formed=false": {
"model": "aphelion:block/rocket_assembler",
"y": 180
},
"facing=west,formed=false": {
"model": "aphelion:block/rocket_assembler",
"y": 270
},
"facing=east,formed=true": {
"model": "aphelion:block/rocket_assembler_formed",
"y": 90
},
"facing=north,formed=true": {
"model": "aphelion:block/rocket_assembler_formed"
},
"facing=south,formed=true": {
"model": "aphelion:block/rocket_assembler_formed",
"y": 180
},
"facing=west,formed=true": {
"model": "aphelion:block/rocket_assembler_formed",
"y": 270
}
}
}

View File

@@ -1,34 +0,0 @@
{
"variants": {
"facing=east,formed=false": {
"model": "aphelion:block/rocket_assembler_block",
"y": 90
},
"facing=north,formed=false": {
"model": "aphelion:block/rocket_assembler_block"
},
"facing=south,formed=false": {
"model": "aphelion:block/rocket_assembler_block",
"y": 180
},
"facing=west,formed=false": {
"model": "aphelion:block/rocket_assembler_block",
"y": 270
},
"facing=east,formed=true": {
"model": "aphelion:block/rocket_assembler_block_formed",
"y": 90
},
"facing=north,formed=true": {
"model": "aphelion:block/rocket_assembler_block_formed"
},
"facing=south,formed=true": {
"model": "aphelion:block/rocket_assembler_block_formed",
"y": 180
},
"facing=west,formed=true": {
"model": "aphelion:block/rocket_assembler_block_formed",
"y": 270
}
}
}

View File

@@ -0,0 +1,13 @@
{
"custom_clouds": false,
"custom_sky": false,
"custom_weather": false,
"dimension": "minecraft:overworld",
"has_fog": true,
"has_thick_fog": false,
"render_in_rain": true,
"sunrise_angle": 45,
"sunrise_color": 14180147,
"horizon_height": -128,
"clear_color_scale": 1.0
}

View File

@@ -52,5 +52,12 @@
"command.aphelion.rocket.get_dim.success": "Target dimension of rocket is %s", "command.aphelion.rocket.get_dim.success": "Target dimension of rocket is %s",
"command.aphelion.rocket.set_pos.success": "Set target position of rocket to %s", "command.aphelion.rocket.set_pos.success": "Set target position of rocket to %s",
"command.aphelion.rocket.get_pos.success": "Target position of rocket is %s", "command.aphelion.rocket.get_pos.success": "Target position of rocket is %s",
"command.aphelion.rocket.get_pos.success.null": "Target position of rocket is not set" "command.aphelion.rocket.get_pos.success.null": "Target position of rocket is not set",
"command.aphelion.rocket.disassemble.success": "Rocket disassembled",
"command.aphelion.rocket.disassemble.failure": "Could not disassemble rocket",
"command.aphelion.station.invalid": "Station is invalid!",
"command.aphelion.station.owner.unset": "Station has no owner",
"command.aphelion.station.owner.get": "Station (%s %s) belongs to %s",
"command.aphelion.station.owner.set.success": "Set station (%s %s)'s owner to %s",
"command.aphelion.player.invalid": "Player is invalid"
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/0010", "top": "aphelion:block/launch_pad/0010",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/0010" "bottom": "aphelion:block/launch_pad/1000"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/0011", "top": "aphelion:block/launch_pad/0011",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/0011" "bottom": "aphelion:block/launch_pad/1001"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/0110", "top": "aphelion:block/launch_pad/0110",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/0110" "bottom": "aphelion:block/launch_pad/1100"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/0111", "top": "aphelion:block/launch_pad/0111",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/0111" "bottom": "aphelion:block/launch_pad/1101"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/1000", "top": "aphelion:block/launch_pad/1000",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/1000" "bottom": "aphelion:block/launch_pad/0010"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/1001", "top": "aphelion:block/launch_pad/1001",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/1001" "bottom": "aphelion:block/launch_pad/0011"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/1100", "top": "aphelion:block/launch_pad/1100",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/1100" "bottom": "aphelion:block/launch_pad/0110"
} }
} }

View File

@@ -3,6 +3,6 @@
"textures": { "textures": {
"top": "aphelion:block/launch_pad/1101", "top": "aphelion:block/launch_pad/1101",
"side": "aphelion:block/launch_pad/topbottom", "side": "aphelion:block/launch_pad/topbottom",
"bottom": "aphelion:block/launch_pad/1101" "bottom": "aphelion:block/launch_pad/0111"
} }
} }

View File

@@ -0,0 +1,8 @@
{
"dimension": "aphelion:mars",
"orbit": "aphelion:orbit/mars",
"orbit_distance": 1.5,
"star_system": "aphelion:sol",
"gravity": 1,
"oxygen": false
}

View File

@@ -1,7 +1,8 @@
{ {
"dimension": "minecraft:overworld", "dimension": "minecraft:overworld",
"orbit": "aphelion:orbit/overworld",
"orbit_distance": 1, "orbit_distance": 1,
"star_system": "aphelon:sol", "star_system": "aphelion:sol",
"gravity": 1, "gravity": 1,
"oxygen": false "oxygen": false
} }