package me.km.plots; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.UUID; import java.util.function.Predicate; import java.util.stream.Collectors; public class PlotMap { public static class Plot { private Plot previous = null; private Plot next = null; private final int id; private final int minX; private final int minY; private final int minZ; private final int maxX; private final int maxY; private final int maxZ; private final ArrayList owners = new ArrayList<>(1); private int flags = 0; private String name = ""; public Plot(int id, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { minX = Math.min(32000, Math.max(-32000, minX)); minY = Math.min(32000, Math.max(-32000, minY)); minZ = Math.min(32000, Math.max(-32000, minZ)); maxX = Math.min(32000, Math.max(-32000, maxX)); maxY = Math.min(32000, Math.max(-32000, maxY)); maxZ = Math.min(32000, Math.max(-32000, maxZ)); this.id = id; if(minX < maxX) { this.minX = minX; this.maxX = maxX; } else { this.minX = maxX; this.maxX = minX; } if(minY < maxY) { this.minY = minY; this.maxY = maxY; } else { this.minY = maxY; this.maxY = minY; } if(minZ < maxZ) { this.minZ = minZ; this.maxZ = maxZ; } else { this.minZ = maxZ; this.maxZ = minZ; } } private boolean isInside(int x, int y, int z) { return minX <= x && x <= maxX && minY <= y && y <= maxY && minZ <= z && z <= maxZ; } public int getMinX() { return minX; } public int getMinY() { return minY; } public int getMinZ() { return minZ; } public int getMaxX() { return maxX; } public int getMaxY() { return maxY; } public int getMaxZ() { return maxZ; } public int getFlags() { return flags; } public boolean hasFlags(int flags) { return (flags & this.flags) == flags; } public void setFlag(int flags, boolean b) { if(b) { this.flags |= flags; } else { this.flags &= ~flags; } } public ArrayList getOwners() { return owners; } public void setName(String s) { name = s; } public String getName() { return name; } public int getId() { return id; } @Override public String toString() { return String.format("Plot(%d, %d, %d, %d, %d, %d)", minX, minY, minZ, maxX, maxY, maxZ); } } private static int idCounter = 0; private static final int SIZE_FACTOR = 64; private final static int[] PRIMES = {17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911, 43853, 87719, 175447, 350899, 701819, 1403641, 2807303, 5614657, 11229331, 22458671, 44917381, 89834777, 179669557, 359339171, 718678369}; private int primeIndex = 0; private int size = 0; @SuppressWarnings("unchecked") private ArrayList[] plots = new ArrayList[PRIMES[primeIndex]]; private Plot last = null; public PlotMap() {} private int hash(int x, int z, int arrayLength) { int h = (x + z * 12195263) % arrayLength; if(h < 0) { return h + arrayLength; } return h; } private void rehash() { // load factor 0.75 if(size < (plots.length * 3 / 4) || primeIndex + 1 >= PRIMES.length) { return; } primeIndex++; @SuppressWarnings("unchecked") ArrayList[] newPlots = new ArrayList[PRIMES[primeIndex]]; Plot p = last; while(p != null) { addIntern(newPlots, p, newPlots.length); p = p.previous; } plots = newPlots; } private void addIntern(ArrayList[] data, Plot p, int arrayLength) { int startX = Math.floorDiv(p.minX, SIZE_FACTOR); int startZ = Math.floorDiv(p.minZ, SIZE_FACTOR); int endX = Math.floorDiv(p.maxX, SIZE_FACTOR); int endZ = Math.floorDiv(p.maxZ, SIZE_FACTOR); for(int x = startX; x <= endX; x++) { for(int z = startZ; z <= endZ; z++) { int hash = hash(x, z, arrayLength); if(data[hash] == null) { data[hash] = new ArrayList<>(); } if(!data[hash].contains(p)) { data[hash].add(p); } } } } public Plot add(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, int id) { if(id >= idCounter) { idCounter = id + 1; } Plot p = new Plot(id, minX, minY, minZ, maxX, maxY, maxZ); if(last == null) { last = p; } else { last.next = p; p.previous = last; last = p; } size++; rehash(); addIntern(plots, p, plots.length); return p; } public Plot add(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { return add(minX, minY, minZ, maxX, maxY, maxZ, idCounter); } public List getPlotAt(int x, int y, int z) { ArrayList list = plots[hash(Math.floorDiv(x, SIZE_FACTOR), Math.floorDiv(z, SIZE_FACTOR), plots.length)]; if(list == null) { return new ArrayList<>(); } return list.stream().filter(p -> p.isInside(x, y, z)).collect(Collectors.toList()); } public boolean anyPlotMatches(int x, int y, int z, boolean empty, Predicate pred) { ArrayList list = plots[hash(Math.floorDiv(x, SIZE_FACTOR), Math.floorDiv(z, SIZE_FACTOR), plots.length)]; if(list == null) { return empty; } boolean r = empty; for(Plot p : list) { if(p.isInside(x, y, z)) { if(pred.test(p)) { return true; } r = false; } } return r; } public void remove(Plot p) { if(last == p) { last = last.previous; if(last != null) { last.next = null; } } else { if(p.previous != null) { p.previous.next = p.next; } if(p.next != null) { p.next.previous = p.previous; } } int startX = Math.floorDiv(p.minX, SIZE_FACTOR); int startZ = Math.floorDiv(p.minZ, SIZE_FACTOR); int endX = Math.floorDiv(p.maxX, SIZE_FACTOR); int endZ = Math.floorDiv(p.maxZ, SIZE_FACTOR); for(int x = startX; x <= endX; x++) { for(int z = startZ; z <= endZ; z++) { int hash = hash(x, z, plots.length); if(plots[hash] == null) { throw new NullPointerException("plot without list at location"); } plots[hash].remove(p); } } } public Iterator getIterator() { return new Iterator() { private Plot current = last; @Override public boolean hasNext() { return current != null; } @Override public Plot next() { Plot p = current; current = current.previous; return p; } }; } public Iterator getIterator(UUID uuid) { return new Iterator() { private Plot current = last; private boolean gotoNext() { if(current == null) { return false; } if(current.getOwners().contains(uuid)) { return true; } while(current.previous != null) { current = current.previous; if(current.getOwners().contains(uuid)) { return true; } } return false; } @Override public boolean hasNext() { return gotoNext(); } @Override public Plot next() { if(!gotoNext()) { throw new IllegalStateException("next without a next element"); } Plot p = current; current = current.previous; return p; } }; } public void save(String path) { File f = new File(path); try(DataOutputStream out = new DataOutputStream(new FileOutputStream(f))) { Iterator iter = getIterator(); while(iter.hasNext()) { Plot p = iter.next(); out.writeInt(p.id); out.writeShort(p.minX); out.writeShort(p.minY); out.writeShort(p.minZ); out.writeShort(p.maxX); out.writeShort(p.maxY); out.writeShort(p.maxZ); int owners = Math.min(127, p.owners.size()); out.writeByte(owners); for(int i = 0; i < owners; i++) { out.writeLong(p.owners.get(i).getLeastSignificantBits()); out.writeLong(p.owners.get(i).getMostSignificantBits()); } out.writeInt(p.flags); out.writeUTF(p.name); } } catch(IOException ex) { ex.printStackTrace(); } } public void read(File f) { if(!f.exists()) { return; } try(DataInputStream in = new DataInputStream(new FileInputStream(f))) { while(true) { int id = in.readInt(); int minX = in.readShort(); int minY = in.readShort(); int minZ = in.readShort(); int maxX = in.readShort(); int maxY = in.readShort(); int maxZ = in.readShort(); Plot p = add(minX, minY, minZ, maxX, maxY, maxZ, id); int owners = in.readByte(); for(int i = 0; i < owners; i++) { long lsb = in.readLong(); long msb = in.readLong(); p.owners.add(new UUID(msb, lsb)); } p.flags = in.readInt(); p.name = in.readUTF(); } } catch(EOFException ex) { } catch(IOException ex) { ex.printStackTrace(); } } public ArrayList getIntersectingPlots(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { if(minX > maxX) { int tmp = minX; minX = maxX; maxX = tmp; } if(minY > maxY) { int tmp = minY; minY = maxY; maxY = tmp; } if(minZ > maxZ) { int tmp = minZ; minZ = maxZ; maxZ = tmp; } ArrayList list = new ArrayList<>(); Plot p = last; while(p != null) { if(maxX > p.minX && p.maxX > minX && maxY > p.minY && p.maxY > minY && maxZ > p.minZ && p.maxZ > minZ) { list.add(p); } p = p.previous; } return list; } }