/**
* Copyright (C) 2002-2012 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.freecol.server.model;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.freecolandroid.xml.stream.XMLStreamException;
import org.freecolandroid.xml.stream.XMLStreamReader;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.Europe;
import net.sf.freecol.common.model.Event;
import net.sf.freecol.common.model.FreeColGameObject;
import net.sf.freecol.common.model.FreeColGameObjectListener;
import net.sf.freecol.common.model.Game;
import net.sf.freecol.common.model.GameOptions;
import net.sf.freecol.common.model.HistoryEvent;
import net.sf.freecol.common.model.IndianSettlement;
import net.sf.freecol.common.model.Limit;
import net.sf.freecol.common.model.ModelMessage;
import net.sf.freecol.common.model.Player;
import net.sf.freecol.common.model.SimpleCombatModel;
import net.sf.freecol.common.model.Specification;
import net.sf.freecol.common.model.StringTemplate;
import net.sf.freecol.common.model.Tile;
import net.sf.freecol.common.model.Unit;
import net.sf.freecol.server.control.ChangeSet;
import net.sf.freecol.server.control.ChangeSet.ChangePriority;
import net.sf.freecol.server.control.ChangeSet.See;
/**
* The server representation of the game.
*/
public class ServerGame extends Game implements ServerModelObject {
private static final Logger logger = Logger.getLogger(ServerGame.class.getName());
/**
* Creates a new game model.
*
* @param specification The <code>Specification</code> to use in this game.
* @see net.sf.freecol.server.FreeColServer
*/
public ServerGame(Specification specification) {
super(specification);
this.combatModel = new SimpleCombatModel();
currentPlayer = null;
}
/**
* Initiate a new <code>ServerGame</code> with information from a
* saved game.
*
* @param freeColGameObjectListener A listener that should be monitoring
* this <code>Game</code>.
* @param in The input stream containing the XML.
* @param serverStrings A list of server object type,ID pairs to create.
* in this <code>Game</code>.
* @param specification The <code>Specification</code> to use in this game.
* @throws XMLStreamException if an error occurred during parsing.
* @see net.sf.freecol.server.FreeColServer#loadGame
*/
public ServerGame(FreeColGameObjectListener freeColGameObjectListener,
XMLStreamReader in, List<String> serverStrings,
Specification specification)
throws XMLStreamException {
super(specification);
setFreeColGameObjectListener(freeColGameObjectListener);
this.combatModel = new SimpleCombatModel();
this.viewOwner = null;
this.setGame(this);
// Need a container to hold a reference to all the server
// objects until the rest of the game is read. Without this,
// because the server objects are just placeholders with no
// real references to them, the WeakReferences in the Game are
// insufficient to preserve them across garbage collections.
List<Object> serverObjects = new ArrayList<Object>();
// Create trivial instantiations of all the server objects.
while (!serverStrings.isEmpty()) {
String type = serverStrings.remove(0);
String id = serverStrings.remove(0);
try {
Object o = makeServerObject(type, id);
serverObjects.add(o);
} catch (Exception e) {
logger.log(Level.WARNING, "Build " + type + " failed", e);
}
}
readFromXML(in);
// Initialize players.
for (Object o : serverObjects) {
if (o instanceof Player) {
Player player = (Player)o;
if (player.isUnknownEnemy()) {
setUnknownEnemy(player);
} else {
players.add(player);
}
}
}
}
/**
* Makes a trivial server object in this game given a server object tag
* and an id.
*
* @param type The server object tag.
* @param id The id.
* @return A trivial server object.
*/
private Object makeServerObject(String type, String id)
throws ClassNotFoundException, IllegalAccessException,
InstantiationException, InvocationTargetException,
NoSuchMethodException {
type = "net.sf.freecol.server.model."
+ type.substring(0,1).toUpperCase() + type.substring(1);
Class<?> c = Class.forName(type);
return c.getConstructor(Game.class, String.class)
.newInstance(this, id);
}
/**
* Get a unique ID to identify a <code>FreeColGameObject</code>.
*
* @return A unique ID.
*/
public String getNextID() {
String id = Integer.toString(nextId);
nextId++;
return id;
}
/**
* Checks if anybody has won this game.
*
* @return The <code>Player</code> who has won the game or null if none.
*/
public Player checkForWinner() {
Specification spec = getSpecification();
if (spec.getBoolean(GameOptions.VICTORY_DEFEAT_REF)) {
for (Player player : getLiveEuropeanPlayers()) {
if (player.getPlayerType() == Player.PlayerType.INDEPENDENT) {
return player;
}
}
}
if (spec.getBoolean(GameOptions.VICTORY_DEFEAT_EUROPEANS)) {
Player winner = null;
for (Player player : getLiveEuropeanPlayers()) {
if (!player.isREF()) {
if (winner != null) { // A live European player
winner = null;
break;
}
winner = player;
}
}
if (winner != null) return winner;
}
if (spec.getBoolean(GameOptions.VICTORY_DEFEAT_HUMANS)) {
Player winner = null;
for (Player player : getLiveEuropeanPlayers()) {
if (!player.isAI()) {
if (winner != null) { // A live human player
winner = null;
break;
}
winner = player;
}
}
if (winner != null) return winner;
}
return null;
}
/**
* Is the next player in a new turn?
*/
public boolean isNextPlayerInNewTurn() {
Player nextPlayer = getNextPlayer();
return players.indexOf(currentPlayer) > players.indexOf(nextPlayer)
|| currentPlayer == nextPlayer;
}
/**
* New turn for this game.
*
* @param random A <code>Random</code> number source.
* @param cs A <code>ChangeSet</code> to update.
*/
public void csNewTurn(Random random, ChangeSet cs) {
TransactionSession.completeAll(cs);
setTurn(getTurn().next());
cs.addTrivial(See.all(), "newTurn", ChangePriority.CHANGE_NORMAL,
"turn", Integer.toString(getTurn().getNumber()));
logger.info("ServerGame.csNewTurn, turn is " + getTurn().toString());
for (Player player : getPlayers()) {
if (!player.isUnknownEnemy()) {
((ServerPlayer) player).csNewTurn(random, cs);
}
}
Event spanishSuccession = getSpecification().getEvent("model.event.spanishSuccession");
if (spanishSuccession != null && !getSpanishSuccession()) {
Limit yearLimit = spanishSuccession.getLimit("model.limit.spanishSuccession.year");
if (yearLimit.evaluate(this)) {
csSpanishSuccession(cs, spanishSuccession);
}
}
}
/**
* Checks for and if necessary performs the War of Spanish
* Succession changes.
*
* @param cs A <code>ChangeSet</code> to update.
* @param spanishSuccession an <code>Event</code> value
*/
private void csSpanishSuccession(ChangeSet cs, Event spanishSuccession) {
Player weakestAIPlayer = null;
Player strongestAIPlayer = null;
for (Player player : getLiveEuropeanPlayers()) {
if (player.isREF() || !player.isAI()) continue;
if (weakestAIPlayer == null
|| weakestAIPlayer.getScore() > player.getScore()) {
weakestAIPlayer = player;
}
if (strongestAIPlayer == null
|| strongestAIPlayer.getScore() < player.getScore()) {
strongestAIPlayer = player;
}
}
// Only eliminate the weakest AI if limits are met
Limit weakestPlayerLimit;
Limit strongestPlayerLimit;
if (weakestAIPlayer != null
&& strongestAIPlayer != null
&& weakestAIPlayer != strongestAIPlayer
&& ((weakestPlayerLimit = spanishSuccession.getLimit("model.limit.spanishSuccession.weakestPlayer")) == null
|| weakestPlayerLimit.evaluate(weakestAIPlayer))
&& ((strongestPlayerLimit = spanishSuccession.getLimit("model.limit.spanishSuccession.strongestPlayer")) == null
|| strongestPlayerLimit.evaluate(strongestAIPlayer))) {
for (Player player : getPlayers()) {
for (IndianSettlement settlement
: player.getIndianSettlementsWithMission(weakestAIPlayer)) {
Unit missionary = settlement.getMissionary();
missionary.setOwner(strongestAIPlayer);
settlement.getTile().updatePlayerExploredTiles();
cs.add(See.perhaps().always((ServerPlayer)strongestAIPlayer),
settlement);
}
}
for (Colony colony : weakestAIPlayer.getColonies()) {
colony.changeOwner(strongestAIPlayer);
for (Tile tile : colony.getOwnedTiles()) {
cs.add(See.perhaps(), tile);
}
}
for (Unit unit : weakestAIPlayer.getUnits()) {
unit.setOwner(strongestAIPlayer);
if (unit.getLocation() instanceof Europe) {
unit.setLocation(strongestAIPlayer.getEurope());
}
cs.add(See.perhaps(), unit);
}
StringTemplate loser = weakestAIPlayer.getNationName();
StringTemplate winner = strongestAIPlayer.getNationName();
cs.addMessage(See.all(),
new ModelMessage(ModelMessage.MessageType.FOREIGN_DIPLOMACY,
"model.diplomacy.spanishSuccession",
strongestAIPlayer)
.addStringTemplate("%loserNation%", loser)
.addStringTemplate("%nation%", winner));
cs.addGlobalHistory(this,
new HistoryEvent(getTurn(),
HistoryEvent.EventType.SPANISH_SUCCESSION)
.addStringTemplate("%loserNation%", loser)
.addStringTemplate("%nation%", winner));
setSpanishSuccession(true);
cs.addPartial(See.all(), this, "spanishSuccession");
((ServerPlayer) weakestAIPlayer).csKill(cs);
}
}
/**
* Returns the tag name of the root element representing this object.
*
* @return "serverGame".
*/
public String getServerXMLElementTagName() {
return "serverGame";
}
/**
* Collects a list of all the ServerModelObjects in this game.
*
* @return A list of all the ServerModelObjects in this game.
*/
public List<ServerModelObject> getServerModelObjects() {
List<ServerModelObject> objs = new ArrayList<ServerModelObject>();
for (WeakReference<FreeColGameObject> wr :freeColGameObjects.values()) {
if (wr.get() instanceof ServerModelObject) {
objs.add((ServerModelObject) wr.get());
}
}
return objs;
}
}