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}