package com.comphenix.xp.history;
import java.lang.reflect.Field;
import java.util.Arrays;
import org.bukkit.Location;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.xp.Debugger;
import uk.co.oliwali.HawkEye.DataType;
import uk.co.oliwali.HawkEye.SearchParser;
import uk.co.oliwali.HawkEye.database.SearchQuery.SearchDir;
import uk.co.oliwali.HawkEye.entry.BlockChangeEntry;
import uk.co.oliwali.HawkEye.entry.DataEntry;
import uk.co.oliwali.HawkEye.util.BlockUtil;
import uk.co.oliwali.HawkEye.util.HawkEyeAPI;
public class HawkeyeService implements HistoryService {
public static final String NAME = "HAWKEYE";
private Debugger debugger;
private volatile boolean searching;
// The result
private DataEntry searchResult;
// Lock token
private final Object lock = new Object();
@Override
public String getServiceName() {
return NAME;
}
public HawkeyeService(Debugger debugger) {
this.debugger = debugger;
}
/**
* Determines if the block service can be loaded.
* @return TRUE if it can be loaded, FALSE otherwise.
*/
public static boolean exists(PluginManager manager) {
Plugin hawkeye = manager.getPlugin("HawkEye");
return hawkeye != null;
}
@Override
public Boolean hasPlayerHistory(Location blockLocation) throws HistoryException {
int blockID = blockLocation.getBlock().getTypeId();
// Async lock
synchronized (lock) {
while (searching) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new HistoryException("Waiting interrupted.", e);
}
}
SearchParser searchParser = new SearchParser();
searchParser.loc = blockLocation.toVector();
searchParser.actions = Arrays.asList(DataType.BLOCK_PLACE, DataType.BLOCK_FORM);
searchParser.radius = 0;
searchParser.worlds = new String[] {blockLocation.getWorld().getName()};
searching = true;
// Search synchronously
// TODO: Make the HawkEye call async.
HawkEyeAPI.performSearch(new HawkeyeCallback(debugger, this),
searchParser, SearchDir.DESC);
// Wait for it to be done
while (searching) {
try {
lock.wait();
} catch (InterruptedException e) {
throw new HistoryException("Waiting interrupted.", e);
}
}
}
// Make sure the ID corresponds
if (searchResult instanceof BlockChangeEntry) {
BlockChangeEntry changeData = (BlockChangeEntry) searchResult;
int to = getToField(changeData);
debugger.printDebug(this, "To field: %s", to);
return to == blockID;
} else {
// We don't know
return null;
}
}
private int getToField(BlockChangeEntry changeData) throws HistoryException {
try {
Field toField = changeData.getClass().getDeclaredField("to");
toField.setAccessible(true);
String toValue = (String) toField.get(changeData);
return BlockUtil.getIdFromString(toValue);
// A ton of potential problems
} catch (SecurityException e1) {
throw new HistoryException("Security violation: Cannot access private member.");
} catch (NoSuchFieldException e1) {
throw new HistoryException("Hawkeye class structure has changed. No field 'to' exists.");
} catch (IllegalArgumentException e) {
throw new HistoryException("Illegal argument.");
} catch (IllegalAccessException e) {
throw new HistoryException("Cannot use reflection. Illegal access.");
}
}
public boolean isSearching() {
return searching;
}
public void setSearching(boolean searching) {
this.searching = searching;
}
public DataEntry getSearchResult() {
return searchResult;
}
public void setSearchResult(DataEntry searchResult) {
this.searchResult = searchResult;
}
public Object getLock() {
return lock;
}
@Override
public LookupSpeed getLookupSpeed() {
return LookupSpeed.SLOW;
}
@Override
public boolean hasFalsePositives() {
return false;
}
@Override
public boolean hasFalseNegatives() {
return false;
}
}