Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(GameState): extract common code from MainMenu and HeadlessSetup #4809

Merged
merged 4 commits into from
Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2021 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0

package org.terasology.engine.core.modes;

import org.terasology.engine.context.Context;
import org.terasology.engine.core.ComponentSystemManager;
import org.terasology.engine.core.bootstrap.EntitySystemSetupUtil;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.engine.entitySystem.entity.internal.EngineEntityManager;
import org.terasology.engine.entitySystem.event.internal.EventSystem;
import org.terasology.engine.logic.console.Console;
import org.terasology.engine.logic.console.ConsoleImpl;
import org.terasology.engine.logic.console.ConsoleSystem;
import org.terasology.engine.logic.console.commands.CoreCommands;
import org.terasology.engine.logic.players.LocalPlayer;
import org.terasology.engine.network.ClientComponent;
import org.terasology.engine.recording.DirectionAndOriginPosRecorderList;
import org.terasology.engine.recording.RecordAndReplayCurrentStatus;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.rendering.nui.NUIManager;
import org.terasology.engine.rendering.nui.internal.NUIManagerInternal;
import org.terasology.engine.rendering.nui.internal.TerasologyCanvasRenderer;
import org.terasology.nui.canvas.CanvasRenderer;

import static com.google.common.base.Verify.verifyNotNull;

public abstract class AbstractState implements GameState {
protected Context context;
protected EngineEntityManager entityManager;
protected EventSystem eventSystem;
protected ComponentSystemManager componentSystemManager;

protected void initEntityAndComponentManagers() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does a lot with the context and strictly requires it. Would it make sense to make the context and argument of this method, similar to how we pass it to createLocalPlayer explcitly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createLocalPlayer is the method I extracted first. Passing context let me make it static, which I figured would make it easier to move the method later if there were more copies of it.

Then I realized if I shuffled some things around I could also extract all this stuff that went in to initEntityAndComponentManagers, but because it assigns things to multiple instance fields, it wouldn't work as static.

Upon review, I see that all these fields are direct references to a thing in the context, so it probably could be split in to a "populate context with this stuff" static method and then a "pull some references out of context and cache them in fields" non-static method, but I worried that would make it more difficult to review if I included it in the same PR.

verifyNotNull(context);
CoreRegistry.setContext(context);

// let's get the entity event system running
EntitySystemSetupUtil.addEntityManagementRelatedClasses(context);
entityManager = context.get(EngineEntityManager.class);

eventSystem = context.get(EventSystem.class);
context.put(Console.class, new ConsoleImpl(context));

NUIManager nuiManager = new NUIManagerInternal((TerasologyCanvasRenderer) context.get(CanvasRenderer.class), context);
context.put(NUIManager.class, nuiManager);

componentSystemManager = new ComponentSystemManager(context);
context.put(ComponentSystemManager.class, componentSystemManager);

componentSystemManager.register(new ConsoleSystem(), "engine:ConsoleSystem");
componentSystemManager.register(new CoreCommands(), "engine:CoreCommands");
}

protected static void createLocalPlayer(Context context) {
EngineEntityManager entityManager = context.get(EngineEntityManager.class);
EntityRef localPlayerEntity = entityManager.create(new ClientComponent());
LocalPlayer localPlayer = new LocalPlayer();
localPlayer.setRecordAndReplayClasses(context.get(DirectionAndOriginPosRecorderList.class),
context.get(RecordAndReplayCurrentStatus.class));
context.put(LocalPlayer.class, localPlayer);
localPlayer.setClientEntity(localPlayerEntity);
}

@Override
public void dispose(boolean shuttingDown) {
// Apparently this can be disposed of before it is completely initialized? Probably only during
// crashes, but crashing again during shutdown complicates the diagnosis.
if (eventSystem != null) {
eventSystem.process();
}
if (componentSystemManager != null) {
componentSystemManager.shutdown();
}
if (entityManager != null) {
entityManager.clear();
}
}

@Override
public Context getContext() {
return context;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.crashreporter.CrashReporter;
import org.terasology.engine.config.Config;
import org.terasology.engine.config.SystemConfig;
import org.terasology.engine.context.Context;
import org.terasology.engine.core.EngineTime;
Expand Down Expand Up @@ -66,17 +65,16 @@ 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 final GameManifest gameManifest;
private final NetworkMode netMode;
private final Queue<LoadProcess> loadProcesses = Queues.newArrayDeque();
private LoadProcess current;
private JoinStatus joinStatus;

private NUIManager nuiManager;

private LoadingScreen loadingScreen;

private Config config;
private SystemConfig systemConfig;

private int progress;
Expand Down Expand Up @@ -109,7 +107,6 @@ public void init(GameEngine engine) {
this.context = engine.createChildContext();
CoreRegistry.setContext(context);

config = context.get(Config.class);
systemConfig = context.get(SystemConfig.class);

this.nuiManager = new NUIManagerInternal((TerasologyCanvasRenderer) context.get(CanvasRenderer.class), context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,27 @@
import org.terasology.engine.audio.AudioManager;
import org.terasology.engine.config.Config;
import org.terasology.engine.config.TelemetryConfig;
import org.terasology.engine.context.Context;
import org.terasology.engine.core.ComponentSystemManager;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.LoggingContext;
import org.terasology.engine.core.bootstrap.EntitySystemSetupUtil;
import org.terasology.engine.core.modes.loadProcesses.RegisterInputSystem;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.engine.entitySystem.entity.internal.EngineEntityManager;
import org.terasology.engine.entitySystem.event.internal.EventSystem;
import org.terasology.engine.i18n.TranslationSystem;
import org.terasology.engine.identity.storageServiceClient.StorageServiceWorker;
import org.terasology.engine.input.InputSystem;
import org.terasology.engine.input.cameraTarget.CameraTargetSystem;
import org.terasology.engine.logic.console.Console;
import org.terasology.engine.logic.console.ConsoleImpl;
import org.terasology.engine.logic.console.ConsoleSystem;
import org.terasology.engine.logic.console.commands.CoreCommands;
import org.terasology.engine.logic.players.LocalPlayer;
import org.terasology.engine.network.ClientComponent;
import org.terasology.engine.recording.DirectionAndOriginPosRecorderList;
import org.terasology.engine.recording.RecordAndReplayCurrentStatus;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.rendering.nui.NUIManager;
import org.terasology.engine.rendering.nui.editor.systems.NUIEditorSystem;
import org.terasology.engine.rendering.nui.editor.systems.NUISkinEditorSystem;
import org.terasology.engine.rendering.nui.internal.NUIManagerInternal;
import org.terasology.engine.rendering.nui.internal.TerasologyCanvasRenderer;
import org.terasology.engine.rendering.nui.layers.mainMenu.LaunchPopup;
import org.terasology.engine.rendering.nui.layers.mainMenu.MessagePopup;
import org.terasology.engine.telemetry.TelemetryScreen;
import org.terasology.engine.telemetry.TelemetryUtils;
import org.terasology.engine.telemetry.logstash.TelemetryLogstashAppender;
import org.terasology.engine.utilities.Assets;
import org.terasology.nui.canvas.CanvasRenderer;

/**
* The class implements the main game menu.
* <br><br>
*
* @version 0.3
*/
public class StateMainMenu implements GameState {
private Context context;
private EngineEntityManager entityManager;
private EventSystem eventSystem;
private ComponentSystemManager componentSystemManager;
public class StateMainMenu extends AbstractState {
private NUIManager nuiManager;
private InputSystem inputSystem;
private Console console;
Expand All @@ -69,33 +44,15 @@ public StateMainMenu(String showMessageOnLoad) {
@Override
public void init(GameEngine gameEngine) {
context = gameEngine.createChildContext();
CoreRegistry.setContext(context);
initEntityAndComponentManagers();

//let's get the entity event system running
EntitySystemSetupUtil.addEntityManagementRelatedClasses(context);
entityManager = context.get(EngineEntityManager.class);
createLocalPlayer(context);

eventSystem = context.get(EventSystem.class);
console = new ConsoleImpl(context);
context.put(Console.class, console);

nuiManager = new NUIManagerInternal((TerasologyCanvasRenderer) context.get(CanvasRenderer.class), context);
context.put(NUIManager.class, nuiManager);
// TODO: REMOVE this and handle refreshing of core game state at the engine level - see Issue #1127
new RegisterInputSystem(context).step();

nuiManager = context.get(NUIManager.class);
eventSystem.registerEventHandler(nuiManager);

componentSystemManager = new ComponentSystemManager(context);
context.put(ComponentSystemManager.class, componentSystemManager);

// TODO: Reduce coupling between Input system and CameraTargetSystem,
// TODO: potentially eliminating the following lines. See Issue #1126
CameraTargetSystem cameraTargetSystem = new CameraTargetSystem();
context.put(CameraTargetSystem.class, cameraTargetSystem);
Comment on lines -90 to -93
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like I've removed this use of CameraTargetSystem in the main menu, but that's not really the case. It still happens within RegisterInputSystem called in this method.


componentSystemManager.register(cameraTargetSystem, "engine:CameraTargetSystem");
componentSystemManager.register(new ConsoleSystem(), "engine:ConsoleSystem");
componentSystemManager.register(new CoreCommands(), "engine:CoreCommands");

NUIEditorSystem nuiEditorSystem = new NUIEditorSystem();
context.put(NUIEditorSystem.class, nuiEditorSystem);
componentSystemManager.register(nuiEditorSystem, "engine:NUIEditorSystem");
Expand All @@ -106,17 +63,9 @@ public void init(GameEngine gameEngine) {

inputSystem = context.get(InputSystem.class);

// TODO: REMOVE this and handle refreshing of core game state at the engine level - see Issue #1127
new RegisterInputSystem(context).step();

EntityRef localPlayerEntity = entityManager.create(new ClientComponent());
LocalPlayer localPlayer = new LocalPlayer();
localPlayer.setRecordAndReplayClasses(context.get(DirectionAndOriginPosRecorderList.class), context.get(RecordAndReplayCurrentStatus.class));
context.put(LocalPlayer.class, localPlayer);
localPlayer.setClientEntity(localPlayerEntity);

componentSystemManager.initialise();

console = context.get(Console.class);
storageServiceWorker = context.get(StorageServiceWorker.class);

playBackgroundMusic();
Expand Down Expand Up @@ -164,22 +113,11 @@ private void pushLaunchPopup() {

@Override
public void dispose(boolean shuttingDown) {
// Apparently this can be disposed of before it is completely initialized? Probably only during
// crashes, but crashing again during shutdown complicates the diagnosis.
if (eventSystem != null) {
eventSystem.process();
}
if (componentSystemManager != null) {
componentSystemManager.shutdown();
}
stopBackgroundMusic();

if (nuiManager != null) {
nuiManager.clear();
}
if (entityManager != null) {
entityManager.clear();
}
super.dispose(shuttingDown);
}

private void playBackgroundMusic() {
Expand Down Expand Up @@ -214,11 +152,6 @@ public String getLoggingPhase() {
return LoggingContext.MENU;
}

@Override
public Context getContext() {
return context;
}

@Override
public boolean isHibernationAllowed() {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,89 +6,44 @@
import org.slf4j.LoggerFactory;
import org.terasology.engine.config.Config;
import org.terasology.engine.config.WorldGenerationConfig;
import org.terasology.engine.context.Context;
import org.terasology.engine.core.ComponentSystemManager;
import org.terasology.engine.core.GameEngine;
import org.terasology.engine.core.LoggingContext;
import org.terasology.engine.core.SimpleUri;
import org.terasology.engine.core.TerasologyConstants;
import org.terasology.engine.core.bootstrap.EntitySystemSetupUtil;
import org.terasology.engine.core.modes.GameState;
import org.terasology.engine.core.modes.AbstractState;
import org.terasology.engine.core.modes.StateLoading;
import org.terasology.engine.core.module.ModuleManager;
import org.terasology.engine.core.module.StandardModuleExtension;
import org.terasology.engine.entitySystem.entity.EntityRef;
import org.terasology.engine.entitySystem.entity.internal.EngineEntityManager;
import org.terasology.engine.entitySystem.event.internal.EventSystem;
import org.terasology.engine.game.GameManifest;
import org.terasology.engine.input.InputSystem;
import org.terasology.engine.logic.console.Console;
import org.terasology.engine.logic.console.ConsoleImpl;
import org.terasology.engine.logic.console.ConsoleSystem;
import org.terasology.engine.logic.console.commands.CoreCommands;
import org.terasology.engine.logic.players.LocalPlayer;
import org.terasology.engine.network.ClientComponent;
import org.terasology.engine.network.NetworkMode;
import org.terasology.engine.recording.DirectionAndOriginPosRecorderList;
import org.terasology.engine.recording.RecordAndReplayCurrentStatus;
import org.terasology.engine.registry.CoreRegistry;
import org.terasology.engine.rendering.nui.NUIManager;
import org.terasology.engine.rendering.nui.internal.NUIManagerInternal;
import org.terasology.engine.rendering.nui.internal.TerasologyCanvasRenderer;
import org.terasology.engine.rendering.nui.layers.mainMenu.savedGames.GameInfo;
import org.terasology.engine.rendering.nui.layers.mainMenu.savedGames.GameProvider;
import org.terasology.engine.world.internal.WorldInfo;
import org.terasology.engine.world.time.WorldTime;
import org.terasology.gestalt.module.Module;
import org.terasology.gestalt.naming.Name;
import org.terasology.nui.canvas.CanvasRenderer;

import java.util.List;

/**
* The class is game selection menu replacement for the headless server.
*
*/
public class StateHeadlessSetup implements GameState {
public class StateHeadlessSetup extends AbstractState {

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

private EngineEntityManager entityManager;
private EventSystem eventSystem;
private ComponentSystemManager componentSystemManager;
private Context context;

public StateHeadlessSetup() {
}

@Override
public void init(GameEngine gameEngine) {
context = gameEngine.createChildContext();
CoreRegistry.setContext(context);

// let's get the entity event system running
EntitySystemSetupUtil.addEntityManagementRelatedClasses(context);
entityManager = context.get(EngineEntityManager.class);

eventSystem = context.get(EventSystem.class);
context.put(Console.class, new ConsoleImpl(context));

NUIManager nuiManager = new NUIManagerInternal((TerasologyCanvasRenderer) context.get(CanvasRenderer.class), context);
context.put(NUIManager.class, nuiManager);
initEntityAndComponentManagers();
createLocalPlayer(context);

componentSystemManager = new ComponentSystemManager(context);
context.put(ComponentSystemManager.class, componentSystemManager);

componentSystemManager.register(new ConsoleSystem(), "engine:ConsoleSystem");
componentSystemManager.register(new CoreCommands(), "engine:CoreCommands");
componentSystemManager.register(context.get(InputSystem.class), "engine:InputSystem");

EntityRef localPlayerEntity = entityManager.create(new ClientComponent());
LocalPlayer localPlayer = new LocalPlayer();
localPlayer.setRecordAndReplayClasses(context.get(DirectionAndOriginPosRecorderList.class), context.get(RecordAndReplayCurrentStatus.class));
context.put(LocalPlayer.class, localPlayer);
localPlayer.setClientEntity(localPlayerEntity);

componentSystemManager.initialise();

GameManifest gameManifest;
Expand Down Expand Up @@ -147,15 +102,6 @@ public GameManifest createGameManifest() {
return gameManifest;
}

@Override
public void dispose(boolean shuttingDown) {
eventSystem.process();

componentSystemManager.shutdown();

entityManager.clear();
}

@Override
public void handleInput(float delta) {
}
Expand All @@ -178,9 +124,4 @@ public boolean isHibernationAllowed() {
public String getLoggingPhase() {
return LoggingContext.INIT_PHASE;
}

@Override
public Context getContext() {
return context;
}
}