Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- Bug fix: TARDIS does not take Valkyrien Skies ships into account when computing travel distance.
- Bug fix: TARDIS does not automatically try to land on ships.
- Bug fix: Flickering when spectating TARDIS exterior on a Valkyrien Skies ship.
- Bug fix: Grown Tardis Item crashes the game if Valkyrien Skies is installed.
- Bug fix: Immersive Portals portal is not rotated correctly when door is on a Valkyrien Skies ship.

- Bug fix: Fixes Console Textures having left over prefabs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ private static int createTardis(CommandContext<CommandSourceStack> context) thro

context.getSource().sendSystemMessage(Component.translatable(ModMessages.CMD_CREATE_TARDIS_IN_PROGRESS, tardisId));

if (TardisHelper.createTardis(pos, level, generatedLevelKey, shellTheme, desktopTheme, Direction.NORTH, true)) {
context.getSource().sendSystemMessage(Component.translatable(ModMessages.CMD_CREATE_TARDIS_SUCCESS, tardisId));
}
TardisHelper.createTardis(
pos, level, generatedLevelKey, shellTheme, desktopTheme, Direction.NORTH, true,
() -> context.getSource().sendSystemMessage(Component.translatable(ModMessages.CMD_CREATE_TARDIS_SUCCESS, tardisId)), () -> {}
);

return Command.SINGLE_SUCCESS;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package whocraft.tardis_refined.common.blockentity.shell;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.resources.ResourceKey;
Expand All @@ -23,31 +26,42 @@
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockSetType;
import whocraft.tardis_refined.TardisRefined;
import whocraft.tardis_refined.api.event.ShellChangeSources;
import whocraft.tardis_refined.common.block.shell.ShellBaseBlock;
import whocraft.tardis_refined.common.capability.tardis.TardisLevelOperator;
import whocraft.tardis_refined.common.capability.tardis.upgrades.UpgradeHandler;
import whocraft.tardis_refined.common.dimension.DimensionHandler;
import whocraft.tardis_refined.common.tardis.TardisNavLocation;
import whocraft.tardis_refined.common.tardis.manager.AestheticHandler;
import whocraft.tardis_refined.common.tardis.manager.TardisExteriorManager;
import whocraft.tardis_refined.common.tardis.manager.TardisInteriorManager;
import whocraft.tardis_refined.common.tardis.manager.TardisPilotingManager;
import whocraft.tardis_refined.common.tardis.themes.DesktopTheme;
import whocraft.tardis_refined.common.util.DimensionUtil;
import whocraft.tardis_refined.common.util.PlayerUtil;
import whocraft.tardis_refined.compat.ModCompatChecker;
import whocraft.tardis_refined.compat.portals.ImmersivePortals;
import whocraft.tardis_refined.constants.ModMessages;
import whocraft.tardis_refined.constants.NbtConstants;
import whocraft.tardis_refined.patterns.ShellPatterns;
import whocraft.tardis_refined.registry.TRUpgrades;

import java.util.Optional;
import java.util.OptionalLong;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class ShellBaseBlockEntity extends BlockEntity implements ExteriorShell, BlockEntityTicker<ShellBaseBlockEntity> {

private static final String SETUP_DATA = "setup_data";

public AnimationState liveliness = new AnimationState();
protected ResourceKey<Level> TARDIS_ID;
private boolean hasPotentialToBeRemoved = false;
private boolean placedByOtherMod = false; // We don't serialize this by design, because other mods might still create duplicates.

private SetupState setupData = null;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been a while since I've been in here. Interesting use of the record here. Does this need to be serialized in any way?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just felt that a record was a nice and compact way to keep all parameters in one place.
I did consider serialising it as well, but thought it was unnecessary considering it will only exist for a single game tick, especially since it's only used when Valkyrien Skies is installed. Also, the Grown TARDIS item is a bit of a creative-mode quality of life item, it even has infinite uses in survival mode (is this a bug?).

The main potential concern I could see is that the shell won't re-trigger the setup on it's own when right-clicked (unlike the root shell), so if the server shuts down after using the grown TARDIS item before the next game tick (very unlikely) it will create an unbreakable (even in creative mode, although it can removed with /setblock) and unusable TARDIS shell without an interior.

Serialising it would solve this, but another solution could be to just have the shell delete itself if it doesn't have a valid TARDIS_ID (which it probably should do anyway in my opinion). Let me know which approach you prefer, I'd probably serialise it if you want to future proof things for a potential survival-mode-friendly way to create a TARDIS directly without the root shell.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, on second thought I decided it would probably be best to serialise it to future-proof the mod.
This also makes it possible to create a TARDIS manually by just placing the shell block with a command, which could be useful.

private OptionalLong setupTick = OptionalLong.empty(); // This is just to prevent a ConcurrentModicationException, should not be serialized.

public ShellBaseBlockEntity(BlockEntityType<?> blockEntityType, BlockPos blockPos, BlockState blockState) {
super(blockEntityType, blockPos, blockState);
}
Expand Down Expand Up @@ -76,6 +90,11 @@ public void load(CompoundTag pTag) {
if (pTag.contains(NbtConstants.TARDIS_ID))
this.TARDIS_ID = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(pTag.getString(NbtConstants.TARDIS_ID)));
updateCurrentLocation();
if (pTag.contains(SETUP_DATA)) {
SetupState.CODEC.parse(NbtOps.INSTANCE, pTag.get(SETUP_DATA)).result().ifPresent(setupData -> {
this.setupData = setupData;
});
}
}

@Override
Expand Down Expand Up @@ -103,14 +122,19 @@ public CompoundTag getUpdateTag() {

@Override
protected void saveAdditional(CompoundTag pTag) {
if (setupData != null) {
pTag.put(SETUP_DATA, SetupState.CODEC.encodeStart(NbtOps.INSTANCE, setupData).result().orElseThrow());
}
super.saveAdditional(pTag);
if (this.TARDIS_ID == null) {
TardisRefined.LOGGER.error("Error in saveAdditional: null Tardis ID (Invalid block or not terraformed yet?) [" + this.getBlockPos().toShortString() + "]");
if (setupData == null) {
TardisRefined.LOGGER.error("Error in saveAdditional: null Tardis ID (Invalid block or not terraformed yet?) [" + this.getBlockPos().toShortString() + "]");
}
return;
}

super.saveAdditional(pTag);
if (this.TARDIS_ID != null)
pTag.putString(NbtConstants.TARDIS_ID, TARDIS_ID.location().toString());

pTag.putString(NbtConstants.TARDIS_ID, TARDIS_ID.location().toString());
}

@Override
Expand All @@ -125,6 +149,65 @@ public boolean shouldSetup() {
return false;
}

private void setUpTardis(
BlockState blockState, Level level, BlockPos blockPos,
ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme, DesktopTheme desktopTheme, boolean openEye,
Runnable onSuccess, Runnable onFail
) {
if (shouldSetup() && level instanceof ServerLevel serverLevel) {

AtomicBoolean generated = new AtomicBoolean(false);

//Set the shell with this level
setTardisId(generatedLevelKey);

//Create the Level on demand which will create our capability
ServerLevel interior = DimensionHandler.getOrCreateInterior(serverLevel, getTardisId().location());

TardisLevelOperator.get(interior).ifPresent(tardisLevelOperator -> {
TardisInteriorManager intManager = tardisLevelOperator.getInteriorManager();
TardisExteriorManager extManager = tardisLevelOperator.getExteriorManager();
TardisPilotingManager pilotManager = tardisLevelOperator.getPilotingManager();
if (!tardisLevelOperator.hasInitiallyGenerated()) {
intManager.generateDesktop(desktopTheme);
tardisLevelOperator.getProgressionManager().addDiscoveredLevel(serverLevel.dimension());
Direction direction = blockState.getValue(ShellBaseBlock.FACING).getOpposite();
TardisNavLocation navLocation = new TardisNavLocation(blockPos, direction, serverLevel);
pilotManager.setCurrentLocation(navLocation);
pilotManager.setTargetLocation(navLocation);
pilotManager.setFuel(pilotManager.getMaximumFuel());
tardisLevelOperator.setInitiallyGenerated(true);
tardisLevelOperator.setTardisState(TardisLevelOperator.STATE_EYE_OF_HARMONY);
intManager.openTheEye(openEye);
serverLevel.setBlock(blockPos, blockState.setValue(ShellBaseBlock.OPEN, true), Block.UPDATE_ALL);
generated.set(true);
tardisLevelOperator.setShellTheme(shellTheme, ShellPatterns.getPatternsForTheme(shellTheme).get(0).id(), ShellChangeSources.ROOT_TO_TARDIS);
tardisLevelOperator.setOrUpdateExteriorBlock(navLocation, Optional.of(blockState), false, ShellChangeSources.ROOT_TO_TARDIS);
}
});

if (generated.get()) {
onSuccess.run();
} else {
onFail.run();
}
}
}

public void setUpTardisOnNextTickIfNecessary(
ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme, DesktopTheme desktopTheme, boolean openEye,
Runnable onSuccess, Runnable onFail
) {
if (ModCompatChecker.valkyrienSkies()) {
setupData = new SetupState(generatedLevelKey, shellTheme, desktopTheme, openEye, onSuccess, onFail);
} else {
setUpTardis(
getBlockState(), getLevel(), getBlockPos(), generatedLevelKey, shellTheme, desktopTheme, openEye,
onSuccess, onFail
);
}
}

@Override
public void onAttemptEnter(BlockState blockState, Level level, BlockPos externalShellPos, Entity entity) {
if (!entity.level().isClientSide() && level instanceof ServerLevel serverLevel) {
Expand Down Expand Up @@ -164,6 +247,20 @@ public void onAttemptEnter(BlockState blockState, Level level, BlockPos external
@Override
public void tick(Level level, BlockPos blockPos, BlockState blockState, ShellBaseBlockEntity blockEntity) {
if (!level.isClientSide) {
if (setupData != null) {
if (setupTick.isEmpty() || setupTick.getAsLong() != level.getGameTime()) {
RootedShellBlockEntity.setUpOnNextTick = true;
setupTick = OptionalLong.of(level.getGameTime()+1);
return;
}
setUpTardis(
blockState, level, blockPos, setupData.generatedLevelKey, setupData.shellTheme, setupData.desktopTheme,
setupData.openEye, setupData.onSuccess, setupData.onFail
);
setupData = null;
setupTick = OptionalLong.empty();
}

ResourceKey<Level> tardisId = getTardisId();
if (tardisId == null) return;
ServerLevel tardisLevel = DimensionUtil.getLevel(tardisId);
Expand Down Expand Up @@ -264,4 +361,26 @@ public boolean isInvalidTardis(TardisLevelOperator tardisLevelOperator) {

return hasPotentialToBeRemoved && !myPosition.equals(currentLocation) && !myPosition.equals(wantedDestination);
}

public record SetupState(
ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme, DesktopTheme desktopTheme, boolean openEye,
Runnable onSuccess, Runnable onFail // We can't serialize onSuccess and onFail in a good way.
) {

public SetupState(
ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme,
DesktopTheme desktopTheme, boolean openEye
) {
this(generatedLevelKey, shellTheme, desktopTheme, openEye, () -> {}, () -> {});
}

public static final Codec<SetupState> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
Level.RESOURCE_KEY_CODEC.fieldOf("interior_dimension").forGetter(SetupState::generatedLevelKey),
ResourceLocation.CODEC.fieldOf("shell_theme").forGetter(SetupState::shellTheme),
DesktopTheme.getCodec().fieldOf("desktop_theme").forGetter(SetupState::desktopTheme),
Codec.BOOL.fieldOf("open_eye").forGetter(SetupState::openEye)
).apply(instance, SetupState::new)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ public InteractionResult useOn(UseOnContext context) {
MutableComponent tardisId = TardisHelper.createTardisIdComponent(generatedLevelKey.location());
server.sendSystemMessage(Component.translatable(ModMessages.CMD_CREATE_TARDIS_IN_PROGRESS, tardisId));

if (TardisHelper.createTardis(pos, serverLevel, generatedLevelKey, shellTheme, desktopTheme, playerFacing, false)) {
server.sendSystemMessage(Component.translatable(ModMessages.CMD_CREATE_TARDIS_SUCCESS, tardisId));
}
TardisHelper.createTardis(
pos, serverLevel, generatedLevelKey, shellTheme, desktopTheme, playerFacing, false,
() -> server.sendSystemMessage(Component.translatable(ModMessages.CMD_CREATE_TARDIS_SUCCESS, tardisId)), () -> {}
);

return super.useOn(context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,21 @@
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import whocraft.tardis_refined.api.event.ShellChangeSources;
import whocraft.tardis_refined.api.event.TardisCommonEvents;
import whocraft.tardis_refined.common.block.shell.GlobalShellBlock;
import whocraft.tardis_refined.common.block.shell.ShellBaseBlock;
import whocraft.tardis_refined.common.blockentity.shell.GlobalShellBlockEntity;
import whocraft.tardis_refined.common.capability.tardis.TardisLevelOperator;
import whocraft.tardis_refined.common.dimension.DimensionHandler;
import whocraft.tardis_refined.common.dimension.TardisTeleportData;
import whocraft.tardis_refined.common.tardis.TardisArchitectureHandler;
import whocraft.tardis_refined.common.tardis.TardisNavLocation;
import whocraft.tardis_refined.common.tardis.manager.TardisExteriorManager;
import whocraft.tardis_refined.common.tardis.manager.TardisInteriorManager;
import whocraft.tardis_refined.common.tardis.manager.TardisPilotingManager;
import whocraft.tardis_refined.common.tardis.themes.DesktopTheme;
import whocraft.tardis_refined.compat.ModCompatChecker;
import whocraft.tardis_refined.compat.valkyrienskies.VSHelper;
import whocraft.tardis_refined.mixin.EndDragonFightAccessor;
import whocraft.tardis_refined.patterns.ShellPatterns;
import whocraft.tardis_refined.registry.TRBlockRegistry;
import whocraft.tardis_refined.registry.TRDimensionTypes;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

import static whocraft.tardis_refined.common.block.shell.ShellBaseBlock.LOCKED;
import static whocraft.tardis_refined.constants.TardisDimensionConstants.ARS_TREE_CORNER_A;
Expand Down Expand Up @@ -89,50 +80,28 @@ public static boolean isInArsArea(BlockPos blockPos) {
return blockPos.getX() >= minX && blockPos.getX() <= maxX && blockPos.getY() >= minY && blockPos.getY() <= maxY && blockPos.getZ() >= minZ && blockPos.getZ() <= maxZ;
}

public static boolean createTardis(BlockPos blockPos, ServerLevel serverLevel, ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme, DesktopTheme desktopTheme, Direction facing, Boolean openEye) {
public static boolean createTardis(
BlockPos blockPos, ServerLevel serverLevel, ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme,
DesktopTheme desktopTheme, Direction facing, Boolean openEye
) {
createTardis(blockPos, serverLevel, generatedLevelKey, shellTheme, desktopTheme, facing, openEye, () -> {}, () -> {});
return true;
}

AtomicBoolean generated = new AtomicBoolean(false);
// Warning, onSuccess and onFail are not always guaranteed to run. Don't use them for anything important.
public static void createTardis(
BlockPos blockPos, ServerLevel serverLevel, ResourceKey<Level> generatedLevelKey, ResourceLocation shellTheme,
DesktopTheme desktopTheme, Direction facing, boolean openEye, Runnable onSuccess, Runnable onFail
) {

//Set global shell block
BlockState targetBlockState = TRBlockRegistry.GLOBAL_SHELL_BLOCK.get().defaultBlockState().setValue(GlobalShellBlock.FACING, facing).setValue(GlobalShellBlock.REGEN, false).setValue(LOCKED, false).setValue(GlobalShellBlock.WATERLOGGED, serverLevel.getBlockState(blockPos).getFluidState().getType() == Fluids.WATER);

serverLevel.setBlock(blockPos, targetBlockState, Block.UPDATE_ALL);

if (serverLevel.getBlockEntity(blockPos) instanceof GlobalShellBlockEntity shellBaseBlockEntity) {
if (shellBaseBlockEntity.shouldSetup()) {

//Set the shell with this level
shellBaseBlockEntity.setTardisId(generatedLevelKey);

//Create the Level on demand which will create our capability
ServerLevel interior = DimensionHandler.getOrCreateInterior(serverLevel, shellBaseBlockEntity.getTardisId().location());

TardisLevelOperator.get(interior).ifPresent(tardisLevelOperator -> {
TardisInteriorManager intManager = tardisLevelOperator.getInteriorManager();
TardisExteriorManager extManager = tardisLevelOperator.getExteriorManager();
TardisPilotingManager pilotManager = tardisLevelOperator.getPilotingManager();
if (!tardisLevelOperator.hasInitiallyGenerated()) {
intManager.generateDesktop(desktopTheme);
tardisLevelOperator.getProgressionManager().addDiscoveredLevel(serverLevel.dimension());
Direction direction = targetBlockState.getValue(ShellBaseBlock.FACING).getOpposite();
TardisNavLocation navLocation = new TardisNavLocation(blockPos, direction, serverLevel);
pilotManager.setCurrentLocation(navLocation);
pilotManager.setTargetLocation(navLocation);
pilotManager.setFuel(pilotManager.getMaximumFuel());
tardisLevelOperator.setInitiallyGenerated(true);
tardisLevelOperator.setTardisState(TardisLevelOperator.STATE_EYE_OF_HARMONY);
intManager.openTheEye(openEye);
serverLevel.setBlock(blockPos, targetBlockState.setValue(ShellBaseBlock.OPEN, true), Block.UPDATE_ALL);
generated.set(true);
tardisLevelOperator.setShellTheme(shellTheme, ShellPatterns.getPatternsForTheme(shellTheme).get(0).id(), ShellChangeSources.ROOT_TO_TARDIS);
tardisLevelOperator.setOrUpdateExteriorBlock(navLocation, Optional.of(targetBlockState), false, ShellChangeSources.ROOT_TO_TARDIS);
}
});

return generated.get();
}
shellBaseBlockEntity.setUpTardisOnNextTickIfNecessary(generatedLevelKey, shellTheme, desktopTheme, openEye, onSuccess, onFail);
}
return false;

}

Expand Down