Created
May 12, 2019 20:11
-
-
Save DaanVanYperen/e2ab0143f2b92e49d064c913c3fc6a02 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.artemis; | |
import java.util.HashMap; | |
/** | |
* @author Daan van Yperen | |
*/ | |
public class DebugWorld extends World { | |
final DebugLogStrategy logStrategy; | |
final HashMap<Integer, DebugComponent> debugComponents; | |
public DebugWorld(WorldConfiguration configuration, DebugLogStrategy logStrategy, HashMap<Integer, DebugComponent> debugComponents) { | |
super(injectDebugSystems(configuration, logStrategy, this.debugComponents = debugComponents)); | |
this.debugComponents = debugComponents; | |
this.logStrategy = logStrategy; | |
} | |
private static WorldConfiguration injectDebugSystems(WorldConfiguration configuration, DebugLogStrategy logStrategy, HashMap<Integer, DebugComponent> debugComponents) { | |
configuration.systems.set(WorldConfiguration.ENTITY_MANAGER_IDX, new DebugEntityManager(configuration.expectedEntityCount(), logStrategy, debugComponents)); | |
return configuration; | |
} | |
public DebugWorld(WorldConfiguration configuration, DebugLogStrategy logStrategy) { | |
this(configuration, logStrategy, new HashMap<>()); | |
} | |
public DebugWorld(WorldConfiguration build) { | |
this(build, new DefaultDebugLogStrategy(), new HashMap<>()); | |
} | |
public DebugWorld() { | |
this(new WorldConfiguration(), new DefaultDebugLogStrategy(), new HashMap<>()); | |
} | |
@Override | |
public void delete(int entityId) { | |
DebugComponent debugComponent = debugComponents.get(entityId); | |
if (debugComponent.deletionStacktrace != null) { | |
logStrategy.log("*********************"); | |
logStrategy.log(new MutationStacktrace(MutationStacktrace.Type.ERROR_ATTEMPT_TO_DELETE_DELETED_ENTITY, entityId, debugComponent.name, Thread.currentThread().getStackTrace())); | |
logStrategy.log("Cause (Already deleted at):"); | |
logStrategy.log(debugComponent.deletionStacktrace); | |
logStrategy.log("*********************"); | |
} | |
debugComponent.deletionStacktrace = new MutationStacktrace(MutationStacktrace.Type.DELETE, entityId, debugComponent.name, Thread.currentThread().getStackTrace()); | |
logStrategy.log(debugComponent.deletionStacktrace); | |
super.delete(entityId); | |
} | |
public interface DebugLogStrategy { | |
void log(String s); | |
void log(MutationStacktrace site); | |
} | |
public static class DefaultDebugLogStrategy implements DebugLogStrategy { | |
private final String[] packages; | |
public DefaultDebugLogStrategy(String... packages) { | |
this.packages = packages; | |
} | |
@Override | |
public void log(String s) { | |
System.out.println(s); | |
} | |
@Override | |
public void log(MutationStacktrace site) { | |
boolean matched = false; | |
String prefix = site.entityDebugName + "(" + site.entityId + ") " + site.type; | |
for (StackTraceElement stackTraceElement : site.stacktrace) { | |
String callsiteSummary = stackTraceElement.toString(); | |
for (String includePackage : packages) { | |
if (callsiteSummary.contains(includePackage)) { | |
// we found a first match! keep reporting as long as we are inside the reported package scope | |
// because we might be inside utility logic and more elements are needed to provide context. | |
if (!matched) { | |
// first line | |
log(prefix + " @ " + callsiteSummary); | |
} else { | |
// beyond first line. | |
log(" .. " + callsiteSummary); | |
} | |
matched = true; | |
} else if (matched) { | |
// no longer matching packages, so done. | |
return; | |
} | |
} | |
} | |
if (!matched) { | |
log(prefix + " @ excluded package."); | |
} | |
} | |
} | |
public static class DebugEntityManager extends EntityManager { | |
private final DebugLogStrategy logStrategy; | |
private final HashMap<Integer, DebugComponent> debugComponents; | |
protected DebugEntityManager(int initialContainerSize, DebugLogStrategy logStrategy, HashMap<Integer, DebugComponent> debugComponents) { | |
super(initialContainerSize); | |
this.logStrategy = logStrategy; | |
this.debugComponents = debugComponents; | |
} | |
protected Entity createEntityInstance() { | |
Entity result = super.createEntityInstance(); | |
beginTracking(result.id); | |
return result; | |
} | |
@Override | |
protected int create() { | |
int entityId = super.create(); | |
beginTracking(entityId); | |
return entityId; | |
} | |
@Override | |
protected Entity getEntity(int entityId) { | |
DebugComponent debugComponent = debugComponents.get(entityId); | |
if (debugComponent != null && debugComponent.deletionStacktrace != null) { | |
logStrategy.log("*********************"); | |
logStrategy.log(new MutationStacktrace(MutationStacktrace.Type.ERROR_ATTEMPT_TO_ACCESS_DELETED_ENTITY, entityId, debugComponent.name, Thread.currentThread().getStackTrace())); | |
logStrategy.log("Cause (Deleted at):"); | |
logStrategy.log(debugComponent.deletionStacktrace); | |
logStrategy.log("*********************"); | |
} | |
return super.getEntity(entityId); | |
} | |
private void beginTracking(int entityId) { | |
// create component for user debug sake. | |
debugComponents.remove(entityId); | |
DebugComponent debugComponent = getEntity(entityId).edit().create(DebugComponent.class); | |
debugComponent.creationStacktrace = new MutationStacktrace(MutationStacktrace.Type.CREATE, entityId, debugComponent.name, Thread.currentThread().getStackTrace()); | |
// the world lifecycle is delayed such that we need our own way to resolve debug components in case of create/destroy within the same system; | |
debugComponents.put(entityId, debugComponent); | |
logStrategy.log(debugComponent.creationStacktrace); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment