Solved VirtualPlayer cannot execute WorldGuard WorldEdit cmds

Discussion in 'Plugin Help/Development/Requests' started by Europia79, Apr 1, 2015.

Thread Status:
Not open for further replies.
  1. Offline

    Europia79

    http://dev.bukkit.org/bukkit-plugins/virtualplayers2/
    https://github.com/Europia79/VirtualPlayers

    VirtualPlayers is a plugin used to simulate real players and have them run commands and interact with the world. They are able to run any command... except recently, users have reported a special command combination that does NOT work:

    This combination of '/region select' followed by '//set stone' (for example) works with WorldGuard 5.x but breaks for WorldGuard 6.0.0

    [​IMG]

    I was hoping that maybe someone was familiar with the WorldGuard WorldEdit codebase that would know what has changed between v5 & v6 that no longer allows a VirtualPlayer to run these commands.

    Here is some information that might be helpful:

    Code:java
    1.  
    2. package mc.alk.virtualplayers.nms.v1_8_R1;
    3.  
    4. import com.mojang.authlib.GameProfile;
    5. import java.util.ArrayList;
    6. import java.util.Collection;
    7. import java.util.HashMap;
    8. import java.util.List;
    9. import java.util.Map;
    10. import java.util.UUID;
    11. import net.minecraft.server.v1_8_R1.EntityPlayer;
    12. import net.minecraft.server.v1_8_R1.MinecraftServer;
    13. import net.minecraft.server.v1_8_R1.PlayerInteractManager;
    14. import net.minecraft.server.v1_8_R1.WorldServer;
    15. import org.apache.commons.lang.ArrayUtils;
    16. import org.bukkit.Bukkit;
    17. import org.bukkit.GameMode;
    18. import org.bukkit.Location;
    19. import org.bukkit.World;
    20. import org.bukkit.craftbukkit.v1_8_R1.CraftServer;
    21. import org.bukkit.craftbukkit.v1_8_R1.CraftWorld;
    22. import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
    23. import org.bukkit.craftbukkit.v1_8_R1.scoreboard.CraftScoreboard;
    24. import org.bukkit.entity.Player;
    25. import org.bukkit.event.player.PlayerChangedWorldEvent;
    26. import org.bukkit.event.player.PlayerRespawnEvent;
    27. import org.bukkit.event.player.PlayerTeleportEvent;
    28. import org.bukkit.inventory.Inventory;
    29. import org.bukkit.inventory.InventoryView;
    30. import org.bukkit.potion.PotionEffect;
    31. import org.bukkit.potion.PotionEffectType;
    32. import org.bukkit.scoreboard.DisplaySlot;
    33. import org.bukkit.scoreboard.Scoreboard;
    34.  
    35. public class VirtualPlayer extends CraftPlayer {
    36.  
    37. static final Map<UUID, VirtualPlayer> vps = new HashMap<UUID, VirtualPlayer>();
    38. static final Map<String, VirtualPlayer> names = new HashMap<String, VirtualPlayer>();
    39.  
    40. public VirtualPlayer(CraftServer cserver, MinecraftServer mcserver, WorldServer worldServer,
    41. GameProfile gameProfile, PlayerInteractManager pim) {
    42. super(cserver, new EntityPlayer(mcserver, worldServer, gameProfile, pim));
    43. this.loc = this.getLocation();
    44. }
    45.  


    No, I don't think the problem is UUID, I ran two seperate commands and logged the UUID to make sure they were the same:

    Code:
    2015-03-31 12:16:02 [Server thread][INFO]  tester executing 'bomb join'
    2015-03-31 12:16:02 [Server thread][INFO] 841e4ee9-cee9-4dad-b313-1d7562c60d9d
    2015-03-31 12:16:02 [Server thread][INFO] [tester] 'tester teleporting from -429.5,4.0,-1210.5 -> -429.74615478515625,4.0,-1206.3194580078125,-354.8993,3.599917'
    2015-03-31 12:16:02 [Server thread][INFO] [tester] 'tester gettingMessage= You joined the BombArena queue.Position: 1/32'
    2015-03-31 12:16:09 [Server thread][INFO]  tester executing 'bomb leave'
    2015-03-31 12:16:09 [Server thread][INFO] 841e4ee9-cee9-4dad-b313-1d7562c60d9d
    First, the names Map is checked to see if the VP was previously created. If so, we get the object from the Map and return that instance. If not, we create a new instance:

    Code:java
    1. public static VirtualPlayer getOrCreate(String name) {
    2. Player vp = names.get(name);
    3. if (vp == null) {
    4. try {
    5. vp = makeVirtualPlayer(name);
    6. } catch (Exception e) {
    7. e.printStackTrace();
    8. }
    9. }
    10. return (VirtualPlayer) vp;
    11. }
    12.  
    13. public static synchronized Player makeVirtualPlayer(String name) throws Exception {
    14. CraftServer cserver = (CraftServer) Bukkit.getServer();
    15. List<World> worlds = cserver.getWorlds();
    16. if (worlds == null || worlds.isEmpty()) {
    17. throw new Exception("There must be at least one world");
    18. }
    19. CraftWorld w = (CraftWorld) worlds.get(0);
    20. Location location = new Location(w, 0, 0, 0);
    21. MinecraftServer mcserver = cserver.getServer();
    22. WorldServer worldServer = mcserver.getWorldServer(0);
    23. PlayerInteractManager pim = new PlayerInteractManager(worldServer);
    24. if (name == null) {
    25. name = "p" + (vps.size() + 1);
    26. }
    27. GameProfile gameProfile = new GameProfile(UUID.randomUUID(), CustomCommandExecutor.colorChat(name));
    28. VirtualPlayer vp = new VirtualPlayer(cserver, mcserver, worldServer, gameProfile, pim);
    29. vps.put(vp.getUniqueId(), vp);
    30. names.put(vp.getName(), vp);
    31. vp.loc = location;
    32. return vp;
    33. }
    34.  


    https://github.com/sk89q/WorldGuard...commands/region/RegionCommands.java#L296-L327

    https://github.com/sk89q/WorldGuard...ands/region/RegionCommandsBase.java#L343-L362
    Code:java
    1.  
    2. /**
    3.   * Get a WorldEdit selection from a region.
    4.   *
    5.   * @param args the arguments
    6.   * @param sender the sender
    7.   * @throws CommandException any error
    8.   */
    9. @Command(aliases = {"select", "sel", "s"},
    10. usage = "[id]",
    11. desc = "Load a region as a WorldEdit selection",
    12. min = 0, max = 1)
    13. public void select(CommandContext args, CommandSender sender) throws CommandException {
    14. Player player = plugin.checkPlayer(sender);
    15. World world = player.getWorld();
    16. RegionManager manager = checkRegionManager(plugin, world);
    17. ProtectedRegion existing;
    18.  
    19. // If no arguments were given, get the region that the player is inside
    20. if (args.argsLength() == 0) {
    21. existing = checkRegionStandingIn(manager, player);
    22. } else {
    23. existing = checkExistingRegion(manager, args.getString(0), false);
    24. }
    25.  
    26. // Check permissions
    27. if (!getPermissionModel(sender).maySelect(existing)) {
    28. throw new CommandPermissionsException();
    29. }
    30.  
    31. // Select
    32. setPlayerSelection(player, existing);
    33. }
    34.  
    35. /**
    36.   * Set a player's selection to a given region.
    37.   *
    38.   * @param player the player
    39.   * @param region the region
    40.   * @throws CommandException thrown on a command error
    41.   */
    42. protected static void setPlayerSelection(Player player, ProtectedRegion region) throws CommandException {
    43. WorldEditPlugin worldEdit = WorldGuardPlugin.inst().getWorldEdit();
    44.  
    45. World world = player.getWorld();
    46.  
    47. // Set selection
    48. if (region instanceof ProtectedCuboidRegion) {
    49. ProtectedCuboidRegion cuboid = (ProtectedCuboidRegion) region;
    50. Vector pt1 = cuboid.getMinimumPoint();
    51. Vector pt2 = cuboid.getMaximumPoint();
    52. CuboidSelection selection = new CuboidSelection(world, pt1, pt2);
    53. worldEdit.setSelection(player, selection);
    54. player.sendMessage(ChatColor.YELLOW + "Region selected as a cuboid.");
    55.  
    56. } else if (region instanceof ProtectedPolygonalRegion) {
    57. ProtectedPolygonalRegion poly2d = (ProtectedPolygonalRegion) region;
    58. Polygonal2DSelection selection = new Polygonal2DSelection(
    59. world, poly2d.getPoints(),
    60. poly2d.getMinimumPoint().getBlockY(),
    61. poly2d.getMaximumPoint().getBlockY() );
    62. worldEdit.setSelection(player, selection);
    63. player.sendMessage(ChatColor.YELLOW + "Region selected as a polygon.");
    64.  
    65. } else if (region instanceof GlobalProtectedRegion) {
    66. throw new CommandException(
    67. "Can't select global regions! " +
    68. "That would cover the entire world.");
    69.  
    70. } else {
    71. throw new CommandException("Unknown region type: " +
    72. region.getClass().getCanonicalName());
    73. }
    74. }
    75.  


    https://github.com/sk89q/WorldEdit/...rldedit/bukkit/WorldEditPlugin.java#L429-L450

    Code:java
    1. public void setSelection(Player player, Selection selection)
    2. {
    3. if (player == null) {
    4. throw new IllegalArgumentException("Null player not allowed");
    5. }
    6. if (!player.isOnline()) {
    7. throw new IllegalArgumentException("Offline player not allowed");
    8. }
    9. if (selection == null) {
    10. throw new IllegalArgumentException("Null selection not allowed");
    11. }
    12. LocalSession session = WorldEdit.getInstance().getSession(wrapPlayer(player));
    13. RegionSelector sel = selection.getRegionSelector();
    14. session.setRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()), sel);
    15. session.dispatchCUISelection(wrapPlayer(player));
    16. }


    Ultimately, if you trace the code long enough, you'll see that WorldEdit is doing the same thing as VirtualPlayers: Mapping a key (UUID) to a value (SessionHolder which contains the LocalSession). If the value is present, it is returned. If not, a new one is created.

    Sadly, no. This is broken. But hopefully someone will have some ideas to help determine the cause. Thanks!
     
  2. Try to add a delay between the commands
     
  3. Offline

    Europia79

    @FisheyLP

    oh sorry... i should have specified... Yeah, you are correct that a lot of people execute these commands via script... But these were not. I manually typed them out one after another.

    Yeah, I could see how a script could potentially cause problems by running too quickly: WorldEdit might try to get a LocalSession before WorldGuard has had a chance to set the selection.
     
    Last edited: Apr 1, 2015
  4. Moved to Bukkit Alternates.
     
  5. Offline

    Europia79

    This problem is probably too complicated for the forums.

    Maybe viewing this problem from a different angle might make it easier: How would you create a fake player that is able to run commands ? What are the different approaches to creating the fake player ? Which approach would work best with WorldEdit ?
     
  6. Offline

    nverdier

  7. Offline

    Europia79

    @nverdier

    The problem in question has nothing to do with "spawning a fake player"

    "create" as in instantiate an object

    This appears to be the solution here:
    https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/Server.java#L480

    Any instance of CommandSender... which I have since I extended CraftPlayer which implements Player. And Player extends CommandSender.

    My "instance" can run commands: But there is a bug with running a WorldGuard/WorldEdit v6.0.0 command combination:

    This used to work fine in WorldEdit v5.x, but v6.0.0 has broken this capability.
     
  8. Offline

    Europia79

    The reason why VirtualPlayers cannot execute this WorldEdit command combination is because
    com.sk89q.worldedit.bukkit.BukkitPlayer has an isActive() method which eventually calls Bukkit.getOnlinePlayers()

    And VirtualPlayers are not listed with the server... Therefore the solution would be to list them with the server.
     
Thread Status:
Not open for further replies.

Share This Page