/*
* 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.engine.modes;
import com.google.common.base.Preconditions;
import com.google.common.collect.Queues;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.context.Context;
import org.terasology.engine.EngineTime;
import org.terasology.engine.GameEngine;
import org.terasology.engine.Time;
import org.terasology.engine.modes.loadProcesses.AwaitCharacterSpawn;
import org.terasology.engine.modes.loadProcesses.CacheBlocks;
import org.terasology.engine.modes.loadProcesses.CacheTextures;
import org.terasology.engine.modes.loadProcesses.CreateRemoteWorldEntity;
import org.terasology.engine.modes.loadProcesses.CreateWorldEntity;
import org.terasology.engine.modes.loadProcesses.EnsureSaveGameConsistency;
import org.terasology.engine.modes.loadProcesses.InitialiseBlockTypeEntities;
import org.terasology.engine.modes.loadProcesses.InitialiseCommandSystem;
import org.terasology.engine.modes.loadProcesses.InitialiseComponentSystemManager;
import org.terasology.engine.modes.loadProcesses.InitialiseEntitySystem;
import org.terasology.engine.modes.loadProcesses.InitialiseGraphics;
import org.terasology.engine.modes.loadProcesses.InitialisePhysics;
import org.terasology.engine.modes.loadProcesses.InitialiseRemoteWorld;
import org.terasology.engine.modes.loadProcesses.InitialiseSystems;
import org.terasology.engine.modes.loadProcesses.InitialiseWorld;
import org.terasology.engine.modes.loadProcesses.InitialiseWorldGenerator;
import org.terasology.engine.modes.loadProcesses.JoinServer;
import org.terasology.engine.modes.loadProcesses.LoadEntities;
import org.terasology.engine.modes.loadProcesses.LoadPrefabs;
import org.terasology.engine.modes.loadProcesses.PostBeginSystems;
import org.terasology.engine.modes.loadProcesses.PreBeginSystems;
import org.terasology.engine.modes.loadProcesses.PrepareWorld;
import org.terasology.engine.modes.loadProcesses.ProcessBlockPrefabs;
import org.terasology.engine.modes.loadProcesses.RegisterBiomes;
import org.terasology.engine.modes.loadProcesses.RegisterBlocks;
import org.terasology.engine.modes.loadProcesses.RegisterInputSystem;
import org.terasology.engine.modes.loadProcesses.RegisterMods;
import org.terasology.engine.modes.loadProcesses.RegisterSystems;
import org.terasology.engine.modes.loadProcesses.SetupLocalPlayer;
import org.terasology.engine.modes.loadProcesses.SetupRemotePlayer;
import org.terasology.engine.modes.loadProcesses.StartServer;
import org.terasology.game.Game;
import org.terasology.game.GameManifest;
import org.terasology.network.JoinStatus;
import org.terasology.network.NetworkMode;
import org.terasology.registry.CoreRegistry;
import org.terasology.rendering.nui.NUIManager;
import org.terasology.rendering.nui.internal.CanvasRenderer;
import org.terasology.rendering.nui.internal.NUIManagerInternal;
import org.terasology.rendering.nui.layers.mainMenu.loadingScreen.LoadingScreen;
import java.util.Queue;
/**
*/
public class StateLoading implements GameState {
private static final Logger logger = LoggerFactory.getLogger(StateLoading.class);
private Context context;
private GameManifest gameManifest;
private NetworkMode netMode;
private Queue<LoadProcess> loadProcesses = Queues.newArrayDeque();
private LoadProcess current;
private JoinStatus joinStatus;
private NUIManager nuiManager;
private LoadingScreen loadingScreen;
private int progress;
private int maxProgress;
/**
* Constructor for server or single player games
*
* @param gameManifest
* @param netMode
*/
public StateLoading(GameManifest gameManifest, NetworkMode netMode) {
Preconditions.checkArgument(netMode != NetworkMode.CLIENT);
this.gameManifest = gameManifest;
this.netMode = netMode;
}
/**
* Constructor for client of multiplayer game
*
* @param joinStatus
*/
public StateLoading(JoinStatus joinStatus) {
this.gameManifest = new GameManifest();
this.netMode = NetworkMode.CLIENT;
this.joinStatus = joinStatus;
}
@Override
public void init(GameEngine engine) {
this.context = engine.createChildContext();
CoreRegistry.setContext(context);
this.nuiManager = new NUIManagerInternal(context.get(CanvasRenderer.class), context);
context.put(NUIManager.class, nuiManager);
EngineTime time = (EngineTime) context.get(Time.class);
time.setPaused(true);
time.setGameTime(gameManifest.getTime());
context.get(Game.class).load(gameManifest);
switch (netMode) {
case CLIENT:
initClient();
break;
default:
initHost();
break;
}
progress = 0;
maxProgress = 0;
for (LoadProcess process : loadProcesses) {
maxProgress += process.getExpectedCost();
}
popStep();
loadingScreen = nuiManager.pushScreen("engine:loadingScreen", LoadingScreen.class);
loadingScreen.updateStatus(current.getMessage(), current.getProgress());
}
private void initClient() {
loadProcesses.add(new JoinServer(context, gameManifest, joinStatus));
loadProcesses.add(new CacheTextures());
loadProcesses.add(new InitialiseEntitySystem(context));
loadProcesses.add(new RegisterBlocks(context, gameManifest));
loadProcesses.add(new RegisterBiomes(context, gameManifest));
loadProcesses.add(new InitialiseGraphics(context));
loadProcesses.add(new CacheBlocks(context));
loadProcesses.add(new LoadPrefabs(context));
loadProcesses.add(new ProcessBlockPrefabs(context));
loadProcesses.add(new InitialiseComponentSystemManager(context));
loadProcesses.add(new RegisterInputSystem(context));
loadProcesses.add(new RegisterSystems(context, netMode));
loadProcesses.add(new InitialiseCommandSystem(context));
loadProcesses.add(new InitialiseRemoteWorld(context, gameManifest));
loadProcesses.add(new InitialisePhysics(context));
loadProcesses.add(new InitialiseSystems(context));
loadProcesses.add(new PreBeginSystems(context));
loadProcesses.add(new CreateRemoteWorldEntity(context));
loadProcesses.add(new PostBeginSystems(context));
loadProcesses.add(new SetupRemotePlayer(context));
loadProcesses.add(new AwaitCharacterSpawn(context));
loadProcesses.add(new PrepareWorld(context));
}
private void initHost() {
loadProcesses.add(new RegisterMods(context, gameManifest));
loadProcesses.add(new CacheTextures());
loadProcesses.add(new InitialiseEntitySystem(context));
loadProcesses.add(new RegisterBlocks(context, gameManifest));
loadProcesses.add(new RegisterBiomes(context, gameManifest));
loadProcesses.add(new InitialiseGraphics(context));
loadProcesses.add(new CacheBlocks(context));
loadProcesses.add(new LoadPrefabs(context));
loadProcesses.add(new ProcessBlockPrefabs(context));
loadProcesses.add(new InitialiseComponentSystemManager(context));
loadProcesses.add(new RegisterInputSystem(context));
loadProcesses.add(new RegisterSystems(context, netMode));
loadProcesses.add(new InitialiseCommandSystem(context));
loadProcesses.add(new InitialiseWorld(gameManifest, context));
loadProcesses.add(new EnsureSaveGameConsistency(context));
loadProcesses.add(new InitialisePhysics(context));
loadProcesses.add(new InitialiseSystems(context));
loadProcesses.add(new PreBeginSystems(context));
loadProcesses.add(new LoadEntities(context));
loadProcesses.add(new InitialiseBlockTypeEntities(context));
loadProcesses.add(new CreateWorldEntity(context));
loadProcesses.add(new InitialiseWorldGenerator(context));
if (netMode.isServer()) {
boolean dedicated;
if (netMode == NetworkMode.DEDICATED_SERVER) {
dedicated = true;
} else if (netMode == NetworkMode.LISTEN_SERVER) {
dedicated = false;
} else {
throw new IllegalStateException("Invalid server mode: " + netMode);
}
loadProcesses.add(new StartServer(context, dedicated));
}
loadProcesses.add(new PostBeginSystems(context));
if (netMode.hasLocalClient()) {
loadProcesses.add(new SetupLocalPlayer(context));
loadProcesses.add(new AwaitCharacterSpawn(context));
}
loadProcesses.add(new PrepareWorld(context));
}
private void popStep() {
if (current != null) {
progress += current.getExpectedCost();
}
current = null;
if (!loadProcesses.isEmpty()) {
current = loadProcesses.remove();
logger.debug(current.getMessage());
current.begin();
}
}
@Override
public void dispose() {
EngineTime time = (EngineTime) context.get(Time.class);
time.setPaused(false);
}
@Override
public void handleInput(float delta) {
}
@Override
public void update(float delta) {
GameEngine gameEngine = context.get(GameEngine.class);
EngineTime time = (EngineTime) context.get(Time.class);
long startTime = time.getRealTimeInMs();
while (current != null && time.getRealTimeInMs() - startTime < 20 && !gameEngine.hasPendingState()) {
if (current.step()) {
popStep();
}
}
if (current == null) {
nuiManager.closeScreen(loadingScreen);
nuiManager.setHUDVisible(true);
context.get(GameEngine.class).changeState(new StateIngame(gameManifest, context));
} else {
float progressValue = (progress + current.getExpectedCost() * current.getProgress()) / maxProgress;
loadingScreen.updateStatus(current.getMessage(), progressValue);
nuiManager.update(delta);
}
}
@Override
public void render() {
nuiManager.render();
}
@Override
public boolean isHibernationAllowed() {
return false;
}
@Override
public String getLoggingPhase() {
return gameManifest.getTitle();
}
}