Package be.demmel.jgws

Source Code of be.demmel.jgws.GameServer

package be.demmel.jgws;

import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.rmi.Naming;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.JAXBIntrospector;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import be.demmel.jgws.cfg.jaxb.Binding;
import be.demmel.jgws.cfg.jaxb.GameserverConfiguration;
import be.demmel.jgws.cfg.jaxb.Jpa;
import be.demmel.jgws.cfg.jaxb.LogBlackList;
import be.demmel.jgws.entities.MapSpawn;
import be.demmel.jgws.entities.NpcMasterData;
import be.demmel.jgws.entities.NpcSpawn;
import be.demmel.jgws.modules.Movement;
import be.demmel.jgws.network.InboundPacketHandler;
import be.demmel.jgws.network.NettyServer;
import be.demmel.jgws.packets.Packet;
import be.demmel.jgws.packets.handlers.PacketHandler;
import be.demmel.jgws.packets.handlers.tools.EntityManagerFactoryTool;
import be.demmel.jgws.packets.serialization.PacketDeserializer;
import be.demmel.jgws.packets.serialization.PacketSerializer;
import be.demmel.jgws.rmi.GameServerPortalImpl;
import be.demmel.jgws.rmi.LoginServerPlayerData;
import be.demmel.jgws.rmi.LoginServerPortal;
import be.demmel.jgws.utils.GWVector;
import be.demmel.jgws.utils.GeneralUtils;

public class GameServer {

  private static final Logger LOGGER = LoggerFactory.getLogger(GameServer.class);
  private static List<MapData> loadedMaps;
  private static List<LoginServerPlayerData> playerDataOfAcceptedClients = new ArrayList<>();
  public static final String[] WELCOME_MESSAGE = new String[] { "Welcome to:", "<--[ JGWLP:R v" + Version.getVersion() + " ]-->",
      "<--[ Credits to (C#): _rusty, ACB, miracle444 & onyxphase ]-->", "<--[ Credits to (Java): iDemmel ]-->" };
  private static EntityManagerFactory entityManagerFactory;

  public static void addClientKeys(LoginServerPlayerData playerData) {
    playerDataOfAcceptedClients.add(playerData);
  }

  public static LoginServerPlayerData findPlayerDataBySecurityKeys(short[] securityKeys1, short[] securityKeys2) {
    outer: for (LoginServerPlayerData playerDataOfAcceptedClient : playerDataOfAcceptedClients) {
      // Check the first set of securityKeys
      for (int i = 0; i < playerDataOfAcceptedClient.getSecurityKey1().length; i++) {
        short storedKey = playerDataOfAcceptedClient.getSecurityKey1()[i];
        short searchedKey = securityKeys1[i];
        if (storedKey != searchedKey) {
          continue outer;
        }
      }

      // Check the second set of securityKeys
      for (int i = 0; i < playerDataOfAcceptedClient.getSecurityKey2().length; i++) {
        short storedKey = playerDataOfAcceptedClient.getSecurityKey2()[i];
        short searchedKey = securityKeys2[i];
        if (storedKey != searchedKey) {
          continue outer;
        }
      }

      // If we reached this point it means that both sets of keys match, so return the PlayerData
      return playerDataOfAcceptedClient;
    }
    return null;
  }

  public static MapData getMap(int mapId) {
    for (MapData currentMap : loadedMaps) {
      if (currentMap.getMapID() == mapId) {
        return currentMap;
      }
    }
    return null;
  }

  public static List<MapData> getLoadedMaps() {
    return loadedMaps;
  }

  // TODO: broadcasting to other players will only happen from Map > Players, so no problem when just READING while treating packets
  // When reading, the map can change, so the setter should be used by the actions when needed!!

  public static void main(String... parameters) {
    Thread.currentThread().setName("Startup");
    LOGGER.info("The \"Java Guild Wars (Game)Server\" (version {}) is starting", Version.getVersion());
    try {
      GameserverConfiguration gameServerConfiguration = getGameServerConfiguration();

      // Retrieve the PacketDeserializers for every outgoing (game) packet
      Map<Integer, PacketDeserializer> packetDeserializers = GeneralUtils.getPacketDeserializers("be.demmel.jgws.packets.gameserver.inbound");
      // Retrieve the PacketSerializers for every incoming (game) packet
      Map<Class<? extends Packet>, PacketSerializer> packetSerializers = GeneralUtils.getPacketSerializers("be.demmel.jgws.packets.gameserver.outbound");

      // Retrieve the PacketHandlers for every incoming (login) packet
      Map<Class<? extends Packet>, PacketHandler> packetHandlers = GeneralUtils.getPacketHandlers("be.demmel.jgws.packets.handlers");

      // Create the ActionQueue that processes actions
      ActionQueue actionQueue = new ActionQueue();
      Thread actionQueueThread = new Thread(actionQueue);
      actionQueueThread.start();

      Jpa jpaConfiguration = gameServerConfiguration.getJpa();

      Map<String, String> properties = new HashMap<>();
      properties.put("javax.persistence.jdbc.driver", jpaConfiguration.getDriver());
      properties.put("javax.persistence.jdbc.url", jpaConfiguration.getUrl());
      properties.put("javax.persistence.jdbc.user", jpaConfiguration.getUser());
      properties.put("javax.persistence.jdbc.password", jpaConfiguration.getPassword());

      entityManagerFactory = Persistence.createEntityManagerFactory("jgws", properties);
     
      EntityManagerFactoryTool.setEntityManagerFactory(entityManagerFactory);

      // build all maps for this GS
      loadedMaps = loadMaps();

      LOGGER.info("Registering Game Server to Login Server");
      // Register this Game Server with the Login Server
      String portalUri = gameServerConfiguration.getPortalUri();
      URI serviceRmiUri = new URI(portalUri);

      LoginServerPortal remoteObject = null;
      // get the remote object from the registry
      try {
        remoteObject = (LoginServerPortal) Naming.lookup(serviceRmiUri.toASCIIString());
      } catch (Exception e) {
        // Add some understandable string
        throw new Exception("RMI connection failed : ", e);
      }

      Binding bindingConfiguration = gameServerConfiguration.getBinding();
      String bindingIp = bindingConfiguration.getIp();
      int bindingPort = bindingConfiguration.getPort();

      try {
        InetSocketAddress hostAndPort = new InetSocketAddress(InetAddress.getByName(bindingIp), bindingPort);
        GameServerPortalImpl gameServerPortal = new GameServerPortalImpl();
        int portalCallbackPort = gameServerConfiguration.getPortalCallbackPort();
        UnicastRemoteObject.exportObject(gameServerPortal, portalCallbackPort);

        be.demmel.jgws.rmi.GameServer gameServer = new be.demmel.jgws.rmi.GameServerImpl(gameServerPortal, hostAndPort);

        remoteObject.registerGameServer(gameServer);
      } catch (Exception e) {
        // Add some understandable string
        throw new Exception("Registering this Game Server to the Login Server failed : ", e);
      }
      LOGGER.info("Registered Game Server to Login Server");

      // TODO: SSL (with certificates) for RMI
      // TODO: also add an option to disable SSL, because listening on an interface which is NOT available to the public could also work
      // (intranet IP...) :)
     
      ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
     
      final InboundPacketHandler<GameServerSession> inboundPacketHandler = new InboundPacketHandler<GameServerSession>(channels, packetHandlers, SessionKey.SESSION_KEY, actionQueue);
     
      LogBlackList logBlackList = gameServerConfiguration.getLogBlackList();
      if(gameServerConfiguration.getLogBlackList() != null) { // don't log some inbound and outbound packets
        Set<Class<? extends Packet>> inboundPacketsLogBlackList = new HashSet<Class<? extends Packet>>(), outboundPacketsLogBlackList = new HashSet<Class<? extends Packet>>();
       
        for(String packet : logBlackList.getInbound().getPacket()) {
          inboundPacketsLogBlackList.add( (Class<? extends Packet>)Class.forName(packet));
        }
       
        for(String packet : logBlackList.getOutbound().getPacket()) {
          outboundPacketsLogBlackList.add( (Class<? extends Packet>)Class.forName(packet));
        }
       
        GameServerChannelInitializer pingChannelInitializer = new GameServerChannelInitializer(packetDeserializers, inboundPacketHandler, packetSerializers, SessionKey.SESSION_KEY, inboundPacketsLogBlackList, outboundPacketsLogBlackList);
       
        Thread serverThread = new Thread(new NettyServer<GameServerSession>(new InetSocketAddress(bindingIp, bindingPort), pingChannelInitializer));
        serverThread.start();
      } else {
        GameServerChannelInitializer pingChannelInitializer = new GameServerChannelInitializer(packetDeserializers, inboundPacketHandler, packetSerializers, SessionKey.SESSION_KEY);
       
        Thread serverThread = new Thread(new NettyServer<GameServerSession>(new InetSocketAddress(bindingIp, bindingPort), pingChannelInitializer));
        serverThread.start();
      }
     
      // Start the Movement module
      Thread movementThread = new Thread(new Movement(actionQueue, channels));
      movementThread.start();

      // The startup process went well. Log the event and keep the main thread alive
      LOGGER.info("The \"Java Guild Wars (Game)Server\" started");

      for (;;) {
        Thread.sleep(Long.MAX_VALUE);
      }
    } catch (Throwable throwable) {
      LOGGER.error("Initializing the \"Java Guild Wars (Game)Server\" failed because: ", throwable);
    }
  }

  private static List<MapData> loadMaps() throws Exception {
    List<MapData> mapsData = new ArrayList<>();
    // all maps
    Set<be.demmel.jgws.entities.Map> maps = getAllMaps();
    // result.next();
    for (be.demmel.jgws.entities.Map map : maps) {
      int mapId = map.getMapId();
      int gameMapId = map.getGameMapId();
      int gameMapFileId = map.getGameMapFileId();
      // Not needed
      // String gameMapName = result.getString("GameMapName");
      MapData mapData = new MapData();
      mapData.setMapID(mapId);
      mapData.setGameMapID(gameMapId);
      mapData.setGameFileID(gameMapFileId);
      // FIXME: set this to false when needed
      mapData.setIsOutpost(true);
      // FIXME: set this to false when needed
      mapData.setIsPve(true);
      // FIXME: why constants??
      mapData.setDistrictCountry(2);
      mapData.setDistrictNumber(1);

      // load the possible spawn locations for the players
      Set<MapSpawn> mapSpawns = getMapSpawns(mapId, mapData.isIsOutpost(), mapData.isIsPve());

      for (MapSpawn mapSpawn : mapSpawns) {
        float spawnX = mapSpawn.getSpawnX();
        float spawnY = mapSpawn.getSpawnY();
        int planeZ = mapSpawn.getSpawnPlane();
        mapData.getPossibleSpawns().add(new GWVector(spawnX, spawnY, planeZ));
      }

      if (mapData.getPossibleSpawns().isEmpty()) {
        LOGGER.warn("No possible spawn locations for map {}", mapId);
        mapData.getPossibleSpawns().add(new GWVector(0, 0, 0));
      }

      Set<NpcSpawn> npcSpawns = getNpcSpawns(mapId, mapData.isIsOutpost(), mapData.isIsPve());

      for (NpcSpawn npcSpawn : npcSpawns) {
        int npcId = npcSpawn.getNpcId();
        int nameId = npcSpawn.getNameId();
        float spawnY = npcSpawn.getSpawnY();
        float spawnX = npcSpawn.getSpawnX();
        int planeZ = npcSpawn.getSpawnPlane();
        float rotation = npcSpawn.getRotation();
        float speed = npcSpawn.getSpeed();
        int level = npcSpawn.getLevel();
        int profession = npcSpawn.getProfession();

        // retrieve the NPC
        NpcMasterData npcMasterData = getNpcMasterDataByNpcId(npcId);

        // FIXME: what if no NPCs are found??

        int npcFileId = npcMasterData.getNpcFileId();
        byte[] modelHash = npcMasterData.getModelHash();
        byte[] appearance = npcMasterData.getAppearance();
        int scale = npcMasterData.getScale();
        int professionFlags = npcMasterData.getProfessionFlags();

        // String selectNpcNamesSql = "select * from npcs_names where nameID=" + nameId;
        // CachedRowSet resultingNpcNames = db.executeSelect(selectNpcNamesSql);

        // FIXME: wtf? there probably aren't any NPCs that why we don't get into this loop
        byte[] nameHash = null;// readBlob(resultingNpc.getBlob("nameHash"));

        // FIXME: what if no NPC names are found??
        // resultingNpcNames.next();

        NpcData npcData = new NpcData();
        npcData.setAgentID(mapData.getAgentIDs().borrowId());
        npcData.setLocalID(npcId);
        npcData.setNpcFileID(npcFileId);
        npcData.setModelHash(modelHash);
        npcData.setAppearance(appearance);
        // TODO: verify encoding here
        npcData.setName(new String(nameHash));
        npcData.setScale(scale);
        npcData.setNpcFlags(professionFlags);
        npcData.setPosition(new GWVector(spawnX, spawnY, planeZ));
        npcData.setRotation(rotation);
        npcData.setIsRotating(false);
        npcData.setSpeed(speed);
        npcData.setLevel(level);
        npcData.setProfessionPrimary((byte) profession);
        npcData.setEnergy(100);
        npcData.setEnergyRegen(0.3f);
        npcData.setHealth(100);
        npcData.setHealthRegen(0.3f);
        npcData.setDirection(new GWVector(0, 0, 0));
        npcData.setMorale(100);
        npcData.setVitalStatus(VitalStatus.ALIVE.getValue());
        npcData.setMoveState(MovementState.MOVE_KEEP_DIR);
        npcData.setMoveType(MovementType.STOP);

        // add NPC data to the map
        mapData.addNpc(npcData);
      }

      mapsData.add(mapData);
    }
    LOGGER.info("{} game maps were loaded", mapsData.size());
    return mapsData;
  }

  public static Set<be.demmel.jgws.entities.Map> getAllMaps() {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    try {
      TypedQuery<be.demmel.jgws.entities.Map> query = entityManager.createNamedQuery("findAllMaps", be.demmel.jgws.entities.Map.class);

      List<be.demmel.jgws.entities.Map> foundCharacters = query.getResultList();

      return new HashSet<>(foundCharacters);
    } finally {
      entityManager.close();
    }
  }

  public static Set<MapSpawn> getMapSpawns(int mapId, boolean outpost, boolean pve) {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    try {
      TypedQuery<MapSpawn> query = entityManager.createNamedQuery("findMapSpawnsByMapIdAndOutpostAndPve", MapSpawn.class);
      query.setParameter("mapId", mapId);
      query.setParameter("outpost", outpost);
      query.setParameter("pve", pve);

      List<MapSpawn> foundCharacters = query.getResultList();

      return new HashSet<>(foundCharacters);
    } finally {
      entityManager.close();
    }
  }

  public static Set<NpcSpawn> getNpcSpawns(int mapId, boolean outpost, boolean pve) {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    try {
      TypedQuery<NpcSpawn> query = entityManager.createNamedQuery("findNpcSpawnsByMapIdAndOutpostAndPve", NpcSpawn.class);
      query.setParameter("mapId", mapId);
      query.setParameter("outpost", outpost);
      query.setParameter("pve", pve);

      List<NpcSpawn> foundCharacters = query.getResultList();

      return new HashSet<>(foundCharacters);
    } finally {
      entityManager.close();
    }
  }

  public static NpcMasterData getNpcMasterDataByNpcId(int npcId) {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    try {
      TypedQuery<NpcMasterData> query = entityManager.createNamedQuery("findNpcMasterDataByNpcId", NpcMasterData.class);
      query.setParameter("npcId", npcId);

      try {
        return query.getSingleResult();
      } catch (NoResultException nre) {
        LOGGER.debug("No NpcMasterData found using the given npcId");
        return null;
      }

    } finally {
      entityManager.close();
    }
  }

  // TODO: generic code in the core!
  private static <T> T validateAndUnmarshal(Class<T> docClass, File xmlFile, String schemaLocation) throws JAXBException, SAXException {
    String packageName = docClass.getPackage().getName();
    JAXBContext jc = JAXBContext.newInstance(packageName);
    Unmarshaller u = jc.createUnmarshaller();

    SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    URL url = GameServer.class.getResource(schemaLocation);
    Schema mySchema = sf.newSchema(url);

    u.setSchema(mySchema);
    return (T) JAXBIntrospector.getValue(u.unmarshal(xmlFile));
  }

  private static GameserverConfiguration getGameServerConfiguration() throws JAXBException, SAXException {
    try {
      String configurationFileLocation = System.getProperty("configurationFile");

      if (configurationFileLocation == null) {
        LOGGER.error("The \"configurationFile\" system property must be set");
        throw new RuntimeException("The \"configurationFile\" system property must be set");
      }

      File configurationFile = new File(configurationFileLocation);
      if (!configurationFile.exists()) {
        LOGGER.error("The given \"configurationFile\" system property point to a non-existing file");
        throw new RuntimeException("The given \"configurationFile\" system property points to a non-existing file");
      }
      return validateAndUnmarshal(GameserverConfiguration.class, configurationFile, "/gameserverConfiguration.xsd");
    } catch (Throwable t) {
      throw t;
    }
  }
}
TOP

Related Classes of be.demmel.jgws.GameServer

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.