@SuppressWarnings("rawtypes")
  public void updateFromData(
      Object obj, ByteBuffer byteBuffer, float[] floatValues, String[] stringValues, Indexes index)
      throws IllegalArgumentException, IllegalAccessException {

    Reporter r = null;

    if (BuildCraftCore.trackNetworkUsage) {
      if (!report.containsKey(clas.getName())) report.put(clas.getName(), new Reporter());

      r = report.get(clas.getName());
      r.clas = clas;
    } else r = new Reporter();

    r.occurences++;

    for (Field f : shortFields) {
      f.setShort(obj, byteBuffer.readShort());
      r.bytes += 2;
      r.dataInt += 1;
    }

    for (Field f : intFields) {
      f.setInt(obj, byteBuffer.readInt());
      r.bytes += 4;
      r.dataInt += 1;
    }

    for (Field f : booleanFields) {
      f.setBoolean(obj, byteBuffer.readUnsignedByte() == 1);
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : enumFields) {
      f.set(obj, ((Class) f.getGenericType()).getEnumConstants()[byteBuffer.readUnsignedByte()]);
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : unsignedByteFields) {
      f.setInt(obj, byteBuffer.readUnsignedByte());
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : floatFields) {
      f.setFloat(obj, floatValues[index.floatIndex]);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : doubleFields) {
      f.setDouble(obj, floatValues[index.floatIndex]);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : stringFields) {
      f.set(obj, stringValues[index.stringIndex]);
      r.bytes += stringValues[index.stringIndex].length();
      index.stringIndex++;
      r.dataString += 1;
    }

    for (ClassMapping c : objectFields) {
      boolean isNull = byteBuffer.readUnsignedByte() == 0;
      r.bytes += 1;
      r.dataInt += 1;

      if (isNull) {
        for (int i = 0; i < c.sizeBytes; ++i) byteBuffer.readUnsignedByte();

        index.floatIndex += c.sizeFloat;
        index.stringIndex += c.sizeString;
      } else c.updateFromData(c.field.get(obj), byteBuffer, floatValues, stringValues, index);
    }

    for (Field f : doubleArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((double[]) f.get(obj))[i] = floatValues[index.floatIndex];
        index.floatIndex++;
        r.bytes += 4;
        r.dataFloat += 1;
      }
    }

    for (Field f : shortArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((short[]) f.get(obj))[i] = byteBuffer.readShort();
        r.bytes += 2;
        r.dataInt += 1;
      }
    }

    for (Field f : intArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((int[]) f.get(obj))[i] = byteBuffer.readInt();
        r.bytes += 4;
        r.dataInt += 1;
      }
    }

    for (Field f : booleanArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((boolean[]) f.get(obj))[i] = byteBuffer.readUnsignedByte() == 1;
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : unsignedByteArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((int[]) f.get(obj))[i] = byteBuffer.readUnsignedByte();
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : stringArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      String[] strs = (String[]) f.get(obj);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        strs[i] = stringValues[index.stringIndex];
        r.bytes += stringValues[index.stringIndex].length();
        index.stringIndex++;
        r.dataString += 1;
      }
    }

    for (ClassMapping c : objectArrayFields) {
      TileNetworkData updateAnnotation = c.field.getAnnotation(TileNetworkData.class);

      Object[] cpts = (Object[]) c.field.get(obj);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        boolean isNull = byteBuffer.readUnsignedByte() == 0;
        r.bytes += 1;
        r.dataInt += 1;

        if (isNull) {
          for (int j = 0; j < c.sizeBytes; ++j) byteBuffer.readUnsignedByte();

          index.floatIndex += c.sizeFloat;
          index.stringIndex += c.sizeString;
        } else c.updateFromData(cpts[i], byteBuffer, floatValues, stringValues, index);
      }
    }
  }
  @SuppressWarnings({"rawtypes", "unchecked"})
  public ClassMapping(final Class<? extends Object> c) {
    clas = c;
    Field[] fields = c.getFields();

    try {
      for (Field f : fields) {
        if (!isSynchronizedField(f)) continue;

        Type t = f.getGenericType();

        // ??? take into account enumerations here!

        if (t instanceof Class && !((Class) t).isArray()) {
          Class fieldClass = (Class) t;

          if (fieldClass.equals(short.class)) {
            sizeBytes += 2;
            shortFields.add(f);
          } else if (fieldClass.equals(int.class)) {
            TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

            if (updateAnnotation.intKind() == TileNetworkData.UNSIGNED_BYTE) {
              sizeBytes += 1;
              unsignedByteFields.add(f);
            } else {
              sizeBytes += 4;
              intFields.add(f);
            }
          } else if (fieldClass.equals(boolean.class)) {
            sizeBytes += 1;
            booleanFields.add(f);
          } else if (Enum.class.isAssignableFrom(fieldClass)) {
            sizeBytes += 1;
            enumFields.add(f);
          } else if (fieldClass.equals(String.class)) {
            sizeString++;
            stringFields.add(f);
          } else if (fieldClass.equals(float.class)) {
            sizeFloat++;
            floatFields.add(f);
          } else if (fieldClass.equals(double.class)) {
            sizeFloat++;
            doubleFields.add(f);
          } else {
            // ADD SOME SAFETY HERE - if we're not child of Object

            ClassMapping mapping = new ClassMapping(fieldClass);
            mapping.field = f;

            objectFields.add(mapping);
            sizeBytes += 1; // to catch null / not null.

            sizeBytes += mapping.sizeBytes;
            sizeFloat += mapping.sizeFloat;
            sizeString += mapping.sizeString;
          }
        }
        if (t instanceof Class && ((Class) t).isArray()) {
          TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

          if (updateAnnotation.staticSize() == -1)
            throw new RuntimeException("arrays must be provided with an explicit size");

          Class fieldClass = (Class) t;

          Class cptClass = fieldClass.getComponentType();

          if (cptClass.equals(double.class)) {
            sizeFloat += updateAnnotation.staticSize();
            doubleArrayFields.add(f);
          } else if (cptClass.equals(short.class)) {
            sizeBytes += updateAnnotation.staticSize() * 2;
            shortArrayFields.add(f);
          } else if (cptClass.equals(int.class)) {
            updateAnnotation = f.getAnnotation(TileNetworkData.class);

            if (updateAnnotation.intKind() == TileNetworkData.UNSIGNED_BYTE) {
              sizeBytes += updateAnnotation.staticSize();
              unsignedByteArrayFields.add(f);
            } else {
              sizeBytes += updateAnnotation.staticSize() * 4;
              intArrayFields.add(f);
            }
          } else if (cptClass.equals(String.class)) {
            sizeString += updateAnnotation.staticSize();
            stringArrayFields.add(f);
          } else if (cptClass.equals(boolean.class)) {
            sizeBytes += updateAnnotation.staticSize();
            booleanArrayFields.add(f);
          } else {
            // ADD SOME SAFETY HERE - if we're not child of Object

            ClassMapping mapping = new ClassMapping(cptClass);
            mapping.field = f;
            objectArrayFields.add(mapping);

            sizeBytes += updateAnnotation.staticSize(); // to catch
            // null /
            // not null.

            sizeBytes += updateAnnotation.staticSize() * mapping.sizeBytes;
            sizeFloat += updateAnnotation.staticSize() * mapping.sizeFloat;
            sizeString += updateAnnotation.staticSize() * mapping.sizeString;
          }
        }
      }
    } catch (IllegalArgumentException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  @SuppressWarnings("rawtypes")
  public void setData(
      Object obj, ByteBuffer byteBuffer, float[] floatValues, String[] stringValues, Indexes index)
      throws IllegalArgumentException, IllegalAccessException {

    Reporter r = null;

    if (BuildCraftCore.trackNetworkUsage) {
      if (!report.containsKey(clas.getName())) report.put(clas.getName(), new Reporter());

      r = report.get(clas.getName());
      r.clas = clas;
    } else r = new Reporter();

    r.occurences++;

    for (Field f : shortFields) {
      byteBuffer.writeShort(f.getShort(obj));
      r.bytes += 2;
      r.dataInt += 1;
    }

    for (Field f : intFields) {
      byteBuffer.writeInt(f.getInt(obj));
      r.bytes += 4;
      r.dataInt += 1;
    }

    for (Field f : booleanFields) {
      byteBuffer.writeUnsignedByte(f.getBoolean(obj) ? 1 : 0);
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : enumFields) {
      byteBuffer.writeUnsignedByte(((Enum) f.get(obj)).ordinal());
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : unsignedByteFields) {
      byteBuffer.writeUnsignedByte(f.getInt(obj));
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : floatFields) {
      floatValues[index.floatIndex] = f.getFloat(obj);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : doubleFields) {
      floatValues[index.floatIndex] = (float) f.getDouble(obj);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : stringFields) {
      stringValues[index.stringIndex] = (String) f.get(obj);
      r.bytes += stringValues[index.stringIndex].length();
      index.stringIndex++;
      r.dataString += 1;
    }

    for (ClassMapping c : objectFields) {
      Object cpt = c.field.get(obj);

      if (cpt == null) {
        byteBuffer.writeUnsignedByte(0);

        for (int i = 0; i < c.sizeBytes; ++i) {
          byteBuffer.writeUnsignedByte(0);
          r.bytes += 1;
          r.dataInt += 1;
        }

        index.floatIndex += c.sizeFloat;
        index.stringIndex += c.sizeString;
      } else {
        byteBuffer.writeUnsignedByte(1);
        r.bytes += 1;
        r.dataInt += 1;

        c.setData(cpt, byteBuffer, floatValues, stringValues, index);
      }
    }

    for (Field f : doubleArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        floatValues[index.floatIndex] = (float) ((double[]) f.get(obj))[i];
        index.floatIndex++;
        r.bytes += 4;
        r.dataFloat += 1;
      }
    }

    for (Field f : shortArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeShort(((short[]) f.get(obj))[i]);
        r.bytes += 2;
        r.dataInt += 1;
      }
    }

    for (Field f : intArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeInt(((int[]) f.get(obj))[i]);
        r.bytes += 4;
        r.dataInt += 1;
      }
    }

    for (Field f : booleanArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeUnsignedByte(((boolean[]) f.get(obj))[i] ? 1 : 0);
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : unsignedByteFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeUnsignedByte(((int[]) f.get(obj))[i]);
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : stringArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        stringValues[index.stringIndex] = ((String[]) f.get(obj))[i];
        r.bytes += stringValues[index.stringIndex].length();
        index.stringIndex++;
        r.dataString += 1;
      }
    }

    for (ClassMapping c : objectArrayFields) {
      TileNetworkData updateAnnotation = c.field.getAnnotation(TileNetworkData.class);

      Object[] cpts = (Object[]) c.field.get(obj);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i)
        if (cpts[i] == null) {
          byteBuffer.writeUnsignedByte(0);

          for (int j = 0; j < c.sizeBytes; ++j) {
            byteBuffer.writeUnsignedByte(0);
            r.bytes += 1;
            r.dataInt += 1;
          }

          index.floatIndex += c.sizeFloat;
          index.stringIndex += c.sizeString;
        } else {
          byteBuffer.writeUnsignedByte(1);
          r.bytes += 1;
          r.dataInt += 1;

          c.setData(cpts[i], byteBuffer, floatValues, stringValues, index);
        }
    }
  }