Package marauroa.server.game.container

Source Code of marauroa.server.game.container.PlayerEntryContainer

/* $Id: PlayerEntryContainer.java,v 1.30 2010/12/19 10:31:45 nhnb Exp $ */
/***************************************************************************
*                      (C) Copyright 2007 - 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.server.game.container;

import java.net.InetAddress;
import java.nio.channels.SocketChannel;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;

import marauroa.common.Log4J;
import marauroa.common.game.RPObject;
import marauroa.server.RWLock;
import marauroa.server.game.Statistics;

/**
* This is a helper class to sort and access PlayerEntry in a controlled way.
*
* This class implements the singleton pattern. PlayerContainer is the data
* structure that contains all of the information about the players while the
* game is running.
* <p>
* It consists of a list of PlayerEntry objects and is heavily linked with the
* database, so we can hide its complexity to GameManager. By making
* PlayerDatabase hidden by PlayerContainer we achieve the illusion that
* managing the runtime behavior we modify automatically the permanent one.
*
* @author miguel
*
*/
public class PlayerEntryContainer implements Iterable<PlayerEntry> {
  /** the logger instance. */
  private static final marauroa.common.Logger logger = Log4J.getLogger(PlayerEntryContainer.class);

  /** A reader/writers lock for controlling the access */
  private RWLock lock;

  /** A random number generator instance. */
  private Random rand;

  /** This map store player entry for fast access using clientid */
  Map<Integer, PlayerEntry> clientidMap;

  private static PlayerEntryContainer playerEntryContainer;

  /** Statistics about actions runs */
  private Statistics stats = Statistics.getStatistics();

  /** Constructor */
  protected PlayerEntryContainer() {
    /* Initialize the random number generator */
    rand = new Random();
    rand.setSeed(new Date().getTime());

    lock = new RWLock();

    /* We initialize the list that will help us sort the player entries. */
    clientidMap = Collections.synchronizedMap(new HashMap<Integer, PlayerEntry>());
  }

  /**
   * This method returns an instance of PlayerEntryContainer
   *
   * @return A shared instance of PlayerEntryContainer
   */
  public static PlayerEntryContainer getContainer() {
    if (playerEntryContainer == null) {
      playerEntryContainer = new PlayerEntryContainer();
    }
    return playerEntryContainer;
  }

  /**
   * This method returns an iterator over tha available player entry objects.
   *
   * @return the iterator
   */
  public Iterator<PlayerEntry> iterator() {
    return new LinkedList<PlayerEntry>(clientidMap.values()).iterator();
  }

  /**
   * This method returns the lock so that you can control how the resource is
   * used
   *
   * @return the RWLock of the object
   */
  public RWLock getLock() {
    return lock;
  }

  /**
   * This method returns the size of the container.
   *
   * @return Container's size.
   */
  public int size() {
    return clientidMap.size();
  }

  /**
   * This method returns true if there is any PlayerEntry which has client id
   * as clientid.
   *
   * @param clientid
   *            the id of the PlayerEntry we are looking for
   * @return true if it is found or false otherwise.
   */
  public boolean has(int clientid) {
    return clientidMap.containsKey(clientid);
  }

  /**
   * This method returns the PlayerEntry whose client id is clientid or null
   * otherwise.
   *
   * @param clientid
   *            the id of the PlayerEntry we are looking for
   * @return the PlayerEntry if is it found or null otherwise
   */
  public PlayerEntry get(int clientid) {
    return clientidMap.get(clientid);
  }

  /**
   * This method returns the entry that has been associated with this
   * SocketChannel, or null if it does not exists.
   *
   * @param channel
   *            the socket channel to check
   * @return the PlayerEntry or null if it is not found.
   */
  public PlayerEntry get(SocketChannel channel) {
    synchronized (clientidMap) {
      for (PlayerEntry entry : clientidMap.values()) {
        if (entry.channel == channel) {
          return entry;
        }
      }
    }

    return null;
  }

  /**
   * This method returns the first entry that has been associated to this player or
   * null if it does not exists. Note: It is possible to login with the same account
   * and different character multiple times.
   *
   * @param username
   *            the username to look for
   * @return the PlayerEntry or null if it is not found
   */
  public PlayerEntry get(String username) {
    synchronized (clientidMap) {
      for (PlayerEntry entry : clientidMap.values()) {
        /*
         * NOTE: Bug fix: We use ignore case to detect already logged
         * players better.
         */
        if (username.equalsIgnoreCase(entry.username)) {
          return entry;
        }
      }
    }
    return null;
  }

  /**
   * This method returns the old entry that has been associated to this player or
   * null if there is no old entry
   *
   * @param oldEntry
   *            old entry with username and password
   * @return the PlayerEntry or null if there is none
   */
  public PlayerEntry getOldEntry(PlayerEntry oldEntry) {
    synchronized (clientidMap) {
      for (PlayerEntry entry : clientidMap.values()) {

        if (oldEntry.character.equalsIgnoreCase(entry.character)
          && entry != oldEntry) {
          return entry;
        }
      }
    }
    return null;
  }

  /**
   * This method returns the entry that has been associated to this player or
   * null if it does not exists.
   *
   * @param object
   *            the RPObject we have to look for
   * @return the PlayerEntry or null if it is not found
   */
  public PlayerEntry get(RPObject object) {
    synchronized (clientidMap) {
      for (PlayerEntry entry : clientidMap.values()) {
        /*
         * We want really to do a fast comparasion
         */
        if (entry.object == object) {
          return entry;
        }
      }
    }
    return null;
  }

  /**
   * This method removed a player entry from the container and return it or
   * null if the entry does not exist.
   *
   * @param clientid
   *            the clientid we want its Player entry to remove.
   * @return the player entry or null if it has not been found.
   */
  public PlayerEntry remove(int clientid) {
    return clientidMap.remove(clientid);

  }

  /**
   * Add a new Player entry to the container. This method assigns
   * automatically a random clientid to this player entry.
   *
   * @param channel
   *            the socket channel associated with the client
   * @return client id resulting
   */
  public PlayerEntry add(SocketChannel channel) {
    /* We create an entry */
    PlayerEntry entry = new PlayerEntry(channel);
    entry.clientid = generateClientID();

    /* Finally adds it to map */
    clientidMap.put(entry.clientid, entry);

    return entry;
  }

  private int generateClientID() {
    int clientid = rand.nextInt();

    synchronized (clientidMap) {
      while (has(clientid) && clientid > 0) {
        clientid = rand.nextInt();
      }
    }

    return clientid;
  }

  /**
   * Obtains an idle player entry from the container. We can define an idle
   * player entry like a player that has connected to server and requested to
   * login, but never actually completed login stage. This client is taking
   * server resources but doing nothing useful at all.
   *
   * @return an idle PlayerEntry or null if not found
   */
  public PlayerEntry getIdleEntry() {
    synchronized (clientidMap) {
      for (PlayerEntry entry : clientidMap.values()) {
        if (entry.isRemovable()) {
          return entry;
        }
      }
    }
    return null;
  }

  /**
   * dumps the statistics
   */
  public void dumpStatistics() {
    HashSet<InetAddress> addresses = new HashSet<InetAddress>();
    int counter = 0;
    synchronized (clientidMap) {
      for (PlayerEntry entry : clientidMap.values()) {
        if (entry.state == ClientState.GAME_BEGIN) {
          addresses.add(entry.getAddress());
          counter++;
        }
      }
    }
    logger.debug("PlayerEntryContainer size: " + counter);
    stats.set("Players online", counter);
    stats.set("Ips online", addresses.size());
 

  /**
   * a string representation useful for debugging.
   *
   * @return String
   */
  @Override
  public String toString() {
    return "PlayerEntryContainer [clientidMap=" + clientidMap + "]";
  }


}
TOP

Related Classes of marauroa.server.game.container.PlayerEntryContainer

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.