public void handleUpdateWorldNode(long oid, WorldManagerClient.UpdateWorldNodeMessage wnodeMsg) {
   PerceiverData perceiverData = perceiverDataMap.get(oid);
   if (perceiverData == null) {
     if (Log.loggingDebug)
       Log.debug(
           "ProximityTracker.handleMessage: ignoring updateWNMsg for oid "
               + oid
               + " because PerceptionData for oid not found");
     return;
   }
   BasicWorldNode bwnode = wnodeMsg.getWorldNode();
   if (Log.loggingDebug)
     Log.debug(
         "ProximityTracker.handleMessage: UpdateWnode for "
             + oid
             + ", loc "
             + bwnode.getLoc()
             + ", dir "
             + bwnode.getDir());
   if (perceiverData.wnode != null) {
     perceiverData.previousLoc = perceiverData.lastLoc;
     perceiverData.wnode.setDirLocOrient(bwnode);
     perceiverData.wnode.setInstanceOid(bwnode.getInstanceOid());
     perceiverData.lastLoc = perceiverData.wnode.getLoc();
   } else
     Log.error(
         "ProximityTracker.handleMessage: In UpdateWorldNodeMessage for oid "
             + oid
             + ", perceiverData.wnode is null!");
   updateEntity(perceiverData);
 }
 public void run() {
   while (true) {
     LinkedList<PacketCallbackStruct> list = null;
     try {
       queuedPacketCallbacksLock.lock();
       try {
         queuedPacketCallbacksNotEmpty.await();
       } catch (Exception e) {
         Log.error(
             "RDPServer.PacketCallbackThread: queuedPacketCallbacksNotEmpty.await() caught exception "
                 + e.getMessage());
       }
       list = queuedPacketCallbacks;
       queuedPacketCallbacks = new LinkedList<PacketCallbackStruct>();
     } finally {
       queuedPacketCallbacksLock.unlock();
     }
     if (Log.loggingNet)
       Log.net("RDPServer.PacketCallbackThread: Got " + list.size() + " queued packets");
     for (PacketCallbackStruct pcs : list) {
       try {
         callbackProcessPacket(pcs.cb, pcs.con, pcs.packet);
       } catch (Exception e) {
         Log.exception("RDPServer.PacketCallbackThread: ", e);
       }
     }
   }
 }
 public void handleMessage(Message msg, int flags) {
   try {
     lock.lock();
     if (activated == false) return; // return true;
     if (msg.getMsgType() == Behavior.MSG_TYPE_COMMAND) {
       Behavior.CommandMessage cmdMsg = (Behavior.CommandMessage) msg;
       String command = cmdMsg.getCmd();
       // Remove the executor, because anything we do will
       // end the current execution.
       Engine.getExecutor().remove(this);
       if (Log.loggingDebug)
         Log.debug(
             "BaseBehavior.onMessage: command = "
                 + command
                 + "; oid = "
                 + obj.getOid()
                 + "; name "
                 + obj.getName());
       if (command.equals(MSG_CMD_TYPE_GOTO)) {
         GotoCommandMessage gotoMsg = (GotoCommandMessage) msg;
         Point destination = gotoMsg.getDestination();
         mode = MSG_CMD_TYPE_GOTO;
         roamingBehavior = true;
         gotoSetup(destination, gotoMsg.getSpeed());
       } else if (command.equals(MSG_CMD_TYPE_STOP)) {
         followTarget = null;
         pathState.clear();
         obj.getWorldNode().setDir(new MVVector(0, 0, 0));
         obj.updateWorldNode();
         mode = MSG_CMD_TYPE_STOP;
         // If roamingBehavior is set, that means that we
         // used formerly had a roaming behavior, so send
         // an ArrivedEventMessage so that the other
         // behavior starts up again.
         if (roamingBehavior) {
           try {
             Engine.getAgent().sendBroadcast(new ArrivedEventMessage(obj));
           } catch (Exception e) {
             Log.error(
                 "BaseBehavior.onMessage: Error sending ArrivedEventMessage, error was '"
                     + e.getMessage()
                     + "'");
             throw new RuntimeException(e);
           }
         }
       } else if (command.equals(BaseBehavior.MSG_CMD_TYPE_FOLLOW)) {
         FollowCommandMessage followMsg = (FollowCommandMessage) msg;
         mode = MSG_CMD_TYPE_FOLLOW;
         followSetup(followMsg.getTarget(), followMsg.getSpeed());
       }
     } else if (msg.getMsgType() == WorldManagerClient.MSG_TYPE_MOB_PATH_CORRECTION) {
       Engine.getExecutor().remove(this);
       interpolatePath();
       interpolatingPath = false;
     }
     // return true;
   } finally {
     lock.unlock();
   }
 }
 public void addTrackedPerceiver(
     Long perceiverOid, InterpolatedWorldNode wnode, Integer reactionRadius) {
   lock.lock();
   try {
     if (perceiverDataMap.containsKey(perceiverOid)) {
       // Don't add the object more than once.
       Log.error(
           "ProximityTracker.addTrackedPerceiver: perceiverOid "
               + perceiverOid
               + " is already in the set of local objects, for ProximityTracker instance "
               + this);
       return;
     }
     PerceiverData perceiverData = new PerceiverData(perceiverOid, reactionRadius, wnode);
     perceiverDataMap.put(perceiverOid, perceiverData);
   } finally {
     lock.unlock();
   }
   if (Log.loggingDebug)
     Log.debug(
         "ProximityTracker.addTrackedPerceiver: perceiverOid="
             + perceiverOid
             + " reactionRadius="
             + reactionRadius
             + " instanceOid="
             + instanceOid);
 }
    public synchronized void pluginAvailable(String pluginType, String pluginName) {
      if (Log.loggingDebug)
        Log.debug("Plugin available type=" + pluginType + " name=" + pluginName);

      PluginSpec pluginSpec = plugins.get(pluginType);
      if (pluginSpec == null) {
        Log.error("DomainServer: unexpected plugin type=" + pluginType + " name=" + pluginName);
        return;
      }

      pluginSpec.running++;
      if (pluginSpec.running > pluginSpec.expected) {
        Log.warn(
            "DomainServer: more plugins than expected, type="
                + pluginType
                + " name="
                + pluginName
                + " expected="
                + pluginSpec.expected
                + " available="
                + pluginSpec.running);
      }

      if (pluginSpec.running >= pluginSpec.expected) {
        this.notifyAll();
      }
    }
    public void run() {
      synchronized (pluginStartGroup) {
        waitForDependencies();
      }

      if (Log.loggingDebug)
        Log.debug(
            "Dependency satisfied for type="
                + await.getPluginType()
                + " name="
                + await.getPluginName());

      MVByteBuffer buffer = new MVByteBuffer(1024);
      ResponseMessage response = new ResponseMessage(await);
      Message.toBytes(response, buffer);
      buffer.flip();

      try {
        if (!ChannelUtil.writeBuffer(buffer, agentSocket)) {
          Log.error("could not write await dependencies response");
        }
      } catch (java.io.IOException e) {
        Log.exception("could not write await dependencies response", e);
      }
    }
    public boolean handleMessage() throws java.io.IOException {
      ByteBuffer buf = ByteBuffer.allocate(4);
      int nBytes = ChannelUtil.fillBuffer(buf, agentSocket);
      if (nBytes == 0) {
        Log.info("DomainServer: agent closed connection " + agentSocket);
        return false;
      }
      if (nBytes < 4) {
        Log.error("DomainServer: invalid message " + nBytes);
        return false;
      }

      int msgLen = buf.getInt();
      if (msgLen < 0) {
        return false;
      }

      MVByteBuffer buffer = new MVByteBuffer(msgLen);
      nBytes = ChannelUtil.fillBuffer(buffer.getNioBuf(), agentSocket);
      if (nBytes == 0) {
        Log.info("DomainServer: agent closed connection " + agentSocket);
        return false;
      }
      if (nBytes < msgLen) {
        Log.error(
            "DomainServer: invalid message, expecting "
                + msgLen
                + " got "
                + nBytes
                + " from "
                + agentSocket);
        return false;
      }

      Message message = (Message) MarshallingRuntime.unmarshalObject(buffer);

      if (message instanceof AgentHelloMessage) {
        // Successfully added agent, clear our socket var
        // so we don't close it.
        if (handleAgentHello((AgentHelloMessage) message)) agentSocket = null;
        return false;
      } else if (message instanceof AllocNameMessage)
        handleAllocName((AllocNameMessage) message, agentSocket);

      return true;
    }
 public void RemoveVoiceGroup() {
   int error = 0;
   // Remove voice group voice server
   error = VoiceClient.removeVoiceGroup(this.GetGroupOid());
   if (error != VoiceClient.SUCCESS) {
     Log.error(
         "MarsGroup.RemoveVoiceGroup : Remove Voice Group Response - "
             + VoiceClient.errorString(error));
   }
 }
  void handlePluginAvailable(PluginAvailableMessage available, SocketChannel agentSocket)
      throws java.io.IOException {
    if (available.getMsgType() != MessageTypes.MSG_TYPE_PLUGIN_AVAILABLE) {
      Log.error("DomainServer: invalid available message");
      return;
    }

    if (pluginStartGroup == null) {
      Log.error(
          "DomainServer: no start group defined for plugin type="
              + available.getPluginType()
              + " name="
              + available.getPluginName());
      // ## return error
      return;
    }

    pluginStartGroup.pluginAvailable(available.getPluginType(), available.getPluginName());
  }
  void handleAwaitPluginDependents(AwaitPluginDependentsMessage await, SocketChannel agentSocket)
      throws java.io.IOException {
    if (await.getMsgType() != MessageTypes.MSG_TYPE_AWAIT_PLUGIN_DEPENDENTS) {
      Log.error("DomainServer: invalid await message");
      return;
    }

    if (pluginStartGroup == null) {
      Log.error(
          "DomainServer: no start group defined for plugin type="
              + await.getPluginType()
              + " name="
              + await.getPluginName());
      // ## return error
      return;
    }

    new Thread(new PluginDependencyWatcher(await, agentSocket)).start();
  }
  protected void SetupVoiceGroup() {
    int error = 0;

    // Create a new voice chat group specific to this group that is non-positional
    error = VoiceClient.addVoiceGroup(this.GetGroupOid(), false, 4);
    if (error != VoiceClient.SUCCESS) {
      Log.error(
          "MarsGroup.SetupGroupVoice : Create Voice Group Response - "
              + VoiceClient.errorString(error));
    }
  }
 /**
  * a DatagramChannel has data ready - process all the pending packets, whether its for a
  * rdpserversocket or rdpconnection.
  */
 void processActiveChannel(DatagramChannel dc) throws ClosedChannelException {
   RDPPacket packet;
   int count = 0;
   // read in the packet
   try {
     Set<RDPConnection> needsAckConnections = new HashSet<RDPConnection>();
     while ((packet = RDPServer.receivePacket(dc)) != null) {
       if (Log.loggingNet)
         Log.net(
             "RDPServer.processActiveChannel: Starting iteration with count of "
                 + count
                 + " packets");
       // see if there is a connection already for this packet
       InetAddress remoteAddr = packet.getInetAddress();
       int remotePort = packet.getPort();
       int localPort = dc.socket().getLocalPort();
       ConnectionInfo conInfo = new ConnectionInfo(remoteAddr, remotePort, localPort);
       RDPConnection con = RDPServer.getConnection(dc, conInfo);
       if (con != null) {
         if (Log.loggingNet)
           Log.net("RDPServer.processActiveChannel: found an existing connection: " + con);
         count++;
         if (processExistingConnection(con, packet)) needsAckConnections.add(con);
         // Prevent this from blocking getActiveChannels by
         // putting an upper bound on the number of packets
         // processed
         if (count >= 20) break;
         continue;
       } else {
         Log.net("RDPServer.processActiveChannel: did not find an existing connection");
       }
       // there is no connection,
       // see if there is a socket listening for new connection
       RDPServerSocket rdpSocket = RDPServer.getRDPSocket(dc);
       if (rdpSocket != null) {
         count++;
         processNewConnection(rdpSocket, packet);
         return;
       }
       return;
     }
     // Finally, send out the acks
     for (RDPConnection con : needsAckConnections) {
       RDPPacket replyPacket = new RDPPacket(con);
       con.sendPacketImmediate(replyPacket, false);
     }
   } catch (ClosedChannelException ex) {
     Log.error("RDPServer.processActiveChannel: ClosedChannel " + dc.socket());
     throw ex;
   } finally {
     if (Log.loggingNet)
       Log.net("RDPServer.processActiveChannel: Returning after processing " + count + " packets");
   }
 }
    boolean handleAgentHello(AgentHelloMessage agentHello) throws java.io.IOException {
      if (agentHello.getMsgType() != MessageTypes.MSG_TYPE_AGENT_HELLO) {
        Log.error(
            "DomainServer: invalid agent hello, got message type "
                + agentHello.getMsgType()
                + " from "
                + agentSocket);
        return false;
      }

      int agentId;
      synchronized (domainServer) {
        agentId = getNextAgentId();
        if (!agentNames.contains(agentHello.getAgentName()))
          agentNames.add(agentHello.getAgentName());
      }

      MVByteBuffer buffer = new MVByteBuffer(1024);
      HelloResponseMessage helloResponse =
          new HelloResponseMessage(agentId, domainStartTime, agentNames, encodedDomainKey);
      Message.toBytes(helloResponse, buffer);
      buffer.flip();

      if (!ChannelUtil.writeBuffer(buffer, agentSocket)) {
        Log.error("could not write to new agent, " + agentSocket);
        return false;
      }

      addNewAgent(
          agentId,
          agentSocket,
          agentHello.getAgentName(),
          agentHello.getAgentIP(),
          agentHello.getAgentPort(),
          agentHello.getFlags());

      return true;
    }
 public List<Long> getOidsInRadius(long perceiverOid) {
   lock.lock();
   try {
     PerceiverData perceiverData = perceiverDataMap.get(perceiverOid);
     if (perceiverData == null) {
       Log.error(
           "ProximityTracker.getOidsInRadius: perceptionData for oid "
               + perceiverOid
               + " is null");
       return new LinkedList<Long>();
     } else return new LinkedList<Long>(perceiverData.inRangeOids);
   } finally {
     lock.unlock();
   }
 }
  protected boolean maybeAddPerceivedObject(PerceptionMessage.ObjectNote objectNote) {
    ObjectType objType = (ObjectType) objectNote.getObjectType();
    long perceivedOid = objectNote.getSubject();
    long perceiverOid = objectNote.getTarget();
    if (perceivedOid == perceiverOid) return true;
    boolean callbackNixedIt = false;
    if (remoteObjectFilter != null)
      callbackNixedIt = !remoteObjectFilter.objectShouldBeTracked(perceivedOid, objectNote);
    if (callbackNixedIt || !(objType.isMob())) {
      //             if (Log.loggingDebug)
      //                 Log.debug("ProximityTracker.maybeAddPerceivedObject: ignoring oid=" +
      // perceivedOid
      //                     + " objType=" + objType
      //                     + " detected by " + perceiverOid
      //                     + ", instanceOid=" + instanceOid);
      return false;
    }

    if (Log.loggingDebug)
      Log.debug(
          "ProximityTracker.maybeAddPerceivedObject: oid="
              + perceivedOid
              + " objType="
              + objType
              + " detected by "
              + perceiverOid
              + ", instanceOid="
              + instanceOid);
    lock.lock();
    try {
      PerceiverData perceiverData = perceiverDataMap.get(perceiverOid);
      if (perceiverData == null) {
        Log.error(
            "ProximityTracker.maybeAddPerceivedObject: got perception msg with perceived obj oid="
                + perceivedOid
                + " for unknown perceiver="
                + perceiverOid);
        return false;
      }
      perceiverData.perceivedOids.add(perceivedOid);
      PerceiverData perceivedData = perceiverDataMap.get(perceivedOid);
      if (perceivedData != null) testProximity(perceiverData, perceivedData, true, false);
    } finally {
      lock.unlock();
    }
    return true;
  }
  /** make sure the packet as the remote address and remote port set */
  static void sendPacket(DatagramChannel dc, RDPPacket packet) {

    sendMeter.add();

    // allocate a buffer
    int bufSize = 100 + (packet.numEacks() * 4);
    if (packet.getData() != null) {
      bufSize += packet.getData().length;
      sendDataMeter.add();
    }
    MVByteBuffer buf = new MVByteBuffer(bufSize);
    packet.toByteBuffer(buf); // function flips the buffer

    int remotePort = packet.getPort();
    InetAddress remoteAddr = packet.getInetAddress();

    if ((remotePort < 0) || (remoteAddr == null)) {
      throw new MVRuntimeException("RDPServer.sendPacket: remotePort or addr is null");
    }

    try {
      int bytes = dc.send(buf.getNioBuf(), new InetSocketAddress(remoteAddr, remotePort));
      if (bytes == 0) {
        Log.error("RDPServer.sendPacket: could not send packet, size=" + bufSize);
      }

      if (Log.loggingNet)
        Log.net(
            "RDPServer.sendPacket: remoteAddr="
                + remoteAddr
                + ", remotePort="
                + remotePort
                + ", numbytes sent="
                + bytes);
    } catch (java.io.IOException e) {
      Log.exception(
          "RDPServer.sendPacket: remoteAddr="
              + remoteAddr
              + ", remotePort="
              + remotePort
              + ", got exception",
          e);
      throw new MVRuntimeException("RDPServer.sendPacket", e);
    }
  }
  void handleAllocName(AllocNameMessage allocName, SocketChannel agentSocket)
      throws java.io.IOException {
    if (allocName.getMsgType() != MessageTypes.MSG_TYPE_ALLOC_NAME) {
      Log.error("DomainServer: invalid alloc name message");
      return;
    }

    String agentName = allocName(allocName.getType(), allocName.getAgentName());

    MVByteBuffer buffer = new MVByteBuffer(1024);
    AllocNameResponseMessage allocNameResponse = new AllocNameResponseMessage(allocName, agentName);
    Message.toBytes(allocNameResponse, buffer);
    buffer.flip();

    if (!ChannelUtil.writeBuffer(buffer, agentSocket)) {
      throw new RuntimeException("could not write alloc name response");
    }
  }
  /**
   * Track union of perceived objects and keep filter in-sync. The filter must be a {@link
   * PerceptionFilter} and the message must be a {@link PerceptionMessage}.
   *
   * @param triggeringMessage The matched message.
   * @param triggeringFilter The matched filter.
   * @param agent The local message agent.
   */
  public synchronized void trigger(
      Message triggeringMessage, IFilter triggeringFilter, MessageAgent agent) {
    PerceptionMessage message = (PerceptionMessage) triggeringMessage;

    List<ObjectNote> gainObjects = message.getGainObjects();
    List<ObjectNote> lostObjects = message.getLostObjects();

    if (gainObjects != null) {
      List<PerceptionFilter.TypedSubject> newSubjects =
          new ArrayList<PerceptionFilter.TypedSubject>(gainObjects.size());

      for (ObjectNote gain : gainObjects) {
        IntHolder refCount = objectRefs.get(gain.subjectOid);
        if (refCount == null) {
          objectRefs.put(gain.subjectOid, new IntHolder(1));
          newSubjects.add(new PerceptionFilter.TypedSubject(gain.subjectOid, gain.objectType));
        } else refCount.count++;
      }

      if (newSubjects.size() > 0) {
        if (Log.loggingDebug) Log.debug("PerceptionTrigger adding " + newSubjects.size());
        filter.addSubjects(newSubjects);
      }
    }

    if (lostObjects == null) return;

    List<Long> freeOids = new ArrayList<Long>(lostObjects.size());

    for (ObjectNote lost : lostObjects) {
      IntHolder refCount = objectRefs.get(lost.subjectOid);
      if (refCount == null) Log.error("PerceptionTrigger: duplicate lost " + lost.subjectOid);
      else if (refCount.count == 1) {
        objectRefs.remove(lost.subjectOid);
        freeOids.add(lost.subjectOid);
      } else refCount.count--;
    }

    if (freeOids.size() > 0) {
      if (Log.loggingDebug) Log.debug("PerceptionTrigger removing " + freeOids.size());
      filter.removeSubjects(freeOids);
    }
  }
 public void run() {
   try {
     lock.lock();
     if (activated == false) {
       return;
     }
     try {
       if (mode == MSG_CMD_TYPE_GOTO) {
         gotoUpdate();
       } else if (mode == MSG_CMD_TYPE_FOLLOW) {
         followUpdate();
       } else if (mode == MSG_CMD_TYPE_STOP) {
       } else {
         Log.error("BaseBehavior.run: invalid mode");
       }
     } catch (Exception e) {
       Log.exception("BaseBehavior.run caught exception raised during run for mode = " + mode, e);
       throw new RuntimeException(e);
     }
   } finally {
     lock.unlock();
   }
 }
 static void callbackProcessPacket(
     ClientConnection.MessageCallback pcb, ClientConnection clientCon, RDPPacket packet) {
   if (packet.isNul()) {
     return;
   }
   byte[] data = packet.getData();
   MVByteBuffer buf = new MVByteBuffer(data);
   RDPConnection con = (RDPConnection) clientCon;
   // If this is a multiple-message message . . .
   if (buf.getLong() == -1 && buf.getInt() == RDPConnection.aggregatedMsgId) {
     con.aggregatedReceives++;
     PacketAggregator.allAggregatedReceives++;
     // Get the count of sub buffers
     int size = buf.getInt();
     con.receivedMessagesAggregated += size;
     PacketAggregator.allReceivedMessagesAggregated += size;
     if (Log.loggingNet)
       Log.net(
           "RDPServer.callbackProcessPacket: processing aggregated message with "
               + size
               + " submessages");
     MVByteBuffer subBuf = null;
     for (int i = 0; i < size; i++) {
       try {
         subBuf = buf.getByteBuffer();
       } catch (Exception e) {
         Log.error("In CallbackThread, error getting aggregated subbuffer: " + e.getMessage());
       }
       if (subBuf != null) pcb.processPacket(con, subBuf);
     }
   } else {
     con.unaggregatedReceives++;
     PacketAggregator.allUnaggregatedReceives++;
     buf.rewind();
     pcb.processPacket(con, buf);
   }
 }
  /**
   * we have a packet that belongs to the passed in connection. process the packet for the
   * connection. It returns true if the connection is open and the packet was a data packet
   */
  boolean processExistingConnection(RDPConnection con, RDPPacket packet) {

    if (Log.loggingNet)
      Log.net("RDPServer.processExistingConnection: con state=" + con + ", packet=" + packet);
    packetCounter.add();

    int state = con.getState();
    if (state == RDPConnection.LISTEN) {
      // something is wrong, we shouldn't be here
      // we get to this method after looking in the connections map
      // but all LISTEN connections should be listed direct
      // from serversockets
      Log.error("RDPServer.processExistingConnection: connection shouldnt be in LISTEN state");
      return false;
    }
    if (state == RDPConnection.SYN_SENT) {
      if (!packet.isAck()) {
        Log.warn("got a non-ack packet when we're in SYN_SENT");
        return false;
      }
      if (!packet.isSyn()) {
        Log.warn("got a non-syn packet when we're in SYN_SENT");
        return false;
      }
      if (Log.loggingNet) Log.net("good: got syn-ack packet in syn_sent");

      // make sure its acking our initial segment #
      if (packet.getAckNum() != con.getInitialSendSeqNum()) {
        if (Log.loggingNet) Log.net("syn's ack number does not match initial seq #");
        return false;
      }

      con.setRcvCur(packet.getSeqNum());
      con.setRcvIrs(packet.getSeqNum());
      con.setMaxSendUnacks(packet.getSendUnacks());
      con.setMaxReceiveSegmentSize(packet.getMaxRcvSegmentSize());
      con.setSendUnackd(packet.getAckNum() + 1);

      // ack first before setting state to open
      // otherwise some other thread will get woken up and send data
      // before we send the ack
      if (Log.loggingNet) Log.net("new connection state: " + con);
      RDPPacket replyPacket = new RDPPacket(con);
      con.sendPacketImmediate(replyPacket, false);
      con.setState(RDPConnection.OPEN);
      return false;
    }
    if (state == RDPConnection.SYN_RCVD) {
      if (packet.getSeqNum() <= con.getRcvIrs()) {
        Log.error("seqnum is not above rcv initial seq num");
        return false;
      }
      if (packet.getSeqNum() > (con.getRcvCur() + (con.getRcvMax() * 2))) {
        Log.error("seqnum is too big");
        return false;
      }
      if (packet.isAck()) {
        if (packet.getAckNum() == con.getInitialSendSeqNum()) {
          if (Log.loggingNet) Log.net("got ack for our syn - setting state to open");
          con.setState(RDPConnection.OPEN); // this will notify()

          // call the accept callback
          // first find the serversocket
          DatagramChannel dc = con.getDatagramChannel();
          if (dc == null) {
            throw new MVRuntimeException(
                "RDPServer.processExistingConnection: no datagramchannel for connection that just turned OPEN");
          }
          RDPServerSocket rdpSocket = RDPServer.getRDPSocket(dc);
          if (rdpSocket == null) {
            throw new MVRuntimeException(
                "RDPServer.processExistingConnection: no socket for connection that just turned OPEN");
          }
          ClientConnection.AcceptCallback acceptCB = rdpSocket.getAcceptCallback();
          if (acceptCB != null) {
            acceptCB.acceptConnection(con);
          } else {
            Log.warn("serversocket has no accept callback");
          }
          if (Log.loggingNet)
            Log.net(
                "RDPServer.processExistingConnection: got ACK, removing from unack list: "
                    + packet.getSeqNum());
          con.removeUnackPacket(packet.getSeqNum());
        }
      }
    }
    if (state == RDPConnection.CLOSE_WAIT) {
      // reply with a reset on all packets
      if (!packet.isRst()) {
        RDPPacket rstPacket = RDPPacket.makeRstPacket();
        con.sendPacketImmediate(rstPacket, false);
      }
    }
    if (state == RDPConnection.OPEN) {
      if (packet.isRst()) {
        // the other side wants to close the connection
        // set the state,
        // dont call con.close() since that will send a reset packet
        if (Log.loggingDebug)
          Log.debug("RDPServer.processExistingConnection: got reset packet for con " + con);
        if (con.getState() != RDPConnection.CLOSE_WAIT) {
          con.setState(RDPConnection.CLOSE_WAIT);
          con.setCloseWaitTimer();
          // Only invoke callback when moving into CLOSE_WAIT
          // state.  This prevents two calls to connectionReset.
          Log.net("RDPServer.processExistingConnection: calling reset callback");
          ClientConnection.MessageCallback pcb = con.getCallback();
          pcb.connectionReset(con);
        }

        return false;
      }
      if (packet.isSyn()) {
        // this will close the connection (put into CLOSE_WAIT)
        // send a reset packet and call the connectionReset callback
        Log.error(
            "RDPServer.processExistingConnection: closing connection because we got a syn packet, con="
                + con);
        con.close();
        return false;
      }

      // TODO: shouldnt it be ok for it to have same seq num?
      // if it is a 0 data packet?
      long rcvCur = con.getRcvCur();
      if (packet.getSeqNum() <= rcvCur) {
        if (Log.loggingNet)
          Log.net("RDPServer.processExistingConnection: seqnum too small - acking/not process");
        if (packet.getData() != null) {
          if (Log.loggingNet)
            Log.net(
                "RDPServer.processExistingConnection: sending ack even though seqnum out of range");
          RDPPacket replyPacket = new RDPPacket(con);
          con.sendPacketImmediate(replyPacket, false);
        }
        return false;
      }
      if (packet.getSeqNum() > (rcvCur + (con.getRcvMax() * 2))) {
        Log.error("RDPServer.processExistingConnection: seqnum too big - discarding");
        return false;
      }
      if (packet.isAck()) {
        if (Log.loggingNet)
          Log.net("RDPServer.processExistingConnection: processing ack " + packet.getAckNum());
        // lock for race condition (read then set)
        con.getLock().lock();
        try {
          if (packet.getAckNum() >= con.getSendNextSeqNum()) {
            // acking something we didnt even send yet
            Log.error(
                "RDPServer.processExistingConnection: discarding -- got ack #"
                    + packet.getAckNum()
                    + ", but our next send seqnum is "
                    + con.getSendNextSeqNum()
                    + " -- "
                    + con);
            return false;
          }
          if (con.getSendUnackd() <= packet.getAckNum()) {
            con.setSendUnackd(packet.getAckNum() + 1);
            if (Log.loggingNet)
              Log.net(
                  "RDPServer.processExistingConnection: updated send_unackd num to "
                      + con.getSendUnackd()
                      + " (one greater than packet ack) - "
                      + con);
            con.removeUnackPacketUpTo(packet.getAckNum());
          }
          if (packet.isEak()) {
            List eackList = packet.getEackList();
            Iterator iter = eackList.iterator();
            while (iter.hasNext()) {
              Long seqNum = (Long) iter.next();
              if (Log.loggingNet)
                Log.net("RDPServer.processExistingConnection: got EACK: " + seqNum);
              con.removeUnackPacket(seqNum.longValue());
            }
          }
        } finally {
          con.getLock().unlock();
          if (Log.loggingNet)
            Log.net("RDPServer.processExistingConnection: processed ack " + packet.getAckNum());
        }
      }
      // process the data
      byte[] data = packet.getData();
      if ((data != null) || packet.isNul()) {
        dataCounter.add();

        // lock - since racecondition: we read then set
        con.getLock().lock();
        try {
          rcvCur = con.getRcvCur(); // update rcvCur
          if (Log.loggingNet) Log.net("RDPServer.processExistingConnection: rcvcur is " + rcvCur);

          ClientConnection.MessageCallback pcb = con.getCallback();
          if (pcb == null) {
            Log.warn("RDPServer.processExistingConnection: no packet callback registered");
          }

          // call callback only if we havent seen it already - eackd
          if (!con.hasEack(packet.getSeqNum())) {
            if (con.isSequenced()) {
              // this is a sequential connection,
              // make sure this is the 'next' packet
              // is this the next sequential packet
              if (packet.getSeqNum() == (rcvCur + 1)) {
                // this is the next packet
                if (Log.loggingNet)
                  Log.net(
                      "RDPServer.processExistingConnection: conn is sequenced and received next packet, rcvCur="
                          + rcvCur
                          + ", packet="
                          + packet);
                if ((pcb != null) && (data != null)) {
                  queueForCallbackProcessing(pcb, con, packet);
                }
              } else {
                // not the next packet, place it in queue
                if (Log.loggingNet)
                  Log.net(
                      "RDPServer.processExistingConnection: conn is sequenced, BUT PACKET is OUT OF ORDER: rcvcur="
                          + rcvCur
                          + ", packet="
                          + packet);
                con.addSequencePacket(packet);
              }
            } else {
              if ((pcb != null) && (data != null)) {
                // make sure we havent already processed packet
                queueForCallbackProcessing(pcb, con, packet);
              }
            }
          } else {
            if (Log.loggingNet) Log.net(con.toString() + " already seen this packet");
          }

          // is this the next sequential packet
          if (packet.getSeqNum() == (rcvCur + 1)) {
            con.setRcvCur(rcvCur + 1);
            if (Log.loggingNet)
              Log.net(
                  "RDPServer.processExistingConnection RCVD: incremented last sequenced rcvd: "
                      + (rcvCur + 1));

            // packet in order - dont add to eack
            // Take any additional sequential packets off eack
            long seqNum = rcvCur + 2;
            while (con.removeEack(seqNum)) {
              if (Log.loggingNet)
                Log.net("RDPServer.processExistingConnection: removing/collapsing eack: " + seqNum);
              con.setRcvCur(seqNum++);
            }

            if (con.isSequenced()) {
              rcvCur++; // since we just process the last one
              Log.net(
                  "RDPServer.processExistingConnection: connection is sequenced, processing collapsed packets.");
              // send any saved sequential packets also
              Iterator iter = con.getSequencePackets().iterator();
              while (iter.hasNext()) {
                RDPPacket p = (RDPPacket) iter.next();
                if (Log.loggingNet)
                  Log.net(
                      "rdpserver: stored packet seqnum="
                          + p.getSeqNum()
                          + ", if equal to (rcvcur + 1)="
                          + (rcvCur + 1));
                if (p.getSeqNum() == (rcvCur + 1)) {
                  Log.net(
                      "RDPServer.processExistingConnection: this is the next packet, processing");
                  // this is the next packet - update rcvcur
                  rcvCur++;

                  // process this packet
                  Log.net(
                      "RDPServer.processExistingConnection: processing stored sequential packet "
                          + p);
                  byte[] storedData = p.getData();
                  if (pcb != null && storedData != null) {
                    queueForCallbackProcessing(pcb, con, packet);
                  }
                  iter.remove();
                }
              }
            } else {
              if (Log.loggingNet)
                Log.net("RDPServer.processExistingConnection: connection is not sequenced");
            }
          } else {
            if (Log.loggingNet)
              Log.net(
                  "RDPServer.processExistingConnection: RCVD OUT OF ORDER: packet seq#: "
                      + packet.getSeqNum()
                      + ", but last sequential rcvd packet was: "
                      + con.getRcvCur()
                      + " -- not incrementing counter");
            if (packet.getSeqNum() > rcvCur) {
              // must be at least + 2 larger than rcvCur
              if (Log.loggingNet) Log.net("adding to eack list " + packet);
              con.addEack(packet);
            }
          }
        } finally {
          con.getLock().unlock();
        }
        return true;
      }
    }
    return false;
  }
  /** returns a list of rdpserversockets */
  Set<DatagramChannel> getActiveChannels() throws InterruptedException, java.io.IOException {
    lock.lock();
    try {
      while (channelMap.isEmpty()) {
        channelMapNotEmpty.await();
      }
    } finally {
      lock.unlock();
    }

    Set<SelectionKey> readyKeys = null;
    do {
      lock.lock();
      try {
        if (!newChannelSet.isEmpty()) {
          if (Log.loggingNet) Log.net("RDPServer.getActiveChannels: newChannelSet is not null");
          Iterator<DatagramChannel> iter = newChannelSet.iterator();
          while (iter.hasNext()) {
            DatagramChannel newDC = iter.next();
            iter.remove();
            newDC.register(selector, SelectionKey.OP_READ);
          }
        }
      } finally {
        lock.unlock();
      }
      int numReady = selector.select(); // this is a blocking call - thread safe
      selectCalls++;
      if (numReady == 0) {
        if (Log.loggingNet) Log.net("RDPServer.getActiveChannels: selector returned 0");
        continue;
      }
      readyKeys = selector.selectedKeys();
      if (Log.loggingNet)
        Log.net(
            "RDPServer.getActiveChannels: called select - # of ready keys = "
                + readyKeys.size()
                + " == "
                + numReady);
    } while (readyKeys == null || readyKeys.isEmpty());

    lock.lock();
    try {
      // get a datagramchannel that is ready
      Set<DatagramChannel> activeChannels = new HashSet<DatagramChannel>();

      Iterator<SelectionKey> iter = readyKeys.iterator();
      while (iter.hasNext()) {
        SelectionKey key = iter.next();
        if (Log.loggingNet)
          Log.net(
              "RDPServer.getActiveChannels: matched selectionkey: "
                  + key
                  + ", isAcceptable="
                  + key.isAcceptable()
                  + ", isReadable="
                  + key.isReadable()
                  + ", isValid="
                  + key.isValid()
                  + ", isWritable="
                  + key.isWritable());
        iter.remove(); // remove from the selected key list

        if (!key.isReadable() || !key.isValid()) {
          Log.error(
              "RDPServer.getActiveChannels: Throwing exception: RDPServer: not readable or invalid");
          throw new MVRuntimeException("RDPServer: not readable or invalid");
        }

        DatagramChannel dc = (DatagramChannel) key.channel();
        activeChannels.add(dc);
      }
      if (Log.loggingNet)
        Log.net(
            "RDPServer.getActiveChannels: returning " + activeChannels.size() + " active channels");
      return activeChannels;
    } finally {
      lock.unlock();
    }
  }
  public void handleMessageData(int length, MVByteBuffer messageData, AgentInfo agentInfo) {
    if (length == -1 || messageData == null) {
      if ((agentInfo.flags & MessageAgent.DOMAIN_FLAG_TRANSIENT) != 0) {
        Log.info("Lost connection to '" + agentInfo.agentName + "' (transient)");
        agents.remove(agentInfo.socket);
        agentNames.remove(agentInfo.agentName);
        messageIO.removeAgent(agentInfo);
      } else Log.info("Lost connection to '" + agentInfo.agentName + "'");

      try {
        agentInfo.socket.close();
      } catch (java.io.IOException ex) {
        Log.exception("close", ex);
      }
      agentInfo.socket = null;
      // ## clear buffers
      // ## keep agentInfo?  to preserve agentId?
      return;
    }

    Message message = (Message) MarshallingRuntime.unmarshalObject(messageData);

    MessageType msgType = message.getMsgType();
    if (Log.loggingDebug)
      Log.debug(
          "handleMessageData from "
              + agentInfo.agentName
              + ","
              + message.getMsgId()
              + " type="
              + msgType.getMsgTypeString()
              + " len="
              + length
              + " class="
              + message.getClass().getName());

    try {
      if (message instanceof AllocNameMessage)
        handleAllocName((AllocNameMessage) message, agentInfo.socket);
      else if (message instanceof AwaitPluginDependentsMessage)
        handleAwaitPluginDependents((AwaitPluginDependentsMessage) message, agentInfo.socket);
      else if (message instanceof PluginAvailableMessage)
        handlePluginAvailable((PluginAvailableMessage) message, agentInfo.socket);
      else
        Log.error(
            "Unsupported message from "
                + agentInfo.agentName
                + ","
                + message.getMsgId()
                + " type="
                + msgType.getMsgTypeString()
                + " len="
                + length
                + " class="
                + message.getClass().getName());
    } catch (java.io.IOException e) {
      Log.error(
          "IO error on message from "
              + agentInfo.agentName
              + ","
              + message.getMsgId()
              + " type="
              + msgType.getMsgTypeString()
              + " len="
              + length
              + " class="
              + message.getClass().getName());
    }
  }
  public static void main(String args[]) {
    String worldName = System.getProperty("multiverse.worldname");
    Properties properties = InitLogAndPid.initLogAndPid(args, worldName, null);

    System.err.println("Multiverse server version " + ServerVersion.getVersionString());

    List<String> agentNames = new LinkedList<String>();

    LongOpt[] longopts = new LongOpt[2];
    longopts[0] = new LongOpt("pid", LongOpt.REQUIRED_ARGUMENT, null, 2);
    longopts[1] = new LongOpt("port", LongOpt.REQUIRED_ARGUMENT, null, 3);
    Getopt opt = new Getopt("DomainServer", args, "a:m:t:p:P:", longopts);
    int c;
    int port = DEFAULT_PORT;

    String portStr = properties.getProperty("multiverse.msgsvr_port");
    if (portStr != null) port = Integer.parseInt(portStr);

    PluginStartGroup pluginStartGroup = new PluginStartGroup();

    while ((c = opt.getopt()) != -1) {
      switch (c) {
        case 'a':
          agentNames.add(opt.getOptarg());
          break;
        case 't':
        case 'm':
          // ignore RuntimeMarshalling flags
          opt.getOptarg();
          break;
        case 'p':
          String pluginSpec = opt.getOptarg();
          String[] pluginDef = pluginSpec.split(",", 2);
          if (pluginDef.length != 2) {
            System.err.println("Invalid plugin spec format: " + pluginSpec);
            Log.error("Invalid plugin spec format: " + pluginSpec);
            System.exit(1);
          }
          int expected = Integer.parseInt(pluginDef[1]);
          pluginStartGroup.add(pluginDef[0], expected);
          break;
        case '?':
          System.exit(1);
          break;
        case 'P':
          break;
        case 2:
          // ignore --pid
          opt.getOptarg();
          break;
          // port
        case 3:
          String arg = opt.getOptarg();
          port = Integer.parseInt(arg);
          break;
        default:
          break;
      }
    }

    String svrName = System.getProperty("multiverse.loggername");
    String runDir = System.getProperty("multiverse.rundir");

    // Windows non-Cygwin only - save process ID for status script
    if (System.getProperty("os.name").contains("Windows") && svrName != null && runDir != null) {
      saveProcessID(svrName, runDir);
    }

    // parse command-line options

    domainServer = new DomainServer(port);
    domainServer.setAgentNames(agentNames);
    domainServer.setWorldName(worldName);
    domainServer.start();

    pluginStartGroup.prepareDependencies(properties, worldName);
    domainServer.addPluginStartGroup(pluginStartGroup);
    pluginStartGroup.pluginAvailable("Domain", "Domain");

    String timeoutStr = properties.getProperty("multiverse.startup_timeout");
    int timeout = 120;
    if (timeoutStr != null) {
      timeout = Integer.parseInt(timeoutStr);
    }

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    ScheduledFuture<?> timeoutHandler =
        scheduler.schedule(new TimeoutRunnable(timeout), timeout, TimeUnit.SECONDS);

    javax.crypto.SecretKey domainKey = SecureTokenUtil.generateDomainKey();
    // XXX Use a random keyID for now. Ideally, this would be semi-unique.
    long keyId = new Random().nextLong();
    encodedDomainKey = Base64.encodeBytes(SecureTokenUtil.encodeDomainKey(keyId, domainKey));
    Log.debug("generated domain key: " + encodedDomainKey);

    try {
      pluginStartGroup.awaitDependency("Domain");
      timeoutHandler.cancel(false);
      String availableMessage = properties.getProperty("multiverse.world_available_message");
      String availableFile = properties.getProperty("multiverse.world_available_file");
      if (availableFile != null) touchFile(FileUtil.expandFileName(availableFile));
      if (availableMessage != null) System.err.println("\n" + availableMessage);
      while (true) {
        Thread.sleep(10000000);
      }
    } catch (Exception ex) {
      Log.exception("DomainServer.main", ex);
    }
  }