Skip to content

Commit

Permalink
Add possibility to extend the unmarshalling of mapobjects
Browse files Browse the repository at this point in the history
If specified, the MapObjectLoader now calls the private "afterTmxUnmarshal(IMapObject)" method on the entity after is has been loaded by the IMapObject. This enables us to implement additional logic after the attributes have been unmarshalled and set via reflection.
  • Loading branch information
steffen-wilke committed Nov 14, 2020
1 parent 57d0394 commit 2a0e41d
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/de/gurkenlabs/litiengine/environment/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,7 @@ public Collection<IEntity> load(final IMapObject mapObject) {
if (loader != null) {
Collection<IEntity> loadedEntities;
loadedEntities = loader.load(this, mapObject);
loader.afterLoad(loadedEntities, mapObject);
for (IEntity entity : loadedEntities) {
if (entity != null) {

Expand Down
10 changes: 9 additions & 1 deletion src/de/gurkenlabs/litiengine/environment/IMapObjectLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@
* <br>
* The engine provides default implementations for all predefined {@code Entity} types (e.g. {@code Prop or Creature}).
* You can inherit/call the abstract {@code MapObjectLoader} implementation to make use of predefined loading logic.
*
*
* @see Environment#registerMapObjectLoader(IMapObjectLoader)
* @see MapObjectLoader#loadDefaultProperties(IEntity, IMapObject)
*/
public interface IMapObjectLoader {
String getMapObjectType();

Collection<IEntity> load(Environment environment, IMapObject mapObject);

/**
* This method is called externally on the loader instance after the entities have been loaded.
*
* @param entities The loaded entities.
* @param mapObject The map object by which the entities have been loaded.
*/
void afterLoad(Collection<IEntity> entities, IMapObject mapObject);
}
50 changes: 41 additions & 9 deletions src/de/gurkenlabs/litiengine/environment/MapObjectLoader.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package de.gurkenlabs.litiengine.environment;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -40,12 +43,9 @@ public String getMapObjectType() {
* <li>tags</li>
* </ul>
* Also, this supports predefined {@code CustomMapObjectProperties}. It loads the specified custom properties via reflection.
*
* @param entity
* The entity instance that will be initialized.
* @param mapObject
* The mapObject that provides the static information for the new entity.
*
*
* @param entity The entity instance that will be initialized.
* @param mapObject The mapObject that provides the static information for the new entity.
* @see TmxProperty
*/
public static void loadDefaultProperties(IEntity entity, IMapObject mapObject) {
Expand Down Expand Up @@ -86,11 +86,15 @@ public static void loadDefaultProperties(IEntity entity, IMapObject mapObject) {
});
}


public void afterLoad(Collection<IEntity> entities, IMapObject mapObject) {
for (IEntity entity : entities) {
callAfterTmxUnmarshal(entity, mapObject);
}
}

protected boolean isMatchingType(IMapObject mapObject) {
if (!mapObject.getType().equalsIgnoreCase(this.getMapObjectType())) {
log.log(Level.SEVERE, "Cannot load a mapobject of the type [{0}] with a loader of type [{1}].", new Object[] { mapObject.getType(), this.getClass() });
log.log(Level.SEVERE, "Cannot load a mapobject of the type [{0}] with a loader of type [{1}].", new Object[]{mapObject.getType(), this.getClass()});
return false;
}

Expand All @@ -110,7 +114,35 @@ private static void loadCustomMapObjectProperties(IEntity entity, IMapObject map
continue;
}

ReflectionUtilities.setFieldValue(field.getDeclaringClass(), entity, field.getName(), value);
if (!ReflectionUtilities.setFieldValue(field.getDeclaringClass(), entity, field.getName(), value)) {
log.warning("entity #" + entity.getMapId() + ": value \"" + value + "\" for custom property " + property.name() + " could not be set.");
}
}
}

/**
* If present, this method calls the private {@code afterTmxUnmarshal(IMapObject)} method on the specified entity.
*
* <p>
* The {@link MapObjectLoader} implementation provides the possibility to extend the unmarshalling of the IMapObject within the entity implementation by
* implementing a private method with the name "afterTmxUnmarshal" accepting a parameter of type {@link IMapObject}. This method is called, after the
* loading has been finished and the entities have been instantiated.
* </p>
*
* @param entity The entity instance to call the "afterTmxUnmarshal" method on.
* @param mapObject The map object to pass to the entity instance when invoking the "afterTmxUnmarshal" method.
*/
private void callAfterTmxUnmarshal(IEntity entity, IMapObject mapObject) {
Method afterTmxUnmarshal = ReflectionUtilities.getMethod("afterTmxUnmarshal", entity.getClass(), IMapObject.class);
if (afterTmxUnmarshal == null) {
return;
}

afterTmxUnmarshal.setAccessible(true);
try {
afterTmxUnmarshal.invoke(entity, mapObject);
} catch (IllegalAccessException | InvocationTargetException e) {
log.log(Level.SEVERE, "Could not invoke afterTmxUnmarshal method on type [{0}]: {1}", new Object[]{entity.getClass().getName(), e.getMessage()});
}
}
}

0 comments on commit 2a0e41d

Please sign in to comment.