// // @(#)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.world.variant.VariantManager; 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 javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.MapKey; import javax.persistence.MapKeyColumn; import javax.persistence.OneToMany; import javax.persistence.OrderColumn; import javax.persistence.Transient; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.Sort; import org.hibernate.annotations.SortType; //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> * * */ @Entity @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class World implements Serializable { /** * */ private static final long serialVersionUID = -2820481380993122362L; // 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<Integer, TurnState> turnStates = null; // turn data private Map<String, Serializable> nonTurnData = null; // non-turn data (misc data & per-player data) private dip.world.Map map = null; // the actual map (constant) private int id; @OneToMany @MapKeyColumn @Sort(type = SortType.NATURAL) public SortedMap<Integer, TurnState> getTurnStates() { return turnStates; } public void setTurnStates(SortedMap<Integer, TurnState> turnStates) { this.turnStates = turnStates; } @ElementCollection @MapKeyColumn @Lob public Map<String, Serializable> getNonTurnData() { return nonTurnData; } public void setNonTurnData(Map nonTurnData) { this.nonTurnData = nonTurnData; } /** * 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() // @Id @GeneratedValue(strategy=GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } public World(){ //Just for Hibernate } /** * Constructs a World object. */ protected World(dip.world.Map map) { this.map = map; turnStates = new TreeMap<Integer, TurnState>(); // synchronize on TreeMap nonTurnData = new HashMap<String, Serializable>(17); }// World() /** Returns the Map (dip.world.Map) associated with this World. */ @Transient public dip.world.Map getMap() { if (map == null){ try { map = WorldFactory.getInstance().createMap(VariantManager.getVariant(getVariantInfo().getVariantName(), getVariantInfo().getVariantVersion())); } catch (InvalidWorldException e) { //What happened here?/! e.printStackTrace(); } } return map; }// getMap() public void setMap(dip.world.Map map){ this.map = map; } /** * 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. */ // @Transient // 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. */ // @Transient // 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 */ @Transient public VictoryConditions getVictoryConditions() { return (VictoryConditions) nonTurnData.get(KEY_VICTORY_CONDITIONS); }// getVictoryConditions() /** Gets the first TurnState object */ @Transient 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. */ @Transient 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 */ @Transient public TurnState getTurnState(Phase phase) { TurnState ts = (TurnState) turnStates.get(phase.hashCode()); 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. */ @Transient public TurnState getNextTurnState(TurnState state) { if(state.getPhase() == null) { return null; } int current = state.getPhase().hashCode(); int next = Integer.MIN_VALUE; Iterator<Integer> iter = turnStates.keySet().iterator(); while(iter.hasNext()) { int phase = iter.next().hashCode(); if(current == phase) { if(iter.hasNext()) { next = (Integer)iter.next(); } break; } } if(next == Integer.MIN_VALUE) { 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). */ @Transient 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. */ @Transient public TurnState getPreviousTurnState(TurnState state) { Phase current = state.getPhase(); if(current == null) { return null; } int previous = Integer.MIN_VALUE; Iterator<Integer> iter = turnStates.keySet().iterator(); while(iter.hasNext()) { int phase = (Integer) iter.next(); if(phase != current.hashCode()) { previous = phase; } else { break; } } if(previous == Integer.MIN_VALUE) { 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().hashCode(), turnState); }// setTurnState() /** * Removes a turnstate from the world. This should * be used with caution! */ public void removeTurnState(TurnState turnState) { turnStates.remove(turnState.getPhase().hashCode()); }// removeTurnState() /** Removes <b>all</b> TurnStates from the World. */ public void removeAllTurnStates() { turnStates.clear(); }// removeAllTurnStates() /** returns sorted (ascending) set of all Phases */ // @Transient // 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. */ @Transient 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.getName(), pmd); }// setPlayerMetadata() /** Gets the metadata for a power. Never returns null. Does not return a copy. */ @Transient public PlayerMetadata getPlayerMetadata(Power power) { if(power == null) { throw new IllegalArgumentException("null power"); } PlayerMetadata pmd = (PlayerMetadata) nonTurnData.get(power.getName()); 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 */ // @Transient // public GameSetup getGameSetup() // { // return (GameSetup) nonTurnData.get(KEY_GAME_SETUP); // }// getGameSetup() /** Get the PressStore object, which stores and retreives Press messages. */ // @Transient // 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. */ @Transient 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. */ @Transient 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 implements Serializable { /** * */ private static final long serialVersionUID = 3933248847724969465L; 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