Add more schematic compat, cleanup.

This commit is contained in:
wizjany 2019-04-20 12:13:58 -04:00 committed by Matthew Miller
parent bfc1fd8fd0
commit 526b3366b5
8 changed files with 230 additions and 128 deletions

View File

@ -34,7 +34,9 @@ import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.FlowerPotCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NoteBlockCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
@ -63,8 +65,10 @@ public class MCEditSchematicReader extends NBTSchematicReader {
private static final ImmutableList<NBTCompatibilityHandler> COMPATIBILITY_HANDLERS private static final ImmutableList<NBTCompatibilityHandler> COMPATIBILITY_HANDLERS
= ImmutableList.of( = ImmutableList.of(
new SignCompatibilityHandler() new SignCompatibilityHandler(),
// TODO Add a handler for skulls, flower pots, note blocks, etc. new FlowerPotCompatibilityHandler(),
new NoteBlockCompatibilityHandler()
// TODO - skulls, item tags for inventories...? DFUs :>
); );
private static final ImmutableList<EntityNBTCompatibilityHandler> ENTITY_COMPATIBILITY_HANDLERS private static final ImmutableList<EntityNBTCompatibilityHandler> ENTITY_COMPATIBILITY_HANDLERS
= ImmutableList.of( = ImmutableList.of(
@ -167,65 +171,54 @@ public class MCEditSchematicReader extends NBTSchematicReader {
// Need to pull out tile entities // Need to pull out tile entities
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue(); List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>(); Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
Map<BlockVector3, BlockState> blockOverrides = new HashMap<>();
for (Tag tag : tileEntities) { for (Tag tag : tileEntities) {
if (!(tag instanceof CompoundTag)) continue; if (!(tag instanceof CompoundTag)) continue;
CompoundTag t = (CompoundTag) tag; CompoundTag t = (CompoundTag) tag;
int x = 0; int x = t.getInt("x");
int y = 0; int y = t.getInt("y");
int z = 0; int z = t.getInt("z");
String id = t.getString("id");
Map<String, Tag> values = new HashMap<>(); Map<String, Tag> values = new HashMap<>(t.getValue());
values.put("id", new StringTag(convertBlockEntityId(id)));
for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
switch (entry.getKey()) {
case "x":
if (entry.getValue() instanceof IntTag) {
x = ((IntTag) entry.getValue()).getValue();
}
break;
case "y":
if (entry.getValue() instanceof IntTag) {
y = ((IntTag) entry.getValue()).getValue();
}
break;
case "z":
if (entry.getValue() instanceof IntTag) {
z = ((IntTag) entry.getValue()).getValue();
}
break;
}
values.put(entry.getKey(), entry.getValue());
}
int index = y * width * length + z * width + x; int index = y * width * length + z * width + x;
BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]);
if (block != null) { BlockState newBlock = block;
if (newBlock != null) {
for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) { for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) {
if (handler.isAffectedBlock(block)) { if (handler.isAffectedBlock(newBlock)) {
handler.updateNBT(block, values); newBlock = handler.updateNBT(block, values);
if (newBlock == null) {
break;
}
} }
} }
} }
BlockVector3 vec = BlockVector3.at(x, y, z); BlockVector3 vec = BlockVector3.at(x, y, z);
tileEntitiesMap.put(vec, values); tileEntitiesMap.put(vec, values);
if (newBlock != block) {
blockOverrides.put(vec, newBlock);
}
} }
BlockArrayClipboard clipboard = new BlockArrayClipboard(region); BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin); clipboard.setOrigin(origin);
// Don't log a torrent of errors
int failedBlockSets = 0;
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) { for (int y = 0; y < height; ++y) {
for (int z = 0; z < length; ++z) { for (int z = 0; z < length; ++z) {
int index = y * width * length + z * width + x; int index = y * width * length + z * width + x;
BlockVector3 pt = BlockVector3.at(x, y, z); BlockVector3 pt = BlockVector3.at(x, y, z);
BlockState state = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]); boolean useOverride = blockOverrides.containsKey(pt);
BlockState state = useOverride
? blockOverrides.get(pt)
: LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]);
try { try {
if (state != null) { if (state != null) {
@ -235,20 +228,11 @@ public class MCEditSchematicReader extends NBTSchematicReader {
clipboard.setBlock(region.getMinimumPoint().add(pt), state); clipboard.setBlock(region.getMinimumPoint().add(pt), state);
} }
} else { } else {
log.warn("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue."); if (!useOverride) {
log.warn("Unknown block when pasting schematic: " + blocks[index] + ":" + blockData[index] + ". Please report this issue.");
}
} }
} catch (WorldEditException e) { } catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this
switch (failedBlockSets) {
case 0:
log.warn("Failed to set block on a Clipboard", e);
break;
case 1:
log.warn("Failed to set block on a Clipboard (again) -- no more messages will be logged", e);
break;
default:
}
failedBlockSets++;
} }
} }
} }
@ -258,8 +242,9 @@ public class MCEditSchematicReader extends NBTSchematicReader {
// Entities // Entities
// ==================================================================== // ====================================================================
List<Tag> entityTags = getTag(schematic, "Entities", ListTag.class).getValue(); ListTag entityList = getTag(schematic, "Entities", ListTag.class);
if (entityTags != null) { if (entityList != null) {
List<Tag> entityTags = entityList.getValue();
for (Tag tag : entityTags) { for (Tag tag : entityTags) {
if (tag instanceof CompoundTag) { if (tag instanceof CompoundTag) {
CompoundTag compound = (CompoundTag) tag; CompoundTag compound = (CompoundTag) tag;
@ -289,32 +274,66 @@ public class MCEditSchematicReader extends NBTSchematicReader {
private String convertEntityId(String id) { private String convertEntityId(String id) {
switch(id) { switch(id) {
case "xp_orb": case "AreaEffectCloud": return "area_effect_cloud";
return "experience_orb"; case "ArmorStand": return "armor_stand";
case "xp_bottle": case "CaveSpider": return "cave_spider";
return "experience_bottle"; case "MinecartChest": return "chest_minecart";
case "eye_of_ender_signal": case "MinecartCommandBlock": return "commandblock_minecart";
return "eye_of_ender"; case "DragonFireball": return "dragon_fireball";
case "ender_crystal": case "ThrownEgg": return "egg";
return "end_crystal"; case "EnderCrystal": return "ender_crystal";
case "fireworks_rocket": case "EnderDragon": return "ender_dragon";
return "firework_rocket"; case "ThrownEnderpearl": return "ender_pearl";
case "commandblock_minecart": case "EyeOfEnderSignal": return "eye_of_ender_signal";
return "command_block_minecart"; case "FallingSand": return "falling_block";
case "snowman": case "FireworksRocketEntity": return "fireworks_rocket";
return "snow_golem"; case "MinecartFurnace": return "furnace_minecart";
case "villager_golem": case "MinecartHopper": return "hopper_minecart";
return "iron_golem"; case "EntityHorse": return "horse";
case "evocation_fangs": case "ItemFrame": return "item_frame";
return "evoker_fangs"; case "LeashKnot": return "leash_knot";
case "evocation_illager": case "LightningBolt": return "lightning_bolt";
return "evoker"; case "LavaSlime": return "magma_cube";
case "vindication_illager": case "MinecartRideable": return "minecart";
return "vindicator"; case "MushroomCow": return "mooshroom";
case "illusion_illager": case "Ozelot": return "ocelot";
return "illusioner"; case "PolarBear": return "polar_bear";
case "ThrownPotion": return "potion";
case "ShulkerBullet": return "shulker_bullet";
case "SmallFireball": return "small_fireball";
case "MinecartSpawner": return "spawner_minecart";
case "SpectralArrow": return "spectral_arrow";
case "PrimedTnt": return "tnt";
case "MinecartTNT": return "tnt_minecart";
case "VillagerGolem": return "villager_golem";
case "WitherBoss": return "wither";
case "WitherSkull": return "wither_skull";
case "ThrownExpBottle": return "xp_bottle";
case "XPOrb": return "xp_orb";
case "PigZombie": return "zombie_pigman";
default: return id;
}
}
private String convertBlockEntityId(String id) {
switch(id) {
case "Cauldron": return "brewing_stand";
case "Control": return "command_block";
case "DLDetector": return "daylight_detector";
case "Trap": return "dispenser";
case "EnchantTable": return "enchanting_table";
case "EndGateway": return "end_gateway";
case "AirPortal": return "end_portal";
case "EnderChest": return "ender_chest";
case "FlowerPot": return "flower_pot";
case "RecordPlayer": return "jukebox";
case "MobSpawner": return "mob_spawner";
case "Music":
case "noteblock":
return "note_block";
case "Structure": return "structure_block";
default: return id;
} }
return id;
} }
@Override @Override

View File

@ -0,0 +1,74 @@
package com.sk89q.worldedit.extent.clipboard.io.legacycompat;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.registry.LegacyMapper;
import java.util.Map;
public class FlowerPotCompatibilityHandler implements NBTCompatibilityHandler {
@Override
public <B extends BlockStateHolder<B>> boolean isAffectedBlock(B block) {
return block.getBlockType() == BlockTypes.FLOWER_POT;
}
@Override
public <B extends BlockStateHolder<B>> B updateNBT(B block, Map<String, Tag> values) {
Tag item = values.get("Item");
if (item instanceof StringTag) {
String id = ((StringTag) item).getValue();
int data = 0;
Tag dataTag = values.get("Data");
if (dataTag instanceof IntTag) {
data = ((IntTag) dataTag).getValue();
}
BlockState newState = convertLegacyBlockType(id, data);
if (newState != null) {
return (B) newState; // generics pls :\
}
}
return block;
}
private BlockState convertLegacyBlockType(String id, int data) {
int newId = 0;
switch (id) {
case "minecraft:red_flower":
newId = 38; // now poppy
break;
case "minecraft:yellow_flower":
newId = 37; // now dandelion
break;
case "minecraft:sapling":
newId = 6; // oak_sapling
break;
case "minecraft:deadbush":
case "minecraft:tallgrass":
newId = 31; // dead_bush with fern and grass (not 32!)
break;
default:
break;
}
String plantedName = null;
if (newId == 0) {
plantedName = id.substring(10);
} else {
BlockState plantedWithData = LegacyMapper.getInstance().getBlockFromLegacy(newId, data);
if (plantedWithData != null) {
plantedName = plantedWithData.getBlockType().getId().substring(10); // remove "minecraft:"
}
}
if (plantedName != null) {
BlockType potAndPlanted = BlockTypes.get("minecraft:potted_" + plantedName);
if (potAndPlanted != null) {
return potAndPlanted.getDefaultState();
}
}
return null;
}
}

View File

@ -26,5 +26,5 @@ import java.util.Map;
public interface NBTCompatibilityHandler { public interface NBTCompatibilityHandler {
<B extends BlockStateHolder<B>> boolean isAffectedBlock(B block); <B extends BlockStateHolder<B>> boolean isAffectedBlock(B block);
<B extends BlockStateHolder<B>> void updateNBT(B block, Map<String, Tag> values); <B extends BlockStateHolder<B>> B updateNBT(B block, Map<String, Tag> values);
} }

View File

@ -0,0 +1,43 @@
package com.sk89q.worldedit.extent.clipboard.io.legacycompat;
import com.sk89q.jnbt.ByteTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.registry.state.IntegerProperty;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import java.util.Map;
public class NoteBlockCompatibilityHandler implements NBTCompatibilityHandler {
private static final IntegerProperty NoteProperty;
static {
IntegerProperty temp;
try {
temp = (IntegerProperty) (Property<?>) BlockTypes.NOTE_BLOCK.getProperty("note");
} catch (NullPointerException | IllegalArgumentException | ClassCastException e) {
temp = null;
}
NoteProperty = temp;
}
@Override
public <B extends BlockStateHolder<B>> boolean isAffectedBlock(B block) {
return NoteProperty != null && block.getBlockType() == BlockTypes.NOTE_BLOCK;
}
@Override
public <B extends BlockStateHolder<B>> B updateNBT(B block, Map<String, Tag> values) {
// note that instrument was note stored (in state or nbt) previously.
// it will be updated to the block below when it gets set into the world for the first time
Tag noteTag = values.get("note");
if (noteTag instanceof ByteTag) {
Byte note = ((ByteTag) noteTag).getValue();
if (note != null) {
return (B) block.with(NoteProperty, (int) note);
}
}
return block;
}
}

View File

@ -30,30 +30,32 @@ public class Pre13HangingCompatibilityHandler implements EntityNBTCompatibilityH
@Override @Override
public boolean isAffectedEntity(EntityType type, CompoundTag tag) { public boolean isAffectedEntity(EntityType type, CompoundTag tag) {
boolean hasLegacyDirection = tag.containsKey("Dir"); if (!type.getId().startsWith("minecraft:")) {
return false;
}
boolean hasLegacyDirection = tag.containsKey("Dir") || tag.containsKey("Direction");
boolean hasFacing = tag.containsKey("Facing"); boolean hasFacing = tag.containsKey("Facing");
return hasLegacyDirection || hasFacing; return hasLegacyDirection || hasFacing;
} }
@Override @Override
public CompoundTag updateNBT(EntityType type, CompoundTag tag) { public CompoundTag updateNBT(EntityType type, CompoundTag tag) {
boolean hasLegacyDirection = tag.containsKey("Dir"); boolean hasLegacyDir = tag.containsKey("Dir");
boolean hasFacing = tag.containsKey("Facing"); boolean hasLegacyDirection = tag.containsKey("Direction");
int d; boolean hasPre113Facing = tag.containsKey("Facing");
if (hasLegacyDirection) { Direction newDirection;
d = MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")); if (hasLegacyDir) {
newDirection = MCDirections.fromPre13Hanging(MCDirections.fromLegacyHanging((byte) tag.asInt("Dir")));
} else if (hasLegacyDirection) {
newDirection = MCDirections.fromPre13Hanging(tag.asInt("Direction"));
} else if (hasPre113Facing) {
newDirection = MCDirections.fromPre13Hanging(tag.asInt("Facing"));
} else { } else {
d = tag.asInt("Facing"); return tag;
} }
Direction newDirection = MCDirections.fromPre13Hanging(d);
byte hangingByte = (byte) MCDirections.toHanging(newDirection); byte hangingByte = (byte) MCDirections.toHanging(newDirection);
CompoundTagBuilder builder = tag.createBuilder(); CompoundTagBuilder builder = tag.createBuilder();
builder.putByte("Direction", hangingByte);
builder.putByte("Facing", hangingByte); builder.putByte("Facing", hangingByte);
builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection)));
return builder.build(); return builder.build();
} }

View File

@ -39,7 +39,7 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler {
} }
@Override @Override
public <B extends BlockStateHolder<B>> void updateNBT(B block, Map<String, Tag> values) { public <B extends BlockStateHolder<B>> B updateNBT(B block, Map<String, Tag> values) {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
String key = "Text" + (i + 1); String key = "Text" + (i + 1);
Tag value = values.get(key); Tag value = values.get(key);
@ -69,5 +69,6 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler {
values.put("Text" + (i + 1), new StringTag(jsonTextObject.toString())); values.put("Text" + (i + 1), new StringTag(jsonTextObject.toString()));
} }
} }
return block;
} }
} }

View File

@ -130,8 +130,6 @@ public class ExtentEntityCopy implements EntityFunction {
if (tag != null) { if (tag != null) {
// Handle hanging entities (paintings, item frames, etc.) // Handle hanging entities (paintings, item frames, etc.)
boolean hasTilePosition = tag.containsKey("TileX") && tag.containsKey("TileY") && tag.containsKey("TileZ"); boolean hasTilePosition = tag.containsKey("TileX") && tag.containsKey("TileY") && tag.containsKey("TileZ");
boolean hasDirection = tag.containsKey("Direction");
boolean hasLegacyDirection = tag.containsKey("Dir");
boolean hasFacing = tag.containsKey("Facing"); boolean hasFacing = tag.containsKey("Facing");
if (hasTilePosition) { if (hasTilePosition) {
@ -143,26 +141,15 @@ public class ExtentEntityCopy implements EntityFunction {
.putInt("TileY", newTilePosition.getBlockY()) .putInt("TileY", newTilePosition.getBlockY())
.putInt("TileZ", newTilePosition.getBlockZ()); .putInt("TileZ", newTilePosition.getBlockZ());
if (hasDirection || hasLegacyDirection || hasFacing) { if (hasFacing) {
Direction direction; Direction direction = MCDirections.fromHanging(tag.asInt("Facing"));
if (hasDirection) {
direction = MCDirections.fromPre13Hanging(tag.asInt("Direction"));
} else if (hasLegacyDirection) {
direction = MCDirections.fromPre13Hanging(
MCDirections.fromLegacyHanging((byte) tag.asInt("Dir"))
);
} else {
direction = MCDirections.fromHanging(tag.asInt("Facing"));
}
if (direction != null) { if (direction != null) {
Vector3 vector = transform.apply(direction.toVector()).subtract(transform.apply(Vector3.ZERO)).normalize(); Vector3 vector = transform.apply(direction.toVector()).subtract(transform.apply(Vector3.ZERO)).normalize();
Direction newDirection = Direction.findClosest(vector, Flag.CARDINAL); Direction newDirection = Direction.findClosest(vector, Flag.CARDINAL);
if (newDirection != null) { if (newDirection != null) {
builder.putByte("Direction", (byte) MCDirections.toPre13Hanging(newDirection));
builder.putByte("Facing", (byte) MCDirections.toHanging(newDirection)); builder.putByte("Facing", (byte) MCDirections.toHanging(newDirection));
builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection)));
} }
} }
} }

View File

@ -82,21 +82,6 @@ public final class MCDirections {
} }
} }
public static int toPre13Hanging(Direction direction) {
switch (direction) {
case SOUTH:
return 0;
case WEST:
return 1;
case NORTH:
return 2;
case EAST:
return 3;
default:
return 0;
}
}
public static int fromLegacyHanging(byte i) { public static int fromLegacyHanging(byte i) {
switch (i) { switch (i) {
case 0: return 2; case 0: return 2;
@ -106,15 +91,6 @@ public final class MCDirections {
} }
} }
public static byte toLegacyHanging(int i) {
switch (i) {
case 0: return (byte) 2;
case 1: return (byte) 1;
case 2: return (byte) 0;
default: return (byte) 3;
}
}
public static Direction fromRotation(int i) { public static Direction fromRotation(int i) {
switch (i) { switch (i) {
case 0: case 0: