// // @(#)World.java 4/2002 // // Copyright 2002 Zachary DelProposto. All rights reserved. // Use is subject to license terms. // // // This program 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. // // This program 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 this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Or from http://www.gnu.org/ // package dip.world; import dip.world.metadata.PlayerMetadata; import dip.world.metadata.GameMetadata; import dip.gui.undo.UndoRedoManager; import dip.net.message.PressStore; import dip.net.message.DefaultPressStore; import java.io.*; import java.util.*; import java.util.Map; import java.net.*; import java.util.zip.*; import JSX.*; /** * The entire game World. This contains the state of an entire game. * <p> * A World contains: * <ol> * <li>Map (dip.world.Map) object [constant] * <li>TurnState objects [in a linked hash map] * <li>HashMap of per-power and global state information (used to set various data) * </ol> * * */ public class World implements Serializable { // constants for non-turn-data lookup private static final String KEY_GLOBAL_DATA = "_global_data_"; private static final String KEY_VICTORY_CONDITIONS = "_victory_conditions_"; private static final String KEY_WORLD_METADATA = "_world_metadata_"; private static final String KEY_UNDOREDOMANAGER = "_undo_redo_manager_"; private static final String KEY_GAME_SETUP = "_game_setup_"; private static final String KEY_PRESS_STORE = "_press_store_"; private static final String KEY_VARIANT_INFO = "_variant_info_"; // instance variables private SortedMap turnStates = null; // turn data private Map nonTurnData = null; // non-turn data (misc data & per-player data) private final dip.world.Map map; // the actual map (constant) /** * Reads a World object from a file. */ public static World open(File file) throws IOException { JSX.ObjectReader in = null; try { GZIPInputStream gzi = new GZIPInputStream(new BufferedInputStream(new FileInputStream(file), 4096)); in = new JSX.ObjectReader(gzi); World w = (World) in.readObject(); return (World) w; } catch(IOException ioe) { throw ioe; } catch(Exception e) { // rethrow all non-IOExceptions as IOExceptions IOException ioe = new IOException(e.getMessage()); ioe.initCause(e); throw ioe; } finally { if(in != null) { in.close(); } } }// open() /** * Saves a World object to a file. */ public static void save(File file, World world) throws IOException { GZIPOutputStream gzos = null; try { gzos = new GZIPOutputStream(new FileOutputStream(file), 2048); JSX.ObjectWriter out = new JSX.ObjectWriter(gzos); out.setPrettyPrint(false); out.writeObject(world); out.close(); gzos.finish(); // this is key. otherwise data is not written. } catch(IOException ioe) { throw ioe; } catch(Exception e) { // rethrow all non-IOExceptions as IOExceptions IOException ioe = new IOException(e.getMessage()); ioe.initCause(e); throw ioe; } finally { if(gzos != null) { gzos.close(); } } }// save() /** * Constructs a World object. */ protected World(dip.world.Map map) { this.map = map; turnStates = Collections.synchronizedSortedMap(new TreeMap()); // synchronize on TreeMap nonTurnData = new HashMap(17); }// World() /** Returns the Map (dip.world.Map) associated with this World. */ public dip.world.Map getMap() { return map; }// getMap() /** * Sets any special per-power state information that is not associated with * a particular TurnState. This may be set to null. */ public void setPowerState(Power power, Object state) { nonTurnData.put(power, state); }// setPowerState() /** * Gets any special per-power state information that is not associated with * a particular TurnState. This may return null. */ public Object getPowerState(Power power) { return nonTurnData.get(power); }// getPowerState() /** Set the Global state object. This may be set to null. */ public void setGlobalState(Object state) { nonTurnData.put(KEY_GLOBAL_DATA, state); }// setGlobalState() /** Get the Global state object. This may return null. */ public Object getGlobalState() { return nonTurnData.get(KEY_GLOBAL_DATA); }// getGlobalState() /** Set the Victory Conditions */ public void setVictoryConditions(VictoryConditions value) { nonTurnData.put(KEY_VICTORY_CONDITIONS, value); }// setVictoryConditions() /** Get the Victory Conditions */ public VictoryConditions getVictoryConditions() { return (VictoryConditions) nonTurnData.get(KEY_VICTORY_CONDITIONS); }// getVictoryConditions() /** Gets the first TurnState object */ public TurnState getInitialTurnState() { TurnState ts = (TurnState) turnStates.get(turnStates.firstKey()); if(ts != null) { ts.setWorld(this); } return ts; }// getInitialTurnState() /** Gets the most current (last in the list) TurnState. */ public TurnState getLastTurnState() { TurnState ts = (TurnState) turnStates.get(turnStates.lastKey()); if(ts != null) { ts.setWorld(this); } return ts; }// getLastTurnState() /** Gets the TurnState associated with the specified Phase */ public TurnState getTurnState(Phase phase) { TurnState ts = (TurnState) turnStates.get(phase); if(ts != null) { ts.setWorld(this); } return ts; }// getTurnState() /** Gets the TurnState that comes after this phase (if it exists). * <p> * Note that the next phase may not be (due to phase skipping) the * same phase generated by phase.getNext(). This will return null * iff we are at the last Phase. */ public TurnState getNextTurnState(TurnState state) { Phase current = state.getPhase(); if(current == null) { return null; } Phase next = null; Iterator iter = turnStates.keySet().iterator(); while(iter.hasNext()) { Phase phase = (Phase) iter.next(); if(current.compareTo(phase) == 0) { if(iter.hasNext()) { next = (Phase) iter.next(); } break; } } if(next == null) { return null; } TurnState ts = (TurnState) turnStates.get(next); ts.setWorld(this); return ts; }// getNextTurnState() /** * Get all TurnStates. Note that the returned List * may be modified, without modifications being reflected * in the World object. However, modifications to individual * TurnState objects will be reflected in the World object * (TurnStates are not cloned here). */ public List getAllTurnStates() { Collection values = turnStates.values(); ArrayList al = new ArrayList(values.size()); al.addAll(values); return al; }// getAllTurnStates() /** Gets the TurnState that comes before the specified phase. * <p> * Note that the previous phase may not be (due to phase skipping) the * same phase generated by phase.getPrevious(). This will return null * iff we are at the first (initial) Phase. */ public TurnState getPreviousTurnState(TurnState state) { Phase current = state.getPhase(); if(current == null) { return null; } Phase previous = null; Iterator iter = turnStates.keySet().iterator(); while(iter.hasNext()) { Phase phase = (Phase) iter.next(); if(phase.compareTo(current) != 0) { previous = phase; } else { break; } } if(previous == null) { return null; } TurnState ts = (TurnState) turnStates.get(previous); ts.setWorld(this); return ts; }// getPreviousTurnState() /** If a TurnState with the given phase already exists, it is replaced. */ public void setTurnState(TurnState turnState) { turnStates.put(turnState.getPhase(), turnState); }// setTurnState() /** * Removes a turnstate from the world. This should * be used with caution! */ public void removeTurnState(TurnState turnState) { turnStates.remove(turnState.getPhase()); }// removeTurnState() /** Removes <b>all</b> TurnStates from the World. */ public void removeAllTurnStates() { turnStates.clear(); }// removeAllTurnStates() /** returns sorted (ascending) set of all Phases */ public Set getPhaseSet() { return turnStates.keySet(); }// getPhaseSet() /** Sets the Game metadata */ public void setGameMetadata(GameMetadata gmd) { if(gmd == null) { throw new IllegalArgumentException("null metadata"); } nonTurnData.put(KEY_WORLD_METADATA, gmd); }// setGameMetadata() /** Gets the Game metadata. Never returns null. Does not return a copy. */ public GameMetadata getGameMetadata() { GameMetadata gmd = (GameMetadata) nonTurnData.get(KEY_WORLD_METADATA); if(gmd == null) { gmd = new GameMetadata(); setGameMetadata(gmd); } return gmd; }// setGameMetadata() /** Sets the metadata for a player, referenced by Power */ public void setPlayerMetadata(Power power, PlayerMetadata pmd) { if(power == null || pmd == null) { throw new IllegalArgumentException("null power or metadata"); } nonTurnData.put(power, pmd); }// setPlayerMetadata() /** Gets the metadata for a power. Never returns null. Does not return a copy. */ public PlayerMetadata getPlayerMetadata(Power power) { if(power == null) { throw new IllegalArgumentException("null power"); } PlayerMetadata pmd = (PlayerMetadata) nonTurnData.get(power); if(pmd == null) { pmd = new PlayerMetadata(); setPlayerMetadata(power, pmd); } return pmd; }// getPlayerMetadata() /** Sets the UndoRedo manager to be saved. This may be set to null. */ public void setUndoRedoManager(UndoRedoManager urm) { nonTurnData.put(KEY_UNDOREDOMANAGER, urm); }// setGlobalState() /** Gets the UndoRedo manager that was saved. Null if none was saved. */ public UndoRedoManager getUndoRedoManager() { return (UndoRedoManager) nonTurnData.get(KEY_UNDOREDOMANAGER); }// getUndoRedoManager() /** Sets the GameSetup object */ public void setGameSetup(GameSetup gs) { if(gs == null) { throw new IllegalArgumentException(); } nonTurnData.put(KEY_GAME_SETUP, gs); }// setGameSetup() /** Returns the GameSetup object */ public GameSetup getGameSetup() { return (GameSetup) nonTurnData.get(KEY_GAME_SETUP); }// getGameSetup() /** Get the PressStore object, which stores and retreives Press messages. */ public synchronized PressStore getPressStore() { // create on demand if(nonTurnData.get(KEY_PRESS_STORE) == null) { nonTurnData.put(KEY_PRESS_STORE, new DefaultPressStore()); } return (PressStore) nonTurnData.get(KEY_PRESS_STORE); }// getPressStore() /** Get the Variant Info object. This returns a Reference to the Variant information. */ public synchronized VariantInfo getVariantInfo() { VariantInfo vi = (VariantInfo) nonTurnData.get(KEY_VARIANT_INFO); if(vi == null) { vi = new VariantInfo(); nonTurnData.put(KEY_VARIANT_INFO, vi); } return vi; }// getVariantInfo() /** Set the Variant Info object. */ public synchronized void setVariantInfo(VariantInfo vi) { nonTurnData.put(KEY_VARIANT_INFO, vi); }// getVariantInfo() /** Convenience method: gets RuleOptions from VariantInfo object. */ public RuleOptions getRuleOptions() { return getVariantInfo().getRuleOptions(); }// getRuleOptions() /** Convenience method: sets RuleOptions in VariantInfo object. */ public void setRuleOptions(RuleOptions ruleOpts) { if(ruleOpts == null) { throw new IllegalArgumentException(); } getVariantInfo().setRuleOptions(ruleOpts); }// getRuleOptions() /** * Variant Info is a class which holds information about * the variant, map, symbols, and symbol options. */ public static class VariantInfo { private String variantName; private String mapName; private String symbolsName; private float variantVersion; private float symbolsVersion; private RuleOptions ruleOptions; /** Create a VariantInfo object */ public VariantInfo() {} /** Set the Variant name. */ public void setVariantName(String value) { this.variantName = value; } /** Set the Map name. */ public void setMapName(String value) { this.mapName = value; } /** Set the Symbol pack name. */ public void setSymbolPackName(String value) { this.symbolsName = value; } /** Set the Variant version. */ public void setVariantVersion(float value) { checkVersion(value); this.variantVersion = value; } /** Set the Symbol pack version. */ public void setSymbolPackVersion(float value) { checkVersion(value); this.symbolsVersion = value; } /** <b>Replaces</b> the current RuleOptions with the given RuleOptions */ public void setRuleOptions(RuleOptions value) { this.ruleOptions = value; } /** Get the Variant name. */ public String getVariantName() { return this.variantName; } /** Get the Map name. */ public String getMapName() { return this.mapName; } /** Get the Symbol pack name. */ public String getSymbolPackName() { return this.symbolsName; } /** Get the Variant version. */ public float getVariantVersion() { return this.variantVersion; } /** Get the Symbol pack version. */ public float getSymbolPackVersion() { return this.symbolsVersion; } /** Gets the RuleOptions */ public RuleOptions getRuleOptions() { if(ruleOptions == null) { ruleOptions = new RuleOptions(); } return ruleOptions; }// getRuleOptions() /** ensures Version is a value >0.0f */ private void checkVersion(float v) { if(v <= 0.0f) { throw new IllegalArgumentException("version: "+v); } }// checkVersion(); }// nested class VariantInfo }// class World