package games.strategy.engine.random;
import java.util.ArrayList;
import java.util.List;
import games.strategy.engine.framework.IGame;
import games.strategy.engine.framework.VerifiedRandomNumbers;
import games.strategy.engine.vault.NotUnlockedException;
import games.strategy.engine.vault.Vault;
import games.strategy.engine.vault.VaultID;
public class RemoteRandom implements IRemoteRandom {
private static List<VerifiedRandomNumbers> s_verifiedRandomNumbers = new ArrayList<>();
public static synchronized List<VerifiedRandomNumbers> getVerifiedRandomNumbers() {
return new ArrayList<>(s_verifiedRandomNumbers);
}
private static synchronized void addVerifiedRandomNumber(final VerifiedRandomNumbers number) {
s_verifiedRandomNumbers.add(number);
}
private final PlainRandomSource m_plainRandom = new PlainRandomSource();
private final IGame m_game;
// remembered from generate to unlock
private VaultID m_remoteVaultID;
private String m_annotation;
private int m_max;
// have we recieved a generate request, but not a unlock request
private boolean m_waitingForUnlock;
private int[] m_localNumbers;
/**
* Creates a new instance of RemoteRandom.
*/
public RemoteRandom(final IGame game) {
m_game = game;
}
@Override
public int[] generate(final int max, final int count, final String annotation, final VaultID remoteVaultID)
throws IllegalStateException {
if (m_waitingForUnlock) {
throw new IllegalStateException("Being asked to generate random numbers, but we havent finished last generation. "
// TODO: maybe we should wait instead of crashing the game?
+ "Asked for: " + count + "x" + max + " for " + annotation);
}
m_waitingForUnlock = true;
// clean up here, we know these keys arent needed anymore so release them
// we cant do this earlier without synchronizing between the server and the client
// but here we know they arent needed anymore
if (m_remoteVaultID != null) {
m_game.getVault().release(m_remoteVaultID);
}
m_remoteVaultID = remoteVaultID;
m_annotation = annotation;
m_max = max;
m_localNumbers = m_plainRandom.getRandom(max, count, annotation);
m_game.getVault().waitForID(remoteVaultID, 15000);
if (!m_game.getVault().knowsAbout(remoteVaultID)) {
throw new IllegalStateException(
"Vault id not known, have:" + m_game.getVault().knownIds() + " looking for:" + remoteVaultID);
}
return m_localNumbers;
}
@Override
public void verifyNumbers() throws IllegalStateException {
final Vault vault = m_game.getVault();
vault.waitForIdToUnlock(m_remoteVaultID, 15000);
if (!vault.isUnlocked(m_remoteVaultID)) {
throw new IllegalStateException("Server did not unlock random numbers, cheating is suspected");
}
int[] remoteNumbers;
try {
remoteNumbers = CryptoRandomSource.bytesToInts(vault.get(m_remoteVaultID));
} catch (final NotUnlockedException e1) {
e1.printStackTrace();
throw new IllegalStateException("Could not unlock numbers, cheating suspected");
}
final int[] verifiedNumbers = CryptoRandomSource.xor(remoteNumbers, m_localNumbers, m_max);
addVerifiedRandomNumber(new VerifiedRandomNumbers(m_annotation, verifiedNumbers));
m_waitingForUnlock = false;
}
}