Make config more like ARM

This commit is contained in:
NotAlexNoyle committed 2026-07-18 18:26:55 -04:00
1 parent 2c5928a3a0
commit 2fa49f9490
11 files changed
+354 -91

No files matched your search

@@ -27,6 +27,7 @@ import me.wiefferink.areashop.modules.BukkitModule;
import me.wiefferink.areashop.modules.DependencyModule;
import me.wiefferink.areashop.modules.PlatformModule;
import me.wiefferink.areashop.platform.adapter.PlatformAdapter;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.regions.RentRegion;
import me.wiefferink.areashop.services.ServiceManager;
import me.wiefferink.areashop.tools.GithubUpdateCheck;
@@ -64,9 +65,11 @@ import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
@@ -102,6 +105,15 @@ public final class AreaShop extends JavaPlugin implements AreaShopApi {
private final ServiceManager serviceManager = new ServiceManager();
// Cached LuckPerms prefix colors so sign updates never block on storage
private static final long PREFIX_COLOR_CACHE_MILLIS = TimeUnit.MINUTES.toMillis(5);
private record CachedPrefixColor(String color, long loadedAt) {
}
private final Map<UUID, CachedPrefixColor> prefixColorCache = new ConcurrentHashMap<>();
private final Set<UUID> prefixColorLoading = ConcurrentHashMap.newKeySet();
// Folders and file names
public static final String languageFolder = "lang";
public static final String schematicFolder = "schem";
@@ -130,6 +142,7 @@ public final class AreaShop extends JavaPlugin implements AreaShopApi {
public static final String tagPrice = "price";
public static final String tagRawPrice = "rawprice";
public static final String tagDuration = "duration";
public static final String tagDurationShort = "durationshort";
public static final String tagRentedUntil = "until";
public static final String tagRentedUntilShort = "untilshort";
public static final String tagWidth = "width"; // x-axis
@@ -580,15 +593,18 @@ public final class AreaShop extends JavaPlugin implements AreaShopApi {
}
// Check if jubilee mode is enabled: only the first rent is charged and gets the maximum
// rent time, extending/buying/reselling is free and there are no Diamond paybacks.
// Check if jubilee mode is enabled: only the first rent is charged and gets the
// maximum
// rent time, extending/buying/reselling is free and there are no Diamond
// paybacks.
public boolean isJubilee() {
return getConfig().getBoolean("jubilee");
}
// Extend all currently rented regions to the maximum rent time, jubilee mode grants
// Extend all currently rented regions to the maximum rent time, jubilee mode
// grants
// every renter the maximum duration.
private void applyJubileeToRents() {
@@ -932,14 +948,10 @@ public final class AreaShop extends JavaPlugin implements AreaShopApi {
}
/**
* Get the color and formatting codes a player's name inherits from their
* LuckPerms prefix.
*
* @param player UUID of the player, may be null
* @return Legacy color codes (&amp;-format) active at the end of the player's
* prefix, empty string if unavailable
*/
// Get the color code a player's name inherits from their LuckPerms prefix,
// in &-format, or an empty string if unavailable. Never blocks the calling
// thread: uncached users are loaded asynchronously and the result is cached,
// affected signs are repainted once the color is known.
public String getPlayerPrefixColors(UUID player) {
if (player == null) {
@@ -957,27 +969,79 @@ public final class AreaShop extends JavaPlugin implements AreaShopApi {
UserManager userManager = permissionProvider.getUserManager();
User user = userManager.getUser(player);
boolean loaded = user != null;
if (user == null) {
if (user != null) {
// Not cached, load it from storage (blocking)
user = userManager.loadUser(player).join();
String color = extractPrefixColor(user.getCachedData().getMetaData().getPrefix());
prefixColorCache.put(player, new CachedPrefixColor(color, System.currentTimeMillis()));
return color;
}
if (user == null) {
CachedPrefixColor cached = prefixColorCache.get(player);
boolean fresh = cached != null && (System.currentTimeMillis() - cached.loadedAt()) < PREFIX_COLOR_CACHE_MILLIS;
if (!fresh && prefixColorLoading.add(player)) {
return "";
// Not cached, load it from storage asynchronously and repaint the
// signs of the regions the player owns when the color changed
userManager.loadUser(player).whenComplete((loadedUser, error) -> {
try {
if (error != null || loadedUser == null) {
return;
}
String color = extractPrefixColor(loadedUser.getCachedData().getMetaData().getPrefix());
CachedPrefixColor previous = prefixColorCache.put(player,
new CachedPrefixColor(color, System.currentTimeMillis()));
userManager.cleanupUser(loadedUser);
if ((previous == null || !previous.color().equals(color)) && isEnabled()) {
getServer().getScheduler().runTask(this, () -> updateRegionsOfPlayer(player));
}
} finally {
prefixColorLoading.remove(player);
}
});
}
String prefix = user.getCachedData().getMetaData().getPrefix();
if (!loaded) {
// Serve the cached color (possibly stale) until the async load finishes
return cached == null ? "" : cached.color();
userManager.cleanupUser(user);
}
// Repaint the signs and flags of all regions owned by the given player
private void updateRegionsOfPlayer(UUID player) {
if (fileManager == null) {
return;
}
for (GeneralRegion region : fileManager.getRegionsRef()) {
if (player.equals(region.getOwner())) {
region.update();
}
}
}
// Extract the color a name inherits from a prefix as an &-code, or ""
private static String extractPrefixColor(String prefix) {
if (prefix == null) {
return "";
@@ -0,0 +1,107 @@
package me.wiefferink.areashop.commands;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import me.wiefferink.areashop.AreaShop;
import me.wiefferink.areashop.commands.util.AreaShopCommandException;
import me.wiefferink.areashop.commands.util.AreashopCommandBean;
import me.wiefferink.areashop.commands.util.RegionParseUtil;
import me.wiefferink.areashop.managers.IFileManager;
import me.wiefferink.areashop.regions.RentRegion;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
import org.incendo.cloud.bean.CommandProperties;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.parser.flag.CommandFlag;
import javax.annotation.Nonnull;
@Singleton
public class PayRentCommand extends AreashopCommandBean {
private final AreaShop plugin;
private final CommandFlag<RentRegion> rentRegionFlag;
@Inject
public PayRentCommand(@Nonnull AreaShop plugin, @Nonnull IFileManager fileManager) {
this.plugin = plugin;
this.rentRegionFlag = RegionParseUtil.createDefaultRent(fileManager);
}
@Override
public String stringDescription() {
return "Allows you to pay the rent of a region, your own or someone else's";
}
@Override
public String getHelpKey(CommandSender target) {
if (target.hasPermission("areashop.payrent")) {
return "help-payrent";
}
return null;
}
@Override
protected @Nonnull CommandProperties properties() {
return CommandProperties.of("payrent");
}
@Override
protected @Nonnull Command.Builder<? extends CommandSender> configureCommand(
@Nonnull Command.Builder<CommandSender> builder)
{
return builder.literal("payrent").flag(this.rentRegionFlag).senderType(Player.class)
.handler(this::handleCommand);
}
private void handleCommand(@Nonnull CommandContext<Player> context) {
if (!context.hasPermission("areashop.payrent")) {
throw new AreaShopCommandException("payrent-noPermission");
}
RentRegion region = RegionParseUtil.getOrParseRentRegion(context, this.rentRegionFlag);
if (!region.isRented()) {
throw new AreaShopCommandException("payrent-notRented", region.getName());
}
// During the jubilee only the first rent is ever charged, extends are
// free and automatic, so there is no rent to pay
if (plugin.isJubilee()) {
throw new AreaShopCommandException("payrent-jubilee", region.getName());
}
Player payer = context.sender();
if (payer.getUniqueId().equals(region.getRenter())) {
region.rent(payer);
return;
}
region.rent(Bukkit.getOfflinePlayer(region.getRenter()), payer);
}
}
@@ -25,6 +25,7 @@ import me.wiefferink.areashop.commands.InfoRegionCommand;
import me.wiefferink.areashop.commands.LinkSignsCommand;
import me.wiefferink.areashop.commands.MeCommand;
import me.wiefferink.areashop.commands.MessageCommand;
import me.wiefferink.areashop.commands.PayRentCommand;
import me.wiefferink.areashop.commands.QuickBuyCommand;
import me.wiefferink.areashop.commands.QuickDeleteCommand;
import me.wiefferink.areashop.commands.QuickRentCommand;
@@ -73,12 +74,12 @@ public class AreashopCommands {
DelSignCommand.class, FindCommand.class, GroupAddCommand.class, GroupDelCommand.class,
GroupInfoCommand.class, GroupListCommand.class, HelpCommand.class, InfoCommand.class, InfoBaseCommand.class,
InfoPlayerCommand.class, InfoRegionCommand.class, LinkSignsCommand.class, MeCommand.class,
MessageCommand.class, QuickBuyCommand.class, QuickDeleteCommand.class, QuickRentCommand.class,
ReloadCommand.class, RentCommand.class, ResellCommand.class, SchematicEventCommand.class, SellCommand.class,
SetDurationCommand.class, SetLandlordCommand.class, SetOwnerCommand.class, SetPriceCommand.class,
SetRestoreCommand.class, SetTeleportCommand.class, SetTransferCommand.class, StackCommand.class,
StopResellCommand.class, TeleportCommand.class, ToggleHomeCommand.class, TransferCommand.class,
UnrentCommand.class);
MessageCommand.class, PayRentCommand.class, QuickBuyCommand.class, QuickDeleteCommand.class,
QuickRentCommand.class, ReloadCommand.class, RentCommand.class, ResellCommand.class,
SchematicEventCommand.class, SellCommand.class, SetDurationCommand.class, SetLandlordCommand.class,
SetOwnerCommand.class, SetPriceCommand.class, SetRestoreCommand.class, SetTeleportCommand.class,
SetTransferCommand.class, StackCommand.class, StopResellCommand.class, TeleportCommand.class,
ToggleHomeCommand.class, TransferCommand.class, UnrentCommand.class);
private final MessageBridge messageBridge;
@@ -510,7 +510,8 @@ public class BuyRegion extends GeneralRegion {
}
// Buying and reselling are free during the jubilee, otherwise the price is charged
// Buying and reselling are free during the jubilee, otherwise the price is
// charged
boolean jubilee = plugin.isJubilee();
UUID oldOwner = getBuyer();
if (isResell && oldOwner != null) {
@@ -548,8 +549,8 @@ public class BuyRegion extends GeneralRegion {
try {
economy.playerPayPlayer(payingPlayer.getUniqueId(), oldOwner,
Utils.diamondsToShards(resellPrice), "AreaShop resell: " + getName(), null);
economy.playerPayPlayer(payingPlayer.getUniqueId(), oldOwner, Utils.diamondsToShards(resellPrice),
"AreaShop resell: " + getName(), null);
} catch (DiamondBankException.InsufficientFundsException e) {
@@ -253,6 +253,7 @@ public class RentRegion extends GeneralRegion {
case AreaShop.tagPrice -> Message.fromString(getFormattedPrice());
case AreaShop.tagRawPrice -> getPrice();
case AreaShop.tagDuration -> getDurationString();
case AreaShop.tagDurationShort -> Utils.millisToCompactFormat(getDuration());
case AreaShop.tagPlayerName -> getPlayerName();
case AreaShop.tagPlayerColor -> Message.fromString(plugin.getPlayerPrefixColors(getRenter()));
case AreaShop.tagPlayerUUID -> getRenter();
@@ -641,12 +642,26 @@ public class RentRegion extends GeneralRegion {
/**
* Rent a region.
*
*
* @param offlinePlayer The player that wants to rent the region
* @return true if it succeeded and false if not
*/
public boolean rent(OfflinePlayer offlinePlayer) {
return rent(offlinePlayer, null);
}
/**
* Rent a region, optionally charged to another player.
*
* @param offlinePlayer The player the rent is for
* @param payer The online player paying the Diamonds, or null to charge
* the renter themselves
* @return true if it succeeded and false if not
*/
public boolean rent(OfflinePlayer offlinePlayer, Player payer) {
if (economy == null) {
message(offlinePlayer, "general-noEconomy");
@@ -818,14 +833,16 @@ public class RentRegion extends GeneralRegion {
}
// During the jubilee only the first rent is charged, otherwise every rent and extend
// costs the (possibly prorated) price. Payment is taken from the renter's physical
// diamonds through DiamondBank-OG, so the renter must be online.
// During the jubilee only the first rent is charged, otherwise every rent and
// extend
// costs the (possibly prorated) price. Payment is taken from the paying
// player's physical
// diamonds through DiamondBank-OG, so the payer must be online.
boolean charged = price > 0 && (!extend || !plugin.isJubilee());
long priceShards = 0;
if (charged) {
Player payingPlayer = offlinePlayer.getPlayer();
Player payingPlayer = payer != null ? payer : offlinePlayer.getPlayer();
if (payingPlayer == null) {
message(offlinePlayer, "rent-payError");
@@ -839,7 +856,7 @@ public class RentRegion extends GeneralRegion {
long balance = economy.getTotalShards(payingPlayer.getUniqueId());
if (balance < priceShards) {
message(offlinePlayer, extend ? "rent-lowMoneyExtend" : "rent-lowMoneyRent",
message(payingPlayer, extend ? "rent-lowMoneyExtend" : "rent-lowMoneyRent",
economy.shardsToDiamonds(balance));
return false;
@@ -847,7 +864,7 @@ public class RentRegion extends GeneralRegion {
} catch (DiamondBankException e) {
message(offlinePlayer, "rent-payError");
message(payingPlayer, "rent-payError");
return false;
}
@@ -864,10 +881,10 @@ public class RentRegion extends GeneralRegion {
}
// Subtract the money from the player's balance (physical diamonds)
// Subtract the money from the paying player's balance (physical diamonds)
if (charged) {
Player payingPlayer = offlinePlayer.getPlayer();
Player payingPlayer = payer != null ? payer : offlinePlayer.getPlayer();
if (payingPlayer == null) {
message(offlinePlayer, "rent-payError");
@@ -892,13 +909,13 @@ public class RentRegion extends GeneralRegion {
}
message(offlinePlayer, extend ? "rent-lowMoneyExtend" : "rent-lowMoneyRent", have);
message(payingPlayer, extend ? "rent-lowMoneyExtend" : "rent-lowMoneyRent", have);
return false;
} catch (DiamondBankException e) {
message(offlinePlayer, "rent-payError");
AreaShop.debug("Something went wrong with getting money from " + offlinePlayer.getName()
message(payingPlayer, "rent-payError");
AreaShop.debug("Something went wrong with getting money from " + payingPlayer.getName()
+ " while renting " + getName() + ": " + e.getMessage());
return false;
@@ -963,6 +980,13 @@ public class RentRegion extends GeneralRegion {
}
// Confirm to the payer when they paid for someone else's rent
if (payer != null && !payer.getUniqueId().equals(offlinePlayer.getUniqueId())) {
message(payer, "payrent-paidOther");
}
// Notify about updates
this.notifyAndUpdate(new RentedRegionEvent(this, extend));
return true;
@@ -1016,7 +1040,8 @@ public class RentRegion extends GeneralRegion {
}
// Pay back (part of) the price for the unused time, jubilee mode pays nothing back.
// Pay back (part of) the price for the unused time, jubilee mode pays nothing
// back.
// If the payback fails the unrent is cancelled completely.
double moneyBack = getMoneyBackAmount();
if (giveMoneyBack && !plugin.isJubilee() && moneyBack > 0) {
@@ -1,12 +1,10 @@
/*
* Adapted from the ParticleBorder minifeature of AdvancedRegionMarket
* (https://github.com/alex9849/advanced-region-market), Copyright alex9849,
* licensed under the Apache License, Version 2.0:
* http://www.apache.org/licenses/LICENSE-2.0
* Modifications for AreaShop: takes a plugin instance instead of using the ARM
* singleton, outlines cuboids only (polygonal regions use their bounding box),
* always uses the purple witch particle effect and restarts cleanly when recreated.
*/
// Adapted from the ParticleBorder minifeature of AdvancedRegionMarket
// (https://github.com/alex9849/advanced-region-market), Copyright alex9849,
// licensed under the Apache License, Version 2.0:
// http://www.apache.org/licenses/LICENSE-2.0
// Modifications for AreaShop: takes a plugin instance instead of using the ARM
// singleton, outlines cuboids only (polygonal regions use their bounding box),
// always uses the purple witch particle effect and restarts cleanly when recreated.
package me.wiefferink.areashop.tools;
import org.bukkit.Bukkit;
@@ -20,9 +18,7 @@ import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
/**
* Shows the edges of a cuboid region to a single player with purple particles.
*/
// Shows the edges of a cuboid region to a single player with purple particles.
public class ParticleBorder {
private final Plugin plugin;
@@ -33,15 +29,8 @@ public class ParticleBorder {
private Integer taskID;
private Integer cancelerID;
/**
* Create a particle border for a cuboid volume.
*
* @param plugin Plugin to schedule the particle tasks with
* @param player Player that should see the particles
* @param world World the volume is in
* @param pos1 First corner of the volume
* @param pos2 Second corner of the volume
*/
// Create a particle border for a cuboid volume that only the given player will
// see.
public ParticleBorder(Plugin plugin, Player player, World world, Vector pos1, Vector pos2) {
this.plugin = plugin;
@@ -87,11 +76,7 @@ public class ParticleBorder {
}
/**
* Start showing the border to the player.
*
* @param ticks How long the border should stay visible, in ticks
*/
// Start showing the border to the player for the given number of ticks.
public void createParticleBorder(int ticks) {
removeBorder();
@@ -128,9 +113,7 @@ public class ParticleBorder {
}
/**
* Stop showing the border.
*/
// Stop showing the border.
public void removeBorder() {
if (taskID == null || cancelerID == null) {
@@ -409,6 +409,73 @@ public class Utils {
}
/**
* Convert milliseconds to the compact time format AdvancedRegionMarket uses on
* signs, for example '30d', '1d 12h', '45min' or '30s'.
*
* @param milliseconds The amount of milliseconds to convert
* @return A compact string with the non-zero day/hour/minute/second parts
*/
public static String millisToCompactFormat(long milliseconds) {
long seconds = (milliseconds + 500) / 1000;
if (seconds <= 0) {
return "0s";
}
long days = seconds / 86400;
long hours = (seconds % 86400) / 3600;
long minutes = (seconds % 3600) / 60;
long secs = seconds % 60;
StringBuilder result = new StringBuilder();
if (days > 0) {
result.append(days).append("d");
}
if (hours > 0) {
if (result.length() > 0) {
result.append(" ");
}
result.append(hours).append("h");
}
if (minutes > 0) {
if (result.length() > 0) {
result.append(" ");
}
result.append(minutes).append("min");
}
if (secs > 0) {
if (result.length() > 0) {
result.append(" ");
}
result.append(secs).append("s");
}
return result.toString();
}
private static final BlockFace[] facings = { BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST,
BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST };
@@ -724,7 +791,8 @@ public class Utils {
}
// Convert an amount of diamonds to DiamondBank-OG shards (9 shards = 1 diamond).
// Convert an amount of diamonds to DiamondBank-OG shards (9 shards = 1
// diamond).
public static long diamondsToShards(double diamonds) {
return Math.round(diamonds * 9.0);
+1
View File
@@ -193,6 +193,7 @@ maximumTries: 50000
# %price% The price of the region formatted with the configured characters before and after the number.
# %rawprice% The price without formatting, like '10.0' or '7.77'.
# %duration% The duration of a rent region, for example '1 d', '4 minutes' or '2 years'.
# %durationshort% The duration in compact form for signs, for example '30d', '1d 12h' or '45min'.
# %world% The name of the world that the region is in.
# %type% The type of the region, 'rent' or 'buy'.
# %until% The end time of a rent formatted as configured with 'timeFormatChat'.
+21 -19
View File
@@ -44,10 +44,10 @@ general:
# Sections with 'Player' at the end will be run by the player that clicks the sign and 'Console' ones from the console.
signProfile:
forrent:
line1: '&0&lFor Rent:'
line2: '&6%region%'
line3: '&eDuration: %duration% (12x)'
line4: '&b%price%'
line1: '&2For Rent:'
line2: '&0&l%region%'
line3: '&e%durationshort% (12x)'
line4: '%price%'
rightClickPlayer:
- 'areashop info region --region %region%'
rightClickConsole:
@@ -55,8 +55,8 @@ general:
leftClickPlayer:
- 'areashop info region --region %region%'
rented:
line1: '&0&lRented:'
line2: '&6%region%'
line1: '&2Rented:'
line2: '&0&l%region%'
line3: '&3&lShopkeeper:'
line4: '&0%playercolor%%player%'
rightClickPlayer:
@@ -65,12 +65,14 @@ general:
# - 'areashop message %clicker% %lang:confirm-extend|%region%|%' # Alternative to first ask for confirmation (remove rightClickPlayer section)
leftClickPlayer:
- 'areashop info region --region %region%'
shiftRightClickConsole:
- 'areashop message %clicker% %lang:confirm-unrent|%region%|%'
shiftRightClickPlayer:
- 'areashop payrent --region %region%'
shiftLeftClickPlayer:
- 'areashop payrent --region %region%'
forsale:
line1: '&0&lFor Sale:'
line2: '&6%region%'
line3: '&b%price%'
line1: '&2For Sale:'
line2: '&0&l%region%'
line3: '%price%'
line4: '&8%width%x%depth%x%height%'
rightClickPlayer:
- 'areashop info region --region %region%'
@@ -79,9 +81,9 @@ general:
leftClickPlayer:
- 'areashop info region --region %region%'
resell:
line1: '&0&lFor Sale:'
line2: '&6%region%'
line3: '&b%resellprice%'
line1: '&2For Sale:'
line2: '&0&l%region%'
line3: '%resellprice%'
line4: '&8%width%x%depth%x%height%'
rightClickPlayer:
- 'areashop info region --region %region%'
@@ -92,8 +94,8 @@ general:
shiftRightClickPlayer:
- 'areashop stopresell --region %region%'
sold:
line1: '&0&lSold:'
line2: '&6%region%'
line1: '&4Sold:'
line2: '&0&l%region%'
line3: '&0%playercolor%%player%'
line4: '&2No Expiry'
rightClickPlayer:
@@ -122,14 +124,14 @@ general:
block-place: 'allow g:members'
chest-access: 'allow g:members'
forrent:
greeting: '%lang:wgPrefix%&6This shop can be rented for &b%price% &6per &2%duration%'
greeting: '%lang:wgPrefix%&6This shop can be rented for %price% &6per &2%duration%'
use: 'allow g:all'
rented:
members: '%uuid%, %friendsuuid%'
greeting: ''
use: 'allow g:members'
forsale:
greeting: '%lang:wgPrefix%&6This shop can be purchased for &b%price%'
greeting: '%lang:wgPrefix%&6This shop can be purchased for %price%'
use: 'allow g:all'
sold:
members: '%uuid%, %friendsuuid%'
@@ -137,7 +139,7 @@ general:
use: 'allow g:members'
resell:
members: '%uuid%, %friendsuuid%'
greeting: '%lang:wgPrefix%&6This shop can be purchased for &b%resellprice%'
greeting: '%lang:wgPrefix%&6This shop can be purchased for %resellprice%'
use: 'allow g:all'
##### Save and restore schematics to clean up regions.
+7
View File
@@ -74,6 +74,7 @@ help-alias: "Command aliases: /areashop, /as."
help-help: "%lang:helpCommand|/as help|% Shows this help page."
help-info: "%lang:helpCommand|/as info|% Get info about current regions."
help-rent: "%lang:helpCommand|/as rent|% Rent a region or extend your current rent."
help-payrent: "%lang:helpCommand|/as payrent|% Pay the rent of a region, your own or someone else's."
help-buy: "%lang:helpCommand|/as buy|% Buy a region."
help-unrent: "%lang:helpCommand|/as unrent|% Unrent a region."
help-sell: "%lang:helpCommand|/as sell|% Sell a region."
@@ -131,6 +132,12 @@ rent-expireWarning: "Your region %lang:region% has %timeleft% left, be sure to e
rent-expiringSoon: "Your region %lang:tRegion|%0%|% has %1% left, be sure to extend it if you want to keep it."
rent-alreadyAtFull: "%lang:region% is already at or above the maximum rental time, you cannot extend it yet."
payrent-help: "/as payrent [region], pay the rent of a region for its current renter, the region you stand in will be used if not specified."
payrent-noPermission: "You don't have permission to pay the rent of a region."
payrent-notRented: "Region '%0%' is not rented, there is no rent to pay."
payrent-jubilee: "There is no rent to pay for region '%0%', during the jubilee only the first rent is charged."
payrent-paidOther: "You paid the rent of %lang:region%, it is now extended for %player%."
buy-help: "/as buy [region], the region you stand in will be used if not specified."
buy-noPermission: "You don't have permission to buy a region."
buy-noPermissionResell: "You don't have permission to buy a region in resell mode."
+4
View File
@@ -24,6 +24,7 @@ permissions:
areashop.destroyrent: true
areashop.destroybuy: true
areashop.rent: true
areashop.payrent: true
areashop.buy: true
areashop.buyresell: true
areashop.buynormal: true
@@ -76,6 +77,9 @@ permissions:
areashop.rent:
description: Allows you to rent a region
default: true
areashop.payrent:
description: Allows you to pay the rent of a region, your own or someone else's
default: true
areashop.buy:
description: Allows you to buy a region
default: true