Beispiel #1
0
 /**
  * Constructs a new UnresolvedReferenceException that takes a list of strings corresponding to
  * the unresolved room IDs left over.
  */
 public UnresolvedReferenceException(ArrayList<String> list) {
   super(
       list.size() > 1
           ? "rooms referenced but never actually defined: " + list
           : "room referenced but never actually defined: " + list.get(0));
 }
Beispiel #2
0
  @SuppressWarnings({"unchecked", "rawtypes"})
  public static void main(String[] args) throws IOException {
    boolean useArguments = false;
    Map config = null;

    try {
      yamlInstance = new Yaml();

      /*
       * Try to load configuration file
       */
      FileReader configFile = new FileReader(YAML_PATH + CONFIGURATION_FILE);
      config = (Map) yamlInstance.load(configFile);

    } catch (FileNotFoundException e) {
      System.out.println(
          "Could not load configuration file. Using program " + "arguments instead.");
      useArguments = true;
    }

    /* Scan for program arguments */
    int port = -1;
    try {

      if (useArguments) port = Integer.parseInt(args[0]);
      else
        // take the port number from the config file
        port = (Integer) config.get("port");

    } catch (ArrayIndexOutOfBoundsException e) {
      System.err.println("DungeonServer: specify a port to which to bind");
      System.out.println(usage());
      System.exit(1);
    }

    /* Attempt to bind to port */
    ServerSocket server = null;
    try {
      server = new ServerSocket(port);
    } catch (IOException e) {
      System.err.printf("DungeonServer: could not listen on port %d\n", port);
      System.exit(2);
    }

    System.out.printf("bound to port %d\n", port);

    System.out.println("loading universe:");

    /* Load universe */
    try {
      System.out.println("\tfinding universe file");

      FileReader universeFile = null;
      if (useArguments) universeFile = new FileReader(YAML_PATH + DEFAULT_UNIVERSE_FILE);
      else universeFile = new FileReader(YAML_PATH + config.get("world") + ".universe.yml");

      Object[] docs = new Object[3];
      Map<String, Object> preamble = null;
      Map<String, Map<String, Object>> rooms = null, items = null;

      try {
        System.out.println("\tchecking universe file");

        int i = 0;
        for (Object o : yamlInstance.loadAll(universeFile)) docs[i++] = o;

        preamble = (Map<String, Object>) docs[0];
        rooms = (Map<String, Map<String, Object>>) docs[1];
        items = (Map<String, Map<String, Object>>) docs[2];

        if (preamble == null || rooms == null || items == null) throw new NullPointerException();

      } catch (ArrayIndexOutOfBoundsException e) {
        System.err.println(
            "DungeonServer: error parsing universe " + "file: too many documents in universe file");
        System.exit(3);
      } catch (NullPointerException e) {
        System.err.println(
            "DungeonServer: error parsing universe " + "file: too few documents in universe file");
        System.exit(3);
      }

      /*
       * Used after parsing rooms to set universe parameters
       */
      boolean doWeather = false;
      String spawnRoomID = null;
      int timescale = -1;
      try {
        System.out.println("\treading preamble");

        /*
         * Load universe parameters from the preamble
         */
        doWeather = (Boolean) validateAndGet(preamble, "weather", Boolean.class);

        spawnRoomID = (String) validateAndGet(preamble, "spawn", String.class);

        timescale = (Integer) validateAndGet(preamble, "timescale", Integer.class);

      } catch (Exception e) {
        System.err.println("DungeonServer: failed parsing preamble (" + e.getMessage() + ")");
        System.exit(4);
      }

      /*
       * Loop through room definitions in universe file
       */

      /**
       * This hash map is used to resolve references from one room to another. Each time a room is
       * parsed, it is added to this map, and henceforth back references to the newly added room
       * will be resolved by checking this map.
       */
      HashMap<String, Room> knownRooms = new HashMap<String, Room>();

      /**
       * This list is maintained to easily check at the end of parsing if there are still references
       * to unseen rooms.
       */
      ArrayList<String> unseenRooms = new ArrayList<String>();

      /**
       * This is a list of triples (A, B, C) such that A is a room waiting for a reference to
       * another room, C, through a direction B. For A and B, the string ID of the rooms are used
       * (the same key used in the knownRooms hash map). This list is used whenever a room's exit
       * references cannot actually be resolved because the destination room has not yet been
       * parsed. At the end of parsing, as long as the unseenRooms list is empty, this list is
       * traversed to resolve the remaining references.
       */
      ArrayList<Triple<String, Direction, String>> unresolved;
      unresolved = new ArrayList<Triple<String, Direction, String>>();

      String thisRoomID = null;
      try {
        System.out.println("\tparsing rooms");

        for (Map.Entry<String, Map<String, Object>> m : rooms.entrySet()) {
          thisRoomID = m.getKey();
          Map<String, Object> thisMap = m.getValue();

          String roomName = (String) validateAndGet(thisMap, "name", String.class);

          String description = (String) validateAndGet(thisMap, "description", String.class);

          Hashtable<Pair<DayPart, Weather>, String> details;
          details = getDetails(thisMap);

          Room r = new Room(roomName, description, details);

          if (thisMap.containsKey("neverUseArticle")) {
            boolean neverUseArticle =
                (Boolean) validateAndGet(thisMap, "neverUseArticle", Boolean.class);

            r.setNeverUseArticle(neverUseArticle);
          }

          if (unseenRooms.contains(thisRoomID)) unseenRooms.remove(thisRoomID);

          knownRooms.put(thisRoomID, r);

          /*
           * Process exits out of this room
           */
          Map<String, String> exits = (Map) validateAndGet(thisMap, "exits", Map.class);

          for (Map.Entry<String, String> exit : exits.entrySet()) {
            String thisDirection = exit.getKey();
            String toRoomID = exit.getValue();

            /*
             * Verify the direction from the file
             */
            Direction dir;
            dir = Direction.fromString(thisDirection);
            if (dir == null) throw new InvalidDirectionException(thisDirection);

            /*
             * Look up the destination room in the hash map
             */
            if (knownRooms.containsKey(toRoomID)) r.addExit(dir, knownRooms.get(toRoomID));
            else {
              if (!unseenRooms.contains(toRoomID)) unseenRooms.add(toRoomID);

              Triple<String, Direction, String> t;
              t = new Triple<String, Direction, String>(thisRoomID, dir, toRoomID);
              unresolved.add(t);
            }
          }
        }
      } catch (Exception e) {
        System.err.println(
            "DungeonServer: failed parsing room '" + thisRoomID + "' (" + e.getMessage() + ")");
        System.exit(4);
      }

      if (!unseenRooms.isEmpty()) throw new UnresolvedReferenceException(unseenRooms);

      /*
       * Invariant: There were no references to undefined rooms in the
       * file. Invariant: All the rooms in the file have been
       * instantiated.
       *
       * All rooms in the universe file have been parsed, but there may
       * still be exits waiting to be added because their destination was
       * not yet parsed at the time. Now loop through the unresolved list
       * to set them up.
       */
      for (Triple<String, Direction, String> t : unresolved) {
        Room fromRoom = knownRooms.get(t.first);
        Room toRoom = knownRooms.get(t.third);
        Direction dir = t.second;

        fromRoom.addExit(dir, toRoom);
      }

      /*
       * Invariant: All exits in the file have been set up among the
       * rooms.
       */

      Room spawnRoom = knownRooms.get(spawnRoomID);
      universe = new DungeonUniverse(spawnRoom, doWeather, timescale, knownRooms.values());

      universeFile.close();

    } catch (Exception e) {
      System.err.println("DungeonServer: failed loading universe " + "(" + e.getMessage() + ")");
      System.exit(2);
    }

    System.out.println("loaded universe");

    /* Start narrator */
    try {
      narrator = new DungeonNarrator();
    } catch (Exception e) {
      System.err.println("DungeonServer: failed starting narrator");
      System.exit(3);
    }

    System.out.println("started narrator");

    /* Start the game tick */
    try {
      tick = new DungeonGameTick();
      tick.start();
    } catch (Exception e) {
      System.err.println("DungeonServer: failed starting game tick");
      System.exit(3);
    }

    System.out.println("started game tick");

    /* Start accepting events */
    try {
      events = new DungeonDispatcher();
      events.start();
    } catch (Exception e) {
      System.err.println("DungeonServer: failed starting event queue");
      System.exit(3);
    }

    System.out.println("started event queue");

    /* Listen for clients */
    try {
      System.out.println("listening for clients");

      while (true) new DungeonConnectionThread(server.accept()).start();
    } catch (IOException e) {
      System.err.printf("DungeonServer: failed accepting client on port %d\n", port);
      System.exit(2);
    } finally {
      server.close();
    }
  }