Package org.gbcpainter.loaders.level

Source Code of org.gbcpainter.loaders.level.SimpleLevelLoader

package org.gbcpainter.loaders.level;

import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;

import org.gbcpainter.env.GameSettings;
import org.gbcpainter.game.levels.Level;
import org.gbcpainter.game.model.Monster;
import org.gbcpainter.game.model.Player;
import org.gbcpainter.game.model.grid.Junction;
import org.gbcpainter.game.model.grid.Pipe;
import org.gbcpainter.game.levels.SimpleLevelImpl;
import org.gbcpainter.geom.ImmutableSegment2D;
import org.gbcpainter.geom.Segment;
import org.gbcpainter.loaders.level.parsers.AssertValidGraphParser;
import org.gbcpainter.loaders.level.parsers.FileLevelParser;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jgrapht.EdgeFactory;
import org.jgrapht.graph.SimpleGraph;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static org.gbcpainter.loaders.level.LOADING_STATUS.*;

/**
* Implementation of the {@link org.gbcpainter.loaders.level.LevelLoader} interface.
* <p/>
* It loads the levels from the folder defined by {@link org.gbcpainter.env.GameSettings.STRING_SETTINGS_TYPE#LEVELS_PATH}. It uses a  {@link
* org.gbcpainter.loaders.level.parsers.AssertValidGraphParser} to load the level graph.
* <p/>
* Then initializes a {@link org.gbcpainter.game.levels.SimpleLevelImpl} with elements generated from a {@link org.gbcpainter.loaders.level.IGameElementFactory} Player has {@link
* #DEFAULT_START_LIVES} lives.
*
* @author Lorenzo Pellegrini
*/
@ThreadSafe
public class SimpleLevelLoader<P extends Player, M extends Monster, J extends Junction, E extends Pipe> implements LevelLoader {

  public static final int DEFAULT_START_LIVES = 5;

  private final IGameElementFactory<P, M, J, E> factory;

  @GuardedBy( "acquireMutex()" )
  private final Set<LevelLoadObserver> observers = new HashSet<>( 1 );

  private final Object mutex = new Object();
  @GuardedBy ("acquireMutex()")
  private volatile LOADING_STATUS status;
  @Nullable
  @GuardedBy ("acquireMutex()")
  private volatile Level loadResult;
  @GuardedBy ("acquireMutex()")
  private volatile boolean alreadyStarted = false;
  @GuardedBy ("acquireMutex()")
  private volatile Exception failException = null;

  /**
   * Creates a level loaders given a element facotry whose status is {@link LOADING_STATUS#NOT_STARTED}
   *
   * @param factory The factory that will be used to generate the level elements
   */
  public SimpleLevelLoader( @NotNull IGameElementFactory<P, M, J, E> factory ) {
    this.factory = factory;
    this.status = NOT_STARTED;
    this.loadResult = null;
  }

  @NotNull
  @Override
  public Level loadLevel( @NotNull @NonNls final String name ) throws IllegalStateException, LevelNotFoundException, IOException, ParsingFailException {
    checkStartable();
    final Level result;
    try {
      result = doLoading( name );
    } catch ( Exception e ) {
      synchronized (acquireMutex()) {
        failException = e;
        setStatus( FAIL );
      }
      throw e;
    }

    synchronized (acquireMutex()) {
      loadResult = result;
      setStatus( LOADING_STATUS.SUCCESS );
    }

    return result;
  }

  @Override
  public void startLoading( @NotNull @NonNls final String name,
                             @Nullable LevelLoadObserver observer ) throws IllegalStateException {
    checkStartable();
    if ( observer != null ) {
      addObserver( observer );
    }

    new Thread( new Runnable() {
      @Override
      public void run() {
        try {

          final Level result = doLoading( name );
          synchronized (acquireMutex()) {
            loadResult = result;
            setStatus( LOADING_STATUS.SUCCESS );
          }
        } catch ( Exception e ) {
          synchronized (acquireMutex()) {
            failException = e;
            setStatus( FAIL );
          }
        }
      }
    } ).start();
  }

  @Override
  public LOADING_STATUS getLoadStatus() {
    synchronized (acquireMutex()) {
      return this.status;
    }
  }


  @Nullable
  @Override
  public Exception getLoadingException() {
    synchronized (acquireMutex()) {
      return this.failException;
    }

  }

  @NotNull
  @Override
  public Level getResult() throws IllegalStateException {
    final Level actualResult;
    synchronized (acquireMutex()) {
      actualResult = this.loadResult;
    }
    if ( actualResult == null ) {
      throw new IllegalStateException();
    }

    return actualResult;
  }

  @Override
  public void addObserver( @NotNull final LevelLoadObserver observer ) {
    synchronized (acquireMutex()) {
      this.observers.add( observer );
    }
  }

  @Override
  public void removeObserver( @NotNull final LevelLoadObserver observer ) {
    synchronized (acquireMutex()) {
      this.observers.remove( observer );
    }
  }

  /**
   * Esegue l'effettivo caricamento del livello.
   *
   * @param name Il nome del livello da caricare.
   *
   * @return Il livello caricato.
   *
   * @throws LevelNotFoundException Se il livello desiderato non è stato trovato.
   * @throws IOException            Se c'è stato un errore di lettura del file.
   * @throws ParsingFailException   Se è stato impossibile effettuare il parsing del file.
   */
  @NotNull
  private Level doLoading( @NotNull @NonNls final String name ) throws LevelNotFoundException, IOException, ParsingFailException {
    setStatus( SEARCH_FILE );
    File levelsPath = GameSettings.getRelativeDirectory( GameSettings.getInstance().getValue( GameSettings.STRING_SETTINGS_TYPE.LEVELS_PATH ) );

    File levelFile = new File( levelsPath, name );
    if ( ! levelFile.canRead() ) {
      throw new LevelNotFoundException( "Couldn't find level: " +
                                        levelFile.getAbsolutePath() );
    }
    Path path = Paths.get( levelFile.getAbsolutePath() );

    setStatus( LOAD_FILE );
    ByteBuffer buffer = ByteBuffer.wrap( Files.readAllBytes( path ) );

    setStatus( PARSE_DATA );
    FileLevelParser parser = new AssertValidGraphParser();

    parser.parseData( buffer );
    SimpleGraph<Point, Segment> levelMap = parser.getLevelMap();
    Map<Integer, Set<Segment>> rawFacesMap = parser.getFacesMap();
    Point initialPlayerPosition = parser.getPlayerPosition();
    Map<String, java.util.List<Point>> monstersPositions = parser.getMonstersPosition();

    /* Creo i dati di gioco */
    setStatus( DEFINE_LEVEL );
    Player player = factory.createPlayer( initialPlayerPosition );

    Set<Monster> initializedMonsters = new HashSet<>();
    for (Map.Entry<String, java.util.List<Point>> monstersEntry : monstersPositions.entrySet()) {
      for (Point mobPosition : monstersEntry.getValue()) {
        initializedMonsters.add( factory.createMonster( monstersEntry.getKey(), mobPosition ) );
      }
    }

    final SimpleGraph<Junction, Pipe> pipeGrid = new SimpleGraph<>( new EdgeFactory<Junction, Pipe>() {
      @Override
      public E createEdge( final Junction sourceVertex, final Junction targetVertex ) {
        return factory.createEdge( new ImmutableSegment2D( sourceVertex.getPosition(), targetVertex.getPosition() ) );
      }
    } );

    final Map<Point, Junction> pointToJunction = new HashMap<>( levelMap.vertexSet().size() );

    for (Point vertex : levelMap.vertexSet()) {
      final J actualVertex = factory.createJunction( vertex, levelMap.edgesOf( vertex ) );
      pipeGrid.addVertex( actualVertex );
      pointToJunction.put( vertex, actualVertex );
    }

    final Map<Segment, Pipe> segmentToPipe = new HashMap<>( levelMap.edgeSet().size() );

    for (Segment edge : levelMap.edgeSet()) {
      Junction vertexA = pointToJunction.get( levelMap.getEdgeSource( edge ) );
      Junction vertexB = pointToJunction.get( levelMap.getEdgeTarget( edge ) );
      segmentToPipe.put( edge, pipeGrid.addEdge( vertexA, vertexB ) );
    }

    final Map<Set<Pipe>, Integer> faceMap = new HashMap<>( rawFacesMap.size() );

    for (Map.Entry<Integer, Set<Segment>> faceEntry : rawFacesMap.entrySet()) {
      final Set<Pipe> toEdges = new HashSet<>( faceEntry.getValue().size() );
      final Integer actualFace = faceEntry.getKey();

      for (Segment perimeterSegment : faceEntry.getValue()) {
        toEdges.add( segmentToPipe.get( perimeterSegment ) );
      }

      faceMap.put( toEdges, actualFace );
    }

    setStatus( INIT_LEVEL );

    return new SimpleLevelImpl( player, pipeGrid, faceMap, DEFAULT_START_LIVES, 0, initializedMonsters );
  }

  @NotNull
  private Object acquireMutex() {
    return mutex;
  }

  private void checkStartable() throws IllegalStateException {
    synchronized (acquireMutex()) {
      if ( alreadyStarted ) {
        throw new IllegalStateException();
      }
      alreadyStarted = true;
    }
  }

  private void setStatus( @NotNull LOADING_STATUS status ) {
    synchronized (acquireMutex()) {
      this.status = status;
      for(LevelLoadObserver observer : this.observers) {
        observer.loadStatusUpdated( this );
      }

      if(status.hasFinished()) {
        this.observers.clear();
      }
    }
  }


  @NonNls
  @Override
  public String toString() {
    synchronized (acquireMutex()) {
      return "SimpleLevelLoader{" +
             "factory=" + factory +
             ", status=" + status +
             ", loadResult=" + loadResult +
             ", alreadyStarted=" + alreadyStarted +
             ", failException=" + failException +
             '}';
    }
  }
}
TOP

Related Classes of org.gbcpainter.loaders.level.SimpleLevelLoader

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.