package games.strategy.engine.framework;
import javax.swing.SwingUtilities;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.history.EventChild;
/**
* Synchronizes a GameData by listening on the history channel for messages.
* All modifications to the History are done in the SwingEventThread, so
* this class can be used to display a history tree to the user.
*/
public class HistorySynchronizer {
// Note the GameData here and the game are not the same
// we are keeping m_data in synch with the history of the game by listening
// for changes
// we do this because our data can change depending where in the history we
// are
// we want to be able to do this without changing the data for the game
private final GameData m_data;
private int m_currentRound;
private final IGame m_game;
public HistorySynchronizer(final GameData data, final IGame game) {
// this is not the way to use this.
if (game.getData() == data) {
throw new IllegalStateException(
"You dont need a history synchronizer to synchronize game data that is managed by an IGame");
}
m_data = data;
m_data.forceChangesOnlyInSwingEventThread();
data.acquireReadLock();
try {
m_currentRound = data.getSequence().getRound();
} finally {
data.releaseReadLock();
}
m_game = game;
m_game.getChannelMessenger().registerChannelSubscriber(m_gameModifiedChannelListener,
IGame.GAME_MODIFICATION_CHANNEL);
}
private final IGameModifiedChannel m_gameModifiedChannelListener = new IGameModifiedChannel() {
@Override
public void gameDataChanged(final Change aChange) {
SwingUtilities.invokeLater(() -> {
final Change localizedChange = (Change) translateIntoMyData(aChange);
m_data.getHistory().getHistoryWriter().addChange(localizedChange);
});
}
@Override
public void startHistoryEvent(final String event, final Object renderingData) {
startHistoryEvent(event);
if (renderingData != null) {
setRenderingData(renderingData);
}
}
@Override
public void startHistoryEvent(final String event) {
SwingUtilities.invokeLater(() -> m_data.getHistory().getHistoryWriter().startEvent(event));
}
@Override
public void addChildToEvent(final String text, final Object renderingData) {
SwingUtilities.invokeLater(() -> {
final Object translatedRenderingData = translateIntoMyData(renderingData);
m_data.getHistory().getHistoryWriter().addChildToEvent(new EventChild(text, translatedRenderingData));
});
}
protected void setRenderingData(final Object renderingData) {
SwingUtilities.invokeLater(() -> {
final Object translatedRenderingData = translateIntoMyData(renderingData);
m_data.getHistory().getHistoryWriter().setRenderingData(translatedRenderingData);
});
}
@Override
public void stepChanged(final String stepName, final String delegateName, final PlayerID player, final int round,
final String displayName, final boolean loadedFromSavedGame) {
// we dont need to advance the game step in this case
if (loadedFromSavedGame) {
return;
}
SwingUtilities.invokeLater(() -> {
if (m_currentRound != round) {
m_currentRound = round;
m_data.getHistory().getHistoryWriter().startNextRound(m_currentRound);
}
m_data.getHistory().getHistoryWriter().startNextStep(stepName, delegateName, player, displayName);
});
}
@Override
public void shutDown() {}
};
public void deactivate() {
m_game.getChannelMessenger().unregisterChannelSubscriber(m_gameModifiedChannelListener,
IGame.GAME_MODIFICATION_CHANNEL);
}
/**
* Serializes the object and then deserializes it, resolving object
* references into m_data. Note the the history we are synching may refer to
* a different game data than the GaneData held by the IGame. A clone is
* made so that we can walk up and down the history without changing the
* game.
*/
private Object translateIntoMyData(final Object msg) {
return GameDataUtils.translateIntoOtherGameData(msg, m_data);
}
}