From 42f8a044a45d9ee225be4df307239f91d74c91ef Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 10 Feb 2023 21:47:10 +0100 Subject: [PATCH] Add completely new schematic format to reduce schematic size to about 7% Signed-off-by: yoyosource --- .../cuboid2/TSchemClipboardFormat.java | 79 ++++++++++++ .../features/cuboid2/TypedClipboard.java | 84 +++++++++++++ .../features/cuboid2/TypedCommand.java | 80 ++++++++++++ .../features/cuboid2/io/TSchemWriter.java | 114 ++++++++++++++++++ 4 files changed, 357 insertions(+) create mode 100644 BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TSchemClipboardFormat.java create mode 100644 BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedClipboard.java create mode 100644 BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedCommand.java create mode 100644 BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/io/TSchemWriter.java diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TSchemClipboardFormat.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TSchemClipboardFormat.java new file mode 100644 index 00000000..4ebbb95d --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TSchemClipboardFormat.java @@ -0,0 +1,79 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.cuboid2; + +import com.fastasyncworldedit.core.internal.io.parallelgzip.ParallelGZIPOutputStream; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import de.steamwar.bausystem.features.cuboid2.io.TSchemWriter; + +import java.io.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.zip.GZIPOutputStream; + +public class TSchemClipboardFormat implements ClipboardFormat { + + @Override + public String getName() { + return "TSchem"; + } + + @Override + public Set getAliases() { + return new HashSet<>(Arrays.asList("tschem")); + } + + @Override + public ClipboardReader getReader(InputStream inputStream) throws IOException { + return null; + } + + @Override + public ClipboardWriter getWriter(OutputStream outputStream) throws IOException { + OutputStream gzip; + if (!(outputStream instanceof ParallelGZIPOutputStream) && !(outputStream instanceof GZIPOutputStream)) { + OutputStream outputStreamx = new BufferedOutputStream(outputStream); + gzip = new GZIPOutputStream(outputStreamx); + } else { + gzip = outputStream; + } + + return new TSchemWriter(new NBTOutputStream(new BufferedOutputStream(gzip))); + } + + @Override + public boolean isFormat(File file) { + return file.getName().endsWith(".tschem"); + } + + @Override + public String getPrimaryFileExtension() { + return "tschem"; + } + + @Override + public Set getFileExtensions() { + return new HashSet<>(Arrays.asList("tschem")); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedClipboard.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedClipboard.java new file mode 100644 index 00000000..72deea9d --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedClipboard.java @@ -0,0 +1,84 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.cuboid2; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.block.BlockState; +import lombok.Getter; + +import java.util.Map; +import java.util.Set; + +@Getter +public class TypedClipboard implements Clipboard { + + private Clipboard parent; + private Map> blocks; + + public TypedClipboard(Clipboard parent, Map> blocks) { + this.parent = parent; + this.blocks = blocks; + } + + @Override + public Region getRegion() { + return parent.getRegion(); + } + + @Override + public BlockVector3 getDimensions() { + return parent.getDimensions(); + } + + @Override + public BlockVector3 getOrigin() { + return parent.getOrigin(); + } + + @Override + public void setOrigin(BlockVector3 origin) { + parent.setOrigin(origin); + } + + @Override + public void removeEntity(Entity entity) { + + } + + @Override + public BlockVector3 getMinimumPoint() { + return parent.getMinimumPoint(); + } + + @Override + public BlockVector3 getMaximumPoint() { + return parent.getMaximumPoint(); + } + + @Override + public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { + return false; + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedCommand.java new file mode 100644 index 00000000..dc251a1c --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/TypedCommand.java @@ -0,0 +1,80 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.cuboid2; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.block.BlockState; +import de.steamwar.bausystem.utils.WorldEditUtils; +import de.steamwar.command.SWCommand; +import de.steamwar.linkage.Linked; +import org.bukkit.entity.Player; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +@Linked +public class TypedCommand extends SWCommand { + + static { + ClipboardFormats.registerClipboardFormat(new TSchemClipboardFormat()); + } + + public TypedCommand() { + super("typed"); + } + + @Register + public void test(Player player) { + Clipboard clipboard = WorldEditUtils.getClipboard(player); + if (clipboard == null) { + player.sendMessage("§cDu hast keine Schematic geladen!"); + return; + } + + long time = System.currentTimeMillis(); + Map> blocks = new HashMap<>(); + clipboard.getRegion().forEach(blockVector3 -> { + BlockVector3 position = blockVector3.subtract(clipboard.getMinimumPoint()); + BlockState block = clipboard.getBlock(blockVector3); + blocks.computeIfAbsent(block, k -> new HashSet<>()).add(position); + }); + TypedClipboard typedClipboard = new TypedClipboard(clipboard, blocks); + AtomicLong size = new AtomicLong(); + try { + typedClipboard.save(new OutputStream() { + @Override + public void write(int b) throws IOException { + size.incrementAndGet(); + } + }, ClipboardFormats.findByAlias("tschem")); + } catch (IOException e) { + } + long time2 = System.currentTimeMillis(); + player.sendMessage("§aDie Größe der Schematic beträgt " + size.get() + " Bytes!"); + player.sendMessage("§aTime: " + (time2 - time) + " ms"); + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/io/TSchemWriter.java b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/io/TSchemWriter.java new file mode 100644 index 00000000..5a901363 --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/cuboid2/io/TSchemWriter.java @@ -0,0 +1,114 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.cuboid2.io; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.NBTOutputStream; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.block.BlockState; +import de.steamwar.bausystem.features.cuboid2.TypedClipboard; + +import java.io.IOException; +import java.util.*; + +public class TSchemWriter implements ClipboardWriter { + + private static final int CURRENT_VERSION = 1; + private final NBTOutputStream outputStream; + + public TSchemWriter(NBTOutputStream outputStream) { + this.outputStream = outputStream; + } + + @Override + public void write(Clipboard clipboard) throws IOException { + if (!(clipboard instanceof TypedClipboard)) { + return; + } + TypedClipboard typedClipboard = (TypedClipboard) clipboard; + + Region region = clipboard.getRegion(); + BlockVector3 origin = clipboard.getOrigin(); + BlockVector3 min = region.getMinimumPoint(); + BlockVector3 offset = min.subtract(origin); + + int volume = (int) region.getVolume(); + + outputStream.writeLazyCompoundTag("Schematic", out -> { + out.writeNamedTag("DataVersion", WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion()); + out.writeNamedTag("Version", CURRENT_VERSION); + List bs = new ArrayList<>(); + writeVarInt(region.getWidth(), bs); + writeVarInt(region.getHeight(), bs); + writeVarInt(region.getLength(), bs); + writeVarInt(min.getBlockX(), bs); + writeVarInt(min.getBlockY(), bs); + writeVarInt(min.getBlockZ(), bs); + writeVarInt(offset.getBlockX(), bs); + writeVarInt(offset.getBlockY(), bs); + writeVarInt(offset.getBlockZ(), bs); + byte[] bsArray = new byte[bs.size()]; + for (int i = 0; i < bsArray.length; i++) { + bsArray[i] = bs.get(i); + } + out.writeNamedTag("SizeMinOffset", new ByteArrayTag(bsArray)); + + Map blockMap = new HashMap<>(); + typedClipboard.getBlocks().forEach((blockState, blockVector3s) -> { + blockMap.put(blockState, new BitSet(volume)); + }); + clipboard.getRegion().forEach(blockVector3 -> { + int index = blockVector3.getBlockX() + blockVector3.getBlockY() * region.getWidth() + blockVector3.getBlockZ() * region.getWidth() * region.getHeight(); + for (Map.Entry> entry : typedClipboard.getBlocks().entrySet()) { + if (entry.getValue().contains(blockVector3)) { + blockMap.get(entry.getKey()).set(index); + } + } + }); + out.writeLazyCompoundTag("Blocks", out2 -> { + for (Map.Entry entry : blockMap.entrySet()) { + out2.writeNamedTag(entry.getKey().getAsString(), entry.getValue().toByteArray()); + } + }); + }); + } + + private static void writeVarInt(int value, List bytes) { + while (true) { + if (value < 128) { + bytes.add((byte) value); + return; + } else { + bytes.add((byte) (value & 127 | 128)); + value >>>= 7; + } + } + } + + @Override + public void close() throws IOException { + outputStream.close(); + } +}