Skip to content

Commit 78203f9

Browse files
author
Julien Poulton
committed
Merge branch 'fix-error-catch' into 'master'
[SDK][ERROS] Game crash cleanly - SDK side. See merge request codingame/game-engine!136
2 parents 20117a9 + 14cbf4b commit 78203f9

File tree

9 files changed

+126
-79
lines changed

9 files changed

+126
-79
lines changed

engine/core/src/main/java/com/codingame/gameengine/core/GameManager.java

Lines changed: 66 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.io.InputStream;
44
import java.io.PrintStream;
5+
import java.io.PrintWriter;
6+
import java.io.StringWriter;
57
import java.util.ArrayList;
68
import java.util.HashMap;
79
import java.util.List;
@@ -74,7 +76,7 @@ abstract public class GameManager<T extends AbstractPlayer> {
7476
private int totalTurnTime = 0;
7577

7678
private boolean viewWarning, summaryWarning;
77-
79+
7880
/**
7981
* GameManager main loop.
8082
*
@@ -85,65 +87,72 @@ abstract public class GameManager<T extends AbstractPlayer> {
8587
*/
8688
void start(InputStream is, PrintStream out) {
8789
s = new Scanner(is);
88-
this.out = out;
89-
this.referee = refereeProvider.get();
90-
91-
// Init ---------------------------------------------------------------
92-
log.info("Init");
93-
InputCommand iCmd = InputCommand.parse(s.nextLine());
94-
int playerCount = s.nextInt();
95-
s.nextLine();
96-
players = new ArrayList<T>(playerCount);
97-
98-
for (int i = 0; i < playerCount; i++) {
99-
T player = playerProvider.get();
100-
player.setIndex(i);
101-
players.add(player);
102-
}
90+
try {
91+
this.out = out;
92+
this.referee = refereeProvider.get();
93+
94+
// Init ---------------------------------------------------------------
95+
log.info("Init");
96+
InputCommand iCmd = InputCommand.parse(s.nextLine());
97+
int playerCount = s.nextInt();
98+
s.nextLine();
99+
players = new ArrayList<T>(playerCount);
100+
101+
for (int i = 0; i < playerCount; i++) {
102+
T player = playerProvider.get();
103+
player.setIndex(i);
104+
players.add(player);
105+
}
103106

104-
readGameProperties(iCmd, s);
107+
readGameProperties(iCmd, s);
105108

106-
prevViewData = null;
107-
currentViewData = new JsonObject();
109+
prevViewData = null;
110+
currentViewData = new JsonObject();
108111

109-
referee.init();
110-
registeredModules.forEach(Module::onGameInit);
111-
initDone = true;
112+
referee.init();
113+
registeredModules.forEach(Module::onGameInit);
114+
initDone = true;
112115

113-
// Game Loop ----------------------------------------------------------
114-
for (turn = 0; turn < getMaxTurns() && !isGameEnd() && !allPlayersInactive(); turn++) {
115-
swapInfoAndViewData();
116-
log.info("Turn " + turn);
117-
newTurn = true;
118-
outputsRead = false; // Set as true after first getOutputs() to forbib sendInputs
116+
// Game Loop ----------------------------------------------------------
117+
for (turn = 0; turn < getMaxTurns() && !isGameEnd() && !allPlayersInactive(); turn++) {
118+
swapInfoAndViewData();
119+
log.info("Turn " + turn);
120+
newTurn = true;
121+
outputsRead = false; // Set as true after first getOutputs() to forbib sendInputs
119122

120-
referee.gameTurn(turn);
121-
registeredModules.forEach(Module::onAfterGameTurn);
123+
referee.gameTurn(turn);
124+
registeredModules.forEach(Module::onAfterGameTurn);
122125

123-
// reset players' outputs
124-
for (AbstractPlayer player : players) {
125-
player.resetOutputs();
126-
player.setHasBeenExecuted(false);
126+
// reset players' outputs
127+
for (AbstractPlayer player : players) {
128+
player.resetOutputs();
129+
player.setHasBeenExecuted(false);
130+
}
127131
}
128-
}
129132

130-
log.info("End");
133+
log.info("End");
131134

132-
referee.onEnd();
133-
registeredModules.forEach(Module::onAfterOnEnd);
135+
referee.onEnd();
136+
registeredModules.forEach(Module::onAfterOnEnd);
134137

135-
// Send last frame ----------------------------------------------------
136-
swapInfoAndViewData();
137-
newTurn = true;
138+
// Send last frame ----------------------------------------------------
139+
swapInfoAndViewData();
140+
newTurn = true;
138141

139-
dumpView();
140-
dumpInfos();
142+
dumpView();
143+
dumpInfos();
141144

142-
dumpGameProperties();
143-
dumpMetadata();
144-
dumpScores();
145+
dumpGameProperties();
146+
dumpMetadata();
147+
dumpScores();
145148

146-
s.close();
149+
s.close();
150+
151+
} catch (RuntimeException e) {
152+
dumpFail(e);
153+
s.close();
154+
throw e;
155+
}
147156
}
148157

149158
abstract protected boolean allPlayersInactive();
@@ -231,6 +240,16 @@ private void dumpScores() {
231240
out.println(data);
232241
}
233242

243+
private void dumpFail(RuntimeException e) {
244+
OutputData data = new OutputData(OutputCommand.FAIL);
245+
StringWriter sw = new StringWriter();
246+
PrintWriter pw = new PrintWriter(sw);
247+
e.printStackTrace(pw);
248+
249+
data.add(sw.toString());
250+
out.println(data);
251+
}
252+
234253
private void dumpView() {
235254
OutputData data = new OutputData(OutputCommand.VIEW);
236255
if (newTurn) {

engine/core/src/main/java/com/codingame/gameengine/core/OutputCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.codingame.gameengine.core;
22
enum OutputCommand {
3-
VIEW, INFOS, NEXT_PLAYER_INPUT, NEXT_PLAYER_INFO, SCORES, UINPUT, TOOLTIP, SUMMARY, METADATA;
3+
VIEW, INFOS, NEXT_PLAYER_INPUT, NEXT_PLAYER_INFO, SCORES, UINPUT, TOOLTIP, SUMMARY, METADATA, FAIL;
44
public String format(int lineCount) {
55
return String.format("[[%s] %d]", this.name(), lineCount);
66
}

runner/src/main/java/com/codingame/gameengine/runner/Command.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static enum OutputCommand implements CommandKey {
1515
}
1616

1717
static enum InputCommand implements CommandKey {
18-
VIEW, INFOS, NEXT_PLAYER_INPUT, NEXT_PLAYER_INFO, SCORES, UINPUT, TOOLTIP, SUMMARY, METADATA;
18+
VIEW, INFOS, NEXT_PLAYER_INPUT, NEXT_PLAYER_INFO, SCORES, UINPUT, TOOLTIP, SUMMARY, METADATA, FAIL;
1919
}
2020

2121
private List<String> lines;

runner/src/main/java/com/codingame/gameengine/runner/GameRunner.java

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ private void runAgents() {
127127
GameTurnInfo turnInfo = readGameInfo(round);
128128
boolean validTurn = turnInfo.isComplete();
129129

130+
gameResult.failCause = turnInfo.get(InputCommand.FAIL).orElse(null);
131+
130132
if (validTurn) {
131133
gameResult.outputs.get("referee").add(refereeStdout.toString());
132134
refereeStdout.reset();
@@ -283,7 +285,7 @@ private GameTurnInfo readGameInfo(int round) {
283285

284286
referee.sendInput(new Command(OutputCommand.GET_GAME_INFO).toString());
285287

286-
while (!turnInfo.isComplete()) {
288+
while (!turnInfo.isComplete() && !turnInfo.refereeHasFailed()) {
287289
Command command = readCommand(referee, round);
288290
if (command == null) {
289291
return turnInfo;
@@ -294,34 +296,39 @@ private GameTurnInfo readGameInfo(int round) {
294296
}
295297

296298
private Command readCommand(Agent agent, int round) {
297-
String output = agent.getOutput(1, 150_000);
298-
if (output != null) {
299-
output = output.replace('\r', '\n');
300-
}
301-
if (checkOutput(output, 1) != OutputResult.OK) {
302-
throw new RuntimeException("Invalid Referee command: " + output);
303-
}
299+
try {
300+
String output = agent.getOutput(1, 150_000);
301+
if (output != null) {
302+
output = output.replace('\r', '\n');
303+
}
304+
if (checkOutput(output, 1) != OutputResult.OK) {
305+
throw new RuntimeException("Invalid Referee command: " + output);
306+
}
304307

305-
Matcher m = COMMAND_HEADER_PATTERN.matcher(output.trim());
306-
if (m.matches()) {
307-
String command = m.group("cmd");
308-
int nbLinesToRead = Integer.parseInt(m.group("lineCount"));
308+
Matcher m = COMMAND_HEADER_PATTERN.matcher(output.trim());
309+
if (m.matches()) {
310+
String command = m.group("cmd");
311+
int nbLinesToRead = Integer.parseInt(m.group("lineCount"));
309312

310-
if (nbLinesToRead >= 0) {
311-
output = agent.getOutput(nbLinesToRead, 150_000, round == 0);
312-
output = output.replace('\r', '\n');
313+
if (nbLinesToRead >= 0) {
314+
output = agent.getOutput(nbLinesToRead, 150_000, round == 0);
315+
output = output.replace('\r', '\n');
316+
} else {
317+
throw new RuntimeException("Invalid Referee command line count: " + output);
318+
}
319+
if (checkOutput(output, nbLinesToRead) != OutputResult.OK) {
320+
throw new RuntimeException(
321+
"Error reading Referee command. Buffer capacity: " + output.length() + " / "
322+
+ (round == 0 ? RefereeAgent.REFEREE_MAX_BUFFER_SIZE_EXTRA : RefereeAgent.REFEREE_MAX_BUFFER_SIZE)
323+
);
324+
}
325+
return new Command(InputCommand.valueOf(command), output);
313326
} else {
314-
throw new RuntimeException("Invalid Referee command line count: " + output);
327+
throw new RuntimeException("Invalid referee command: " + output);
315328
}
316-
if (checkOutput(output, nbLinesToRead) != OutputResult.OK) {
317-
throw new RuntimeException(
318-
"Error reading Referee command. Buffer capacity: " + output.length() + " / "
319-
+ (round == 0 ? RefereeAgent.REFEREE_MAX_BUFFER_SIZE_EXTRA : RefereeAgent.REFEREE_MAX_BUFFER_SIZE)
320-
);
321-
}
322-
return new Command(InputCommand.valueOf(command), output);
323-
} else {
324-
throw new RuntimeException("Invalid referee command: " + output);
329+
} catch (RuntimeException err) {
330+
err.printStackTrace();
331+
return new Command(InputCommand.FAIL, err.toString());
325332
}
326333
}
327334

@@ -408,12 +415,15 @@ public void write(int b) throws IOException {
408415
public void write(int b) throws IOException {
409416
err.write(b);
410417
refereeStderr.write(b);
411-
}
418+
}
412419
}));
413420
requireGameNotEnded();
414421
Properties conf = new Properties();
415422
initialize(conf);
423+
416424
runAgents();
425+
426+
referee.destroy();
417427
destroyPlayers();
418428
gameEnded = true;
419429
System.setOut(out);

runner/src/main/java/com/codingame/gameengine/runner/GameTurnInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ boolean isComplete() {
2626
boolean isEndTurn() {
2727
return isCompleteEndTurn();
2828
}
29+
30+
boolean refereeHasFailed() {
31+
return received.containsKey(InputCommand.FAIL);
32+
}
2933

3034
private boolean isCompleteEndTurn() {
3135
return received.containsKey(InputCommand.SCORES)

runner/src/main/java/com/codingame/gameengine/runner/JavaPlayerAgent.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ protected void runInputOutput() throws Exception {
8787

8888
@Override
8989
public void destroy() {
90-
// Does nothing
9190
if (javaRunnerThread != null) {
9291
javaRunnerThread.setStopping(true);
9392
javaRunnerThread.interrupt();

runner/src/main/java/com/codingame/gameengine/runner/RefereeAgent.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class RefereeAgent extends Agent {
2222
private OutputStream processStdin = null;
2323
private InputStream processStdout = null;
2424
private InputStream processStderr = null;
25+
26+
private Thread thread;
2527

2628
public RefereeAgent() {
2729
super();
@@ -34,6 +36,14 @@ public RefereeAgent() {
3436
throw new RuntimeException("Cannot initialize Referee Agent");
3537
}
3638
}
39+
40+
@Override
41+
public void destroy() {
42+
if (thread != null) {
43+
thread.interrupt();
44+
}
45+
}
46+
3747

3848
@Override
3949
protected OutputStream getInputStream() {
@@ -53,13 +63,12 @@ protected InputStream getErrorStream() {
5363
@Override
5464
protected void runInputOutput() throws Exception {
5565

56-
Thread t = new Thread() {
66+
thread = new Thread() {
5767
public void run() {
5868
RefereeMain.start(agentStdin, new PrintStream(agentStdout));
5969
}
6070
};
61-
62-
t.start();
71+
thread.start();
6372
}
6473

6574
@Override

runner/src/main/java/com/codingame/gameengine/runner/dto/GameResult.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ public class GameResult {
1616
public List<Tooltip> tooltips = new ArrayList<>();
1717
public Map<Integer, Integer> ids = new HashMap<>();
1818
public List<AgentDto> agents = new ArrayList<>();
19+
public String failCause = null;
1920
}

runner/src/main/resources/view/app.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ function PlayerCtrl ($scope, $timeout, $interval, $filter, drawerFactory, gameMa
6262
if ($scope.gameLoaded || !ctrl.data) {
6363
return
6464
}
65+
if (ctrl.data.failCause) {
66+
$scope.errors = ctrl.data.failCause
67+
return;
68+
}
69+
6570
$scope.gameLoaded = true
6671
$scope.uinput = ctrl.data.uinput
6772
ctrl.gameInfo = convertFrameFormat(ctrl.data)

0 commit comments

Comments
 (0)