org.terasology.world.biomes.BiomeManager.java Source code

Java tutorial

Introduction

Here is the source code for org.terasology.world.biomes.BiomeManager.java

Source

/*
 * Copyright 2014 MovingBlocks
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.terasology.world.biomes;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.module.ModuleEnvironment;

import java.util.Collections;
import java.util.List;
import java.util.Map;

public class BiomeManager implements BiomeRegistry {

    private static final Logger logger = LoggerFactory.getLogger(BiomeManager.class);

    // Bi Di map between biome short id and biome
    private final BiMap<Short, Biome> biomeShortIdMap = HashBiMap.create();

    // Map from biome id to biome
    private final BiMap<String, Biome> biomeIdMap = HashBiMap.create();

    /**
     * Creates a new Biome Manager that auto discovers all biomes in the given environment.
     */
    public BiomeManager(ModuleEnvironment environment) {
        this(environment, Collections.<String, Short>emptyMap());
    }

    /**
     * Create a BiomeManager from known state such as a world save, that already contains
     * a mapping between Biome URIs and their short ids.
     *
     * @param knownBiomeIdMap A mapping between Biome URIs (combination of module id + biome id) and
     *                        their short ids that are applicable to this world save.
     */
    public BiomeManager(ModuleEnvironment moduleEnvironment, Map<String, Short> knownBiomeIdMap) {

        for (Class<?> biomeRegistrator : moduleEnvironment.getSubtypesOf(BiomeRegistrator.class)) {

            BiomeRegistrator registrator;
            try {
                registrator = (BiomeRegistrator) biomeRegistrator.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                logger.error("Cannot call biome registrator {} because it cannot be instantiated.",
                        biomeRegistrator, e);
                continue;
            }

            registrator.registerBiomes(this);

        }

        BiMap<Short, Biome> currentIdMap = HashBiMap.create(biomeShortIdMap); // Make a copy before we start modifying it
        biomeShortIdMap.clear();

        // Always register the unknown biome first, so it gets id 0, which is the default for all chunks
        registerBiome(UnknownBiome.INSTANCE);

        for (Map.Entry<String, Short> entry : knownBiomeIdMap.entrySet()) {
            if (entry.getKey().equals(getUnknownBiome().getId())) {
                continue; // The unknown biome is handled internally
            }

            Biome biome = biomeIdMap.get(entry.getKey());
            if (biome == null) {
                throw new IllegalStateException(
                        "Save game references biome " + entry.getKey() + " which is no " + "longer available.");
            }
            if (biomeShortIdMap.put(entry.getValue(), biome) != null) {
                throw new IllegalStateException("Biome short id " + entry.getValue() + " is present multiple times "
                        + "in the save game (latest is mapped to " + biome.getId() + ".");
            }
            logger.info("Restored biome {} with short id {} from save game.", entry.getKey(), entry.getValue());
            currentIdMap.values().remove(biome);
        }

        // Handle all new biomes that weren't present in the save game
        for (Biome biome : currentIdMap.values()) {
            short freeBiomeId = getFreeBiomeId();
            biomeShortIdMap.put(freeBiomeId, biome);
            logger.info("Registered new biome {} with id {} that wasn't present in the save game.", biome.getId(),
                    freeBiomeId);
        }

    }

    public String getBiomeId(Biome biome) {
        return biomeIdMap.inverse().get(biome);
    }

    public void registerBiome(Biome biome) {
        String fullId = biome.getId();

        if (biomeShortIdMap.containsValue(biome)) {
            throw new IllegalArgumentException("The biome " + fullId + " is already registered.");
        }

        if (biomeIdMap.containsKey(fullId)) {
            throw new IllegalArgumentException("A biome with id " + fullId + " is already registered!");
        }

        short biomeShortId = getFreeBiomeId();

        logger.info("Registering biome {} with short id {}.", biome, biomeShortId);

        biomeShortIdMap.put(biomeShortId, biome);
        biomeIdMap.put(fullId, biome);
    }

    @Override
    public Biome getBiomeById(String id) {
        return biomeIdMap.get(id);
    }

    @Override
    public List<Biome> getBiomes() {
        return ImmutableList.copyOf(biomeIdMap.values());
    }

    /**
     * Returns a biome with the given id and of the given biome type, only if the biome actually is of the given type.
     *
     * @param id
     * @param biomeClass
     * @param <T>
     * @return
     */
    @Override
    public <T extends Biome> T getBiomeById(String id, Class<T> biomeClass) {
        Biome biome = getBiomeById(id);
        if (biome != null) {
            if (biomeClass.isAssignableFrom(biome.getClass())) {
                return biomeClass.cast(biome);
            }
        }
        return null;
    }

    @Override
    public <T extends Biome> List<T> getBiomes(Class<T> biomeClass) {

        ImmutableList.Builder<T> builder = ImmutableList.builder();

        for (Biome biome : biomeIdMap.values()) {
            if (biomeClass.isAssignableFrom(biome.getClass())) {
                builder.add(biomeClass.cast(biome));
            }
        }

        return builder.build();

    }

    public Biome getBiomeByShortId(short biomeShortId) {
        Biome result = biomeShortIdMap.get(biomeShortId);
        if (result == null) {
            result = getUnknownBiome();
        }
        return result;
    }

    public short getBiomeShortId(Biome biome) {
        Short result = biomeShortIdMap.inverse().get(biome);
        if (result == null) {
            throw new IllegalArgumentException("No short id for biome " + biome + " exists.");
        }
        return result;
    }

    private short getFreeBiomeId() {
        short id = 0;
        while (id < Short.MAX_VALUE) {
            if (!biomeShortIdMap.containsKey(id)) {
                return id;
            }
            id++;
        }
        throw new IllegalStateException("The maximum number of biomes has been reached: " + biomeShortIdMap.size());
    }

    /**
     * @return A biome that can be used to avoid a null pointer in cases where the real biome is unknown, i.e.
     * when the chunk has not yet been loaded.
     * <p/>
     * TODO: Decide how a caller can determine that he got the unknown biome and not the real one.
     */
    public static Biome getUnknownBiome() {
        return UnknownBiome.INSTANCE;
    }

}