Skip to main content

Selling Plots of Land for $WRLD

The source code for this example project can be found at https://github.com/NFT-Worlds/WRLD-Example-Projects/tree/main/wrld-buy-plot.

tip

Get your hands dirty to learn quicker! Clone the examples repository and open this project using IntelliJ. Then run Maven package and install the plugin on a server.

Here's a video of what this example project does:

plugin.yml

This example has the following plugin.yml file which defines an admin-only command for selling a plot and a /buyplot command accessible to everyone.

name: wrld-buy-plot
version: 1.0
authors: [Jackson, ArkDev]
main: com.nftworlds.wrldplots.WRLDPlots
api-version: 1.17
depend: [WRLDPaymentsAPI, WorldGuard]

commands:
sellplot:
description: List the plot you're standing on up for sale.
usage: /sellplot
permission: wrldplots.sellplot
default: op
buyplot:
description: Buy the plot you're standing on.
usage: /buyplot
default: true

Transaction payload

This project defines a transaction payload called PlayerBuyPlotPayload. It contains a single member variable that contains a ProtectedRegion referring to the plot the player is purchasing.

public class PlayerBuyPlotPayload {
public ProtectedRegion plotToBuy;

public PlayerBuyPlotPayload(ProtectedRegion plotToBuy) {
this.plotToBuy = plotToBuy;
}
}

Listing a plot for sale

tip

This example plugin does not create plots. You'll need to create them using the standard WorldGuard commands prior to selling them.

Finding plots from player location

We need to find the plot a player is standing on, so we define the following class that makes some WorldGuard API code reusable:

public class PlotUtility {
public static ProtectedRegion getPlotAtLocation(Location l) {
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
RegionManager regions = container.get(BukkitAdapter.adapt(l.getWorld()));
BlockVector3 position = BlockVector3.at(l.getX(), l.getY(), l.getZ());
if (regions == null) {
return null;
}
ApplicableRegionSet set = regions.getApplicableRegions(position);

// If we're at an intersection of multiple plots or not on a plot, abort.
if (set.size() != 1) {
return null;
}

return set.iterator().next();

}
}

Now we're ready to implement our CommandExecutor for /sellplot:

public class CommandListPlotForSale implements CommandExecutor {
// Persisting this data between server reboots is left as an exercise to the developer.
private static final HashMap<String, Double> plotsForSaleAndPrice = new HashMap<>();

public static Double getPlotPrice(String plotId) {
return plotsForSaleAndPrice.get(plotId);
}

public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (args.length != 1) {
sender.sendMessage("Usage: /sellplot <amount>");
return false;
}

Double amount = Double.valueOf(args[0]);
// get the plot
ProtectedRegion region = PlotUtility.getPlotAtLocation(((Player) sender).getLocation());
if (region != null) {
String plotId = region.getId();

sender.sendMessage("You have listed " + plotId + " for sale for " + amount + " WLRD.");

region.setFlag(Flags.GREET_MESSAGE, "&aThis plot is for sale for &6" + amount + " WRLD&f. Run /buyplot to purchase it!");
plotsForSaleAndPrice.put(plotId, amount);

return true;
}
sender.sendMessage("There is no plot at your current location to sell!");
return false;
}
}

Buying a plot

Now that we an admin-only command for listing a plot, we need to implement the CommandExecutor for buying it:

public class CommandBuyPlot implements CommandExecutor {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
ProtectedRegion region = PlotUtility.getPlotAtLocation(((Player)sender).getLocation());
if (region != null) {
Double price = CommandListPlotForSale.getPlotPrice(region.getId());
if (price == null) {
sender.sendMessage("Error: There is no plot for sale here!");
return false;
}
PlayerBuyPlotPayload payload = new PlayerBuyPlotPayload(region);
WRLDPlots.getPayments().getNFTPlayer((Player) sender).requestWRLD(
price, Network.POLYGON, "Purchasing plot " + region.getId(), payload
);
}
return true;
}
}

Handling the PlayerTransactEvent

Now that we've implemented the command which sends a payment request, we need to write the handler that processes the completed transaction:

public class PlayerTransactEventListener implements Listener {
@EventHandler
public void onPlayerTransact(PlayerTransactEvent<?> e) {
if (e.getPayload() instanceof PlayerBuyPlotPayload payload) {
e.getPlayer().playSound(e.getPlayer().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 15, 0.5F);
e.getPlayer().sendMessage("Your transaction has been finalized, enjoy your new plot!");
payload.plotToBuy.getOwners().clear();
payload.plotToBuy.getOwners().addPlayer(WorldGuardPlugin.inst().wrapPlayer(e.getPlayer()));
payload.plotToBuy.setFlag(Flags.GREET_MESSAGE, null);
}
}
}

Setting up a test server

Once you've compiled your plugin, place it in the plugins folder of a Paper server alongside the WRLD-Payments-API jarfile which you can download from GitHub.