More stuff for stations. Cleanup.

This commit is contained in:
XevianLight
2026-02-22 00:36:48 -07:00
parent 018886768e
commit 0f4b98a912
17 changed files with 466 additions and 102 deletions

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,6 +2,7 @@ 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.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
@@ -15,6 +16,9 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.phys.BlockHitResult; 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.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;

View File

@@ -37,14 +37,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,77 @@
package net.xevianlight.aphelion.block.custom.base;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
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;
private @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) {
}
/**
* Handles station travel logic for this engine.
*
* <p>If the associated station is currently traveling, this method advances
* its movement using the value returned by {@link #getTravelSpeed()}.</p>
*
* <p>Subclasses may override this method to add additional server-side behavior.
* When doing so, {@code super.serverTick(...)} should be called to preserve
* the default travel logic.</p>
*
* <p>This method is invoked once per server tick.</p>
*
* @param level the server level the block entity exists in
* @param time the current tick time offset used for scheduling
* @param state the current block state
* @param pos the world position of the block entity
*/
@Override
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
double speed = getTravelSpeed();
if (data != null) {
if (data.isTraveling()) {
data.travel(speed);
}
}
}
@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);
}
}
isInitialized = true;
}
}

View File

@@ -0,0 +1,32 @@
package net.xevianlight.aphelion.block.custom.base;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.xevianlight.aphelion.core.saveddata.types.PartitionData;
import net.xevianlight.aphelion.util.Constants;
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);
@Override
public double getTravelSpeed() {
return SPEED;
}
protected StationRocketEngineBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState blockState) {
// TODO change type to ModBlockEntities.STATION_ROCKET_ENGINE_BLOCK_ENTITY.get()
super(type, pos, blockState);
}
@Override
public void serverTick(ServerLevel level, long time, BlockState state, BlockPos pos) {
super.serverTick(level, time, state, pos); // IMPORTANT!!!
}
}

View File

@@ -9,41 +9,74 @@ 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>
*
* @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>
*
* @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

@@ -21,6 +21,9 @@ 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.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;
@@ -38,6 +41,8 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
private PadInfo padBounds; private PadInfo padBounds;
RocketEntity lastRocket; RocketEntity lastRocket;
private @Nullable PartitionData data;
public @Nullable PadInfo getPadBounds() { public @Nullable PadInfo getPadBounds() {
return padBounds; return padBounds;
} }
@@ -60,9 +65,19 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
return dx * dy * dz; return dx * dy * dz;
} }
public BlockPos getCenter() {
int centerX = (min.getX() + max.getX()) / 2;
int centerZ = (min.getZ() + max.getZ()) / 2;
// bottom Y level
int y = min.getY();
return new BlockPos(centerX, y, centerZ);
}
} }
private final Block TOWER_BLOCK = ModBlocks.BLOCK_STEEL.get(); private static final Block TOWER_BLOCK = ModBlocks.BLOCK_STEEL.get();
public BlockPos towerBasePos; public BlockPos towerBasePos;
private boolean connected(BlockState state, Direction dir) { private boolean connected(BlockState state, Direction dir) {
@@ -318,23 +333,44 @@ public class RocketAssemblerBlockEntity extends BlockEntity implements TickableB
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) { private static boolean isTower(BlockState s) {
return s.is(ModBlocks.BLOCK_STEEL); return s.is(TOWER_BLOCK);
} }
@Override @Override

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;
@@ -37,6 +41,21 @@ public class RocketAssemblerBlockEntityRenderer implements BlockEntityRenderer<R
).inflate(0.5); ).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;
@@ -53,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

@@ -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,8 +43,8 @@ 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("");

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,8 +67,8 @@ 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");

View File

@@ -11,7 +11,6 @@ import net.minecraft.commands.arguments.*;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument; import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.UUIDUtil;
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.network.chat.*; import net.minecraft.network.chat.*;
@@ -26,10 +25,8 @@ 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.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.Collection;
@@ -47,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();
@@ -85,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);
@@ -110,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();
@@ -148,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);
@@ -195,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);
@@ -217,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;
@@ -255,8 +252,8 @@ public class AphelionCommand {
.then(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 px = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x()); int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
int pz = 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();
@@ -277,8 +274,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 px = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x()); int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
int pz = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").z()); int pz = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
ServerLevel level = context.getSource().getLevel(); ServerLevel level = context.getSource().getLevel();
PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz); PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz);
@@ -306,8 +303,8 @@ public class AphelionCommand {
.then(Commands.argument("pos", ColumnPosArgument.columnPos()) .then(Commands.argument("pos", ColumnPosArgument.columnPos())
.then(Commands.argument("player", GameProfileArgument.gameProfile()) .then(Commands.argument("player", GameProfileArgument.gameProfile())
.executes(context -> { .executes(context -> {
int px = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").x()); int px = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").x());
int pz = SpacePartitionHelper.get(ColumnPosArgument.getColumnPos(context, "pos").z()); int pz = SpacePartition.get(ColumnPosArgument.getColumnPos(context, "pos").z());
ServerLevel level = context.getSource().getLevel(); ServerLevel level = context.getSource().getLevel();
PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz); PartitionData data = SpacePartitionSavedData.get(level).getData(px, pz);

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

@@ -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,10 @@ 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")) { if (e.hasUUID("Owner")) {
@@ -102,8 +104,8 @@ 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.getTripDistanceAU());
if (pd.getOwner() != null) { if (pd.getOwner() != null) {
e.putUUID("Owner", pd.getOwner()); e.putUUID("Owner", pd.getOwner());
@@ -158,15 +160,23 @@ public class SpacePartitionSavedData extends SavedData {
} }
/** /**
* Gets the mutable PartitionData object stored at px, pz * Returns the {@link PartitionData} stored at the given partition indices.
* @param px *
* @param pz * <p>The returned {@code PartitionData} is mutable. Any modifications to the returned
* @return * 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 @Nullable PartitionData getData(int px, int pz) { 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);
@@ -175,6 +185,61 @@ 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;

View File

@@ -3,17 +3,13 @@ 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.BlockPos;
import net.minecraft.core.UUIDUtil; import net.minecraft.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
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.util.BigCodec; import net.xevianlight.aphelion.util.BigCodec;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.*;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
public class PartitionData { public class PartitionData {
public static final int MAX_PADS = 64; public static final int MAX_PADS = 64;
@@ -22,32 +18,37 @@ public class PartitionData {
@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 boolean generated;
private UUID owner; private UUID owner;
private List<BlockPos> landingPadControllers; private List<BlockPos> landingPadControllers;
private List<BlockPos> engines;
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.generated = false;
this.owner = null; this.owner = null;
this.landingPadControllers = List.of(); this.landingPadControllers = List.of();
this.engines = 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.generated = other.generated;
this.owner = other.owner; this.owner = other.owner;
this.landingPadControllers = other.landingPadControllers; this.landingPadControllers = other.landingPadControllers;
this.engines = other.engines;
} }
public static final StreamCodec<ByteBuf, PartitionData> STREAM_CODEC = public static final StreamCodec<ByteBuf, PartitionData> STREAM_CODEC =
@@ -64,10 +65,10 @@ public class PartitionData {
// doubles -> DOUBLE codec // doubles -> DOUBLE codec
ByteBufCodecs.DOUBLE, ByteBufCodecs.DOUBLE,
PartitionData::getDistanceTraveled, PartitionData::getDistanceTraveledAU,
ByteBufCodecs.DOUBLE, ByteBufCodecs.DOUBLE,
PartitionData::getDistanceToDest, PartitionData::getTripDistanceAU,
ByteBufCodecs.optional(UUIDUtil.STREAM_CODEC), ByteBufCodecs.optional(UUIDUtil.STREAM_CODEC),
d -> Optional.ofNullable(d.getOwner()), d -> Optional.ofNullable(d.getOwner()),
@@ -78,15 +79,19 @@ public class PartitionData {
BLOCKPOS_LIST_CODEC, BLOCKPOS_LIST_CODEC,
PartitionData::getLandingPadControllers, PartitionData::getLandingPadControllers,
(orbitOpt, destOpt, traveling, distTraveled, distToDest, ownerOpt, generated, controllers) -> { 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.owner = ownerOpt.orElse(null);
data.generated = generated; data.generated = generated;
data.landingPadControllers = controllers; data.landingPadControllers = controllers;
data.engines = engines;
return data; return data;
} }
); );
@@ -115,24 +120,35 @@ 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 getTripDistanceAU() {
return distanceToDest; return tripDistanceAU;
} }
public void setDistanceToDest(double distanceToDest) { public void setTripDistanceAU(double tripDistanceAU) {
this.distanceToDest = 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)
*/
public void travel(double distance) { public void travel(double distance) {
distanceTraveled = Math.min( distanceTraveled + distance, distanceToDest); distanceTraveledAU = Math.min(distanceTraveledAU + distance, tripDistanceAU);
} }
public boolean isGenerated() { public boolean isGenerated() {
@@ -151,14 +167,40 @@ public class PartitionData {
this.owner = 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() { public List<BlockPos> getLandingPadControllers() {
return landingPadControllers; return new ArrayList<>(landingPadControllers);
} }
public void setLandingPadControllers(List<BlockPos> landingPadControllers) { public void setLandingPadControllers(List<BlockPos> landingPadControllers) {
this.landingPadControllers = 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) { public boolean addLandingPadController(BlockPos pos) {
if (!landingPadControllers.contains(pos)) { if (!landingPadControllers.contains(pos)) {
landingPadControllers.add(pos); landingPadControllers.add(pos);
@@ -167,10 +209,40 @@ public class PartitionData {
return false; 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) { public boolean removeLandingPadController(BlockPos pos) {
return landingPadControllers.remove(pos); return landingPadControllers.remove(pos);
} }
public List<BlockPos> getEngines() {
return engines;
}
public void setEngines(List<BlockPos> engines) {
this.engines = engines;
}
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
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) return true; if (this == obj) return true;
@@ -181,8 +253,8 @@ 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 && this.generated == that.generated
&& Objects.equals(this.owner, that.owner); && Objects.equals(this.owner, that.owner);
} }

View File

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

View File

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