More fixes
-Add lots of materials to LegacyMats -Improved ground checking for non-solid blocks -Fixed tons of edge cases involving climbing over/going into walls
This commit is contained in:
@@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile;
|
|||||||
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
import net.nuggetmc.tplus.api.agent.legacyagent.ai.NeuralNetwork;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.LivingEntity;
|
import org.bukkit.entity.LivingEntity;
|
||||||
@@ -50,6 +51,8 @@ public interface Terminator {
|
|||||||
boolean isBotInWater();
|
boolean isBotInWater();
|
||||||
|
|
||||||
boolean isBotOnGround();
|
boolean isBotOnGround();
|
||||||
|
|
||||||
|
Block getStandingOn();
|
||||||
|
|
||||||
void setBotPitch(float pitch);
|
void setBotPitch(float pitch);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import net.nuggetmc.tplus.api.utils.PlayerUtils;
|
|||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
|
import org.bukkit.block.data.type.*;
|
||||||
import org.bukkit.entity.*;
|
import org.bukkit.entity.*;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
@@ -44,23 +45,6 @@ public class LegacyAgent extends Agent {
|
|||||||
private final Map<BukkitRunnable, Byte> mining = new HashMap<>();
|
private final Map<BukkitRunnable, Byte> mining = new HashMap<>();
|
||||||
private final Set<Terminator> fallDamageCooldown = new HashSet<>();
|
private final Set<Terminator> fallDamageCooldown = new HashSet<>();
|
||||||
public boolean offsets = true;
|
public boolean offsets = true;
|
||||||
private List<Material> instantBreakBlocks = Arrays.asList(
|
|
||||||
Material.TALL_GRASS,
|
|
||||||
Material.GRASS,
|
|
||||||
Material.KELP_PLANT,
|
|
||||||
Material.WHEAT,
|
|
||||||
Material.POTATOES,
|
|
||||||
Material.CARROT,
|
|
||||||
Material.BEETROOT,
|
|
||||||
Material.SUGAR_CANE,
|
|
||||||
Material.SWEET_BERRY_BUSH,
|
|
||||||
Material.LILY_PAD,
|
|
||||||
Material.DANDELION,
|
|
||||||
Material.POPPY,
|
|
||||||
Material.ROSE_BUSH,
|
|
||||||
Material.PUMPKIN_STEM,
|
|
||||||
Material.MELON_STEM
|
|
||||||
);
|
|
||||||
private EnumTargetGoal goal;
|
private EnumTargetGoal goal;
|
||||||
|
|
||||||
public LegacyAgent(BotManager manager, Plugin plugin) {
|
public LegacyAgent(BotManager manager, Plugin plugin) {
|
||||||
@@ -181,7 +165,9 @@ public class LegacyAgent extends Agent {
|
|||||||
|
|
||||||
if (checkAt(bot, block, botPlayer)) return;
|
if (checkAt(bot, block, botPlayer)) return;
|
||||||
|
|
||||||
if (checkFence(bot, loc.getBlock(), botPlayer)) return;
|
if (checkFenceAndGates(bot, loc.getBlock(), botPlayer)) return;
|
||||||
|
|
||||||
|
if (checkObstacles(bot, loc.getBlock(), botPlayer)) return;
|
||||||
|
|
||||||
if (checkDown(bot, botPlayer, livingTarget.getLocation(), bothXZ)) return;
|
if (checkDown(bot, botPlayer, livingTarget.getLocation(), bothXZ)) return;
|
||||||
|
|
||||||
@@ -443,7 +429,7 @@ public class LegacyAgent extends Agent {
|
|||||||
if (level == null) {
|
if (level == null) {
|
||||||
resetHand(npc, target, playerNPC);
|
resetHand(npc, target, playerNPC);
|
||||||
return 1;
|
return 1;
|
||||||
} else if (level.isSide()) {
|
} else if (level.isSide() || level == LegacyLevel.BELOW) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return 2;
|
return 2;
|
||||||
@@ -467,6 +453,40 @@ public class LegacyAgent extends Agent {
|
|||||||
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
||||||
get = get.getLocation().add(0, -1, 0).getBlock();
|
get = get.getLocation().add(0, -1, 0).getBlock();
|
||||||
level = LegacyLevel.NORTH_D;
|
level = LegacyLevel.NORTH_D;
|
||||||
|
} else if (LegacyMats.FENCE.contains(get.getLocation().add(0, -2, 0).getBlock().getType())) {
|
||||||
|
get = get.getLocation().add(0, -2, 0).getBlock();
|
||||||
|
level = LegacyLevel.NORTH_D_2;
|
||||||
|
} else {
|
||||||
|
Block standing = npc.getStandingOn();
|
||||||
|
if(standing == null)
|
||||||
|
break;
|
||||||
|
boolean obstructed = standing.getLocation().getBlockY() == player.getLocation().getBlockY()
|
||||||
|
|| (standing.getLocation().getBlockY() + 1 == player.getLocation().getBlockY()
|
||||||
|
&& (LegacyMats.FENCE.contains(standing.getType()) || LegacyMats.GATES.contains(standing.getType())));
|
||||||
|
if(obstructed) {
|
||||||
|
Block belowStanding = standing.getLocation().add(0, -1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(belowStanding.getType()) && !LegacyMats.NONSOLID.contains(belowStanding.getType())) {
|
||||||
|
//Break standing block
|
||||||
|
get = standing;
|
||||||
|
level = LegacyLevel.BELOW;
|
||||||
|
|
||||||
|
noJump.add(player);
|
||||||
|
scheduler.runTaskLater(plugin, () -> {
|
||||||
|
noJump.remove(player);
|
||||||
|
}, 15);
|
||||||
|
} else {
|
||||||
|
//Break above
|
||||||
|
Block above = npc.getLocation().add(0, 2, 0).getBlock();
|
||||||
|
Block aboveSide = get.getLocation().add(0, 1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(above.getType())) {
|
||||||
|
get = above;
|
||||||
|
level = LegacyLevel.ABOVE;
|
||||||
|
} else if(!LegacyMats.BREAK.contains(aboveSide.getType())) {
|
||||||
|
get = aboveSide;
|
||||||
|
level = LegacyLevel.NORTH_U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SOUTH:
|
case SOUTH:
|
||||||
@@ -476,6 +496,40 @@ public class LegacyAgent extends Agent {
|
|||||||
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
||||||
get = get.getLocation().add(0, -1, 0).getBlock();
|
get = get.getLocation().add(0, -1, 0).getBlock();
|
||||||
level = LegacyLevel.SOUTH_D;
|
level = LegacyLevel.SOUTH_D;
|
||||||
|
} else if (LegacyMats.FENCE.contains(get.getLocation().add(0, -2, 0).getBlock().getType())) {
|
||||||
|
get = get.getLocation().add(0, -2, 0).getBlock();
|
||||||
|
level = LegacyLevel.SOUTH_D_2;
|
||||||
|
} else {
|
||||||
|
Block standing = npc.getStandingOn();
|
||||||
|
if(standing == null)
|
||||||
|
break;
|
||||||
|
boolean obstructed = standing.getLocation().getBlockY() == player.getLocation().getBlockY()
|
||||||
|
|| (standing.getLocation().getBlockY() + 1 == player.getLocation().getBlockY()
|
||||||
|
&& (LegacyMats.FENCE.contains(standing.getType()) || LegacyMats.GATES.contains(standing.getType())));
|
||||||
|
if(obstructed) {
|
||||||
|
Block belowStanding = standing.getLocation().add(0, -1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(belowStanding.getType()) && !LegacyMats.NONSOLID.contains(belowStanding.getType())) {
|
||||||
|
//Break standing block
|
||||||
|
get = standing;
|
||||||
|
level = LegacyLevel.BELOW;
|
||||||
|
|
||||||
|
noJump.add(player);
|
||||||
|
scheduler.runTaskLater(plugin, () -> {
|
||||||
|
noJump.remove(player);
|
||||||
|
}, 15);
|
||||||
|
} else {
|
||||||
|
//Break above
|
||||||
|
Block above = npc.getLocation().add(0, 2, 0).getBlock();
|
||||||
|
Block aboveSide = get.getLocation().add(0, 1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(above.getType())) {
|
||||||
|
get = above;
|
||||||
|
level = LegacyLevel.ABOVE;
|
||||||
|
} else if(!LegacyMats.BREAK.contains(aboveSide.getType())) {
|
||||||
|
get = aboveSide;
|
||||||
|
level = LegacyLevel.SOUTH_U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EAST:
|
case EAST:
|
||||||
@@ -485,6 +539,40 @@ public class LegacyAgent extends Agent {
|
|||||||
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
||||||
get = get.getLocation().add(0, -1, 0).getBlock();
|
get = get.getLocation().add(0, -1, 0).getBlock();
|
||||||
level = LegacyLevel.EAST_D;
|
level = LegacyLevel.EAST_D;
|
||||||
|
} else if (LegacyMats.FENCE.contains(get.getLocation().add(0, -2, 0).getBlock().getType())) {
|
||||||
|
get = get.getLocation().add(0, -2, 0).getBlock();
|
||||||
|
level = LegacyLevel.EAST_D_2;
|
||||||
|
} else {
|
||||||
|
Block standing = npc.getStandingOn();
|
||||||
|
if(standing == null)
|
||||||
|
break;
|
||||||
|
boolean obstructed = standing.getLocation().getBlockY() == player.getLocation().getBlockY()
|
||||||
|
|| (standing.getLocation().getBlockY() + 1 == player.getLocation().getBlockY()
|
||||||
|
&& (LegacyMats.FENCE.contains(standing.getType()) || LegacyMats.GATES.contains(standing.getType())));
|
||||||
|
if(obstructed) {
|
||||||
|
Block belowStanding = standing.getLocation().add(0, -1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(belowStanding.getType()) && !LegacyMats.NONSOLID.contains(belowStanding.getType())) {
|
||||||
|
//Break standing block
|
||||||
|
get = standing;
|
||||||
|
level = LegacyLevel.BELOW;
|
||||||
|
|
||||||
|
noJump.add(player);
|
||||||
|
scheduler.runTaskLater(plugin, () -> {
|
||||||
|
noJump.remove(player);
|
||||||
|
}, 15);
|
||||||
|
} else {
|
||||||
|
//Break above
|
||||||
|
Block above = npc.getLocation().add(0, 2, 0).getBlock();
|
||||||
|
Block aboveSide = get.getLocation().add(0, 1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(above.getType())) {
|
||||||
|
get = above;
|
||||||
|
level = LegacyLevel.ABOVE;
|
||||||
|
} else if(!LegacyMats.BREAK.contains(aboveSide.getType())) {
|
||||||
|
get = aboveSide;
|
||||||
|
level = LegacyLevel.EAST_U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WEST:
|
case WEST:
|
||||||
@@ -494,15 +582,51 @@ public class LegacyAgent extends Agent {
|
|||||||
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
} else if (checkSideBreak(get.getLocation().add(0, -1, 0).getBlock().getType())) {
|
||||||
get = get.getLocation().add(0, -1, 0).getBlock();
|
get = get.getLocation().add(0, -1, 0).getBlock();
|
||||||
level = LegacyLevel.WEST_D;
|
level = LegacyLevel.WEST_D;
|
||||||
|
} else if (LegacyMats.FENCE.contains(get.getLocation().add(0, -2, 0).getBlock().getType())) {
|
||||||
|
get = get.getLocation().add(0, -2, 0).getBlock();
|
||||||
|
level = LegacyLevel.WEST_D_2;
|
||||||
|
} else {
|
||||||
|
Block standing = npc.getStandingOn();
|
||||||
|
if(standing == null)
|
||||||
|
break;
|
||||||
|
boolean obstructed = standing.getLocation().getBlockY() == player.getLocation().getBlockY()
|
||||||
|
|| (standing.getLocation().getBlockY() + 1 == player.getLocation().getBlockY()
|
||||||
|
&& (LegacyMats.FENCE.contains(standing.getType()) || LegacyMats.GATES.contains(standing.getType())));
|
||||||
|
if(obstructed) {
|
||||||
|
Block belowStanding = standing.getLocation().add(0, -1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(belowStanding.getType()) && !LegacyMats.NONSOLID.contains(belowStanding.getType())) {
|
||||||
|
//Break standing block
|
||||||
|
get = standing;
|
||||||
|
level = LegacyLevel.BELOW;
|
||||||
|
|
||||||
|
noJump.add(player);
|
||||||
|
scheduler.runTaskLater(plugin, () -> {
|
||||||
|
noJump.remove(player);
|
||||||
|
}, 15);
|
||||||
|
} else {
|
||||||
|
//Break above
|
||||||
|
Block above = npc.getLocation().add(0, 2, 0).getBlock();
|
||||||
|
Block aboveSide = get.getLocation().add(0, 1, 0).getBlock();
|
||||||
|
if(!LegacyMats.BREAK.contains(above.getType())) {
|
||||||
|
get = above;
|
||||||
|
level = LegacyLevel.ABOVE;
|
||||||
|
} else if(!LegacyMats.BREAK.contains(aboveSide.getType())) {
|
||||||
|
get = aboveSide;
|
||||||
|
level = LegacyLevel.WEST_U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level == LegacyLevel.EAST_D || level == LegacyLevel.WEST_D || level == LegacyLevel.NORTH_D || level == LegacyLevel.SOUTH_D) {
|
if (level == LegacyLevel.EAST_D || level == LegacyLevel.WEST_D || level == LegacyLevel.NORTH_D || level == LegacyLevel.SOUTH_D
|
||||||
|
|| level == LegacyLevel.EAST_D_2 || level == LegacyLevel.WEST_D_2 || level == LegacyLevel.NORTH_D_2 || level == LegacyLevel.SOUTH_D_2) {
|
||||||
if (LegacyMats.AIR.contains(player.getLocation().add(0, 2, 0).getBlock().getType())
|
if (LegacyMats.AIR.contains(player.getLocation().add(0, 2, 0).getBlock().getType())
|
||||||
&& LegacyMats.AIR.contains(get.getLocation().add(0, 2, 0).getBlock().getType())) {
|
&& LegacyMats.AIR.contains(get.getLocation().add(0, 2, 0).getBlock().getType())
|
||||||
|
&& !LegacyMats.FENCE.contains(get.getType()) && !LegacyMats.GATES.contains(get.getType())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -658,7 +782,9 @@ public class LegacyAgent extends Agent {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (c && npc.getLocation().getBlockY() > loc.getBlockY() + 1) {
|
if (c && npc.getLocation().getBlockY() > loc.getBlockY() + 1) {
|
||||||
Block block = npc.getLocation().add(0, -1, 0).getBlock();
|
Block block = npc.getStandingOn();
|
||||||
|
if (block == null)
|
||||||
|
return false;
|
||||||
npc.look(BlockFace.DOWN);
|
npc.look(BlockFace.DOWN);
|
||||||
|
|
||||||
downMine(npc, player, block);
|
downMine(npc, player, block);
|
||||||
@@ -672,7 +798,9 @@ public class LegacyAgent extends Agent {
|
|||||||
b.setY(0);
|
b.setY(0);
|
||||||
|
|
||||||
if (npc.getLocation().getBlockY() > loc.getBlockY() + 10 && a.distance(b) < 10) {
|
if (npc.getLocation().getBlockY() > loc.getBlockY() + 10 && a.distance(b) < 10) {
|
||||||
Block block = npc.getLocation().add(0, -1, 0).getBlock();
|
Block block = npc.getStandingOn();
|
||||||
|
if (block == null)
|
||||||
|
return false;
|
||||||
npc.look(BlockFace.DOWN);
|
npc.look(BlockFace.DOWN);
|
||||||
|
|
||||||
downMine(npc, player, block);
|
downMine(npc, player, block);
|
||||||
@@ -720,8 +848,17 @@ public class LegacyAgent extends Agent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkFence(Terminator bot, Block block, LivingEntity player) {
|
private boolean checkFenceAndGates(Terminator bot, Block block, LivingEntity player) {
|
||||||
if (LegacyMats.FENCE.contains(block.getType())) {
|
if (LegacyMats.FENCE.contains(block.getType()) || LegacyMats.GATES.contains(block.getType())) {
|
||||||
|
preBreak(bot, player, block, LegacyLevel.AT_D);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkObstacles(Terminator bot, Block block, LivingEntity player) {
|
||||||
|
if (LegacyMats.OBSTACLES.contains(block.getType())) {
|
||||||
preBreak(bot, player, block, LegacyLevel.AT_D);
|
preBreak(bot, player, block, LegacyLevel.AT_D);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -752,13 +889,16 @@ public class LegacyAgent extends Agent {
|
|||||||
|
|
||||||
bot.setItem(new ItemStack(item));
|
bot.setItem(new ItemStack(item));
|
||||||
|
|
||||||
if (level == LegacyLevel.EAST_D || level == LegacyLevel.NORTH_D || level == LegacyLevel.SOUTH_D || level == LegacyLevel.WEST_D) {
|
if (level == LegacyLevel.EAST_D || level == LegacyLevel.NORTH_D || level == LegacyLevel.SOUTH_D || level == LegacyLevel.WEST_D
|
||||||
|
|| level == LegacyLevel.EAST_D_2 || level == LegacyLevel.NORTH_D_2 || level == LegacyLevel.SOUTH_D_2 || level == LegacyLevel.WEST_D_2) {
|
||||||
bot.setBotPitch(69);
|
bot.setBotPitch(69);
|
||||||
|
|
||||||
scheduler.runTaskLater(plugin, () -> {
|
scheduler.runTaskLater(plugin, () -> {
|
||||||
btCheck.put(player, true);
|
btCheck.put(player, true);
|
||||||
}, 5);
|
}, 5);
|
||||||
} else if (level == LegacyLevel.AT_D || level == LegacyLevel.AT) {
|
} else if (level == LegacyLevel.EAST_U || level == LegacyLevel.NORTH_U || level == LegacyLevel.SOUTH_U || level == LegacyLevel.WEST_U) {
|
||||||
|
bot.setBotPitch(-53);
|
||||||
|
}else if (level == LegacyLevel.AT_D || level == LegacyLevel.AT) {
|
||||||
Location blockLoc = block.getLocation().add(0.5, -1, 0.5);
|
Location blockLoc = block.getLocation().add(0.5, -1, 0.5);
|
||||||
bot.faceLocation(blockLoc);
|
bot.faceLocation(blockLoc);
|
||||||
}
|
}
|
||||||
@@ -778,10 +918,10 @@ public class LegacyAgent extends Agent {
|
|||||||
miningAnim.put(player, task);
|
miningAnim.put(player, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
blockBreakEffect(player, block, level);
|
blockBreakEffect(bot, player, block, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void blockBreakEffect(LivingEntity player, Block block, LegacyLevel level) {
|
private void blockBreakEffect(Terminator bot, LivingEntity player, Block block, LegacyLevel level) {
|
||||||
|
|
||||||
if (LegacyMats.NO_CRACK.contains(block.getType())) return;
|
if (LegacyMats.NO_CRACK.contains(block.getType())) return;
|
||||||
|
|
||||||
@@ -798,7 +938,19 @@ public class LegacyAgent extends Agent {
|
|||||||
cur = player.getLocation().add(0, 2, 0).getBlock();
|
cur = player.getLocation().add(0, 2, 0).getBlock();
|
||||||
break;
|
break;
|
||||||
case BELOW:
|
case BELOW:
|
||||||
cur = player.getLocation().add(0, -1, 0).getBlock();
|
cur = bot.getStandingOn();
|
||||||
|
break;
|
||||||
|
case NORTH_U:
|
||||||
|
cur = player.getLocation().add(0, 2, -1).getBlock();
|
||||||
|
break;
|
||||||
|
case SOUTH_U:
|
||||||
|
cur = player.getLocation().add(0, 2, 1).getBlock();
|
||||||
|
break;
|
||||||
|
case EAST_U:
|
||||||
|
cur = player.getLocation().add(1, 2, 0).getBlock();
|
||||||
|
break;
|
||||||
|
case WEST_U:
|
||||||
|
cur = player.getLocation().add(-1, 2, 0).getBlock();
|
||||||
break;
|
break;
|
||||||
case NORTH:
|
case NORTH:
|
||||||
cur = player.getLocation().add(0, 1, -1).getBlock();
|
cur = player.getLocation().add(0, 1, -1).getBlock();
|
||||||
@@ -824,6 +976,18 @@ public class LegacyAgent extends Agent {
|
|||||||
case WEST_D:
|
case WEST_D:
|
||||||
cur = player.getLocation().add(-1, 0, 0).getBlock();
|
cur = player.getLocation().add(-1, 0, 0).getBlock();
|
||||||
break;
|
break;
|
||||||
|
case NORTH_D_2:
|
||||||
|
cur = player.getLocation().add(0, -1, -1).getBlock();
|
||||||
|
break;
|
||||||
|
case SOUTH_D_2:
|
||||||
|
cur = player.getLocation().add(0, -1, 1).getBlock();
|
||||||
|
break;
|
||||||
|
case EAST_D_2:
|
||||||
|
cur = player.getLocation().add(1, -1, 0).getBlock();
|
||||||
|
break;
|
||||||
|
case WEST_D_2:
|
||||||
|
cur = player.getLocation().add(-1, -1, 0).getBlock();
|
||||||
|
break;
|
||||||
case AT_D:
|
case AT_D:
|
||||||
cur = player.getLocation().getBlock();
|
cur = player.getLocation().getBlock();
|
||||||
break;
|
break;
|
||||||
@@ -833,7 +997,7 @@ public class LegacyAgent extends Agent {
|
|||||||
|
|
||||||
// wow this repeated code is so bad lmao
|
// wow this repeated code is so bad lmao
|
||||||
|
|
||||||
if (player.isDead() || (!block.equals(cur) || block.getType() != cur.getType())) {
|
if (player.isDead() || cur == null || (!block.equals(cur) || block.getType() != cur.getType())) {
|
||||||
this.cancel();
|
this.cancel();
|
||||||
|
|
||||||
TerminatorPlusAPI.getInternalBridge().sendBlockDestructionPacket(crackList.get(block), block.getX(), block.getY(), block.getZ(), -1);
|
TerminatorPlusAPI.getInternalBridge().sendBlockDestructionPacket(crackList.get(block), block.getX(), block.getY(), block.getZ(), -1);
|
||||||
@@ -882,7 +1046,7 @@ public class LegacyAgent extends Agent {
|
|||||||
|| block.getType() == Material.CHAIN_COMMAND_BLOCK)
|
|| block.getType() == Material.CHAIN_COMMAND_BLOCK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (instantBreakBlocks.contains(block.getType())) { // instant break blocks
|
if (LegacyMats.INSTANT_BREAK.contains(block.getType())) { // instant break blocks
|
||||||
block.breakNaturally();
|
block.breakNaturally();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ public enum LegacyLevel {
|
|||||||
BELOW,
|
BELOW,
|
||||||
AT,
|
AT,
|
||||||
AT_D,
|
AT_D,
|
||||||
|
NORTH_U,
|
||||||
|
SOUTH_U,
|
||||||
|
EAST_U,
|
||||||
|
WEST_U,
|
||||||
NORTH,
|
NORTH,
|
||||||
SOUTH,
|
SOUTH,
|
||||||
EAST,
|
EAST,
|
||||||
@@ -16,20 +20,20 @@ public enum LegacyLevel {
|
|||||||
NORTH_D,
|
NORTH_D,
|
||||||
SOUTH_D,
|
SOUTH_D,
|
||||||
EAST_D,
|
EAST_D,
|
||||||
WEST_D;
|
WEST_D,
|
||||||
|
NORTH_D_2,
|
||||||
|
SOUTH_D_2,
|
||||||
|
EAST_D_2,
|
||||||
|
WEST_D_2;
|
||||||
|
|
||||||
private static final Set<LegacyLevel> SIDE = new HashSet<>(Arrays.asList(
|
private static final Set<LegacyLevel> NON_SIDE = new HashSet<>(Arrays.asList(
|
||||||
NORTH,
|
ABOVE,
|
||||||
SOUTH,
|
BELOW,
|
||||||
EAST,
|
AT,
|
||||||
WEST,
|
AT_D
|
||||||
NORTH_D,
|
|
||||||
SOUTH_D,
|
|
||||||
EAST_D,
|
|
||||||
WEST_D
|
|
||||||
));
|
));
|
||||||
|
|
||||||
public boolean isSide() {
|
public boolean isSide() {
|
||||||
return SIDE.contains(this);
|
return !NON_SIDE.contains(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package net.nuggetmc.tplus.api.agent.legacyagent;
|
package net.nuggetmc.tplus.api.agent.legacyagent;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.data.type.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class LegacyMats {
|
public class LegacyMats {
|
||||||
|
|
||||||
@@ -40,7 +47,7 @@ public class LegacyMats {
|
|||||||
Material.SOUL_FIRE
|
Material.SOUL_FIRE
|
||||||
));
|
));
|
||||||
|
|
||||||
public static final Set<Material> SHOVEL = new HashSet<>(Arrays.asList(
|
public static final Set<Material> SHOVEL = new HashSet<>(concatTypes(Lists.newArrayList(
|
||||||
Material.CLAY,
|
Material.CLAY,
|
||||||
Material.DIRT,
|
Material.DIRT,
|
||||||
Material.GRASS_BLOCK,
|
Material.GRASS_BLOCK,
|
||||||
@@ -55,23 +62,8 @@ public class LegacyMats {
|
|||||||
Material.SOUL_SAND,
|
Material.SOUL_SAND,
|
||||||
Material.SOUL_SOIL,
|
Material.SOUL_SOIL,
|
||||||
Material.SNOW,
|
Material.SNOW,
|
||||||
Material.SNOW_BLOCK,
|
Material.SNOW_BLOCK
|
||||||
Material.BLACK_CONCRETE_POWDER,
|
), Arrays.asList(), Arrays.asList(), m -> m.name().endsWith("_CONCRETE_POWDER")));
|
||||||
Material.BLUE_CONCRETE_POWDER,
|
|
||||||
Material.CYAN_CONCRETE_POWDER,
|
|
||||||
Material.GRAY_CONCRETE_POWDER,
|
|
||||||
Material.GREEN_CONCRETE_POWDER,
|
|
||||||
Material.LIGHT_BLUE_CONCRETE_POWDER,
|
|
||||||
Material.LIGHT_GRAY_CONCRETE_POWDER,
|
|
||||||
Material.LIME_CONCRETE_POWDER,
|
|
||||||
Material.MAGENTA_CONCRETE_POWDER,
|
|
||||||
Material.ORANGE_CONCRETE_POWDER,
|
|
||||||
Material.PINK_CONCRETE_POWDER,
|
|
||||||
Material.PURPLE_CONCRETE_POWDER,
|
|
||||||
Material.RED_CONCRETE_POWDER,
|
|
||||||
Material.WHITE_CONCRETE_POWDER,
|
|
||||||
Material.YELLOW_CONCRETE_POWDER
|
|
||||||
));
|
|
||||||
|
|
||||||
public static final Set<Material> AXE = new HashSet<>(Arrays.asList(
|
public static final Set<Material> AXE = new HashSet<>(Arrays.asList(
|
||||||
Material.OAK_PLANKS, Material.OAK_DOOR, Material.OAK_FENCE, Material.OAK_FENCE_GATE, Material.OAK_LOG,
|
Material.OAK_PLANKS, Material.OAK_DOOR, Material.OAK_FENCE, Material.OAK_FENCE_GATE, Material.OAK_LOG,
|
||||||
@@ -101,13 +93,13 @@ public class LegacyMats {
|
|||||||
Material.LAVA,
|
Material.LAVA,
|
||||||
Material.TALL_GRASS,
|
Material.TALL_GRASS,
|
||||||
Material.SNOW,
|
Material.SNOW,
|
||||||
Material.DIRT_PATH,
|
|
||||||
Material.CAVE_AIR,
|
Material.CAVE_AIR,
|
||||||
Material.VINE,
|
Material.VINE,
|
||||||
Material.FERN,
|
Material.FERN,
|
||||||
Material.LARGE_FERN,
|
Material.LARGE_FERN,
|
||||||
Material.SUGAR_CANE,
|
Material.SUGAR_CANE,
|
||||||
Material.TWISTING_VINES,
|
Material.TWISTING_VINES,
|
||||||
|
Material.TWISTING_VINES_PLANT,
|
||||||
Material.WEEPING_VINES,
|
Material.WEEPING_VINES,
|
||||||
Material.SEAGRASS,
|
Material.SEAGRASS,
|
||||||
Material.TALL_SEAGRASS,
|
Material.TALL_SEAGRASS,
|
||||||
@@ -165,85 +157,133 @@ public class LegacyMats {
|
|||||||
Material.WATER
|
Material.WATER
|
||||||
));
|
));
|
||||||
|
|
||||||
public static final Set<Material> FENCE = new HashSet<>(Arrays.asList(
|
public static final Set<Material> FENCE = new HashSet<>(concatTypes(new ArrayList<>(),
|
||||||
Material.OAK_FENCE,
|
Arrays.asList(Material.GLASS_PANE, Material.IRON_BARS), Arrays.asList(Fence.class, Wall.class)));
|
||||||
Material.ACACIA_FENCE,
|
|
||||||
Material.BIRCH_FENCE,
|
|
||||||
Material.CRIMSON_FENCE,
|
|
||||||
Material.DARK_OAK_FENCE,
|
|
||||||
Material.JUNGLE_FENCE,
|
|
||||||
Material.NETHER_BRICK_FENCE,
|
|
||||||
Material.SPRUCE_FENCE,
|
|
||||||
Material.WARPED_FENCE,
|
|
||||||
Material.MANGROVE_FENCE,
|
|
||||||
Material.COBBLESTONE_WALL,
|
|
||||||
Material.MOSSY_COBBLESTONE_WALL,
|
|
||||||
Material.MOSSY_STONE_BRICK_WALL,
|
|
||||||
Material.STONE_BRICK_WALL,
|
|
||||||
Material.PRISMARINE_WALL,
|
|
||||||
Material.ANDESITE_WALL,
|
|
||||||
Material.BLACKSTONE_WALL,
|
|
||||||
Material.POLISHED_BLACKSTONE_BRICK_WALL,
|
|
||||||
Material.POLISHED_BLACKSTONE_WALL,
|
|
||||||
Material.BRICK_WALL,
|
|
||||||
Material.GRANITE_WALL,
|
|
||||||
Material.DIORITE_WALL,
|
|
||||||
Material.SANDSTONE_WALL,
|
|
||||||
Material.RED_SANDSTONE_WALL,
|
|
||||||
Material.RED_NETHER_BRICK_WALL,
|
|
||||||
Material.NETHER_BRICK_WALL,
|
|
||||||
Material.END_STONE_BRICK_WALL,
|
|
||||||
Material.POLISHED_DEEPSLATE_WALL,
|
|
||||||
Material.COBBLED_DEEPSLATE_WALL,
|
|
||||||
Material.DEEPSLATE_BRICK_WALL,
|
|
||||||
Material.DEEPSLATE_TILE_WALL,
|
|
||||||
Material.MUD_BRICK_WALL
|
|
||||||
));
|
|
||||||
|
|
||||||
public static final Set<Material> GATES = new HashSet<>(Arrays.asList(
|
public static final Set<Material> GATES = new HashSet<>(concatTypes(Gate.class));
|
||||||
Material.ACACIA_FENCE_GATE,
|
|
||||||
Material.BIRCH_FENCE_GATE,
|
|
||||||
Material.DARK_OAK_FENCE_GATE,
|
|
||||||
Material.JUNGLE_FENCE_GATE,
|
|
||||||
Material.OAK_FENCE_GATE,
|
|
||||||
Material.SPRUCE_FENCE_GATE,
|
|
||||||
Material.MANGROVE_FENCE_GATE,
|
|
||||||
Material.CRIMSON_FENCE_GATE,
|
|
||||||
Material.WARPED_FENCE_GATE
|
|
||||||
));
|
|
||||||
|
|
||||||
public static final Set<Material> OBSTACLES = new HashSet<>(Arrays.asList(
|
public static final Set<Material> OBSTACLES = new HashSet<>(concatTypes(Lists.newArrayList(
|
||||||
Material.IRON_BARS,
|
Material.IRON_BARS,
|
||||||
Material.CHAIN,
|
Material.CHAIN,
|
||||||
Material.END_ROD,
|
Material.END_ROD,
|
||||||
|
Material.LIGHTNING_ROD,
|
||||||
Material.COBWEB,
|
Material.COBWEB,
|
||||||
|
Material.SWEET_BERRY_BUSH,
|
||||||
Material.FLOWER_POT,
|
Material.FLOWER_POT,
|
||||||
Material.GLASS_PANE
|
Material.GLASS_PANE
|
||||||
));
|
), Arrays.asList(), Arrays.asList(GlassPane.class), m -> m.name().startsWith("POTTED_")));
|
||||||
|
|
||||||
public static final Set<Material> IGNORED = new HashSet<>(Arrays.asList(
|
//Notice: We exclude blocks that cannot exist without a solid block below (such as rails or crops)
|
||||||
Material.ACACIA_BUTTON,
|
public static final Set<Material> NONSOLID = new HashSet<>(concatTypes(Lists.newArrayList(
|
||||||
Material.BIRCH_BUTTON,
|
Material.COBWEB,
|
||||||
Material.CRIMSON_BUTTON,
|
Material.END_GATEWAY,
|
||||||
Material.DARK_OAK_BUTTON,
|
Material.END_PORTAL,
|
||||||
Material.JUNGLE_BUTTON,
|
Material.NETHER_PORTAL,
|
||||||
Material.MANGROVE_BUTTON,
|
Material.CAVE_VINES_PLANT,
|
||||||
Material.OAK_BUTTON,
|
Material.GLOW_LICHEN,
|
||||||
Material.POLISHED_BLACKSTONE_BUTTON,
|
Material.HANGING_ROOTS,
|
||||||
Material.SPRUCE_BUTTON,
|
Material.POWDER_SNOW,
|
||||||
Material.STONE_BUTTON,
|
Material.SCULK_VEIN,
|
||||||
Material.WARPED_BUTTON,
|
Material.STRING,
|
||||||
Material.TWISTING_VINES,
|
Material.TRIPWIRE_HOOK,
|
||||||
Material.TWISTING_VINES_PLANT
|
Material.LADDER,
|
||||||
));
|
Material.VINE,
|
||||||
|
Material.SOUL_WALL_TORCH,
|
||||||
|
Material.REDSTONE_WALL_TORCH,
|
||||||
|
Material.WALL_TORCH,
|
||||||
|
Material.WEEPING_VINES_PLANT,
|
||||||
|
Material.WEEPING_VINES,
|
||||||
|
Material.CAVE_VINES_PLANT,
|
||||||
|
Material.CAVE_VINES
|
||||||
|
), Arrays.asList(), Arrays.asList(Switch.class, CoralWallFan.class, WallSign.class), m -> m.name().endsWith("_WALL_BANNER")));
|
||||||
|
|
||||||
public static final Set<Material> LEAVES = new HashSet<>(Arrays.asList(
|
public static final Set<Material> LEAVES = new HashSet<>(concatTypes(Leaves.class));
|
||||||
Material.BIRCH_LEAVES,
|
|
||||||
Material.DARK_OAK_LEAVES,
|
public static final Set<Material> INSTANT_BREAK = new HashSet<>(concatTypes(Lists.newArrayList(
|
||||||
Material.JUNGLE_LEAVES,
|
Material.TALL_GRASS,
|
||||||
Material.OAK_LEAVES,
|
Material.GRASS,
|
||||||
Material.SPRUCE_LEAVES,
|
Material.FERN,
|
||||||
Material.FLOWERING_AZALEA_LEAVES,
|
Material.LARGE_FERN,
|
||||||
Material.MANGROVE_LEAVES
|
Material.KELP_PLANT,
|
||||||
));
|
Material.DEAD_BUSH,
|
||||||
|
Material.WHEAT_SEEDS,
|
||||||
|
Material.POTATOES,
|
||||||
|
Material.CARROTS,
|
||||||
|
Material.BEETROOT_SEEDS,
|
||||||
|
Material.PUMPKIN_STEM,
|
||||||
|
Material.MELON_STEM,
|
||||||
|
Material.SUGAR_CANE,
|
||||||
|
Material.SWEET_BERRY_BUSH,
|
||||||
|
Material.LILY_PAD,
|
||||||
|
Material.DANDELION,
|
||||||
|
Material.POPPY,
|
||||||
|
Material.BLUE_ORCHID,
|
||||||
|
Material.ALLIUM,
|
||||||
|
Material.AZURE_BLUET,
|
||||||
|
Material.RED_TULIP,
|
||||||
|
Material.ORANGE_TULIP,
|
||||||
|
Material.WHITE_TULIP,
|
||||||
|
Material.PINK_TULIP,
|
||||||
|
Material.OXEYE_DAISY,
|
||||||
|
Material.CORNFLOWER,
|
||||||
|
Material.LILY_OF_THE_VALLEY,
|
||||||
|
Material.WITHER_ROSE,
|
||||||
|
Material.SUNFLOWER,
|
||||||
|
Material.LILAC,
|
||||||
|
Material.ROSE_BUSH,
|
||||||
|
Material.PEONY,
|
||||||
|
Material.NETHER_WART,
|
||||||
|
Material.FLOWER_POT,
|
||||||
|
Material.AZALEA,
|
||||||
|
Material.FLOWERING_AZALEA,
|
||||||
|
Material.REPEATER,
|
||||||
|
Material.COMPARATOR,
|
||||||
|
Material.REDSTONE_WIRE,
|
||||||
|
Material.REDSTONE_TORCH,
|
||||||
|
Material.REDSTONE_WALL_TORCH,
|
||||||
|
Material.TORCH,
|
||||||
|
Material.WALL_TORCH,
|
||||||
|
Material.SOUL_TORCH,
|
||||||
|
Material.SOUL_WALL_TORCH,
|
||||||
|
Material.SCAFFOLDING,
|
||||||
|
Material.SLIME_BLOCK,
|
||||||
|
Material.HONEY_BLOCK,
|
||||||
|
Material.TNT,
|
||||||
|
Material.TRIPWIRE,
|
||||||
|
Material.TRIPWIRE_HOOK,
|
||||||
|
Material.SPORE_BLOSSOM,
|
||||||
|
Material.RED_MUSHROOM,
|
||||||
|
Material.BROWN_MUSHROOM,
|
||||||
|
Material.CRIMSON_FUNGUS,
|
||||||
|
Material.WARPED_FUNGUS,
|
||||||
|
Material.CRIMSON_ROOTS,
|
||||||
|
Material.WARPED_ROOTS,
|
||||||
|
Material.HANGING_ROOTS,
|
||||||
|
Material.WEEPING_VINES,
|
||||||
|
Material.WEEPING_VINES_PLANT,
|
||||||
|
Material.TWISTING_VINES,
|
||||||
|
Material.TWISTING_VINES_PLANT,
|
||||||
|
Material.CAVE_VINES,
|
||||||
|
Material.CAVE_VINES_PLANT,
|
||||||
|
Material.SEA_PICKLE
|
||||||
|
), Arrays.asList(), Arrays.asList(Sapling.class, CoralWallFan.class), m -> m.name().endsWith("_CORAL_FAN") || m.name().endsWith("_CORAL")
|
||||||
|
|| m.name().startsWith("POTTED_")));
|
||||||
|
|
||||||
|
private static List<Material> concatTypes(Class<?>... types) {
|
||||||
|
return concatTypes(new ArrayList<>(), Arrays.asList(types));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Material> concatTypes(List<Material> materials, List<Class<?>> types) {
|
||||||
|
return concatTypes(materials, Arrays.asList(), types);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Material> concatTypes(List<Material> materials, List<Material> exclusions, List<Class<?>> types) {
|
||||||
|
return concatTypes(materials, exclusions, types, m -> false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Material> concatTypes(List<Material> materials, List<Material> exclusions, List<Class<?>> types, Predicate<Material> otherFilter) {
|
||||||
|
materials.addAll(Stream.of(Material.values()).filter(m -> (types.contains(m.data) || otherFilter.test(m))
|
||||||
|
&& !exclusions.contains(m) && !m.isLegacy()).toList());
|
||||||
|
return materials;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -526,7 +526,12 @@ public class Bot extends ServerPlayer implements Terminator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
World world = getBukkitEntity().getWorld();
|
return getStandingOn() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Block getStandingOn() {
|
||||||
|
World world = getBukkitEntity().getWorld();
|
||||||
AABB box = getBoundingBox();
|
AABB box = getBoundingBox();
|
||||||
|
|
||||||
double[] xVals = new double[]{
|
double[] xVals = new double[]{
|
||||||
@@ -547,7 +552,7 @@ public class Bot extends ServerPlayer implements Terminator {
|
|||||||
Block block = world.getBlockAt(loc);
|
Block block = world.getBlockAt(loc);
|
||||||
|
|
||||||
if ((block.getType().isSolid() || canStandOn(block.getType())) && BotUtils.solidAt(playerBox, block.getBoundingBox())) {
|
if ((block.getType().isSolid() || canStandOn(block.getType())) && BotUtils.solidAt(playerBox, block.getBoundingBox())) {
|
||||||
return true;
|
return block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -563,29 +568,29 @@ public class Bot extends ServerPlayer implements Terminator {
|
|||||||
|
|
||||||
if ((LegacyMats.FENCE.contains(block.getType()) || LegacyMats.GATES.contains(block.getType()))
|
if ((LegacyMats.FENCE.contains(block.getType()) || LegacyMats.GATES.contains(block.getType()))
|
||||||
&& block.getType().isSolid() && BotUtils.solidAt(playerBox, modifiedBox)) {
|
&& block.getType().isSolid() && BotUtils.solidAt(playerBox, modifiedBox)) {
|
||||||
return true;
|
return block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for non-solid blocks that can hold an entity up.
|
* Checks for non-solid blocks that can hold an entity up.
|
||||||
*/
|
*/
|
||||||
private boolean canStandOn(Material mat)
|
private boolean canStandOn(Material mat) {
|
||||||
{
|
|
||||||
if(mat == Material.END_ROD || mat == Material.FLOWER_POT || mat == Material.REPEATER || mat == Material.COMPARATOR
|
if(mat == Material.END_ROD || mat == Material.FLOWER_POT || mat == Material.REPEATER || mat == Material.COMPARATOR
|
||||||
|| mat == Material.SNOW || mat == Material.LADDER || mat == Material.VINE)
|
|| mat == Material.SNOW || mat == Material.LADDER || mat == Material.VINE)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(mat == Material.BLACK_CARPET || mat == Material.BLUE_CARPET || mat == Material.BROWN_CARPET || mat == Material.CYAN_CARPET
|
if(mat.name().endsWith("_CARPET"))
|
||||||
|| mat == Material.GRAY_CARPET || mat == Material.GREEN_CARPET || mat == Material.LIGHT_BLUE_CARPET
|
return true;
|
||||||
|| mat == Material.LIGHT_GRAY_CARPET || mat == Material.LIME_CARPET || mat == Material.MAGENTA_CARPET
|
|
||||||
|| mat == Material.MOSS_CARPET || mat == Material.ORANGE_CARPET || mat == Material.PINK_CARPET
|
if(mat.name().startsWith("POTTED_"))
|
||||||
|| mat == Material.PURPLE_CARPET || mat == Material.RED_CARPET || mat == Material.WHITE_CARPET
|
return true;
|
||||||
|| mat == Material.YELLOW_CARPET)
|
|
||||||
|
if((mat.name().endsWith("_HEAD") || mat.name().endsWith("_SKULL")) && !mat.name().equals("PISTON_HEAD"))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if(mat.data == Candle.class)
|
if(mat.data == Candle.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user