TechReborn/src/main/java/techreborn/blockentity/cable/CableBlockEntity.java
2022-05-18 20:20:44 +01:00

304 lines
9.2 KiB
Java

/*
* This file is part of TechReborn, licensed under the MIT License (MIT).
*
* Copyright (c) 2020 TechReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package techreborn.blockentity.cable;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.text.Text;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import reborncore.api.IListInfoProvider;
import reborncore.api.IToolDrop;
import reborncore.common.network.ClientBoundPackets;
import reborncore.common.network.NetworkManager;
import reborncore.common.powerSystem.PowerSystem;
import reborncore.common.util.StringUtils;
import team.reborn.energy.api.EnergyStorage;
import team.reborn.energy.api.base.SimpleSidedEnergyContainer;
import techreborn.blocks.cable.CableBlock;
import techreborn.init.TRBlockEntities;
import techreborn.init.TRContent;
import java.util.ArrayList;
import java.util.List;
public class CableBlockEntity extends BlockEntity
implements BlockEntityTicker<CableBlockEntity>, IListInfoProvider, IToolDrop {
// Can't use SimpleEnergyStorage because the cable type is not available when the BE is constructed.
final SimpleSidedEnergyContainer energyContainer = new SimpleSidedEnergyContainer() {
@Override
public long getCapacity() {
return getCableType().transferRate * 4L;
}
@Override
public long getMaxInsert(Direction side) {
if (allowTransfer(side)) return getCableType().transferRate;
else return 0;
}
@Override
public long getMaxExtract(Direction side) {
if (allowTransfer(side)) return getCableType().transferRate;
else return 0;
}
};
private TRContent.Cables cableType = null;
private BlockState cover = null;
long lastTick = 0;
// null means that it needs to be re-queried
List<CableTarget> targets = null;
/**
* Adjacent caches, used to quickly query adjacent cable block entities.
*/
@SuppressWarnings("unchecked")
private final BlockApiCache<EnergyStorage, Direction>[] adjacentCaches = new BlockApiCache[6];
/**
* Bitmask to prevent input or output into/from the cable when the cable already transferred in the target direction.
* This prevents double transfer rates, and back and forth between two cables.
*/
int blockedSides = 0;
/**
* This is only used during the cable tick, whereas {@link #blockedSides} is used between ticks.
*/
boolean ioBlocked = false;
public CableBlockEntity(BlockPos pos, BlockState state) {
super(TRBlockEntities.CABLE, pos, state);
}
public CableBlockEntity(BlockPos pos, BlockState state, TRContent.Cables type) {
super(TRBlockEntities.CABLE, pos, state);
this.cableType = type;
}
TRContent.Cables getCableType() {
if (cableType != null) {
return cableType;
}
if (world == null) {
return TRContent.Cables.COPPER;
}
Block block = world.getBlockState(pos).getBlock();
if (block instanceof CableBlock) {
return ((CableBlock) block).type;
}
//Something has gone wrong if this happens
return TRContent.Cables.COPPER;
}
private boolean allowTransfer(Direction side) {
return !ioBlocked && (blockedSides & (1 << side.ordinal())) == 0;
}
public EnergyStorage getSideEnergyStorage(@Nullable Direction side) {
return energyContainer.getSideStorage(side);
}
public BlockState getCover() {
return cover;
}
public void setCover(BlockState cover) {
this.cover = cover;
if (world != null && !world.isClient) {
NetworkManager.sendToTracking(ClientBoundPackets.createCustomDescriptionPacket(this), this);
}
}
public long getEnergy() {
return energyContainer.amount;
}
public void setEnergy(long energy) {
energyContainer.amount = energy;
}
private BlockApiCache<EnergyStorage, Direction> getAdjacentCache(Direction direction) {
if (adjacentCaches[direction.getId()] == null) {
adjacentCaches[direction.getId()] = BlockApiCache.create(EnergyStorage.SIDED, (ServerWorld) world, pos.offset(direction));
}
return adjacentCaches[direction.getId()];
}
@Nullable
BlockEntity getAdjacentBlockEntity(Direction direction) {
return getAdjacentCache(direction).getBlockEntity();
}
void appendTargets(List<OfferedEnergyStorage> targetStorages) {
ServerWorld serverWorld = (ServerWorld) world;
if (serverWorld == null) {
return;
}
// Update our targets if necessary.
if (targets == null) {
BlockState newBlockState = getCachedState();
targets = new ArrayList<>();
for (Direction direction : Direction.values()) {
boolean foundSomething = false;
BlockApiCache<EnergyStorage, Direction> adjCache = getAdjacentCache(direction);
if (adjCache.getBlockEntity() instanceof CableBlockEntity adjCable) {
if (adjCable.getCableType().transferRate == getCableType().transferRate) {
// Make sure cables are not used as regular targets.
foundSomething = true;
}
} else if (adjCache.find(direction.getOpposite()) != null) {
foundSomething = true;
targets.add(new CableTarget(direction, adjCache));
}
newBlockState = newBlockState.with(CableBlock.PROPERTY_MAP.get(direction), foundSomething);
}
serverWorld.setBlockState(getPos(), newBlockState);
}
// Fill the list.
for (CableTarget target : targets) {
EnergyStorage storage = target.find();
if (storage == null) {
// Schedule a rebuild next tick.
// This is just a reference change, the iterator remains valid.
targets = null;
} else {
targetStorages.add(new OfferedEnergyStorage(this, target.directionTo, storage));
}
}
// Reset blocked sides.
blockedSides = 0;
}
// BlockEntity
@Override
public NbtCompound toInitialChunkDataNbt() {
return createNbt();
}
@Override
public BlockEntityUpdateS2CPacket toUpdatePacket() {
NbtCompound nbtTag = new NbtCompound();
writeNbt(nbtTag);
return BlockEntityUpdateS2CPacket.create(this);
}
@Override
public void readNbt(NbtCompound compound) {
super.readNbt(compound);
if (compound.contains("energy")) {
energyContainer.amount = compound.getLong("energy");
}
if (compound.contains("cover")) {
cover = NbtHelper.toBlockState(compound.getCompound("cover"));
} else {
cover = null;
}
}
@Override
public void writeNbt(NbtCompound compound) {
super.writeNbt(compound);
compound.putLong("energy", energyContainer.amount);
if (cover != null) {
compound.put("cover", NbtHelper.fromBlockState(cover));
}
}
public void neighborUpdate() {
targets = null;
}
// BlockEntityTicker
@Override
public void tick(World world, BlockPos pos, BlockState state, CableBlockEntity blockEntity2) {
if (world == null || world.isClient) {
return;
}
CableTickManager.handleCableTick(this);
}
// IListInfoProvider
@Override
public void addInfo(List<Text> info, boolean isReal, boolean hasData) {
info.add(
Text.translatable("techreborn.tooltip.transferRate")
.formatted(Formatting.GRAY)
.append(": ")
.append(PowerSystem.getLocalizedPower(getCableType().transferRate))
.formatted(Formatting.GOLD)
.append("/t")
);
info.add(
Text.translatable("techreborn.tooltip.tier")
.formatted(Formatting.GRAY)
.append(": ")
.append(
Text.literal(StringUtils.toFirstCapitalAllLowercase(getCableType().tier.toString()))
.formatted(Formatting.GOLD)
)
);
if (!getCableType().canKill) {
info.add(Text.translatable("techreborn.tooltip.cable.can_cover").formatted(Formatting.GRAY));
}
}
// IToolDrop
@Override
public ItemStack getToolDrop(PlayerEntity playerIn) {
return new ItemStack(getCableType().block);
}
private record CableTarget(Direction directionTo, BlockApiCache<EnergyStorage, Direction> cache) {
@Nullable
EnergyStorage find() {
return cache.find(directionTo.getOpposite());
}
}
}