Progress, lower memory usage

This commit is contained in:
Jesse Boyd 2017-10-09 19:41:44 +11:00
parent 911f613f2f
commit b2395f7aa9
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
7 changed files with 1220 additions and 187 deletions

View File

@ -1,7 +1,6 @@
package com.boydti.fawe.object.clipboard; package com.boydti.fawe.object.clipboard;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
@ -58,7 +57,6 @@ public class ItemWikiScraper {
} }
private Map<String, Integer> scrape(ClipboardRemapper.RemapPlatform platform) throws IOException { private Map<String, Integer> scrape(ClipboardRemapper.RemapPlatform platform) throws IOException {
Fawe.debug("Downloading item mappings for " + platform + ". Please wait...");
String url = (platform == ClipboardRemapper.RemapPlatform.PC) ? PC : PE; String url = (platform == ClipboardRemapper.RemapPlatform.PC) ? PC : PE;
String text = MainUtil.getText(url); String text = MainUtil.getText(url);
@ -96,7 +94,6 @@ public class ItemWikiScraper {
} }
id++; id++;
} }
Fawe.debug("Download complete.");
return map; return map;
} }
} }

View File

@ -0,0 +1,409 @@
package com.sk89q.jnbt;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The {@code TAG_Compound} tag.
*/
public final class CompoundTag extends Tag {
private final Map<String, Tag> value;
/**
* Creates the tag with an empty name.
*
* @param value the value of the tag
*/
public CompoundTag(Map<String, Tag> value) {
super();
this.value = Collections.unmodifiableMap(value);
}
@Override
public Map<String, Object> getRaw() {
HashMap<String, Object> raw = new HashMap<>();
for (Map.Entry<String, Tag> entry : value.entrySet()) {
raw.put(entry.getKey(), entry.getValue().getRaw());
}
return raw;
}
/**
* Returns whether this compound tag contains the given key.
*
* @param key the given key
* @return true if the tag contains the given key
*/
public boolean containsKey(String key) {
return value.containsKey(key);
}
@Override
public Map<String, Tag> getValue() {
return value;
}
/**
* Return a new compound tag with the given values.
*
* @param value the value
* @return the new compound tag
*/
public CompoundTag setValue(Map<String, Tag> value) {
return new CompoundTag(value);
}
/**
* Create a compound tag builder.
*
* @return the builder
*/
public CompoundTagBuilder createBuilder() {
return new CompoundTagBuilder(new HashMap<String, Tag>(value));
}
/**
* Get a byte array named with the given key.
*
* <p>If the key does not exist or its value is not a byte array tag,
* then an empty byte array will be returned.</p>
*
* @param key the key
* @return a byte array
*/
public byte[] getByteArray(String key) {
Tag tag = value.get(key);
if (tag instanceof ByteArrayTag) {
return ((ByteArrayTag) tag).getValue();
} else {
return new byte[0];
}
}
/**
* Get a byte named with the given key.
*
* <p>If the key does not exist or its value is not a byte tag,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a byte
*/
public byte getByte(String key) {
Tag tag = value.get(key);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else {
return (byte) 0;
}
}
/**
* Get a double named with the given key.
*
* <p>If the key does not exist or its value is not a double tag,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a double
*/
public double getDouble(String key) {
Tag tag = value.get(key);
if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a double named with the given key, even if it's another
* type of number.
*
* <p>If the key does not exist or its value is not a number,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a double
*/
public double asDouble(String key) {
Tag tag = value.get(key);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else if (tag instanceof LongTag) {
return ((LongTag) tag).getValue();
} else if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue();
} else if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a float named with the given key.
*
* <p>If the key does not exist or its value is not a float tag,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a float
*/
public float getFloat(String key) {
Tag tag = value.get(key);
if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a {@code int[]} named with the given key.
*
* <p>If the key does not exist or its value is not an int array tag,
* then an empty array will be returned.</p>
*
* @param key the key
* @return an int array
*/
public int[] getIntArray(String key) {
Tag tag = value.get(key);
if (tag instanceof IntArrayTag) {
return ((IntArrayTag) tag).getValue();
} else {
return new int[0];
}
}
/**
* Get an int named with the given key.
*
* <p>If the key does not exist or its value is not an int tag,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return an int
*/
public int getInt(String key) {
Tag tag = value.get(key);
if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get an int named with the given key, even if it's another
* type of number.
*
* <p>If the key does not exist or its value is not a number,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return an int
*/
public int asInt(String key) {
Tag tag = value.get(key);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else if (tag instanceof LongTag) {
return ((LongTag) tag).getValue().intValue();
} else if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue().intValue();
} else if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue().intValue();
} else {
return 0;
}
}
/**
* Get a list of tags named with the given key.
*
* <p>If the key does not exist or its value is not a list tag,
* then an empty list will be returned.</p>
*
* @param key the key
* @return a list of tags
*/
public List<Tag> getList(String key) {
Tag tag = value.get(key);
if (tag instanceof ListTag) {
return ((ListTag) tag).getValue();
} else {
return Collections.emptyList();
}
}
/**
* Get a {@code TagList} named with the given key.
*
* <p>If the key does not exist or its value is not a list tag,
* then an empty tag list will be returned.</p>
*
* @param key the key
* @return a tag list instance
*/
public ListTag getListTag(String key) {
Tag tag = value.get(key);
if (tag instanceof ListTag) {
return (ListTag) tag;
} else {
return new ListTag(StringTag.class, Collections.<Tag>emptyList());
}
}
/**
* Get a list of tags named with the given key.
*
* <p>If the key does not exist or its value is not a list tag,
* then an empty list will be returned. If the given key references
* a list but the list of of a different type, then an empty
* list will also be returned.</p>
*
* @param key the key
* @param listType the class of the contained type
* @return a list of tags
* @param <T> the type of list
*/
@SuppressWarnings("unchecked")
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
Tag tag = value.get(key);
if (tag instanceof ListTag) {
ListTag listTag = (ListTag) tag;
if (listTag.getType().equals(listType)) {
return (List<T>) listTag.getValue();
} else {
return Collections.emptyList();
}
} else {
return Collections.emptyList();
}
}
/**
* Get a long named with the given key.
*
* <p>If the key does not exist or its value is not a long tag,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a long
*/
public long getLong(String key) {
Tag tag = value.get(key);
if (tag instanceof LongTag) {
return ((LongTag) tag).getValue();
} else {
return 0L;
}
}
/**
* Get a long named with the given key, even if it's another
* type of number.
*
* <p>If the key does not exist or its value is not a number,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a long
*/
public long asLong(String key) {
Tag tag = value.get(key);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else if (tag instanceof LongTag) {
return ((LongTag) tag).getValue();
} else if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue().longValue();
} else if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue().longValue();
} else {
return 0L;
}
}
/**
* Get a short named with the given key.
*
* <p>If the key does not exist or its value is not a short tag,
* then {@code 0} will be returned.</p>
*
* @param key the key
* @return a short
*/
public short getShort(String key) {
Tag tag = value.get(key);
if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a string named with the given key.
*
* <p>If the key does not exist or its value is not a string tag,
* then {@code ""} will be returned.</p>
*
* @param key the key
* @return a string
*/
public String getString(String key) {
Tag tag = value.get(key);
if (tag instanceof StringTag) {
return ((StringTag) tag).getValue();
} else {
return "";
}
}
@Override
public String toString() {
StringBuilder bldr = new StringBuilder();
bldr.append("TAG_Compound").append(": ").append(value.size()).append(" entries\r\n{\r\n");
for (Map.Entry<String, Tag> entry : value.entrySet()) {
bldr.append(" ").append(entry.getValue().toString().replaceAll("\r\n", "\r\n ")).append("\r\n");
}
bldr.append("}");
return bldr.toString();
}
}

View File

@ -0,0 +1,422 @@
package com.sk89q.jnbt;
import java.util.ArrayList;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* The {@code TAG_List} tag.
*/
public final class ListTag extends Tag {
private final Class<? extends Tag> type;
private final List<Tag> value;
/**
* Creates the tag with an empty name.
*
* @param type the type of tag
* @param value the value of the tag
*/
public ListTag(Class<? extends Tag> type, List<? extends Tag> value) {
super();
checkNotNull(value);
this.type = type;
this.value = Collections.unmodifiableList(value);
}
@Override
public List<Object> getRaw() {
ArrayList<Object> raw = new ArrayList<>();
for (Tag t : value) {
raw.add(t.getRaw());
}
return raw;
}
/**
* Gets the type of item in this list.
*
* @return The type of item in this list.
*/
public Class<? extends Tag> getType() {
return type;
}
@Override
public List<Tag> getValue() {
return value;
}
/**
* Create a new list tag with this tag's name and type.
*
* @param list the new list
* @return a new list tag
*/
public ListTag setValue(List<Tag> list) {
return new ListTag(getType(), list);
}
/**
* Get the tag if it exists at the given index.
*
* @param index the index
* @return the tag or null
*/
@Nullable
public Tag getIfExists(int index) {
try {
return value.get(index);
} catch (NoSuchElementException e) {
return null;
}
}
/**
* Get a byte array named with the given index.
*
* <p>If the index does not exist or its value is not a byte array tag,
* then an empty byte array will be returned.</p>
*
* @param index the index
* @return a byte array
*/
public byte[] getByteArray(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ByteArrayTag) {
return ((ByteArrayTag) tag).getValue();
} else {
return new byte[0];
}
}
/**
* Get a byte named with the given index.
*
* <p>If the index does not exist or its value is not a byte tag,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a byte
*/
public byte getByte(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else {
return (byte) 0;
}
}
/**
* Get a double named with the given index.
*
* <p>If the index does not exist or its value is not a double tag,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a double
*/
public double getDouble(int index) {
Tag tag = getIfExists(index);
if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a double named with the given index, even if it's another
* type of number.
*
* <p>If the index does not exist or its value is not a number,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a double
*/
public double asDouble(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else if (tag instanceof LongTag) {
return ((LongTag) tag).getValue();
} else if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue();
} else if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a float named with the given index.
*
* <p>If the index does not exist or its value is not a float tag,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a float
*/
public float getFloat(int index) {
Tag tag = getIfExists(index);
if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a {@code int[]} named with the given index.
*
* <p>If the index does not exist or its value is not an int array tag,
* then an empty array will be returned.</p>
*
* @param index the index
* @return an int array
*/
public int[] getIntArray(int index) {
Tag tag = getIfExists(index);
if (tag instanceof IntArrayTag) {
return ((IntArrayTag) tag).getValue();
} else {
return new int[0];
}
}
/**
* Get an int named with the given index.
*
* <p>If the index does not exist or its value is not an int tag,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return an int
*/
public int getInt(int index) {
Tag tag = getIfExists(index);
if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get an int named with the given index, even if it's another
* type of number.
*
* <p>If the index does not exist or its value is not a number,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return an int
*/
public int asInt(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else if (tag instanceof LongTag) {
return ((LongTag) tag).getValue().intValue();
} else if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue().intValue();
} else if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue().intValue();
} else {
return 0;
}
}
/**
* Get a list of tags named with the given index.
*
* <p>If the index does not exist or its value is not a list tag,
* then an empty list will be returned.</p>
*
* @param index the index
* @return a list of tags
*/
public List<Tag> getList(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ListTag) {
return ((ListTag) tag).getValue();
} else {
return Collections.emptyList();
}
}
/**
* Get a {@code TagList} named with the given index.
*
* <p>If the index does not exist or its value is not a list tag,
* then an empty tag list will be returned.</p>
*
* @param index the index
* @return a tag list instance
*/
public ListTag getListTag(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ListTag) {
return (ListTag) tag;
} else {
return new ListTag(StringTag.class, Collections.<Tag>emptyList());
}
}
/**
* Get a list of tags named with the given index.
*
* <p>If the index does not exist or its value is not a list tag,
* then an empty list will be returned. If the given index references
* a list but the list of of a different type, then an empty
* list will also be returned.</p>
*
* @param index the index
* @param listType the class of the contained type
* @return a list of tags
* @param <T> the NBT type
*/
@SuppressWarnings("unchecked")
public <T extends Tag> List<T> getList(int index, Class<T> listType) {
Tag tag = getIfExists(index);
if (tag instanceof ListTag) {
ListTag listTag = (ListTag) tag;
if (listTag.getType().equals(listType)) {
return (List<T>) listTag.getValue();
} else {
return Collections.emptyList();
}
} else {
return Collections.emptyList();
}
}
/**
* Get a long named with the given index.
*
* <p>If the index does not exist or its value is not a long tag,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a long
*/
public long getLong(int index) {
Tag tag = getIfExists(index);
if (tag instanceof LongTag) {
return ((LongTag) tag).getValue();
} else {
return 0L;
}
}
/**
* Get a long named with the given index, even if it's another
* type of number.
*
* <p>If the index does not exist or its value is not a number,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a long
*/
public long asLong(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ByteTag) {
return ((ByteTag) tag).getValue();
} else if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else if (tag instanceof IntTag) {
return ((IntTag) tag).getValue();
} else if (tag instanceof LongTag) {
return ((LongTag) tag).getValue();
} else if (tag instanceof FloatTag) {
return ((FloatTag) tag).getValue().longValue();
} else if (tag instanceof DoubleTag) {
return ((DoubleTag) tag).getValue().longValue();
} else {
return 0;
}
}
/**
* Get a short named with the given index.
*
* <p>If the index does not exist or its value is not a short tag,
* then {@code 0} will be returned.</p>
*
* @param index the index
* @return a short
*/
public short getShort(int index) {
Tag tag = getIfExists(index);
if (tag instanceof ShortTag) {
return ((ShortTag) tag).getValue();
} else {
return 0;
}
}
/**
* Get a string named with the given index.
*
* <p>If the index does not exist or its value is not a string tag,
* then {@code ""} will be returned.</p>
*
* @param index the index
* @return a string
*/
public String getString(int index) {
Tag tag = getIfExists(index);
if (tag instanceof StringTag) {
return ((StringTag) tag).getValue();
} else {
return "";
}
}
@Override
public String toString() {
StringBuilder bldr = new StringBuilder();
bldr.append("TAG_List").append(": ").append(value.size()).append(" entries of type ").append(NBTUtils.getTypeName(type)).append("\r\n{\r\n");
for (Tag t : value) {
bldr.append(" ").append(t.toString().replaceAll("\r\n", "\r\n ")).append("\r\n");
}
bldr.append("}");
return bldr.toString();
}
}

View File

@ -0,0 +1,38 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.jnbt;
/**
* Represents a NBT tag.
*/
public abstract class Tag {
/**
* Gets the value of this tag.
*
* @return the value
*/
public abstract Object getValue();
public Object getRaw() {
return getValue();
}
}

Binary file not shown.

View File

@ -11,6 +11,8 @@ import com.boydti.fawe.installer.MinimizeButton;
import com.boydti.fawe.installer.MovablePanel; import com.boydti.fawe.installer.MovablePanel;
import com.boydti.fawe.installer.TextAreaOutputStream; import com.boydti.fawe.installer.TextAreaOutputStream;
import com.boydti.fawe.installer.URLButton; import com.boydti.fawe.installer.URLButton;
import com.boydti.fawe.object.clipboard.ClipboardRemapper;
import com.boydti.fawe.object.clipboard.ItemWikiScraper;
import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.wrappers.FakePlayer; import com.boydti.fawe.wrappers.FakePlayer;
import java.awt.BorderLayout; import java.awt.BorderLayout;
@ -30,6 +32,10 @@ import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.JButton; import javax.swing.JButton;
@ -37,13 +43,17 @@ import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
public class ConverterFrame extends JFrame { public class ConverterFrame extends JFrame {
private final InvisiblePanel loggerPanel; private final InvisiblePanel loggerPanel;
private final JProgressBar progressBar;
private Color LIGHT_GRAY = new Color(0x66, 0x66, 0x66); private Color LIGHT_GRAY = new Color(0x66, 0x66, 0x66);
private Color GRAY = new Color(0x44, 0x44, 0x46); private Color GRAY = new Color(0x44, 0x44, 0x46);
private Color DARK_GRAY = new Color(0x33, 0x33, 0x36); private Color DARK_GRAY = new Color(0x33, 0x33, 0x36);
@ -56,6 +66,8 @@ public class ConverterFrame extends JFrame {
private BrowseButton browseSave; private BrowseButton browseSave;
public ConverterFrame() throws Exception { public ConverterFrame() throws Exception {
async(() -> downloadDependencies());
final MovablePanel movable = new MovablePanel(this); final MovablePanel movable = new MovablePanel(this);
movable.setBorder(BorderFactory.createLineBorder(new Color(0x28, 0x28, 0x29))); movable.setBorder(BorderFactory.createLineBorder(new Color(0x28, 0x28, 0x29)));
@ -69,23 +81,27 @@ public class ConverterFrame extends JFrame {
int y = (int) ((dimension.getHeight() - this.getHeight()) / 2); int y = (int) ((dimension.getHeight() - this.getHeight()) / 2);
this.setLocation(x, y); this.setLocation(x, y);
this.setVisible(true); this.setVisible(true);
this.setOpacity(0);
if (this.transparency(0)) {
fadeIn();
}
movable.setBackground(DARK_GRAY); movable.setBackground(DARK_GRAY);
movable.setLayout(new BorderLayout()); movable.setLayout(new BorderLayout());
fadeIn();
JPanel topBar = new InvisiblePanel(new BorderLayout()); JPanel topBar = new InvisiblePanel(new BorderLayout());
{ {
JPanel topBarLeft = new InvisiblePanel(); JPanel topBarLeft = new InvisiblePanel();
JPanel topBarCenter = new InvisiblePanel(); JPanel topBarCenter = new InvisiblePanel();
JPanel topBarRight = new InvisiblePanel(); JPanel topBarRight = new InvisiblePanel();
JLabel title = new JLabel("(FAWE) Anvil and LevelDB converter"); JLabel title = new JLabel();
title.setHorizontalAlignment(SwingConstants.CENTER); title.setHorizontalAlignment(SwingConstants.CENTER);
title.setAlignmentX(Component.RIGHT_ALIGNMENT); title.setAlignmentX(Component.RIGHT_ALIGNMENT);
title.setForeground(Color.LIGHT_GRAY); title.setForeground(Color.LIGHT_GRAY);
title.setText("(FAWE) Anvil and LevelDB converter");
title.setFont(new Font("Lucida Sans Unicode", Font.PLAIN, 15)); title.setFont(new Font("Lucida Sans Unicode", Font.PLAIN, 15));
setTitle(null);
MinimizeButton minimize = new MinimizeButton(this); MinimizeButton minimize = new MinimizeButton(this);
CloseButton exit = new CloseButton(); CloseButton exit = new CloseButton();
@ -195,8 +211,7 @@ public class ConverterFrame extends JFrame {
installContent.add(install); installContent.add(install);
installContent.setBorder(new EmptyBorder(10, 0, 10, 0)); installContent.setBorder(new EmptyBorder(10, 0, 10, 0));
this.loggerPanel = new InvisiblePanel(new BorderLayout()); this.loggerPanel = new InvisiblePanel(new BorderLayout());
this.loggerPanel.setBackground(Color.GREEN); loggerPanel.setPreferredSize(new Dimension(Integer.MAX_VALUE, 380));
loggerPanel.setPreferredSize(new Dimension(416, 442));
loggerTextArea = new JTextArea(); loggerTextArea = new JTextArea();
loggerTextArea.setBackground(Color.GRAY); loggerTextArea.setBackground(Color.GRAY);
loggerTextArea.setForeground(Color.DARK_GRAY); loggerTextArea.setForeground(Color.DARK_GRAY);
@ -208,10 +223,22 @@ public class ConverterFrame extends JFrame {
loggerPanel.add(scroll); loggerPanel.add(scroll);
loggerPanel.setVisible(false); loggerPanel.setVisible(false);
this.progressBar = new JProgressBar();
progressBar.setVisible(false);
progressBar.setStringPainted(true);
progressBar.setBackground(DARK_GRAY);
progressBar.setForeground(OFF_WHITE);
mainContent.setBorder(new EmptyBorder(6, 32, 6, 32)); mainContent.setBorder(new EmptyBorder(6, 32, 6, 32));
mainContent.add(browseContent, BorderLayout.NORTH); mainContent.add(browseContent, BorderLayout.NORTH);
mainContent.add(installContent, BorderLayout.CENTER); mainContent.add(installContent, BorderLayout.CENTER);
mainContent.add(loggerPanel, BorderLayout.SOUTH);
final JPanel console = new InvisiblePanel(new BorderLayout());
console.setBorder(new EmptyBorder(6, 0, 6, 0));
console.add(progressBar, BorderLayout.SOUTH);
console.add(loggerPanel, BorderLayout.NORTH);
mainContent.add(console, BorderLayout.SOUTH);
} }
JPanel bottomBar = new InvisiblePanel(); JPanel bottomBar = new InvisiblePanel();
{ {
@ -270,6 +297,47 @@ public class ConverterFrame extends JFrame {
this.repaint(); this.repaint();
} }
public void prompt(String message) {
JOptionPane.showMessageDialog(null, message);
Fawe.debug(message);
}
public void debug(String m) {
System.out.println(m);
}
public void setProgress(String text, int percent) {
Border border = BorderFactory.createTitledBorder(new EmptyBorder(0, 0, 0, 0), "Time remaining: " + text, TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Lucida Sans Unicode",Font.PLAIN,12), OFF_WHITE);
progressBar.setVisible(true);
progressBar.setBorder(border);
progressBar.setValue(percent);
repaint();
}
private void fadeIn() {
async(() -> {
for (float i = 0; i <= 1.015; i += 0.016) {
if (!transparency(Math.min(1, i))) return;
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (UnsupportedOperationException ignore) {
return;
}
}
});
}
private boolean transparency(float val) {
try {
super.setOpacity(val);
return true;
} catch (UnsupportedOperationException ignore) {
return false;
}
}
private File getDefaultOutput() { private File getDefaultOutput() {
if (MainUtil.getPlatform() == MainUtil.OS.WINDOWS) { if (MainUtil.getPlatform() == MainUtil.OS.WINDOWS) {
String applicationData = System.getenv("APPDATA"); String applicationData = System.getenv("APPDATA");
@ -281,16 +349,61 @@ public class ConverterFrame extends JFrame {
return new File("."); return new File(".");
} }
public void prompt(String message) { private void async(Runnable r) {
JOptionPane.showMessageDialog(null, message); new Thread(r).start();
Fawe.debug(message);
} }
public void debug(String m) { private AtomicBoolean dependenciesLoaded = new AtomicBoolean(false);
System.out.println(m); private void downloadDependencies() {
synchronized (dependenciesLoaded) {
if (dependenciesLoaded.get()) return;
try {
ExecutorService pool = Executors.newCachedThreadPool();
ItemWikiScraper scraper = new ItemWikiScraper();
File lib = new File("lib");
File leveldb = new File(lib, "leveldb_v1.jar");
URL levelDbUrl = new URL("https://git.io/vdZ9e");
pool.submit((Runnable) () -> {
try {
MainUtil.download(levelDbUrl, leveldb);
MainUtil.loadURLClasspath(leveldb.toURL());
} catch (IOException e) {
e.printStackTrace();
}
});
pool.submit((Runnable) () -> {
try {
scraper.scapeOrCache(ClipboardRemapper.RemapPlatform.PE);
} catch (IOException e) {
e.printStackTrace();
}
});
pool.submit((Runnable) () -> {
try {
scraper.scapeOrCache(ClipboardRemapper.RemapPlatform.PC);
} catch (IOException e) {
e.printStackTrace();
}
});
pool.shutdown();
try {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
dependenciesLoaded.set(true);
} catch (IOException e) {
e.printStackTrace();
}
}
} }
public void install(String input, String output) throws Exception { private void install(String input, String output) throws Exception {
if (!loggerPanel.isVisible()) { if (!loggerPanel.isVisible()) {
loggerPanel.setVisible(true); loggerPanel.setVisible(true);
this.repaint(); this.repaint();
@ -324,30 +437,10 @@ public class ConverterFrame extends JFrame {
public void run() { public void run() {
FakePlayer console = FakePlayer.getConsole(); FakePlayer console = FakePlayer.getConsole();
try { try {
debug("Loading leveldb.jar"); debug("Downloading levedb.jar and mappings (~4MB), please wait...");
downloadDependencies();
File lib = new File("lib");
File leveldb = new File(lib, "leveldb.jar");
URL levelDbUrl = new URL("https://git.io/vdZ9e");
// File blocksPE = new File(lib, "blocks-pe.json");
// File blocksPC = new File(lib, "blocks-pc.json");
// URL urlPE = new URL("https://git.io/vdZSj");
// URL urlPC = new URL("https://git.io/vdZSx");
MainUtil.download(levelDbUrl, leveldb);
// MainUtil.download(urlPE, blocksPC);
// MainUtil.download(urlPC, blocksPE);
MainUtil.loadURLClasspath(leveldb.toURL());
File newWorldFile = new File(output, dirMc.getName());
debug("Starting converter..."); debug("Starting converter...");
File newWorldFile = new File(output, dirMc.getName());
MapConverter converter = MapConverter.get(dirMc, newWorldFile); MapConverter converter = MapConverter.get(dirMc, newWorldFile);
converter.accept(ConverterFrame.this); converter.accept(ConverterFrame.this);
} catch (Throwable e) { } catch (Throwable e) {
@ -355,28 +448,13 @@ public class ConverterFrame extends JFrame {
prompt("[ERROR] Conversion failed, you will have to do it manually (Nukkit server + anvil2leveldb command)"); prompt("[ERROR] Conversion failed, you will have to do it manually (Nukkit server + anvil2leveldb command)");
return; return;
} }
System.gc();
System.gc();
} }
}); });
installThread.start(); installThread.start();
} }
public void fadeIn() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (float i = 0; i <= 1.015; i += 0.016) {
ConverterFrame.this.setOpacity(Math.min(1, i));
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
ConverterFrame window = new ConverterFrame(); ConverterFrame window = new ConverterFrame();
} }

View File

@ -12,6 +12,7 @@ import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.clipboard.ClipboardRemapper; import com.boydti.fawe.object.clipboard.ClipboardRemapper;
import com.boydti.fawe.object.io.LittleEndianOutputStream; import com.boydti.fawe.object.io.LittleEndianOutputStream;
import com.boydti.fawe.object.number.MutableLong; import com.boydti.fawe.object.number.MutableLong;
import com.boydti.fawe.util.MainUtil;
import com.boydti.fawe.util.MemUtil; import com.boydti.fawe.util.MemUtil;
import com.boydti.fawe.util.ReflectionUtils; import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.StringMan; import com.boydti.fawe.util.StringMan;
@ -52,6 +53,7 @@ import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import org.iq80.leveldb.CompressionType;
import org.iq80.leveldb.DB; import org.iq80.leveldb.DB;
import org.iq80.leveldb.Options; import org.iq80.leveldb.Options;
import org.iq80.leveldb.WriteBatch; import org.iq80.leveldb.WriteBatch;
@ -61,16 +63,22 @@ public class MCAFile2LevelDB extends MapConverter {
private final byte[] VERSION = new byte[] { 4 }; private final byte[] VERSION = new byte[] { 4 };
private final byte[] COMPLETE_STATE = new byte[] { 2, 0, 0, 0 }; private final byte[] COMPLETE_STATE = new byte[] { 2, 0, 0, 0 };
private DB db; private final DB db;
private final ClipboardRemapper remapper; private final ClipboardRemapper remapper;
private final ForkJoinPool pool; private final ForkJoinPool pool;
private boolean closed; private boolean closed;
private LongAdder submitted = new LongAdder(); private LongAdder submittedChunks = new LongAdder();
private LongAdder submittedFiles = new LongAdder();
private LongAdder totalOperations = new LongAdder();
private long estimatedOperations;
private long time; private long time;
private boolean remap; private boolean remap;
private final long startTime;
private ConverterFrame app;
private ConcurrentLinkedQueue<CompoundTag> portals = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue<CompoundTag> portals = new ConcurrentLinkedQueue<>();
private ConcurrentHashMap<Thread, WriteBatch> batches = new ConcurrentHashMap<Thread, WriteBatch>() { private ConcurrentHashMap<Thread, WriteBatch> batches = new ConcurrentHashMap<Thread, WriteBatch>() {
@ -79,7 +87,7 @@ public class MCAFile2LevelDB extends MapConverter {
public WriteBatch get(Object key) { public WriteBatch get(Object key) {
WriteBatch value = super.get(key); WriteBatch value = super.get(key);
if (value == null) { if (value == null) {
synchronized (batches) { synchronized (MCAFile2LevelDB.this) {
synchronized (Thread.currentThread()) { synchronized (Thread.currentThread()) {
value = db.createWriteBatch(); value = db.createWriteBatch();
put((Thread) key, value); put((Thread) key, value);
@ -92,6 +100,7 @@ public class MCAFile2LevelDB extends MapConverter {
public MCAFile2LevelDB(File folderFrom, File folderTo) { public MCAFile2LevelDB(File folderFrom, File folderTo) {
super(folderFrom, folderTo); super(folderFrom, folderTo);
this.startTime = System.currentTimeMillis();
try { try {
if (!folderTo.exists()) { if (!folderTo.exists()) {
folderTo.mkdirs(); folderTo.mkdirs();
@ -104,15 +113,61 @@ public class MCAFile2LevelDB extends MapConverter {
this.pool = new ForkJoinPool(); this.pool = new ForkJoinPool();
this.remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE); this.remapper = new ClipboardRemapper(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE);
BundledBlockData.getInstance().loadFromResource(); BundledBlockData.getInstance().loadFromResource();
flush(true);
int bufferSize = (int) Math.min(Integer.MAX_VALUE, Math.max((long) (MemUtil.getFreeBytes() * 0.8), 134217728));
this.db = Iq80DBFactory.factory.open(new File(folderTo, "db"),
new Options()
.createIfMissing(true)
.verifyChecksums(false)
.compressionType(CompressionType.ZLIB)
.blockSize(262144) // 256K
.cacheSize(8388608) // 8MB
.writeBufferSize(134217728) // >=512MB
);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
public void flush(boolean openDB) throws IOException { private double lastPercent;
private double lastTimeRatio;
private void progress(int increment) {
if (app == null) return;
totalOperations.add(increment);
long completedOps = totalOperations.longValue();
double percent = Math.max(0, Math.min(100, (Math.pow(completedOps, 1.5) * 100 / Math.pow(estimatedOperations, 1.5))));
double lastPercent = (this.lastPercent == 0 ? percent : this.lastPercent);
percent = (percent + lastPercent * 19) / 20;
if (increment != 0) this.lastPercent = percent;
double remaining = estimatedOperations - completedOps;
long timeSpent = System.currentTimeMillis() - startTime;
double totalTime = Math.pow(estimatedOperations, 1.5) * 1000;
double estimatedTimeSpent = Math.pow(completedOps, 1.5) * 1000;
double timeRemaining;
if (completedOps > 32) {
double timeRatio = (timeSpent * totalTime / estimatedTimeSpent);
double lastTimeRatio = this.lastTimeRatio == 0 ? timeRatio : this.lastTimeRatio;
timeRatio = (timeRatio + lastTimeRatio * 19) / 20;
if (increment != 0) this.lastTimeRatio = timeRatio;
timeRemaining = timeRatio - timeSpent;
} else {
timeRemaining = ((long) totalTime >> 5) - timeSpent;
}
String msg = MainUtil.secToTime((long) (timeRemaining / 1000));
app.setProgress(msg, (int) percent);
}
public synchronized void flush() throws IOException {
pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS); pool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
synchronized (batches) { progress(1);
synchronized (MCAFile2LevelDB.this) {
int count = pool.getParallelism();
Iterator<Map.Entry<Thread, WriteBatch>> iter = batches.entrySet().iterator(); Iterator<Map.Entry<Thread, WriteBatch>> iter = batches.entrySet().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
Map.Entry<Thread, WriteBatch> entry = iter.next(); Map.Entry<Thread, WriteBatch> entry = iter.next();
@ -121,29 +176,20 @@ public class MCAFile2LevelDB extends MapConverter {
db.write(batch); db.write(batch);
batch.close(); batch.close();
iter.remove(); iter.remove();
progress(2);
count--;
} }
} }
if (openDB) { System.gc();
Fawe.debug("Flushing changes, please wait"); System.gc();
if (db != null) db.close(); progress(count * 2);
System.gc();
System.gc();
int bufferSize = (int) Math.min(Integer.MAX_VALUE, Math.max((long) (MemUtil.getFreeBytes() * 0.8), 134217728));
this.db = Iq80DBFactory.factory.open(new File(folderTo, "db"),
new Options()
.createIfMissing(true)
.verifyChecksums(false)
.blockSize(262144) // 256K
.cacheSize(8388608) // 8MB
.writeBufferSize(536870912) // >=512MB
);
}
} }
} }
public DelegateMCAFilter<MutableLong> toFilter(final int dimension) { public DelegateMCAFilter<MutableLong> toFilter(final int dimension) {
RemapFilter filter = new RemapFilter(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE); RemapFilter filter = new RemapFilter(ClipboardRemapper.RemapPlatform.PC, ClipboardRemapper.RemapPlatform.PE);
filter.setDimension(dimension); filter.setDimension(dimension);
DelegateMCAFilter<MutableLong> delegate = new DelegateMCAFilter<MutableLong>(filter) { DelegateMCAFilter<MutableLong> delegate = new DelegateMCAFilter<MutableLong>(filter) {
@Override @Override
public void finishFile(MCAFile file, MutableLong cache) { public void finishFile(MCAFile file, MutableLong cache) {
@ -158,6 +204,16 @@ public class MCAFile2LevelDB extends MapConverter {
} }
}); });
file.clear(); file.clear();
progress(1);
submittedFiles.increment();
if ((submittedFiles.longValue() & 7) == 0) {
try {
flush();
} catch (IOException e) {
e.printStackTrace();
}
}
} }
}; };
return delegate; return delegate;
@ -166,6 +222,7 @@ public class MCAFile2LevelDB extends MapConverter {
@Override @Override
public void accept(ConverterFrame app) { public void accept(ConverterFrame app) {
this.app = app;
File levelDat = new File(folderFrom, "level.dat"); File levelDat = new File(folderFrom, "level.dat");
if (levelDat.exists()) { if (levelDat.exists()) {
try { try {
@ -177,12 +234,34 @@ public class MCAFile2LevelDB extends MapConverter {
List<CompoundTag> portals = new ArrayList<>(); List<CompoundTag> portals = new ArrayList<>();
String[] dimDirs = {"region", "DIM-1/region", "DIM1/region"}; String[] dimDirs = {"region", "DIM-1/region", "DIM1/region"};
File[] regionFolders = new File[dimDirs.length];
int totalFiles = 0;
for (int dim = 0; dim < 3; dim++) { for (int dim = 0; dim < 3; dim++) {
File source = new File(folderFrom, dimDirs[dim]); File source = new File(folderFrom, dimDirs[dim]);
if (source.exists()) { if (source.exists()) {
regionFolders[dim] = source;
totalFiles += source.listFiles().length;
}
}
this.estimatedOperations = totalFiles + (totalFiles >> 3) + (totalFiles >> 3) * pool.getParallelism() * 2;
Thread progressThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
progress(0);
} catch (InterruptedException e) {
return;
}
}
});
progressThread.start();
for (int dim = 0; dim < regionFolders.length; dim++) {
File source = regionFolders[dim];
if (source != null) {
DelegateMCAFilter filter = toFilter(dim); DelegateMCAFilter filter = toFilter(dim);
MCAQueue queue = new MCAQueue(null, source, true); MCAQueue queue = new MCAQueue(null, source, true);
MCAFilter result = queue.filterWorld(filter); MCAFilter result = queue.filterWorld(filter);
portals.addAll(((RemapFilter) filter.getFilter()).getPortals()); portals.addAll(((RemapFilter) filter.getFilter()).getPortals());
} }
@ -201,11 +280,13 @@ public class MCAFile2LevelDB extends MapConverter {
try { try {
flush(false); flush();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
progressThread.interrupt();
close(); close();
app.setProgress("Done", 0);
app.prompt( app.prompt(
"Conversion complete!\n" + "Conversion complete!\n" +
" - The world save is still being compacted, but you can close the program anytime\n" + " - The world save is still being compacted, but you can close the program anytime\n" +
@ -223,7 +304,7 @@ public class MCAFile2LevelDB extends MapConverter {
} }
@Override @Override
public void close() { public synchronized void close() {
try { try {
if (closed == (closed = true)) return; if (closed == (closed = true)) return;
Fawe.debug("Collecting threads"); Fawe.debug("Collecting threads");
@ -238,7 +319,7 @@ public class MCAFile2LevelDB extends MapConverter {
} }
} }
public void compact() { public synchronized void compact() {
// Since the library doesn't support it, only way to flush the cache is to loop over everything // 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(folderTo, "db"), new Options() try (DB newDb = Iq80DBFactory.factory.open(new File(folderTo, "db"), new Options()
.verifyChecksums(false) .verifyChecksums(false)
@ -315,8 +396,9 @@ public class MCAFile2LevelDB extends MapConverter {
} }
public void write(MCAChunk chunk, boolean remap, int dim) throws IOException { public void write(MCAChunk chunk, boolean remap, int dim) throws IOException {
submitted.add(1); submittedChunks.add(1);
if ((submitted.longValue() & 1023) == 0) { long numChunks = submittedChunks.longValue();
if ((numChunks & 1023) == 0) {
long queued = pool.getQueuedTaskCount() + pool.getQueuedSubmissionCount(); long queued = pool.getQueuedTaskCount() + pool.getQueuedSubmissionCount();
if (queued > 127) { if (queued > 127) {
System.gc(); System.gc();
@ -329,110 +411,106 @@ public class MCAFile2LevelDB extends MapConverter {
} }
} }
} }
if ((submitted.longValue() & 8191) == 0) { }
boolean reopen = (submitted.longValue() & 65535) == 0; synchronized (MCAFile2LevelDB.this) {
flush(reopen); try {
update(getKey(chunk, Tag.Version, dim), VERSION);
update(getKey(chunk, Tag.FinalizedState, dim), COMPLETE_STATE);
ByteBuffer data2d = ByteBuffer.wrap(new byte[512 + 256]);
int[] heightMap = chunk.getHeightMapArray();
for (int i = 0; i < heightMap.length; i++) {
data2d.putShort((short) heightMap[i]);
}
if (chunk.biomes != null) {
System.arraycopy(chunk.biomes, 0, data2d.array(), 512, 256);
}
update(getKey(chunk, Tag.Data2D, dim), data2d.array());
if (!chunk.tiles.isEmpty()) {
List<CompoundTag> tickList = null;
List<com.sk89q.jnbt.Tag> tiles = new ArrayList<>();
for (Map.Entry<Short, CompoundTag> entry : chunk.getTiles().entrySet()) {
CompoundTag tag = entry.getValue();
if (transform(chunk, tag) && time != 0l) {
// Needs tick
if (tickList == null) tickList = new ArrayList<>();
int x = tag.getInt("x");
int y = tag.getInt("y");
int z = tag.getInt("z");
BaseBlock block = chunk.getBlock(x & 15, y, z & 15);
Map<String, com.sk89q.jnbt.Tag> tickable = new HashMap<>();
tickable.put("tileID", new ByteTag((byte) block.getId()));
tickable.put("x", new IntTag(x));
tickable.put("y", new IntTag(y));
tickable.put("z", new IntTag(z));
tickable.put("time", new LongTag(1));
tickList.add(new CompoundTag(tickable));
}
tiles.add(tag);
}
update(getKey(chunk, Tag.BlockEntity, dim), write(tiles));
if (tickList != null) {
HashMap<String, com.sk89q.jnbt.Tag> root = new HashMap<String, com.sk89q.jnbt.Tag>();
root.put("tickList", new ListTag(CompoundTag.class, tickList));
update(getKey(chunk, Tag.PendingTicks, dim), write(Arrays.asList(new CompoundTag(root))));
}
}
if (!chunk.entities.isEmpty()) {
List<com.sk89q.jnbt.Tag> entities = new ArrayList<>();
for (CompoundTag tag : chunk.getEntities()) {
transform(chunk, tag);
entities.add(tag);
}
update(getKey(chunk, Tag.Entity, dim), write(entities));
}
int maxLayer = chunk.ids.length - 1;
while (maxLayer >= 0 && chunk.ids[maxLayer] == null) maxLayer--;
if (maxLayer >= 0) {
for (int layer = 0; layer <= maxLayer; layer++) {
// Set layer
byte[] key = getSectionKey(chunk, layer, dim);
byte[] value = new byte[1 + 4096 + 2048 + 2048 + 2048];
byte[] ids = chunk.ids[layer];
if (ids == null) {
Arrays.fill(value, (byte) 0);
} else {
byte[] data = chunk.data[layer];
byte[] skyLight = chunk.skyLight[layer];
byte[] blockLight = chunk.blockLight[layer];
if (remap) {
copySection(ids, value, 1);
copySection(data, value, 1 + 4096);
copySection(skyLight, value, 1 + 4096 + 2048);
copySection(blockLight, value, 1 + 4096 + 2048 + 2048);
} else {
System.arraycopy(ids, 0, value, 1, ids.length);
System.arraycopy(data, 0, value, 1 + 4096, data.length);
System.arraycopy(skyLight, 0, value, 1 + 4096 + 2048, skyLight.length);
System.arraycopy(blockLight, 0, value, 1 + 4096 + 2048 + 2048, blockLight.length);
}
}
update(key, value);
}
}
} catch (Throwable e) {
e.printStackTrace();
} }
} }
pool.submit((Runnable) () -> {
synchronized (Thread.currentThread()) {
try {
update(getKey(chunk, Tag.Version, dim), VERSION);
update(getKey(chunk, Tag.FinalizedState, dim), COMPLETE_STATE);
ByteBuffer data2d = ByteBuffer.wrap(new byte[512 + 256]);
int[] heightMap = chunk.getHeightMapArray();
for (int i = 0; i < heightMap.length; i++) {
data2d.putShort((short) heightMap[i]);
}
if (chunk.biomes != null) {
System.arraycopy(chunk.biomes, 0, data2d.array(), 512, 256);
}
update(getKey(chunk, Tag.Data2D, dim), data2d.array());
if (!chunk.tiles.isEmpty()) {
List<CompoundTag> tickList = null;
List<com.sk89q.jnbt.Tag> tiles = new ArrayList<>();
for (Map.Entry<Short, CompoundTag> entry : chunk.getTiles().entrySet()) {
CompoundTag tag = entry.getValue();
if (transform(chunk, tag) && time != 0l) {
// Needs tick
if (tickList == null) tickList = new ArrayList<>();
int x = tag.getInt("x");
int y = tag.getInt("y");
int z = tag.getInt("z");
BaseBlock block = chunk.getBlock(x & 15, y, z & 15);
Map<String, com.sk89q.jnbt.Tag> tickable = new HashMap<>();
tickable.put("tileID", new ByteTag((byte) block.getId()));
tickable.put("x", new IntTag(x));
tickable.put("y", new IntTag(y));
tickable.put("z", new IntTag(z));
tickable.put("time", new LongTag(1));
tickList.add(new CompoundTag(tickable));
}
tiles.add(tag);
}
update(getKey(chunk, Tag.BlockEntity, dim), write(tiles));
if (tickList != null) {
HashMap<String, com.sk89q.jnbt.Tag> root = new HashMap<String, com.sk89q.jnbt.Tag>();
root.put("tickList", new ListTag(CompoundTag.class, tickList));
update(getKey(chunk, Tag.PendingTicks, dim), write(Arrays.asList(new CompoundTag(root))));
}
}
if (!chunk.entities.isEmpty()) {
List<com.sk89q.jnbt.Tag> entities = new ArrayList<>();
for (CompoundTag tag : chunk.getEntities()) {
transform(chunk, tag);
entities.add(tag);
}
update(getKey(chunk, Tag.Entity, dim), write(entities));
}
int maxLayer = chunk.ids.length - 1;
while (maxLayer >= 0 && chunk.ids[maxLayer] == null) maxLayer--;
if (maxLayer >= 0) {
for (int layer = maxLayer; layer >= 0; layer--) {
// Set layer
byte[] key = getSectionKey(chunk, layer, dim);
byte[] value = new byte[1 + 4096 + 2048 + 2048 + 2048];
byte[] ids = chunk.ids[layer];
if (ids == null) {
Arrays.fill(value, (byte) 0);
} else {
byte[] data = chunk.data[layer];
byte[] skyLight = chunk.skyLight[layer];
byte[] blockLight = chunk.blockLight[layer];
if (remap) {
copySection(ids, value, 1);
copySection(data, value, 1 + 4096);
copySection(skyLight, value, 1 + 4096 + 2048);
copySection(blockLight, value, 1 + 4096 + 2048 + 2048);
} else {
System.arraycopy(ids, 0, value, 1, ids.length);
System.arraycopy(data, 0, value, 1 + 4096, data.length);
System.arraycopy(skyLight, 0, value, 1 + 4096 + 2048, skyLight.length);
System.arraycopy(blockLight, 0, value, 1 + 4096 + 2048 + 2048, blockLight.length);
}
}
update(key, value);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
});
} }
private void update(byte[] key, byte[] value) { private void update(byte[] key, byte[] value) {
WriteBatch batch = batches.get(Thread.currentThread()); synchronized (Thread.currentThread()) {
batch.put(key, value); WriteBatch batch = batches.get(Thread.currentThread());
batch.put(key, value);
}
} }
private void copySection(byte[] src, byte[] dest, int destPos) { private void copySection(byte[] src, byte[] dest, int destPos) {
@ -541,6 +619,9 @@ public class MCAFile2LevelDB extends MapConverter {
Map<String, com.sk89q.jnbt.Tag> map = ReflectionUtils.getMap(tag.getValue()); Map<String, com.sk89q.jnbt.Tag> map = ReflectionUtils.getMap(tag.getValue());
id = remapper.remapEntityId(id); id = remapper.remapEntityId(id);
map.put("id", new StringTag(id)); map.put("id", new StringTag(id));
if (map.containsKey("Pos")) {
map.put("id", new IntTag(11));
}
{ // Convert items { // Convert items
com.sk89q.jnbt.ListTag items = tag.getListTag("Items"); com.sk89q.jnbt.ListTag items = tag.getListTag("Items");
((List<CompoundTag>) (List) items.getValue()).forEach(this::transformItem); ((List<CompoundTag>) (List) items.getValue()).forEach(this::transformItem);
@ -555,13 +636,21 @@ public class MCAFile2LevelDB extends MapConverter {
} }
} }
{ // Health { // Health
com.sk89q.jnbt.Tag health = map.get("Health"); com.sk89q.jnbt.Tag tVal = map.get("Health");
if (health != null && health instanceof FloatTag) { if (tVal != null) {
map.put("Health", new ShortTag((short) tag.getFloat("Health"))); short newVal = ((Number) tVal.getValue()).shortValue();
map.put("Health", new ShortTag((short) (newVal * 2)));
}
}
for (String key : new String[] {"Age", "Health"}) {
com.sk89q.jnbt.Tag tVal = map.get(key);
if (tVal != null) {
short newVal = ((Number) tVal.getValue()).shortValue();
map.put(key, new ShortTag(newVal));
} }
} }
{ // Orientation / Position { // Orientation / Position
for (String key : new String[] {"Orientation", "Position"}) { for (String key : new String[] {"Orientation", "Position", "Rotation", "Pos", "Motion"}) {
ListTag list = (ListTag) map.get(key); ListTag list = (ListTag) map.get(key);
if (list != null) { if (list != null) {
List<com.sk89q.jnbt.Tag> value = list.getValue(); List<com.sk89q.jnbt.Tag> value = list.getValue();