Package marauroa.common.net.message

Source Code of marauroa.common.net.message.MessageS2CPerception$CachedCompressedPerception$CacheKey

/* $Id: MessageS2CPerception.java,v 1.13 2010/07/05 06:33:54 nhnb Exp $ */
/***************************************************************************
*                      (C) Copyright 2003 - Marauroa                      *
***************************************************************************
***************************************************************************
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
***************************************************************************/
package marauroa.common.net.message;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.DeflaterOutputStream;

import marauroa.common.Log4J;
import marauroa.common.TimeoutConf;
import marauroa.common.Utility;
import marauroa.common.game.DetailLevel;
import marauroa.common.game.IRPZone;
import marauroa.common.game.Perception;
import marauroa.common.game.RPObject;
import marauroa.common.net.InputSerializer;
import marauroa.common.net.OutputSerializer;

import org.apache.log4j.NDC;

/**
* This message indicate the client the objects that the server has determined
* that this client is able to see.
*
* @see marauroa.common.net.message.Message
* @see marauroa.common.game.IRPZone
*/
public class MessageS2CPerception extends Message {

  /** the logger instance. */
  private static final marauroa.common.Logger logger = Log4J
          .getLogger(MessageS2CPerception.class);

  private byte typePerception;

  private int timestampPerception;

  private IRPZone.ID zoneid;

  private List<RPObject> addedRPObjects;

  private List<RPObject> modifiedAddedAttribsRPObjects;

  private List<RPObject> modifiedDeletedAttribsRPObjects;

  private List<RPObject> deletedRPObjects;

  private RPObject myRPObjectModifiedAdded;

  private RPObject myRPObjectModifiedDeleted;

  private static CachedCompressedPerception cache = CachedCompressedPerception.get();

  /** Constructor for allowing creation of an empty message */
  public MessageS2CPerception() {
    super(MessageType.S2C_PERCEPTION, null);
  }

  /**
   * Constructor with a TCP/IP source/destination of the message and
   * perception to send.
   *
   * @param source
   *            The TCP/IP address associated to this message
   * @param perception
   *            the perception we are going to send.
   */
  public MessageS2CPerception(SocketChannel source, Perception perception) {
    super(MessageType.S2C_PERCEPTION, source);

    typePerception = perception.type;
    zoneid = perception.zoneid;
    addedRPObjects = perception.addedList;
    modifiedAddedAttribsRPObjects = perception.modifiedAddedList;
    modifiedDeletedAttribsRPObjects = perception.modifiedDeletedList;
    deletedRPObjects = perception.deletedList;
  }

  public void setMyRPObject(RPObject added, RPObject deleted) {
    myRPObjectModifiedAdded = added;
    myRPObjectModifiedDeleted = deleted;
  }

  public RPObject getMyRPObjectAdded() {
    return myRPObjectModifiedAdded;
  }

  public RPObject getMyRPObjectDeleted() {
    return myRPObjectModifiedDeleted;
  }

  public void setPerceptionTimestamp(int ts) {
    timestampPerception = ts;
  }

  public int getPerceptionTimestamp() {
    return timestampPerception;
  }

  public byte getPerceptionType() {
    return typePerception;
  }

  public IRPZone.ID getRPZoneID() {
    return zoneid;
  }

  /**
   * This method returns the list of modified objects
   *
   * @return List<RPObject> of added objects
   */
  public List<RPObject> getAddedRPObjects() {
    return addedRPObjects;
  }

  /**
   * This method returns the list of modified objects
   *
   * @return List<RPObject> of modified objects that has attributes added
   */
  public List<RPObject> getModifiedAddedRPObjects() {
    return modifiedAddedAttribsRPObjects;
  }

  /**
   * This method returns the list of modified objects
   *
   * @return List<RPObject> of modified objects that has attributes removed
   */
  public List<RPObject> getModifiedDeletedRPObjects() {
    return modifiedDeletedAttribsRPObjects;
  }

  /**
   * This method returns the list of deleted objects
   *
   * @return List<RPObject> of deleted objects
   */
  public List<RPObject> getDeletedRPObjects() {
    return deletedRPObjects;
  }

  /**
   * This method returns a String that represent the object
   *
   * @return a string representing the object.
   */
  @Override
  public String toString() {
    StringBuilder perception_string = new StringBuilder();
    perception_string.append("Type: " + typePerception + " Timestamp: " + timestampPerception
            + ") contents: ");

    perception_string.append("\n  zoneid: " + zoneid + "\n");
    perception_string.append("\n  added: \n");
    for (RPObject object : addedRPObjects) {
      perception_string.append("    " + object + "\n");
    }

    perception_string.append("\n  modified added: \n");
    for (RPObject object : modifiedAddedAttribsRPObjects) {
      perception_string.append("    " + object + "\n");
    }

    perception_string.append("\n  modified deleted: \n");
    for (RPObject object : modifiedDeletedAttribsRPObjects) {
      perception_string.append("    " + object + "\n");
    }

    perception_string.append("\n  deleted: \n");
    for (RPObject object : deletedRPObjects) {
      perception_string.append("    " + object + "\n");
    }

    perception_string.append("\n  my object modified added: \n");
    perception_string.append("    " + myRPObjectModifiedAdded + "\n");
    perception_string.append("\n  my object modified deleted: \n");
    perception_string.append("    " + myRPObjectModifiedDeleted + "\n");

    return perception_string.toString();
  }

  @Override
  public void writeObject(marauroa.common.net.OutputSerializer out) throws IOException {
    if (logger.isDebugEnabled()) {
      logger.debug("writing Object: [" + this + "]");
    }

    super.writeObject(out);
    out.write(getPrecomputedStaticPartPerception());
    out.write(getDynamicPartPerception(out.getProtocolVersion()));
  }

  private void setZoneid(RPObject object, String zoneid) {
    object.put("zoneid", zoneid);

//    Iterator<RPSlot> it = object.slotsIterator();
//    while (it.hasNext()) {
//      RPSlot slot = it.next();
//      for (RPObject son : slot) {
//        setZoneid(son, zoneid);
//      }
//    }
  }

  @Override
  public void readObject(marauroa.common.net.InputSerializer in) throws IOException {
    super.readObject(in);

    byte[] byteArray = in.readByteArray();
    ByteArrayInputStream array = new ByteArrayInputStream(byteArray);
    java.util.zip.InflaterInputStream szlib = new java.util.zip.InflaterInputStream(array,
            new java.util.zip.Inflater());
    InputSerializer ser = new InputSerializer(szlib);
    ser.setProtocolVersion(protocolVersion);

    try {
      typePerception = ser.readByte();
      zoneid = (IRPZone.ID) ser.readObject(new IRPZone.ID(""));
      addedRPObjects = new LinkedList<RPObject>();
      deletedRPObjects = new LinkedList<RPObject>();
      modifiedAddedAttribsRPObjects = new LinkedList<RPObject>();
      modifiedDeletedAttribsRPObjects = new LinkedList<RPObject>();

      int added = ser.readInt();

      if (added > TimeoutConf.MAX_ARRAY_ELEMENTS) {
        throw new IOException("Illegal request of an list of " + String.valueOf(added)
                + " size");
      }
      logger.debug(added + " added objects.");
      for (int i = 0; i < added; ++i) {
        RPObject object = (RPObject) ser.readObject(new RPObject());
        setZoneid(object, zoneid.getID());
        addedRPObjects.add(object);
      }

      int modAdded = ser.readInt();

      if (modAdded > TimeoutConf.MAX_ARRAY_ELEMENTS) {
        throw new IOException("Illegal request of an list of " + String.valueOf(modAdded)
                + " size");
      }
      logger.debug(modAdded + " modified Added objects..");
      for (int i = 0; i < modAdded; ++i) {
        RPObject object = (RPObject) ser.readObject(new RPObject());
        setZoneid(object, zoneid.getID());
        modifiedAddedAttribsRPObjects.add(object);
      }

      int modDeleted = ser.readInt();

      if (modDeleted > TimeoutConf.MAX_ARRAY_ELEMENTS) {
        throw new IOException("Illegal request of an list of " + String.valueOf(modDeleted)
                + " size");
      }
      logger.debug(modDeleted + " modified Deleted objects..");
      for (int i = 0; i < modDeleted; ++i) {
        RPObject object = (RPObject) ser.readObject(new RPObject());
        setZoneid(object, zoneid.getID());
        modifiedDeletedAttribsRPObjects.add(object);
      }

      int del = ser.readInt();

      if (del > TimeoutConf.MAX_ARRAY_ELEMENTS) {
        throw new IOException("Illegal request of an list of " + String.valueOf(del)
                + " size");
      }
      logger.debug(del + " deleted objects..");
      for (int i = 0; i < del; ++i) {
        RPObject object = (RPObject) ser.readObject(new RPObject());
        setZoneid(object, zoneid.getID());
        deletedRPObjects.add(object);
      }
    } catch (IOException ioe) {
      InputStream stream = new java.util.zip.InflaterInputStream(new ByteArrayInputStream(
              byteArray), new java.util.zip.Inflater());
      NDC.push("message is [" + this + "]\n");
      NDC.push("message dump is [\n" + Utility.dumpInputStream(stream) + "\n]\n");
      logger.error("error in getMessage", ioe);
      NDC.pop();
      NDC.pop();
      return;
    }

    /** Dynamic part */
    array = new ByteArrayInputStream(in.readByteArray());
    ser = new InputSerializer(array);
    ser.setProtocolVersion(protocolVersion);

    timestampPerception = ser.readInt();

    logger.debug("read My RPObject");
    byte modifiedAddedMyRPObjectPresent = ser.readByte();
    if (modifiedAddedMyRPObjectPresent == 1) {
      myRPObjectModifiedAdded = (RPObject) ser.readObject(new RPObject());
      setZoneid(myRPObjectModifiedAdded, zoneid.getID());
    } else {
      myRPObjectModifiedAdded = null;
    }

    byte modifiedDeletedMyRPObjectPresent = ser.readByte();
    if (modifiedDeletedMyRPObjectPresent == 1) {
      myRPObjectModifiedDeleted = (RPObject) ser.readObject(new RPObject());
      setZoneid(myRPObjectModifiedDeleted, zoneid.getID());
    } else {
      myRPObjectModifiedDeleted = null;
    }
  }

  static class CachedCompressedPerception {

    static class CacheKey {

      private byte type;

      private IRPZone.ID zoneid;

      private int protocolVersion;

      public CacheKey(byte type, IRPZone.ID zoneid, int protocolVersion) {
        this.type = type;
        this.zoneid = zoneid;
        this.protocolVersion = protocolVersion;
      }

      @Override
      public boolean equals(Object obj) {
        if (obj instanceof CacheKey) {
          CacheKey a = (CacheKey) obj;
          if (a.type == type && a.zoneid.equals(zoneid) && a.protocolVersion == protocolVersion) {
            return true;
          }
        }

        return false;
      }

      @Override
      public int hashCode() {
        return (type + 1) * zoneid.hashCode() * protocolVersion;
      }
    }

    private Map<CacheKey, byte[]> cachedContent;

    private CachedCompressedPerception() {
      cachedContent = new HashMap<CacheKey, byte[]>();
    }

    static CachedCompressedPerception instance;

    synchronized static public CachedCompressedPerception get() {
      if (instance == null) {
        instance = new CachedCompressedPerception();
      }

      return instance;
    }

    synchronized public void clear() {
      cachedContent.clear();
    }

    synchronized public byte[] get(MessageS2CPerception perception) throws IOException {
      CacheKey key = new CacheKey(perception.typePerception, perception.zoneid, perception.protocolVersion);

      if (!cachedContent.containsKey(key)) {
        logger.debug("Perception not found in cache");
        ByteArrayOutputStream array = new ByteArrayOutputStream();
        DeflaterOutputStream out_stream = new DeflaterOutputStream(array);
        OutputSerializer serializer = new OutputSerializer(out_stream);
        serializer.setProtocolVersion(perception.getProtocolVersion());

        perception.computeStaticPartPerception(serializer);
        out_stream.close();
        byte[] content = array.toByteArray();

        cachedContent.put(key, content);
      } else {
        logger.debug("Perception FOUND in cache");
      }

      return cachedContent.get(key);
    }
  }

  public static void clearPrecomputedPerception() {
    cache.clear();
  }

  private byte[] getPrecomputedStaticPartPerception() throws IOException {
    return cache.get(this);
  }

  private byte[] getDynamicPartPerception(int protocolVersion) throws IOException {
    ByteArrayOutputStream array = new ByteArrayOutputStream();
    OutputSerializer serializer = new OutputSerializer(array);
    serializer.setProtocolVersion(protocolVersion);

    serializer.write(timestampPerception);
    if (myRPObjectModifiedAdded == null) {
      serializer.write((byte) 0);
    } else {
      serializer.write((byte) 1);
      myRPObjectModifiedAdded.writeObject(serializer, DetailLevel.PRIVATE);
    }

    if (myRPObjectModifiedDeleted == null) {
      serializer.write((byte) 0);
    } else {
      serializer.write((byte) 1);
      myRPObjectModifiedDeleted.writeObject(serializer, DetailLevel.PRIVATE);
    }

    return array.toByteArray();
  }

  private void computeStaticPartPerception(OutputSerializer ser) throws IOException {
    ser.write(typePerception);
    ser.write(zoneid);

    ser.write(addedRPObjects.size());
    for (RPObject object : addedRPObjects) {
      ser.write(object);
    }

    ser.write(modifiedAddedAttribsRPObjects.size());
    for (RPObject object : modifiedAddedAttribsRPObjects) {
      ser.write(object);
    }

    ser.write(modifiedDeletedAttribsRPObjects.size());
    for (RPObject object : modifiedDeletedAttribsRPObjects) {
      ser.write(object);
    }

    ser.write(deletedRPObjects.size());
    for (RPObject object : deletedRPObjects) {
      ser.write(object);
    }
  }
}
TOP

Related Classes of marauroa.common.net.message.MessageS2CPerception$CachedCompressedPerception$CacheKey

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.