Various improvements to the interface

More time units are supported, which makes long temporary durations more readable
Makes right-clicking a sign display a full description, and requires sneaking while clicking to perform the transaction
Makes signs only be destroyed when sneaking
Improves sign protection a bit, but improvements are required
Prevents a creative player breaking a sign from also interacting with it
This commit is contained in:
Kristian Knarvik 2022-01-17 18:42:15 +01:00
parent bec45ae968
commit 23c023a027
8 changed files with 228 additions and 23 deletions

View File

@ -33,6 +33,9 @@ import java.util.Queue;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
/**
* The main permissionsigns class
*/
public final class PermissionSigns extends JavaPlugin { public final class PermissionSigns extends JavaPlugin {
private static final Queue<SignCreationRequest> signCreationRequests = new PriorityQueue<>(); private static final Queue<SignCreationRequest> signCreationRequests = new PriorityQueue<>();
@ -131,6 +134,7 @@ public final class PermissionSigns extends JavaPlugin {
PluginDescriptionFile pluginDescriptionFile = this.getDescription(); PluginDescriptionFile pluginDescriptionFile = this.getDescription();
pluginVersion = pluginDescriptionFile.getVersion(); pluginVersion = pluginDescriptionFile.getVersion();
//TODO: Allow for custom language files. Perhaps just look for strings.yml in the folder //TODO: Allow for custom language files. Perhaps just look for strings.yml in the folder
//TODO: Display a notice in the console if a new version is available
FileConfiguration config = this.getConfig(); FileConfiguration config = this.getConfig();
config.options().copyDefaults(true); config.options().copyDefaults(true);

View File

@ -148,11 +148,34 @@ public class PermissionSign {
* *
* @return <p>The string used for displaying this sign's duration</p> * @return <p>The string used for displaying this sign's duration</p>
*/ */
private String getDurationString() { public String getDurationString() {
if (duration == 0) { if (duration == 0) {
return Translator.getTranslatedMessage(TranslatableMessage.SIGN_PERMANENT); return Translator.getTranslatedMessage(TranslatableMessage.SIGN_PERMANENT);
} else { } else {
return duration + " " + Translator.getTranslatedMessage(TranslatableMessage.SIGN_TIME_UNIT); double minute = 60;
double hour = minute * 60;
double day = hour * 24;
if (duration / day >= 1) {
double days = round(duration / day);
if (days == 1) {
return (int) days + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_DAY);
} else {
return days + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_DAYS);
}
} else if (duration / hour >= 1) {
double hours = round(duration / hour);
if (hours == 1) {
return (int) hours + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_HOUR);
} else {
return hours + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_HOURS);
}
} else {
if (duration == 1) {
return duration + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_SECOND);
} else {
return duration + " " + Translator.getTranslatedMessage(TranslatableMessage.UNIT_SECONDS);
}
}
} }
} }
@ -161,7 +184,7 @@ public class PermissionSign {
* *
* @return <p>The string used for displaying this sign's cost</p> * @return <p>The string used for displaying this sign's cost</p>
*/ */
private String getCostString() { public String getCostString() {
if (cost == 0) { if (cost == 0) {
return Translator.getTranslatedMessage(TranslatableMessage.SIGN_COST_FREE); return Translator.getTranslatedMessage(TranslatableMessage.SIGN_COST_FREE);
} else { } else {
@ -170,4 +193,14 @@ public class PermissionSign {
} }
} }
/**
* Rounds a number to its last two digits
*
* @param number <p>The number to round</p>
* @return <p>The rounded number</p>
*/
private double round(double number) {
return Math.round(number * 100.0) / 100.0;
}
} }

View File

@ -16,9 +16,44 @@ public enum TranslatableMessage {
SIGN_PREFIX, SIGN_PREFIX,
/** /**
* The text to display as a permission sign's duration time unit * The text to display for 1 second
*/ */
SIGN_TIME_UNIT, UNIT_SECOND,
/**
* The text to display for a number of seconds
*/
UNIT_SECONDS,
/**
* The text to display for 1 minute
*/
UNIT_MINUTE,
/**
* The text to display for a number of minutes
*/
UNIT_MINUTES,
/**
* The text to display for 1 hour
*/
UNIT_HOUR,
/**
* The text to display for a number of hours
*/
UNIT_HOURS,
/**
* The text to display for 1 day
*/
UNIT_DAY,
/**
* The text to display for a number of days
*/
UNIT_DAYS,
/** /**
* The text to display on a permission sign that is entirely free * The text to display on a permission sign that is entirely free
@ -50,6 +85,11 @@ public enum TranslatableMessage {
*/ */
PERMISSION_SIGN_DESTROY_DENY, PERMISSION_SIGN_DESTROY_DENY,
/**
* The message to display to a non-sneaking player trying to destroy a permissions sign
*/
PERMISSION_SIGN_DESTROY_SNEAK,
/** /**
* The error message to display if the user has not provided necessary information to create a permission sign * The error message to display if the user has not provided necessary information to create a permission sign
*/ */
@ -113,6 +153,11 @@ public enum TranslatableMessage {
/** /**
* The message to display when a reload action has been successfully performed * The message to display when a reload action has been successfully performed
*/ */
RELOAD_SUCCESSFUL RELOAD_SUCCESSFUL,
/**
* The text describing all properties of a permission sign
*/
SIGN_INFO
} }

View File

@ -6,7 +6,6 @@ import org.bukkit.configuration.file.YamlConfiguration;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
@ -57,7 +56,7 @@ public final class Translator {
BufferedReader reader; BufferedReader reader;
try { try {
reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml"); reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml");
} catch (FileNotFoundException | UnsupportedEncodingException e) { } catch (FileNotFoundException e) {
PermissionSigns.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages"); PermissionSigns.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages");
return null; return null;
} }

View File

@ -6,12 +6,19 @@ import net.knarcraft.permissionsigns.manager.SignManager;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Sign; import org.bukkit.block.Sign;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import java.util.ArrayList;
import java.util.List;
/**
* A listener for relevant block events such as signs being broken
*/
public class BlockListener implements Listener { public class BlockListener implements Listener {
@EventHandler @EventHandler
@ -23,11 +30,60 @@ public class BlockListener implements Listener {
return; return;
} }
//if (block instanceof FallingBlock) {
//TODO: Search recursively upwards (and downwards if pointed dripstone) until a non-falling block is found.
// If upwards, check the non-falling block, but not downwards. If player does not have the create permission,
// just stop and cancel once a permission sign is found. If it has, all affected signs need to be
// de-registered. Must account for stacks of sand and signs on top of each-other. So if a normal sign is
// found, the recursion upward must also happen.
//}
Material material = block.getBlockData().getMaterial(); Material material = block.getBlockData().getMaterial();
if (!Tag.SIGNS.isTagged(material) && !Tag.WALL_SIGNS.isTagged(material)) { if (Tag.SIGNS.isTagged(material) || Tag.WALL_SIGNS.isTagged(material)) {
checkIfBlockIsPermissionSign(block, player, event);
if (event.isCancelled()) {
return; return;
} }
}
Block relativeBlock = block.getRelative(BlockFace.UP);
if (Tag.SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) {
checkIfBlockIsPermissionSign(relativeBlock, player, event);
if (event.isCancelled()) {
return;
}
}
if (block.getBlockData().getMaterial() == Material.POINTED_DRIPSTONE) {
relativeBlock = block.getRelative(BlockFace.DOWN);
if (Tag.WALL_SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) {
checkIfBlockIsPermissionSign(relativeBlock, player, event);
if (event.isCancelled()) {
return;
}
}
}
for (BlockFace blockFace : getRelevantBlockFaces()) {
relativeBlock = block.getRelative(blockFace);
if (Tag.WALL_SIGNS.isTagged(relativeBlock.getBlockData().getMaterial())) {
checkIfBlockIsPermissionSign(relativeBlock, player, event);
if (event.isCancelled()) {
return;
}
}
}
//TODO: Need to protect against other things that might damage the sign, such as explosions
}
/**
* Check if the given block is a permission sign, and perform necessary tasks if it is
*
* @param block <p>The block to check</p>
* @param player <p>The player that caused the event</p>
* @param event <p>The triggered block break event</p>
*/
private void checkIfBlockIsPermissionSign(Block block, Player player, BlockBreakEvent event) {
Sign sign = (Sign) block.getState(); Sign sign = (Sign) block.getState();
boolean registered = SignManager.getSign(sign.getLocation()) != null; boolean registered = SignManager.getSign(sign.getLocation()) != null;
if (!registered) { if (!registered) {
@ -37,12 +93,28 @@ public class BlockListener implements Listener {
event.setCancelled(true); event.setCancelled(true);
player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.PERMISSION_SIGN_DESTROY_DENY)); player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.PERMISSION_SIGN_DESTROY_DENY));
} else { } else {
if (!player.isSneaking()) {
event.setCancelled(true);
player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.PERMISSION_SIGN_DESTROY_SNEAK));
return;
}
SignManager.removeSign(sign.getLocation()); SignManager.removeSign(sign.getLocation());
player.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.PERMISSION_SIGN_REMOVED)); player.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.PERMISSION_SIGN_REMOVED));
} }
}
//TODO: Need to protect against other things that might damage the sign, such as explosions /**
//TODO: Only allow the sign to be broken if shift-clicking? * Gets the relevant block faces for checking around a block
*
* @return <p>The relevant block faces for checking around a block</p>
*/
private List<BlockFace> getRelevantBlockFaces() {
List<BlockFace> relevantBlockFaces = new ArrayList<>();
relevantBlockFaces.add(BlockFace.WEST);
relevantBlockFaces.add(BlockFace.EAST);
relevantBlockFaces.add(BlockFace.NORTH);
relevantBlockFaces.add(BlockFace.SOUTH);
return relevantBlockFaces;
} }
} }

View File

@ -9,6 +9,8 @@ import net.knarcraft.permissionsigns.formatting.Translator;
import net.knarcraft.permissionsigns.manager.EconomyManager; import net.knarcraft.permissionsigns.manager.EconomyManager;
import net.knarcraft.permissionsigns.manager.PermissionManager; import net.knarcraft.permissionsigns.manager.PermissionManager;
import net.knarcraft.permissionsigns.manager.SignManager; import net.knarcraft.permissionsigns.manager.SignManager;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Tag; import org.bukkit.Tag;
import org.bukkit.block.Block; import org.bukkit.block.Block;
@ -16,9 +18,11 @@ import org.bukkit.block.Sign;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
/** /**
@ -46,7 +50,7 @@ public class SignListener implements Listener {
} }
Sign sign = (Sign) block.getState(); Sign sign = (Sign) block.getState();
handleSignClick(sign, player); handleSignClick(sign, player, event.getAction());
} }
/** /**
@ -54,19 +58,26 @@ public class SignListener implements Listener {
* *
* @param sign <p>The clicked sign</p> * @param sign <p>The clicked sign</p>
* @param player <p>The player that clicked the sign</p> * @param player <p>The player that clicked the sign</p>
* @param action <p>The hand the player used to interact with the sign</p>
*/ */
private void handleSignClick(Sign sign, Player player) { private void handleSignClick(Sign sign, Player player, Action action) {
//Check if the sign is a registered permission sign //Check if the sign is a registered permission sign
PermissionSign permissionSign = SignManager.getSign(sign.getLocation()); PermissionSign permissionSign = SignManager.getSign(sign.getLocation());
if (permissionSign != null) { if (permissionSign != null) {
if (player.getGameMode() == GameMode.CREATIVE && action == Action.LEFT_CLICK_BLOCK) {
return;
}
if (!player.hasPermission("permissionsigns.use")) { if (!player.hasPermission("permissionsigns.use")) {
player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.INTERACT_PERMISSION_MISSING)); player.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.INTERACT_PERMISSION_MISSING));
return; return;
} }
if (player.isSneaking()) {
handlePermissionSignInteract(permissionSign, player); handlePermissionSignInteract(permissionSign, player);
} else {
player.sendMessage(getSignInfoText(permissionSign));
}
return; return;
} }
//TODO: Display information about granted permissions when shift-clicking?
//Check if the player has a creation request that can be fulfilled //Check if the player has a creation request that can be fulfilled
SignCreationRequest request = PermissionSigns.getSignCreationRequest(player.getUniqueId()); SignCreationRequest request = PermissionSigns.getSignCreationRequest(player.getUniqueId());
@ -75,6 +86,31 @@ public class SignListener implements Listener {
} }
} }
/**
* Gets information about a sign
*
* @param sign <p>The sign to get information about</p>
* @return <p>The information to display</p>
*/
private String getSignInfoText(PermissionSign sign) {
String rawSignInfo = Translator.getTranslatedMessage(TranslatableMessage.SIGN_INFO);
StringBuilder permissionString = new StringBuilder();
List<String> permissionNodes = sign.getPermissionNodes();
if (permissionNodes.size() == 1) {
permissionString = new StringBuilder(permissionNodes.get(0));
} else {
for (String permissionNode : permissionNodes) {
permissionString.append("\n" + "&f| &7- ").append(permissionNode);
}
permissionString.append("\n");
}
String replacedString = StringFormatter.replacePlaceholders(rawSignInfo, new String[]{"{Name}", "{Permissions}",
"{Cost}", "{Duration}"}, new String[]{sign.getName(), permissionString.toString(), sign.getCostString(),
sign.getDurationString()});
return ChatColor.translateAlternateColorCodes('&', replacedString);
}
/** /**
* Handles the interaction with a permission sign * Handles the interaction with a permission sign
* *
@ -137,9 +173,8 @@ public class SignListener implements Listener {
String grantedPermissions = permissionsJoiner.toString(); String grantedPermissions = permissionsJoiner.toString();
if (permissionSign.getDuration() > 0) { if (permissionSign.getDuration() > 0) {
String successMessage = StringFormatter.getTranslatedInfoMessage(TranslatableMessage.PERMISSIONS_GRANTED); String successMessage = StringFormatter.getTranslatedInfoMessage(TranslatableMessage.PERMISSIONS_GRANTED);
String timeUnit = Translator.getTranslatedMessage(TranslatableMessage.SIGN_TIME_UNIT);
player.sendMessage(StringFormatter.replacePlaceholders(successMessage, new String[]{"{permissions}", "{time}"}, player.sendMessage(StringFormatter.replacePlaceholders(successMessage, new String[]{"{permissions}", "{time}"},
new String[]{grantedPermissions, permissionSign.getDuration() + " " + timeUnit})); new String[]{grantedPermissions, permissionSign.getDurationString()}));
} else { } else {
player.sendMessage(StringFormatter.replacePlaceholder(StringFormatter.getTranslatedInfoMessage( player.sendMessage(StringFormatter.replacePlaceholder(StringFormatter.getTranslatedInfoMessage(
TranslatableMessage.PERMISSIONS_GRANTED_PERMANENTLY), "{permissions}", grantedPermissions)); TranslatableMessage.PERMISSIONS_GRANTED_PERMANENTLY), "{permissions}", grantedPermissions));

View File

@ -4,7 +4,6 @@ import java.io.BufferedReader;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
/** /**
@ -18,7 +17,7 @@ public class FileHelper {
* @return <p>A buffered read for reading the file</p> * @return <p>A buffered read for reading the file</p>
* @throws FileNotFoundException <p>If unable to get an input stream for the given file</p> * @throws FileNotFoundException <p>If unable to get an input stream for the given file</p>
*/ */
public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException, UnsupportedEncodingException { public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException {
InputStream inputStream = FileHelper.class.getResourceAsStream(file); InputStream inputStream = FileHelper.class.getResourceAsStream(file);
if (inputStream == null) { if (inputStream == null) {
throw new FileNotFoundException("Unable to read the given file"); throw new FileNotFoundException("Unable to read the given file");

View File

@ -2,13 +2,21 @@ en:
PREFIX: "[PermissionSigns]" PREFIX: "[PermissionSigns]"
SIGN_PREFIX: "&4[PermSign]" SIGN_PREFIX: "&4[PermSign]"
MISSING_CREATION_INFO: "&7You must specify a sign name and a comma-separated list of permissions to create a permission sign" MISSING_CREATION_INFO: "&7You must specify a sign name and a comma-separated list of permissions to create a permission sign"
SIGN_TIME_UNIT: "seconds" UNIT_SECOND: "second"
UNIT_SECONDS: "seconds"
UNIT_MINUTE: "minute"
UNIT_MINUTES: "minutes"
UNIT_HOUR: "hour"
UNIT_HOURS: "hours"
UNIT_DAY: "day"
UNIT_DAYS: "days"
SIGN_PERMANENT: "Permanent" SIGN_PERMANENT: "Permanent"
SIGN_COST_FREE: "Free" SIGN_COST_FREE: "Free"
COST_INVALID_NUMBER: "&7The given cost is not a valid number" COST_INVALID_NUMBER: "&7The given cost is not a valid number"
DURATION_INVALID_NUMBER: "&7The given duration is not a valid number" DURATION_INVALID_NUMBER: "&7The given duration is not a valid number"
COMMAND_PLAYER_ONLY: "&7This command is only available to players" COMMAND_PLAYER_ONLY: "&7This command is only available to players"
PERMISSION_SIGN_DESTROY_DENY: "&7You do not have permissions to delete permissions signs" PERMISSION_SIGN_DESTROY_DENY: "&7You do not have permissions to delete permissions signs"
PERMISSION_SIGN_DESTROY_SNEAK: "&7You must be sneaking to remove a permissions sign"
PERMISSION_SIGN_REMOVED: "&7Permissions sign removed" PERMISSION_SIGN_REMOVED: "&7Permissions sign removed"
COMMAND_PERMISSION_DENIED: "&7You do not have necessary permissions to perform this command" COMMAND_PERMISSION_DENIED: "&7You do not have necessary permissions to perform this command"
CREATION_REQUEST_CREATED: "&7Permission Sign request created. Right-click an empty sign within 60 seconds to create the new permission sign" CREATION_REQUEST_CREATED: "&7Permission Sign request created. Right-click an empty sign within 60 seconds to create the new permission sign"
@ -21,17 +29,26 @@ en:
INTERACT_PERMISSION_MISSING: "&7You do not have the necessary permissions required to use permission signs" INTERACT_PERMISSION_MISSING: "&7You do not have the necessary permissions required to use permission signs"
CREATION_REQUEST_CANCELLED: "&7Your last permission sign creation request has been cancelled" CREATION_REQUEST_CANCELLED: "&7Your last permission sign creation request has been cancelled"
RELOAD_SUCCESSFUL: "&7The plugin has been successfully reloaded" RELOAD_SUCCESSFUL: "&7The plugin has been successfully reloaded"
SIGN_INFO: "&f---- &4Permission Sign&f ----\n&f| &bName: &7{Name}\n&f| &bPermission(s): &7{Permissions}\n&f| &bCost: &7{Cost}\n&f| &bDuration: &7{Duration}\n&f| &7Right-click the sign while sneaking to buy the permission(s)\n&f-------------------------"
nb-no: nb-no:
PREFIX: "[Tilgangsrettighetsskilt]" PREFIX: "[Tilgangsrettighetsskilt]"
SIGN_PREFIX: "&4[PermSign]" SIGN_PREFIX: "&4[PermSign]"
MISSING_CREATION_INFO: "&7Du må spesifisere et skiltnavn og en komma-separert liste av tilgangsrettigheter for å opprette et tilgangsrettighetsskilt" MISSING_CREATION_INFO: "&7Du må spesifisere et skiltnavn og en komma-separert liste av tilgangsrettigheter for å opprette et tilgangsrettighetsskilt"
SIGN_TIME_UNIT: "sekunder" UNIT_SECOND: "sekund"
UNIT_SECONDS: "sekunder"
UNIT_MINUTE: "minutt"
UNIT_MINUTES: "minutter"
UNIT_HOUR: "time"
UNIT_HOURS: "timer"
UNIT_DAY: "dag"
UNIT_DAYS: "dager"
SIGN_PERMANENT: "Permanent" SIGN_PERMANENT: "Permanent"
SIGN_COST_FREE: "Gratis" SIGN_COST_FREE: "Gratis"
COST_INVALID_NUMBER: "&7Den gitte kostnaden er ikke et gyldig nummer" COST_INVALID_NUMBER: "&7Den gitte kostnaden er ikke et gyldig nummer"
DURATION_INVALID_NUMBER: "&7Den gitte varigheten er ikke et gyldig nummer" DURATION_INVALID_NUMBER: "&7Den gitte varigheten er ikke et gyldig nummer"
COMMAND_PLAYER_ONLY: "&7Denne kommandoen kan bare brukes av spillere" COMMAND_PLAYER_ONLY: "&7Denne kommandoen kan bare brukes av spillere"
PERMISSION_SIGN_DESTROY_DENY: "&7Du har ikke tillatelse til å ødelegge tilgangsrettighetsskilter" PERMISSION_SIGN_DESTROY_DENY: "&7Du har ikke tillatelse til å ødelegge tilgangsrettighetsskilter"
PERMISSION_SIGN_DESTROY_SNEAK: "&7Du må snike for å fjerne et tilgangsrettighetsskilt"
PERMISSION_SIGN_REMOVED: "&7Tilgangsrettighetsskilt fjernet" PERMISSION_SIGN_REMOVED: "&7Tilgangsrettighetsskilt fjernet"
COMMAND_PERMISSION_DENIED: "&7Du har ikke de nødvendige tilgangsrettighetene for å kjøre denne kommandoen" COMMAND_PERMISSION_DENIED: "&7Du har ikke de nødvendige tilgangsrettighetene for å kjøre denne kommandoen"
CREATION_REQUEST_CREATED: "&7Tilgangsrettighetsskiltsforespørsel opprettet. Høyre-klikk på et tomt skilt innen 60 sekunder for å opprette det nye tilgangsrettighetsskiltet" CREATION_REQUEST_CREATED: "&7Tilgangsrettighetsskiltsforespørsel opprettet. Høyre-klikk på et tomt skilt innen 60 sekunder for å opprette det nye tilgangsrettighetsskiltet"
@ -44,3 +61,4 @@ nb-no:
INTERACT_PERMISSION_MISSING: "&7Du har ikke de nødvendige tilgangsrettighetene for å bruke tilgangsrettighetsskilt" INTERACT_PERMISSION_MISSING: "&7Du har ikke de nødvendige tilgangsrettighetene for å bruke tilgangsrettighetsskilt"
CREATION_REQUEST_CANCELLED: "&7Din siste tilgangsrettighetsskiltsforespørsel har blitt kansellert" CREATION_REQUEST_CANCELLED: "&7Din siste tilgangsrettighetsskiltsforespørsel har blitt kansellert"
RELOAD_SUCCESSFUL: "&7Plugin-modulen har blitt lastet inn på nytt" RELOAD_SUCCESSFUL: "&7Plugin-modulen har blitt lastet inn på nytt"
SIGN_INFO: "&f---- &4Tilgangsrettighetsskilt&f ----\n&f| &bNavn: &7{Name}\n&f| &bTilgangsrettighet(er): &7{Permissions}\n&f| &bKostnad: &7{Cost}\n&f| &bVarighet: &7{Duration}\n&f| &7Høyreklikk skiltet mens du sniker for å kjøpe tilgangsrettighet(en/ene)\n&f-------------------------"