Skip to content

Instantly share code, notes, and snippets.

@DaanVanYperen
Created May 12, 2019 20:11
Show Gist options
  • Save DaanVanYperen/e2ab0143f2b92e49d064c913c3fc6a02 to your computer and use it in GitHub Desktop.
Save DaanVanYperen/e2ab0143f2b92e49d064c913c3fc6a02 to your computer and use it in GitHub Desktop.
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