Merge branch 'master' into 1.13.2
This commit is contained in:
@@ -19,8 +19,9 @@
|
|||||||

|

|
||||||
[](https://github.com/cabaletta/baritone/graphs/contributors/)
|
[](https://github.com/cabaletta/baritone/graphs/contributors/)
|
||||||
[](https://github.com/cabaletta/baritone/commit/)
|
[](https://github.com/cabaletta/baritone/commit/)
|
||||||
[](https://impactdevelopment.github.io/)
|
[](https://impactdevelopment.github.io/)
|
||||||
[](https://github.com/fr1kin/ForgeHax/)
|
[](https://github.com/fr1kin/ForgeHax/)
|
||||||
|
[](https://gitlab.com/emc-mods-indrit/baritone_api)
|
||||||
[](https://wweclient.com/)
|
[](https://wweclient.com/)
|
||||||
[](https://futureclient.net/)
|
[](https://futureclient.net/)
|
||||||
[](http://forthebadge.com/)
|
[](http://forthebadge.com/)
|
||||||
@@ -30,14 +31,14 @@ A Minecraft pathfinder bot.
|
|||||||
|
|
||||||
Baritone is the pathfinding system used in [Impact](https://impactdevelopment.github.io/) since 4.4. There's a [showcase video](https://www.youtube.com/watch?v=yI8hgW_m6dQ) made by @Adovin#3153 on Baritone's integration into Impact. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a video I made showing off what it can do.
|
Baritone is the pathfinding system used in [Impact](https://impactdevelopment.github.io/) since 4.4. There's a [showcase video](https://www.youtube.com/watch?v=yI8hgW_m6dQ) made by @Adovin#3153 on Baritone's integration into Impact. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a video I made showing off what it can do.
|
||||||
|
|
||||||
The easiest way to install Baritone is to install [Impact](https://impactdevelopment.github.io/), which comes with Baritone. The second easiest way (for 1.12.2 only) is to install the forge jar from [releases](https://github.com/cabaletta/baritone/releases). Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it.
|
The easiest way to install Baritone is to install [Impact](https://impactdevelopment.github.io/), which comes with Baritone. The second easiest way (for 1.12.2 only) is to install the v1.2.* forge api jar from [releases](https://github.com/cabaletta/baritone/releases). Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it.
|
||||||
|
|
||||||
For 1.14.4, [click here](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1).
|
For 1.14.4, [click here](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1). Or [with optifine](https://github.com/cabaletta/baritone/issues/797).
|
||||||
|
|
||||||
This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/),
|
This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/),
|
||||||
the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 and 1.13.2. Baritone focuses on reliability and particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths).
|
the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 and 1.13.2. Baritone focuses on reliability and particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths).
|
||||||
|
|
||||||
Have committed at least once a day for the last 11 months =D 🦀
|
Have committed at least once a day from Aug 1 2018 to Aug 1 2019.
|
||||||
|
|
||||||
1Leijurv3DWTrGAfmmiTphjhXLvQiHg7K2
|
1Leijurv3DWTrGAfmmiTphjhXLvQiHg7K2
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ These releases are not always completely up to date with latest features, and ar
|
|||||||
|
|
||||||
Link to the releases page: [Releases](https://github.com/cabaletta/baritone/releases)
|
Link to the releases page: [Releases](https://github.com/cabaletta/baritone/releases)
|
||||||
|
|
||||||
|
v1.2.* is for 1.12.2, v1.3.* is for 1.13.2
|
||||||
|
|
||||||
Any official release will be GPG signed by leijurv (44A3EA646EADAC6A) and ZeroMemes (73A788379A197567). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by those two public keys of `checksums.txt`.
|
Any official release will be GPG signed by leijurv (44A3EA646EADAC6A) and ZeroMemes (73A788379A197567). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by those two public keys of `checksums.txt`.
|
||||||
|
|
||||||
The build is fully deterministic and reproducible, and you can verify Travis did it properly by running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too).
|
The build is fully deterministic and reproducible, and you can verify Travis did it properly by running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too).
|
||||||
|
|||||||
+1
-1
@@ -70,7 +70,7 @@ sourceSets {
|
|||||||
schematica_api {
|
schematica_api {
|
||||||
compileClasspath += main.compileClasspath
|
compileClasspath += main.compileClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
compileClasspath += schematica_api.output
|
compileClasspath += schematica_api.output
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,13 @@ public final class Settings {
|
|||||||
*/
|
*/
|
||||||
public final Setting<Boolean> allowJumpAt256 = new Setting<>(false);
|
public final Setting<Boolean> allowJumpAt256 = new Setting<>(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This should be monetized it's so good
|
||||||
|
* <p>
|
||||||
|
* Defaults to true, but only actually takes effect if allowParkour is also true
|
||||||
|
*/
|
||||||
|
public final Setting<Boolean> allowParkourAscend = new Setting<>(true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow descending diagonally
|
* Allow descending diagonally
|
||||||
* <p>
|
* <p>
|
||||||
@@ -178,7 +185,7 @@ public final class Settings {
|
|||||||
* Enables some more advanced vine features. They're honestly just gimmicks and won't ever be needed in real
|
* Enables some more advanced vine features. They're honestly just gimmicks and won't ever be needed in real
|
||||||
* pathing scenarios. And they can cause Baritone to get trapped indefinitely in a strange scenario.
|
* pathing scenarios. And they can cause Baritone to get trapped indefinitely in a strange scenario.
|
||||||
* <p>
|
* <p>
|
||||||
* Never turn this on lol
|
* Almost never turn this on lol
|
||||||
*/
|
*/
|
||||||
public final Setting<Boolean> allowVines = new Setting<>(false);
|
public final Setting<Boolean> allowVines = new Setting<>(false);
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ import net.minecraft.util.text.TextComponentString;
|
|||||||
import net.minecraft.util.text.TextFormatting;
|
import net.minecraft.util.text.TextFormatting;
|
||||||
import net.minecraft.util.text.event.ClickEvent;
|
import net.minecraft.util.text.event.ClickEvent;
|
||||||
import net.minecraft.world.chunk.Chunk;
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
import net.minecraft.world.dimension.DimensionType;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNumeric;
|
||||||
|
|
||||||
public class ExampleBaritoneControl implements Helper, AbstractGameEventListener {
|
public class ExampleBaritoneControl implements Helper, AbstractGameEventListener {
|
||||||
private static final String COMMAND_PREFIX = "#";
|
private static final String COMMAND_PREFIX = "#";
|
||||||
|
|
||||||
@@ -258,7 +261,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener
|
|||||||
try {
|
try {
|
||||||
String[] coords = msg.substring("build".length()).trim().split(" ");
|
String[] coords = msg.substring("build".length()).trim().split(" ");
|
||||||
file = coords[0] + ".schematic";
|
file = coords[0] + ".schematic";
|
||||||
origin = new BlockPos(parseOrDefault(coords[1], ctx.playerFeet().x), parseOrDefault(coords[2], ctx.playerFeet().y), parseOrDefault(coords[3], ctx.playerFeet().z));
|
origin = new BlockPos(parseOrDefault(coords[1], ctx.playerFeet().x, 1), parseOrDefault(coords[2], ctx.playerFeet().y, 1), parseOrDefault(coords[3], ctx.playerFeet().z, 1));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
file = msg.substring(5).trim() + ".schematic";
|
file = msg.substring(5).trim() + ".schematic";
|
||||||
origin = ctx.playerFeet();
|
origin = ctx.playerFeet();
|
||||||
@@ -368,7 +371,8 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener
|
|||||||
}
|
}
|
||||||
if (msg.equals("render")) {
|
if (msg.equals("render")) {
|
||||||
BetterBlockPos pf = ctx.playerFeet();
|
BetterBlockPos pf = ctx.playerFeet();
|
||||||
Minecraft.getInstance().worldRenderer.markBlockRangeForRenderUpdate(pf.x - 500, pf.y - 500, pf.z - 500, pf.x + 500, pf.y + 500, pf.z + 500);
|
int dist = (Minecraft.getInstance().gameSettings.renderDistanceChunks + 1) * 16;
|
||||||
|
Minecraft.getInstance().worldRenderer.markBlockRangeForRenderUpdate(pf.x - dist, pf.y - 256, pf.z - dist, pf.x + dist, pf.y + 256, pf.z + dist);
|
||||||
logDirect("okay");
|
logDirect("okay");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -670,8 +674,8 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int parseOrDefault(String str, int i) {
|
private int parseOrDefault(String str, int i, double dimensionFactor) {
|
||||||
return str.equals("~") ? i : str.startsWith("~") ? Integer.parseInt(str.substring(1)) + i : Integer.parseInt(str);
|
return str.equals("~") ? i : str.startsWith("~") ? (int) (Integer.parseInt(str.substring(1)) * dimensionFactor) + i : (int) (Integer.parseInt(str) * dimensionFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void log(List<ItemStack> stacks) {
|
private void log(List<ItemStack> stacks) {
|
||||||
@@ -686,18 +690,23 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener
|
|||||||
Goal goal;
|
Goal goal;
|
||||||
try {
|
try {
|
||||||
BetterBlockPos playerFeet = ctx.playerFeet();
|
BetterBlockPos playerFeet = ctx.playerFeet();
|
||||||
switch (params.length) {
|
|
||||||
|
int length = params.length - 1; // length has to be smaller when a dimension parameter is added
|
||||||
|
if (params.length < 1 || (isNumeric(params[params.length - 1]) || params[params.length - 1].startsWith("~"))) {
|
||||||
|
length = params.length;
|
||||||
|
}
|
||||||
|
switch (length) {
|
||||||
case 0:
|
case 0:
|
||||||
goal = new GoalBlock(playerFeet);
|
goal = new GoalBlock(playerFeet);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
goal = new GoalYLevel(parseOrDefault(params[0], playerFeet.y));
|
goal = new GoalYLevel(parseOrDefault(params[0], playerFeet.y, 1));
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
goal = new GoalXZ(parseOrDefault(params[0], playerFeet.x), parseOrDefault(params[1], playerFeet.z));
|
goal = new GoalXZ(parseOrDefault(params[0], playerFeet.x, calculateDimensionFactor(params[params.length - 1])), parseOrDefault(params[1], playerFeet.z, calculateDimensionFactor(params[params.length - 1])));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
goal = new GoalBlock(new BlockPos(parseOrDefault(params[0], playerFeet.x), parseOrDefault(params[1], playerFeet.y), parseOrDefault(params[2], playerFeet.z)));
|
goal = new GoalBlock(new BlockPos(parseOrDefault(params[0], playerFeet.x, calculateDimensionFactor(params[params.length - 1])), parseOrDefault(params[1], playerFeet.y, 1), parseOrDefault(params[2], playerFeet.z, calculateDimensionFactor(params[params.length - 1]))));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logDirect("unable to understand lol");
|
logDirect("unable to understand lol");
|
||||||
@@ -709,4 +718,23 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener
|
|||||||
}
|
}
|
||||||
return goal;
|
return goal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private double calculateDimensionFactor(String to) {
|
||||||
|
return Math.pow(8, ctx.world().dimension.getType().getId() - getDimensionByName(to.toLowerCase()).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DimensionType getDimensionByName(String name) {
|
||||||
|
if ("the_end".contains(name)) {
|
||||||
|
return DimensionType.THE_END;
|
||||||
|
}
|
||||||
|
if ("the_overworld".contains(name) || "surface".contains(name)) {
|
||||||
|
return DimensionType.OVERWORLD;
|
||||||
|
}
|
||||||
|
if ("the_nether".contains(name) || "hell".contains(name)) {
|
||||||
|
return DimensionType.NETHER;
|
||||||
|
}
|
||||||
|
return ctx.world().dimension.getType();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class MyChunkPos {
|
|||||||
@SerializedName("x")
|
@SerializedName("x")
|
||||||
public int x;
|
public int x;
|
||||||
|
|
||||||
@SerializedName("y")
|
@SerializedName("z")
|
||||||
public int z;
|
public int z;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -148,12 +148,12 @@ public class Baritone implements IBaritone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CustomGoalProcess getCustomGoalProcess() { // Iffy
|
public CustomGoalProcess getCustomGoalProcess() {
|
||||||
return this.customGoalProcess;
|
return this.customGoalProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GetToBlockProcess getGetToBlockProcess() { // Iffy
|
public GetToBlockProcess getGetToBlockProcess() {
|
||||||
return this.getToBlockProcess;
|
return this.getToBlockProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
|||||||
) {
|
) {
|
||||||
// when it was *just* started, currentBest will be empty so we need to also check calcFrom since that's always present
|
// when it was *just* started, currentBest will be empty so we need to also check calcFrom since that's always present
|
||||||
inProgress.cancel(); // cancellation doesn't dispatch any events
|
inProgress.cancel(); // cancellation doesn't dispatch any events
|
||||||
inProgress = null; // this is safe since we hold both locks
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -339,7 +338,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// just cancel the current path
|
// just cancel the current path
|
||||||
public void secretInternalSegmentCancel() {
|
private void secretInternalSegmentCancel() {
|
||||||
queuePathEvent(PathEvent.CANCELED);
|
queuePathEvent(PathEvent.CANCELED);
|
||||||
synchronized (pathPlanLock) {
|
synchronized (pathPlanLock) {
|
||||||
getInProgress().ifPresent(AbstractNodeCostSearch::cancel);
|
getInProgress().ifPresent(AbstractNodeCostSearch::cancel);
|
||||||
@@ -361,12 +360,6 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public void secretCursedFunctionDoNotCall(IPath path) {
|
|
||||||
synchronized (pathPlanLock) {
|
|
||||||
current = new PathExecutor(this, path);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public CalculationContext secretInternalGetCalculationContext() {
|
public CalculationContext secretInternalGetCalculationContext() {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@@ -495,7 +488,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (talkAboutIt && current != null && current.getPath() != null) {
|
if (talkAboutIt && current != null && current.getPath() != null) {
|
||||||
if (goal == null || goal.isInGoal(current.getPath().getDest())) {
|
if (goal.isInGoal(current.getPath().getDest())) {
|
||||||
logDebug("Finished finding a path from " + start + " to " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered");
|
logDebug("Finished finding a path from " + start + " to " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered");
|
||||||
} else {
|
} else {
|
||||||
logDebug("Found path segment from " + start + " towards " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered");
|
logDebug("Found path segment from " + start + " towards " + goal + ". " + current.getPath().getNumNodesConsidered() + " nodes considered");
|
||||||
@@ -508,7 +501,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) {
|
private static AbstractNodeCostSearch createPathfinder(BlockPos start, Goal goal, IPath previous, CalculationContext context) {
|
||||||
Goal transformed = goal;
|
Goal transformed = goal;
|
||||||
if (Baritone.settings().simplifyUnloadedYCoord.value && goal instanceof IGoalRenderPos) {
|
if (Baritone.settings().simplifyUnloadedYCoord.value && goal instanceof IGoalRenderPos) {
|
||||||
BlockPos pos = ((IGoalRenderPos) goal).getGoalPos();
|
BlockPos pos = ((IGoalRenderPos) goal).getGoalPos();
|
||||||
|
|||||||
+11
-2
@@ -199,8 +199,17 @@ public final class CachedChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == PathingBlockType.SOLID && y == 127 && dimension == -1) {
|
if (type == PathingBlockType.SOLID) {
|
||||||
return Blocks.BEDROCK.getDefaultState();
|
if (y == 127 && dimension == -1) {
|
||||||
|
// nether roof is always unbreakable
|
||||||
|
return Blocks.BEDROCK.getDefaultState();
|
||||||
|
}
|
||||||
|
if (y < 5 && dimension == 0) {
|
||||||
|
// solid blocks below 5 are commonly bedrock
|
||||||
|
// however, returning bedrock always would be a little yikes
|
||||||
|
// discourage paths that include breaking blocks below 5 a little more heavily just so that it takes paths breaking what's known to be stone (at 5 or above) instead of what could maybe be bedrock (below 5)
|
||||||
|
return Blocks.OBSIDIAN.getDefaultState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ChunkPacker.pathingTypeToBlock(type, dimension);
|
return ChunkPacker.pathingTypeToBlock(type, dimension);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch {
|
|||||||
int timeCheckInterval = 1 << 6;
|
int timeCheckInterval = 1 << 6;
|
||||||
int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.value; // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior
|
int pathingMaxChunkBorderFetch = Baritone.settings().pathingMaxChunkBorderFetch.value; // grab all settings beforehand so that changing settings during pathing doesn't cause a crash or unpredictable behavior
|
||||||
double minimumImprovement = Baritone.settings().minimumImprovementRepropagation.value ? MIN_IMPROVEMENT : 0;
|
double minimumImprovement = Baritone.settings().minimumImprovementRepropagation.value ? MIN_IMPROVEMENT : 0;
|
||||||
|
Moves[] allMoves = Moves.values();
|
||||||
while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) {
|
while (!openSet.isEmpty() && numEmptyChunk < pathingMaxChunkBorderFetch && !cancelRequested) {
|
||||||
if ((numNodes & (timeCheckInterval - 1)) == 0) { // only call this once every 64 nodes (about half a millisecond)
|
if ((numNodes & (timeCheckInterval - 1)) == 0) { // only call this once every 64 nodes (about half a millisecond)
|
||||||
long now = System.currentTimeMillis(); // since nanoTime is slow on windows (takes many microseconds)
|
long now = System.currentTimeMillis(); // since nanoTime is slow on windows (takes many microseconds)
|
||||||
@@ -95,7 +96,7 @@ public final class AStarPathFinder extends AbstractNodeCostSearch {
|
|||||||
logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered");
|
logDebug("Took " + (System.currentTimeMillis() - startTime) + "ms, " + numMovementsConsidered + " movements considered");
|
||||||
return Optional.of(new Path(startNode, currentNode, numNodes, goal, calcContext));
|
return Optional.of(new Path(startNode, currentNode, numNodes, goal, calcContext));
|
||||||
}
|
}
|
||||||
for (Moves moves : Moves.values()) {
|
for (Moves moves : allMoves) {
|
||||||
int newX = currentNode.x + moves.xOffset;
|
int newX = currentNode.x + moves.xOffset;
|
||||||
int newZ = currentNode.z + moves.zOffset;
|
int newZ = currentNode.z + moves.zOffset;
|
||||||
if ((newX >> 4 != currentNode.x >> 4 || newZ >> 4 != currentNode.z >> 4) && !calcContext.isLoaded(newX, newZ)) {
|
if ((newX >> 4 != currentNode.x >> 4 || newZ >> 4 != currentNode.z >> 4) && !calcContext.isLoaded(newX, newZ)) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ public abstract class AbstractNodeCostSearch implements IPathFinder, Helper {
|
|||||||
return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_SEGMENT, path);
|
return new PathCalculationResult(PathCalculationResult.Type.SUCCESS_SEGMENT, path);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Helper.HELPER.logDebug("Pathing exception: " + e);
|
Helper.HELPER.logDirect("Pathing exception: " + e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return new PathCalculationResult(PathCalculationResult.Type.EXCEPTION);
|
return new PathCalculationResult(PathCalculationResult.Type.EXCEPTION);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -72,28 +72,9 @@ class Path extends PathBase {
|
|||||||
this.start = new BetterBlockPos(start.x, start.y, start.z);
|
this.start = new BetterBlockPos(start.x, start.y, start.z);
|
||||||
this.end = new BetterBlockPos(end.x, end.y, end.z);
|
this.end = new BetterBlockPos(end.x, end.y, end.z);
|
||||||
this.numNodes = numNodes;
|
this.numNodes = numNodes;
|
||||||
this.path = new ArrayList<>();
|
|
||||||
this.movements = new ArrayList<>();
|
this.movements = new ArrayList<>();
|
||||||
this.nodes = new ArrayList<>();
|
|
||||||
this.goal = goal;
|
this.goal = goal;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
assemblePath(end);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Goal getGoal() {
|
|
||||||
return goal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assembles this path given the end node.
|
|
||||||
*
|
|
||||||
* @param end The end node
|
|
||||||
*/
|
|
||||||
private void assemblePath(PathNode end) {
|
|
||||||
if (!path.isEmpty() || !movements.isEmpty()) {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
PathNode current = end;
|
PathNode current = end;
|
||||||
LinkedList<BetterBlockPos> tempPath = new LinkedList<>();
|
LinkedList<BetterBlockPos> tempPath = new LinkedList<>();
|
||||||
LinkedList<PathNode> tempNodes = new LinkedList<>();
|
LinkedList<PathNode> tempNodes = new LinkedList<>();
|
||||||
@@ -107,8 +88,13 @@ class Path extends PathBase {
|
|||||||
// Can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is
|
// Can't directly convert from the PathNode pseudo linked list to an array because we don't know how long it is
|
||||||
// inserting into a LinkedList<E> keeps track of length, then when we addall (which calls .toArray) it's able
|
// inserting into a LinkedList<E> keeps track of length, then when we addall (which calls .toArray) it's able
|
||||||
// to performantly do that conversion since it knows the length.
|
// to performantly do that conversion since it knows the length.
|
||||||
path.addAll(tempPath);
|
this.path = new ArrayList<>(tempPath);
|
||||||
nodes.addAll(tempNodes);
|
this.nodes = new ArrayList<>(tempNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Goal getGoal() {
|
||||||
|
return goal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean assembleMovements() {
|
private boolean assembleMovements() {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public class CalculationContext {
|
|||||||
public final boolean allowParkour;
|
public final boolean allowParkour;
|
||||||
public final boolean allowParkourPlace;
|
public final boolean allowParkourPlace;
|
||||||
public final boolean allowJumpAt256;
|
public final boolean allowJumpAt256;
|
||||||
|
public final boolean allowParkourAscend;
|
||||||
public final boolean assumeWalkOnWater;
|
public final boolean assumeWalkOnWater;
|
||||||
public final boolean allowDiagonalDescend;
|
public final boolean allowDiagonalDescend;
|
||||||
public final boolean allowDownward;
|
public final boolean allowDownward;
|
||||||
@@ -90,6 +91,7 @@ public class CalculationContext {
|
|||||||
this.allowParkour = Baritone.settings().allowParkour.value;
|
this.allowParkour = Baritone.settings().allowParkour.value;
|
||||||
this.allowParkourPlace = Baritone.settings().allowParkourPlace.value;
|
this.allowParkourPlace = Baritone.settings().allowParkourPlace.value;
|
||||||
this.allowJumpAt256 = Baritone.settings().allowJumpAt256.value;
|
this.allowJumpAt256 = Baritone.settings().allowJumpAt256.value;
|
||||||
|
this.allowParkourAscend = Baritone.settings().allowParkourAscend.value;
|
||||||
this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.value;
|
this.assumeWalkOnWater = Baritone.settings().assumeWalkOnWater.value;
|
||||||
this.allowDiagonalDescend = Baritone.settings().allowDiagonalDescend.value;
|
this.allowDiagonalDescend = Baritone.settings().allowDiagonalDescend.value;
|
||||||
this.allowDownward = Baritone.settings().allowDownward.value;
|
this.allowDownward = Baritone.settings().allowDownward.value;
|
||||||
|
|||||||
@@ -520,7 +520,7 @@ public interface MovementHelper extends ActionCosts, Helper {
|
|||||||
return PlaceResult.NO_OPTION;
|
return PlaceResult.NO_OPTION;
|
||||||
}
|
}
|
||||||
double faceX = (placeAt.getX() + against1.getX() + 1.0D) * 0.5D;
|
double faceX = (placeAt.getX() + against1.getX() + 1.0D) * 0.5D;
|
||||||
double faceY = (placeAt.getY() + against1.getY() + 1.0D) * 0.5D;
|
double faceY = (placeAt.getY() + against1.getY() + 0.5D) * 0.5D;
|
||||||
double faceZ = (placeAt.getZ() + against1.getZ() + 1.0D) * 0.5D;
|
double faceZ = (placeAt.getZ() + against1.getZ() + 1.0D) * 0.5D;
|
||||||
Rotation place = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations());
|
Rotation place = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(faceX, faceY, faceZ), ctx.playerRotations());
|
||||||
RayTraceResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance());
|
RayTraceResult res = RayTraceUtils.rayTraceTowards(ctx.player(), place, ctx.playerController().getBlockReachDistance());
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ public enum Moves {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
PARKOUR_NORTH(0, 0, -4, true, false) {
|
PARKOUR_NORTH(0, 0, -4, true, true) {
|
||||||
@Override
|
@Override
|
||||||
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
||||||
return MovementParkour.cost(context, src, EnumFacing.NORTH);
|
return MovementParkour.cost(context, src, EnumFacing.NORTH);
|
||||||
@@ -288,7 +288,7 @@ public enum Moves {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
PARKOUR_SOUTH(0, 0, +4, true, false) {
|
PARKOUR_SOUTH(0, 0, +4, true, true) {
|
||||||
@Override
|
@Override
|
||||||
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
||||||
return MovementParkour.cost(context, src, EnumFacing.SOUTH);
|
return MovementParkour.cost(context, src, EnumFacing.SOUTH);
|
||||||
@@ -300,7 +300,7 @@ public enum Moves {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
PARKOUR_EAST(+4, 0, 0, true, false) {
|
PARKOUR_EAST(+4, 0, 0, true, true) {
|
||||||
@Override
|
@Override
|
||||||
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
||||||
return MovementParkour.cost(context, src, EnumFacing.EAST);
|
return MovementParkour.cost(context, src, EnumFacing.EAST);
|
||||||
@@ -312,7 +312,7 @@ public enum Moves {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
PARKOUR_WEST(-4, 0, 0, true, false) {
|
PARKOUR_WEST(-4, 0, 0, true, true) {
|
||||||
@Override
|
@Override
|
||||||
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
public Movement apply0(CalculationContext context, BetterBlockPos src) {
|
||||||
return MovementParkour.cost(context, src, EnumFacing.WEST);
|
return MovementParkour.cost(context, src, EnumFacing.WEST);
|
||||||
|
|||||||
@@ -44,18 +44,20 @@ public class MovementParkour extends Movement {
|
|||||||
|
|
||||||
private final EnumFacing direction;
|
private final EnumFacing direction;
|
||||||
private final int dist;
|
private final int dist;
|
||||||
|
private final boolean ascend;
|
||||||
|
|
||||||
private MovementParkour(IBaritone baritone, BetterBlockPos src, int dist, EnumFacing dir) {
|
private MovementParkour(IBaritone baritone, BetterBlockPos src, int dist, EnumFacing dir, boolean ascend) {
|
||||||
super(baritone, src, src.offset(dir, dist), EMPTY, src.offset(dir, dist).down());
|
super(baritone, src, src.offset(dir, dist).up(ascend ? 1 : 0), EMPTY, src.offset(dir, dist).down(ascend ? 0 : 1));
|
||||||
this.direction = dir;
|
this.direction = dir;
|
||||||
this.dist = dist;
|
this.dist = dist;
|
||||||
|
this.ascend = ascend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MovementParkour cost(CalculationContext context, BetterBlockPos src, EnumFacing direction) {
|
public static MovementParkour cost(CalculationContext context, BetterBlockPos src, EnumFacing direction) {
|
||||||
MutableMoveResult res = new MutableMoveResult();
|
MutableMoveResult res = new MutableMoveResult();
|
||||||
cost(context, src.x, src.y, src.z, direction, res);
|
cost(context, src.x, src.y, src.z, direction, res);
|
||||||
int dist = Math.abs(res.x - src.x) + Math.abs(res.z - src.z);
|
int dist = Math.abs(res.x - src.x) + Math.abs(res.z - src.z);
|
||||||
return new MovementParkour(context.getBaritone(), src, dist, direction);
|
return new MovementParkour(context.getBaritone(), src, dist, direction, res.y > src.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cost(CalculationContext context, int x, int y, int z, EnumFacing dir, MutableMoveResult res) {
|
public static void cost(CalculationContext context, int x, int y, int z, EnumFacing dir, MutableMoveResult res) {
|
||||||
@@ -104,19 +106,36 @@ public class MovementParkour extends Movement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 2; i <= maxJump; i++) {
|
for (int i = 2; i <= maxJump; i++) {
|
||||||
// TODO perhaps dest.up(3) doesn't need to be fullyPassable, just canWalkThrough, possibly?
|
int destX = x + xDiff * i;
|
||||||
for (int y2 = 0; y2 < 4; y2++) {
|
int destZ = z + zDiff * i;
|
||||||
if (!MovementHelper.fullyPassable(context, x + xDiff * i, y + y2, z + zDiff * i)) {
|
if (!MovementHelper.fullyPassable(context, destX, y + 1, destZ)) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
IBlockState landingOn = context.bsi.get0(x + xDiff * i, y - 1, z + zDiff * i);
|
if (!MovementHelper.fullyPassable(context, destX, y + 2, destZ)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IBlockState destInto = context.bsi.get0(destX, y, destZ);
|
||||||
|
if (!MovementHelper.fullyPassable(destInto)) {
|
||||||
|
if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) {
|
||||||
|
res.x = destX;
|
||||||
|
res.y = y + 1;
|
||||||
|
res.z = destZ;
|
||||||
|
res.cost = i * SPRINT_ONE_BLOCK_COST + context.jumpPenalty;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IBlockState landingOn = context.bsi.get0(destX, y - 1, destZ);
|
||||||
// farmland needs to be canwalkon otherwise farm can never work at all, but we want to specifically disallow ending a jumy on farmland haha
|
// farmland needs to be canwalkon otherwise farm can never work at all, but we want to specifically disallow ending a jumy on farmland haha
|
||||||
if (landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context.bsi, x + xDiff * i, y - 1, z + zDiff * i, landingOn)) {
|
if (landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) {
|
||||||
res.x = x + xDiff * i;
|
if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) {
|
||||||
res.y = y;
|
res.x = destX;
|
||||||
res.z = z + zDiff * i;
|
res.y = y;
|
||||||
res.cost = costFromJumpDistance(i) + context.jumpPenalty;
|
res.z = destZ;
|
||||||
|
res.cost = costFromJumpDistance(i) + context.jumpPenalty;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!MovementHelper.fullyPassable(context, destX, y + 3, destZ)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,6 +156,9 @@ public class MovementParkour extends Movement {
|
|||||||
if (!MovementHelper.isReplacable(destX, y - 1, destZ, toReplace, context.bsi)) {
|
if (!MovementHelper.isReplacable(destX, y - 1, destZ, toReplace, context.bsi)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset();
|
int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset();
|
||||||
int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset();
|
int againstY = y - 1 + HORIZONTALS_BUT_ALSO_DOWN_____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset();
|
||||||
@@ -154,6 +176,11 @@ public class MovementParkour extends Movement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean checkOvershootSafety(BlockStateInterface bsi, int x, int y, int z) {
|
||||||
|
// we're going to walk into these two blocks after the landing of the parkour anyway, so make sure they aren't avoidWalkingInto
|
||||||
|
return !MovementHelper.avoidWalkingInto(bsi.get0(x, y, z)) && !MovementHelper.avoidWalkingInto(bsi.get0(x, y + 1, z));
|
||||||
|
}
|
||||||
|
|
||||||
private static double costFromJumpDistance(int dist) {
|
private static double costFromJumpDistance(int dist) {
|
||||||
switch (dist) {
|
switch (dist) {
|
||||||
case 2:
|
case 2:
|
||||||
@@ -172,7 +199,7 @@ public class MovementParkour extends Movement {
|
|||||||
public double calculateCost(CalculationContext context) {
|
public double calculateCost(CalculationContext context) {
|
||||||
MutableMoveResult res = new MutableMoveResult();
|
MutableMoveResult res = new MutableMoveResult();
|
||||||
cost(context, src.x, src.y, src.z, direction, res);
|
cost(context, src.x, src.y, src.z, direction, res);
|
||||||
if (res.x != dest.x || res.z != dest.z) {
|
if (res.x != dest.x || res.y != dest.y || res.z != dest.z) {
|
||||||
return COST_INF;
|
return COST_INF;
|
||||||
}
|
}
|
||||||
return res.cost;
|
return res.cost;
|
||||||
@@ -203,16 +230,12 @@ public class MovementParkour extends Movement {
|
|||||||
if (state.getStatus() != MovementStatus.RUNNING) {
|
if (state.getStatus() != MovementStatus.RUNNING) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
if (ctx.player().isHandActive()) {
|
|
||||||
logDebug("Pausing parkour since hand is active");
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
if (ctx.playerFeet().y < src.y) {
|
if (ctx.playerFeet().y < src.y) {
|
||||||
// we have fallen
|
// we have fallen
|
||||||
logDebug("sorry");
|
logDebug("sorry");
|
||||||
return state.setStatus(MovementStatus.UNREACHABLE);
|
return state.setStatus(MovementStatus.UNREACHABLE);
|
||||||
}
|
}
|
||||||
if (dist >= 4) {
|
if (dist >= 4 || ascend) {
|
||||||
state.setInput(Input.SPRINT, true);
|
state.setInput(Input.SPRINT, true);
|
||||||
}
|
}
|
||||||
MovementHelper.moveTowards(ctx, state, dest);
|
MovementHelper.moveTowards(ctx, state, dest);
|
||||||
@@ -232,7 +255,8 @@ public class MovementParkour extends Movement {
|
|||||||
// go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory
|
// go in the opposite order to check DOWN before all horizontals -- down is preferable because you don't have to look to the side while in midair, which could mess up the trajectory
|
||||||
state.setInput(Input.CLICK_RIGHT, true);
|
state.setInput(Input.CLICK_RIGHT, true);
|
||||||
}
|
}
|
||||||
if (dist == 3) { // this is a 2 block gap, dest = src + direction * 3
|
// prevent jumping too late by checking for ascend
|
||||||
|
if (dist == 3 && !ascend) { // this is a 2 block gap, dest = src + direction * 3
|
||||||
double xDiff = (src.x + 0.5) - ctx.player().posX;
|
double xDiff = (src.x + 0.5) - ctx.player().posX;
|
||||||
double zDiff = (src.z + 0.5) - ctx.player().posZ;
|
double zDiff = (src.z + 0.5) - ctx.player().posZ;
|
||||||
double distFromStart = Math.max(Math.abs(xDiff), Math.abs(zDiff));
|
double distFromStart = Math.max(Math.abs(xDiff), Math.abs(zDiff));
|
||||||
|
|||||||
@@ -42,8 +42,7 @@ import java.util.*;
|
|||||||
import static baritone.api.pathing.movement.MovementStatus.*;
|
import static baritone.api.pathing.movement.MovementStatus.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Behavior to execute a precomputed path. Does not (yet) deal with path segmentation or stitching
|
* Behavior to execute a precomputed path
|
||||||
* or cutting (jumping onto the next path if it starts with a backtrack of this path's ending)
|
|
||||||
*
|
*
|
||||||
* @author leijurv
|
* @author leijurv
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -451,7 +451,6 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Goal goal = assemble(bcc, approxPlacable.subList(0, 9));
|
Goal goal = assemble(bcc, approxPlacable.subList(0, 9));
|
||||||
if (goal == null) {
|
if (goal == null) {
|
||||||
goal = assemble(bcc, approxPlacable); // we're far away, so assume that we have our whole inventory to recalculate placable properly
|
goal = assemble(bcc, approxPlacable); // we're far away, so assume that we have our whole inventory to recalculate placable properly
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ public final class ExploreProcess extends BaritoneProcessHelper implements IExpl
|
|||||||
centers.add(new BlockPos(centerX, 0, centerZ));
|
centers.add(new BlockPos(centerX, 0, centerZ));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dist % 10 == 0) {
|
||||||
|
count = Math.min(filter.countRemain(), Baritone.settings().exploreChunkSetMinimumSize.value);
|
||||||
|
}
|
||||||
if (centers.size() >= count) {
|
if (centers.size() >= count) {
|
||||||
return centers.stream().map(pos -> createGoal(pos.getX(), pos.getZ())).toArray(Goal[]::new);
|
return centers.stream().map(pos -> createGoal(pos.getX(), pos.getZ())).toArray(Goal[]::new);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,9 +125,7 @@ public final class PathRenderer implements Helper {
|
|||||||
|
|
||||||
public static void drawPath(IPath path, int startIndex, Entity player, float partialTicks, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) {
|
public static void drawPath(IPath path, int startIndex, Entity player, float partialTicks, Color color, boolean fadeOut, int fadeStart0, int fadeEnd0) {
|
||||||
GlStateManager.enableBlend();
|
GlStateManager.enableBlend();
|
||||||
GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
GlStateManager.disableLighting();
|
||||||
GlStateManager.color4f(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F);
|
|
||||||
GlStateManager.lineWidth(Baritone.settings().pathRenderLineWidthPixels.value);
|
|
||||||
GlStateManager.disableTexture2D();
|
GlStateManager.disableTexture2D();
|
||||||
GlStateManager.depthMask(false);
|
GlStateManager.depthMask(false);
|
||||||
if (Baritone.settings().renderPathIgnoreDepth.value) {
|
if (Baritone.settings().renderPathIgnoreDepth.value) {
|
||||||
@@ -179,6 +177,7 @@ public final class PathRenderer implements Helper {
|
|||||||
//GlStateManager.color(0.0f, 0.0f, 0.0f, 0.4f);
|
//GlStateManager.color(0.0f, 0.0f, 0.0f, 0.4f);
|
||||||
GlStateManager.depthMask(true);
|
GlStateManager.depthMask(true);
|
||||||
GlStateManager.enableTexture2D();
|
GlStateManager.enableTexture2D();
|
||||||
|
GlStateManager.enableLighting();
|
||||||
GlStateManager.disableBlend();
|
GlStateManager.disableBlend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user