mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-23 15:36:54 +00:00
Attempt at splitting up pocket computer logic
Oh, I hate the pocket computer code so much. Minecraft was really not designed to attach this sort of behaviour to computers. This commit is an attempt of cleaning this up[^1]. Firstly, we move the the pocket computer state (upgrades, light) out of PocketServerComputer and into a new PocketBrain class. This now acts as the sole source-of-truth, with all state being synced back to the original item stack on the entity tick. This also adds a new PocketHolder interface, which generalises over the various types that can hold a pocket computer (players and item entities right now, possibly lecterns in the future). [^1]: I'd say simplifying, but this would be a lie.
This commit is contained in:
parent
4dd0735066
commit
ed0b156e05
@ -27,7 +27,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
||||
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
||||
clientId = computer.getInstanceUUID();
|
||||
state = computer.getState();
|
||||
lightState = computer.getLight();
|
||||
lightState = computer.getBrain().getLight();
|
||||
terminal = sendTerminal ? computer.getTerminalState() : null;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.pocket.core;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds additional state for a pocket computer. This includes pocket computer upgrade,
|
||||
* {@linkplain IPocketAccess#getLight() light colour} and {@linkplain IPocketAccess#getColour() colour}.
|
||||
* <p>
|
||||
* This state is read when the brain is created, and written back to the holding item stack when the holding entity is
|
||||
* ticked (see {@link #updateItem(ItemStack)}).
|
||||
*/
|
||||
public final class PocketBrain implements IPocketAccess {
|
||||
private final PocketServerComputer computer;
|
||||
|
||||
private PocketHolder holder;
|
||||
|
||||
private boolean dirty = false;
|
||||
private @Nullable UpgradeData<IPocketUpgrade> upgrade;
|
||||
private int colour = -1;
|
||||
private int lightColour = -1;
|
||||
|
||||
public PocketBrain(PocketHolder holder, int computerID, @Nullable String label, ComputerFamily family, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
this.computer = new PocketServerComputer(this, holder, computerID, label, family);
|
||||
this.holder = holder;
|
||||
this.upgrade = UpgradeData.copyOf(upgrade);
|
||||
invalidatePeripheral();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the corresponding pocket computer for this brain.
|
||||
*
|
||||
* @return The pocket computer.
|
||||
*/
|
||||
public PocketServerComputer computer() {
|
||||
return computer;
|
||||
}
|
||||
|
||||
PocketHolder holder() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the position and holder for this computer.
|
||||
*
|
||||
* @param newHolder The new holder
|
||||
*/
|
||||
public void updateHolder(PocketHolder newHolder) {
|
||||
computer.setPosition(newHolder.level(), newHolder.blockPos());
|
||||
|
||||
var oldHolder = this.holder;
|
||||
if (holder.equals(newHolder)) return;
|
||||
holder = newHolder;
|
||||
|
||||
// If a new player has picked it up then rebroadcast the terminal to them
|
||||
var oldPlayer = oldHolder instanceof PocketHolder.PlayerHolder p ? p.entity() : null;
|
||||
if (newHolder instanceof PocketHolder.PlayerHolder player && player.entity() != oldPlayer) {
|
||||
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(computer, true), player.entity());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write back properties of the pocket brain to the item.
|
||||
*
|
||||
* @param stack The pocket computer stack to update.
|
||||
* @return Whether the item was changed.
|
||||
*/
|
||||
public boolean updateItem(ItemStack stack) {
|
||||
if (!dirty) return false;
|
||||
this.dirty = false;
|
||||
|
||||
IColouredItem.setColourBasic(stack, colour);
|
||||
PocketComputerItem.setUpgrade(stack, UpgradeData.copyOf(upgrade));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Entity getEntity() {
|
||||
return holder instanceof PocketHolder.EntityHolder entity && holder.isValid(computer) ? entity.entity() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColour() {
|
||||
return colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColour(int colour) {
|
||||
if (this.colour == colour) return;
|
||||
dirty = true;
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLight() {
|
||||
return lightColour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLight(int colour) {
|
||||
if (colour < 0 || colour > 0xFFFFFF) colour = -1;
|
||||
lightColour = colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getUpgradeNBTData() {
|
||||
var upgrade = this.upgrade;
|
||||
return upgrade == null ? new CompoundTag() : upgrade.data();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUpgradeNBTData() {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePeripheral() {
|
||||
var peripheral = upgrade == null ? null : upgrade.upgrade().createPeripheral(this);
|
||||
computer.setPeripheral(ComputerSide.BACK, peripheral);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated(forRemoval = true)
|
||||
public Map<ResourceLocation, IPeripheral> getUpgrades() {
|
||||
var upgrade = this.upgrade;
|
||||
return upgrade == null ? Map.of() : Collections.singletonMap(upgrade.upgrade().getUpgradeID(), computer.getPeripheral(ComputerSide.BACK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
||||
return upgrade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upgrade for this pocket computer, also updating the item stack.
|
||||
* <p>
|
||||
* Note this method is not thread safe - it must be called from the server thread.
|
||||
*
|
||||
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
||||
*/
|
||||
@Override
|
||||
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
this.upgrade = upgrade;
|
||||
dirty = true;
|
||||
invalidatePeripheral();
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.pocket.core;
|
||||
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
|
||||
/**
|
||||
* An object that holds a pocket computer item.
|
||||
*/
|
||||
public sealed interface PocketHolder permits PocketHolder.EntityHolder {
|
||||
/**
|
||||
* The level this holder is in.
|
||||
*
|
||||
* @return The holder's level.
|
||||
*/
|
||||
ServerLevel level();
|
||||
|
||||
/**
|
||||
* The block position of this holder.
|
||||
*
|
||||
* @return The position of this holder.
|
||||
*/
|
||||
BlockPos blockPos();
|
||||
|
||||
/**
|
||||
* Determine if this holder is still valid for a particular computer.
|
||||
*
|
||||
* @param computer The current computer.
|
||||
* @return Whether this holder is valid.
|
||||
*/
|
||||
boolean isValid(ServerComputer computer);
|
||||
|
||||
/**
|
||||
* Mark the pocket computer item as having changed.
|
||||
*/
|
||||
void setChanged();
|
||||
|
||||
/**
|
||||
* An {@link Entity} holding a pocket computer.
|
||||
*/
|
||||
sealed interface EntityHolder extends PocketHolder permits PocketHolder.PlayerHolder, PocketHolder.ItemEntityHolder {
|
||||
/**
|
||||
* Get the entity holding this pocket computer.
|
||||
*
|
||||
* @return The holding entity.
|
||||
*/
|
||||
Entity entity();
|
||||
|
||||
@Override
|
||||
default ServerLevel level() {
|
||||
return (ServerLevel) entity().level();
|
||||
}
|
||||
|
||||
@Override
|
||||
default BlockPos blockPos() {
|
||||
return entity().blockPosition();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A pocket computer in a player's slot.
|
||||
*
|
||||
* @param entity The current player.
|
||||
* @param slot The slot the pocket computer is in.
|
||||
*/
|
||||
record PlayerHolder(ServerPlayer entity, int slot) implements EntityHolder {
|
||||
@Override
|
||||
public boolean isValid(ServerComputer computer) {
|
||||
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, entity().getInventory().getItem(this.slot()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
entity.getInventory().setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A pocket computer in an {@link ItemEntity}.
|
||||
*
|
||||
* @param entity The item entity.
|
||||
*/
|
||||
record ItemEntityHolder(ItemEntity entity) implements EntityHolder {
|
||||
@Override
|
||||
public boolean isValid(ServerComputer computer) {
|
||||
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, this.entity().getItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
entity.setItem(entity.getItem().copy());
|
||||
}
|
||||
}
|
||||
}
|
@ -4,12 +4,6 @@
|
||||
|
||||
package dan200.computercraft.shared.pocket.core;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
@ -18,29 +12,26 @@ import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class PocketServerComputer extends ServerComputer implements IPocketAccess {
|
||||
private @Nullable IPocketUpgrade upgrade;
|
||||
private @Nullable Entity entity;
|
||||
private ItemStack stack = ItemStack.EMPTY;
|
||||
|
||||
private int lightColour = -1;
|
||||
/**
|
||||
* A {@link ServerComputer}-subclass for {@linkplain PocketComputerItem pocket computers}.
|
||||
* <p>
|
||||
* This extends default {@link ServerComputer} behaviour by also syncing pocket computer state to nearby players, and
|
||||
* syncing the terminal to the current player.
|
||||
* <p>
|
||||
* The actual pocket computer state (upgrade, light) is maintained in {@link PocketBrain}. The two classes are tightly
|
||||
* coupled, and maintain a reference to each other.
|
||||
*
|
||||
* @see PocketComputerDataMessage
|
||||
* @see PocketComputerDeletedClientMessage
|
||||
*/
|
||||
public final class PocketServerComputer extends ServerComputer {
|
||||
private final PocketBrain brain;
|
||||
|
||||
// The state the previous tick, used to determine if the state needs to be sent to the client.
|
||||
private int oldLightColour = -1;
|
||||
@ -48,107 +39,13 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
|
||||
private Set<ServerPlayer> tracking = Set.of();
|
||||
|
||||
public PocketServerComputer(ServerLevel world, BlockPos position, int computerID, @Nullable String label, ComputerFamily family) {
|
||||
super(world, position, computerID, label, family, Config.pocketTermWidth, Config.pocketTermHeight);
|
||||
PocketServerComputer(PocketBrain brain, PocketHolder holder, int computerID, @Nullable String label, ComputerFamily family) {
|
||||
super(holder.level(), holder.blockPos(), computerID, label, family, Config.pocketTermWidth, Config.pocketTermHeight);
|
||||
this.brain = brain;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Entity getEntity() {
|
||||
var entity = this.entity;
|
||||
if (entity == null || stack.isEmpty() || !entity.isAlive()) return null;
|
||||
|
||||
if (entity instanceof Player) {
|
||||
var inventory = ((Player) entity).getInventory();
|
||||
return inventory.items.contains(stack) || inventory.offhand.contains(stack) ? entity : null;
|
||||
} else if (entity instanceof LivingEntity living) {
|
||||
return living.getMainHandItem() == stack || living.getOffhandItem() == stack ? entity : null;
|
||||
} else if (entity instanceof ItemEntity itemEntity) {
|
||||
return itemEntity.getItem() == stack ? entity : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColour() {
|
||||
return IColouredItem.getColourBasic(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColour(int colour) {
|
||||
IColouredItem.setColourBasic(stack, colour);
|
||||
updateUpgradeNBTData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLight() {
|
||||
return lightColour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLight(int colour) {
|
||||
if (colour < 0 || colour > 0xFFFFFF) colour = -1;
|
||||
lightColour = colour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getUpgradeNBTData() {
|
||||
return PocketComputerItem.getUpgradeInfo(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUpgradeNBTData() {
|
||||
if (entity instanceof Player player) player.getInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidatePeripheral() {
|
||||
var peripheral = upgrade == null ? null : upgrade.createPeripheral(this);
|
||||
setPeripheral(ComputerSide.BACK, peripheral);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated(forRemoval = true)
|
||||
public Map<ResourceLocation, IPeripheral> getUpgrades() {
|
||||
return upgrade == null ? Map.of() : Collections.singletonMap(upgrade.getUpgradeID(), getPeripheral(ComputerSide.BACK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
||||
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upgrade for this pocket computer, also updating the item stack.
|
||||
* <p>
|
||||
* Note this method is not thread safe - it must be called from the server thread.
|
||||
*
|
||||
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
||||
*/
|
||||
@Override
|
||||
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
synchronized (this) {
|
||||
PocketComputerItem.setUpgrade(stack, upgrade);
|
||||
updateUpgradeNBTData();
|
||||
this.upgrade = upgrade == null ? null : upgrade.upgrade();
|
||||
invalidatePeripheral();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateValues(@Nullable Entity entity, ItemStack stack, @Nullable IPocketUpgrade upgrade) {
|
||||
if (entity != null) setPosition((ServerLevel) entity.level(), entity.blockPosition());
|
||||
|
||||
// If a new entity has picked it up then rebroadcast the terminal to them
|
||||
if (entity != this.entity && entity instanceof ServerPlayer) markTerminalChanged();
|
||||
|
||||
this.entity = entity;
|
||||
this.stack = stack;
|
||||
|
||||
if (this.upgrade != upgrade) {
|
||||
this.upgrade = upgrade;
|
||||
invalidatePeripheral();
|
||||
}
|
||||
public PocketBrain getBrain() {
|
||||
return brain;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -161,9 +58,10 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
|
||||
// And now find any new players, add them to the tracking list, and broadcast state where appropriate.
|
||||
var state = getState();
|
||||
if (oldLightColour != lightColour || oldComputerState != state) {
|
||||
var light = brain.getLight();
|
||||
if (oldLightColour != light || oldComputerState != state) {
|
||||
oldComputerState = state;
|
||||
oldLightColour = lightColour;
|
||||
oldLightColour = light;
|
||||
|
||||
// Broadcast the state to all players
|
||||
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), newTracking);
|
||||
@ -182,9 +80,9 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
protected void onTerminalChanged() {
|
||||
super.onTerminalChanged();
|
||||
|
||||
if (entity instanceof ServerPlayer player && entity.isAlive()) {
|
||||
if (brain.holder() instanceof PocketHolder.PlayerHolder holder && holder.isValid(this)) {
|
||||
// Broadcast the terminal to the current player.
|
||||
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), player);
|
||||
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), holder.entity());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,21 +14,26 @@ import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
||||
import dan200.computercraft.shared.pocket.core.PocketBrain;
|
||||
import dan200.computercraft.shared.pocket.core.PocketHolder;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
@ -72,12 +77,33 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean tick(ItemStack stack, Entity entity, PocketServerComputer computer) {
|
||||
var upgrade = getUpgrade(stack);
|
||||
/**
|
||||
* Tick a pocket computer.
|
||||
*
|
||||
* @param stack The current pocket computer stack.
|
||||
* @param holder The entity holding the pocket item.
|
||||
* @param brain The pocket computer brain.
|
||||
*/
|
||||
private void tick(ItemStack stack, PocketHolder holder, PocketBrain brain) {
|
||||
brain.updateHolder(holder);
|
||||
|
||||
computer.updateValues(entity, stack, upgrade);
|
||||
// Update pocket upgrade
|
||||
var upgrade = brain.getUpgrade();
|
||||
if (upgrade != null) upgrade.upgrade().update(brain, brain.computer().getPeripheral(ComputerSide.BACK));
|
||||
|
||||
var changed = false;
|
||||
if (updateItem(stack, brain)) holder.setChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy properties from the brain back to the item stack.
|
||||
*
|
||||
* @param stack The current pocket computer stack.
|
||||
* @param brain The current pocket brain.
|
||||
* @return Whether the item was changed.
|
||||
*/
|
||||
private boolean updateItem(ItemStack stack, PocketBrain brain) {
|
||||
var changed = brain.updateItem(stack);
|
||||
var computer = brain.computer();
|
||||
|
||||
// Sync ID
|
||||
var id = computer.getID();
|
||||
@ -99,21 +125,20 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
stack.getOrCreateTag().putBoolean(NBT_ON, on);
|
||||
}
|
||||
|
||||
// Update pocket upgrade
|
||||
if (upgrade != null) upgrade.update(computer, computer.getPeripheral(ComputerSide.BACK));
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack stack, Level world, Entity entity, int slotNum, boolean selected) {
|
||||
if (world.isClientSide) return;
|
||||
Container inventory = entity instanceof Player player ? player.getInventory() : null;
|
||||
var computer = createServerComputer((ServerLevel) world, entity, inventory, stack);
|
||||
computer.keepAlive();
|
||||
// This (in vanilla at least) is only called for players. Don't bother to handle other entities.
|
||||
if (world.isClientSide || !(entity instanceof ServerPlayer player)) return;
|
||||
|
||||
var changed = tick(stack, entity, computer);
|
||||
if (changed && inventory != null) inventory.setChanged();
|
||||
// If we're in the inventory, create a computer and keep it alive.
|
||||
var holder = new PocketHolder.PlayerHolder(player, slotNum);
|
||||
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
|
||||
brain.computer().keepAlive();
|
||||
|
||||
tick(stack, holder, brain);
|
||||
}
|
||||
|
||||
@ForgeOverride
|
||||
@ -121,8 +146,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
var level = entity.level();
|
||||
if (level.isClientSide || level.getServer() == null) return false;
|
||||
|
||||
// If we're an item entity, tick an already existing computer (as to update the position), but do not keep the
|
||||
// computer alive.
|
||||
var computer = getServerComputer(level.getServer(), stack);
|
||||
if (computer != null && tick(stack, entity, computer)) entity.setItem(stack.copy());
|
||||
if (computer != null) tick(stack, new PocketHolder.ItemEntityHolder(entity), computer.getBrain());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -130,14 +158,18 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
var stack = player.getItemInHand(hand);
|
||||
if (!world.isClientSide) {
|
||||
var computer = createServerComputer((ServerLevel) world, player, player.getInventory(), stack);
|
||||
var holder = new PocketHolder.PlayerHolder((ServerPlayer) player, InventoryUtil.getHandSlot(player, hand));
|
||||
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
|
||||
var computer = brain.computer();
|
||||
computer.turnOn();
|
||||
|
||||
var stop = false;
|
||||
var upgrade = getUpgrade(stack);
|
||||
if (upgrade != null) {
|
||||
computer.updateValues(player, stack, upgrade);
|
||||
stop = upgrade.onRightClick(world, computer, computer.getPeripheral(ComputerSide.BACK));
|
||||
brain.updateHolder(holder);
|
||||
stop = upgrade.onRightClick(world, brain, computer.getPeripheral(ComputerSide.BACK));
|
||||
// Sync back just in case. We don't need to setChanged, as we'll return the item anyway.
|
||||
updateItem(stack, brain);
|
||||
}
|
||||
|
||||
if (!stop) {
|
||||
@ -187,40 +219,51 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
return ComputerCraftAPI.MOD_ID;
|
||||
}
|
||||
|
||||
public PocketServerComputer createServerComputer(ServerLevel level, Entity entity, @Nullable Container inventory, ItemStack stack) {
|
||||
|
||||
private PocketBrain getOrCreateBrain(ServerLevel level, PocketHolder holder, ItemStack stack) {
|
||||
var registry = ServerContext.get(level.getServer()).registry();
|
||||
var computer = (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
||||
if (computer == null) {
|
||||
var computerID = getComputerID(stack);
|
||||
if (computerID < 0) {
|
||||
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), IDAssigner.COMPUTER);
|
||||
setComputerID(stack, computerID);
|
||||
}
|
||||
|
||||
computer = new PocketServerComputer(level, entity.blockPosition(), getComputerID(stack), getLabel(stack), getFamily());
|
||||
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.putInt(NBT_SESSION, registry.getSessionID());
|
||||
tag.putUUID(NBT_INSTANCE, computer.register());
|
||||
|
||||
var upgrade = getUpgrade(stack);
|
||||
|
||||
computer.updateValues(entity, stack, upgrade);
|
||||
computer.addAPI(new PocketAPI(computer));
|
||||
|
||||
// Only turn on when initially creating the computer, rather than each tick.
|
||||
if (isMarkedOn(stack) && entity instanceof Player) computer.turnOn();
|
||||
|
||||
if (inventory != null) inventory.setChanged();
|
||||
{
|
||||
var computer = getServerComputer(registry, stack);
|
||||
if (computer != null) return computer.getBrain();
|
||||
}
|
||||
|
||||
return computer;
|
||||
var computerID = getComputerID(stack);
|
||||
if (computerID < 0) {
|
||||
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), IDAssigner.COMPUTER);
|
||||
setComputerID(stack, computerID);
|
||||
}
|
||||
|
||||
var brain = new PocketBrain(holder, getComputerID(stack), getLabel(stack), getFamily(), getUpgradeWithData(stack));
|
||||
var computer = brain.computer();
|
||||
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.putInt(NBT_SESSION, registry.getSessionID());
|
||||
tag.putUUID(NBT_INSTANCE, computer.register());
|
||||
|
||||
computer.addAPI(new PocketAPI(brain));
|
||||
|
||||
// Only turn on when initially creating the computer, rather than each tick.
|
||||
if (isMarkedOn(stack) && holder instanceof PocketHolder.PlayerHolder) computer.turnOn();
|
||||
|
||||
updateItem(stack, brain);
|
||||
|
||||
holder.setChanged();
|
||||
|
||||
return brain;
|
||||
}
|
||||
|
||||
public static boolean isServerComputer(ServerComputer computer, ItemStack stack) {
|
||||
return stack.getItem() instanceof PocketComputerItem
|
||||
&& getServerComputer(computer.getLevel().getServer(), stack) == computer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PocketServerComputer getServerComputer(ServerComputerRegistry registry, ItemStack stack) {
|
||||
return (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PocketServerComputer getServerComputer(MinecraftServer server, ItemStack stack) {
|
||||
return (PocketServerComputer) ServerContext.get(server).registry().get(getSessionID(stack), getInstanceID(stack));
|
||||
return getServerComputer(ServerContext.get(server).registry(), stack);
|
||||
}
|
||||
|
||||
// IComputerItem implementation
|
||||
|
@ -8,6 +8,9 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@ -18,6 +21,20 @@ public final class InventoryUtil {
|
||||
private InventoryUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the inventory slot for a given hand.
|
||||
*
|
||||
* @param player The player to get the slot from.
|
||||
* @param hand The hand to get.
|
||||
* @return The current slot.
|
||||
*/
|
||||
public static int getHandSlot(Player player, InteractionHand hand) {
|
||||
return switch (hand) {
|
||||
case MAIN_HAND -> player.getInventory().selected;
|
||||
case OFF_HAND -> Inventory.SLOT_OFFHAND;
|
||||
};
|
||||
}
|
||||
|
||||
public static @Nullable Container getEntityContainer(ServerLevel level, BlockPos pos, Direction side) {
|
||||
var vecStart = new Vec3(
|
||||
pos.getX() + 0.5 + 0.6 * side.getStepX(),
|
||||
|
Loading…
Reference in New Issue
Block a user