From be0f9ded060c09c4aa29cec8bc811ded35dc14c1 Mon Sep 17 00:00:00 2001 From: Kevin Turner <83819+keturn@users.noreply.github.com> Date: Mon, 6 Jun 2022 08:41:15 -0700 Subject: [PATCH] chore: CoreRegistry removal from engine.network (#5033) Fixes some race conditions in MTE tests. * test(network): ClientHandler's GameEngine reference is optional? seems sus, but it's only used for a disconnection event, so maybe. * fix(network): don't replace this object every tick --- .../engine/HeadlessEnvironment.java | 6 ++- .../engine/context/internal/ContextImpl.java | 4 ++ .../engine/core/modes/StateLoading.java | 4 +- .../subsystem/common/NetworkSubsystem.java | 5 +- .../engine/network/ServerInfoService.java | 9 ++-- .../internal/ClientConnectionHandler.java | 41 ++++++++++------ .../network/internal/ClientHandler.java | 14 ++++-- .../internal/ClientHandshakeHandler.java | 13 ++--- .../engine/network/internal/LocalClient.java | 10 ++-- .../engine/network/internal/NetClient.java | 34 ++++++------- .../network/internal/NetworkSystemImpl.java | 49 ++++++++++++------- .../internal/ServerConnectionHandler.java | 26 ++++++---- .../internal/ServerHandshakeHandler.java | 7 ++- .../engine/network/internal/ServerImpl.java | 32 ++++++------ .../InfoRequestPipelineFactory.java | 21 ++++++-- .../TerasologyClientPipelineFactory.java | 24 +++++---- .../TerasologyServerPipelineFactory.java | 20 ++++---- .../nui/layers/mainMenu/JoinGameScreen.java | 9 +++- 18 files changed, 203 insertions(+), 125 deletions(-) diff --git a/engine-tests/src/main/java/org/terasology/engine/HeadlessEnvironment.java b/engine-tests/src/main/java/org/terasology/engine/HeadlessEnvironment.java index 75ebf4c7c81..52b5239827a 100644 --- a/engine-tests/src/main/java/org/terasology/engine/HeadlessEnvironment.java +++ b/engine-tests/src/main/java/org/terasology/engine/HeadlessEnvironment.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine; @@ -11,6 +11,7 @@ import org.terasology.engine.audio.nullAudio.NullSound; import org.terasology.engine.audio.nullAudio.NullStreamingSound; import org.terasology.engine.config.Config; +import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.context.Context; import org.terasology.engine.core.ComponentSystemManager; import org.terasology.engine.core.EngineTime; @@ -28,6 +29,7 @@ import org.terasology.engine.entitySystem.entity.internal.EngineEntityManager; import org.terasology.engine.entitySystem.prefab.Prefab; import org.terasology.engine.entitySystem.prefab.internal.PojoPrefab; +import org.terasology.engine.identity.storageServiceClient.StorageServiceWorker; import org.terasology.engine.logic.behavior.asset.BehaviorTree; import org.terasology.engine.network.NetworkSystem; import org.terasology.engine.network.internal.NetworkSystemImpl; @@ -256,6 +258,8 @@ protected void setupConfig() { Config config = new Config(context); config.loadDefaults(); context.put(Config.class, config); + context.put(StorageServiceWorker.class, mock(StorageServiceWorker.class)); + context.put(PlayerConfig.class, mock(PlayerConfig.class)); } @Override diff --git a/engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java b/engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java index b04bd4fd26f..3f55c335f0c 100644 --- a/engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java +++ b/engine/src/main/java/org/terasology/engine/context/internal/ContextImpl.java @@ -50,6 +50,10 @@ public void put(Class type, U object) { map.put(type, object); } + public boolean isDirectDescendantOf(Context other) { + return parent == other; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java b/engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java index ce1cb503469..8b3e4d35b6f 100644 --- a/engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java +++ b/engine/src/main/java/org/terasology/engine/core/modes/StateLoading.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.core.modes; @@ -52,6 +52,7 @@ import org.terasology.engine.game.GameManifest; import org.terasology.engine.network.JoinStatus; import org.terasology.engine.network.NetworkMode; +import org.terasology.engine.network.NetworkSystem; import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.rendering.nui.NUIManager; import org.terasology.engine.rendering.nui.internal.NUIManagerInternal; @@ -110,6 +111,7 @@ public void init(GameEngine engine) { headless = context.get(DisplayDevice.class).isHeadless(); CoreRegistry.setContext(context); + context.getValue(NetworkSystem.class).setContext(context); systemConfig = context.get(SystemConfig.class); if (!headless) { diff --git a/engine/src/main/java/org/terasology/engine/core/subsystem/common/NetworkSubsystem.java b/engine/src/main/java/org/terasology/engine/core/subsystem/common/NetworkSubsystem.java index 779693879c1..fb2c7f82fcf 100644 --- a/engine/src/main/java/org/terasology/engine/core/subsystem/common/NetworkSubsystem.java +++ b/engine/src/main/java/org/terasology/engine/core/subsystem/common/NetworkSubsystem.java @@ -1,8 +1,9 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.core.subsystem.common; import org.terasology.engine.context.Context; +import org.terasology.engine.core.EngineTime; import org.terasology.engine.core.GameEngine; import org.terasology.engine.core.Time; import org.terasology.engine.core.modes.GameState; @@ -23,7 +24,7 @@ public String getName() { @Override public void initialise(GameEngine engine, Context rootContext) { - networkSystem = new NetworkSystemImpl(rootContext.get(Time.class), rootContext); + networkSystem = new NetworkSystemImpl((EngineTime) rootContext.get(Time.class), rootContext); rootContext.put(NetworkSystem.class, networkSystem); } diff --git a/engine/src/main/java/org/terasology/engine/network/ServerInfoService.java b/engine/src/main/java/org/terasology/engine/network/ServerInfoService.java index 2bcd996244e..553bbd78521 100644 --- a/engine/src/main/java/org/terasology/engine/network/ServerInfoService.java +++ b/engine/src/main/java/org/terasology/engine/network/ServerInfoService.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network; @@ -11,12 +11,15 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; +import org.terasology.engine.context.Context; import org.terasology.engine.network.internal.ServerInfoRequestHandler; import org.terasology.engine.network.internal.pipelineFactory.InfoRequestPipelineFactory; import java.net.InetSocketAddress; import java.util.concurrent.Future; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + /** * Performs temporary connections to one or more game servers. */ @@ -25,12 +28,12 @@ public class ServerInfoService implements AutoCloseable { private final Bootstrap bootstrap; private final EventLoopGroup eventLoopGroup; - public ServerInfoService() { + public ServerInfoService(Context context) { eventLoopGroup = new NioEventLoopGroup(); bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup); bootstrap.channel(NioSocketChannel.class); - bootstrap.handler(new InfoRequestPipelineFactory()); + bootstrap.handler(createWithConstructorInjection(InfoRequestPipelineFactory.class, context)); bootstrap.option(ChannelOption.TCP_NODELAY, true); bootstrap.option(ChannelOption.SO_KEEPALIVE, true); bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000); diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java b/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java index 302abdc95d9..b5a45ccf07c 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ClientConnectionHandler.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -10,12 +10,12 @@ import org.slf4j.LoggerFactory; import org.terasology.engine.config.Config; import org.terasology.engine.config.PlayerConfig; +import org.terasology.engine.context.Context; +import org.terasology.engine.context.internal.ContextImpl; import org.terasology.engine.core.EngineTime; import org.terasology.engine.core.PathManager; -import org.terasology.engine.core.Time; import org.terasology.engine.core.module.ModuleManager; import org.terasology.engine.network.JoinStatus; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.gestalt.naming.Name; import org.terasology.gestalt.naming.Version; import org.terasology.protobuf.NetData; @@ -29,15 +29,20 @@ import java.util.Set; import java.util.Timer; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + public class ClientConnectionHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(ClientConnectionHandler.class); + private final Config config; + private final Context context; private final JoinStatusImpl joinStatus; - private NetworkSystemImpl networkSystem; - private ServerImpl server; - private ModuleManager moduleManager; + private final ModuleManager moduleManager; + private final PlayerConfig playerConfig; + private final EngineTime time; + private ServerImpl server; private Set missingModules = Sets.newHashSet(); private NetData.ModuleDataHeader receivingModule; private Path tempModuleLocation; @@ -50,15 +55,17 @@ public class ClientConnectionHandler extends ChannelInboundHandlerAdapter { /** * Initialises: network system, join status, and module manager. - * @param joinStatus - * @param networkSystem */ - public ClientConnectionHandler(JoinStatusImpl joinStatus, NetworkSystemImpl networkSystem) { - this.networkSystem = networkSystem; + public ClientConnectionHandler(JoinStatusImpl joinStatus, Config config, Context parentContext, ModuleManager moduleManager, + PlayerConfig playerConfig, EngineTime time) { + this.config = config; + this.context = new ContextImpl(parentContext); + this.moduleManager = moduleManager; + this.playerConfig = playerConfig; + this.time = time; this.joinStatus = joinStatus; // TODO: implement translation of errorMessage in messageReceived once context is available // See https://github.com/MovingBlocks/Terasology/pull/3332#discussion_r187081375 - this.moduleManager = CoreRegistry.get(ModuleManager.class); } /** @@ -85,6 +92,12 @@ public void run() { }, timeoutThreshold + 200); } + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + context.put(Channel.class, ctx.channel()); + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // If we timed out, don't handle anymore messages. @@ -257,8 +270,8 @@ private void completeJoin(ChannelHandlerContext channelHandlerContext, NetData.J */ private void receivedServerInfo(ChannelHandlerContext channelHandlerContext, NetData.ServerInfoMessage message) { logger.info("Received server info"); - ((EngineTime) CoreRegistry.get(Time.class)).setGameTime(message.getTime()); - this.server = new ServerImpl(networkSystem, channelHandlerContext.channel()); + time.setGameTime(message.getTime()); + this.server = createWithConstructorInjection(ServerImpl.class, context); server.setServerInfo(message); // Request missing modules @@ -287,8 +300,6 @@ private void receivedServerInfo(ChannelHandlerContext channelHandlerContext, Net * @param channelHandlerContext */ private void sendJoin(ChannelHandlerContext channelHandlerContext) { - Config config = CoreRegistry.get(Config.class); - PlayerConfig playerConfig = CoreRegistry.get(PlayerConfig.class); NetData.JoinMessage.Builder bldr = NetData.JoinMessage.newBuilder(); NetData.Color.Builder clrbldr = NetData.Color.newBuilder(); diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ClientHandler.java b/engine/src/main/java/org/terasology/engine/network/internal/ClientHandler.java index 7308873f5cb..ad2d7b9465c 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ClientHandler.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ClientHandler.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -10,7 +10,8 @@ import org.slf4j.LoggerFactory; import org.terasology.engine.core.GameEngine; import org.terasology.engine.core.modes.StateMainMenu; -import org.terasology.engine.registry.CoreRegistry; + +import java.util.Optional; import static org.terasology.protobuf.NetData.NetMessage; @@ -21,16 +22,19 @@ public class ClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(ClientHandler.class); - private NetworkSystemImpl networkSystem; + private final GameEngine gameEngine; + private final NetworkSystemImpl networkSystem; + private ServerImpl server; - public ClientHandler(NetworkSystemImpl networkSystem) { + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public ClientHandler(NetworkSystemImpl networkSystem, Optional gameEngine) { + this.gameEngine = gameEngine.orElse(null); this.networkSystem = networkSystem; } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - GameEngine gameEngine = CoreRegistry.get(GameEngine.class); if (gameEngine != null) { gameEngine.changeState(new StateMainMenu("Disconnected From Server")); } diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ClientHandshakeHandler.java b/engine/src/main/java/org/terasology/engine/network/internal/ClientHandshakeHandler.java index a8bb8497351..774c39e2b6e 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ClientHandshakeHandler.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ClientHandshakeHandler.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -17,7 +17,6 @@ import org.terasology.engine.identity.storageServiceClient.StorageServiceWorker; import org.terasology.engine.identity.storageServiceClient.StorageServiceWorkerStatus; import org.terasology.protobuf.NetData; -import org.terasology.engine.registry.CoreRegistry; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -37,8 +36,9 @@ public class ClientHandshakeHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(ClientHandshakeHandler.class); private static final String AUTHENTICATION_FAILURE = "Authentication failure"; - private Config config = CoreRegistry.get(Config.class); - private JoinStatusImpl joinStatus; + private final Config config; + private final StorageServiceWorker storageServiceWorker; + private final JoinStatusImpl joinStatus; private byte[] serverRandom; private byte[] clientRandom; @@ -50,7 +50,9 @@ public class ClientHandshakeHandler extends ChannelInboundHandlerAdapter { private ClientIdentity identity; private PublicIdentityCertificate serverCertificate; - public ClientHandshakeHandler(JoinStatusImpl joinStatus) { + public ClientHandshakeHandler(Config config, StorageServiceWorker storageServiceWorker, JoinStatusImpl joinStatus) { + this.config = config; + this.storageServiceWorker = storageServiceWorker; this.joinStatus = joinStatus; } @@ -151,7 +153,6 @@ private void processNewIdentity(NetData.ProvisionIdentity provisionIdentity, Cha config.save(); //Try to upload the new identity to the identity storage service (if user is logged in) - StorageServiceWorker storageServiceWorker = CoreRegistry.get(StorageServiceWorker.class); if (storageServiceWorker != null && storageServiceWorker.getStatus() == StorageServiceWorkerStatus.LOGGED_IN) { storageServiceWorker.putIdentity(serverCertificate, identity); } diff --git a/engine/src/main/java/org/terasology/engine/network/internal/LocalClient.java b/engine/src/main/java/org/terasology/engine/network/internal/LocalClient.java index 9510e17ac96..5dea016e619 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/LocalClient.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/LocalClient.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -10,7 +10,6 @@ import org.terasology.engine.logic.common.DisplayNameComponent; import org.terasology.engine.network.ClientComponent; import org.terasology.engine.network.ColorComponent; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.rendering.world.viewDistance.ViewDistance; import org.terasology.engine.world.chunks.Chunk; import org.terasology.gestalt.entitysystem.event.Event; @@ -22,15 +21,18 @@ */ public class LocalClient extends AbstractClient { - private Config config = CoreRegistry.get(Config.class); + private final Config config; /** * Creates an entity for the new local client. + * * @param preferredName Clients preferred name. * @param color Clients preferred color. * @param entityManager Entity manager for the clients entity creation. + * @param config */ - public LocalClient(String preferredName, Color color, EntityManager entityManager) { + public LocalClient(String preferredName, Color color, EntityManager entityManager, Config config) { + this.config = config; createEntity(preferredName, color, entityManager); } diff --git a/engine/src/main/java/org/terasology/engine/network/internal/NetClient.java b/engine/src/main/java/org/terasology/engine/network/internal/NetClient.java index 2de4bad37be..371abc01ebb 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/NetClient.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/NetClient.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -38,7 +38,6 @@ import org.terasology.engine.network.serialization.ServerComponentFieldCheck; import org.terasology.engine.persistence.serializers.EventSerializer; import org.terasology.engine.persistence.serializers.NetworkEntitySerializer; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.rendering.world.viewDistance.ViewDistance; import org.terasology.engine.world.WorldChangeListener; import org.terasology.engine.world.WorldProvider; @@ -71,9 +70,13 @@ public class NetClient extends AbstractClient implements WorldChangeListener { private static final Logger logger = LoggerFactory.getLogger(NetClient.class); private static final float NET_TICK_RATE = 0.05f; - private Time time; - private NetworkSystemImpl networkSystem; - private Channel channel; + private final Channel channel; + private final NetworkSystemImpl networkSystem; + private final PredictionSystem predictionSystem; + private final PublicIdentityCertificate identity; + private final Time time; + private final WorldProvider worldProvider; + private NetworkEntitySerializer entitySerializer; private EventSerializer eventSerializer; private EventLibrary eventLibrary; @@ -98,8 +101,6 @@ public class NetClient extends AbstractClient implements WorldChangeListener { private float chunkSendRate = 0.05469f; - private PublicIdentityCertificate identity; - // Outgoing messages private BlockingQueue queuedOutgoingBlockChanges = Queues.newLinkedBlockingQueue(); private BlockingQueue queuedOutgoingExtraDataChanges = Queues.newLinkedBlockingQueue(); @@ -122,19 +123,20 @@ public class NetClient extends AbstractClient implements WorldChangeListener { /** * Sets up a new net client with metrics, time, identity, and a world provider. - * @param channel - * @param networkSystem - * @param identity Publice certificate for the client. + * + * @param identity Public certificate for the client. */ - public NetClient(Channel channel, NetworkSystemImpl networkSystem, PublicIdentityCertificate identity) { + public NetClient(Channel channel, NetworkSystemImpl networkSystem, PredictionSystem predictionSystem, + PublicIdentityCertificate identity, Time time, WorldProvider worldProvider) { + this.predictionSystem = predictionSystem; + this.time = time; + this.worldProvider = worldProvider; this.channel = channel; metricSource = (NetMetricSource) channel.pipeline().get(MetricRecordingHandler.NAME); this.networkSystem = networkSystem; - this.time = CoreRegistry.get(Time.class); this.identity = identity; - WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class); - if (worldProvider != null) { - worldProvider.registerListener(this); + if (this.worldProvider != null) { + this.worldProvider.registerListener(this); } } @@ -196,7 +198,6 @@ public void disconnect() { channel.close().awaitUninterruptibly(); } - WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class); if (worldProvider != null) { worldProvider.unregisterListener(this); } @@ -504,7 +505,6 @@ private void sendInitialEntities(NetData.NetMessage.Builder message) { private void processEvents(NetData.NetMessage message) { boolean lagCompensated = false; - PredictionSystem predictionSystem = CoreRegistry.get(PredictionSystem.class); for (NetData.EventMessage eventMessage : message.getEventList()) { try { Event event = eventSerializer.deserialize(eventMessage.getEvent()); diff --git a/engine/src/main/java/org/terasology/engine/network/internal/NetworkSystemImpl.java b/engine/src/main/java/org/terasology/engine/network/internal/NetworkSystemImpl.java index 76e1fcf3d32..a0e72df4dfb 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/NetworkSystemImpl.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/NetworkSystemImpl.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -31,8 +31,9 @@ import org.terasology.engine.config.Config; import org.terasology.engine.config.NetworkConfig; import org.terasology.engine.context.Context; +import org.terasology.engine.context.internal.ContextImpl; import org.terasology.engine.core.ComponentSystemManager; -import org.terasology.engine.core.Time; +import org.terasology.engine.core.EngineTime; import org.terasology.engine.core.module.ModuleManager; import org.terasology.engine.core.module.StandardModuleExtension; import org.terasology.engine.core.subsystem.common.hibernation.HibernationManager; @@ -62,7 +63,6 @@ import org.terasology.engine.persistence.StorageManager; import org.terasology.engine.persistence.serializers.EventSerializer; import org.terasology.engine.persistence.serializers.NetworkEntitySerializer; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.world.BlockEntityRegistry; import org.terasology.engine.world.WorldProvider; import org.terasology.engine.world.block.BlockManager; @@ -88,6 +88,9 @@ import java.util.Set; import java.util.concurrent.BlockingQueue; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + + /** * Implementation of the Network System using Netty and TCP/IP */ @@ -99,7 +102,7 @@ public class NetworkSystemImpl implements EntityChangeSubscriber, NetworkSystem private final Set clientList = Sets.newLinkedHashSet(); private final Set netClientList = Sets.newLinkedHashSet(); // Shared - private Context context; + private ContextImpl context; private Optional hibernationSettings; private NetworkConfig config; private NetworkMode mode = NetworkMode.NONE; @@ -111,7 +114,7 @@ public class NetworkSystemImpl implements EntityChangeSubscriber, NetworkSystem private BlockManager blockManager; private OwnershipHelper ownershipHelper; private TIntLongMap netIdToEntityId = new TIntLongHashMap(); - private Time time; + private EngineTime time; private long nextNetworkTick; private boolean kicked; // Server only @@ -132,10 +135,11 @@ public class NetworkSystemImpl implements EntityChangeSubscriber, NetworkSystem private ServerImpl server; private EventLoopGroup clientGroup; - public NetworkSystemImpl(Time time, Context context) { + public NetworkSystemImpl(EngineTime time, Context context) { this.time = time; this.config = context.get(Config.class).getNetwork(); - this.hibernationSettings = Optional.ofNullable(context.get(HibernationManager.class)); + this.hibernationSettings = context.getMaybe(HibernationManager.class); + setContext(context); } @Override @@ -161,7 +165,7 @@ public void host(int port, boolean dedicatedServer) throws HostingFailedExceptio .localAddress(port) .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.SO_KEEPALIVE, true) - .childHandler(new TerasologyServerPipelineFactory(this)); + .childHandler(createWithConstructorInjection(TerasologyServerPipelineFactory.class, context)); // Start the server. serverChannelFuture = b.bind(); @@ -208,7 +212,7 @@ public JoinStatus join(String address, int port) throws InterruptedException { clientBootstrap.option(ChannelOption.SO_KEEPALIVE, true); clientBootstrap.option(ChannelOption.TCP_NODELAY, true); clientBootstrap.remoteAddress(new InetSocketAddress(address, port)); - clientBootstrap.handler(new TerasologyClientPipelineFactory(this)); + clientBootstrap.handler(createWithConstructorInjection(TerasologyClientPipelineFactory.class, context)); connectCheck = clientBootstrap.connect(); connectCheck.sync(); @@ -299,7 +303,7 @@ public void shutdown() { @Override public Client joinLocal(String preferredName, Color color) { - Client localClient = new LocalClient(preferredName, color, entityManager); + Client localClient = new LocalClient(preferredName, color, entityManager, context.getValue(Config.class)); clientList.add(localClient); clientPlayerLookup.put(localClient.getEntity(), localClient); connectClient(localClient); @@ -545,7 +549,7 @@ public void connectToEntitySystem(EngineEntityManager newEntityManager, EventLib } this.entityManager = newEntityManager; this.entityManager.subscribeForChanges(this); - this.blockManager = context.get(BlockManager.class); + this.blockManager = context.getValue(BlockManager.class); this.ownershipHelper = new OwnershipHelper(newEntityManager.getComponentLibrary()); this.storageManager = context.get(StorageManager.class); this.eventLibrary = newEventLibrary; @@ -568,7 +572,8 @@ public void connectToEntitySystem(EngineEntityManager newEntityManager, EventLib } if (server != null) { - server.connectToEntitySystem(newEntityManager, entitySerializer, eventSerializer, blockEntityRegistry); + server.connectToEntitySystem(newEntityManager, entitySerializer, eventSerializer, + blockEntityRegistry, context); } } @@ -771,13 +776,21 @@ public void forceDisconnect(Client client) { } /** - * Sets the context within which this system should operate - * - * @param context + * Sets the context within which this system should operate. + *

+ * As a client transitions from connecting to loading to in-game, it moves + * through different {@link org.terasology.engine.core.modes.GameState GameStates}, + * and the context changes along the way. */ @Override - public void setContext(Context context) { - this.context = context; + public void setContext(Context newContext) { + if (context != null && context.isDirectDescendantOf(newContext)) { + return; // Already using this context! + } + // Our internal context gets internal views of some objects. + context = new ContextImpl(newContext); + context.put(NetworkSystemImpl.class, this); + context.put(EngineTime.class, time); } void removeKickedClient(NetClient client) { @@ -885,7 +898,7 @@ private NetData.ServerInfoMessage getServerInfoMessage(String errorMessage) { if (worldGen != null) { serverInfoMessageBuilder.setReflectionHeight(worldGen.getWorld().getSeaLevel() + 0.5f); } - for (Module module : CoreRegistry.get(ModuleManager.class).getEnvironment()) { + for (Module module : context.get(ModuleManager.class).getEnvironment()) { if (!StandardModuleExtension.isServerSideOnly(module)) { serverInfoMessageBuilder.addModule(NetData.ModuleInfo.newBuilder() .setModuleId(module.getId().toString()) diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ServerConnectionHandler.java b/engine/src/main/java/org/terasology/engine/network/internal/ServerConnectionHandler.java index 24021b0b498..571a22c64ee 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ServerConnectionHandler.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ServerConnectionHandler.java @@ -1,15 +1,17 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; import com.google.protobuf.ByteString; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.terasology.engine.context.Context; +import org.terasology.engine.context.internal.ContextImpl; import org.terasology.engine.core.module.ModuleManager; import org.terasology.engine.identity.PublicIdentityCertificate; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.rendering.world.viewDistance.ViewDistance; import org.terasology.gestalt.module.Module; import org.terasology.gestalt.module.resources.ArchiveFileSource; @@ -22,27 +24,31 @@ import java.io.InputStream; import java.util.List; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + public class ServerConnectionHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(ServerConnectionHandler.class); - private NetworkSystemImpl networkSystem; + private final Context context; + private final ModuleManager moduleManager; + private final NetworkSystemImpl networkSystem; + private ServerHandler serverHandler; private ChannelHandlerContext channelHandlerContext; - private PublicIdentityCertificate identity; - - private ModuleManager moduleManager = CoreRegistry.get(ModuleManager.class); - - public ServerConnectionHandler(NetworkSystemImpl networkSystem) { + public ServerConnectionHandler(NetworkSystemImpl networkSystem, Context parentContext, ModuleManager moduleManager) { this.networkSystem = networkSystem; + this.context = new ContextImpl(parentContext); + this.moduleManager = moduleManager; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); this.channelHandlerContext = ctx; + context.put(Channel.class, ctx.channel()); serverHandler = ctx.pipeline().get(ServerHandler.class); } @@ -63,7 +69,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception } public void channelAuthenticated(PublicIdentityCertificate id) { - this.identity = id; + context.put(PublicIdentityCertificate.class, id); } private void sendModules(List moduleRequestList) { @@ -97,7 +103,7 @@ private void sendModules(List moduleRequestList) { private void receivedConnect(NetData.JoinMessage message) { logger.info("Received Start Join"); - NetClient client = new NetClient(channelHandlerContext.channel(), networkSystem, identity); + NetClient client = createWithConstructorInjection(NetClient.class, context); client.setPreferredName(message.getName()); client.setColor(new Color(message.getColor().getRgba())); client.setViewDistanceMode(ViewDistance.forIndex(message.getViewDistanceLevel())); diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ServerHandshakeHandler.java b/engine/src/main/java/org/terasology/engine/network/internal/ServerHandshakeHandler.java index 1e80f0ac655..4c695ea5b9d 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ServerHandshakeHandler.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ServerHandshakeHandler.java @@ -13,7 +13,6 @@ import org.terasology.engine.identity.CertificatePair; import org.terasology.engine.identity.IdentityConstants; import org.terasology.engine.identity.PublicIdentityCertificate; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.protobuf.NetData; import javax.crypto.BadPaddingException; @@ -33,11 +32,15 @@ public class ServerHandshakeHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = LoggerFactory.getLogger(ServerHandshakeHandler.class); - private Config config = CoreRegistry.get(Config.class); + private final Config config; private ServerConnectionHandler serverConnectionHandler; private byte[] serverRandom = new byte[IdentityConstants.SERVER_CLIENT_RANDOM_LENGTH]; private NetData.HandshakeHello serverHello; + public ServerHandshakeHandler(Config config) { + this.config = config; + } + @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); diff --git a/engine/src/main/java/org/terasology/engine/network/internal/ServerImpl.java b/engine/src/main/java/org/terasology/engine/network/internal/ServerImpl.java index 97eb9e606c2..ba4ec87bb59 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/ServerImpl.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/ServerImpl.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal; @@ -18,8 +18,8 @@ import org.joml.Vector3ic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.terasology.engine.context.Context; import org.terasology.engine.core.EngineTime; -import org.terasology.engine.core.Time; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.EngineEntityManager; import org.terasology.engine.network.NetMetricSource; @@ -29,7 +29,6 @@ import org.terasology.engine.network.serialization.ClientComponentFieldCheck; import org.terasology.engine.persistence.serializers.EventSerializer; import org.terasology.engine.persistence.serializers.NetworkEntitySerializer; -import org.terasology.engine.registry.CoreRegistry; import org.terasology.engine.world.BlockEntityRegistry; import org.terasology.engine.world.WorldProvider; import org.terasology.engine.world.block.Block; @@ -67,8 +66,11 @@ public class ServerImpl implements Server { private int clientEntityNetId; - private NetworkSystemImpl networkSystem; - private Channel channel; + private final Channel channel; + private Context context; + private final NetworkSystemImpl networkSystem; + private final EngineTime time; + private NetMetricSource metricsSource; private BlockingQueue queuedMessages = Queues.newLinkedBlockingQueue(); private List queuedOutgoingEvents = Lists.newArrayList(); @@ -88,24 +90,24 @@ public class ServerImpl implements Server { private ListMultimap awaitingChunkReadyBlockUpdates = ArrayListMultimap.create(); private ListMultimap awaitingChunkReadyExtraDataUpdates = ArrayListMultimap.create(); - private EngineTime time; - - public ServerImpl(NetworkSystemImpl system, Channel channel) { + public ServerImpl(NetworkSystemImpl system, EngineTime time, Channel channel, Context context) { this.channel = channel; metricsSource = (NetMetricSource) channel.pipeline().get(MetricRecordingHandler.NAME); this.networkSystem = system; - this.time = (EngineTime) CoreRegistry.get(Time.class); + this.time = time; + this.context = context; } void connectToEntitySystem(EngineEntityManager newEntityManager, NetworkEntitySerializer newEntitySerializer, - EventSerializer newEventSerializer, BlockEntityRegistry newBlockEntityRegistry) { + EventSerializer newEventSerializer, BlockEntityRegistry newBlockEntityRegistry, Context newContext) { this.entityManager = newEntityManager; this.eventSerializer = newEventSerializer; this.entitySerializer = newEntitySerializer; this.blockEntityRegistry = newBlockEntityRegistry; - blockManager = (BlockManagerImpl) CoreRegistry.get(BlockManager.class); - extraDataManager = CoreRegistry.get(ExtraBlockDataManager.class); + this.context = newContext; + blockManager = (BlockManagerImpl) newContext.getValue(BlockManager.class); + extraDataManager = newContext.getValue(ExtraBlockDataManager.class); } void setServerInfo(NetData.ServerInfoMessage serverInfo) { @@ -284,11 +286,11 @@ private void processRemoveEntities(NetData.NetMessage message) { * Apply the block changes from the message to the local world. */ private void processBlockChanges(NetData.NetMessage message) { + WorldProvider worldProvider = context.get(WorldProvider.class); for (NetData.BlockChangeMessage blockChange : message.getBlockChangeList()) { Block newBlock = blockManager.getBlock((short) blockChange.getNewBlock()); logger.debug("Received block change to {}", newBlock); // TODO: Store changes to blocks that aren't ready to be modified (the surrounding chunks aren't available) - WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class); Vector3i pos = NetMessageUtil.convert(blockChange.getPos()); if (worldProvider.isBlockRelevant(pos)) { worldProvider.setBlock(pos, newBlock); @@ -302,8 +304,8 @@ private void processBlockChanges(NetData.NetMessage message) { * Apply the extra-data changes from the message to the local world. */ private void processExtraDataChanges(NetData.NetMessage message) { + WorldProvider worldProvider = context.get(WorldProvider.class); for (NetData.ExtraDataChangeMessage extraDataChange : message.getExtraDataChangeList()) { - WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class); Vector3i pos = NetMessageUtil.convert(extraDataChange.getPos()); if (worldProvider.isBlockRelevant(pos)) { worldProvider.setExtraData(extraDataChange.getIndex(), pos, extraDataChange.getNewData()); @@ -399,7 +401,7 @@ public NetMetricSource getMetrics() { @Override public void onChunkReady(Vector3ic chunkPos) { - WorldProvider worldProvider = CoreRegistry.get(WorldProvider.class); + WorldProvider worldProvider = context.get(WorldProvider.class); List updateBlockMessages = awaitingChunkReadyBlockUpdates.removeAll(new Vector3i(chunkPos)); for (NetData.BlockChangeMessage message : updateBlockMessages) { diff --git a/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/InfoRequestPipelineFactory.java b/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/InfoRequestPipelineFactory.java index 93079fbf095..92e47f76ef8 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/InfoRequestPipelineFactory.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/InfoRequestPipelineFactory.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal.pipelineFactory; @@ -12,21 +12,32 @@ import io.netty.handler.codec.compression.Lz4FrameEncoder; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; +import org.terasology.engine.context.Context; +import org.terasology.engine.context.internal.ContextImpl; import org.terasology.engine.network.internal.ClientHandshakeHandler; import org.terasology.engine.network.internal.JoinStatusImpl; import org.terasology.engine.network.internal.MetricRecordingHandler; import org.terasology.engine.network.internal.ServerInfoRequestHandler; import org.terasology.protobuf.NetData; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + /** * A pipeline that requests {@link org.terasology.engine.network.ServerInfoMessage} before it auto-disconnects. This is similar * to {@link TerasologyClientPipelineFactory}. */ -public class InfoRequestPipelineFactory extends ChannelInitializer { +public class InfoRequestPipelineFactory extends ChannelInitializer { + + private final Context parentContext; + + public InfoRequestPipelineFactory(Context parentContext) { + this.parentContext = parentContext; + } @Override - protected void initChannel(Channel ch) throws Exception { - JoinStatusImpl joinStatus = new JoinStatusImpl(); + protected void initChannel(Channel ch) { + var context = new ContextImpl(parentContext); + context.put(JoinStatusImpl.class, new JoinStatusImpl()); ChannelPipeline p = ch.pipeline(); p.addLast(MetricRecordingHandler.NAME, new MetricRecordingHandler()); @@ -38,7 +49,7 @@ protected void initChannel(Channel ch) throws Exception { p.addLast("frameLengthEncoder", new LengthFieldPrepender(3)); p.addLast("protobufEncoder", new ProtobufEncoder()); - p.addLast("authenticationHandler", new ClientHandshakeHandler(joinStatus)); + p.addLast("authenticationHandler", createWithConstructorInjection(ClientHandshakeHandler.class, context)); p.addLast("connectionHandler", new ServerInfoRequestHandler()); } } diff --git a/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyClientPipelineFactory.java b/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyClientPipelineFactory.java index dd0a3edede1..7aca4184dc4 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyClientPipelineFactory.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyClientPipelineFactory.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal.pipelineFactory; @@ -12,29 +12,33 @@ import io.netty.handler.codec.compression.Lz4FrameEncoder; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; +import org.terasology.engine.context.Context; +import org.terasology.engine.context.internal.ContextImpl; import org.terasology.engine.network.internal.ClientConnectionHandler; import org.terasology.engine.network.internal.ClientHandler; import org.terasology.engine.network.internal.ClientHandshakeHandler; import org.terasology.engine.network.internal.JoinStatusImpl; import org.terasology.engine.network.internal.MetricRecordingHandler; -import org.terasology.engine.network.internal.NetworkSystemImpl; import org.terasology.protobuf.NetData; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + /** * Netty pipeline for Clients */ -public class TerasologyClientPipelineFactory extends ChannelInitializer { +public class TerasologyClientPipelineFactory extends ChannelInitializer { - private NetworkSystemImpl networkSystem; + private final Context parentContext; - public TerasologyClientPipelineFactory(NetworkSystemImpl networkSystem) { - this.networkSystem = networkSystem; + public TerasologyClientPipelineFactory(Context parentContext) { + this.parentContext = parentContext; } @Override protected void initChannel(Channel ch) throws Exception { - JoinStatusImpl joinStatus = new JoinStatusImpl(); + var context = new ContextImpl(parentContext); + context.put(JoinStatusImpl.class, new JoinStatusImpl()); ChannelPipeline p = ch.pipeline(); p.addLast(MetricRecordingHandler.NAME, new MetricRecordingHandler()); @@ -46,8 +50,8 @@ protected void initChannel(Channel ch) throws Exception { p.addLast("frameLengthEncoder", new LengthFieldPrepender(3)); p.addLast("protobufEncoder", new ProtobufEncoder()); - p.addLast("authenticationHandler", new ClientHandshakeHandler(joinStatus)); - p.addLast("connectionHandler", new ClientConnectionHandler(joinStatus, networkSystem)); - p.addLast("handler", new ClientHandler(networkSystem)); + p.addLast("authenticationHandler", createWithConstructorInjection(ClientHandshakeHandler.class, context)); + p.addLast("connectionHandler", createWithConstructorInjection(ClientConnectionHandler.class, context)); + p.addLast("handler", createWithConstructorInjection(ClientHandler.class, context)); } } diff --git a/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyServerPipelineFactory.java b/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyServerPipelineFactory.java index 1aef69f9a7b..69e1066c76b 100644 --- a/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyServerPipelineFactory.java +++ b/engine/src/main/java/org/terasology/engine/network/internal/pipelineFactory/TerasologyServerPipelineFactory.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.network.internal.pipelineFactory; @@ -12,22 +12,24 @@ import io.netty.handler.codec.compression.Lz4FrameEncoder; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; +import org.terasology.engine.context.Context; import org.terasology.engine.network.internal.MetricRecordingHandler; -import org.terasology.engine.network.internal.NetworkSystemImpl; import org.terasology.engine.network.internal.ServerConnectionHandler; import org.terasology.engine.network.internal.ServerHandler; import org.terasology.engine.network.internal.ServerHandshakeHandler; import org.terasology.protobuf.NetData; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + /** * Netty Pipeline for the server */ -public class TerasologyServerPipelineFactory extends ChannelInitializer { +public class TerasologyServerPipelineFactory extends ChannelInitializer { - private NetworkSystemImpl networkSystem; + private final Context context; - public TerasologyServerPipelineFactory(NetworkSystemImpl networkSystem) { - this.networkSystem = networkSystem; + public TerasologyServerPipelineFactory(Context context) { + this.context = context; } @Override @@ -43,8 +45,8 @@ protected void initChannel(Channel ch) throws Exception { p.addLast("frameLengthEncoder", new LengthFieldPrepender(3)); p.addLast("protobufEncoder", new ProtobufEncoder()); - p.addLast("authenticationHandler", new ServerHandshakeHandler()); - p.addLast("connectionHandler", new ServerConnectionHandler(networkSystem)); - p.addLast("handler", new ServerHandler(networkSystem)); + p.addLast("authenticationHandler", createWithConstructorInjection(ServerHandshakeHandler.class, context)); + p.addLast("connectionHandler", createWithConstructorInjection(ServerConnectionHandler.class, context)); + p.addLast("handler", createWithConstructorInjection(ServerHandler.class, context)); } } diff --git a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java index 0a23106e6ff..e6cf83ad02a 100644 --- a/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java +++ b/engine/src/main/java/org/terasology/engine/rendering/nui/layers/mainMenu/JoinGameScreen.java @@ -1,4 +1,4 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2022 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 package org.terasology.engine.rendering.nui.layers.mainMenu; @@ -10,6 +10,7 @@ import org.terasology.engine.config.Config; import org.terasology.engine.config.PlayerConfig; import org.terasology.engine.config.ServerInfo; +import org.terasology.engine.context.Context; import org.terasology.engine.core.GameEngine; import org.terasology.engine.core.GameThread; import org.terasology.engine.core.modes.StateLoading; @@ -54,11 +55,15 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import static org.terasology.engine.registry.InjectionHelper.createWithConstructorInjection; + public class JoinGameScreen extends CoreScreenLayer { public static final ResourceUrn ASSET_URI = new ResourceUrn("engine:joinGameScreen"); private static final Logger logger = LoggerFactory.getLogger(JoinGameScreen.class); + @In + private Context context; @In private Config config; @In @@ -147,7 +152,7 @@ public void initialise() { public void onOpened() { super.onOpened(); - infoService = new ServerInfoService(); + infoService = createWithConstructorInjection(ServerInfoService.class, context); if (playerConfig.playerName.getDefaultValue().equals(playerConfig.playerName.get())) { getManager().pushScreen(EnterUsernamePopup.ASSET_URI, EnterUsernamePopup.class);