Bug fix: don't change the relPos of existing WPObjects during object exporting and manipulation

Bug fix: correctly import exporting, rotation and mirroring of entities from custom objects
master
Captain Chaos 2022-08-19 18:49:20 +02:00
parent 5d194dadd6
commit 0bb514a5f8
15 changed files with 259 additions and 101 deletions

View File

@ -729,6 +729,8 @@ public final class Constants {
public static final String TAG_LARGE_BIOMES_ = "large_biomes";
public static final String TAG_SPAWN_FORCED = "SpawnForced";
public static final String TAG_SPAWN_DIMENSION = "SpawnDimension";
public static final String TAG_FACING = "Facing";
public static final String TAG_FACING_ = "facing";
/**
* Possibly unofficial, SpoutCraft-specific

View File

@ -7,11 +7,13 @@ package org.pepsoft.minecraft;
import org.jnbt.CompoundTag;
import org.jnbt.StringTag;
import org.pepsoft.util.MathUtils;
import java.util.HashMap;
import java.util.UUID;
import static org.pepsoft.minecraft.Constants.*;
import static org.pepsoft.util.ArrayUtils.indexOf;
/**
*
@ -71,6 +73,11 @@ public class Entity extends AbstractNBTItem {
throw new IllegalArgumentException();
}
setDoubleList(TAG_POS, pos);
if (containsTag(TAG_TILE_X)) {
setInt(TAG_TILE_X, (int) Math.floor(pos[0]));
setInt(TAG_TILE_Y, (int) Math.floor(pos[1]));
setInt(TAG_TILE_Z, (int) Math.floor(pos[2]));
}
}
public final float[] getRot() {
@ -112,6 +119,97 @@ public class Entity extends AbstractNBTItem {
setString(TAG_UUID, null);
}
/**
* Return a clone of this entity that has been rotated clockwise by {@code steps} times 90 degrees.
*/
public Entity rotate(int steps) {
final Entity rotEntity = clone();
if (steps != 0) {
final float[] rot = rotEntity.getRot();
rot[0] = MathUtils.mod(rot[0] + steps * 90.0f, 360.0f);
rotEntity.setRot(rot);
if (containsTag(TAG_FACING_)) {
int facing = getByte(TAG_FACING_);
if ((facing >= 0) && (facing <= 3)) {
facing = (facing + steps) & 0x3;
rotEntity.setByte(TAG_FACING_, (byte) facing);
}
}
if (containsTag(TAG_FACING)) {
int facing = getByte(TAG_FACING);
if ((facing >= 2) && (facing <= 5)) {
facing = FACING_VALUES[(indexOf(FACING_VALUES, facing) + steps) & 0x3];
rotEntity.setByte(TAG_FACING, (byte) facing);
}
}
// TODO are there other properties to adjust?
// TODO adjust velocity
}
return rotEntity;
}
/**
* Return a clone of this entity that has been mirrored in the indicated axis.
*/
public Entity mirror(boolean mirrorYAxis) {
final Entity mirEntity = clone();
final double[] vel = mirEntity.getVel();
if (mirrorYAxis) {
vel[2] = -vel[2];
} else {
vel[0] = -vel[0];
}
mirEntity.setVel(vel);
final float[] rot = mirEntity.getRot();
if (mirrorYAxis) {
rot[0] = MathUtils.mod(180.0f - rot[0], 360.0f);
} else {
rot[0] = MathUtils.mod(-rot[0], 360.0f);
}
mirEntity.setRot(rot);
if (containsTag(TAG_FACING_)) {
int facing = getByte(TAG_FACING_);
if (mirrorYAxis) {
switch (facing) {
case 0: facing = 2; break;
case 2: facing = 0; break;
}
} else {
switch (facing) {
case 1: facing = 3; break;
case 3: facing = 1; break;
}
}
mirEntity.setByte(TAG_FACING_, (byte) facing);
}
if (containsTag(TAG_FACING)) {
int facing = getByte(TAG_FACING);
if (mirrorYAxis) {
switch (facing) {
case 2: facing = 3; break;
case 3: facing = 2; break;
}
} else {
switch (facing) {
case 4: facing = 5; break;
case 5: facing = 4; break;
}
}
mirEntity.setByte(TAG_FACING, (byte) facing);
}
// TODO are there other properties to adjust?
return mirEntity;
}
@Override
public Entity clone() {
final Entity clone = (Entity) super.clone();
if (relPos != null) {
clone.relPos = relPos.clone();
}
return clone;
}
public static Entity fromNBT(CompoundTag entityTag) {
return fromNBT(entityTag, null);
}
@ -132,5 +230,6 @@ public class Entity extends AbstractNBTItem {
private double[] relPos;
private static final int[] FACING_VALUES = { 3, 4, 2, 5 };
private static final long serialVersionUID = 1L;
}

View File

@ -637,16 +637,9 @@ public final class MC115AnvilChunk extends MCNamedBlocksChunk implements Section
throw new UnsupportedOperationException();
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
entity = (Entity) entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
entity = (Entity) entity.clone();
entity = entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}

View File

@ -647,16 +647,9 @@ public final class MC118AnvilChunk extends MCNamedBlocksChunk implements Section
throw new UnsupportedOperationException();
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
entity = (Entity) entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
entity = (Entity) entity.clone();
entity = entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}

View File

@ -504,16 +504,9 @@ public final class MC12AnvilChunk extends MCNumberedBlocksChunk implements Minec
throw new UnsupportedOperationException("Not supported");
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
entity = (Entity) entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
entity = (Entity) entity.clone();
entity = entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}

View File

@ -376,16 +376,9 @@ public class MemoryChunk implements Chunk, MinecraftWorld, Serializable {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
entity = (Entity) entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
entity = (Entity) entity.clone();
entity = entity.clone();
entity.setPos(new double[] {x, height, y});
getEntities().add(entity);
}

View File

@ -269,11 +269,6 @@ public class CachingMinecraftWorld implements MinecraftWorld {
chunkStore.flush();
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
addEntity(x + 0.5, y + 0.5, height + 1.5, entity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
if (readOnly) {
@ -281,7 +276,7 @@ public class CachingMinecraftWorld implements MinecraftWorld {
}
Chunk chunk = getChunkForEditing(((int) x) >> 4, ((int) y) >> 4);
if ((chunk != null) && (! chunk.isReadOnly())) {
Entity clone = (Entity) entity.clone();
Entity clone = entity.clone();
clone.setPos(new double[] {x, height, y});
chunk.getEntities().add(clone);
}

View File

@ -95,18 +95,9 @@ public class InvertedWorld implements MinecraftWorld {
return maxHeight;
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
Entity worldEntity = (Entity) entity.clone();
double[] pos = worldEntity.getPos();
pos[1] = maxZ - pos[1];
worldEntity.setPos(pos);
world.addEntity(x, y, maxZ - height, worldEntity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
Entity worldEntity = (Entity) entity.clone();
Entity worldEntity = entity.clone();
double[] pos = worldEntity.getPos();
pos[1] = maxZ - pos[1];
worldEntity.setPos(pos);

View File

@ -67,8 +67,6 @@ public interface MinecraftWorld extends ChunkProvider {
int getMaxHeight();
void addEntity(int x, int y, int height, Entity entity);
void addEntity(double x, double y, double height, Entity entity);
void addTileEntity(int x, int y, int height, TileEntity tileEntity);

View File

@ -131,16 +131,11 @@ public class WorldRegion implements MinecraftWorld {
return maxHeight;
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
addEntity(x + 0.5, y + 0.5, height + 1.5, entity);
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
Chunk chunk = getChunkForEditing(((int) x) >> 4, ((int) y) >> 4);
Chunk chunk = getChunkForEditing((int) Math.floor(x) >> 4, (int) Math.floor(y) >> 4);
if (chunk != null) {
Entity clone = (Entity) entity.clone();
Entity clone = entity.clone();
clone.setPos(new double[] {x, height, y});
chunk.getEntities().add(clone);
}

View File

@ -181,11 +181,6 @@ public final class MinecraftWorldObject implements MinecraftWorld, WPObject {
return maxHeight;
}
@Override
public void addEntity(int x, int y, int height, Entity entity) {
// Do nothing
}
@Override
public void addEntity(double x, double y, double height, Entity entity) {
// Do nothing

View File

@ -9,7 +9,6 @@ import org.pepsoft.minecraft.Entity;
import org.pepsoft.minecraft.Material;
import org.pepsoft.minecraft.TileEntity;
import org.pepsoft.util.AttributeKey;
import org.pepsoft.util.MathUtils;
import org.pepsoft.worldpainter.Platform;
import javax.vecmath.Point3i;
@ -71,21 +70,14 @@ public class MirroredObject extends AbstractObject {
if (objectEntities != null) {
List<Entity> entities = new ArrayList<>(objectEntities.size());
for (Entity objectEntity: objectEntities) {
Entity entity = (Entity) objectEntity.clone();
Entity entity = objectEntity.mirror(mirrorYAxis);
double[] relPos = entity.getRelPos();
double[] vel = entity.getVel();
if (mirrorYAxis) {
relPos[2] = dimensions.y - relPos[2];
vel[2] = -vel[2];
} else {
relPos[0] = dimensions.x - relPos[0];
vel[0] = -vel[0];
}
entity.setRelPos(relPos);
entity.setVel(vel);
float[] rot = entity.getRot();
rot[0] = MathUtils.mod(rot[0] + 180.0f, 360.0f);
entity.setRot(rot);
entities.add(entity);
}
return entities;

View File

@ -8,7 +8,6 @@ import org.pepsoft.minecraft.Entity;
import org.pepsoft.minecraft.Material;
import org.pepsoft.minecraft.TileEntity;
import org.pepsoft.util.AttributeKey;
import org.pepsoft.util.MathUtils;
import org.pepsoft.worldpainter.Platform;
import javax.vecmath.Point3i;
@ -110,36 +109,33 @@ public class RotatedObject extends AbstractObject {
@Override
public List<Entity> getEntities() {
if (steps == 0) {
return object.getEntities();
}
List<Entity> objectEntities = object.getEntities();
if (objectEntities != null) {
List<Entity> entities = new ArrayList<>(objectEntities.size());
for (Entity origEntity: objectEntities) {
Entity rotEntity = (Entity) origEntity.clone();
if (steps != 0) {
double[] origRelPos = origEntity.getRelPos();
double[] rotRelPos = rotEntity.getRelPos();
switch (steps) {
case 1:
rotRelPos[0] = dimensions.x - origRelPos[2];
rotRelPos[2] = origRelPos[0];
break;
case 2:
rotRelPos[0] = dimensions.x - origRelPos[0];
rotRelPos[2] = dimensions.y - origRelPos[2];
break;
case 3:
rotRelPos[0] = origRelPos[2];
rotRelPos[2] = dimensions.y - origRelPos[0];
break;
default:
throw new InternalError();
}
rotEntity.setRelPos(rotRelPos);
float[] rot = origEntity.getRot();
rot[0] = MathUtils.mod(rot[0] + steps * 90.0f, 360.0f);
rotEntity.setRot(rot);
// TODO: adjust velocity
Entity rotEntity = origEntity.rotate(steps);
double[] origRelPos = origEntity.getRelPos();
double[] rotRelPos = rotEntity.getRelPos();
switch (steps) {
case 1:
rotRelPos[0] = dimensions.x - origRelPos[2];
rotRelPos[2] = origRelPos[0];
break;
case 2:
rotRelPos[0] = dimensions.x - origRelPos[0];
rotRelPos[2] = dimensions.y - origRelPos[2];
break;
case 3:
rotRelPos[0] = origRelPos[2];
rotRelPos[2] = dimensions.y - origRelPos[0];
break;
default:
throw new InternalError();
}
rotEntity.setRelPos(rotRelPos);
entities.add(rotEntity);
}
return entities;

View File

@ -4,6 +4,8 @@
*/
package org.pepsoft.worldpainter.objects;
import org.jnbt.ByteTag;
import org.jnbt.CompoundTag;
import org.pepsoft.minecraft.Entity;
import org.pepsoft.minecraft.Material;
import org.pepsoft.minecraft.TileEntity;
@ -13,10 +15,10 @@ import org.pepsoft.worldpainter.Dimension;
import javax.vecmath.Point3i;
import java.io.File;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static org.pepsoft.minecraft.Constants.TAG_FACING;
import static org.pepsoft.minecraft.Constants.TAG_FACING_;
/**
* A three dimensional object, consisting of Minecraft blocks, which can be
@ -243,6 +245,108 @@ public interface WPObject extends Serializable, Cloneable {
return true;
}
/**
* Dumps the object to the console.
*/
default void dump() {
final Point3i dim = getDimensions();
final Map<Point3i, List<Entity>> entities = new HashMap<>();
if (getEntities() != null) {
for (Entity entity : getEntities()) {
final Point3i pos = new Point3i(
(int) Math.floor(entity.getRelPos()[0]),
(int) Math.floor(entity.getRelPos()[2]),
(int) Math.floor(entity.getRelPos()[1])
);
entities.computeIfAbsent(pos, k -> new ArrayList<>()).add(entity);
}
}
for (int z = 0; z < dim.z; z++) {
final List<Entity> entitiesEncountered = new ArrayList<>(entities.size());
System.out.println("X-->");
for (int y = 0; y < dim.y; y++) {
for (int row = 0; row < 4; row++) {
switch (row) {
case 0:
for (int x = 0; x < dim.x; x++) {
System.out.print("+------");
}
System.out.print('+');
break;
case 1:
for (int x = 0; x < dim.x; x++) {
if (getMask(x, y, z)) {
System.out.printf("|%6.6s", getMaterial(x, y, z).simpleName);
} else {
System.out.print("| ");
}
}
System.out.print('|');
break;
case 2:
for (int x = 0; x < dim.x; x++) {
final Point3i pos = new Point3i(x, y, z);
if (entities.containsKey(pos)) {
final Entity entity = entities.get(pos).get(0);
entitiesEncountered.add(entity);
final String id = entity.getId();
System.out.printf("|%d:%4.4s", entitiesEncountered.size(), id.substring(id.indexOf(':') + 1));
} else {
System.out.print("| ");
}
}
System.out.print('|');
break;
case 3:
for (int x = 0; x < dim.x; x++) {
final Point3i pos = new Point3i(x, y, z);
if (entities.containsKey(pos) && (entities.get(pos).size() > 1)) {
final Entity entity = entities.get(pos).get(1);
entitiesEncountered.add(entity);
final String id = entity.getId();
System.out.printf("|%d:%4.4s", entitiesEncountered.size(), id.substring(id.indexOf(':') + 1));
} else {
System.out.print("| ");
}
}
System.out.print('|');
break;
}
if ((y * 4 + row) == 0) {
System.out.print(" Y");
} else if ((y * 4 + row) == 1) {
System.out.print(" |");
} else if ((y * 4 + row) == 2) {
System.out.print(" v");
}
System.out.println();
}
}
for (int x = 0; x < dim.x; x++) {
System.out.print("+------");
}
System.out.println("+ Z: " + z);
for (int i = 0; i < entitiesEncountered.size(); i++) {
final Entity entity = entitiesEncountered.get(i);
final StringBuilder sb = new StringBuilder();
sb.append(entity.getId());
sb.append(", relPos: ");
sb.append(Arrays.toString(entity.getRelPos()));
CompoundTag nbt = entity.toNBT();
if (nbt.containsTag(TAG_FACING_)) {
sb.append(", facing: ");
sb.append(((ByteTag) nbt.getTag(TAG_FACING_)).getValue());
}
if (nbt.containsTag(TAG_FACING)) {
sb.append(", Facing: ");
sb.append(((ByteTag) nbt.getTag(TAG_FACING)).getValue());
}
System.out.println("*" + (i + 1) + ": " + sb);
}
System.out.println();
}
}
// Standard attribute values
int COLLISION_MODE_ALL = 1;
int COLLISION_MODE_SOLID = 2;

View File

@ -0,0 +1,19 @@
package org.pepsoft.util;
/**
* Utility methods for working with arrays.
*/
public final class ArrayUtils {
private ArrayUtils() {
// Prevent instantiation
}
public static int indexOf(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
return i;
}
}
return -1;
}
}