11package sc .server .gaming ;
22
3- import com .thoughtworks .xstream .XStream ;
43import org .slf4j .Logger ;
54import org .slf4j .LoggerFactory ;
65import sc .api .plugins .IGameInstance ;
1211import sc .api .plugins .exceptions .TooManyPlayersException ;
1312import sc .api .plugins .host .IGameListener ;
1413import sc .framework .HelperMethods ;
14+ import sc .framework .ReplayListener ;
1515import sc .framework .plugins .AbstractGame ;
1616import sc .framework .plugins .Player ;
17- import sc .networking .XStreamProvider ;
1817import sc .networking .clients .IClient ;
1918import sc .networking .clients .XStreamClient ;
2019import sc .protocol .ProtocolPacket ;
@@ -42,9 +41,9 @@ public class GameRoom implements IGameListener {
4241 private final ScoreDefinition scoreDefinition ;
4342 private final List <PlayerSlot > playerSlots = new ArrayList <>(getMaximumPlayerCount ());
4443 private GameStatus status = GameStatus .CREATED ;
45- private GameResult result = null ;
44+ private GameResult result ;
4645 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 ;
4847
4948 public final IGameInstance game ; // TODO make inaccessible
5049 public final List <IClient > observers = new ArrayList <>();
@@ -74,6 +73,7 @@ public synchronized void onGameOver(Map<Player, PlayerScore> results) {
7473 try {
7574 result = generateGameResult (results );
7675 logger .info ("{} is over (regular={})" , game , result .isRegular ());
76+ saveReplayMessage (result );
7777 // save playerScore if test mode enabled
7878 if (Boolean .parseBoolean (Configuration .get (Configuration .TEST_MODE ))) {
7979 List <Player > players = game .getPlayers ();
@@ -84,22 +84,38 @@ public synchronized void onGameOver(Map<Player, PlayerScore> results) {
8484 logger .error ("Failed to generate GameResult from " + results , t );
8585 }
8686
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 ) {
8899 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 )));
90103 } catch (IOException e ) {
91104 logger .error ("Failed to save replay" , e );
92105 }
93106 }
107+ }
94108
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 );
96112 }
97113
98114 public File createReplayFile () throws IOException {
99115 String fileName = HelperMethods .getReplayFilename (this .game .getPluginUUID (),
100116 playerSlots .stream ().map (it -> it .getPlayer ().getDisplayName ()).collect (Collectors .toList ()));
101117
102- File file = new File (fileName );
118+ File file = new File (fileName ). getAbsoluteFile () ;
103119 if (file .getParentFile ().mkdirs ())
104120 if (file .createNewFile ())
105121 return file ;
@@ -130,26 +146,6 @@ private GameResult generateGameResult(Map<Player, PlayerScore> results) {
130146 return new GameResult (scoreDefinition , scores , this .game .getWinners ());
131147 }
132148
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-
153149 /** Send the given message to all Players and Observers in this room. */
154150 private void broadcast (ObservableRoomMessage message ) {
155151 broadcast (createRoomPacket (message ));
@@ -175,10 +171,12 @@ private void kickAllClients() {
175171 /** Send updated GameState to all players and observers. */
176172 @ Override
177173 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 ) {
181177 sendStateToPlayers (data );
178+ saveReplayMessage (memento );
179+ }
182180 }
183181
184182 /**
@@ -321,7 +319,7 @@ public synchronized void onEvent(Client source, IMove move) throws GameRoomExcep
321319 ErrorMessage errorMessage = new ErrorMessage (move , error );
322320 player .notifyListeners (errorMessage );
323321 observerBroadcast (errorMessage );
324- history . add (errorMessage );
322+ saveReplayMessage (errorMessage );
325323 cancel ();
326324 throw new GameLogicException (e .toString (), e );
327325 } catch (GameLogicException e ) {
0 commit comments