WIP UndoRedo overhaul.
Fixed concurrent modification issue in ServerBlockPlacer.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package nl.requios.effortlessbuilding;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import nl.requios.effortlessbuilding.create.foundation.render.SuperByteBufferCache;
|
||||
|
||||
import static net.minecraftforge.common.ForgeConfigSpec.*;
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public class CommonEvents {
|
||||
return;
|
||||
}
|
||||
|
||||
UndoRedo.clear(player);
|
||||
EffortlessBuilding.UNDO_REDO.clear(player);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -129,7 +129,7 @@ public class CommonEvents {
|
||||
}
|
||||
|
||||
//Undo redo has no dimension data, so clear it
|
||||
UndoRedo.clear(player);
|
||||
EffortlessBuilding.UNDO_REDO.clear(player);
|
||||
|
||||
//TODO disable build mode and modifiers?
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import nl.requios.effortlessbuilding.proxy.ClientProxy;
|
||||
import nl.requios.effortlessbuilding.proxy.IProxy;
|
||||
import nl.requios.effortlessbuilding.proxy.ServerProxy;
|
||||
import nl.requios.effortlessbuilding.systems.ServerBlockPlacer;
|
||||
import nl.requios.effortlessbuilding.systems.UndoRedo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -41,6 +42,7 @@ public class EffortlessBuilding {
|
||||
public static IProxy proxy = DistExecutor.unsafeRunForDist(() -> ClientProxy::new, () -> ServerProxy::new);
|
||||
|
||||
public static final ServerBlockPlacer SERVER_BLOCK_PLACER = new ServerBlockPlacer();
|
||||
public static final UndoRedo UNDO_REDO = new UndoRedo();
|
||||
|
||||
//Registration
|
||||
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
|
||||
|
||||
@@ -2,7 +2,6 @@ package nl.requios.effortlessbuilding;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -45,7 +44,7 @@ public class ServerConfig {
|
||||
builder.push("Memory");
|
||||
|
||||
undoStackSize = builder
|
||||
.comment("How many placements are remembered for the undo functionality.")
|
||||
.comment("How many sets of blocks are remembered for the undo functionality, per player.")
|
||||
.worldRestart()
|
||||
.defineInRange("undoStackSize", 50, 10, 200);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
|
||||
blocks.clear();
|
||||
for (BlockPos pos : getIntermediateBlocks(player, x1, y1, z1, x2, y2, z2)) {
|
||||
if (blocks.containsKey(pos)) continue;
|
||||
blocks.add(new BlockEntry(pos));
|
||||
}
|
||||
blocks.firstPos = firstPos;
|
||||
@@ -116,6 +117,7 @@ public abstract class ThreeClicksBuildMode extends BaseBuildMode {
|
||||
|
||||
blocks.clear();
|
||||
for (BlockPos pos : getFinalBlocks(player, x1, y1, z1, x2, y2, z2, x3, y3, z3)) {
|
||||
if (blocks.containsKey(pos)) continue;
|
||||
blocks.add(new BlockEntry(pos));
|
||||
}
|
||||
blocks.firstPos = firstPos;
|
||||
|
||||
@@ -65,6 +65,7 @@ public abstract class TwoClicksBuildMode extends BaseBuildMode {
|
||||
|
||||
blocks.clear();
|
||||
for (BlockPos pos : getAllBlocks(player, x1, y1, z1, x2, y2, z2)) {
|
||||
if (blocks.containsKey(pos)) continue;
|
||||
blocks.add(new BlockEntry(pos));
|
||||
}
|
||||
blocks.firstPos = firstPos;
|
||||
|
||||
@@ -2,6 +2,7 @@ package nl.requios.effortlessbuilding.network;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.systems.UndoRedo;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
@@ -19,7 +20,7 @@ public class PerformRedoPacket {
|
||||
public static class Handler {
|
||||
public static void handle(PerformRedoPacket message, Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() -> {
|
||||
UndoRedo.redo(ctx.get().getSender());
|
||||
EffortlessBuilding.UNDO_REDO.redo(ctx.get().getSender());
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package nl.requios.effortlessbuilding.network;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.systems.UndoRedo;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
@@ -19,7 +20,7 @@ public class PerformUndoPacket {
|
||||
public static class Handler {
|
||||
public static void handle(PerformUndoPacket message, Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() -> {
|
||||
UndoRedo.undo(ctx.get().getSender());
|
||||
EffortlessBuilding.UNDO_REDO.undo(ctx.get().getSender());
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package nl.requios.effortlessbuilding.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource.BufferSource;
|
||||
import com.mojang.math.Matrix4f;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.RenderGuiEvent;
|
||||
@@ -28,7 +27,6 @@ public class RenderHandler {
|
||||
@SubscribeEvent
|
||||
public static void onRender(RenderLevelStageEvent event) {
|
||||
if (event.getStage() != RenderLevelStageEvent.Stage.AFTER_TRANSLUCENT_BLOCKS) return;
|
||||
|
||||
Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
|
||||
|
||||
PoseStack ms = event.getPoseStack();
|
||||
@@ -36,7 +34,7 @@ public class RenderHandler {
|
||||
MultiBufferSource.BufferSource buffer = MultiBufferSource.immediate(bufferBuilder);
|
||||
|
||||
ms.pushPose();
|
||||
ms.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
|
||||
ms.translate(-cameraPos.x(), -cameraPos.y(), -cameraPos.z());
|
||||
|
||||
//Mirror and radial mirror lines and areas
|
||||
ModifierRenderer.render(ms, buffer);
|
||||
|
||||
@@ -19,7 +19,6 @@ import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import nl.requios.effortlessbuilding.ClientConfig;
|
||||
import nl.requios.effortlessbuilding.ClientEvents;
|
||||
import nl.requios.effortlessbuilding.CommonConfig;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuildingClient;
|
||||
import nl.requios.effortlessbuilding.buildmode.BuildModeEnum;
|
||||
import nl.requios.effortlessbuilding.compatibility.CompatHelper;
|
||||
@@ -238,11 +237,6 @@ public class BuilderChain {
|
||||
startPos = startPos.relative(lookingAt.getDirection());
|
||||
}
|
||||
|
||||
//Get under tall grass and other replaceable blocks
|
||||
if (shouldOffsetStartPosition && replaceable) {
|
||||
startPos = startPos.below();
|
||||
}
|
||||
|
||||
} else {
|
||||
//We can only break
|
||||
|
||||
|
||||
@@ -28,11 +28,13 @@ public class ServerBlockPlacer {
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
for (DelayedEntry entry : delayedEntries) {
|
||||
//Iterator to prevent concurrent modification exception
|
||||
for (var iterator = delayedEntries.iterator(); iterator.hasNext(); ) {
|
||||
DelayedEntry entry = iterator.next();
|
||||
long gameTime = entry.player.level.getGameTime();
|
||||
if (gameTime >= entry.placeTime) {
|
||||
placeBlocks(entry.player, entry.blocks);
|
||||
delayedEntries.remove(entry);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,35 +42,44 @@ public class ServerBlockPlacer {
|
||||
public void placeBlocks(Player player, BlockSet blocks) {
|
||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||
// EffortlessBuilding.log(player, "Placing " + blocks.size() + " blocks");
|
||||
|
||||
|
||||
var undoSet = new BlockSet();
|
||||
for (BlockEntry block : blocks) {
|
||||
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||
placeBlock(player, block);
|
||||
if (placeBlock(player, block)) {
|
||||
undoSet.add(block);
|
||||
}
|
||||
}
|
||||
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
|
||||
}
|
||||
|
||||
private void placeBlock(Player player, BlockEntry block) {
|
||||
private boolean placeBlock(Player player, BlockEntry block) {
|
||||
Level world = player.level;
|
||||
if (!world.isLoaded(block.blockPos)) return;
|
||||
if (!world.isLoaded(block.blockPos)) return false;
|
||||
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
boolean placedBlock = BlockUtilities.placeBlockEntry(player, block) == InteractionResult.SUCCESS;
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
return placedBlock;
|
||||
}
|
||||
|
||||
public void breakBlocks(Player player, BlockSet blocks) {
|
||||
if (!checkAndNotifyAllowedToUseMod(player)) return;
|
||||
// EffortlessBuilding.log(player, "Breaking " + blocks.size() + " blocks");
|
||||
|
||||
var undoSet = new BlockSet();
|
||||
for (BlockEntry block : blocks) {
|
||||
if (blocks.skipFirst && block.blockPos == blocks.firstPos) continue;
|
||||
breakBlock(player, block);
|
||||
if (breakBlock(player, block)) {
|
||||
undoSet.add(block);
|
||||
}
|
||||
}
|
||||
EffortlessBuilding.UNDO_REDO.addUndo(player, undoSet);
|
||||
}
|
||||
|
||||
private void breakBlock(Player player, BlockEntry block) {
|
||||
private boolean breakBlock(Player player, BlockEntry block) {
|
||||
ServerLevel world = (ServerLevel) player.level;
|
||||
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return;
|
||||
if (!world.isLoaded(block.blockPos) || world.isEmptyBlock(block.blockPos)) return false;
|
||||
|
||||
isPlacingOrBreakingBlocks = true;
|
||||
boolean brokeBlock = BlockHelper.destroyBlockAs(world, block.blockPos, player, player.getMainHandItem(), 0f, stack -> {
|
||||
@@ -77,9 +88,16 @@ public class ServerBlockPlacer {
|
||||
}
|
||||
});
|
||||
isPlacingOrBreakingBlocks = false;
|
||||
return brokeBlock;
|
||||
}
|
||||
|
||||
public boolean checkAndNotifyAllowedToUseMod(Player player) {
|
||||
//TODO TEMP
|
||||
if (!player.isCreative()) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "Effortless Building is not yet supported in survival mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isAllowedToUseMod(player)) {
|
||||
EffortlessBuilding.log(player, ChatFormatting.RED + "You are not allowed to use Effortless Building.");
|
||||
return false;
|
||||
|
||||
@@ -8,187 +8,234 @@ import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import nl.requios.effortlessbuilding.CommonConfig;
|
||||
import nl.requios.effortlessbuilding.EffortlessBuilding;
|
||||
import nl.requios.effortlessbuilding.ServerConfig;
|
||||
import nl.requios.effortlessbuilding.utilities.UndoRedoBlockSet;
|
||||
import nl.requios.effortlessbuilding.utilities.FixedStack;
|
||||
import nl.requios.effortlessbuilding.utilities.InventoryHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.SurvivalHelper;
|
||||
import nl.requios.effortlessbuilding.utilities.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
//Server only
|
||||
public class UndoRedo {
|
||||
|
||||
//Undo and redo stacks per player
|
||||
//Gets added to twice in singleplayer (server and client) if not careful. So separate stacks.
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksClient = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksServer = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksClient = new HashMap<>();
|
||||
private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksServer = new HashMap<>();
|
||||
public final Map<UUID, FixedStack<BlockSet>> undoStacks = new HashMap<>();
|
||||
public final Map<UUID, FixedStack<BlockSet>> redoStacks = new HashMap<>();
|
||||
|
||||
//add to undo stack
|
||||
public static void addUndo(Player player, UndoRedoBlockSet blockSet) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
|
||||
//Assert coordinates is as long as previous and new blockstate lists
|
||||
if (blockSet.getCoordinates().size() != blockSet.getPreviousBlockStates().size() ||
|
||||
blockSet.getCoordinates().size() != blockSet.getNewBlockStates().size()) {
|
||||
EffortlessBuilding.logger.error("Coordinates and blockstate lists are not equal length. Coordinates: {}. Previous blockstates: {}. New blockstates: {}.",
|
||||
blockSet.getCoordinates().size(), blockSet.getPreviousBlockStates().size(), blockSet.getNewBlockStates().size());
|
||||
}
|
||||
|
||||
//Warn if previous and new blockstate are equal
|
||||
//Can happen in a lot of valid cases
|
||||
// for (int i = 0; i < blockSet.getCoordinates().size(); i++) {
|
||||
// if (blockSet.getPreviousBlockStates().get(i).equals(blockSet.getNewBlockStates().get(i))) {
|
||||
// EffortlessBuilding.logger.warn("Previous and new blockstates are equal at index {}. Blockstate: {}.",
|
||||
// i, blockSet.getPreviousBlockStates().get(i));
|
||||
// }
|
||||
// }
|
||||
public void addUndo(Player player, BlockSet blockSet) {
|
||||
if (blockSet.isEmpty()) return;
|
||||
|
||||
//If no stack exists, make one
|
||||
if (!undoStacks.containsKey(player.getUUID())) {
|
||||
undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()]));
|
||||
undoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[ServerConfig.memory.undoStackSize.get()]));
|
||||
}
|
||||
|
||||
undoStacks.get(player.getUUID()).push(blockSet);
|
||||
}
|
||||
|
||||
private static void addRedo(Player player, UndoRedoBlockSet blockSet) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
|
||||
//(No asserts necessary, it's private)
|
||||
private void addRedo(Player player, BlockSet blockSet) {
|
||||
if (blockSet.isEmpty()) return;
|
||||
|
||||
//If no stack exists, make one
|
||||
if (!redoStacks.containsKey(player.getUUID())) {
|
||||
redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()]));
|
||||
redoStacks.put(player.getUUID(), new FixedStack<>(new BlockSet[ServerConfig.memory.undoStackSize.get()]));
|
||||
}
|
||||
|
||||
redoStacks.get(player.getUUID()).push(blockSet);
|
||||
}
|
||||
|
||||
public static boolean undo(Player player) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
|
||||
public boolean undo(Player player) {
|
||||
if (!undoStacks.containsKey(player.getUUID())) return false;
|
||||
|
||||
FixedStack<UndoRedoBlockSet> undoStack = undoStacks.get(player.getUUID());
|
||||
|
||||
FixedStack<BlockSet> undoStack = undoStacks.get(player.getUUID());
|
||||
if (undoStack.isEmpty()) return false;
|
||||
|
||||
UndoRedoBlockSet blockSet = undoStack.pop();
|
||||
List<BlockPos> coordinates = blockSet.getCoordinates();
|
||||
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
|
||||
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
|
||||
BlockSet blockSet = undoStack.pop();
|
||||
// blockSet.undo(player.level);
|
||||
|
||||
//Find up to date itemstacks in player inventory
|
||||
List<ItemStack> itemStacks = findItemStacksInInventory(player, previousBlockStates);
|
||||
|
||||
if (player.level.isClientSide) {
|
||||
// BlockPreviews.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos());
|
||||
} else {
|
||||
//break all those blocks, reset to what they were
|
||||
for (int i = 0; i < coordinates.size(); i++) {
|
||||
BlockPos coordinate = coordinates.get(i);
|
||||
ItemStack itemStack = itemStacks.get(i);
|
||||
|
||||
if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue;
|
||||
|
||||
//get blockstate from itemstack
|
||||
BlockState previousBlockState = previousBlockStates.get(i);
|
||||
if (itemStack.getItem() instanceof BlockItem) {
|
||||
previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
}
|
||||
|
||||
if (player.level.isLoaded(coordinate)) {
|
||||
//check itemstack empty
|
||||
if (itemStack.isEmpty() && !player.isCreative()) {
|
||||
itemStack = findItemStackInInventory(player, previousBlockStates.get(i));
|
||||
//get blockstate from new itemstack
|
||||
if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) {
|
||||
previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
} else {
|
||||
if (previousBlockStates.get(i).getBlock() != Blocks.AIR)
|
||||
EffortlessBuilding.logTranslate(player, "", previousBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true);
|
||||
previousBlockState = Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
}
|
||||
if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true);
|
||||
//if previousBlockState is air, placeBlock will set it to air
|
||||
SurvivalHelper.placeBlock(player.level, player, coordinate, previousBlockState, itemStack, true, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//add to redo
|
||||
addRedo(player, blockSet);
|
||||
BlockSet redoSet = new BlockSet();
|
||||
addRedo(player, redoSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean redo(Player player) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
|
||||
public boolean redo(Player player) {
|
||||
if (!redoStacks.containsKey(player.getUUID())) return false;
|
||||
|
||||
FixedStack<UndoRedoBlockSet> redoStack = redoStacks.get(player.getUUID());
|
||||
|
||||
FixedStack<BlockSet> redoStack = redoStacks.get(player.getUUID());
|
||||
if (redoStack.isEmpty()) return false;
|
||||
|
||||
UndoRedoBlockSet blockSet = redoStack.pop();
|
||||
List<BlockPos> coordinates = blockSet.getCoordinates();
|
||||
List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
|
||||
List<BlockState> newBlockStates = blockSet.getNewBlockStates();
|
||||
|
||||
//Find up to date itemstacks in player inventory
|
||||
List<ItemStack> itemStacks = findItemStacksInInventory(player, newBlockStates);
|
||||
|
||||
if (player.level.isClientSide) {
|
||||
// BlockPreviews.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos());
|
||||
} else {
|
||||
//place blocks
|
||||
for (int i = 0; i < coordinates.size(); i++) {
|
||||
BlockPos coordinate = coordinates.get(i);
|
||||
ItemStack itemStack = itemStacks.get(i);
|
||||
|
||||
if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue;
|
||||
|
||||
//get blockstate from itemstack
|
||||
BlockState newBlockState = newBlockStates.get(i);
|
||||
if (itemStack.getItem() instanceof BlockItem) {
|
||||
newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
}
|
||||
|
||||
if (player.level.isLoaded(coordinate)) {
|
||||
//check itemstack empty
|
||||
if (itemStack.isEmpty() && !player.isCreative()) {
|
||||
itemStack = findItemStackInInventory(player, newBlockStates.get(i));
|
||||
//get blockstate from new itemstack
|
||||
if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) {
|
||||
newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
} else {
|
||||
if (newBlockStates.get(i).getBlock() != Blocks.AIR)
|
||||
EffortlessBuilding.logTranslate(player, "", newBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true);
|
||||
newBlockState = Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
}
|
||||
if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true);
|
||||
SurvivalHelper.placeBlock(player.level, player, coordinate, newBlockState, itemStack, true, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//add to undo
|
||||
BlockSet blockSet = redoStack.pop();
|
||||
// blockSet.redo(player.level);
|
||||
addUndo(player, blockSet);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void clear(Player player) {
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
//Undo and redo stacks per player
|
||||
//Gets added to twice in singleplayer (server and client) if not careful. So separate stacks.
|
||||
// private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksClient = new HashMap<>();
|
||||
// private static final Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacksServer = new HashMap<>();
|
||||
// private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksClient = new HashMap<>();
|
||||
// private static final Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacksServer = new HashMap<>();
|
||||
//
|
||||
// //add to undo stack
|
||||
// public static void addUndo(Player player, UndoRedoBlockSet blockSet) {
|
||||
// Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
//
|
||||
// //Assert coordinates is as long as previous and new blockstate lists
|
||||
// if (blockSet.getCoordinates().size() != blockSet.getPreviousBlockStates().size() ||
|
||||
// blockSet.getCoordinates().size() != blockSet.getNewBlockStates().size()) {
|
||||
// EffortlessBuilding.logger.error("Coordinates and blockstate lists are not equal length. Coordinates: {}. Previous blockstates: {}. New blockstates: {}.",
|
||||
// blockSet.getCoordinates().size(), blockSet.getPreviousBlockStates().size(), blockSet.getNewBlockStates().size());
|
||||
// }
|
||||
//
|
||||
// //Warn if previous and new blockstate are equal
|
||||
// //Can happen in a lot of valid cases
|
||||
//// for (int i = 0; i < blockSet.getCoordinates().size(); i++) {
|
||||
//// if (blockSet.getPreviousBlockStates().get(i).equals(blockSet.getNewBlockStates().get(i))) {
|
||||
//// EffortlessBuilding.logger.warn("Previous and new blockstates are equal at index {}. Blockstate: {}.",
|
||||
//// i, blockSet.getPreviousBlockStates().get(i));
|
||||
//// }
|
||||
//// }
|
||||
//
|
||||
// //If no stack exists, make one
|
||||
// if (!undoStacks.containsKey(player.getUUID())) {
|
||||
// undoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()]));
|
||||
// }
|
||||
//
|
||||
// undoStacks.get(player.getUUID()).push(blockSet);
|
||||
// }
|
||||
//
|
||||
// private static void addRedo(Player player, UndoRedoBlockSet blockSet) {
|
||||
// Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
//
|
||||
// //(No asserts necessary, it's private)
|
||||
//
|
||||
// //If no stack exists, make one
|
||||
// if (!redoStacks.containsKey(player.getUUID())) {
|
||||
// redoStacks.put(player.getUUID(), new FixedStack<>(new UndoRedoBlockSet[ServerConfig.memory.undoStackSize.get()]));
|
||||
// }
|
||||
//
|
||||
// redoStacks.get(player.getUUID()).push(blockSet);
|
||||
// }
|
||||
//
|
||||
// public static boolean undo(Player player) {
|
||||
// Map<UUID, FixedStack<UndoRedoBlockSet>> undoStacks = player.level.isClientSide ? undoStacksClient : undoStacksServer;
|
||||
//
|
||||
// if (!undoStacks.containsKey(player.getUUID())) return false;
|
||||
//
|
||||
// FixedStack<UndoRedoBlockSet> undoStack = undoStacks.get(player.getUUID());
|
||||
//
|
||||
// if (undoStack.isEmpty()) return false;
|
||||
//
|
||||
// UndoRedoBlockSet blockSet = undoStack.pop();
|
||||
// List<BlockPos> coordinates = blockSet.getCoordinates();
|
||||
// List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
|
||||
// List<BlockState> newBlockStates = blockSet.getNewBlockStates();
|
||||
//
|
||||
// //Find up to date itemstacks in player inventory
|
||||
// List<ItemStack> itemStacks = findItemStacksInInventory(player, previousBlockStates);
|
||||
//
|
||||
// if (player.level.isClientSide) {
|
||||
//// BlockPreviews.onBlocksBroken(coordinates, itemStacks, newBlockStates, blockSet.getSecondPos(), blockSet.getFirstPos());
|
||||
// } else {
|
||||
// //break all those blocks, reset to what they were
|
||||
// for (int i = 0; i < coordinates.size(); i++) {
|
||||
// BlockPos coordinate = coordinates.get(i);
|
||||
// ItemStack itemStack = itemStacks.get(i);
|
||||
//
|
||||
// if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue;
|
||||
//
|
||||
// //get blockstate from itemstack
|
||||
// BlockState previousBlockState = previousBlockStates.get(i);
|
||||
// if (itemStack.getItem() instanceof BlockItem) {
|
||||
// previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
// }
|
||||
//
|
||||
// if (player.level.isLoaded(coordinate)) {
|
||||
// //check itemstack empty
|
||||
// if (itemStack.isEmpty() && !player.isCreative()) {
|
||||
// itemStack = findItemStackInInventory(player, previousBlockStates.get(i));
|
||||
// //get blockstate from new itemstack
|
||||
// if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) {
|
||||
// previousBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
// } else {
|
||||
// if (previousBlockStates.get(i).getBlock() != Blocks.AIR)
|
||||
// EffortlessBuilding.logTranslate(player, "", previousBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true);
|
||||
// previousBlockState = Blocks.AIR.defaultBlockState();
|
||||
// }
|
||||
// }
|
||||
// if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true);
|
||||
// //if previousBlockState is air, placeBlock will set it to air
|
||||
// SurvivalHelper.placeBlock(player.level, player, coordinate, previousBlockState, itemStack, true, false, false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //add to redo
|
||||
// addRedo(player, blockSet);
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// public static boolean redo(Player player) {
|
||||
// Map<UUID, FixedStack<UndoRedoBlockSet>> redoStacks = player.level.isClientSide ? redoStacksClient : redoStacksServer;
|
||||
//
|
||||
// if (!redoStacks.containsKey(player.getUUID())) return false;
|
||||
//
|
||||
// FixedStack<UndoRedoBlockSet> redoStack = redoStacks.get(player.getUUID());
|
||||
//
|
||||
// if (redoStack.isEmpty()) return false;
|
||||
//
|
||||
// UndoRedoBlockSet blockSet = redoStack.pop();
|
||||
// List<BlockPos> coordinates = blockSet.getCoordinates();
|
||||
// List<BlockState> previousBlockStates = blockSet.getPreviousBlockStates();
|
||||
// List<BlockState> newBlockStates = blockSet.getNewBlockStates();
|
||||
//
|
||||
// //Find up to date itemstacks in player inventory
|
||||
// List<ItemStack> itemStacks = findItemStacksInInventory(player, newBlockStates);
|
||||
//
|
||||
// if (player.level.isClientSide) {
|
||||
//// BlockPreviews.onBlocksPlaced(coordinates, itemStacks, newBlockStates, blockSet.getFirstPos(), blockSet.getSecondPos());
|
||||
// } else {
|
||||
// //place blocks
|
||||
// for (int i = 0; i < coordinates.size(); i++) {
|
||||
// BlockPos coordinate = coordinates.get(i);
|
||||
// ItemStack itemStack = itemStacks.get(i);
|
||||
//
|
||||
// if (previousBlockStates.get(i).equals(newBlockStates.get(i))) continue;
|
||||
//
|
||||
// //get blockstate from itemstack
|
||||
// BlockState newBlockState = newBlockStates.get(i);
|
||||
// if (itemStack.getItem() instanceof BlockItem) {
|
||||
// newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
// }
|
||||
//
|
||||
// if (player.level.isLoaded(coordinate)) {
|
||||
// //check itemstack empty
|
||||
// if (itemStack.isEmpty() && !player.isCreative()) {
|
||||
// itemStack = findItemStackInInventory(player, newBlockStates.get(i));
|
||||
// //get blockstate from new itemstack
|
||||
// if (!itemStack.isEmpty() && itemStack.getItem() instanceof BlockItem) {
|
||||
// newBlockState = ((BlockItem) itemStack.getItem()).getBlock().defaultBlockState();
|
||||
// } else {
|
||||
// if (newBlockStates.get(i).getBlock() != Blocks.AIR)
|
||||
// EffortlessBuilding.logTranslate(player, "", newBlockStates.get(i).getBlock().getDescriptionId(), " not found in inventory", true);
|
||||
// newBlockState = Blocks.AIR.defaultBlockState();
|
||||
// }
|
||||
// }
|
||||
// if (itemStack.isEmpty()) SurvivalHelper.breakBlock(player.level, player, coordinate, true);
|
||||
// SurvivalHelper.placeBlock(player.level, player, coordinate, newBlockState, itemStack, true, false, false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //add to undo
|
||||
// addUndo(player, blockSet);
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
|
||||
public void clear(Player player) {
|
||||
if (undoStacks.containsKey(player.getUUID())) {
|
||||
undoStacks.get(player.getUUID()).clear();
|
||||
}
|
||||
@@ -197,7 +244,7 @@ public class UndoRedo {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ItemStack> findItemStacksInInventory(Player player, List<BlockState> blockStates) {
|
||||
private List<ItemStack> findItemStacksInInventory(Player player, List<BlockState> blockStates) {
|
||||
List<ItemStack> itemStacks = new ArrayList<>(blockStates.size());
|
||||
for (BlockState blockState : blockStates) {
|
||||
itemStacks.add(findItemStackInInventory(player, blockState));
|
||||
@@ -205,7 +252,7 @@ public class UndoRedo {
|
||||
return itemStacks;
|
||||
}
|
||||
|
||||
private static ItemStack findItemStackInInventory(Player player, BlockState blockState) {
|
||||
private ItemStack findItemStackInInventory(Player player, BlockState blockState) {
|
||||
ItemStack itemStack = ItemStack.EMPTY;
|
||||
if (blockState == null) return itemStack;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user