Skip to content

Commit 444d60f

Browse files
committed
Merge pull request #1082 from msteiger/join_server
Add message box dialogs for long-running operations
2 parents 4f204c8 + 0bc6f8e commit 444d60f

File tree

13 files changed

+309
-45
lines changed

13 files changed

+309
-45
lines changed

engine/src/main/java/org/terasology/engine/modes/StateMainMenu.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import org.terasology.network.NetworkSystem;
4040
import org.terasology.rendering.nui.NUIManager;
4141
import org.terasology.rendering.nui.internal.NUIManagerInternal;
42-
import org.terasology.rendering.nui.layers.mainMenu.ErrorMessagePopup;
42+
import org.terasology.rendering.nui.layers.mainMenu.MessagePopup;
4343

4444
/**
4545
* The class implements the main game menu.
@@ -105,7 +105,7 @@ public void init(GameEngine gameEngine) {
105105
//guiManager.openWindow("main");
106106
CoreRegistry.get(NUIManager.class).pushScreen("engine:mainMenuScreen");
107107
if (!messageOnLoad.isEmpty()) {
108-
nuiManager.pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class).setError("Error", messageOnLoad);
108+
nuiManager.pushScreen(MessagePopup.ASSET_URI, MessagePopup.class).setMessage("Error", messageOnLoad);
109109
}
110110
}
111111

engine/src/main/java/org/terasology/engine/modes/loadProcesses/StartServer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.terasology.network.exceptions.HostingFailedException;
2222
import org.terasology.registry.CoreRegistry;
2323
import org.terasology.rendering.nui.NUIManager;
24-
import org.terasology.rendering.nui.layers.mainMenu.ErrorMessagePopup;
24+
import org.terasology.rendering.nui.layers.mainMenu.MessagePopup;
2525

2626
/**
2727
* @author Immortius
@@ -47,7 +47,7 @@ public boolean step() {
4747
try {
4848
CoreRegistry.get(NetworkSystem.class).host(TerasologyConstants.DEFAULT_PORT, dedicated);
4949
} catch (HostingFailedException e) {
50-
CoreRegistry.get(NUIManager.class).pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class).setError("Failed to Host",
50+
CoreRegistry.get(NUIManager.class).pushScreen(MessagePopup.ASSET_URI, MessagePopup.class).setMessage("Failed to Host",
5151
e.getMessage() + " - Reverting to single player");
5252
}
5353
return true;

engine/src/main/java/org/terasology/network/NetworkSystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public interface NetworkSystem extends BlockRegistrationListener {
3535

3636
void host(int port, boolean dedicatedServer) throws HostingFailedException;
3737

38-
JoinStatus join(String address, int port);
38+
JoinStatus join(String address, int port) throws InterruptedException;
3939

4040
void shutdown();
4141

engine/src/main/java/org/terasology/network/internal/NetworkSystemImpl.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,22 @@ public void host(int port, boolean dedicatedServer) throws HostingFailedExceptio
175175
}
176176

177177
@Override
178-
public JoinStatus join(String address, int port) {
178+
public JoinStatus join(String address, int port) throws InterruptedException {
179179
if (mode == NetworkMode.NONE) {
180180
factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
181181
ClientBootstrap bootstrap = new ClientBootstrap(factory);
182182
bootstrap.setPipelineFactory(new TerasologyClientPipelineFactory(this));
183183
bootstrap.setOption("tcpNoDelay", true);
184184
bootstrap.setOption("keepAlive", true);
185185
ChannelFuture connectCheck = bootstrap.connect(new InetSocketAddress(address, port));
186-
connectCheck.awaitUninterruptibly();
186+
try {
187+
connectCheck.await();
188+
} catch (InterruptedException e) {
189+
connectCheck.cancel();
190+
connectCheck.getChannel().getCloseFuture().awaitUninterruptibly();
191+
factory.releaseExternalResources();
192+
throw e;
193+
}
187194
if (!connectCheck.isSuccess()) {
188195
logger.warn("Failed to connect to server", connectCheck.getCause());
189196
connectCheck.getChannel().getCloseFuture().awaitUninterruptibly();

engine/src/main/java/org/terasology/rendering/nui/internal/NUIManagerInternal.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import org.terasology.registry.InjectionHelper;
4646
import org.terasology.rendering.nui.ControlWidget;
4747
import org.terasology.rendering.nui.CoreScreenLayer;
48-
import org.terasology.rendering.nui.FocusManager;
4948
import org.terasology.rendering.nui.NUIManager;
5049
import org.terasology.rendering.nui.UIScreenLayer;
5150
import org.terasology.rendering.nui.UIWidget;
@@ -58,7 +57,7 @@
5857
/**
5958
* @author Immortius
6059
*/
61-
public class NUIManagerInternal extends BaseComponentSystem implements NUIManager, FocusManager {
60+
public class NUIManagerInternal extends BaseComponentSystem implements NUIManager {
6261

6362
private Deque<UIScreenLayer> screens = Queues.newArrayDeque();
6463
private HUDScreenLayer hudScreenLayer = new HUDScreenLayer();
@@ -196,8 +195,10 @@ public UIScreenLayer pushScreen(String screenUri) {
196195
public UIScreenLayer pushScreen(UIElement element) {
197196
if (element != null && element.getRootWidget() instanceof CoreScreenLayer) {
198197
CoreScreenLayer result = (CoreScreenLayer) element.getRootWidget();
199-
result.setId(element.getURI().toNormalisedSimpleString());
200-
pushScreen(result, element.getURI());
198+
if (!screens.contains(result)) {
199+
result.setId(element.getURI().toNormalisedSimpleString());
200+
pushScreen(result, element.getURI());
201+
}
201202
return result;
202203
}
203204
return null;
@@ -230,7 +231,7 @@ public <T extends CoreScreenLayer> T pushScreen(UIElement element, Class<T> expe
230231
return null;
231232
}
232233

233-
public void pushScreen(CoreScreenLayer screen, AssetUri uri) {
234+
private void pushScreen(CoreScreenLayer screen, AssetUri uri) {
234235
screen.setManager(this);
235236
prepare(screen);
236237
screens.push(screen);
@@ -312,6 +313,7 @@ public void clear() {
312313
forceReleaseMouse = false;
313314
}
314315

316+
@Override
315317
public void render() {
316318
canvas.preRender();
317319
Deque<UIScreenLayer> screensToRender = Queues.newArrayDeque();
@@ -330,6 +332,7 @@ public void render() {
330332
canvas.postRender();
331333
}
332334

335+
@Override
333336
public void update(float delta) {
334337
canvas.processMousePosition(Mouse.getPosition());
335338

engine/src/main/java/org/terasology/rendering/nui/layers/mainMenu/CreateGameScreen.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.terasology.rendering.nui.layers.mainMenu;
1717

1818
import com.google.common.collect.Lists;
19+
1920
import org.slf4j.Logger;
2021
import org.slf4j.LoggerFactory;
2122
import org.terasology.config.Config;
@@ -154,9 +155,9 @@ public void onActivated(UIWidget button) {
154155
@Override
155156
public void onActivated(UIWidget button) {
156157
if (worldGenerator.getSelection() == null) {
157-
ErrorMessagePopup errorMessagePopup = getManager().pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class);
158+
MessagePopup errorMessagePopup = getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class);
158159
if (errorMessagePopup != null) {
159-
errorMessagePopup.setError("No World Generator Selected", "Select a world generator (you may need to activate a mod with a generator first).");
160+
errorMessagePopup.setMessage("No World Generator Selected", "Select a world generator (you may need to activate a mod with a generator first).");
160161
}
161162
} else {
162163
GameManifest gameManifest = new GameManifest();

engine/src/main/java/org/terasology/rendering/nui/layers/mainMenu/JoinGameScreen.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515
*/
1616
package org.terasology.rendering.nui.layers.mainMenu;
1717

18+
import java.util.concurrent.Callable;
19+
1820
import org.terasology.config.Config;
1921
import org.terasology.config.ServerInfo;
2022
import org.terasology.engine.GameEngine;
2123
import org.terasology.engine.TerasologyConstants;
2224
import org.terasology.engine.modes.StateLoading;
23-
import org.terasology.registry.In;
2425
import org.terasology.network.JoinStatus;
2526
import org.terasology.network.NetworkSystem;
27+
import org.terasology.registry.In;
2628
import org.terasology.rendering.nui.CoreScreenLayer;
2729
import org.terasology.rendering.nui.UIWidget;
2830
import org.terasology.rendering.nui.WidgetUtil;
@@ -34,6 +36,8 @@
3436
import org.terasology.rendering.nui.widgets.UILabel;
3537
import org.terasology.rendering.nui.widgets.UIList;
3638

39+
import com.google.common.base.Function;
40+
3741
/**
3842
* @author Immortius
3943
*/
@@ -49,7 +53,7 @@ public class JoinGameScreen extends CoreScreenLayer {
4953
private GameEngine engine;
5054

5155
private UIList<ServerInfo> serverList;
52-
56+
5357
@Override
5458
public void initialise() {
5559
serverList = find("serverList", UIList.class);
@@ -122,13 +126,32 @@ public boolean isLowerLayerVisible() {
122126
return false;
123127
}
124128

125-
private void join(String address) {
126-
JoinStatus joinStatus = networkSystem.join(address, TerasologyConstants.DEFAULT_PORT);
127-
if (joinStatus.getStatus() != JoinStatus.Status.FAILED) {
128-
engine.changeState(new StateLoading(joinStatus));
129-
} else {
130-
getManager().pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class)
131-
.setError("Failed to Join", "Could not connect to server - " + joinStatus.getErrorMessage());
132-
}
129+
private void join(final String address) {
130+
Callable<JoinStatus> operation = new Callable<JoinStatus>() {
131+
132+
@Override
133+
public JoinStatus call() throws InterruptedException {
134+
JoinStatus joinStatus = networkSystem.join(address, TerasologyConstants.DEFAULT_PORT);
135+
return joinStatus;
136+
}
137+
};
138+
139+
final WaitPopup<JoinStatus> popup = getManager().pushScreen(WaitPopup.ASSET_URI, WaitPopup.class);
140+
popup.setMessage("Join Game", "Connecting to '" + address + "' - please wait ...");
141+
popup.onSuccess(new Function<JoinStatus, Void>() {
142+
143+
@Override
144+
public Void apply(JoinStatus result) {
145+
if (result.getStatus() != JoinStatus.Status.FAILED) {
146+
engine.changeState(new StateLoading(result));
147+
} else {
148+
MessagePopup screen = getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class);
149+
screen.setMessage("Failed to Join", "Could not connect to server - " + result.getErrorMessage());
150+
}
151+
return null;
152+
}
153+
});
154+
popup.startOperation(operation);
155+
133156
}
134157
}

engine/src/main/java/org/terasology/rendering/nui/layers/mainMenu/JoinServerPopup.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.terasology.rendering.nui.layers.mainMenu;
1717

18+
import java.util.concurrent.Callable;
19+
1820
import org.terasology.config.Config;
1921
import org.terasology.engine.GameEngine;
2022
import org.terasology.engine.TerasologyConstants;
@@ -28,6 +30,8 @@
2830
import org.terasology.rendering.nui.widgets.ActivateEventListener;
2931
import org.terasology.rendering.nui.widgets.UIText;
3032

33+
import com.google.common.base.Function;
34+
3135
/**
3236
* @author Immortius
3337
*/
@@ -41,21 +45,14 @@ public class JoinServerPopup extends CoreScreenLayer {
4145

4246
@In
4347
private GameEngine engine;
44-
48+
4549
@Override
4650
public void initialise() {
4751
WidgetUtil.trySubscribe(this, "join", new ActivateEventListener() {
4852
@Override
4953
public void onActivated(UIWidget button) {
50-
getManager().popScreen();
5154
UIText address = find("address", UIText.class);
52-
JoinStatus status = networkSystem.join(address.getText(), TerasologyConstants.DEFAULT_PORT);
53-
if (status.getStatus() != JoinStatus.Status.FAILED) {
54-
engine.changeState(new StateLoading(status));
55-
} else {
56-
getManager().pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class)
57-
.setError("Failed to Join", "Could not connect to server - " + status.getErrorMessage());
58-
}
55+
join(address.getText());
5956
}
6057
});
6158

@@ -66,6 +63,35 @@ public void onActivated(UIWidget button) {
6663
}
6764
});
6865
}
66+
67+
private void join(final String address) {
68+
Callable<JoinStatus> operation = new Callable<JoinStatus>() {
69+
70+
@Override
71+
public JoinStatus call() throws InterruptedException {
72+
JoinStatus joinStatus = networkSystem.join(address, TerasologyConstants.DEFAULT_PORT);
73+
return joinStatus;
74+
}
75+
};
76+
77+
final WaitPopup<JoinStatus> popup = getManager().pushScreen(WaitPopup.ASSET_URI, WaitPopup.class);
78+
popup.setMessage("Join Game", "Connecting to '" + address + "' - please wait ...");
79+
popup.onSuccess(new Function<JoinStatus, Void>() {
80+
81+
@Override
82+
public Void apply(JoinStatus result) {
83+
if (result.getStatus() != JoinStatus.Status.FAILED) {
84+
engine.changeState(new StateLoading(result));
85+
} else {
86+
MessagePopup screen = getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class);
87+
screen.setMessage("Failed to Join", "Could not connect to server - " + result.getErrorMessage());
88+
}
89+
90+
return null;
91+
}
92+
});
93+
popup.startOperation(operation);
94+
}
6995

7096
@Override
7197
public void onOpened() {
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package org.terasology.rendering.nui.layers.mainMenu;
1717

18+
import org.terasology.asset.AssetType;
19+
import org.terasology.asset.AssetUri;
20+
import org.terasology.asset.Assets;
1821
import org.terasology.rendering.nui.CoreScreenLayer;
1922
import org.terasology.rendering.nui.UIWidget;
2023
import org.terasology.rendering.nui.WidgetUtil;
@@ -24,19 +27,23 @@
2427
/**
2528
* @author Immortius
2629
*/
27-
public class ErrorMessagePopup extends CoreScreenLayer {
30+
public class MessagePopup extends CoreScreenLayer {
31+
32+
public static final AssetUri ASSET_URI = new AssetUri(AssetType.UI_ELEMENT, "engine:messagePopup");
33+
34+
public final ActivateEventListener defaultCloseAction = new ActivateEventListener() {
35+
@Override
36+
public void onActivated(UIWidget button) {
37+
getManager().popScreen();
38+
}
39+
};
2840

2941
@Override
3042
public void initialise() {
31-
WidgetUtil.trySubscribe(this, "ok", new ActivateEventListener() {
32-
@Override
33-
public void onActivated(UIWidget button) {
34-
getManager().popScreen();
35-
}
36-
});
43+
WidgetUtil.trySubscribe(this, "ok", defaultCloseAction);
3744
}
3845

39-
public void setError(String title, String message) {
46+
public void setMessage(String title, String message) {
4047
UILabel titleLabel = find("title", UILabel.class);
4148
if (titleLabel != null) {
4249
titleLabel.setText(title);
@@ -47,4 +54,12 @@ public void setError(String title, String message) {
4754
messageLabel.setText(message);
4855
}
4956
}
57+
58+
@Override
59+
public void onClosed() {
60+
super.onClosed();
61+
62+
// don't save this asset in the cache -> don't persist changes to this class
63+
Assets.dispose(Assets.get(MessagePopup.ASSET_URI));
64+
}
5065
}

engine/src/main/java/org/terasology/rendering/nui/layers/mainMenu/SelectGameScreen.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public void onActivated(UIWidget button) {
9191
gameList.setSelection(null);
9292
} catch (Exception e) {
9393
logger.error("Failed to delete saved game", e);
94-
getManager().pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class).setError("Error Deleting Game", e.getMessage());
94+
getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class).setMessage("Error Deleting Game", e.getMessage());
9595
}
9696
}
9797
}
@@ -119,7 +119,7 @@ private void loadGame(GameInfo item) {
119119
CoreRegistry.get(GameEngine.class).changeState(new StateLoading(manifest, (loadingAsServer) ? NetworkMode.DEDICATED_SERVER : NetworkMode.NONE));
120120
} catch (Exception e) {
121121
logger.error("Failed to load saved game", e);
122-
getManager().pushScreen("engine:errorMessagePopup", ErrorMessagePopup.class).setError("Error Loading Game", e.getMessage());
122+
getManager().pushScreen(MessagePopup.ASSET_URI, MessagePopup.class).setMessage("Error Loading Game", e.getMessage());
123123
}
124124
}
125125

0 commit comments

Comments
 (0)