Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 40 additions & 45 deletions src/main/java/opendota/Parse.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
import opendota.combatlogvisitors.TrackVisitor;
import opendota.combatlogvisitors.GreevilsGreedVisitor;
import opendota.combatlogvisitors.TrackVisitor.TrackStatus;
import opendota.processors.warding.OnWardExpired;
import opendota.processors.warding.OnWardKilled;
import opendota.processors.warding.OnWardPlaced;

public class Parse {

Expand Down Expand Up @@ -401,7 +404,6 @@ else if (cle.getType() == DOTA_COMBATLOG_TYPES.DOTA_COMBATLOG_XP) {

@OnEntityEntered
public void onEntityEntered(Context ctx, Entity e) {
processEntity(ctx, e, false);
if (e.getDtClass().getDtName().equals("CDOTAWearableItem")) {
Integer accountId = getEntityProperty(e, "m_iAccountID", null);
Integer itemDefinitionIndex = getEntityProperty(e, "m_iItemDefinitionIndex", null);
Expand All @@ -418,11 +420,6 @@ public void onEntityEntered(Context ctx, Entity e) {
}
}
}

@OnEntityLeft
public void onEntityLeft(Context ctx, Entity e) {
processEntity(ctx, e, true);
}

@UsesStringTable("EntityNames")
@UsesEntities
Expand Down Expand Up @@ -727,45 +724,43 @@ public <T> T getEntityProperty(Entity e, String property, Integer idx) {
}
}

public void processEntity(Context ctx, Entity e, boolean entityLeft)
{
//CDOTA_NPC_Observer_Ward
//CDOTA_NPC_Observer_Ward_TrueSight
//s1 "DT_DOTA_NPC_Observer_Ward"
//s1 "DT_DOTA_NPC_Observer_Ward_TrueSight"
boolean isObserver = e.getDtClass().getDtName().equals("CDOTA_NPC_Observer_Ward");
boolean isSentry = e.getDtClass().getDtName().equals("CDOTA_NPC_Observer_Ward_TrueSight");
if (isObserver || isSentry) {
//System.err.println(e);
Entry entry = new Entry(time);
Integer x = getEntityProperty(e, "CBodyComponent.m_cellX", null);
Integer y = getEntityProperty(e, "CBodyComponent.m_cellY", null);
Integer z = getEntityProperty(e, "CBodyComponent.m_cellZ", null);
Integer[] pos = {x, y};
entry.x = x;
entry.y = y;
entry.z = z;
if (entityLeft)
{
entry.type = isObserver ? "obs_left" : "sen_left";
}
else
{
entry.type = isObserver ? "obs" : "sen";
}
entry.key = Arrays.toString(pos);
entry.entityleft = entityLeft;
entry.ehandle = e.getHandle();
//System.err.println(entry.key);
Integer owner = getEntityProperty(e, "m_hOwnerEntity", null);
Entity ownerEntity = ctx.getProcessor(Entities.class).getByHandle(owner);
entry.slot = ownerEntity != null ? (Integer) getEntityProperty(ownerEntity, "m_iPlayerID", null) : ward_ehandle_to_slot.get(entry.ehandle);
if (entry.slot != null && !ward_ehandle_to_slot.containsKey(entry.ehandle)) {
ward_ehandle_to_slot.put(entry.ehandle, entry.slot);
}
//2/3 radiant/dire
//entry.team = e.getProperty("m_iTeamNum");
output(entry);
@OnWardKilled
public void onWardKilled(Context ctx, Entity e, String killerHeroName) {
Entry wardEntry = buildWardEntry(ctx, e);
wardEntry.attackername = killerHeroName;
output(wardEntry);
}

@OnWardExpired
@OnWardPlaced
public void onWardExistenceChanged(Context ctx, Entity e) {
output(buildWardEntry(ctx, e));
}

private Entry buildWardEntry(Context ctx, Entity e) {
Entry entry = new Entry(time);
boolean isObserver = !e.getDtClass().getDtName().contains("TrueSight");
Integer x = getEntityProperty(e, "CBodyComponent.m_cellX", null);
Integer y = getEntityProperty(e, "CBodyComponent.m_cellY", null);
Integer z = getEntityProperty(e, "CBodyComponent.m_cellZ", null);
Integer life_state = getEntityProperty(e, "m_lifeState", null);
Integer[] pos = {x, y};
entry.x = x;
entry.y = y;
entry.z = z;
entry.type = isObserver ? "obs" : "sen";
entry.entityleft = life_state == 1;
entry.key = Arrays.toString(pos);
entry.ehandle = e.getHandle();

if (entry.entityleft) {
entry.type += "_left";
}

Integer owner = getEntityProperty(e, "m_hOwnerEntity", null);
Entity ownerEntity = ctx.getProcessor(Entities.class).getByHandle(owner);
entry.slot = ownerEntity != null ? (Integer) getEntityProperty(ownerEntity, "m_iPlayerID", null) : null;

return entry;
}
}
19 changes: 19 additions & 0 deletions src/main/java/opendota/processors/warding/OnWardExpired.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package opendota.processors.warding;

import java.lang.annotation.Annotation;

import skadistats.clarity.event.UsagePointMarker;
import skadistats.clarity.event.UsagePointType;
import skadistats.clarity.model.Entity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@UsagePointMarker(value = UsagePointType.EVENT_LISTENER, parameterClasses = { Entity.class })
public @interface OnWardExpired {
}

19 changes: 19 additions & 0 deletions src/main/java/opendota/processors/warding/OnWardKilled.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package opendota.processors.warding;

import java.lang.annotation.Annotation;

import skadistats.clarity.event.UsagePointMarker;
import skadistats.clarity.event.UsagePointType;
import skadistats.clarity.model.Entity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@UsagePointMarker(value = UsagePointType.EVENT_LISTENER, parameterClasses = { Entity.class, String.class })
public @interface OnWardKilled {
}

19 changes: 19 additions & 0 deletions src/main/java/opendota/processors/warding/OnWardPlaced.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package opendota.processors.warding;

import java.lang.annotation.Annotation;

import skadistats.clarity.event.UsagePointMarker;
import skadistats.clarity.event.UsagePointType;
import skadistats.clarity.model.Entity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@UsagePointMarker(value = UsagePointType.EVENT_LISTENER, parameterClasses = { Entity.class })
public @interface OnWardPlaced {
}

202 changes: 202 additions & 0 deletions src/main/java/opendota/processors/warding/Wards.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package opendota.processors.warding;

import java.util.ArrayDeque;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import skadistats.clarity.event.Event;
import skadistats.clarity.event.EventListener;
import skadistats.clarity.event.Initializer;
import skadistats.clarity.event.Provides;
import skadistats.clarity.model.Entity;
import skadistats.clarity.model.FieldPath;
import skadistats.clarity.model.CombatLogEntry;
import skadistats.clarity.processor.entities.OnEntityCreated;
import skadistats.clarity.processor.entities.OnEntityDeleted;
import skadistats.clarity.processor.entities.OnEntityUpdated;
import skadistats.clarity.processor.entities.UsesEntities;
import skadistats.clarity.processor.gameevents.OnCombatLogEntry;
import skadistats.clarity.processor.reader.OnTickEnd;
import skadistats.clarity.processor.runner.Context;
import skadistats.clarity.wire.common.proto.DotaUserMessages;

/**
* @author micaelbergeron
*/
@UsesEntities
@Provides({ OnWardKilled.class, OnWardExpired.class, OnWardPlaced.class })
public class Wards {

private static final Map<String, String> WARDS_TARGET_NAME_BY_DT_CLASS;
private static final Set<String> WARDS_DT_CLASSES;
private static final Set<String> WARDS_TARGET_NAMES;

static {
final String TARGET_NAME_OBSERVER = "npc_dota_observer_wards";
final String TARGET_NAME_SENTRY = "npc_dota_sentry_wards";
HashMap<String, String> target_by_dtclass = new HashMap<>(4);

target_by_dtclass.put("DT_DOTA_NPC_Observer_Ward", TARGET_NAME_OBSERVER);
target_by_dtclass.put("CDOTA_NPC_Observer_Ward", TARGET_NAME_OBSERVER);
target_by_dtclass.put("DT_DOTA_NPC_Observer_Ward_TrueSight", TARGET_NAME_SENTRY);
target_by_dtclass.put("CDOTA_NPC_Observer_Ward_TrueSight", TARGET_NAME_SENTRY);

WARDS_TARGET_NAME_BY_DT_CLASS = Collections.unmodifiableMap(target_by_dtclass);
WARDS_DT_CLASSES = Collections.unmodifiableSet(target_by_dtclass.keySet());
WARDS_TARGET_NAMES = Collections.unmodifiableSet(new HashSet<>(target_by_dtclass.values()));
}

private final Map<Integer, FieldPath> lifeStatePaths = new HashMap<>();
private final Map<Integer, Integer> currentLifeState = new HashMap<>();

private final Map<String, Queue<String>> wardKillersByWardClass = new HashMap<>();
private Queue<ProcessEntityCommand> toProcess = new ArrayDeque<>();

private Event<OnWardKilled> evKilled;
private Event<OnWardExpired> evExpired;
private Event<OnWardPlaced> evPlaced;

private class ProcessEntityCommand {

private final Entity entity;
private final FieldPath fieldPath;

public ProcessEntityCommand(Entity e, FieldPath p) {
entity = e;
fieldPath = p;
}
}

@Initializer(OnWardKilled.class)
public void initOnWardKilled(final Context ctx, final EventListener<OnWardKilled> listener) {
evKilled = ctx.createEvent(OnWardKilled.class, Entity.class, String.class);
}

@Initializer(OnWardExpired.class)
public void initOnWardExpired(final Context ctx, final EventListener<OnWardExpired> listener) {
evExpired = ctx.createEvent(OnWardExpired.class, Entity.class);
}

@Initializer(OnWardPlaced.class)
public void initOnWardPlaced(final Context ctx, final EventListener<OnWardPlaced> listener) {
evPlaced = ctx.createEvent(OnWardPlaced.class, Entity.class);
}

public Wards() {
WARDS_TARGET_NAMES.forEach((cls) -> {
wardKillersByWardClass.put(cls, new ArrayDeque<>());
});
}

@OnEntityCreated
public void onCreated(Context ctx, Entity e) {
if (!isWard(e)) return;

FieldPath lifeStatePath;

clearCachedState(e);
ensureFieldPathForEntityInitialized(e);
if ((lifeStatePath = getFieldPathForEntity(e)) != null) {
processLifeStateChange(e, lifeStatePath);
}
}

@OnEntityUpdated
public void onUpdated(Context ctx, Entity e, FieldPath[] fieldPaths, int num) {
FieldPath p;
if ((p = getFieldPathForEntity(e)) != null) {
for (int i = 0; i < num; i++) {
if (fieldPaths[i].equals(p)) {
toProcess.add(new ProcessEntityCommand(e, p));
break;
}
}
}
}

@OnEntityDeleted
public void onDeleted(Context ctx, Entity e) {
clearCachedState(e);
}

@OnCombatLogEntry
public void onCombatLogEntry(Context ctx, CombatLogEntry entry) {
if (!isWardDeath(entry)) return;

String killer;
if ((killer = entry.getDamageSourceName()) != null) {
wardKillersByWardClass.get(entry.getTargetName()).add(killer);
}
}

@OnTickEnd
public void onTickEnd(Context ctx, boolean synthetic) {
if (!synthetic) return;

ProcessEntityCommand cmd;
while ((cmd = toProcess.poll()) != null) {
processLifeStateChange(cmd.entity, cmd.fieldPath);
}
}

private FieldPath getFieldPathForEntity(Entity e) {
return lifeStatePaths.get(e.getDtClass().getClassId());
}

private void clearCachedState(Entity e) {
currentLifeState.remove(e.getIndex());
}

private void ensureFieldPathForEntityInitialized(Entity e) {
Integer cid = e.getDtClass().getClassId();
if (!lifeStatePaths.containsKey(cid)) {
lifeStatePaths.put(cid, e.getDtClass().getFieldPathForName("m_lifeState"));
}
}

private boolean isWard(Entity e) {
return WARDS_DT_CLASSES.contains(e.getDtClass().getDtName());
}

private boolean isWardDeath(CombatLogEntry e) {
return e.getType().equals(DotaUserMessages.DOTA_COMBATLOG_TYPES.DOTA_COMBATLOG_DEATH)
&& WARDS_TARGET_NAMES.contains(e.getTargetName());
}

public void processLifeStateChange(Entity e, FieldPath p) {
int oldState = currentLifeState.containsKey(e.getIndex()) ? currentLifeState.get(e.getIndex()) : 2;
int newState = e.getPropertyForFieldPath(p);
if (oldState != newState) {
switch(newState) {
case 0:
if (evPlaced != null) {
evPlaced.raise(e);
}
break;
case 1:
String killer;
if ((killer = wardKillersByWardClass.get(getWardTargetName(e.getDtClass().getDtName())).poll()) != null) {
if (evKilled != null) {
evKilled.raise(e, killer);
}
} else {
if (evExpired != null) {
evExpired.raise(e);
}
}
break;
}
}

currentLifeState.put(e.getIndex(), newState);
}

private String getWardTargetName(String ward_dtclass_name) {
return WARDS_TARGET_NAME_BY_DT_CLASS.get(ward_dtclass_name);
}
}