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