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.Clipboard;
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.NoteBlockCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.Pre13HangingCompatibilityHandler;
import com.sk89q.worldedit.extent.clipboard.io.legacycompat.SignCompatibilityHandler;
import com.sk89q.worldedit.math.BlockVector3;
@ -63,8 +65,10 @@ public class MCEditSchematicReader extends NBTSchematicReader {
private static final ImmutableList<NBTCompatibilityHandler> COMPATIBILITY_HANDLERS
= ImmutableList.of(
new SignCompatibilityHandler()
// TODO Add a handler for skulls, flower pots, note blocks, etc.
new SignCompatibilityHandler(),
new FlowerPotCompatibilityHandler(),
new NoteBlockCompatibilityHandler()
// TODO - skulls, item tags for inventories...? DFUs :>
);
private static final ImmutableList<EntityNBTCompatibilityHandler> ENTITY_COMPATIBILITY_HANDLERS
= ImmutableList.of(
@ -167,65 +171,54 @@ public class MCEditSchematicReader extends NBTSchematicReader {
// Need to pull out tile entities
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
Map<BlockVector3, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
Map<BlockVector3, BlockState> blockOverrides = new HashMap<>();
for (Tag tag : tileEntities) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag t = (CompoundTag) tag;
int x = 0;
int y = 0;
int z = 0;
int x = t.getInt("x");
int y = t.getInt("y");
int z = t.getInt("z");
String id = t.getString("id");
Map<String, Tag> values = new HashMap<>();
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());
}
Map<String, Tag> values = new HashMap<>(t.getValue());
values.put("id", new StringTag(convertBlockEntityId(id)));
int index = y * width * length + z * width + x;
BlockState block = LegacyMapper.getInstance().getBlockFromLegacy(blocks[index], blockData[index]);
if (block != null) {
BlockState newBlock = block;
if (newBlock != null) {
for (NBTCompatibilityHandler handler : COMPATIBILITY_HANDLERS) {
if (handler.isAffectedBlock(block)) {
handler.updateNBT(block, values);
if (handler.isAffectedBlock(newBlock)) {
newBlock = handler.updateNBT(block, values);
if (newBlock == null) {
break;
}
}
}
}
BlockVector3 vec = BlockVector3.at(x, y, z);
tileEntitiesMap.put(vec, values);
if (newBlock != block) {
blockOverrides.put(vec, newBlock);
}
}
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin);
// Don't log a torrent of errors
int failedBlockSets = 0;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
for (int z = 0; z < length; ++z) {
int index = y * width * length + z * width + x;
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 {
if (state != null) {
@ -235,20 +228,11 @@ public class MCEditSchematicReader extends NBTSchematicReader {
clipboard.setBlock(region.getMinimumPoint().add(pt), state);
}
} 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) {
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++;
} catch (WorldEditException ignored) { // BlockArrayClipboard won't throw this
}
}
}
@ -258,8 +242,9 @@ public class MCEditSchematicReader extends NBTSchematicReader {
// Entities
// ====================================================================
List<Tag> entityTags = getTag(schematic, "Entities", ListTag.class).getValue();
if (entityTags != null) {
ListTag entityList = getTag(schematic, "Entities", ListTag.class);
if (entityList != null) {
List<Tag> entityTags = entityList.getValue();
for (Tag tag : entityTags) {
if (tag instanceof CompoundTag) {
CompoundTag compound = (CompoundTag) tag;
@ -289,32 +274,66 @@ public class MCEditSchematicReader extends NBTSchematicReader {
private String convertEntityId(String id) {
switch(id) {
case "xp_orb":
return "experience_orb";
case "xp_bottle":
return "experience_bottle";
case "eye_of_ender_signal":
return "eye_of_ender";
case "ender_crystal":
return "end_crystal";
case "fireworks_rocket":
return "firework_rocket";
case "commandblock_minecart":
return "command_block_minecart";
case "snowman":
return "snow_golem";
case "villager_golem":
return "iron_golem";
case "evocation_fangs":
return "evoker_fangs";
case "evocation_illager":
return "evoker";
case "vindication_illager":
return "vindicator";
case "illusion_illager":
return "illusioner";
case "AreaEffectCloud": return "area_effect_cloud";
case "ArmorStand": return "armor_stand";
case "CaveSpider": return "cave_spider";
case "MinecartChest": return "chest_minecart";
case "MinecartCommandBlock": return "commandblock_minecart";
case "DragonFireball": return "dragon_fireball";
case "ThrownEgg": return "egg";
case "EnderCrystal": return "ender_crystal";
case "EnderDragon": return "ender_dragon";
case "ThrownEnderpearl": return "ender_pearl";
case "EyeOfEnderSignal": return "eye_of_ender_signal";
case "FallingSand": return "falling_block";
case "FireworksRocketEntity": return "fireworks_rocket";
case "MinecartFurnace": return "furnace_minecart";
case "MinecartHopper": return "hopper_minecart";
case "EntityHorse": return "horse";
case "ItemFrame": return "item_frame";
case "LeashKnot": return "leash_knot";
case "LightningBolt": return "lightning_bolt";
case "LavaSlime": return "magma_cube";
case "MinecartRideable": return "minecart";
case "MushroomCow": return "mooshroom";
case "Ozelot": return "ocelot";
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

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 {
<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
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");
return hasLegacyDirection || hasFacing;
}
@Override
public CompoundTag updateNBT(EntityType type, CompoundTag tag) {
boolean hasLegacyDirection = tag.containsKey("Dir");
boolean hasFacing = tag.containsKey("Facing");
int d;
if (hasLegacyDirection) {
d = MCDirections.fromLegacyHanging((byte) tag.asInt("Dir"));
boolean hasLegacyDir = tag.containsKey("Dir");
boolean hasLegacyDirection = tag.containsKey("Direction");
boolean hasPre113Facing = tag.containsKey("Facing");
Direction newDirection;
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 {
d = tag.asInt("Facing");
return tag;
}
Direction newDirection = MCDirections.fromPre13Hanging(d);
byte hangingByte = (byte) MCDirections.toHanging(newDirection);
CompoundTagBuilder builder = tag.createBuilder();
builder.putByte("Direction", hangingByte);
builder.putByte("Facing", hangingByte);
builder.putByte("Dir", MCDirections.toLegacyHanging(MCDirections.toHanging(newDirection)));
return builder.build();
}

View File

@ -39,7 +39,7 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler {
}
@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) {
String key = "Text" + (i + 1);
Tag value = values.get(key);
@ -69,5 +69,6 @@ public class SignCompatibilityHandler implements NBTCompatibilityHandler {
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) {
// Handle hanging entities (paintings, item frames, etc.)
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");
if (hasTilePosition) {
@ -143,26 +141,15 @@ public class ExtentEntityCopy implements EntityFunction {
.putInt("TileY", newTilePosition.getBlockY())
.putInt("TileZ", newTilePosition.getBlockZ());
if (hasDirection || hasLegacyDirection || hasFacing) {
Direction direction;
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 (hasFacing) {
Direction direction = MCDirections.fromHanging(tag.asInt("Facing"));
if (direction != null) {
Vector3 vector = transform.apply(direction.toVector()).subtract(transform.apply(Vector3.ZERO)).normalize();
Direction newDirection = Direction.findClosest(vector, Flag.CARDINAL);
if (newDirection != null) {
builder.putByte("Direction", (byte) MCDirections.toPre13Hanging(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) {
switch (i) {
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) {
switch (i) {
case 0: