001/* 002 * This file is part of Baritone. 003 * 004 * Baritone is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU Lesser General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * Baritone is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License 015 * along with Baritone. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package baritone.api.utils; 019 020import net.minecraft.util.EnumFacing; 021import net.minecraft.util.math.BlockPos; 022import net.minecraft.util.math.MathHelper; 023import net.minecraft.util.math.Vec3i; 024 025import javax.annotation.Nonnull; 026 027/** 028 * A better BlockPos that has fewer hash collisions (and slightly more performant offsets) 029 * <p> 030 * Is it really faster to subclass BlockPos and calculate a hash in the constructor like this, taking everything into account? 031 * Yes. 20% faster actually. It's called BETTER BlockPos for a reason. Source: 032 * <a href="https://docs.google.com/spreadsheets/d/1GWjOjOZINkg_0MkRgKRPH1kUzxjsnEROD9u3UFh_DJc">Benchmark Spreadsheet</a> 033 * 034 * @author leijurv 035 */ 036public final class BetterBlockPos extends BlockPos { 037 038 public static final BetterBlockPos ORIGIN = new BetterBlockPos(0, 0, 0); 039 040 public final int x; 041 public final int y; 042 public final int z; 043 044 public BetterBlockPos(int x, int y, int z) { 045 super(x, y, z); 046 this.x = x; 047 this.y = y; 048 this.z = z; 049 } 050 051 public BetterBlockPos(double x, double y, double z) { 052 this(MathHelper.floor(x), MathHelper.floor(y), MathHelper.floor(z)); 053 } 054 055 public BetterBlockPos(BlockPos pos) { 056 this(pos.getX(), pos.getY(), pos.getZ()); 057 } 058 059 /** 060 * Like constructor but returns null if pos is null, good if you just need to possibly censor coordinates 061 * 062 * @param pos The BlockPos, possibly null, to convert 063 * @return A BetterBlockPos or null if pos was null 064 */ 065 public static BetterBlockPos from(BlockPos pos) { 066 if (pos == null) { 067 return null; 068 } 069 070 return new BetterBlockPos(pos); 071 } 072 073 @Override 074 public int hashCode() { 075 return (int) longHash(x, y, z); 076 } 077 078 public static long longHash(BetterBlockPos pos) { 079 return longHash(pos.x, pos.y, pos.z); 080 } 081 082 public static long longHash(int x, int y, int z) { 083 // TODO use the same thing as BlockPos.fromLong(); 084 // invertibility would be incredibly useful 085 /* 086 * This is the hashcode implementation of Vec3i (the superclass of the class which I shall not name) 087 * 088 * public int hashCode() { 089 * return (this.getY() + this.getZ() * 31) * 31 + this.getX(); 090 * } 091 * 092 * That is terrible and has tons of collisions and makes the HashMap terribly inefficient. 093 * 094 * That's why we grab out the X, Y, Z and calculate our own hashcode 095 */ 096 long hash = 3241; 097 hash = 3457689L * hash + x; 098 hash = 8734625L * hash + y; 099 hash = 2873465L * hash + z; 100 return hash; 101 } 102 103 @Override 104 public boolean equals(Object o) { 105 if (o == null) { 106 return false; 107 } 108 if (o instanceof BetterBlockPos) { 109 BetterBlockPos oth = (BetterBlockPos) o; 110 return oth.x == x && oth.y == y && oth.z == z; 111 } 112 // during path execution, like "if (whereShouldIBe.equals(whereAmI)) {" 113 // sometimes we compare a BlockPos to a BetterBlockPos 114 BlockPos oth = (BlockPos) o; 115 return oth.getX() == x && oth.getY() == y && oth.getZ() == z; 116 } 117 118 @Override 119 public BetterBlockPos up() { 120 // this is unimaginably faster than blockpos.up 121 // that literally calls 122 // this.up(1) 123 // which calls this.offset(EnumFacing.UP, 1) 124 // which does return n == 0 ? this : new BlockPos(this.getX() + facing.getXOffset() * n, this.getY() + facing.getYOffset() * n, this.getZ() + facing.getZOffset() * n); 125 126 // how many function calls is that? up(), up(int), offset(EnumFacing, int), new BlockPos, getX, getXOffset, getY, getYOffset, getZ, getZOffset 127 // that's ten. 128 // this is one function call. 129 return new BetterBlockPos(x, y + 1, z); 130 } 131 132 @Override 133 public BetterBlockPos up(int amt) { 134 // see comment in up() 135 return amt == 0 ? this : new BetterBlockPos(x, y + amt, z); 136 } 137 138 @Override 139 public BetterBlockPos down() { 140 // see comment in up() 141 return new BetterBlockPos(x, y - 1, z); 142 } 143 144 @Override 145 public BetterBlockPos down(int amt) { 146 // see comment in up() 147 return amt == 0 ? this : new BetterBlockPos(x, y - amt, z); 148 } 149 150 @Override 151 public BetterBlockPos offset(EnumFacing dir) { 152 Vec3i vec = dir.getDirectionVec(); 153 return new BetterBlockPos(x + vec.getX(), y + vec.getY(), z + vec.getZ()); 154 } 155 156 @Override 157 public BetterBlockPos offset(EnumFacing dir, int dist) { 158 if (dist == 0) { 159 return this; 160 } 161 Vec3i vec = dir.getDirectionVec(); 162 return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist); 163 } 164 165 @Override 166 public BetterBlockPos north() { 167 return new BetterBlockPos(x, y, z - 1); 168 } 169 170 @Override 171 public BetterBlockPos north(int amt) { 172 return amt == 0 ? this : new BetterBlockPos(x, y, z - amt); 173 } 174 175 @Override 176 public BetterBlockPos south() { 177 return new BetterBlockPos(x, y, z + 1); 178 } 179 180 @Override 181 public BetterBlockPos south(int amt) { 182 return amt == 0 ? this : new BetterBlockPos(x, y, z + amt); 183 } 184 185 @Override 186 public BetterBlockPos east() { 187 return new BetterBlockPos(x + 1, y, z); 188 } 189 190 @Override 191 public BetterBlockPos east(int amt) { 192 return amt == 0 ? this : new BetterBlockPos(x + amt, y, z); 193 } 194 195 @Override 196 public BetterBlockPos west() { 197 return new BetterBlockPos(x - 1, y, z); 198 } 199 200 @Override 201 public BetterBlockPos west(int amt) { 202 return amt == 0 ? this : new BetterBlockPos(x - amt, y, z); 203 } 204 205 @Override 206 @Nonnull 207 public String toString() { 208 return String.format( 209 "BetterBlockPos{x=%s,y=%s,z=%s}", 210 SettingsUtil.maybeCensor(x), 211 SettingsUtil.maybeCensor(y), 212 SettingsUtil.maybeCensor(z) 213 ); 214 } 215}