Is This Safe? PlotMe CmdSetOwner Switcheroo

Discussion in 'Spigot Plugin Development' started by rightfootmessi, Apr 24, 2017.

  1. Okay, so updating PlotMe to 0.17.4 seemed to fix my generator issue. The problem is, I don't like how we can no longer use /plot setowner <player> if the specified player is not online.

    I don't want to downgrade again, so instead I thought this: what if I just edit the code to allow for that to happen?

    Simplest way to do it, I thought, would to take the 0.16 code that let you do this and substitute it in for the 0.17 code. Would that be a safe thing to do? Here's the class files, for reference. I'm still a java noob so I can't tell if it's a good idea.
    Code (Java):
    package com.worldcretornica.plotme_core.commands;

    import com.worldcretornica.plotme_core.Plot;
    import com.worldcretornica.plotme_core.PlotMapInfo;
    import com.worldcretornica.plotme_core.PlotMeCoreManager;
    import com.worldcretornica.plotme_core.PlotMe_Core;
    import com.worldcretornica.plotme_core.api.IOfflinePlayer;
    import com.worldcretornica.plotme_core.api.IPlayer;
    import com.worldcretornica.plotme_core.api.IServerBridge;
    import com.worldcretornica.plotme_core.api.IWorld;
    import com.worldcretornica.plotme_core.api.event.IEventFactory;
    import com.worldcretornica.plotme_core.api.event.InternalPlotOwnerChangeEvent;
    import com.worldcretornica.plotme_core.utils.Util;
    import java.util.logging.Logger;
    import net.milkbowl.vault.economy.EconomyResponse;

    public class CmdSetOwner
      extends PlotCommand
    {
      public CmdSetOwner(PlotMe_Core instance)
      {
        super(instance);
      }
     
      public boolean exec(IPlayer player, String[] args)
      {
        IWorld world = player.getWorld();
        PlotMapInfo pmi = this.manager.getMap(world);
        if (player.hasPermission("PlotMe.admin.setowner"))
        {
          if (this.manager.isPlotWorld(world))
          {
            String id = this.manager.getPlotId(player);
            if (id.isEmpty())
            {
              player.sendMessage("�c" + C("MsgNoPlotFound"));
            }
            else
            {
              String newOwner = args[1];
              if (newOwner.startsWith("group:"))
              {
                player.sendMessage("You cannot make a group an owner. Try adding them to the plot instead.");
                return true;
              }
              String oldowner = "<" + C("WordNotApplicable") + ">";
              String playerName = player.getName();
              if (!this.manager.isPlotAvailable(id, pmi))
              {
                Plot plot = this.manager.getPlotById(id, pmi);
               
                oldowner = plot.getOwner();
                InternalPlotOwnerChangeEvent event;
                if (this.manager.isEconomyEnabled(world))
                {
                  InternalPlotOwnerChangeEvent event;
                  if ((pmi.isRefundClaimPriceOnSetOwner()) && (!newOwner.equals(oldowner)))
                  {
                    InternalPlotOwnerChangeEvent event = this.serverBridge.getEventFactory().callPlotOwnerChangeEvent(this.plugin, world, plot, player, newOwner);
                    if (event.isCancelled()) {
                      return true;
                    }
                    if (plot.getOwnerId() != null)
                    {
                      IOfflinePlayer playeroldowner = this.serverBridge.getOfflinePlayer(plot.getOwnerId());
                      EconomyResponse er = this.serverBridge.depositPlayer(playeroldowner, pmi.getClaimPrice());
                      if (er.transactionSuccess())
                      {
                        IPlayer oldOwner = this.serverBridge.getPlayer(plot.getOwnerId());
                        if (oldOwner != null) {
                          oldOwner.sendMessage(
                            C("MsgYourPlot") + " " + id + " " + C("MsgNowOwnedBy") + " " + newOwner + ". " + Util()
                            .moneyFormat(pmi.getClaimPrice(), true));
                        }
                      }
                      else
                      {
                        player.sendMessage("�c" + er.errorMessage);
                        this.serverBridge.getLogger().warning(er.errorMessage);
                        return true;
                      }
                    }
                  }
                  else
                  {
                    event = this.serverBridge.getEventFactory().callPlotOwnerChangeEvent(this.plugin, world, plot, player, newOwner);
                  }
                  if (plot.getCurrentBidderId() != null)
                  {
                    IOfflinePlayer playercurrentbidder = this.serverBridge.getOfflinePlayer(plot.getCurrentBidderId());
                    EconomyResponse er = this.serverBridge.depositPlayer(playercurrentbidder, plot.getCurrentBid());
                    if (er.transactionSuccess())
                    {
                      IPlayer currentBidder = this.serverBridge.getPlayer(playercurrentbidder.getUniqueId());
                      if (currentBidder != null) {
                        currentBidder.sendMessage(
                          C("WordPlot") + " " + id + " " + C("MsgChangedOwnerFrom") + " " + oldowner + " " + C("WordTo") + " " + newOwner + ". " +
                          Util().moneyFormat(plot.getCurrentBid(), true));
                      }
                    }
                    else
                    {
                      player.sendMessage(er.errorMessage);
                      this.serverBridge.getLogger().warning(er.errorMessage);
                    }
                  }
                }
                else
                {
                  event = this.serverBridge.getEventFactory().callPlotOwnerChangeEvent(this.plugin, world, plot, player, newOwner);
                }
                if (!event.isCancelled())
                {
                  plot.setCurrentBidder(null);
                  plot.setCurrentBidderId(null);
                  plot.setCurrentBid(0.0D);
                  plot.setAuctioned(false);
                  plot.setForSale(false);
                 
                  this.manager.removeAuctionSign(world, id);
                  this.manager.removeSellSign(world, id);
                 
                  plot.updateField("currentbidder", null);
                  plot.updateField("currentbid", Integer.valueOf(0));
                  plot.updateField("auctionned", Boolean.valueOf(false));
                  plot.updateField("forsale", Boolean.valueOf(false));
                  plot.updateField("currentbidderid", null);
                 
                  plot.setOwner(newOwner);
                 
                  this.manager.setOwnerSign(world, plot);
                 
                  plot.updateField("owner", newOwner);
                }
              }
              else
              {
                this.manager.createPlot(world, id, newOwner, null, pmi);
              }
              player.sendMessage(C("MsgOwnerChangedTo") + " �c" + newOwner);
              if (isAdvancedLogging()) {
                this.serverBridge.getLogger().info(playerName + " " + C("MsgChangedOwnerOf") + " " + id + " " + C("WordFrom") + " " + oldowner + " " + C("WordTo") + " " + newOwner);
              }
            }
          }
          else
          {
            player.sendMessage("�c" + C("MsgNotPlotWorld"));
          }
        }
        else
        {
          player.sendMessage("�c" + C("MsgPermissionDenied"));
          return false;
        }
        return true;
      }
    }
     
    Code (Java):
    package com.worldcretornica.plotme_core.commands;

    import com.worldcretornica.plotme_core.Plot;
    import com.worldcretornica.plotme_core.PlotMapInfo;
    import com.worldcretornica.plotme_core.PlotMeCoreManager;
    import com.worldcretornica.plotme_core.PlotMe_Core;
    import com.worldcretornica.plotme_core.api.CommandExBase;
    import com.worldcretornica.plotme_core.api.ICommandSender;
    import com.worldcretornica.plotme_core.api.IPlayer;
    import com.worldcretornica.plotme_core.api.IServerBridge;
    import com.worldcretornica.plotme_core.api.IWorld;
    import com.worldcretornica.plotme_core.api.event.PlotOwnerChangeEvent;
    import com.worldcretornica.plotme_core.api.event.eventbus.EventBus;
    import com.worldcretornica.plotme_core.storage.Database;
    import java.util.UUID;

    public class CmdSetOwner
      extends PlotCommand
    {
      public CmdSetOwner(PlotMe_Core instance, CommandExBase commandExBase)
      {
        super(instance);
      }
     
      public String getName()
      {
        return "setowner";
      }
     
      public boolean execute(ICommandSender sender, String[] args)
      {
        if ((args.length < 2) && (args.length >= 3))
        {
          sender.sendMessage(getUsage());
          return true;
        }
        if (args[1].length() > 16) {
          throw new IllegalArgumentException(C("InvalidCommandInput", new Object[0]));
        }
        IPlayer player = (IPlayer)sender;
        IWorld world = player.getWorld();
        if ((player.hasPermission("PlotMe.admin.setowner")) && (this.manager.isPlotWorld(world)))
        {
          PlotMapInfo pmi = this.manager.getMap(world);
          Plot plot = this.manager.getPlot(player);
          if (plot == null)
          {
            player.sendMessage("Set Owner only works on claimed plots");
            return true;
          }
          IPlayer newOwner = this.serverBridge.getPlayer(args[1]);
          if (newOwner == null)
          {
            player.sendMessage(C("MsgNoPlayerFound", new Object[0]));
            return true;
          }
          if (!plot.getOwnerId().equals(newOwner.getUniqueId()))
          {
            PlotOwnerChangeEvent event = new PlotOwnerChangeEvent(plot, player, newOwner);
            this.plugin.getEventBus().post(event);
            if (!event.isCancelled())
            {
              plot.setForSale(false);
              plot.resetExpire(pmi.getDaysToExpiration());
              plot.setOwner(newOwner.getName());
              plot.setOwnerId(newOwner.getUniqueId());
              this.plugin.getSqlManager().savePlot(plot);
              this.manager.setOwnerSign(plot);
              player.sendMessage(C("MsgOwnerChangedTo", new Object[0]) + " " + newOwner);
            }
          }
          else
          {
            player.sendMessage("This person already owns this plot!");
          }
          return true;
        }
        return false;
      }
     
      public String getUsage()
      {
        return C("CmdSetOwnerUsage", new Object[0]);
      }
    }
     
     
  2. Just modifying the set owner to use names and look up an offline player should do it. Once you get their uuid and assign it to the plot


    Sent from my iPhone using Tapatalk
     
  3. The class setup confuses me, idk how to do that.
     
  4. Does it give you a message that the player isn't online? Should be able to dig through where it sets newOwner and if that only works for Player (being online) you'll have to change it to look up OfflinePlayer


    Sent from my iPhone using Tapatalk
     
  5. Did you look at any of the code?
     
  6. I did, did you want me to write it for you? Just offering advice on how you could go about modifying the new code to support offline players.


    Sent from my iPhone using Tapatalk
     
  7. It's just that I can't really wrap my head around how IPlayer newOwner = this.serverBridge.getPlayer(args[1]); returns null if the player is offline.

    getPlayer() is just this: public abstract IPlayer getPlayer(String paramString);

    IPlayer is just this:
    Code (Java):
    package com.worldcretornica.plotme_core.api;

    public abstract interface IPlayer
      extends ICommandSender, IEntity, IOfflinePlayer
    {
      public abstract IItemStack getItemInHand();
    }
     
    serverBridge refers to this class:
    Code (Java):
    package com.worldcretornica.plotme_core.api;

    import com.google.common.base.Optional;
    import com.worldcretornica.configuration.ConfigAccessor;
    import java.io.File;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.List;
    import java.util.UUID;
    import java.util.logging.Logger;
    import net.milkbowl.vault.economy.Economy;
    import net.milkbowl.vault.economy.EconomyResponse;
    import org.bukkit.configuration.ConfigurationSection;
    import org.bukkit.configuration.file.YamlConfiguration;

    public abstract class IServerBridge
    {
      private final Logger logger;
      private final List<String> biomes = Arrays.asList(new String[] { "Ocean", "Plains", "Desert", "Extreme Hills", "Forest", "Tiaga", "Forest", "Swampland", "River", "Hell", "The End", "FrozenOcean", "FrozenRiver", "Ice Plains", "Ice Mountains", "MushroomIsland", "MushroomIslandShore", "Beach", "DesertHills", "ForestHills", "TiagaHills", "Jungle", "JungleHills", "JungleEdge", "Deep Ocean", "Stone Beach", "Cold Beach", "Birch Forest", "Birch Forest Hills", "Roofed Forest", "Cold Taiga", "Cold Taiga Hills", "Mega Taiga", "Mega Taiga Hills", "Extreme Hills+", "Savanna", "Savanna Plateau", "Mesa", "Mesa Plateau F", "Mesa Plateau" });
      private boolean usingLwc;
     
      protected IServerBridge(Logger bridgeLogger)
      {
        this.logger = bridgeLogger;
      }
     
      public abstract IOfflinePlayer getOfflinePlayer(UUID paramUUID);
     
      public abstract IOfflinePlayer getOfflinePlayer(String paramString);
     
      public abstract IPlayer getPlayer(UUID paramUUID);
     
      public abstract IPlayer getPlayer(String paramString);
     
      public abstract Collection<IPlayer> getOnlinePlayers();
     
      public Logger getLogger()
      {
        return this.logger;
      }
     
      public abstract int runTaskTimerAsynchronously(Runnable paramRunnable, long paramLong1, long paramLong2);
     
      public abstract int scheduleSyncRepeatingTask(Runnable paramRunnable, long paramLong1, long paramLong2);
     
      public abstract void cancelTask(int paramInt);
     
      public abstract void scheduleSyncDelayedTask(Runnable paramRunnable, int paramInt);
     
      public abstract void setupHooks();
     
      public abstract Optional<Economy> getEconomy();
     
      public abstract boolean has(IPlayer paramIPlayer, double paramDouble);
     
      public abstract EconomyResponse withdrawPlayer(IPlayer paramIPlayer, double paramDouble);
     
      public abstract EconomyResponse depositPlayer(IOfflinePlayer paramIOfflinePlayer, double paramDouble);
     
      public boolean isUsingLwc()
      {
        return this.usingLwc;
      }
     
      protected void setUsingLwc(boolean usingLwc)
      {
        this.usingLwc = usingLwc;
      }
     
      public abstract void runTaskAsynchronously(Runnable paramRunnable);
     
      public abstract void runTaskLaterAsynchronously(Runnable paramRunnable, long paramLong);
     
      public Optional<String> getBiome(String name)
      {
        for (String biome : this.biomes) {
          if (biome.equalsIgnoreCase(name)) {
            return Optional.of(biome);
          }
        }
        return Optional.absent();
      }
     
      public abstract File getDataFolder();
     
      public List<String> getBiomes()
      {
        return this.biomes;
      }
     
      public abstract int runTask(Runnable paramRunnable);
     
      public abstract Collection<IWorld> getWorlds();
     
      public ConfigurationSection loadDefaultConfig(ConfigAccessor configFile, String world)
      {
        ConfigurationSection defaultWorld = getDefaultWorld();
        ConfigurationSection configSection;
        ConfigurationSection configSection;
        if (configFile.getConfig().contains(world))
        {
          configSection = configFile.getConfig().getConfigurationSection(world);
        }
        else
        {
          configFile.getConfig().set(world, defaultWorld);
          configFile.saveConfig();
          configSection = configFile.getConfig().getConfigurationSection(world);
        }
        for (String path : defaultWorld.getKeys(true)) {
          configSection.addDefault(path, defaultWorld.get(path));
        }
        configFile.saveConfig();
        return configSection;
      }
     
      public abstract ConfigurationSection getDefaultWorld();
     
      public abstract File getWorldFolder();
     
      public abstract void runTaskLater(Runnable paramRunnable, long paramLong);
    }
     
     
  8. It looks like iOfflinePlayer might could be substituted for iPlayer it might be as simple as changing iPlayer to iOfflinePlayer or looking up iOfflinePlayer when iPlayer is null.

    As for just replacing the class I don't know that that will work, if they rewrote the class it may just fail to work.

    Sent from my iPhone using Tapatalk
     
    #8 dNiym, Apr 24, 2017
    Last edited: Apr 24, 2017
  9. I'd try to do that, but I can't figure out how to import the PlotMe plugin into Eclipse. I got soo close, but it wouldn't let me edit the file, only view it.
     
  10. If you have the source files it's as easy as creating a new project and importing all the source files, you'll need the plugin.yml from the PlotMe.jar as well.

    Any plugins listed in the plugin.yml depend or softdepend will also need to be downloaded and added as libraries before the plugin will compile.

    If you just wanted to change one class file however you can create the class file and save it directly to the jar file, however you'll not be able to compile the single class file and check it for errors so unless you know exactly what you're doing this isn't a real great way to do it.




    Sent from my iPhone using Tapatalk
     
  11. Code (Java):
    package com.worldcretornica.configuration;

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.nio.charset.StandardCharsets;
    import org.bukkit.configuration.file.YamlConfiguration;

    public class ConfigAccessor
    {
      private final String fileName;
      private final File configFile;
      private YamlConfiguration fileConfiguration;
     
      public ConfigAccessor(File pluginFolder, String fileName)
      {
        this.fileName = fileName;
        this.configFile = new File(pluginFolder, fileName);
      }
     
      public void reloadFile()
      {
        this.fileConfiguration = YamlConfiguration.loadConfiguration(this.configFile);
        try
        {
          InputStream defConfigStream = getResource(this.fileName);Throwable localThrowable3 = null;
          try
          {
            YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defConfigStream, StandardCharsets.UTF_8));
            this.fileConfiguration.setDefaults(defConfig);
          }
          catch (Throwable localThrowable1)
          {
            localThrowable3 = localThrowable1;throw localThrowable1; //THIS
          }
          finally
          {
            if (defConfigStream != null) {
              if (localThrowable3 != null) {
                try
                {
                  defConfigStream.close();
                }
                catch (Throwable localThrowable2)
                {
                  localThrowable3.addSuppressed(localThrowable2);
                }
              } else {
                defConfigStream.close();
              }
            }
          }
        }
        catch (IOException e)
        {
          e.printStackTrace();
        }
      }
     
      private InputStream getResource(String fileName)
      {
        return getClass().getClassLoader().getResourceAsStream(fileName);
      }
     
      public YamlConfiguration getConfig()
      {
        if (this.fileConfiguration == null) {
          reloadFile();
        }
        return this.fileConfiguration;
      }
     
      public void saveConfig()
      {
        if (this.fileConfiguration != null) {
          try
          {
            getConfig().save(this.configFile);
          }
          catch (IOException ignored) {}
        }
      }
     
      public boolean createFile()
      {
        if (!this.configFile.exists())
        {
          saveFile(true);
          return true;
        }
        return false;
      }
     
      private void saveFile(boolean overwrite)
      {
        if (overwrite) {
          try
          {
            InputStream in = getResource(this.fileName);Throwable localThrowable6 = null;
            try
            {
              OutputStream out = new FileOutputStream(this.configFile);Throwable localThrowable7 = null;
              try
              {
                byte[] buf = new byte['?'];
                int len;
                while ((len = in.read(buf)) > 0) {
                  out.write(buf, 0, len);
                }
              }
              catch (Throwable localThrowable1)
              {
                localThrowable7 = localThrowable1;throw localThrowable1;
              }
              finally {}
            }
            catch (Throwable localThrowable4)
            {
              localThrowable6 = localThrowable4;throw localThrowable4; //THIS
            }
            finally
            {
              if (in != null) {
                if (localThrowable6 != null) {
                  try
                  {
                    in.close();
                  }
                  catch (Throwable localThrowable5)
                  {
                    localThrowable6.addSuppressed(localThrowable5);
                  }
                } else {
                  in.close();
                }
              }
            }
          }
          catch (IOException ignored) {}
        }
      }
    }
     
    The lines labeled //THIS are showing as errors in Eclipse - "Unhandled exception type Throwable" but if I add a throws declaration then a bunch of other methods start showing that error. What do I do?
     
  12. Eh, those throwables and the ? 'S make me thin that didn't decompile correctly, run it through a different decompiler and see if the code is more readable.


    Sent from my iPhone using Tapatalk
     
  13. This seems to be happening in all of the classes.
     
  14. Okay, so I tried decompiling with fernflower and I got some drastically different results:
    Code (Java):
    package com.worldcretornica.configuration;

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    import org.bukkit.configuration.file.YamlConfiguration;

    public class ConfigAccessor {

       private final String fileName;
       private final File configFile;
       private YamlConfiguration fileConfiguration;


       public ConfigAccessor(File pluginFolder, String fileName) {
          this.fileName = fileName;
          this.configFile = new File(pluginFolder, fileName);
       }

       public void reloadFile() {
          this.fileConfiguration = YamlConfiguration.loadConfiguration(this.configFile);

          try {
             InputStream e = this.getResource(this.fileName);
             Throwable var2 = null;

             try {
                YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(e, StandardCharsets.UTF_8));
                this.fileConfiguration.setDefaults(defConfig);
             } catch (Throwable var12) {
                var2 = var12;
                throw var12;
             } finally {
                if(e != null) {
                   if(var2 != null) {
                      try {
                         e.close();
                      } catch (Throwable var11) {
                         var2.addSuppressed(var11);
                      }
                   } else {
                      e.close();
                   }
                }

             }
          } catch (IOException var14) {
             var14.printStackTrace();
          }

       }

       private InputStream getResource(String fileName) {
          return this.getClass().getClassLoader().getResourceAsStream(fileName);
       }

       public YamlConfiguration getConfig() {
          if(this.fileConfiguration == null) {
             this.reloadFile();
          }

          return this.fileConfiguration;
       }

       public void saveConfig() {
          if(this.fileConfiguration != null) {
             try {
                this.getConfig().save(this.configFile);
             } catch (IOException var2) {
                ;
             }
          }

       }

       public boolean createFile() {
          if(!this.configFile.exists()) {
             this.saveFile(true);
             return true;
          } else {
             return false;
          }
       }

       private void saveFile(boolean overwrite) {
          if(overwrite) {
             try {
                InputStream ignored = this.getResource(this.fileName);
                Throwable var3 = null;

                try {
                   FileOutputStream out = new FileOutputStream(this.configFile);
                   Throwable var5 = null;

                   try {
                      byte[] buf = new byte[1024];

                      int len;
                      while((len = ignored.read(buf)) > 0) {
                         out.write(buf, 0, len);
                      }
                   } catch (Throwable var31) {
                      var5 = var31;
                      throw var31;
                   } finally {
                      if(out != null) {
                         if(var5 != null) {
                            try {
                               out.close();
                            } catch (Throwable var30) {
                               var5.addSuppressed(var30);
                            }
                         } else {
                            out.close();
                         }
                      }

                   }
                } catch (Throwable var33) {
                   var3 = var33;
                   throw var33;
                } finally {
                   if(ignored != null) {
                      if(var3 != null) {
                         try {
                            ignored.close();
                         } catch (Throwable var29) {
                            var3.addSuppressed(var29);
                         }
                      } else {
                         ignored.close();
                      }
                   }

                }
             } catch (IOException var35) {
                ;
             }
          }

       }
    }
     
     
  15. There doesn't seem to be any errors in the fernflower decompiled code, but for some reason it gave all the variables generic names so it'd be unnecessarily difficult to edit that.

    At this point I just don't know what I'm doing XD if you're feeling generous you can do it for me, but what I'd prefer is maybe a specific, step-by-step guide on decompiling a plugin and importing it into Eclipse, as that's bound to help me in the future.