Package marauroa.server.game

Source Code of marauroa.server.game.GameServerManager

/* $Id: GameServerManager.java,v 1.152 2010/06/01 20:00:35 nhnb Exp $ */
/***************************************************************************
*                   (C) Copyright 2003-2010 - 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;

import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;

import marauroa.common.Log4J;
import marauroa.common.crypto.RSAKey;
import marauroa.common.net.message.Message;
import marauroa.server.game.container.PlayerEntry;
import marauroa.server.game.container.PlayerEntryContainer;
import marauroa.server.game.messagehandler.DelayedEventHandlerThread;
import marauroa.server.game.messagehandler.DisconnectHandler;
import marauroa.server.game.messagehandler.MessageDispatcher;
import marauroa.server.game.rp.RPServerManager;
import marauroa.server.net.IDisconnectedListener;
import marauroa.server.net.INetworkServerManager;

/**
* The GameServerManager is a active entity of the marauroa.game package, it is
* in charge of processing all the messages and modify PlayerEntry Container
* accordingly.
* <p>
* The logic is similar to this:
*
* <pre>
*   GameManager
*   {
*   NetworkManager read Message
*
*   switch(Message type)
*   {
*   case ...;
*   }
*   }
* </pre>
*
* So let's define the reply to each message. First, let's clarify that the best
* way of modelling this system is using finite automates, (a finite state
* machine) where, based on the input, we change the state we are currently in
* and produce an output.
* <p>
* Login
*
* <pre>
*   Process C2S Login ( STATE_BEGIN_LOGIN )
*   Precondition: The state MUST be NULL
*
*   Test if there is room for more players.
*   if there is no more room
*   {
*   reply S2C Login NACK( SERVER_FULL )
*   state = NULL
*   }
*
*   if check username, password in database is correct
*   {
*   create clientid
*   add PlayerEntry
*   notify database
*
*   reply S2C Login ACK
*
*   get characters list of the player
*   reply S2C CharacterList
*
*   state = STATE_LOGIN_COMPLETE
*   }
*   else
*   {
*   notify database
*
*   reply S2C Login NACK( LOGIN_INCORRECT )
*   state = NULL
*   }
*
*   Postcondition: The state MUST be NULL or STATE_LOGIN_COMPLETE
*   and a we have created a PlayerEntry for this player with a unique clientid.
* </pre>
*
* Choose Character
*
* <pre>
*   Process C2S ChooseCharacter ( STATE_LOGIN_COMPLETE )
*   Precondition: The state MUST be STATE_LOGIN_COMPLETE
*
*   if character exists in database
*   {
*   add character to Player's PlayerEntry
*   add character to game
*   reply S2C Choose Character ACK
*
*   state = STATE_GAME_BEGIN
*   }
*   else
*   {
*   reply S2C Choose Character NACK
*   state = STATE_LOGIN_COMPLETE
*   }
*
*   Postcondition: The state MUST be STATE_GAME_BEGIN and the PlayerStructure
*   should be completely filled or if the character choise was wrong the state is STATE_LOGIN_COMPLETE
* </pre>
*
* Logout stage
*
* <pre>
*   Process C2S Logout ( STATE_GAME_END )
*   Precondition: The state can be anything but STATE_LOGIN_BEGIN
*
*   if( rpEngine allows player to logout )
*   {
*   reply S2C Logout ACK
*   state = NULL
*
*   store character in database
*   remove character from game
*   delete PlayerEntry
*   }
*   else
*   {
*   reply S2C Logout NACK
*   }
*
*   Postcondition: Either the same as the input state or the state currently in
* </pre>
*
* @author miguel
*/
public final class GameServerManager extends Thread implements IDisconnectedListener {

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

  /** We need network server manager to be able to send messages */
  private INetworkServerManager netMan;

  /** We need rp manager to run the messages and actions from players */
  private RPServerManager rpMan;

  /** The playerContainer handles all the player management */
  private PlayerEntryContainer playerContainer;

  /** The thread will be running while keepRunning is true */
  private boolean keepRunning;

  /** isFinished is true when the thread has really exited. */
  private boolean isfinished;

  /** processes delayed events */
  private DelayedEventHandlerThread delayedEventHandler;
 
  /** dispatches messages to the appropriate handlers */
  private MessageDispatcher messageDispatcher;

  /** handles disconnects */
  private DisconnectHandler disconnectHandler = new DisconnectHandler();

  /**
   * Constructor that initialize also the RPManager
   *
   * @param key
   *            the server private key
   * @param netMan
   *            a NetworkServerManager instance.
   * @param rpMan
   *            a RPServerManager instance.
   * @throws Exception
   *             is there is any problem.
   */
  public GameServerManager(RSAKey key, INetworkServerManager netMan, RPServerManager rpMan)
      throws Exception {
    super("GameServerManager");
    keepRunning = true;
    isfinished = false;

    this.netMan = netMan;
    this.rpMan = rpMan;
   

    netMan.registerDisconnectedListener(this);

    playerContainer = PlayerEntryContainer.getContainer();
   
    delayedEventHandler= new DelayedEventHandlerThread(rpMan);

    messageDispatcher = new MessageDispatcher();
    messageDispatcher.init(netMan, rpMan, playerContainer, Statistics.getStatistics(), key);
  }
 
  /**
   * Starting this thread makes it to start the thread that disconnect players.
   */
  @Override
  public synchronized void start() {
    super.start();
    delayedEventHandler.start();
  }


  /**
   * This method request the active object to finish its execution and store
   * all the players back to database.
   */
  public void finish() {
    /* We store all the players when we are requested to exit */
    storeConnectedPlayers();
   
    rpMan.finish();
    keepRunning = false;
   
    interrupt();
    delayedEventHandler.setKeepRunning(false);
   
   
    while (isfinished == false) {
      Thread.yield();
    }
  }

  /*
   * Disconnect any connected player and if the player has already login and
   * was playing game it is stored back to database.
   */
  private void storeConnectedPlayers() {
    /*
     * We want to avoid concurrentComodification of playerContainer.
     */
    List<PlayerEntry> list=new LinkedList<PlayerEntry>();
    for (PlayerEntry entry : playerContainer) {     
      list.add(entry);
    }
   
    /*
     * Now we iterate the list and remove characters.
     */   
    for (PlayerEntry entry : list) {
      logger.info("STORING ("+entry.username+") :"+entry.object);
      /*
       * It may be a bit slower than disconnecting here, but server is
       * going down so there is no hurry.
       */
      onDisconnect(entry.channel);
    }
  }

  /**
   * Runs the game glue logic. This class is responsible of receiving messages
   * from clients and instruct RP about actions clients did.
   */
  @Override
  public void run() {
    try {
      while (keepRunning) {
        Message msg = netMan.getMessage();

        if (msg != null) {
//          playerContainer.getLock().requestWriteLock();
          messageDispatcher.dispatchMessage(msg);
//          playerContainer.getLock().releaseLock();
        }

        // Finally store stats about logged players.
        playerContainer.dumpStatistics();
      }
    } catch (Throwable e) {
      logger.error("Unhandled exception, server will shut down.", e);
    }

    isfinished = true;
  }

  /**
   * This method is called by network manager when a client connection is lost
   * or even when the client logout correctly.
   *
   * @param channel
   *            the channel that was closed.
   */
  public void onDisconnect(SocketChannel channel) {
    logger.info("GAME Disconnecting " + channel);
    delayedEventHandler.addDelayedEvent(disconnectHandler, channel);
  }

}
TOP

Related Classes of marauroa.server.game.GameServerManager

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.