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 javax.annotation.Nonnull;
021import net.minecraft.core.BlockPos;
022import net.minecraft.core.Direction;
023import net.minecraft.core.Vec3i;
024import net.minecraft.util.Mth;
025
026/**
027 * A better BlockPos that has fewer hash collisions (and slightly more performant offsets)
028 * <p>
029 * Is it really faster to subclass BlockPos and calculate a hash in the constructor like this, taking everything into account?
030 * Yes. 20% faster actually. It's called BETTER BlockPos for a reason. Source:
031 * <a href="https://docs.google.com/spreadsheets/d/1GWjOjOZINkg_0MkRgKRPH1kUzxjsnEROD9u3UFh_DJc">Benchmark Spreadsheet</a>
032 *
033 * @author leijurv
034 */
035public final class BetterBlockPos extends BlockPos {
036
037    private static final int NUM_X_BITS = 26;
038    private static final int NUM_Z_BITS = NUM_X_BITS;
039    private static final int NUM_Y_BITS = 64 - NUM_X_BITS - NUM_Z_BITS;
040    private static final int Y_SHIFT = NUM_Z_BITS;
041    private static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS;
042    private static final long X_MASK = (1L << NUM_X_BITS) - 1L;
043    private static final long Y_MASK = (1L << NUM_Y_BITS) - 1L;
044    private static final long Z_MASK = (1L << NUM_Z_BITS) - 1L;
045
046    public static final BetterBlockPos ORIGIN = new BetterBlockPos(0, 0, 0);
047
048    public final int x;
049    public final int y;
050    public final int z;
051
052    public BetterBlockPos(int x, int y, int z) {
053        super(x, y, z);
054        this.x = x;
055        this.y = y;
056        this.z = z;
057    }
058
059    public BetterBlockPos(double x, double y, double z) {
060        this(Mth.floor(x), Mth.floor(y), Mth.floor(z));
061    }
062
063    public BetterBlockPos(BlockPos pos) {
064        this(pos.getX(), pos.getY(), pos.getZ());
065    }
066
067    /**
068     * Like constructor but returns null if pos is null, good if you just need to possibly censor coordinates
069     *
070     * @param pos The BlockPos, possibly null, to convert
071     * @return A BetterBlockPos or null if pos was null
072     */
073    public static BetterBlockPos from(BlockPos pos) {
074        if (pos == null) {
075            return null;
076        }
077
078        return new BetterBlockPos(pos);
079    }
080
081    @Override
082    public int hashCode() {
083        return (int) longHash(x, y, z);
084    }
085
086    public static long longHash(BetterBlockPos pos) {
087        return longHash(pos.x, pos.y, pos.z);
088    }
089
090    public static long longHash(int x, int y, int z) {
091        // TODO use the same thing as BlockPos.fromLong();
092        // invertibility would be incredibly useful
093        /*
094         *   This is the hashcode implementation of Vec3i (the superclass of the class which I shall not name)
095         *
096         *   public int hashCode() {
097         *       return (this.getY() + this.getZ() * 31) * 31 + this.getX();
098         *   }
099         *
100         *   That is terrible and has tons of collisions and makes the HashMap terribly inefficient.
101         *
102         *   That's why we grab out the X, Y, Z and calculate our own hashcode
103         */
104        long hash = 3241;
105        hash = 3457689L * hash + x;
106        hash = 8734625L * hash + y;
107        hash = 2873465L * hash + z;
108        return hash;
109    }
110
111    @Override
112    public boolean equals(Object o) {
113        if (o == null) {
114            return false;
115        }
116        if (o instanceof BetterBlockPos) {
117            BetterBlockPos oth = (BetterBlockPos) o;
118            return oth.x == x && oth.y == y && oth.z == z;
119        }
120        // during path execution, like "if (whereShouldIBe.equals(whereAmI)) {"
121        // sometimes we compare a BlockPos to a BetterBlockPos
122        BlockPos oth = (BlockPos) o;
123        return oth.getX() == x && oth.getY() == y && oth.getZ() == z;
124    }
125
126    @Override
127    public BetterBlockPos above() {
128        // this is unimaginably faster than blockpos.up
129        // that literally calls
130        // this.up(1)
131        // which calls this.offset(Direction.UP, 1)
132        // which does return n == 0 ? this : new BlockPos(this.getX() + facing.getXOffset() * n, this.getY() + facing.getYOffset() * n, this.getZ() + facing.getZOffset() * n);
133
134        // how many function calls is that? up(), up(int), offset(Direction, int), new BlockPos, getX, getXOffset, getY, getYOffset, getZ, getZOffset
135        // that's ten.
136        // this is one function call.
137        return new BetterBlockPos(x, y + 1, z);
138    }
139
140    @Override
141    public BetterBlockPos above(int amt) {
142        // see comment in up()
143        return amt == 0 ? this : new BetterBlockPos(x, y + amt, z);
144    }
145
146    @Override
147    public BetterBlockPos below() {
148        // see comment in up()
149        return new BetterBlockPos(x, y - 1, z);
150    }
151
152    @Override
153    public BetterBlockPos below(int amt) {
154        // see comment in up()
155        return amt == 0 ? this : new BetterBlockPos(x, y - amt, z);
156    }
157
158    @Override
159    public BetterBlockPos relative(Direction dir) {
160        Vec3i vec = dir.getUnitVec3i();
161        return new BetterBlockPos(x + vec.getX(), y + vec.getY(), z + vec.getZ());
162    }
163
164    @Override
165    public BetterBlockPos relative(Direction dir, int dist) {
166        if (dist == 0) {
167            return this;
168        }
169        Vec3i vec = dir.getUnitVec3i();
170        return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist);
171    }
172
173    @Override
174    public BetterBlockPos north() {
175        return new BetterBlockPos(x, y, z - 1);
176    }
177
178    @Override
179    public BetterBlockPos north(int amt) {
180        return amt == 0 ? this : new BetterBlockPos(x, y, z - amt);
181    }
182
183    @Override
184    public BetterBlockPos south() {
185        return new BetterBlockPos(x, y, z + 1);
186    }
187
188    @Override
189    public BetterBlockPos south(int amt) {
190        return amt == 0 ? this : new BetterBlockPos(x, y, z + amt);
191    }
192
193    @Override
194    public BetterBlockPos east() {
195        return new BetterBlockPos(x + 1, y, z);
196    }
197
198    @Override
199    public BetterBlockPos east(int amt) {
200        return amt == 0 ? this : new BetterBlockPos(x + amt, y, z);
201    }
202
203    @Override
204    public BetterBlockPos west() {
205        return new BetterBlockPos(x - 1, y, z);
206    }
207
208    @Override
209    public BetterBlockPos west(int amt) {
210        return amt == 0 ? this : new BetterBlockPos(x - amt, y, z);
211    }
212
213    public double distanceSq(final BetterBlockPos to) {
214        double dx = (double) this.x - to.x;
215        double dy = (double) this.y - to.y;
216        double dz = (double) this.z - to.z;
217        return dx * dx + dy * dy + dz * dz;
218    }
219
220    public double distanceTo(final BetterBlockPos to) {
221        double dx = (double) this.x - to.x;
222        double dy = (double) this.y - to.y;
223        double dz = (double) this.z - to.z;
224        return Math.sqrt(dx * dx + dy * dy + dz * dz);
225    }
226
227    @Override
228    @Nonnull
229    public String toString() {
230        return String.format(
231                "BetterBlockPos{x=%s,y=%s,z=%s}",
232                SettingsUtil.maybeCensor(x),
233                SettingsUtil.maybeCensor(y),
234                SettingsUtil.maybeCensor(z)
235        );
236    }
237
238    public static long serializeToLong(final int x, final int y, final int z) {
239        return ((long) x & X_MASK) << X_SHIFT | ((long) y & Y_MASK) << Y_SHIFT | ((long) z & Z_MASK);
240    }
241
242    public static BetterBlockPos deserializeFromLong(final long serialized) {
243        final int x = (int) (serialized << 64 - X_SHIFT - NUM_X_BITS >> 64 - NUM_X_BITS);
244        final int y = (int) (serialized << 64 - Y_SHIFT - NUM_Y_BITS >> 64 - NUM_Y_BITS);
245        final int z = (int) (serialized << 64 - NUM_Z_BITS >> 64 - NUM_Z_BITS);
246        return new BetterBlockPos(x, y, z);
247    }
248}