diff --git a/pom.xml b/pom.xml
index 2ab7333d..c7c5c178 100644
--- a/pom.xml
+++ b/pom.xml
@@ -128,6 +128,12 @@
R7
compile
+
+
+ org.jgrapht
+ jgrapht-core
+ 1.4.0
+
@@ -250,6 +256,8 @@
com.google.code.gson:gson
org.jdom:jdom2
org.mcstats.bukkit:metrics
+ org.jgrapht:jgrapht-core
+ org.jheaps:jheaps
@@ -258,4 +266,4 @@
-
+
\ No newline at end of file
diff --git a/src/main/java/org/mctourney/autoreferee/AutoRefMatch.java b/src/main/java/org/mctourney/autoreferee/AutoRefMatch.java
index de806c73..70f7e408 100644
--- a/src/main/java/org/mctourney/autoreferee/AutoRefMatch.java
+++ b/src/main/java/org/mctourney/autoreferee/AutoRefMatch.java
@@ -9,6 +9,7 @@
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
@@ -22,6 +23,8 @@
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -61,6 +64,7 @@
import org.bukkit.material.Redstone;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.scheduler.BukkitTask;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Scoreboard;
@@ -87,6 +91,7 @@
import org.mctourney.autoreferee.listeners.ZoneListener;
import org.mctourney.autoreferee.regions.AutoRefRegion;
import org.mctourney.autoreferee.regions.CuboidRegion;
+import org.mctourney.autoreferee.regions.RegionGraph;
import org.mctourney.autoreferee.util.ArmorPoints;
import org.mctourney.autoreferee.util.BlockData;
import org.mctourney.autoreferee.util.BookUtil;
@@ -930,6 +935,7 @@ public void run()
public AutoRefMatch(World world, boolean tmp, MatchStatus state)
{ this(world, tmp); setCurrentState(state); }
+ @SuppressWarnings("deprecation")
public AutoRefMatch(World world, boolean tmp)
{
setPrimaryWorld(world);
@@ -968,7 +974,29 @@ public AutoRefMatch(World world, boolean tmp)
messageReferees("match", getWorld().getName(), "init");
loadWorldConfiguration();
-
+
+ this.createRegionGraphs();
+
+ try {
+ this.loadRegionJSON();
+ } catch (FileNotFoundException | ClassCastException e) {
+ AutoReferee.log("Failed to load " + REGION_CFG_FILENAME);
+ e.printStackTrace();
+ }
+
+ /*if(AutoReferee.getInstance().isExperimentalMode()) { // experimental feature
+ this.initRegionGraphs();
+
+ graphTask =
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ computeRegionGraphs();
+ graphTask = null;
+ }
+ }.runTaskAsynchronously(AutoReferee.getInstance());
+ }*/
+
messageReferees("match", getWorld().getName(), "map", getMapName());
setCurrentState(MatchStatus.WAITING);
@@ -1181,7 +1209,7 @@ protected void clearScoreboardData(Scoreboard sb)
}
protected void loadScoreboardData()
- {
+ {
clearScoreboardData(scoreboard);
clearScoreboardData( infoboard);
@@ -2116,7 +2144,57 @@ public Set getRegions(AutoRefTeam team)
public boolean addRegion(AutoRefRegion reg)
{ return reg != null && !regions.contains(reg) && regions.add(reg); }
-
+
+ protected void createRegionGraphs() {
+ for ( AutoRefTeam t : this.getTeams() ) {
+ t.createRegionGraph();
+ }
+ }
+
+ protected void initRegionGraphs() {
+ for ( AutoRefTeam t : this.getTeams() ) {
+ t.initRegionGraph();
+ }
+ }
+
+ public void computeRegionGraphs() {
+ for ( AutoRefTeam t : this.getTeams() ) {
+ t.computeRegionGraph();
+ }
+ }
+
+ public boolean regionGraphsLoaded() {
+ return this.getTeams().stream()
+ .allMatch(t -> t.getRegGraph().loaded());
+ }
+
+ public static final String REGION_CFG_FILENAME = "regions.json";
+
+ public void loadRegionJSON( ) throws FileNotFoundException, ClassCastException {
+ if(!AutoReferee.getInstance().isExperimentalMode()) return;
+
+ File f = new File(this.getWorld().getWorldFolder(), REGION_CFG_FILENAME);
+ if(!f.exists()) return;
+
+ Gson gson = new Gson();
+ Reader reader = new FileReader(f);
+
+ Map data = gson.fromJson(reader, Map.class);
+
+ for( String key : data.keySet() ) {
+ AutoRefTeam team = this.getTeam(key);
+ if(team == null) continue;
+
+ Map data2 = (Map) data.get(key);
+
+ List restricted = (List) data2.get("restricted");
+
+ if(restricted != null) {
+ team.setRestrictionRegions( team.getRegGraph().fromInts( restricted ) );
+ }
+ }
+ }
+
/**
* A redstone mechanism necessary to start a match.
*
@@ -3624,4 +3702,4 @@ public void sendMatchInfo(CommandSender sender)
: String.format(ChatColor.GRAY + "The current match time is: " +
"%02d:%02d:%02d", timestamp/3600L, (timestamp/60L)%60L, timestamp%60L));
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/mctourney/autoreferee/AutoRefTeam.java b/src/main/java/org/mctourney/autoreferee/AutoRefTeam.java
index bffd80ae..ae2346e0 100644
--- a/src/main/java/org/mctourney/autoreferee/AutoRefTeam.java
+++ b/src/main/java/org/mctourney/autoreferee/AutoRefTeam.java
@@ -1,10 +1,12 @@
package org.mctourney.autoreferee;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
+import java.util.stream.Collectors;
import com.google.common.collect.Maps;
@@ -28,11 +30,13 @@
import org.mctourney.autoreferee.listeners.GoalsInventorySnapshot;
import org.mctourney.autoreferee.listeners.ZoneListener;
import org.mctourney.autoreferee.regions.AutoRefRegion;
+import org.mctourney.autoreferee.regions.AutoRefRegion.Flag;
+import org.mctourney.autoreferee.regions.RegionGraph;
import org.mctourney.autoreferee.util.BlockData;
import org.mctourney.autoreferee.util.Metadatable;
import org.mctourney.autoreferee.util.PlayerKit;
import org.mctourney.autoreferee.util.PlayerUtil;
-
+import org.mctourney.autoreferee.util.Vec3;
import org.apache.commons.lang.StringUtils;
import com.google.common.collect.Sets;
@@ -298,7 +302,18 @@ public Location getVictoryMonumentLocation()
*/
public Set getRegions()
{ return match.getRegions(this); }
-
+
+ /**
+ * Returns whether a particular Location
+ * is in team's lane or not
+ * @author char
+ *
+ * @param loc
+ * @return
+ */
+ public boolean containsLoc(Location loc)
+ { return this.getRegions().stream().anyMatch(reg -> reg.contains(loc)); }
+
public boolean addRegion(AutoRefRegion reg)
{
for (AutoRefRegion ereg : match.getRegions())
@@ -343,6 +358,102 @@ public Location getSpawnLocation()
return regs[random.nextInt(spawnRegions.size())].getLocation();
}
+ private RegionGraph graph;
+ private Set> restrictedRegions;
+
+ public RegionGraph getRegGraph() { return this.graph; }
+ public void setRestrictionRegions(Set> regions)
+ { this.restrictedRegions = regions; }
+
+ public boolean regGraphLoaded() { return this.getRegGraph().loaded(); }
+
+ public void initRegionGraph() {
+ createRegionGraph();
+ graph.computeGraph();
+ }
+
+ public void createRegionGraph() {
+ // this is an experimental feature
+ if(!AutoReferee.getInstance().isExperimentalMode()) return;
+
+ if(this.getMatch() == null) return;
+ World w = this.getMatch().getWorld();
+ if(w == null) return;
+
+ if(this.getRegions() == null) return;
+
+ graph = new RegionGraph(w, this.getRegions(), AutoReferee.getInstance().getLogger(), this)
+ .regions(this.getRegions());
+ /*.setDungeonOpenings( this.getRegions().stream()
+ .filter(r -> r.getFlags().contains(Flag.DUNGEON_BOUNDARY))
+ .collect(Collectors.toSet()));*/
+ }
+
+ // safe from async thread
+ public void computeRegionGraph() {
+ // this is an expiremental feature
+ if(!AutoReferee.getInstance().isExperimentalMode()) return;
+ if(this.getRegions() == null) return;
+
+ RegionGraph graph = this.getRegGraph();
+ if(graph == null) return;
+
+ if(this.getMatch() == null) return;
+ World w = this.getMatch().getWorld();
+ if(w == null) return;
+
+ graph.findConnectedRegions();
+ }
+
+ public Set unrestrictedPts() {
+ if(this.getRegions() == null) return null;
+
+ return this.getRegions().stream()
+ .filter(reg -> reg.getFlags().contains(Flag.NON_RESTRICTED))
+ .map(reg -> reg.getBoundingCuboid().getMinimumPoint().getBlock().getLocation())
+ .collect(Collectors.toSet());
+ }
+
+ /*public Set dungeonOpenings() {
+ if(this.getRegions() == null) return null;
+
+ return this.regions().stream()
+ .filter(r -> r.getFlags().contains(Flag.DUNGEON_BOUNDARY))
+ .collect(Collectors.toSet());
+ }*/
+
+ public Set restrictedRegion(Location l) {
+ if(this.getRegGraph() == null) return null;
+
+ if(this.restrictedRegions != null) {
+ return this.restrictedRegions.stream()
+ .filter(reg -> reg.contains( this.getRegGraph().vec(l) ))
+ .findAny().orElse(null);
+ }
+
+ if(!this.getRegGraph().loaded()) return null;
+ if(this.getRegGraph().connectedRegions().isEmpty()) return null;
+
+ return this.getRegGraph().connectedRegions().stream()
+ .filter(reg -> reg.contains( this.getRegGraph().vec(l) ))
+ .findAny().orElse(null);
+ }
+
+ public boolean isRestrictedLoc(Location l) {
+ boolean def = false;
+ if(this.getRegGraph() == null) return def;
+
+ if(this.restrictedRegions != null) {
+ return this.getRegGraph()
+ .isRestricted(l, this.restrictedRegions, this.getRegions());
+ }
+
+ if(!this.getRegGraph().loaded()) return def;
+ if(this.getRegGraph().connectedRegions().isEmpty()) return def;
+
+ return this.getRegGraph().isInRestrictedArea(l, this.unrestrictedPts());
+ }
+
private Set goals = Sets.newHashSet();
/**
@@ -488,7 +599,7 @@ public static AutoRefTeam create(AutoRefMatch match, String name, ChatColor colo
}
protected void setupScoreboard()
- {
+ {
String sbteam = this.getScoreboardTeamName();
// set team data on spectators' scoreboard
diff --git a/src/main/java/org/mctourney/autoreferee/AutoReferee.java b/src/main/java/org/mctourney/autoreferee/AutoReferee.java
index 5506a3f8..94ab0ce8 100644
--- a/src/main/java/org/mctourney/autoreferee/AutoReferee.java
+++ b/src/main/java/org/mctourney/autoreferee/AutoReferee.java
@@ -40,6 +40,7 @@
import org.mctourney.autoreferee.commands.PracticeCommands;
import org.mctourney.autoreferee.commands.ScoreboardCommands;
import org.mctourney.autoreferee.commands.SpectatorCommands;
+import org.mctourney.autoreferee.entity.EntityAREnderPearl;
import org.mctourney.autoreferee.listeners.CombatListener;
import org.mctourney.autoreferee.listeners.ObjectiveTracker;
import org.mctourney.autoreferee.listeners.ObjectiveTracer;
@@ -353,6 +354,16 @@ public void onEnable()
consoleLog = getConfig().getBoolean("console-log", true);
consoleLogInColor = getConfig().getBoolean("console-colors", true);
+ // experimental mode?
+ if(this.isExperimentalMode()) {
+ getLogger().info(this.getName() + " loaded in Experimental Mode. This is not intended for regular use!");
+
+ if(EntityAREnderPearl.patch())
+ getLogger().info("Successfully patched EntityEnderPearl!");
+ else
+ getLogger().severe("Failed to patch EntityEnderPearl! Please let a dev know about this.");
+ }
+
// setup the map library folder
AutoRefMap.getMapLibrary();
@@ -478,6 +489,16 @@ public void sendMessageSync(CommandSender recipient, String ...msgs)
catch (IllegalStateException ignored) { }
}
+ /**
+ * Get whether server is in experimental mode or not
+ * @author char
+ *
+ * @return Whether server is in experimental mode
+ */
+ public boolean isExperimentalMode() {
+ return this.getConfig().getBoolean("experimental-mode", false);
+ }
+
private class SyncMessageTask extends BukkitRunnable
{
private class RoutedMessage
diff --git a/src/main/java/org/mctourney/autoreferee/commands/ConfigurationCommands.java b/src/main/java/org/mctourney/autoreferee/commands/ConfigurationCommands.java
index f2599370..720209ac 100644
--- a/src/main/java/org/mctourney/autoreferee/commands/ConfigurationCommands.java
+++ b/src/main/java/org/mctourney/autoreferee/commands/ConfigurationCommands.java
@@ -1,14 +1,19 @@
package org.mctourney.autoreferee.commands;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
+import java.util.HashMap;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
+import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
+import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.NPC;
@@ -28,6 +33,7 @@
import org.mctourney.autoreferee.regions.CuboidRegion;
import org.mctourney.autoreferee.util.BlockData;
import org.mctourney.autoreferee.util.LocationUtil;
+import org.mctourney.autoreferee.util.Vec3;
import org.mctourney.autoreferee.util.commands.AutoRefCommand;
import org.mctourney.autoreferee.util.commands.AutoRefPermission;
import org.mctourney.autoreferee.util.commands.CommandHandler;
@@ -36,12 +42,14 @@
import org.apache.commons.lang.StringUtils;
import com.google.common.collect.Sets;
+import com.google.gson.Gson;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
+import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
import com.sk89q.worldedit.bukkit.selections.Selection;
@@ -524,4 +532,109 @@ public boolean scoreboardSave(CommandSender sender, AutoRefMatch match, String[]
return true;
}
+
+ @AutoRefCommand(name= {"autoref", "regions"}, argmin=1, argmax=1, options="flprs")
+ @AutoRefPermission(console=false, nodes={"autoreferee.configure"})
+ public boolean arRegions(CommandSender sender, AutoRefMatch match, String[] args, CommandLine options) {
+ if(!AutoReferee.getInstance().isExperimentalMode()) return false;
+ if ( match == null ) return false;
+ Player player = (Player) sender;
+
+ WorldEditPlugin worldEdit = AutoReferee.getWorldEdit();
+ if (worldEdit == null)
+ {
+ // world edit not installed
+ sender.sendMessage("This method requires WorldEdit installed and running.");
+ return true;
+ }
+
+ AutoRefTeam team = match.getTeam(args[0]);
+
+ if(options.hasOption('r')) {
+ player.sendMessage("Computing Region Graph. This may take a while...");
+ //match.cancelGraphTask();
+ team.initRegionGraph();
+ team.computeRegionGraph();
+ player.sendMessage( "Found " + team.getRegGraph().connectedRegions().size() + " regions.");
+ }
+
+ if(options.hasOption('p')) {
+ Selection sel = worldEdit.getSelection(player);
+ if(sel == null || !(sel instanceof CuboidSelection)) return true;
+
+ CuboidSelection csel = (CuboidSelection) sel;
+ if(!sel.getRegionSelector().isDefined()) return true;
+
+ com.sk89q.worldedit.regions.CuboidRegion reg;
+
+ try {
+ reg
+ = (com.sk89q.worldedit.regions.CuboidRegion) csel.getRegionSelector().getRegion();
+ } catch (IncompleteRegionException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ Location l0 = new Location(player.getWorld(), reg.getPos1().getBlockX(), reg.getPos1().getBlockY(), reg.getPos1().getBlockZ());
+ Location l1 = new Location(player.getWorld(), reg.getPos2().getBlockX(), reg.getPos2().getBlockY(), reg.getPos2().getBlockZ());
+
+ Set path = team.getRegGraph().shortestPath(l0, l1);
+
+ if(path == null) {
+ player.sendMessage("No path found");
+ return true;
+ }
+
+ path.forEach(b -> b.setType(Material.WOOL));
+ }
+
+ if(options.hasOption('f')) {
+ byte data = 1;
+
+ for( Set reg : team.getRegGraph().regionsWithoutPointsLoc( team.unrestrictedPts() ) ) {
+ for( Vec3 v : reg ) {
+ Block b = v.loc(player.getWorld()).getBlock();
+ b.setType(Material.WOOL);
+ b.setData(data);
+ }
+
+ data = (byte)(data + 1);
+ }
+ }
+
+ if(options.hasOption('s')) {
+ //System.out.println(team.getRegGraph().toJSON( team.unrestrictedPts() ));
+
+ HashMap json = new HashMap();
+
+ for(AutoRefTeam t : match.getTeams()) {
+ json.put( t.getName() , t.getRegGraph().toJSON( t.unrestrictedPts() ) );
+ }
+
+ Gson gson = new Gson();
+
+ File f = new File( match.getWorld().getWorldFolder(), AutoRefMatch.REGION_CFG_FILENAME );
+
+ try (FileWriter writer = new FileWriter( f )) {
+ gson.toJson(json, writer);
+ } catch(IOException e) {
+ player.sendMessage("Error writing file");
+ e.printStackTrace();
+ }
+
+ player.sendMessage("Successfully wrote " + AutoRefMatch.REGION_CFG_FILENAME + "!");
+ }
+
+ if(options.hasOption('l')) {
+ try {
+ match.loadRegionJSON();
+ player.sendMessage("Successfully loaded regions file");
+ } catch (FileNotFoundException | ClassCastException e) {
+ player.sendMessage("Failed loading regions file");
+ e.printStackTrace();
+ }
+ }
+
+ return true;
+ }
}
diff --git a/src/main/java/org/mctourney/autoreferee/entity/EntityAREnderPearl.java b/src/main/java/org/mctourney/autoreferee/entity/EntityAREnderPearl.java
new file mode 100644
index 00000000..a63ce04f
--- /dev/null
+++ b/src/main/java/org/mctourney/autoreferee/entity/EntityAREnderPearl.java
@@ -0,0 +1,112 @@
+package org.mctourney.autoreferee.entity;
+
+import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.bukkit.ChatColor;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.entity.Player;
+import org.mctourney.autoreferee.AutoRefMatch;
+import org.mctourney.autoreferee.AutoRefTeam;
+import org.mctourney.autoreferee.AutoReferee;
+import org.mctourney.autoreferee.regions.AutoRefRegion;
+
+import com.sk89q.worldedit.Location;
+
+import net.minecraft.server.v1_8_R3.EntityEnderPearl;
+import net.minecraft.server.v1_8_R3.EntityLiving;
+import net.minecraft.server.v1_8_R3.EntityPlayer;
+import net.minecraft.server.v1_8_R3.EntityTypes;
+import net.minecraft.server.v1_8_R3.World;
+
+public class EntityAREnderPearl extends EntityEnderPearl {
+ private AutoReferee ar;
+ private AutoRefTeam team;
+ private Player player;
+ private boolean canTravelThroughVoid = false;
+
+ public EntityAREnderPearl(Player player, AutoRefTeam team, boolean travelThroughVoid) {
+ super(((CraftWorld) ((CraftPlayer) player).getWorld()).getHandle(), ((CraftPlayer) player).getHandle());
+ this.team = team;
+ this.player = player;
+ this.canTravelThroughVoid = travelThroughVoid;
+ }
+
+ // Called when pearl updates
+ @Override
+ public void t_() {
+ if(!this.canTravelThroughVoid()) {
+ if(!this.regions().stream()
+ .anyMatch(r -> r.distanceToRegion( this.locX, this.locY, this.locZ ) <= 0.0 ) ) {
+ if(this.getAR() != null)
+ this.getAR().sendMessageSync(this.player(), ChatColor.RED + "Illegal pearl!");
+
+ this.die(); return;
+ }
+ }
+
+ super.t_();
+ }
+
+ public void spawn() {
+ this.getWorld().addEntity(this);
+ }
+
+ public static boolean PATCHED = false;
+
+ public static boolean patch() {
+ try {
+ registerEntity(EntityAREnderPearl.class, "ThrownAREnderpearl", 14);
+ PATCHED = true;
+
+ return true;
+ } catch (Exception ignored) {
+ return false;
+ }
+ }
+
+ private static void registerEntity(Class entityClass, String entityName, int entityId)
+ throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
+ //ReflectionUtil.get
+
+ for(String fieldname : Arrays.asList("c", "d", "f", "g")) {
+ Field field = EntityTypes.class.getDeclaredField(fieldname);
+ field.setAccessible(true);
+
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+
+ Map m = (Map) field.get(null);
+
+ switch(fieldname) {
+ case "c":
+ m.put(entityName, entityClass);
+ break;
+ case "d":
+ m.put(entityClass, entityName);
+ break;
+ case "f":
+ m.put(entityClass, entityId);
+ break;
+ case "g":
+ m.put(entityName, entityId);
+ break;
+ }
+ }
+ }
+
+ public EntityAREnderPearl setAR(AutoReferee ar) { this.ar = ar; return this; }
+
+ private AutoRefTeam team() { return this.team; }
+ private Set regions() { return this.team().getRegions(); }
+ private Player player() { return this.player; }
+ private boolean canTravelThroughVoid() { return this.canTravelThroughVoid; }
+ private AutoReferee getAR() { return this.ar; }
+}
diff --git a/src/main/java/org/mctourney/autoreferee/listeners/ZoneListener.java b/src/main/java/org/mctourney/autoreferee/listeners/ZoneListener.java
index 0a5958a3..1aac9584 100644
--- a/src/main/java/org/mctourney/autoreferee/listeners/ZoneListener.java
+++ b/src/main/java/org/mctourney/autoreferee/listeners/ZoneListener.java
@@ -3,12 +3,14 @@
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
+import org.bukkit.entity.EnderPearl;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Minecart;
@@ -26,6 +28,7 @@
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityTargetEvent;
+import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.player.PlayerBedEnterEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
@@ -35,6 +38,7 @@
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.weather.WeatherChangeEvent;
import org.bukkit.plugin.Plugin;
@@ -42,11 +46,13 @@
import org.mctourney.autoreferee.AutoRefPlayer;
import org.mctourney.autoreferee.AutoRefTeam;
import org.mctourney.autoreferee.AutoReferee;
+import org.mctourney.autoreferee.entity.EntityAREnderPearl;
import org.mctourney.autoreferee.AutoRefMatch.MatchStatus;
import org.mctourney.autoreferee.AutoRefMatch.Role;
import org.mctourney.autoreferee.goals.BlockGoal;
import org.mctourney.autoreferee.regions.AutoRefRegion;
import org.mctourney.autoreferee.regions.AutoRefRegion.Flag;
+import org.mctourney.autoreferee.regions.RegionGraph;
import org.mctourney.autoreferee.util.BlockData;
import org.mctourney.autoreferee.util.LocationUtil;
@@ -155,7 +161,9 @@ else if (exit != null && fallspeed < FREEFALL_THRESHOLD && onGround)
}
}
}
-
+
+ Set trackedPearls = Sets.newHashSet();
+
@EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true)
public void enderpearlThrow(ProjectileLaunchEvent event)
{
@@ -164,7 +172,7 @@ public void enderpearlThrow(ProjectileLaunchEvent event)
if (match == null || match.getCurrentState() == MatchStatus.NONE) return;
if (event.getEntityType() == EntityType.ENDER_PEARL)
- {
+ {
Player player = (Player) event.getEntity().getShooter();
AutoRefPlayer apl = match.getPlayer(player);
@@ -174,8 +182,43 @@ public void enderpearlThrow(ProjectileLaunchEvent event)
ChatColor.DARK_GRAY + " has thrown an enderpearl while out of bounds.";
for (Player ref : match.getReferees()) ref.sendMessage(msg);
}
+
+ // Expiremental: track ender pearls
+ if(!AutoReferee.getInstance().isExperimentalMode()) return;
+ if(match.getCurrentState() != MatchStatus.PLAYING) return;
+ if(!EntityAREnderPearl.PATCHED) return;
+
+ if(trackedPearls.contains(event.getEntity())) return;
+
+ if(apl != null && apl.getTeam() != null) {
+ AutoRefTeam team = apl.getTeam();
+
+ if(team.isRestrictedLoc(player.getLocation())) {
+ event.setCancelled(true);
+
+ EntityAREnderPearl pearl = new EntityAREnderPearl(player, team, false)
+ .setAR(AutoReferee.getInstance());
+
+ trackedPearls.add(pearl.getBukkitEntity());
+
+ pearl.spawn();
+ }
+ }
+ }
+ }
+
+ @EventHandler(priority=EventPriority.MONITOR)
+ public void enderpearlHit(ProjectileHitEvent event) {
+ if(!AutoReferee.getInstance().isExperimentalMode()) return;
+
+ if(event.getEntity() instanceof EnderPearl) {
+ trackedPearls.remove(event.getEntity());
}
}
+
+ private boolean doPreventDungeonExits() {
+ return AutoReferee.getInstance().isExperimentalMode();
+ }
public boolean validPlayer(Player player)
{
@@ -396,7 +439,11 @@ public void creatureSpawn(CreatureSpawnEvent event)
{ event.setCancelled(true); return; }
}
- public void teleportEvent(Player pl, Location fm, Location to, String reason)
+ public static enum TeleportType {
+ ENDER_PEARL, BED, VEHICLE, MISC
+ }
+
+ public void teleportEvent(Player pl, Location fm, Location to, String reason, TeleportType type)
{
// cannot compare locations in different worlds
if (fm.getWorld() != to.getWorld()) return;
@@ -412,7 +459,7 @@ public void teleportEvent(Player pl, Location fm, Location to, String reason)
AutoRefPlayer apl = match.getPlayer(pl);
if (apl == null) return;
apl.setLastTeleportLocation(to);
-
+
// generate message regarding the teleport event
String bedrock = BlockGoal.blockInRange(BlockData.BEDROCK, to, 5) != null ? " (near bedrock)" : "";
String message = apl.getDisplayName() + ChatColor.GRAY + " has teleported @ " +
@@ -421,6 +468,48 @@ public void teleportEvent(Player pl, Location fm, Location to, String reason)
boolean excludeStreamers = dsq <= LONG_TELE_DISTANCE * LONG_TELE_DISTANCE;
for (Player ref : match.getReferees(excludeStreamers)) ref.sendMessage(message);
}
+
+ public boolean teleportationLegal(Player pl, Location fm, Location to, AutoRefMatch match, TeleportType type) {
+ boolean def = true;
+ if(type == TeleportType.MISC) return def;
+ // This is an experimental feature
+ AutoReferee plugin = AutoReferee.getInstance();
+ if(!plugin.isExperimentalMode()) return def;
+ if(match.getCurrentState() != MatchStatus.PLAYING) return def;
+
+ AutoRefTeam t = match.getPlayerTeam(pl);
+ if(t == null) return def;
+
+ // Prevents teleporting through bedrock
+ if( RegionGraph.unbreakableInRange(to, 0) != null )
+ { return false; }
+
+ // Cannot teleport between regions
+ if(t.restrictedRegion(fm) != t.restrictedRegion(to)) {
+ return false;
+ }
+
+ // Cannot teleport outside of lane
+ if(!t.containsLoc( to )) {
+ return false;
+ }
+
+ if(type == TeleportType.BED || type == TeleportType.VEHICLE) {
+ //if(match.isPracticeMode()) return def;
+
+ // cannot enter bed/vehicle from void lane
+ if(!t.containsLoc( fm )) {
+ return false;
+ }
+
+ // cannot enter bed/vehicle from far distances
+ if(fm.distance(to) >= 1.5) {
+ return false;
+ }
+ }
+
+ return def;
+ }
@EventHandler(priority=EventPriority.MONITOR)
public void playerTeleport(PlayerTeleportEvent event)
@@ -448,8 +537,16 @@ public void playerTeleport(PlayerTeleportEvent event)
if (apl.getTeam().hasFlag(event.getTo(), Flag.NO_TELEPORT))
{ event.setCancelled(true); return; }
+ if(event.getCause() == TeleportCause.ENDER_PEARL) {
+ if(!teleportationLegal(event.getPlayer(), event.getFrom(), event.getTo(), match, TeleportType.ENDER_PEARL)) {
+ event.getPlayer().sendMessage(ChatColor.RED + "Illegal pearl!");
+ event.setCancelled(true); return;
+ }
+ }
+
String reason = "by " + event.getCause().name().toLowerCase().replaceAll("_", " ");
- teleportEvent(event.getPlayer(), event.getFrom(), event.getTo(), reason);
+ teleportEvent(event.getPlayer(), event.getFrom(), event.getTo(), reason,
+ event.getCause() == TeleportCause.ENDER_PEARL ? TeleportType.ENDER_PEARL : TeleportType.MISC );
break;
}
@@ -484,16 +581,39 @@ public void playerVehicleEnter(VehicleEnterEvent event)
for (Map.Entry, String> e : entityRenames.entrySet())
if (e.getKey().isAssignableFrom(clazz)) vehicleType = e.getValue();
- if (event.getEntered() instanceof Player)
+ AutoRefMatch match = plugin.getMatch(event.getVehicle().getWorld());
+ if (match == null || match.getCurrentState() == MatchStatus.NONE) return;
+
+ if (event.getEntered() instanceof Player) {
+ Player pl = (Player) event.getEntered(); Location fm = event.getEntered().getLocation();
+ Location to = event.getVehicle().getLocation(); TeleportType type = TeleportType.VEHICLE;
+
+ if(!teleportationLegal(pl, fm, to, match, type)) {
+ pl.sendMessage(ChatColor.RED + "Invalid teleport");
+ event.setCancelled(true); return;
+ }
+
teleportEvent((Player) event.getEntered(), event.getEntered().getLocation(),
- event.getVehicle().getLocation(), "into a " + vehicleType);
+ event.getVehicle().getLocation(), "into a " + vehicleType, TeleportType.VEHICLE);
+ }
}
@EventHandler(priority=EventPriority.MONITOR)
public void playerBedEnter(PlayerBedEnterEvent event)
{
+ AutoRefMatch match = plugin.getMatch(event.getPlayer().getWorld());
+ if (match == null || match.getCurrentState() == MatchStatus.NONE) return;
+
+ Player pl = event.getPlayer(); Location fm = event.getPlayer().getLocation();
+ Location to = event.getBed().getLocation(); TeleportType type = TeleportType.BED;
+
+ if(!teleportationLegal(pl, fm, to, match, type)) {
+ pl.sendMessage(ChatColor.RED + "Invalid teleport");
+ event.setCancelled(true); return;
+ }
+
teleportEvent(event.getPlayer(), event.getPlayer().getLocation(),
- event.getBed().getLocation(), "into a bed");
+ event.getBed().getLocation(), "into a bed", TeleportType.BED);
}
@EventHandler
diff --git a/src/main/java/org/mctourney/autoreferee/regions/AutoRefRegion.java b/src/main/java/org/mctourney/autoreferee/regions/AutoRefRegion.java
index 53c85d72..2ff1ade5 100644
--- a/src/main/java/org/mctourney/autoreferee/regions/AutoRefRegion.java
+++ b/src/main/java/org/mctourney/autoreferee/regions/AutoRefRegion.java
@@ -38,10 +38,12 @@ public static enum Flag
NO_ACCESS (1 << 4, false, 'a', "noaccess"),
NO_TELEPORT (1 << 5, false, 't', "noteleport"),
SPAWNERS_ONLY (1 << 6, false, 'w', "spawnersonly"),
- NO_FLOW (1 << 7, true, 'f', "noflow");
+ NO_FLOW (1 << 7, true, 'f', "noflow"),
+ DUNGEON_BOUNDARY (1 << 8, false, 'd', "dungeonboundary"),
+ NON_RESTRICTED (1 << 9, false, 'r', "nonrestricted");
// generated from above values
- public static final String OPTIONS = "abenstwf";
+ public static final String OPTIONS = "abdenrstwf";
private int value;
private String name;
@@ -102,7 +104,9 @@ public void announceRegion(AutoRefPlayer apl)
}
// these methods need to be implemented
- public abstract double distanceToRegion(Location loc);
+ // NOTE: distanceToRegion is accessed by an async
+ // task and so MUST NOT ACCESS THE BUKKIT API
+ public abstract double distanceToRegion(double x, double y, double z);
public abstract Location getRandomLocation(Random r);
public abstract CuboidRegion getBoundingCuboid();
@@ -164,8 +168,10 @@ public Location getGroundedCenter()
return loc;
}
+ public double distanceToRegion(Location loc) { return this.distanceToRegion(loc.getX(), loc.getY(), loc.getZ()); }
+
public boolean contains(Location loc)
- { return distanceToRegion(loc) <= 0.0; }
+ { return distanceToRegion(loc.getX(), loc.getY(), loc.getZ()) <= 0.0; }
public boolean containsBlock(Block block)
{ return this.contains(block.getLocation().clone().add(0.5, 0.5, 0.5)); }
diff --git a/src/main/java/org/mctourney/autoreferee/regions/CuboidRegion.java b/src/main/java/org/mctourney/autoreferee/regions/CuboidRegion.java
index 914b4bb5..b431bf01 100644
--- a/src/main/java/org/mctourney/autoreferee/regions/CuboidRegion.java
+++ b/src/main/java/org/mctourney/autoreferee/regions/CuboidRegion.java
@@ -89,13 +89,13 @@ public static CuboidRegion combine(CuboidRegion a, CuboidRegion b)
// distance from region, axially aligned (value less than actual distance, but
// appropriate for measurements on cuboid regions)
@Override
- public double distanceToRegion(Location v)
+ public double distanceToRegion(double x, double y, double z)
{
- // garbage-in, garbage-out
+ /*// garbage-in, garbage-out
if (v == null || v.getWorld() != world)
- return Double.POSITIVE_INFINITY;
+ return Double.POSITIVE_INFINITY;*/
- double x = v.getX(), y = v.getY(), z = v.getZ();
+ //double x = v.getX(), y = v.getY(), z = v.getZ();
Location mx = getMaximumPoint(), mn = getMinimumPoint();
// return maximum distance from this region
diff --git a/src/main/java/org/mctourney/autoreferee/regions/CylinderRegion.java b/src/main/java/org/mctourney/autoreferee/regions/CylinderRegion.java
index e91d991d..82f4c8f5 100644
--- a/src/main/java/org/mctourney/autoreferee/regions/CylinderRegion.java
+++ b/src/main/java/org/mctourney/autoreferee/regions/CylinderRegion.java
@@ -9,6 +9,7 @@
import org.mctourney.autoreferee.AutoRefMatch;
import org.mctourney.autoreferee.util.LocationUtil;
+import org.mctourney.autoreferee.util.MathUtil;
public class CylinderRegion extends AutoRefRegion
{
@@ -39,10 +40,12 @@ public CylinderRegion(World world, Element elt)
}
@Override
- public double distanceToRegion(Location loc)
+ public double distanceToRegion(double x0, double y0, double z0)
{
- return multimax(0, y - loc.getY(), loc.getY() - (y + h),
- new Location(world, x, loc.getY(), z).distance(loc) - r);
+ double dist = MathUtil.dist(x, y, z, x0, y, z0);
+
+ return multimax(0, y - y0, y0 - (y + h),
+ dist - r);
}
public Location getBase()
diff --git a/src/main/java/org/mctourney/autoreferee/regions/PointRegion.java b/src/main/java/org/mctourney/autoreferee/regions/PointRegion.java
index 60ffed54..508c9a8f 100644
--- a/src/main/java/org/mctourney/autoreferee/regions/PointRegion.java
+++ b/src/main/java/org/mctourney/autoreferee/regions/PointRegion.java
@@ -9,13 +9,16 @@
import org.mctourney.autoreferee.AutoRefMatch;
import org.mctourney.autoreferee.util.LocationUtil;
+import org.mctourney.autoreferee.util.MathUtil;
public class PointRegion extends AutoRefRegion
{
Location pos = null;
-
+ double x = 0; double y = 0; double z = 0;
+
public PointRegion(Location loc)
- { this.pos = loc.getBlock().getLocation(); this.yaw = (int)loc.getYaw(); }
+ { this.pos = loc.getBlock().getLocation(); this.yaw = (int)loc.getYaw();
+ this.x = pos.getX(); this.y = pos.getY(); this.z = pos.getY(); }
public PointRegion(AutoRefMatch match, Element e)
{ this(match.getWorld(), e); }
@@ -30,8 +33,8 @@ public Element toElement()
}
@Override
- public double distanceToRegion(Location loc)
- { return pos.distance(loc); }
+ public double distanceToRegion(double x0, double y0, double z0)
+ { return MathUtil.dist(x, y, z, x0, y0, z0); }
@Override
public Location getRandomLocation(Random r)
diff --git a/src/main/java/org/mctourney/autoreferee/regions/RegionGraph.java b/src/main/java/org/mctourney/autoreferee/regions/RegionGraph.java
new file mode 100644
index 00000000..cf8ca986
--- /dev/null
+++ b/src/main/java/org/mctourney/autoreferee/regions/RegionGraph.java
@@ -0,0 +1,357 @@
+package org.mctourney.autoreferee.regions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.jgrapht.Graph;
+import org.jgrapht.GraphPath;
+import org.jgrapht.alg.connectivity.ConnectivityInspector;
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath;
+import org.jgrapht.graph.DefaultEdge;
+import org.jgrapht.graph.SimpleGraph;
+import org.json.simple.JSONObject;
+import org.mctourney.autoreferee.AutoRefTeam;
+import org.mctourney.autoreferee.regions.AutoRefRegion.Flag;
+import org.mctourney.autoreferee.util.BlockData;
+import org.mctourney.autoreferee.util.Vec3;
+
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.sk89q.worldedit.Vector;
+
+public class RegionGraph {
+ public static final List UNBREAKABLE_BLOCKS
+ = Arrays.asList(Material.BEDROCK, Material.ENDER_PORTAL_FRAME, Material.BARRIER);
+
+ private Graph