Gridarta Editor
AbstractMapManager.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.model.mapmanager;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.concurrent.CopyOnWriteArrayList;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43 
48 public abstract class AbstractMapManager<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> implements MapManager<G, A, R> {
49 
53  private static final int DELETED_OBJECTS_TO_REPORT = 10;
54 
58  @NotNull
60 
64  @NotNull
66 
70  @NotNull
72 
76  @NotNull
77  private final PathManager pathManager;
78 
82  private final List<MapControl<G, A, R>> mapControls = new CopyOnWriteArrayList<>();
83 
88 
92  @Nullable
94 
102  protected AbstractMapManager(@NotNull final MapReaderFactory<G, A> mapReaderFactory, @NotNull final ProjectSettings projectSettings, @NotNull final FaceObjectProviders faceObjectProviders, @NotNull final PathManager pathManager) {
103  this.projectSettings = projectSettings;
104  this.mapReaderFactory = mapReaderFactory;
105  this.pathManager = pathManager;
106 
107  final FaceObjectProvidersListener faceObjectProvidersListener = new FaceObjectProvidersListener() {
108 
109  @Override
110  public void facesReloaded() {
111  for (final MapControl<G, A, R> mapControl : mapControls) {
112  mapControl.getMapModel().facesReloaded();
113  }
114  }
115 
116  };
117  faceObjectProviders.addFaceObjectProvidersListener(faceObjectProvidersListener);
118  }
119 
120  @Deprecated
121  public void setFileControl(@NotNull final FileControl<G, A, R> fileControl) {
122  this.fileControl = fileControl;
123  }
124 
125  @NotNull
126  @Override
127  public MapControl<G, A, R> newMap(@Nullable final List<G> objects, @NotNull final A mapArchObject, @Nullable final MapFile mapFile, final boolean interactive) {
128  final MapControl<G, A, R> mapControl = createMapControl(objects, mapArchObject, mapFile);
129  mapControls.add(mapControl);
130  for (final MapManagerListener<G, A, R> listener : listenerList.getListeners()) {
131  listener.mapCreated(mapControl, interactive);
132  }
133  if (interactive) {
134  setCurrentMap(mapControl);
135  }
136  return mapControl;
137  }
138 
146  @NotNull
147  protected abstract MapControl<G, A, R> createMapControl(@Nullable List<G> objects, @NotNull A mapArchObject, @Nullable MapFile mapFile);
148 
149  @Nullable
150  @Override
152  if (currentMapControl != null) {
153  return currentMapControl;
154  }
155 
156  if (!mapControls.isEmpty()) {
157  return mapControls.get(mapControls.size() - 1);
158  }
159 
160  return null;
161 
162  }
163 
164  @Override
165  public void closeMap(@NotNull final MapControl<G, A, R> mapControl) {
166  for (final MapManagerListener<G, A, R> listener : listenerList.getListeners()) {
167  listener.mapClosing(mapControl);
168  }
169  assert mapControl.getUseCounter() <= 0;
170  mapControls.remove(mapControl);
171  if (currentMapControl == mapControl) {
172  setCurrentMap(mapControls.isEmpty() ? null : mapControls.get(0));
173  }
174  for (final MapManagerListener<G, A, R> listener : listenerList.getListeners()) {
175  listener.mapClosed(mapControl);
176  }
177  mapControl.getMapModel().mapClosed();
178  }
179 
180  @NotNull
181  @Override
182  public MapControl<G, A, R> openMapFile(@NotNull final MapFile mapFile, final boolean interactive) throws IOException {
183  // First look whether the file isn't already open.
184  // If so, return the previously loaded map.
185  for (final MapControl<G, A, R> mapControl : mapControls) {
186  final MapFile tmpMapFile = mapControl.getMapModel().getMapFile();
187  if (tmpMapFile != null && tmpMapFile.equals(mapFile)) {
188  mapControl.acquire();
189  return mapControl;
190  }
191  }
192 
193  final MapReader<G, A> decoder = decodeMapFile(mapFile.getFile(), interactive);
194  return newMap(decoder.getGameObjects(), decoder.getMapArchObject(), mapFile, interactive);
195  }
196 
197  @NotNull
198  @Override
199  public MapControl<G, A, R> openMapFile(@NotNull final File file, final boolean interactive) throws IOException {
200  return openMapFile(pathManager.getMapFile(file), interactive);
201  }
202 
212  @NotNull
213  private MapReader<G, A> decodeMapFile(@NotNull final File file, final boolean interactive) throws IOException {
214  final MapReader<G, A> decoder;
215  //noinspection ErrorNotRethrown
216  try {
217  decoder = mapReaderFactory.newMapReader(file);
218  } catch (final OutOfMemoryError ex) {
219  throw new IOException("out of memory", ex);
220  }
221 
222  int outOfMapBoundsDeleted = 0;
223  final StringBuilder outOfMapBoundsObjects = new StringBuilder();
224  final int mapWidth = decoder.getMapArchObject().getMapSize().getWidth();
225  final int mapHeight = decoder.getMapArchObject().getMapSize().getHeight();
226  final Iterator<G> it = decoder.getGameObjects().iterator();
227  while (it.hasNext()) {
228  final GameObject<G, A, R> gameObject = it.next();
229  if (!gameObject.isInContainer()) {
230  final int minX = gameObject.getHead().getMapX() + gameObject.getMinX();
231  final int minY = gameObject.getHead().getMapY() + gameObject.getMinY();
232  final int maxX = gameObject.getHead().getMapX() + gameObject.getMaxX();
233  final int maxY = gameObject.getHead().getMapY() + gameObject.getMaxY();
234  if (minX < 0 || minY < 0 || maxX >= mapWidth || maxY >= mapHeight) {
235  it.remove();
236  if (gameObject.isHead()) {
237  outOfMapBoundsDeleted++;
238  if (outOfMapBoundsDeleted <= DELETED_OBJECTS_TO_REPORT) {
239  outOfMapBoundsObjects.append('\n');
240  outOfMapBoundsObjects.append(gameObject.getBestName());
241  outOfMapBoundsObjects.append(" at ");
242  outOfMapBoundsObjects.append(minX).append('/').append(minY);
243  if (minX != maxX || minY != maxY) {
244  outOfMapBoundsObjects.append("..");
245  outOfMapBoundsObjects.append(maxX).append('/').append(maxY);
246  }
247  } else if (outOfMapBoundsDeleted == DELETED_OBJECTS_TO_REPORT + 1) {
248  outOfMapBoundsObjects.append("\n...");
249  }
250  }
251  }
252  }
253  }
254  if (interactive && outOfMapBoundsDeleted > 0) {
255  fileControl.reportOutOfMapBoundsDeleted(file, outOfMapBoundsDeleted, outOfMapBoundsObjects);
256  }
257 
258  return decoder;
259  }
260 
261  @Override
262  public void setCurrentMap(@Nullable final MapControl<G, A, R> mapControl) {
263  if (currentMapControl == mapControl) {
264  return;
265  }
266 
267  currentMapControl = mapControl;
268 
269  for (final MapManagerListener<G, A, R> listener : listenerList.getListeners()) {
270  listener.currentMapChanged(currentMapControl);
271  }
272  }
273 
274  @NotNull
275  @Override
276  public List<MapControl<G, A, R>> getOpenedMaps() {
277  return Collections.unmodifiableList(mapControls);
278  }
279 
285  @Nullable
286  @Override
288  return currentMapControl;
289  }
290 
291  @Override
292  public void addMapManagerListener(@NotNull final MapManagerListener<G, A, R> listener) {
293  listenerList.add(listener);
294  }
295 
296  @Override
297  public void removeMapManagerListener(@NotNull final MapManagerListener<G, A, R> listener) {
298  listenerList.remove(listener);
299  }
300 
301  @Override
302  public void revert(@NotNull final MapControl<G, A, R> mapControl) throws IOException {
303  final MapFile mapFile = mapControl.getMapModel().getMapFile();
304  if (mapFile == null) {
305  return;
306  }
307 
308  final MapReader<G, A> decoder = decodeMapFile(mapFile.getFile(), false);
309  final MapModel<G, A, R> mapModel = mapControl.getMapModel();
310  final A mapArchObject = mapModel.getMapArchObject();
311  mapModel.beginTransaction("revert");
312  try {
313  mapArchObject.setMapSize(decoder.getMapArchObject().getMapSize());
314  mapArchObject.beginTransaction();
315  try {
316  mapArchObject.setState(decoder.getMapArchObject());
317  } finally {
318  mapArchObject.endTransaction();
319  }
320  mapModel.clearMap();
321  mapModel.addObjectListToMap(decoder.getGameObjects());
322  } finally {
323  mapModel.endTransaction();
324  }
325  mapModel.resetModified();
326  }
327 
328  @Override
329  public File getLocalMapDir() {
330  final MapControl<G, A, R> mapControl = currentMapControl;
331  if (mapControl == null) {
332  return projectSettings.getMapsDirectory();
333  }
334 
335  final MapFile mapFile = mapControl.getMapModel().getMapFile();
336  if (mapFile == null) {
337  return projectSettings.getMapsDirectory();
338  }
339 
340  return mapFile.getFile().getParentFile();
341  }
342 
343  @Override
344  public void release(@NotNull final MapControl<G, A, R> mapControl) {
345  mapControl.release();
346  if (mapControl.getUseCounter() <= 0) {
347  closeMap(mapControl);
348  }
349  }
350 
351 }
MapControl< G, A, R > newMap(@Nullable final List< G > objects, @NotNull final A mapArchObject, @Nullable final MapFile mapFile, final boolean interactive)
T getHead()
Return the head part of a multi-part object.
boolean isInContainer()
Check whether this GameObject is in a Container (in Gridarta sense, which means being in a MapSquare ...
void closeMap(@NotNull final MapControl< G, A, R > mapControl)
int getMaxX()
Determines the maximum x-coordinate of any part relative to the head part.
This class contains methods for converting relative map paths to absolute map paths and vice versa...
MapReader< G, A > decodeMapFile(@NotNull final File file, final boolean interactive)
Load a map file.
A MapModel reflects the data of a map.
Definition: MapModel.java:75
A MapManager manages all opened maps.
Definition: MapManager.java:37
T [] getListeners()
Returns an array of all the listeners.
Reading and writing of maps, handling of paths.
Settings that apply to a project.
final ProjectSettings projectSettings
The project settings instance.
MapReader< G, A > newMapReader(@NotNull File file)
Create a new MapReader instance.
A factory for creating MapReader instances.
MapFile getMapFile(@NotNull final AbsoluteMapPath mapPath)
Returns a MapFile instance from an AbsoluteMapPath.
final MapReaderFactory< G, A > mapReaderFactory
The gridarta objects factory instance.
AbstractMapManager(@NotNull final MapReaderFactory< G, A > mapReaderFactory, @NotNull final ProjectSettings projectSettings, @NotNull final FaceObjectProviders faceObjectProviders, @NotNull final PathManager pathManager)
Create a new map manager.
void release(@NotNull final MapControl< G, A, R > mapControl)
List< G > getGameObjects()
Return the decoded game objects.
MapModel< G, A, R > getMapModel()
Returns the map model.
Abstract base class for MapManager implementations.
Base package of all Gridarta classes.
Interface for classes that read map files.
Definition: MapReader.java:31
Reflects a game object (object on a map).
Definition: GameObject.java:36
boolean equals(@Nullable final Object obj)
Definition: MapFile.java:122
MapControl< G, A, R > openMapFile(@NotNull final File file, final boolean interactive)
Interface for listeners listening to MapManager changes.
int getMaxY()
Determines the maximum y-coordinate of any part relative to the head part.
void remove(@NotNull final T listener)
Removes a listener.
int getMinX()
Determines the minimum x-coordinate of any part relative to the head part.
void setFileControl(@NotNull final FileControl< G, A, R > fileControl)
GameObjects are the objects based on Archetypes found on maps.
void add(@NotNull final T listener)
Adds a listener.
MapControl< G, A, R > currentMapControl
The current top map we are working with.
FileControl< G, A, R > fileControl
The main control.
void revert(@NotNull final MapControl< G, A, R > mapControl)
final PathManager pathManager
The PathManager for converting File instances.
MapControl< G, A, R > getCurrentMap()
Returns the current top map we are working with.
void removeMapManagerListener(@NotNull final MapManagerListener< G, A, R > listener)
String getBestName()
Returns the name which is best appropriate to describe this GameObject.
boolean isHead()
Returns whether this object is a single-part object or the head of the multi-part object...
Type-safe version of EventListenerList.
Interface for listeners interested in FaceObjectProviders related events.
A getMapArchObject()
Returns the Map Arch Object with the meta information about the map.
Provider for faces of GameObjects and Archetypes.
abstract MapControl< G, A, R > createMapControl(@Nullable List< G > objects, @NotNull A mapArchObject, @Nullable MapFile mapFile)
Creates a new MapControl instance.
void setCurrentMap(@Nullable final MapControl< G, A, R > mapControl)
The face is the appearance of an object.
Currently nothing more than a marker interface for unification.
Definition: MapControl.java:35
MapFile getMapFile()
Returns the map file.
File getFile()
Returns a File for this map file.
Definition: MapFile.java:102
void reportOutOfMapBoundsDeleted(@NotNull File file, int outOfMapBoundsDeleted, @NotNull StringBuilder outOfMapBoundsObjects)
File getMapsDirectory()
Returns the default maps directory.
final EventListenerList2< MapManagerListener< G, A, R > > listenerList
The MapManagerListeners to inform of changes.
int getMinY()
Determines the minimum y-coordinate of any part relative to the head part.
final List< MapControl< G, A, R > > mapControls
All open maps.
void addMapManagerListener(@NotNull final MapManagerListener< G, A, R > listener)
static final int DELETED_OBJECTS_TO_REPORT
The maximum number of deleted game objects to report.
A getMapArchObject()
Returns the MapArchObject read from this MapFileDecode.
void facesReloaded()
Called whenever the faces have been reloaded.
The location of a map file with a map directory.
Definition: MapFile.java:31
MapControl< G, A, R > openMapFile(@NotNull final MapFile mapFile, final boolean interactive)