Further changes in data synchronization.

ClassMapping now supports more advanced behavior, in particlar cases where
the destination object do not exist. It has now a much more comprehensive
documentation. TileNetworkData has been renamed NetworkData, as it's used
for more than just tile synchronization. Some additional markers have been
placed on the blueprints to make progress and identify which areas still
need support.
This commit is contained in:
SpaceToad 2014-01-09 09:02:23 +01:00
parent 17ed47d2a2
commit ce215587a8
15 changed files with 156 additions and 73 deletions

View file

@ -15,7 +15,7 @@ import buildcraft.builders.blueprints.BlueprintDatabase;
import buildcraft.core.Box;
import buildcraft.core.TileBuildCraft;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.Utils;
import java.io.IOException;
@ -29,12 +29,12 @@ import net.minecraftforge.common.ForgeDirection;
public class TileArchitect extends TileBuildCraft implements IInventory {
public @TileNetworkData
public @NetworkData
Box box = new Box();
private ItemStack items[] = new ItemStack[2];
private boolean isComputing = false;
public int computingTime = 0;
public @TileNetworkData
public @NetworkData
String name = "";
@Override

View file

@ -10,7 +10,7 @@ import buildcraft.core.network.RPC;
import buildcraft.core.network.RPCHandler;
import buildcraft.core.network.RPCMessageInfo;
import buildcraft.core.network.RPCSide;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.Utils;

View file

@ -24,7 +24,7 @@ import buildcraft.core.inventory.InvUtils;
import buildcraft.core.inventory.InventoryMapper;
import buildcraft.core.inventory.SimpleInventory;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import java.io.IOException;
import java.util.ListIterator;
import net.minecraft.entity.player.EntityPlayer;
@ -37,7 +37,7 @@ import static net.minecraftforge.common.ForgeDirection.*;
public class TileBuilder extends TileBuildCraft implements IBuilderInventory, IPowerReceptor, IMachine {
private static final int SLOT_BLUEPRINT = 0;
public @TileNetworkData
public @NetworkData
Box box = new Box();
private PowerHandler powerHandler;
private EntityRobot builderRobot;

View file

@ -16,7 +16,7 @@ import buildcraft.api.core.Position;
import buildcraft.core.EntityBlock;
import buildcraft.core.TileBuildCraft;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.Utils;
import net.minecraft.nbt.NBTTagCompound;
@ -28,7 +28,7 @@ public class TileMarker extends TileBuildCraft implements IAreaProvider {
public static class TileWrapper {
public @TileNetworkData
public @NetworkData
int x, y, z;
public TileWrapper() {
@ -73,20 +73,20 @@ public class TileMarker extends TileBuildCraft implements IAreaProvider {
return vectO.isSet();
}
public @TileNetworkData
public @NetworkData
TileWrapper vectO = new TileWrapper();
public @TileNetworkData
public @NetworkData
TileWrapper[] vect = { new TileWrapper(), new TileWrapper(), new TileWrapper() };
public @TileNetworkData
public @NetworkData
int xMin, yMin, zMin, xMax, yMax, zMax;
}
public @TileNetworkData
public @NetworkData
Origin origin = new Origin();
private EntityBlock[] lasers;
private EntityBlock[] signals;
public @TileNetworkData
public @NetworkData
boolean showSignals = false;
public void updateSignals() {

View file

@ -5,7 +5,7 @@ import buildcraft.core.BlockIndex;
import buildcraft.core.EntityLaser;
import buildcraft.core.EntityPowerLaser;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import java.io.IOException;
import java.util.Iterator;
@ -20,7 +20,7 @@ public class TilePathMarker extends TileMarker {
public EntityLaser lasers[] = new EntityLaser[2];
public int x0, y0, z0, x1, y1, z1;
public boolean loadLink0 = false, loadLink1 = false;
public @TileNetworkData
public @NetworkData
boolean tryingToConnect = false;
public TilePathMarker links[] = new TilePathMarker[2];
public static int searchSize = 64; // TODO: this should be moved to default props

View file

@ -10,6 +10,7 @@ package buildcraft.builders.blueprints;
import buildcraft.BuildCraftBuilders;
import buildcraft.api.builder.BlockHandler;
import buildcraft.core.inventory.StackHelper;
import buildcraft.core.network.NetworkData;
import buildcraft.core.utils.BCLog;
import buildcraft.core.utils.NBTUtils;
import buildcraft.factory.TileQuarry;
@ -24,7 +25,6 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeDirection;
/**
@ -35,11 +35,23 @@ import net.minecraftforge.common.ForgeDirection;
* @author Player
*/
public class Blueprint {
private BlueprintMeta meta;
@NetworkData
public BlueprintMeta meta;
// TODO: may need additional support from the serialization system
private final Schematic[][][] schematics;
public final int sizeX, sizeY, sizeZ;
@NetworkData
public int sizeX, sizeY, sizeZ;
@NetworkData
public int anchorX, anchorY, anchorZ;
// TODO: may need additional support from the serialization system
public ForgeDirection anchorOrientation = ForgeDirection.NORTH;
// TODO: may need additional support from the serialization system
private List<ItemStack> costs;
public static Blueprint create(int sizeX, int sizeY, int sizeZ) {

View file

@ -4,7 +4,12 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import buildcraft.core.network.NetworkData;
public final class BlueprintId {
@NetworkData
public byte[] id;
public static BlueprintId generate(byte[] data) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
@ -22,6 +27,9 @@ public final class BlueprintId {
return new BlueprintId(id);
}
public BlueprintId() {
}
private BlueprintId(byte[] id) {
this.id = id;
}
@ -59,6 +67,4 @@ public final class BlueprintId {
private char toHex(int i) {
return (char) (i < 10 ? '0' + i : 'a' - 10 + i);
}
private final byte[] id;
}

View file

@ -1,25 +1,23 @@
package buildcraft.builders.blueprints;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import net.minecraft.nbt.NBTTagCompound;
public class BlueprintMeta {
public final String version = "Blueprint-2.0";
/**
* FIXME: The format of id is not completely supported by the serialiser
* yet. To improve...
*/
private BlueprintId id;
@NetworkData
public BlueprintId id;
@TileNetworkData
@NetworkData
public String name;
@TileNetworkData
@NetworkData
public String creator;
public BlueprintMeta() {
}
protected BlueprintMeta(BlueprintId id, NBTTagCompound nbt) {

View file

@ -11,7 +11,7 @@ import buildcraft.api.core.IAreaProvider;
import buildcraft.api.core.IBox;
import buildcraft.api.core.LaserKind;
import buildcraft.api.core.Position;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.Utils;
import java.io.DataInputStream;
@ -24,9 +24,9 @@ import net.minecraft.world.World;
public class Box implements IBox {
public @TileNetworkData
public @NetworkData
int xMin, yMin, zMin, xMax, yMax, zMax;
public @TileNetworkData
public @NetworkData
boolean initialized;
private EntityBlock lasers[];

View file

@ -154,7 +154,7 @@ public class ClassMapping {
}
private boolean isSynchronizedField(Field f) {
TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);
NetworkData updateAnnotation = f.getAnnotation(NetworkData.class);
return updateAnnotation != null;
}
@ -291,30 +291,75 @@ public class ClassMapping {
data.writeInt(val.length);
for (int i = 0; i < val.length; ++i) {
data.writeUTF(val [i]);
if (val [i] == null) {
data.writeBoolean(false);
} else {
data.writeBoolean(true);
data.writeUTF(val [i]);
}
}
}
}
for (ClassMapping c : objectArrayFields) {
TileNetworkData updateAnnotation = c.field.getAnnotation(TileNetworkData.class);
NetworkData updateAnnotation = c.field.getAnnotation(NetworkData.class);
Object[] cpts = (Object[]) c.field.get(obj);
for (int i = 0; i < cpts.length; ++i)
if (cpts[i] == null) {
data.writeBoolean(false);
} else {
data.writeBoolean(true);
data.writeInt(cpts.length);
c.setData(cpts[i], data);
}
if (cpts == null) {
data.writeBoolean(false);
} else {
data.writeBoolean(true);
data.writeInt(cpts.length);
for (int i = 0; i < cpts.length; ++i)
if (cpts[i] == null) {
data.writeBoolean(false);
} else {
data.writeBoolean(true);
c.setData(cpts[i], data);
}
}
}
}
/**
* This class will update data in an object from a stream. Public data
* market #NetworkData will get synchronized. The following rules will
* apply:
*
* In the following description, we consider strings as primitive objects.
*
* Market primitives data will be directly updated on the destination
* object after the value of the source object
*
* Market primitive arrays will be re-created in the destination object
* after the primitive array of the source object. This means that array
* references are not preserved by the proccess. TODO if an array is null
* in the source array and not in the destination one, it will be turned to
* null.
*
* Market object will be synchronized - that it we do not create new
* instances in the destination object if they are already there but rather
* recursively synchronize values. TODO if destination is null and not
* source, the destination will get the instance created. If destination is
* not null and source is, the destination will get truned to null.
*
* Market object arrays will be synchronized - not re-created. TODO if if
* destination is null and not source, the destination will get the instance
* created. If destination is not null and source is, the destination will
* get turned to null. The same behavior applies to the contents of the
* array. Trying to synchronize two arrays of different size is an error
* and will lead to an exception - so if the array needs to change on the
* destination it needs to be set to null first.
*
* WARNING - class instantiation will be done after the field type, not
* the actual type used in serialization. Generally speaking, this system
* is not robust to polymorphism
*/
@SuppressWarnings("rawtypes")
public void updateFromData(Object obj, DataInputStream data) throws IllegalArgumentException,
IllegalAccessException, IOException {
IllegalAccessException, IOException, InstantiationException {
for (Field f : shortFields) {
f.setShort(obj, data.readShort());
@ -343,16 +388,21 @@ public class ClassMapping {
for (Field f : stringFields) {
if (data.readBoolean()) {
f.set(obj, data.readUTF());
} else {
f.set(obj, null);
}
}
for (ClassMapping c : objectFields) {
boolean isNull = data.readBoolean();
if (data.readBoolean()) {
if (c.field.get(obj) == null) {
c.field.set
(obj, c.field.getDeclaringClass().newInstance());
}
if (!isNull) {
// WARNING! Because we consider the object to exist already,
// we perform the following. What if it's not the case?
c.updateFromData(c.field.get(obj), data);
} else {
c.updateFromData(c.field.get(obj), null);
}
}
@ -433,7 +483,11 @@ public class ClassMapping {
String [] tmp = new String [length];
for (int i = 0; i < tmp.length; ++i) {
tmp [i] = data.readUTF();
if (data.readBoolean()) {
tmp [i] = data.readUTF();
} else {
tmp [i] = null;
}
}
f.set(obj, tmp);
@ -445,14 +499,29 @@ public class ClassMapping {
for (ClassMapping c : objectArrayFields) {
Object[] cpts = (Object[]) c.field.get(obj);
for (int i = 0; i < cpts.length; ++i) {
boolean isNull = data.readBoolean();
if (data.readBoolean()) {
int serializedSize = data.readInt();
if (!isNull) {
// WARNING! Because we consider the object to exist already,
// we perform the following. What if it's not the case?
c.updateFromData(cpts[i], data);
if (serializedSize != cpts.length) {
throw new IOException
("Expected size doesn't match serialized size on object array");
}
for (int i = 0; i < cpts.length; ++i) {
boolean isNull = data.readBoolean();
if (!isNull) {
if (cpts [i] == null) {
cpts [i] = c.field.getDeclaringClass().getComponentType().newInstance();
}
c.updateFromData(cpts[i], data);
} else {
cpts [i] = null;
}
}
} else {
c.field.set(obj, null);
}
}
}

View file

@ -15,8 +15,6 @@ import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Inherited
//FIXME: Given the usage of this in RPCs, this should be renamed, e.g.
//NetworkData
public @interface TileNetworkData {
public @interface NetworkData {
}

View file

@ -29,7 +29,7 @@ import buildcraft.api.transport.IPipeTile.PipeType;
import buildcraft.core.DefaultProps;
import buildcraft.core.TileBuffer;
import buildcraft.core.TileBuildCraft;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.energy.gui.ContainerEngine;
@ -58,11 +58,11 @@ public abstract class TileEngine extends TileBuildCraft implements IPowerRecepto
public double energy;
public float heat = MIN_HEAT;
//
public @TileNetworkData
public @NetworkData
EnergyStage energyStage = EnergyStage.BLUE;
public @TileNetworkData
public @NetworkData
ForgeDirection orientation = ForgeDirection.UP;
public @TileNetworkData
public @NetworkData
boolean isPumping = false; // Used for SMP synch
public TileEngine() {

View file

@ -27,7 +27,7 @@ import buildcraft.core.IBuilderInventory;
import buildcraft.core.IMachine;
import buildcraft.core.TileBuildCraft;
import buildcraft.core.network.PacketUpdate;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.proxy.CoreProxy;
import buildcraft.core.utils.BlockUtil;
import buildcraft.core.utils.Utils;
@ -54,17 +54,17 @@ import net.minecraftforge.common.ForgeDirection;
public class TileQuarry extends TileBuildCraft implements IMachine, IPowerReceptor, IBuilderInventory {
public @TileNetworkData
public @NetworkData
Box box = new Box();
public @TileNetworkData
public @NetworkData
boolean inProcess = false;
public @TileNetworkData
public @NetworkData
int targetX, targetY, targetZ;
public @TileNetworkData
public @NetworkData
double headPosX, headPosY, headPosZ;
public @TileNetworkData
public @NetworkData
double speed = 0.03;
public @TileNetworkData
public @NetworkData
boolean builderDone = false;
public EntityRobot builder;
private BlueprintBuilder blueprintBuilder;
@ -122,7 +122,7 @@ public class TileQuarry extends TileBuildCraft implements IMachine, IPowerRecept
private boolean movingVertically;
private double headTrajectory;
private Ticket chunkTicket;
public @TileNetworkData
public @NetworkData
boolean isAlive;
public EntityPlayer placedBy;

View file

@ -15,7 +15,7 @@ import buildcraft.api.power.PowerHandler.PowerReceiver;
import buildcraft.api.power.PowerHandler.Type;
import buildcraft.api.transport.IPipeTile;
import buildcraft.api.transport.PipeManager;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.transport.Pipe;
import buildcraft.transport.PipeIconProvider;
import buildcraft.transport.PipeTransportFluids;
@ -31,7 +31,7 @@ import net.minecraftforge.fluids.IFluidHandler;
public class PipeFluidsWood extends Pipe<PipeTransportFluids> implements IPowerReceptor {
public @TileNetworkData
public @NetworkData
int liquidToExtract;
private PowerHandler powerHandler;
protected int standardIconIndex = PipeIconProvider.TYPE.PipeFluidsWood_Standard.ordinal();

View file

@ -11,7 +11,7 @@ import buildcraft.BuildCraftTransport;
import buildcraft.api.core.IIconProvider;
import buildcraft.api.gates.IAction;
import buildcraft.api.tools.IToolWrench;
import buildcraft.core.network.TileNetworkData;
import buildcraft.core.network.NetworkData;
import buildcraft.core.utils.EnumColor;
import buildcraft.transport.Pipe;
import buildcraft.transport.PipeIconProvider;
@ -38,7 +38,7 @@ public class PipeItemsDaizuli extends Pipe<PipeTransportItems> {
private int standardIconIndex = PipeIconProvider.TYPE.PipeItemsDaizuli_Black.ordinal();
private int solidIconIndex = PipeIconProvider.TYPE.PipeAllDaizuli_Solid.ordinal();
@TileNetworkData
@NetworkData
private int color = EnumColor.BLACK.ordinal();
private PipeLogicIron logic = new PipeLogicIron(this) {
@Override