/* * Copyright 2013 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; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.nio.file.ShrinkWrapFileSystems; import org.jboss.shrinkwrap.api.spec.JavaArchive; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.terasology.assets.AssetFactory; import org.terasology.assets.management.AssetManager; import org.terasology.assets.module.ModuleAwareAssetTypeManager; import org.terasology.audio.AudioManager; import org.terasology.audio.StaticSound; import org.terasology.audio.StreamingSound; import org.terasology.audio.nullAudio.NullAudioManager; import org.terasology.audio.nullAudio.NullSound; import org.terasology.audio.nullAudio.NullStreamingSound; import org.terasology.config.Config; import org.terasology.engine.ComponentSystemManager; import org.terasology.engine.EngineTime; import org.terasology.engine.Time; import org.terasology.engine.bootstrap.EntitySystemSetupUtil; import org.terasology.engine.modes.loadProcesses.LoadPrefabs; import org.terasology.engine.module.ModuleManager; import org.terasology.engine.paths.PathManager; import org.terasology.engine.subsystem.headless.assets.HeadlessMaterial; import org.terasology.engine.subsystem.headless.assets.HeadlessMesh; import org.terasology.engine.subsystem.headless.assets.HeadlessShader; import org.terasology.engine.subsystem.headless.assets.HeadlessSkeletalMesh; import org.terasology.engine.subsystem.headless.assets.HeadlessTexture; import org.terasology.entitySystem.entity.internal.EngineEntityManager; import org.terasology.entitySystem.prefab.Prefab; import org.terasology.entitySystem.prefab.PrefabData; import org.terasology.entitySystem.prefab.internal.PojoPrefab; import org.terasology.logic.behavior.asset.BehaviorTree; import org.terasology.logic.behavior.asset.BehaviorTreeData; import org.terasology.module.DependencyResolver; import org.terasology.module.ModuleEnvironment; import org.terasology.module.ModuleRegistry; import org.terasology.module.ResolutionResult; import org.terasology.naming.Name; import org.terasology.network.NetworkSystem; import org.terasology.network.internal.NetworkSystemImpl; import org.terasology.persistence.StorageManager; import org.terasology.persistence.internal.ReadWriteStorageManager; import org.terasology.persistence.typeHandling.TypeSerializationLibrary; import org.terasology.persistence.typeHandling.extensionTypes.BlockFamilyTypeHandler; import org.terasology.persistence.typeHandling.extensionTypes.BlockTypeHandler; import org.terasology.persistence.typeHandling.extensionTypes.CollisionGroupTypeHandler; import org.terasology.physics.CollisionGroup; import org.terasology.physics.CollisionGroupManager; import org.terasology.rendering.assets.animation.MeshAnimation; import org.terasology.rendering.assets.animation.MeshAnimationData; import org.terasology.rendering.assets.animation.MeshAnimationImpl; import org.terasology.rendering.assets.atlas.Atlas; import org.terasology.rendering.assets.atlas.AtlasData; import org.terasology.rendering.assets.font.Font; import org.terasology.rendering.assets.font.FontData; import org.terasology.rendering.assets.font.FontImpl; import org.terasology.rendering.assets.material.Material; import org.terasology.rendering.assets.material.MaterialData; import org.terasology.rendering.assets.mesh.Mesh; import org.terasology.rendering.assets.mesh.MeshData; import org.terasology.rendering.assets.shader.Shader; import org.terasology.rendering.assets.shader.ShaderData; import org.terasology.rendering.assets.skeletalmesh.SkeletalMesh; import org.terasology.rendering.assets.skeletalmesh.SkeletalMeshData; import org.terasology.rendering.assets.texture.PNGTextureFormat; import org.terasology.rendering.assets.texture.Texture; import org.terasology.rendering.assets.texture.TextureData; import org.terasology.rendering.assets.texture.subtexture.Subtexture; import org.terasology.rendering.assets.texture.subtexture.SubtextureData; import org.terasology.rendering.nui.asset.UIData; import org.terasology.rendering.nui.asset.UIElement; import org.terasology.rendering.nui.skin.UISkin; import org.terasology.rendering.nui.skin.UISkinData; import org.terasology.testUtil.ModuleManagerFactory; import org.terasology.world.WorldProvider; import org.terasology.world.biomes.BiomeManager; import org.terasology.world.block.Block; import org.terasology.world.block.BlockManager; import org.terasology.world.block.family.AttachedToSurfaceFamilyFactory; import org.terasology.world.block.family.BlockFamily; import org.terasology.world.block.family.DefaultBlockFamilyFactoryRegistry; import org.terasology.world.block.family.HorizontalBlockFamilyFactory; import org.terasology.world.block.internal.BlockManagerImpl; import org.terasology.world.block.loader.BlockFamilyDefinition; import org.terasology.world.block.loader.BlockFamilyDefinitionData; import org.terasology.world.block.loader.BlockFamilyDefinitionFormat; import org.terasology.world.block.shapes.BlockShape; import org.terasology.world.block.shapes.BlockShapeData; import org.terasology.world.block.shapes.BlockShapeImpl; import org.terasology.world.block.sounds.BlockSounds; import org.terasology.world.block.sounds.BlockSoundsData; import org.terasology.world.block.tiles.BlockTile; import org.terasology.world.block.tiles.NullWorldAtlas; import org.terasology.world.block.tiles.TileData; import org.terasology.world.block.tiles.WorldAtlas; import org.terasology.world.internal.WorldInfo; import org.terasology.world.sun.BasicCelestialModel; import org.terasology.world.sun.CelestialSystem; import org.terasology.world.sun.DefaultCelestialSystem; import org.terasology.world.time.WorldTime; import org.terasology.world.time.WorldTimeImpl; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Path; import java.util.Set; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Setup a headless ( = no graphics ) environment. * Based on TerasologyTestingEnvironment code. * */ public class HeadlessEnvironment extends Environment { protected static final WorldTime worldTime = new WorldTimeImpl(); private static final Logger logger = LoggerFactory.getLogger(HeadlessEnvironment.class); /** * Setup a headless ( = no graphics ) environment * * @param modules a set of module names that should be loaded (latest version) */ public HeadlessEnvironment(Name... modules) { super(modules); } @Override protected void setupStorageManager() throws IOException { ModuleManager moduleManager = context.get(ModuleManager.class); EngineEntityManager engineEntityManager = context.get(EngineEntityManager.class); BlockManager blockManager = context.get(BlockManager.class); BiomeManager biomeManager = context.get(BiomeManager.class); Path savePath = PathManager.getInstance().getSavePath("world1"); context.put(StorageManager.class, new ReadWriteStorageManager(savePath, moduleManager.getEnvironment(), engineEntityManager, blockManager, biomeManager)); } @Override protected void setupNetwork() { EngineTime mockTime = mock(EngineTime.class); context.put(Time.class, mockTime); NetworkSystem networkSystem = new NetworkSystemImpl(mockTime, getContext()); context.put(NetworkSystem.class, networkSystem); } @Override protected void setupEntitySystem() { EntitySystemSetupUtil.addEntityManagementRelatedClasses(context); } @Override protected void setupCollisionManager() { CollisionGroupManager collisionGroupManager = new CollisionGroupManager(); context.put(CollisionGroupManager.class, collisionGroupManager); context.get(TypeSerializationLibrary.class).add(CollisionGroup.class, new CollisionGroupTypeHandler(collisionGroupManager)); } @Override protected void setupBlockManager(AssetManager assetManager) { WorldAtlas worldAtlas = new NullWorldAtlas(); BlockManagerImpl blockManager = new BlockManagerImpl(worldAtlas, assetManager); context.put(BlockManager.class, blockManager); TypeSerializationLibrary typeSerializationLibrary = context.get(TypeSerializationLibrary.class); typeSerializationLibrary.add(BlockFamily.class, new BlockFamilyTypeHandler(blockManager)); typeSerializationLibrary.add(Block.class, new BlockTypeHandler(blockManager)); } @Override protected AssetManager setupEmptyAssetManager() { ModuleAwareAssetTypeManager assetTypeManager = new ModuleAwareAssetTypeManager(); assetTypeManager.switchEnvironment(context.get(ModuleManager.class).getEnvironment()); context.put(ModuleAwareAssetTypeManager.class, assetTypeManager); context.put(AssetManager.class, assetTypeManager.getAssetManager()); return assetTypeManager.getAssetManager(); } @Override protected AssetManager setupAssetManager() { ModuleAwareAssetTypeManager assetTypeManager = new ModuleAwareAssetTypeManager(); // cast lambdas explicitly to avoid inconsistent compiler behavior wrt. type inference assetTypeManager.registerCoreAssetType(Prefab.class, (AssetFactory<Prefab, PrefabData>) PojoPrefab::new, false, "prefabs"); assetTypeManager.registerCoreAssetType(BlockShape.class, (AssetFactory<BlockShape, BlockShapeData>) BlockShapeImpl::new, "shapes"); assetTypeManager.registerCoreAssetType(BlockSounds.class, (AssetFactory<BlockSounds, BlockSoundsData>) BlockSounds::new, "blockSounds"); assetTypeManager.registerCoreAssetType(BlockTile.class, (AssetFactory<BlockTile, TileData>) BlockTile::new, "blockTiles"); assetTypeManager.registerCoreAssetType(BlockFamilyDefinition.class, (AssetFactory<BlockFamilyDefinition, BlockFamilyDefinitionData>) BlockFamilyDefinition::new, "blocks"); assetTypeManager.registerCoreAssetType(StaticSound.class, NullSound::new, "sounds"); assetTypeManager.registerCoreAssetType(StreamingSound.class, NullStreamingSound::new, "music"); DefaultBlockFamilyFactoryRegistry blockFamilyFactoryRegistry = new DefaultBlockFamilyFactoryRegistry(); blockFamilyFactoryRegistry.setBlockFamilyFactory("horizontal", new HorizontalBlockFamilyFactory()); blockFamilyFactoryRegistry.setBlockFamilyFactory("alignToSurface", new AttachedToSurfaceFamilyFactory()); assetTypeManager.registerCoreFormat(BlockFamilyDefinition.class, new BlockFamilyDefinitionFormat(assetTypeManager.getAssetManager(), blockFamilyFactoryRegistry)); assetTypeManager.registerCoreAssetType(UISkin.class, (AssetFactory<UISkin, UISkinData>) UISkin::new, "skins"); assetTypeManager.registerCoreAssetType(BehaviorTree.class, (AssetFactory<BehaviorTree, BehaviorTreeData>) BehaviorTree::new, false, "behaviors"); assetTypeManager.registerCoreAssetType(UIElement.class, (AssetFactory<UIElement, UIData>) UIElement::new, "ui"); assetTypeManager.registerCoreAssetType(Font.class, (AssetFactory<Font, FontData>) FontImpl::new, "fonts"); assetTypeManager.registerCoreAssetType(Texture.class, (AssetFactory<Texture, TextureData>) HeadlessTexture::new, "textures", "fonts"); assetTypeManager.registerCoreFormat(Texture.class, new PNGTextureFormat(Texture.FilterMode.NEAREST, path -> path.getName(2).toString().equals("textures"))); assetTypeManager.registerCoreFormat(Texture.class, new PNGTextureFormat(Texture.FilterMode.LINEAR, path -> path.getName(2).toString().equals("fonts"))); assetTypeManager.registerCoreAssetType(Shader.class, (AssetFactory<Shader, ShaderData>) HeadlessShader::new, "shaders"); assetTypeManager.registerCoreAssetType(Material.class, (AssetFactory<Material, MaterialData>) HeadlessMaterial::new, "materials"); assetTypeManager.registerCoreAssetType(Mesh.class, (AssetFactory<Mesh, MeshData>) HeadlessMesh::new, "mesh"); assetTypeManager.registerCoreAssetType(SkeletalMesh.class, (AssetFactory<SkeletalMesh, SkeletalMeshData>) HeadlessSkeletalMesh::new, "skeletalMesh"); assetTypeManager.registerCoreAssetType(MeshAnimation.class, (AssetFactory<MeshAnimation, MeshAnimationData>) MeshAnimationImpl::new, "animations"); assetTypeManager.registerCoreAssetType(Atlas.class, (AssetFactory<Atlas, AtlasData>) Atlas::new, "atlas"); assetTypeManager.registerCoreAssetType(Subtexture.class, (AssetFactory<Subtexture, SubtextureData>) Subtexture::new); assetTypeManager.switchEnvironment(context.get(ModuleManager.class).getEnvironment()); context.put(ModuleAwareAssetTypeManager.class, assetTypeManager); context.put(AssetManager.class, assetTypeManager.getAssetManager()); return assetTypeManager.getAssetManager(); } @Override protected void setupAudio() { NullAudioManager audioManager = new NullAudioManager(); context.put(AudioManager.class, audioManager); } @Override protected void setupConfig() { Config config = new Config(); config.loadDefaults(); context.put(Config.class, config); } @Override protected void setupModuleManager(Set<Name> moduleNames) throws Exception { ModuleManager moduleManager = ModuleManagerFactory.create(); ModuleRegistry registry = moduleManager.getRegistry(); DependencyResolver resolver = new DependencyResolver(registry); ResolutionResult result = resolver.resolve(moduleNames); if (result.isSuccess()) { ModuleEnvironment modEnv = moduleManager.loadEnvironment(result.getModules(), true); logger.debug("Loaded modules: " + modEnv.getModuleIdsOrderedByDependencies()); } else { logger.error("Could not resolve module dependencies for " + moduleNames); } context.put(ModuleManager.class, moduleManager); EntitySystemSetupUtil.addReflectionBasedLibraries(context); } /** * @throws IOException ShrinkWrap errors */ @Override protected void setupPathManager() throws IOException { final JavaArchive homeArchive = ShrinkWrap.create(JavaArchive.class); final FileSystem vfs = ShrinkWrapFileSystems.newFileSystem(homeArchive); PathManager.getInstance().useOverrideHomePath(vfs.getPath("")); } @Override protected void setupComponentManager() { ComponentSystemManager componentSystemManager = new ComponentSystemManager(context); componentSystemManager.initialise(); context.put(ComponentSystemManager.class, componentSystemManager); } @Override protected void setupWorldProvider() { WorldProvider worldProvider = mock(WorldProvider.class); when(worldProvider.getWorldInfo()).thenReturn(new WorldInfo()); when(worldProvider.getTime()).thenReturn(worldTime); context.put(WorldProvider.class, worldProvider); } @Override protected void setupCelestialSystem() { DefaultCelestialSystem celestialSystem = new DefaultCelestialSystem(new BasicCelestialModel(), context); context.put(CelestialSystem.class, celestialSystem); } @Override protected void loadPrefabs() { LoadPrefabs prefabLoadStep = new LoadPrefabs(context); boolean complete = false; prefabLoadStep.begin(); while (!complete) { complete = prefabLoadStep.step(); } } @Override public void close() throws Exception { // it would be nice, if elements in the context implemented (Auto)Closeable // The StorageManager creates a thread pool (through TaskMaster) // which isn't closed automatically StorageManager storageManager = context.get(StorageManager.class); if (storageManager != null) { storageManager.finishSavingAndShutdown(); } super.close(); } }