From e7bb1e6669499139abfb505663df7ac41bd860dd Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 2 Sep 2023 18:23:30 +0200 Subject: [PATCH 1/4] Pathplanning Signed-off-by: Lixfel --- .../src/de/steamwar/fightsystem/ai/AI.java | 10 +- .../de/steamwar/fightsystem/ai/LixfelAI.java | 22 ++- .../fightsystem/ai/LixfelPathplanner.java | 129 ++++++++++++++++++ 3 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java index 96d9df9..1706a93 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java @@ -42,9 +42,7 @@ import org.bukkit.block.data.Powerable; import org.bukkit.block.data.type.Comparator; import org.bukkit.block.data.type.NoteBlock; import org.bukkit.block.data.type.Repeater; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; +import org.bukkit.entity.*; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; @@ -149,6 +147,9 @@ public abstract class AI { queue.add(new Action(1) { @Override public void run() { + if(FightState.getFightState() != FightState.RUNNING) + return; + Location location = translate(pos, true); if(interactionDistanceViolation(location)) return; @@ -206,6 +207,9 @@ public abstract class AI { if(Math.abs(location.getX() - target.getX()) > 1 || Math.abs(location.getY() - target.getY()) > 1 || Math.abs(location.getZ() - target.getZ()) > 1) return; + if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target)) + return; + entity.teleport(target); } }); diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java index fcc834b..2a89353 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java @@ -23,24 +23,38 @@ import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; +import org.bukkit.util.Vector; import java.util.List; import java.util.Random; public class LixfelAI extends AI { - public LixfelAI(FightTeam team) { - super(team, SteamwarUser.get("public")); + + private final Random random = new Random(); + private LixfelPathplanner pathplanner; + + public LixfelAI(FightTeam team, String user) { + super(team, SteamwarUser.get(user)); } + @Override public SchematicNode chooseSchematic() { List publics = SchematicNode.getAllSchematicsOfType(0, Config.SchematicType.toDB()); - return publics.get(new Random().nextInt(publics.size())); + SchematicNode schem = publics.get(new Random().nextInt(publics.size())); + pathplanner = new LixfelPathplanner(schem); + return schem; } @Override protected void plan() { setReady(); - getEntity().setAI(true); + Vector destination = pathplanner.getWalkable().get(random.nextInt(pathplanner.getWalkable().size())); + List path = pathplanner.plan(getPosition(), destination); + if(!path.isEmpty()) + chat("Path size: " + path.size()); + for(Vector p : path) { + move(p); + } } } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java new file mode 100644 index 0000000..463bed5 --- /dev/null +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java @@ -0,0 +1,129 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.fightsystem.ai; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.world.block.BlockType; +import de.steamwar.fightsystem.Config; +import de.steamwar.sql.SchematicData; +import de.steamwar.sql.SchematicNode; +import org.bukkit.util.Vector; + +import java.io.IOException; +import java.util.*; + +public class LixfelPathplanner { + + private static BlockType getBlockType(Clipboard clipboard, BlockVector3 vector) { + return clipboard.getBlock(vector).getBlockType(); + } + + private static boolean nonsolid(Clipboard clipboard, BlockVector3 vector) { + return !getBlockType(clipboard, vector).getMaterial().isSolid(); + } + + private static Vector toBukkit(BlockVector3 vector) { + return new Vector(vector.getX(), vector.getY(), vector.getZ()); + } + + private final List walkable = new ArrayList<>(); + private final Map neighbours = new HashMap<>(); + + public LixfelPathplanner(SchematicNode schem) { + try { + fillWalkable(new SchematicData(schem).load()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public List getWalkable() { + return walkable; + } + + private void fillWalkable(Clipboard clipboard) { + BlockVector3 min = clipboard.getRegion().getMinimumPoint().subtract(Config.PreperationArea, 0, Config.PreperationArea); //TODO assumes nonextended Schematic with maximal size + Region region = clipboard.getRegion(); + clipboard.getRegion().forEach(vector -> { + BlockVector3 below = vector.subtract(0, 1, 0); + if(!region.contains(below)) + return; + + BlockType belowMaterial = getBlockType(clipboard, below); + BlockVector3 above = vector.add(0, 1, 0); + if( + (belowMaterial.getMaterial().isSolid() || belowMaterial.getId().equals("minecraft:ladder")) && + nonsolid(clipboard, vector) && + (!region.contains(above) || nonsolid(clipboard, above)) + ) + walkable.add(toBukkit(vector.subtract(min))); + }); + + for(Vector vector : walkable) { + neighbours.put(vector, walkable.stream().filter(neighbour -> neighbouring(neighbour, vector)).filter(neighbour -> neighbour != vector).toArray(Vector[]::new)); + } + } + + public List planToAnywhere(Vector start, Vector destination) { + return plan(start, destination); //TODO (destination neighbor search) + } + + public List plan(Vector start, Vector destination) { + if(neighbouring(start, destination)) + return Collections.singletonList(destination); + + Map approach = new HashMap<>(); + Set checking = Collections.singleton(destination); + + while(!checking.isEmpty()) { + Set toCheck = new HashSet<>(); + for(Vector current : checking) { + Vector firstStep = Arrays.stream(neighbours.get(current)) + .filter(vector -> !approach.containsKey(vector)) + .filter(next -> { + approach.put(next, current); + toCheck.add(next); + return neighbouring(next, start); + }) + .findAny().orElse(null); + + if(firstStep != null) { + List path = new ArrayList<>(); + path.add(firstStep); + + while(path.get(path.size()-1) != destination) { + path.add(approach.get(path.get(path.size()-1))); + } + + return path; + } + } + checking = toCheck; + } + + return Collections.emptyList(); + } + + private boolean neighbouring(Vector a, Vector b) { + return Math.abs(a.getX() - b.getX()) <= 1 && Math.abs(a.getY() - b.getY()) <= 1 && Math.abs(a.getZ() - b.getZ()) <= 1; + } +} From 15e0fbde069663452283b578e7c6bb139d527336 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 2 Sep 2023 19:58:47 +0200 Subject: [PATCH 2/4] AI and Pathplanning fixes Signed-off-by: Lixfel --- .../src/de/steamwar/fightsystem/ai/AI.java | 24 ++++++++++--------- .../fightsystem/ai/LixfelPathplanner.java | 18 ++++++++------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java index 1706a93..56b3205 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java @@ -42,7 +42,10 @@ import org.bukkit.block.data.Powerable; import org.bukkit.block.data.type.Comparator; import org.bukkit.block.data.type.NoteBlock; import org.bukkit.block.data.type.Repeater; -import org.bukkit.entity.*; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Villager; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; @@ -74,7 +77,7 @@ public abstract class AI { entity = (LivingEntity) Config.world.spawnEntity(Config.SpecSpawn, EntityType.VILLAGER); entity.setCustomName(user.getUserName()); - entity.setAI(false); + ((Villager)entity).setAware(false); task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), this::run, 1, 1); ais.put(entity.getUniqueId(), this); @@ -112,7 +115,7 @@ public abstract class AI { } protected void chat(String message) { - FightSystem.getPlugin().getLogger().log(Level.INFO, entity.getName() + "» " + message); + FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + "» " + message); Chat.broadcastChat("PARTICIPANT_CHAT", team.getColoredName(), entity.getName(), message); } @@ -122,13 +125,13 @@ public abstract class AI { if(Fight.getUnrotated() == team) return new Vector( location.getX() - extend.getMinX(), - location.getY() - extend.getMinY(), + location.getY() - team.getSchemRegion().getMinY(), location.getZ() - extend.getMinZ() ); else return new Vector( extend.getMaxX() - location.getX(), - location.getY() - extend.getMinY(), + location.getY() - team.getSchemRegion().getMinY(), extend.getMaxZ() - location.getZ() ); } @@ -199,13 +202,12 @@ public abstract class AI { queue.add(new Action(2) { @Override public void run() { - if(!entity.isOnGround()) - return; - Location location = entity.getLocation(); Location target = translate(pos, false); - if(Math.abs(location.getX() - target.getX()) > 1 || Math.abs(location.getY() - target.getY()) > 1 || Math.abs(location.getZ() - target.getZ()) > 1) + if(Math.abs(location.getX() - target.getX()) > 1 || Math.abs(location.getY() - target.getY()) > 1.2 || Math.abs(location.getZ() - target.getZ()) > 1) { + FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + ": Overdistance movement " + location.toVector() + " " + target.toVector()); return; + } if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target)) return; @@ -268,14 +270,14 @@ public abstract class AI { return new Location( Config.world, pos.getX() + extend.getMinX(), - pos.getY() + extend.getMinY(), + pos.getY() + team.getSchemRegion().getMinY(), pos.getZ() + extend.getMinZ() ); else return new Location( Config.world, extend.getMaxX() - pos.getX() - (blockPos ? 1 : 0), - pos.getY() + extend.getMinY(), + pos.getY() + team.getSchemRegion().getMinY(), extend.getMaxZ() - pos.getZ() - (blockPos ? 1 : 0) ); } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java index 463bed5..df56c82 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java @@ -42,7 +42,7 @@ public class LixfelPathplanner { } private static Vector toBukkit(BlockVector3 vector) { - return new Vector(vector.getX(), vector.getY(), vector.getZ()); + return new Vector(vector.getX() + 0.5, vector.getY(), vector.getZ() + 0.5); } private final List walkable = new ArrayList<>(); @@ -70,12 +70,16 @@ public class LixfelPathplanner { BlockType belowMaterial = getBlockType(clipboard, below); BlockVector3 above = vector.add(0, 1, 0); - if( - (belowMaterial.getMaterial().isSolid() || belowMaterial.getId().equals("minecraft:ladder")) && - nonsolid(clipboard, vector) && - (!region.contains(above) || nonsolid(clipboard, above)) - ) - walkable.add(toBukkit(vector.subtract(min))); + if(nonsolid(clipboard, vector)) { + if( + (belowMaterial.getMaterial().isSolid() || belowMaterial.getId().equals("minecraft:ladder")) && + (!region.contains(above) || nonsolid(clipboard, above)) + ) + walkable.add(toBukkit(vector.subtract(min))); + } else { + if(!region.contains(above)) + walkable.add(toBukkit(above.subtract(min))); + } }); for(Vector vector : walkable) { From 26f2fb2afdb9c984ea7a9bd90327ca9a360b98cc Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 2 Sep 2023 20:11:47 +0200 Subject: [PATCH 3/4] AI and Pathplanning fixes Signed-off-by: Lixfel --- FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java | 3 ++- .../src/de/steamwar/fightsystem/fight/FightPlayer.java | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java index 56b3205..6f5cb00 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java @@ -46,6 +46,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Villager; +import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; @@ -212,7 +213,7 @@ public abstract class AI { if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target)) return; - entity.teleport(target); + entity.teleport(target, PlayerTeleportEvent.TeleportCause.COMMAND); } }); } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java b/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java index 1df6ef3..2ea6f58 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java @@ -89,8 +89,11 @@ public class FightPlayer { } public void ifAI(Consumer function) { - if(!(entity instanceof Player)) - function.accept(AI.getAI(entity.getUniqueId())); + if(entity instanceof Player) + return; + AI ai = AI.getAI(entity.getUniqueId()); + if(ai != null) + function.accept(ai); } public void ifPlayer(Consumer function) { From 97113b1ae2203e5f980bda5c7103732bd00bca9b Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 2 Sep 2023 20:21:51 +0200 Subject: [PATCH 4/4] Implement planToAnywhere Signed-off-by: Lixfel --- .../de/steamwar/fightsystem/ai/LixfelPathplanner.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java index df56c82..b2e593e 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java @@ -88,7 +88,15 @@ public class LixfelPathplanner { } public List planToAnywhere(Vector start, Vector destination) { - return plan(start, destination); //TODO (destination neighbor search) + Vector intermediate = walkable.stream().filter(vector -> neighbouring(vector, destination)).findAny().orElse(null); + + if(intermediate == null) + return Collections.emptyList(); + + List plan = plan(start, intermediate); + plan.add(destination); + + return plan; } public List plan(Vector start, Vector destination) {