MCPE fixes
Fix some MCPE mappings Support MCPE -> PC
This commit is contained in:
parent
ad593deb0f
commit
845ea0e9dc
@ -737,7 +737,6 @@ public class FaweCache {
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public static ShortTag asTag(short value) {
|
||||
return new ShortTag(value);
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ import com.intellectualcrafters.plot.PS;
|
||||
import com.intellectualcrafters.plot.commands.Auto;
|
||||
import com.intellectualcrafters.plot.config.C;
|
||||
import com.intellectualcrafters.plot.config.Settings;
|
||||
import com.intellectualcrafters.plot.database.DBFunc;
|
||||
import com.intellectualcrafters.plot.object.Plot;
|
||||
import com.intellectualcrafters.plot.object.PlotArea;
|
||||
import com.intellectualcrafters.plot.object.PlotId;
|
||||
import com.intellectualcrafters.plot.object.PlotPlayer;
|
||||
import com.intellectualcrafters.plot.object.worlds.PlotAreaManager;
|
||||
@ -139,6 +141,23 @@ public class CFICommands extends MethodCommands {
|
||||
fp.sendMessage(BBC.getPrefix() + "Cancelled!");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void autoClaimFromDatabase(PlotPlayer player, PlotArea area, PlotId start, com.intellectualcrafters.plot.object.RunnableVal<Plot> whenDone) {
|
||||
final Plot plot = area.getNextFreePlot(player, start);
|
||||
if (plot == null) {
|
||||
whenDone.run(null);
|
||||
return;
|
||||
}
|
||||
whenDone.value = plot;
|
||||
plot.owner = player.getUUID();
|
||||
DBFunc.createPlotSafe(plot, whenDone, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
autoClaimFromDatabase(player, area, plot.getId(), whenDone);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = {"done", "create"},
|
||||
usage = "",
|
||||
@ -164,6 +183,7 @@ public class CFICommands extends MethodCommands {
|
||||
C.CANT_CLAIM_MORE_PLOTS_NUM.send(player, -diff);
|
||||
return;
|
||||
}
|
||||
|
||||
if (area.getMeta("lastPlot") == null) {
|
||||
area.setMeta("lastPlot", new PlotId(0, 0));
|
||||
}
|
||||
|
@ -530,6 +530,35 @@ public class MCAChunk extends FaweChunk<Void> {
|
||||
streamer.readFully();
|
||||
}
|
||||
|
||||
public void filterBlocks(MutableMCABackedBaseBlock mutableBlock, MCAFilter filter) {
|
||||
mutableBlock.setChunk(this);
|
||||
int bx = getX() << 4;
|
||||
int bz = getZ() << 4;
|
||||
int tx = bx + 15;
|
||||
int tz = bz + 15;
|
||||
for (int layer = 0; layer < ids.length; layer++) {
|
||||
if (doesSectionExist(layer)) {
|
||||
mutableBlock.setArrays(layer);
|
||||
int yStart = layer << 4;
|
||||
int yEnd = yStart + 15;
|
||||
for (int y = yStart, y0 = (yStart & 15); y <= yEnd; y++, y0++) {
|
||||
int yIndex = ((y0) << 8);
|
||||
mutableBlock.setY(y);
|
||||
for (int z = bz, z0 = bz & 15; z <= tz; z++, z0++) {
|
||||
int zIndex = yIndex + ((z0) << 4);
|
||||
mutableBlock.setZ(z);
|
||||
for (int x = bx, x0 = bx & 15; x <= tx; x++, x0++) {
|
||||
int xIndex = zIndex + x0;
|
||||
mutableBlock.setX(x);
|
||||
mutableBlock.setIndex(xIndex);
|
||||
filter.applyBlock(x, y, z, mutableBlock, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getHeightMapArray() {
|
||||
return heightMap;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import com.boydti.fawe.object.RunnableVal;
|
||||
import com.boydti.fawe.object.exception.FaweException;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.SetQueue;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
@ -40,7 +41,7 @@ public class MCAQueueMap implements IFaweQueueMap {
|
||||
private int lastX = Integer.MIN_VALUE;
|
||||
private int lastZ = Integer.MIN_VALUE;
|
||||
|
||||
public synchronized MCAFile getMCAFile(int cx, int cz) {
|
||||
public synchronized MCAFile getMCAFile(int cx, int cz, boolean create) {
|
||||
int mcaX = cx >> 5;
|
||||
int mcaZ = cz >> 5;
|
||||
if (mcaX == lastFileX && mcaZ == lastFileZ) {
|
||||
@ -52,7 +53,13 @@ public class MCAQueueMap implements IFaweQueueMap {
|
||||
if (lastFile == null) {
|
||||
try {
|
||||
queue.setMCA(lastFileX, lastFileZ, RegionWrapper.GLOBAL(), null, true, false);
|
||||
lastFile = tmp = new MCAFile(queue, lastFileX, lastFileZ);
|
||||
File file = new File(queue.getSaveFolder(), "r." + lastFileX + "." + lastFileZ + ".mca");
|
||||
if (create) {
|
||||
File parent = file.getParentFile();
|
||||
if (!parent.exists()) parent.mkdirs();
|
||||
if (!file.exists()) file.createNewFile();
|
||||
}
|
||||
lastFile = tmp = new MCAFile(queue, file);
|
||||
} catch (FaweException.FaweChunkLoadException ignore) {
|
||||
lastFile = null;
|
||||
return null;
|
||||
@ -85,8 +92,7 @@ public class MCAQueueMap implements IFaweQueueMap {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk getFaweChunk(int cx, int cz) {
|
||||
public FaweChunk getFaweChunk(int cx, int cz, boolean create) {
|
||||
if (cx == lastX && cz == lastZ) {
|
||||
if (nullChunk == lastChunk) {
|
||||
nullChunk.setLoc(queue, lastX, lastZ);
|
||||
@ -103,12 +109,17 @@ public class MCAQueueMap implements IFaweQueueMap {
|
||||
}
|
||||
}
|
||||
try {
|
||||
MCAFile mcaFile = getMCAFile(cx, cz);
|
||||
MCAFile mcaFile = getMCAFile(cx, cz, create);
|
||||
if (mcaFile != null) {
|
||||
mcaFile.init();
|
||||
lastChunk = mcaFile.getChunk(cx, cz);
|
||||
if (lastChunk != null) {
|
||||
return lastChunk;
|
||||
} else if (create) {
|
||||
MCAChunk chunk = new MCAChunk(queue, cx, cz);
|
||||
mcaFile.setChunk(chunk);
|
||||
lastChunk = chunk;
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
@ -122,6 +133,11 @@ public class MCAQueueMap implements IFaweQueueMap {
|
||||
return lastChunk = nullChunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk getFaweChunk(int cx, int cz) {
|
||||
return getFaweChunk(cx, cz, !isHybridQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaweChunk getCachedFaweChunk(int cx, int cz) {
|
||||
int mcaX = cx >> 5;
|
||||
@ -171,6 +187,7 @@ public class MCAQueueMap implements IFaweQueueMap {
|
||||
lastZ = Integer.MIN_VALUE;
|
||||
lastFileX = Integer.MIN_VALUE;
|
||||
lastFileZ = Integer.MIN_VALUE;
|
||||
System.out.println("Files " + mcaFileMap);
|
||||
if (!mcaFileMap.isEmpty()) {
|
||||
Iterator<Map.Entry<Long, MCAFile>> iter = mcaFileMap.entrySet().iterator();
|
||||
boolean result;
|
||||
|
@ -48,7 +48,7 @@ public class RemapFilter extends MCAFilterCounter {
|
||||
@Override
|
||||
public void applyBlock(int x, int y, int z, BaseBlock block, MutableLong cache) {
|
||||
int id = block.getId();
|
||||
if (remapper.hasRemap(block.getId())) {
|
||||
if (remapper.hasRemap(id)) {
|
||||
BaseBlock result = remapper.remap(block);
|
||||
if (result != block) {
|
||||
cache.add(1);
|
||||
@ -63,6 +63,7 @@ public class RemapFilter extends MCAFilterCounter {
|
||||
block.setData(result.getData());
|
||||
}
|
||||
}
|
||||
if (from != null) {
|
||||
outer:
|
||||
switch (from) {
|
||||
case PC: {
|
||||
@ -122,7 +123,8 @@ public class RemapFilter extends MCAFilterCounter {
|
||||
if (z < 16) {
|
||||
if ((newLight = chunk.getSkyLight(x, y, z + 1)) > currentLight) break;
|
||||
}
|
||||
default: break outer;
|
||||
default:
|
||||
break outer;
|
||||
}
|
||||
if (newLight != 0) {
|
||||
((MutableMCABackedBaseBlock) block).getChunk().setSkyLight(x, y, z, newLight);
|
||||
@ -131,4 +133,5 @@ public class RemapFilter extends MCAFilterCounter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import com.sk89q.worldedit.regions.Region;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import static com.boydti.fawe.FaweCache.getBlock;
|
||||
|
||||
public class ClipboardRemapper {
|
||||
public enum RemapPlatform {
|
||||
@ -65,6 +66,11 @@ public class ClipboardRemapper {
|
||||
mapPEtoPC.put(new BaseBlock(peId,5), new BaseBlock(pcId,3));
|
||||
}
|
||||
|
||||
for (int id : new int[] {29, 33}) {
|
||||
addBoth(getBlock(id, 3), getBlock(id, 4));
|
||||
addBoth(getBlock(id, 10), getBlock(id, 11));
|
||||
}
|
||||
|
||||
mapPEtoPC.put(new BaseBlock(236,-1), new BaseBlock(251,-1));
|
||||
mapPEtoPC.put(new BaseBlock(237,-1), new BaseBlock(252,-1));
|
||||
mapPEtoPC.put(new BaseBlock(240,-1), new BaseBlock(199,-1));
|
||||
@ -114,6 +120,7 @@ public class ClipboardRemapper {
|
||||
for (int data = 4; data < 8; data++) mapPEtoPC.put(new BaseBlock(18,data + 8), new BaseBlock(161,data));
|
||||
|
||||
for (int id : new int[] {96, 167}) { // trapdoor
|
||||
for (int data = 0; data < 4; data++) mapPEtoPC.put(new BaseBlock(id, data), new BaseBlock(id, 3 - data));
|
||||
for (int data = 4; data < 12; data++) mapPEtoPC.put(new BaseBlock(id, data), new BaseBlock(id, 15 - data));
|
||||
for (int data = 12; data < 15; data++) mapPEtoPC.put(new BaseBlock(id, data), new BaseBlock(id, 27 - data));
|
||||
}
|
||||
@ -137,6 +144,11 @@ public class ClipboardRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
public void addBoth(BaseBlock from, BaseBlock to) {
|
||||
add(from, to);
|
||||
add(to, from);
|
||||
}
|
||||
|
||||
public void apply(Clipboard clipboard) throws WorldEditException {
|
||||
if (clipboard instanceof BlockArrayClipboard) {
|
||||
BlockArrayClipboard bac = (BlockArrayClipboard) clipboard;
|
||||
@ -214,8 +226,6 @@ public class ClipboardRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BaseBlock remap(BaseBlock block) {
|
||||
int combined = block.getCombined();
|
||||
if (remap[combined]) {
|
||||
|
@ -496,8 +496,8 @@ public class MainUtil {
|
||||
private static final Class[] parameters = new Class[]{URL.class};
|
||||
|
||||
public static void loadURLClasspath(URL u) throws IOException {
|
||||
ClassLoader sysloader = ClassLoader.getSystemClassLoader();
|
||||
|
||||
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
||||
Class sysclass = URLClassLoader.class;
|
||||
|
||||
try {
|
||||
|
@ -316,8 +316,12 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
|
||||
if (this.limit.SPEED_REDUCTION > 0) {
|
||||
this.bypassHistory = new SlowExtent(this.bypassHistory, this.limit.SPEED_REDUCTION);
|
||||
}
|
||||
if (changeSet instanceof NullChangeSet && Fawe.imp().getBlocksHubApi() != null && player != null) {
|
||||
changeSet = LoggingChangeSet.wrap(player, changeSet);
|
||||
}
|
||||
System.out.println("Changeset " + changeSet);
|
||||
if (!(changeSet instanceof NullChangeSet)) {
|
||||
if (player != null && Fawe.imp().getBlocksHubApi() != null) {
|
||||
if (!(changeSet instanceof LoggingChangeSet) && player != null && Fawe.imp().getBlocksHubApi() != null) {
|
||||
changeSet = LoggingChangeSet.wrap(player, changeSet);
|
||||
}
|
||||
if (this.blockBag != null) {
|
||||
@ -1269,6 +1273,15 @@ public class EditSession extends AbstractWorld implements HasFaweQueue, Lighting
|
||||
editSession.changes = 1;
|
||||
}
|
||||
|
||||
public void setBlocks(ChangeSet changeSet, ChangeSetExecutor.Type type) {
|
||||
final UndoContext context = new UndoContext();
|
||||
Extent bypass = (history == null) ? bypassAll : history;
|
||||
context.setExtent(bypass);
|
||||
Operations.completeBlindly(ChangeSetExecutor.create(changeSet, context, type, getBlockBag(), getLimit().INVENTORY_MODE));
|
||||
flushQueue();
|
||||
changes = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets to new state.
|
||||
*
|
||||
|
@ -52,6 +52,7 @@ import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.mask.Masks;
|
||||
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
|
||||
import com.sk89q.worldedit.history.changeset.ChangeSet;
|
||||
import com.sk89q.worldedit.internal.cui.CUIEvent;
|
||||
import com.sk89q.worldedit.internal.cui.CUIRegion;
|
||||
@ -516,13 +517,13 @@ public class LocalSession {
|
||||
EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld())
|
||||
.allowedRegionsEverywhere()
|
||||
.checkMemory(false)
|
||||
.changeSet(changeSet)
|
||||
.changeSetNull()
|
||||
.fastmode(false)
|
||||
.limitUnprocessed(fp)
|
||||
.player(fp)
|
||||
.blockBag(getBlockBag(player))
|
||||
.build();
|
||||
newEditSession.undo(newEditSession);
|
||||
newEditSession.setBlocks(changeSet, ChangeSetExecutor.Type.UNDO);
|
||||
setDirty();
|
||||
historyNegativeIndex++;
|
||||
return newEditSession;
|
||||
@ -565,13 +566,13 @@ public class LocalSession {
|
||||
EditSession newEditSession = new EditSessionBuilder(changeSet.getWorld())
|
||||
.allowedRegionsEverywhere()
|
||||
.checkMemory(false)
|
||||
.changeSet(changeSet)
|
||||
.changeSetNull()
|
||||
.fastmode(false)
|
||||
.limitUnprocessed(fp)
|
||||
.player(fp)
|
||||
.blockBag(getBlockBag(player))
|
||||
.build();
|
||||
newEditSession.redo(newEditSession);
|
||||
newEditSession.setBlocks(changeSet, ChangeSetExecutor.Type.REDO);
|
||||
return newEditSession;
|
||||
}
|
||||
return null;
|
||||
|
@ -41,7 +41,7 @@ public class ConvertCommands extends MethodCommands {
|
||||
RemapFilter filter = new RemapFilter(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE);
|
||||
|
||||
FaweQueue defaultQueue = SetQueue.IMP.getNewQueue(folder, true, false);
|
||||
try (MCAFile2LevelDB converter = new MCAFile2LevelDB(defaultQueue.getSaveFolder().getParentFile())) {
|
||||
try (MCAFile2LevelDB converter = new MCAFile2LevelDB(null, defaultQueue.getSaveFolder().getParentFile())) {
|
||||
|
||||
DelegateMCAFilter<MutableLong> delegate = new DelegateMCAFilter<MutableLong>(filter) {
|
||||
@Override
|
||||
|
@ -11,8 +11,6 @@ import com.boydti.fawe.installer.MinimizeButton;
|
||||
import com.boydti.fawe.installer.MovablePanel;
|
||||
import com.boydti.fawe.installer.TextAreaOutputStream;
|
||||
import com.boydti.fawe.installer.URLButton;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAFilter;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAQueue;
|
||||
import com.boydti.fawe.util.MainUtil;
|
||||
import com.boydti.fawe.wrappers.FakePlayer;
|
||||
import java.awt.BorderLayout;
|
||||
@ -88,7 +86,7 @@ public class ConverterFrame extends JFrame {
|
||||
JPanel topBarCenter = new InvisiblePanel();
|
||||
JPanel topBarRight = new InvisiblePanel();
|
||||
|
||||
JLabel title = new JLabel("(FAWE) Anvil to LevelDB converter");
|
||||
JLabel title = new JLabel("(FAWE) Anvil and LevelDB converter");
|
||||
title.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
title.setAlignmentX(Component.RIGHT_ALIGNMENT);
|
||||
title.setForeground(Color.LIGHT_GRAY);
|
||||
@ -345,32 +343,9 @@ public class ConverterFrame extends JFrame {
|
||||
MainUtil.loadURLClasspath(leveldb.toURL());
|
||||
|
||||
File newWorldFile = new File(output, dirMc.getName());
|
||||
try (MCAFile2LevelDB converter = new MCAFile2LevelDB(newWorldFile)) {
|
||||
debug("Starting world conversion");
|
||||
|
||||
MCAFilter filter = converter.toFilter();
|
||||
MCAQueue queue = new MCAQueue(null, new File(dirMc, "region"), true);
|
||||
MCAFilter result = queue.filterWorld(filter);
|
||||
|
||||
File levelDat = new File(dirMc, "level.dat");
|
||||
if (levelDat.exists()) {
|
||||
converter.copyLevelDat(levelDat);
|
||||
}
|
||||
converter.close();
|
||||
prompt(
|
||||
"Conversion complete!\n" +
|
||||
" - The world save is still being compacted, but you can close the program anytime\n" +
|
||||
" - There will be another prompt when this finishes\n" +
|
||||
"\n" +
|
||||
"What is not converted?\n" +
|
||||
" - Inventory is not copied\n" +
|
||||
" - Some block nbt may not copy\n" +
|
||||
" - Any custom generator settings may not work\n" +
|
||||
" - May not match up with new terrain"
|
||||
);
|
||||
converter.compact();
|
||||
prompt("Compaction complete!");
|
||||
}
|
||||
MapConverter converter = MapConverter.get(dirMc, newWorldFile);
|
||||
converter.accept(ConverterFrame.this);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
prompt("[ERROR] Conversion failed, you will have to do it manually (Nukkit server + anvil2leveldb command)");
|
||||
|
@ -1,33 +1,57 @@
|
||||
package com.boydti.fawe.nukkit.core.converter;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAChunk;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAQueue;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAQueueMap;
|
||||
import com.boydti.fawe.jnbt.anvil.MutableMCABackedBaseBlock;
|
||||
import com.boydti.fawe.jnbt.anvil.filters.RemapFilter;
|
||||
import com.boydti.fawe.object.clipboard.ClipboardRemapper;
|
||||
import com.boydti.fawe.object.io.PGZIPOutputStream;
|
||||
import com.boydti.fawe.util.MemUtil;
|
||||
import com.boydti.fawe.util.ReflectionUtils;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
import com.sk89q.jnbt.ByteTag;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.LongTag;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.NBTOutputStream;
|
||||
import com.sk89q.jnbt.NamedTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.worldedit.world.registry.BundledBlockData;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import org.iq80.leveldb.DB;
|
||||
import org.iq80.leveldb.Options;
|
||||
import org.iq80.leveldb.impl.Iq80DBFactory;
|
||||
|
||||
public class LevelDBToMCAFile implements Closeable, Runnable{
|
||||
public class LevelDBToMCAFile extends MapConverter {
|
||||
|
||||
private final DB db;
|
||||
private final ClipboardRemapper remapper;
|
||||
private final ForkJoinPool pool;
|
||||
|
||||
public LevelDBToMCAFile(File folder) {
|
||||
public LevelDBToMCAFile(File from, File to) {
|
||||
super(from, to);
|
||||
try {
|
||||
this.pool = new ForkJoinPool();
|
||||
this.remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE);
|
||||
BundledBlockData.getInstance().loadFromResource();
|
||||
this.pool = new ForkJoinPool();
|
||||
this.remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PE, ClipboardRemapper.RemapPlatform.PC);
|
||||
int bufferSize = (int) Math.min(Integer.MAX_VALUE, Math.max((long) (MemUtil.getFreeBytes() * 0.8), 134217728));
|
||||
this.db = Iq80DBFactory.factory.open(new File(folder, "db"),
|
||||
this.db = Iq80DBFactory.factory.open(new File(from, "db"),
|
||||
new Options()
|
||||
.createIfMissing(false)
|
||||
.verifyChecksums(false)
|
||||
@ -57,17 +81,259 @@ public class LevelDBToMCAFile implements Closeable, Runnable{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
db.forEach(new Consumer<Map.Entry<byte[], byte[]>>() {
|
||||
@Override
|
||||
public void accept(Map.Entry<byte[], byte[]> entry) {
|
||||
public void accept(ConverterFrame app) {
|
||||
try {
|
||||
// World name
|
||||
String worldName;
|
||||
File levelName = new File(folderFrom, "levelname.txt");
|
||||
if (levelName.exists()) {
|
||||
byte[] encoded = Files.readAllBytes(levelName.toPath());
|
||||
worldName = new String(encoded);
|
||||
} else {
|
||||
worldName = folderFrom.toString();
|
||||
}
|
||||
File worldOut = new File(folderTo, worldName);
|
||||
|
||||
// Level dat
|
||||
File levelDat = new File(folderFrom, "level.dat");
|
||||
copyLevelDat(levelDat);
|
||||
|
||||
|
||||
// Chunks
|
||||
MCAQueue queue = new MCAQueue(worldName, new File(worldOut, "region"), true);
|
||||
RemapFilter filter = new RemapFilter(this.remapper);
|
||||
db.forEach(entry -> {
|
||||
byte[] key = entry.getKey();
|
||||
if (key.length != 10) {
|
||||
Tag tag = Tag.get(key);
|
||||
if (tag == null) {
|
||||
if (key.length > 8) {
|
||||
String name = new String(key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// byte[] value = entry.getValue();
|
||||
int cx = tag.getX(key);
|
||||
int cz = tag.getZ(key);
|
||||
byte[] value = entry.getValue();
|
||||
MCAChunk chunk = (MCAChunk) queue.getFaweChunk(cx, cz);
|
||||
|
||||
switch (tag) {
|
||||
case Data2D:
|
||||
// height
|
||||
ByteBuffer buffer = ByteBuffer.wrap(value);
|
||||
int[] heightArray = chunk.getHeightMapArray();
|
||||
for (int i = 0, j = 0; i < heightArray.length; i++, j += 2) {
|
||||
heightArray[i] = buffer.getShort();
|
||||
}
|
||||
// biome
|
||||
int biomeOffset = (heightArray.length << 1);
|
||||
if (value.length > biomeOffset) {
|
||||
System.arraycopy(value, biomeOffset, chunk.biomes, 0, chunk.biomes.length);
|
||||
}
|
||||
break;
|
||||
case SubChunkPrefix:
|
||||
int layer = key[9];
|
||||
byte[] ids = getOrCreate(chunk.ids, layer, 4096);
|
||||
byte[] data = getOrCreate(chunk.data, layer, 2048);
|
||||
byte[] blockLight = getOrCreate(chunk.blockLight, layer, 2048);
|
||||
byte[] skyLight = getOrCreate(chunk.skyLight, layer, 2048);
|
||||
|
||||
copySection(ids, value, 1);
|
||||
copySection(data, value, 1 + 4096);
|
||||
copySection(skyLight, value, 1 + 4096 + 2048);
|
||||
copySection(blockLight, value, 1 + 4096 + 2048 + 2048);
|
||||
|
||||
chunk.filterBlocks(new MutableMCABackedBaseBlock(), filter);
|
||||
break;
|
||||
case BlockEntity:
|
||||
break;
|
||||
case Entity:
|
||||
break;
|
||||
// Ignore
|
||||
case LegacyTerrain:
|
||||
case BiomeState:
|
||||
case Data2DLegacy:
|
||||
System.out.println("Legacy terrain not supported, please update.");
|
||||
case FinalizedState:
|
||||
case PendingTicks:
|
||||
case BlockExtraData:
|
||||
case Version:
|
||||
break;
|
||||
}
|
||||
});
|
||||
// TODO
|
||||
MCAQueueMap map = (MCAQueueMap) queue.getFaweQueueMap();
|
||||
while (map.next(0, Long.MAX_VALUE));
|
||||
queue.clear();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
close();
|
||||
app.prompt("Compaction complete!");
|
||||
}
|
||||
}
|
||||
|
||||
private void copySection(byte[] dest, byte[] src, int srcPos) {
|
||||
if (src.length <= srcPos) return;
|
||||
switch (dest.length) {
|
||||
case 4096: {
|
||||
int index = 0;
|
||||
int i1, i2, i3;
|
||||
for (int y = 0; y < 16; y++) {
|
||||
i1 = y;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
i2 = i1 + (z << 4);
|
||||
for (int x = 0; x < 16; x++) {
|
||||
i3 = i2 + (x << 8);
|
||||
dest[index] = src[srcPos + i3];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2048: {
|
||||
int index = 0;
|
||||
int i1, i2, i3, i4;
|
||||
for (int x = 0; x < 16;) {
|
||||
{
|
||||
i1 = x;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
i2 = i1 + (z << 4);
|
||||
for (int y = 0; y < 16; y += 2) {
|
||||
i3 = i2 + (y << 8);
|
||||
i4 = i2 + ((y + 1) << 8);
|
||||
byte newVal = (byte) ((src[srcPos + (i3 >> 1)] & 0xF) + ((src[srcPos + (i4 >> 1)] & 0xF) << 4));
|
||||
dest[index] = newVal;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
x++;
|
||||
{
|
||||
i1 = x;
|
||||
for (int z = 0; z < 16; z++) {
|
||||
i2 = i1 + (z << 4);
|
||||
for (int y = 0; y < 16; y += 2) {
|
||||
i3 = i2 + (y << 8);
|
||||
i4 = i2 + ((y + 1) << 8);
|
||||
byte newVal = (byte) (((src[srcPos + (i3 >> 1)] & 0xF0) >> 4) + ((src[srcPos + (i4 >> 1)] & 0xF0)));
|
||||
dest[index] = newVal;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
x++;
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
System.arraycopy(src, srcPos, dest, 0, dest.length);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getOrCreate(byte[][] arr, int index, int len) {
|
||||
byte[] data = arr[index];
|
||||
if (data == null) {
|
||||
arr[index] = data = new byte[len];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void copyLevelDat(File in) throws IOException {
|
||||
File levelDat = new File(folderTo, "level.dat");
|
||||
if (!levelDat.exists()) {
|
||||
levelDat.createNewFile();
|
||||
}
|
||||
try (LittleEndianDataInputStream ledis = new LittleEndianDataInputStream(new FileInputStream(in))) {
|
||||
int version = ledis.readInt(); // Ignored
|
||||
int length = ledis.readInt(); // Ignored
|
||||
NBTInputStream nis = new NBTInputStream((DataInput) ledis);
|
||||
NamedTag named = nis.readNamedTag();
|
||||
com.sk89q.jnbt.CompoundTag tag = (CompoundTag) named.getTag();
|
||||
Map<String, com.sk89q.jnbt.Tag> map = ReflectionUtils.getMap(tag.getValue());
|
||||
|
||||
Map<String, String> gameRules = new HashMap<>();
|
||||
gameRules.put("firedamage", "firedamage");
|
||||
gameRules.put("falldamage", "falldamage");
|
||||
gameRules.put("dofiretick", "doFireTick");
|
||||
gameRules.put("drowningdamage", "drowningdamage");
|
||||
gameRules.put("doentitydrops", "doEntityDrops");
|
||||
gameRules.put("keepinventory", "keepInventory");
|
||||
gameRules.put("sendcommandfeedback", "sendCommandFeedback");
|
||||
gameRules.put("dodaylightcycle", "doDaylightCycle");
|
||||
gameRules.put("commandblockoutput", "commandBlockOutput");
|
||||
gameRules.put("domobloot", "doMobLoot");
|
||||
gameRules.put("domobspawning", "doMobSpawning");
|
||||
gameRules.put("doweathercycle", "doWeatherCycle");
|
||||
gameRules.put("mobgriefing", "mobGriefing");
|
||||
gameRules.put("dotiledrops", "doTileDrops");
|
||||
|
||||
HashMap<String, com.sk89q.jnbt.Tag> ruleTagValue = new HashMap<>();
|
||||
for (Map.Entry<String, String> rule : gameRules.entrySet()) {
|
||||
com.sk89q.jnbt.Tag value = map.remove(rule.getKey());
|
||||
if (value instanceof ByteTag) {
|
||||
value = new StringTag((Byte) value.getValue() == 1 ? "true" : "false");
|
||||
}
|
||||
ruleTagValue.put(rule.getValue(), value);
|
||||
}
|
||||
|
||||
HashSet<String> allowed = new HashSet<>(Arrays.asList(
|
||||
"lightningTime", "pvp", "LevelName", "Difficulty", "GameType", "Generator", "LastPlayed", "RandomSeed", "StorageVersion", "Time", "commandsEnabled", "currentTick", "rainTime", "SpawnX", "SpawnY", "SpawnZ", "SizeOnDisk"
|
||||
));
|
||||
Iterator<Map.Entry<String, com.sk89q.jnbt.Tag>> iterator = map.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, com.sk89q.jnbt.Tag> entry = iterator.next();
|
||||
if (!allowed.contains(entry.getKey())) {
|
||||
System.out.println("TODO (Unsupported): " + entry.getKey() + " | " + entry.getValue());
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
map.put("GameRules", new CompoundTag(ruleTagValue));
|
||||
|
||||
map.put("version", new IntTag(19133));
|
||||
map.put("DataVersion", new IntTag(1343));
|
||||
map.put("initialized", new ByteTag((byte) 1));
|
||||
map.putIfAbsent("SizeOnDisk", new LongTag(0));
|
||||
|
||||
// generator
|
||||
int generator = tag.getInt("Generator");
|
||||
String name;
|
||||
switch (generator) {
|
||||
default:
|
||||
case 1:
|
||||
name = "default";
|
||||
break;
|
||||
case 2:
|
||||
name = "flat";
|
||||
break;
|
||||
}
|
||||
map.put("generatorName", new StringTag(name));
|
||||
map.put("generatorOptions", new StringTag(""));
|
||||
map.put("generatorVersion", new IntTag(1));
|
||||
map.put("Difficulty", new ByteTag((byte) tag.getInt("Difficulty")));
|
||||
map.put("DifficultyLocked", new ByteTag((byte) 0));
|
||||
map.put("MapFeatures", new ByteTag((byte) 1));
|
||||
map.put("allowCommands", new ByteTag(tag.getByte("commandsEnabled")));
|
||||
long time = tag.getLong("Time");
|
||||
if (time == 0) time = tag.getLong("CurrentTick");
|
||||
map.put("Time", new LongTag(time));
|
||||
map.put("spawnMobs", new ByteTag((byte) 1));
|
||||
Long lastPlayed = tag.getLong("LastPlayed");
|
||||
if (lastPlayed != null && lastPlayed < Integer.MAX_VALUE) {
|
||||
lastPlayed = lastPlayed * 1000;
|
||||
map.put("LastPlayed", new LongTag(lastPlayed));
|
||||
}
|
||||
|
||||
HashMap<String, com.sk89q.jnbt.Tag> data = new HashMap<>();
|
||||
data.put("Data", new CompoundTag(map));
|
||||
CompoundTag root = new CompoundTag(data);
|
||||
|
||||
try (NBTOutputStream nos = new NBTOutputStream(new PGZIPOutputStream(new FileOutputStream(levelDat)))) {
|
||||
nos.writeNamedTag("level.dat", root);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import com.boydti.fawe.config.BBC;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAChunk;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAFile;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAFilter;
|
||||
import com.boydti.fawe.jnbt.anvil.MCAQueue;
|
||||
import com.boydti.fawe.jnbt.anvil.filters.DelegateMCAFilter;
|
||||
import com.boydti.fawe.jnbt.anvil.filters.RemapFilter;
|
||||
import com.boydti.fawe.object.RunnableVal;
|
||||
@ -26,7 +27,6 @@ import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.world.registry.BundledBlockData;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -49,7 +49,7 @@ import org.iq80.leveldb.DB;
|
||||
import org.iq80.leveldb.Options;
|
||||
import org.iq80.leveldb.impl.Iq80DBFactory;
|
||||
|
||||
public class MCAFile2LevelDB implements Closeable {
|
||||
public class MCAFile2LevelDB extends MapConverter {
|
||||
|
||||
private final ByteStore bufFinalizedState = new ByteStore(4);
|
||||
private final ByteStore keyStore9 = new ByteStore(9);
|
||||
@ -63,26 +63,25 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
private final DB db;
|
||||
private final ClipboardRemapper remapper;
|
||||
private final ForkJoinPool pool;
|
||||
private final File folder;
|
||||
private boolean closed;
|
||||
private LongAdder submitted = new LongAdder();
|
||||
|
||||
private boolean remap;
|
||||
|
||||
public MCAFile2LevelDB(File folder) {
|
||||
public MCAFile2LevelDB(File folderFrom, File folderTo) {
|
||||
super(folderFrom, folderTo);
|
||||
try {
|
||||
this.folder = folder;
|
||||
if (!folder.exists()) {
|
||||
folder.mkdirs();
|
||||
if (!folderTo.exists()) {
|
||||
folderTo.mkdirs();
|
||||
}
|
||||
String worldName = folder.getName();
|
||||
try (PrintStream out = new PrintStream(new FileOutputStream(new File(folder, "levelname.txt")))) {
|
||||
String worldName = folderTo.getName();
|
||||
try (PrintStream out = new PrintStream(new FileOutputStream(new File(folderTo, "levelname.txt")))) {
|
||||
out.print(worldName);
|
||||
}
|
||||
this.pool = new ForkJoinPool();
|
||||
this.remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE);
|
||||
BundledBlockData.getInstance().loadFromResource();
|
||||
this.db = Iq80DBFactory.factory.open(new File(folder, "db"),
|
||||
this.db = Iq80DBFactory.factory.open(new File(folderTo, "db"),
|
||||
new Options()
|
||||
.createIfMissing(true)
|
||||
.verifyChecksums(false)
|
||||
@ -116,6 +115,37 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void accept(ConverterFrame app) {
|
||||
MCAFilter filter = toFilter();
|
||||
MCAQueue queue = new MCAQueue(null, new File(folderFrom, "region"), true);
|
||||
MCAFilter result = queue.filterWorld(filter);
|
||||
|
||||
File levelDat = new File(folderFrom, "level.dat");
|
||||
if (levelDat.exists()) {
|
||||
try {
|
||||
copyLevelDat(levelDat);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
close();
|
||||
app.prompt(
|
||||
"Conversion complete!\n" +
|
||||
" - The world save is still being compacted, but you can close the program anytime\n" +
|
||||
" - There will be another prompt when this finishes\n" +
|
||||
"\n" +
|
||||
"What is not converted?\n" +
|
||||
" - Inventory is not copied\n" +
|
||||
" - Some block nbt may not copy\n" +
|
||||
" - Any custom generator settings may not work\n" +
|
||||
" - May not match up with new terrain"
|
||||
);
|
||||
compact();
|
||||
app.prompt("Compaction complete!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
@ -133,7 +163,7 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
|
||||
public void compact() {
|
||||
// Since the library doesn't support it, only way to flush the cache is to loop over everything
|
||||
try (DB newDb = Iq80DBFactory.factory.open(new File(folder, "db"), new Options()
|
||||
try (DB newDb = Iq80DBFactory.factory.open(new File(folderTo, "db"), new Options()
|
||||
.verifyChecksums(false)
|
||||
.blockSize(262144) // 256K
|
||||
.cacheSize(8388608) // 8MB
|
||||
@ -147,7 +177,7 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
}
|
||||
|
||||
public void copyLevelDat(File in) throws IOException {
|
||||
File levelDat = new File(folder, "level.dat");
|
||||
File levelDat = new File(folderTo, "level.dat");
|
||||
try (NBTInputStream nis = new NBTInputStream(new GZIPInputStream(new FileInputStream(in)))) {
|
||||
if (!levelDat.exists()) {
|
||||
levelDat.createNewFile();
|
||||
@ -175,7 +205,7 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
map.put(key, new ByteTag((byte) (value.equals("true") ? 1 : 0)));
|
||||
}
|
||||
}
|
||||
map.put("LevelName", new StringTag(folder.getName()));
|
||||
map.put("LevelName", new StringTag(folderTo.getName()));
|
||||
map.put("StorageVersion", new IntTag(5));
|
||||
Byte difficulty = tag.getByte("Difficulty");
|
||||
map.put("Difficulty", new IntTag(difficulty == null ? 2 : difficulty));
|
||||
@ -300,7 +330,6 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
}
|
||||
|
||||
private void copySection(byte[] src, byte[] dest, int destPos) {
|
||||
int len = src.length;
|
||||
switch (src.length) {
|
||||
case 4096: {
|
||||
int index = 0;
|
||||
@ -355,41 +384,7 @@ public class MCAFile2LevelDB implements Closeable {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
System.arraycopy(src, 0, dest, destPos, len);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Tag {
|
||||
Data2D(45),
|
||||
@Deprecated Data2DLegacy(46),
|
||||
SubChunkPrefix(47),
|
||||
@Deprecated LegacyTerrain(48),
|
||||
BlockEntity(49),
|
||||
Entity(50),
|
||||
PendingTicks(51),
|
||||
BlockExtraData(52),
|
||||
BiomeState(53),
|
||||
FinalizedState(54),
|
||||
Version(118),
|
||||
|
||||
;
|
||||
public final byte value;
|
||||
|
||||
Tag(int value) {
|
||||
this.value = (byte) value;
|
||||
}
|
||||
|
||||
public byte[] fill(int chunkX, int chunkZ, byte[] key) {
|
||||
key[0] = (byte) (chunkX & 255);
|
||||
key[1] = (byte) (chunkX >>> 8 & 255);
|
||||
key[2] = (byte) (chunkX >>> 16 & 255);
|
||||
key[3] = (byte) (chunkX >>> 24 & 255);
|
||||
key[4] = (byte) (chunkZ & 255);
|
||||
key[5] = (byte) (chunkZ >>> 8 & 255);
|
||||
key[6] = (byte) (chunkZ >>> 16 & 255);
|
||||
key[7] = (byte) (chunkZ >>> 24 & 255);
|
||||
key[8] = value;
|
||||
return key;
|
||||
System.arraycopy(src, 0, dest, destPos, src.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,79 @@
|
||||
package com.boydti.fawe.nukkit.core.converter;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class MapConverter implements Consumer<ConverterFrame>, Closeable {
|
||||
|
||||
protected final File folderTo;
|
||||
protected final File folderFrom;
|
||||
|
||||
public MapConverter(File worldFrom, File worldTo) {
|
||||
this.folderFrom = worldFrom;
|
||||
this.folderTo = worldTo;
|
||||
}
|
||||
|
||||
public static MapConverter get(File worldFrom, File worldTo) {
|
||||
if (new File(worldFrom, "db").exists()) {
|
||||
return new LevelDBToMCAFile(worldFrom, worldTo);
|
||||
} else {
|
||||
return new MCAFile2LevelDB(worldFrom, worldTo);
|
||||
}
|
||||
}
|
||||
|
||||
private static Tag[] tags = new Tag[256];
|
||||
|
||||
public enum Tag {
|
||||
Data2D(45),
|
||||
@Deprecated Data2DLegacy(46),
|
||||
SubChunkPrefix(47),
|
||||
@Deprecated LegacyTerrain(48),
|
||||
BlockEntity(49),
|
||||
Entity(50),
|
||||
PendingTicks(51),
|
||||
BlockExtraData(52),
|
||||
BiomeState(53),
|
||||
FinalizedState(54),
|
||||
Version(118),
|
||||
|
||||
;
|
||||
|
||||
public final byte value;
|
||||
|
||||
Tag(int value) {
|
||||
this.value = (byte) value;
|
||||
tags[value & 0xFF] = this;
|
||||
}
|
||||
|
||||
public static Tag valueOf(byte key) {
|
||||
return tags[key & 0xFF];
|
||||
}
|
||||
|
||||
public static Tag get(byte[] key) {
|
||||
if (key.length != 9 && key.length != 10) return null;
|
||||
return valueOf(key[8]);
|
||||
}
|
||||
|
||||
public int getX(byte[] key) {
|
||||
return ((key[3] & 0xFF << 24) + (key[2] & 0xFF << 16) + (key[1] & 0xFF << 8) + (key[0] & 0xFF << 0));
|
||||
}
|
||||
|
||||
public int getZ(byte[] key) {
|
||||
return ((key[7] & 0xFF << 24) + (key[6] & 0xFF << 16) + (key[5] & 0xFF << 8) + (key[4] & 0xFF << 0));
|
||||
}
|
||||
|
||||
public byte[] fill(int chunkX, int chunkZ, byte[] key) {
|
||||
key[0] = (byte) (chunkX & 255);
|
||||
key[1] = (byte) (chunkX >>> 8 & 255);
|
||||
key[2] = (byte) (chunkX >>> 16 & 255);
|
||||
key[3] = (byte) (chunkX >>> 24 & 255);
|
||||
key[4] = (byte) (chunkZ & 255);
|
||||
key[5] = (byte) (chunkZ >>> 8 & 255);
|
||||
key[6] = (byte) (chunkZ >>> 16 & 255);
|
||||
key[7] = (byte) (chunkZ >>> 24 & 255);
|
||||
key[8] = value;
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user