1
1
package sc .server .gaming ;
2
2
3
- import com .thoughtworks .xstream .XStream ;
4
3
import org .slf4j .Logger ;
5
4
import org .slf4j .LoggerFactory ;
6
5
import sc .api .plugins .IGameInstance ;
12
11
import sc .api .plugins .exceptions .TooManyPlayersException ;
13
12
import sc .api .plugins .host .IGameListener ;
14
13
import sc .framework .HelperMethods ;
14
+ import sc .framework .ReplayListener ;
15
15
import sc .framework .plugins .AbstractGame ;
16
16
import sc .framework .plugins .Player ;
17
- import sc .networking .XStreamProvider ;
18
17
import sc .networking .clients .IClient ;
19
18
import sc .networking .clients .XStreamClient ;
20
19
import sc .protocol .ProtocolPacket ;
@@ -42,9 +41,9 @@ public class GameRoom implements IGameListener {
42
41
private final ScoreDefinition scoreDefinition ;
43
42
private final List <PlayerSlot > playerSlots = new ArrayList <>(getMaximumPlayerCount ());
44
43
private GameStatus status = GameStatus .CREATED ;
45
- private GameResult result = null ;
44
+ private GameResult result ;
46
45
private boolean pauseRequested = false ;
47
- private List < RoomMessage > history = new ArrayList <>();
46
+ private final ReplayListener < RoomPacket > replayListener = Boolean . parseBoolean ( Configuration . get ( Configuration . SAVE_REPLAY )) ? new ReplayListener <>() : null ;
48
47
49
48
public final IGameInstance game ; // TODO make inaccessible
50
49
public final List <IClient > observers = new ArrayList <>();
@@ -74,6 +73,7 @@ public synchronized void onGameOver(Map<Player, PlayerScore> results) {
74
73
try {
75
74
result = generateGameResult (results );
76
75
logger .info ("{} is over (regular={})" , game , result .isRegular ());
76
+ saveReplayMessage (result );
77
77
// save playerScore if test mode enabled
78
78
if (Boolean .parseBoolean (Configuration .get (Configuration .TEST_MODE ))) {
79
79
List <Player > players = game .getPlayers ();
@@ -84,22 +84,38 @@ public synchronized void onGameOver(Map<Player, PlayerScore> results) {
84
84
logger .error ("Failed to generate GameResult from " + results , t );
85
85
}
86
86
87
- if (Boolean .parseBoolean (Configuration .get (Configuration .SAVE_REPLAY ))) {
87
+ saveReplay ();
88
+ destroy ();
89
+ }
90
+
91
+ private void saveReplayMessage (ObservableRoomMessage message ) {
92
+ if (replayListener != null )
93
+ replayListener .addMessage (createRoomPacket (message instanceof MementoMessage ? ((MementoMessage ) message ).clone () : message ));
94
+ }
95
+
96
+ /** If enabled, save the recorded replay to the default file. */
97
+ public void saveReplay () {
98
+ if (replayListener != null ) {
88
99
try {
89
- saveReplay (new BufferedWriter (new FileWriter (createReplayFile ())));
100
+ File file = createReplayFile ();
101
+ logger .debug ("Saving replay to {}" , file );
102
+ saveReplay (new BufferedWriter (new FileWriter (file )));
90
103
} catch (IOException e ) {
91
104
logger .error ("Failed to save replay" , e );
92
105
}
93
106
}
107
+ }
94
108
95
- destroy ();
109
+ /** Saves a replay to the writer, assuming replays have been enabled. */
110
+ public void saveReplay (Writer writer ) throws IOException , NullPointerException {
111
+ replayListener .saveReplay (writer );
96
112
}
97
113
98
114
public File createReplayFile () throws IOException {
99
115
String fileName = HelperMethods .getReplayFilename (this .game .getPluginUUID (),
100
116
playerSlots .stream ().map (it -> it .getPlayer ().getDisplayName ()).collect (Collectors .toList ()));
101
117
102
- File file = new File (fileName );
118
+ File file = new File (fileName ). getAbsoluteFile () ;
103
119
if (file .getParentFile ().mkdirs ())
104
120
if (file .createNewFile ())
105
121
return file ;
@@ -130,26 +146,6 @@ private GameResult generateGameResult(Map<Player, PlayerScore> results) {
130
146
return new GameResult (scoreDefinition , scores , this .game .getWinners ());
131
147
}
132
148
133
- /** Write replay of game to a writer. */
134
- public void saveReplay (Writer writer ) throws IOException {
135
- XStream xStream = XStreamProvider .loadPluginXStream ();
136
-
137
- writer .write ("<protocol>\n " );
138
- for (RoomMessage element : history ) {
139
- if (!(element instanceof IGameState ))
140
- continue ;
141
- IGameState state = (IGameState ) element ;
142
- // TODO do we need to save RoomPackets?
143
- RoomPacket roomPacket = createRoomPacket (new MementoMessage (state , null ));
144
- writer .write (xStream .toXML (roomPacket ) + "\n " );
145
- writer .flush ();
146
- }
147
-
148
- writer .write (xStream .toXML (createRoomPacket (this .result )) + "\n " );
149
- writer .write ("</protocol>" );
150
- writer .close ();
151
- }
152
-
153
149
/** Send the given message to all Players and Observers in this room. */
154
150
private void broadcast (ObservableRoomMessage message ) {
155
151
broadcast (createRoomPacket (message ));
@@ -175,10 +171,12 @@ private void kickAllClients() {
175
171
/** Send updated GameState to all players and observers. */
176
172
@ Override
177
173
public void onStateChanged (IGameState data , boolean observersOnly ) {
178
- history . add (data );
179
- observerBroadcast (new MementoMessage ( data , null ) );
180
- if (!observersOnly )
174
+ MementoMessage memento = new MementoMessage (data , null );
175
+ observerBroadcast (memento );
176
+ if (!observersOnly ) {
181
177
sendStateToPlayers (data );
178
+ saveReplayMessage (memento );
179
+ }
182
180
}
183
181
184
182
/**
@@ -321,7 +319,7 @@ public synchronized void onEvent(Client source, IMove move) throws GameRoomExcep
321
319
ErrorMessage errorMessage = new ErrorMessage (move , error );
322
320
player .notifyListeners (errorMessage );
323
321
observerBroadcast (errorMessage );
324
- history . add (errorMessage );
322
+ saveReplayMessage (errorMessage );
325
323
cancel ();
326
324
throw new GameLogicException (e .toString (), e );
327
325
} catch (GameLogicException e ) {
0 commit comments