Compare commits

...

24 Commits

Author SHA1 Message Date
814a6dc701 Adds POM relocations
All checks were successful
KnarCraft/PaidSigns/pipeline/head This commit looks good
2024-05-03 15:40:12 +02:00
4f3c27e4bd Bumps dev version
All checks were successful
KnarCraft/PaidSigns/pipeline/head This commit looks good
2023-06-25 16:42:14 +02:00
2bc4ab22f5 Bumps version for release
Some checks failed
EpicKnarvik97/PaidSigns/pipeline/head This commit looks good
KnarCraft/PaidSigns/pipeline/head There was a failure building this commit
2023-06-25 16:41:16 +02:00
396bcfc68b Adds proper support for dual-side paid signs 2023-06-24 15:07:03 +02:00
35a5e40eeb Bumps version for release
All checks were successful
EpicKnarvik97/PaidSigns/pipeline/head This commit looks good
2023-06-24 12:52:17 +02:00
a3cce66848 Bumps KnarLib version
All checks were successful
EpicKnarvik97/PaidSigns/pipeline/head This commit looks good
2023-06-24 12:10:24 +02:00
8f3af1f288 Bumps version to 1.0.2-SNAPSHOT
All checks were successful
EpicKnarvik97/PaidSigns/pipeline/head This commit looks good
2023-06-24 11:40:18 +02:00
d8aabeee88 Updates for 1.20
Some checks failed
EpicKnarvik97/PaidSigns/pipeline/head There was a failure building this commit
Makes sure to unregister and refund on the sign edit event, as the sign might be registered as a paid sign already.
Updates Spigot
Updates Vault repository
2023-06-23 20:34:20 +02:00
05898d7d65 Adds Jenkinsfile
All checks were successful
EpicKnarvik97/PaidSigns/pipeline/head This commit looks good
2022-11-26 15:25:18 +01:00
7355b7fc60 Uses Economy.format to display costs 2022-11-14 00:36:23 +00:00
8e3a71a489 Fixes Vault repo URL http instead of https 2022-11-13 22:48:18 +00:00
1154d7ddaf Fixes README table formatting 2022-11-13 22:42:52 +00:00
dd47ce06eb Makes sure forced refunds always refund the full amount 2022-11-13 22:39:11 +00:00
e7c40fb4b0 Makes a method to check if a block is a sign
This will hopefully prevent any more mistakes related to checking if something is a sign or not.
2022-11-12 12:48:21 +01:00
cf66113ac1 Fixes some problems in the plugin destroyed sign check 2022-11-12 01:27:46 +01:00
4fee628469 Adds a delayed sign removed check #13
This commit adds a delayed check for whether the block at the sign change event's location is still a sign. If it isn't, it's assumed that a plugin blocked the sign creation by destroying the sign. In this case, the player is immediately refunded, even if refunds are disabled.
2022-11-08 12:53:26 +01:00
5c095e79f6 Updates code to account for KnarLib changes 2022-11-07 22:26:22 +01:00
a9c97f71f9 Changes things for the non-static Translator 2022-11-07 15:21:02 +01:00
bb63fb59d1 Uses KnarLib where possible 2022-11-07 11:29:33 +01:00
6a7d106d31 Bumps version to 1.0.1 2022-11-05 04:55:31 +01:00
79f3f8e3d2 Saves tracked signs after loading
Previously, if a tracked sign was removed during loading, while it wasn't added to tracked signs, tracked signs wasn't saved, so on the next startup it would be loaded again.
2022-11-05 04:12:51 +01:00
d50d9b42c4 Delays refunding on startup to prevent a crash 2022-11-05 03:26:25 +01:00
b0c3fea730 Improves some README information 2022-10-20 11:50:54 +02:00
04cfd1b89e Fixes two small misses in plugin.yml and the README 2022-10-19 18:22:07 +02:00
32 changed files with 598 additions and 611 deletions

33
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,33 @@
pipeline {
agent any
tools {
jdk 'JDK17'
}
stages {
stage('Build') {
steps {
echo 'Building...'
sh 'mvn clean & mvn validate & mvn compile'
}
}
stage('Test') {
steps {
echo 'Testing...'
sh 'mvn test'
}
}
stage('Verify') {
steps {
echo 'Verifying...'
sh 'mvn verify -Dmaven.test.skip=true'
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
sh 'mvn deploy -Dmaven.install.skip=true -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}
}
}

140
README.md
View File

@ -1,7 +1,7 @@
# Paid signs
The paid-signs plugin is a plugin that lets you add costs for creating plugin signs. This allows restricting usage of
signs such as CraftBook's gate/lift and other signs while still allowing every player to use the signs.
signs such as CraftBook's gate/lift and other signs while still allowing every player to create the signs.
Note: OP players, and players with the '*' permission will have the `paidsigns.paymentexempt` permission and will not
see any payment messages. For testing, you'll need to un-set the `paidsigns.paymentexempt` permission.
@ -25,8 +25,9 @@ There are three ways this can be solved:
1. Make the player (or anyone else) place an empty sign in the same location and break it again. The player should be
refunded as long as refunds are enabled.
2. Reload PaidSigns. The player should be refunded as long as refunds are enabled.
3. Restart the server. The player should be refunded as long as refunds are enabled. Never place a new paid sign in this
situation, as the old sign will be overwritten, and the ability to refund the player automatically will be lost.
3. Restart the server. The player should be refunded as long as refunds are enabled. Never place a new paid sign in the
same location in this situation, as the old sign will be overwritten, and the ability to refund the player
automatically will be lost.
### Players have to pay for signs, even if the plugin sign isn't created
@ -44,40 +45,46 @@ This plugin is known to be helpful with two tasks:
2. Add a swearing jar/swearing filter to the server. This can be done with a paid sign matching ANY condition, where the
list of swear words is given as a regEx. The same regEx should be added for each of the four sign lines.
## Custom parser
Before anything else, it's important to note that as an experiment, I used a custom command-parser for this plugin. This
means that sign names can contain spaces, as long as the name is in quotes. In most cases, this actually works fine,
even though it differs a lot from the default behavior, but if you tab-complete the first word in the name first, and
then try to tab-complete the rest, tab-completion will fill in the second world with the entire name, which is
unfortunate. That's really just how tab-completion works, and not something I think I can do anything with.
## Commands
An argument marked by "<>" is required to execute the command. An argument marked by "[]" is optional. For empty
arguments, such as no paid sign permission, you should use empty quotes ("").
| Command | Arguments | Permission | Description |
| -------- | ------ | ------- | ------- |
| /paidsigns | | paidsigns.info | Used to display in-game information about all other commands |
| [/addpaidsign](#addpaidsign) | \<name> \<cost> \[permission] \[ignore case] \[ignore color] \[match any condition] | paidsigns.manage | Used to add a new paid sign |
| [/addpaidsigncondition](#addpaidsigncondition) | \<name (of a paid sign)> \<line number> \<string to match> \[executeRegEx] \[ignoreCase] \[ignoreColor] | paidsigns.manage | Used to add a condition to a paid sign |
| [/listpaidsigns](#listpaidsigns) | \[name (of a paid sign)] \[line number] | paidsigns.manage | Used to list registered paid signs or a registered paid sign's conditions |
| [/editpaidsign](#editpaidsign) | \<sign name> \<property>/\<line number> \[new value]/\<property> \[new value] | paidsigns.manage | Used to modify a registered paid sign or one of its conditions |
| [/removepaidsigncondition](#removepaidsigncondition) | \<name (of a paid sign)> \<line number> | paidsigns.manage | Used to remove a condition from a registered paid sign |
| [/removepaidsign](#removepaidsign) | \<name (of a paid sign)> | paidsigns.manage | Used to remove a registered paid sign |
| /reload | | paidsigns.reload | Used to reload the configuration file |
| Command | Arguments | Permission | Description |
|------------------------------------------------------|---------------------------------------------------------------------------------------------------------|------------------|---------------------------------------------------------------------------|
| /paidsigns | | paidsigns.info | Used to display in-game information about all other commands |
| [/addpaidsign](#addpaidsign) | \<name> \<cost> \[permission] \[ignore case] \[ignore color] \[match any condition] | paidsigns.manage | Used to add a new paid sign |
| [/addpaidsigncondition](#addpaidsigncondition) | \<name (of a paid sign)> \<line number> \<string to match> \[executeRegEx] \[ignoreCase] \[ignoreColor] | paidsigns.manage | Used to add a condition to a paid sign |
| [/listpaidsigns](#listpaidsigns) | \[name (of a paid sign)] \[line number] | paidsigns.manage | Used to list registered paid signs or a registered paid sign's conditions |
| [/editpaidsign](#editpaidsign) | \<sign name> \<property>/\<line number> \[new value]/\<property> \[new value] | paidsigns.manage | Used to modify a registered paid sign or one of its conditions |
| [/removepaidsigncondition](#removepaidsigncondition) | \<name (of a paid sign)> \<line number> | paidsigns.manage | Used to remove a condition from a registered paid sign |
| [/removepaidsign](#removepaidsign) | \<name (of a paid sign)> | paidsigns.manage | Used to remove a registered paid sign |
| /reload | | paidsigns.reload | Used to reload the configuration file |
## Command explanation
### /paidsigns
### /addpaidsign
This command adds a new paid sign that does nothing until a condition is added.
`/addpaidsign <name> <cost> [permission] [ignore case] [ignore color] [match any condition]`
| Argument | Usage |
| ----- | ----- |
|name | A recognizable name only used to differentiate between registered paid signs |
|cost | The cost a player needs to pay to create any sign matching the paid sign |
|permission | If the paid sign is used to represent a plugin sign, the permission should be the permission necessary for creating the plugin sign. This is used to decide if the plugin sign was created, or if the player was denied. |
|ignore case | Whether any conditions of the paid sign should ignore case by default, when matching against text (default uses the config file value). |
|ignore color | Whether any condition of the paid sign should ignore color by default, when matching against text (default uses the config file value). |
|match any condition | Whether to trigger a paid sign match if a single one of the sign's conditions is true. This is mainly useful if several lines may contain the match string, or if trying to match a word. |
| Argument | Usage |
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | A recognizable name only used to differentiate between registered paid signs |
| cost | The cost a player needs to pay to create any sign matching the paid sign |
| permission | If the paid sign is used to represent a plugin sign, the permission should be the permission necessary for creating the plugin sign. This is used to decide if the plugin sign was created, or if the player was denied. |
| ignore case | Whether any conditions of the paid sign should ignore case by default, when matching against text (default uses the config file value). |
| ignore color | Whether any condition of the paid sign should ignore color by default, when matching against text (default uses the config file value). |
| match any condition | Whether to trigger a paid sign match if a single one of the sign's conditions is true. This is mainly useful if several lines may contain the match string, or if trying to match a word. |
### /addpaidsigncondition
@ -86,25 +93,26 @@ a paid sign condition to a line that already has one will replace the previous c
`/addpaidsigncondition <name (of a paid sign)> <line number> <string to match> [executeRegEx] [ignoreCase] [ignoreColor]`
| Argument | Usage |
| ----- | ----- |
| name | The name of the paid sign to add the condition to |
| line number | The line on the sign (1-4) to search for any matches |
| string to match | The string or regular expression to look for on a sign |
| executeRegEx | Whether to use a regular expression match instead of looking for the exact string |
| ignoreCase | Whether this condition should ignore case when trying to match the string (default uses the "parent" sign's value) |
| ignoreColor | Whether this condition should ignore color when trying to match the string (default uses the "parent" sign's value) |
| Argument | Usage |
|-----------------|---------------------------------------------------------------------------------------------------------------------|
| name | The name of the paid sign to add the condition to |
| line number | The line on the sign (1-4) to search for any matches |
| string to match | The string or regular expression to look for on a sign |
| executeRegEx | Whether to use a regular expression match instead of looking for the exact string |
| ignoreCase | Whether this condition should ignore case when trying to match the string (default uses the "parent" sign's value) |
| ignoreColor | Whether this condition should ignore color when trying to match the string (default uses the "parent" sign's value) |
### /listpaidsigns
This lists registered paid signs and paid sign conditions. No arguments will print a list of paid signs
`/listpaidsigns [name (of a paid sign)] [line number]`
`/listpaidsigns [page number]/[name (of a paid sign)] [line number]`
| Argument | Usage |
| ----- | ----- |
| name | The name of the paid sign to see information about |
| line number | The line number of the condition to see information about |
| Argument | Usage |
|-------------|---------------------------------------------------------------------------------|
| page number | Paid signs are listed 7 at a time, so the page number is used to see the next 7 |
| name | The name of the paid sign to see information about |
| line number | The line number of the condition to see information about |
### /editpaidsign
@ -112,12 +120,12 @@ This command changes a property of a paid sign or a paid sign condition
`/editpaidsign <sign name> <property>/<line number> [new value]/<property> [new value]`
| Argument | Usage |
| ----- | ----- |
| name | The name of the paid sign to edit |
| property / line number | The property to edit for the sign (name, cost, permission, ignoreCase, ignoreColor, matchAnyCondition), or the line of the condition to edit |
| new value / property | The new property value if a property was specified in the second argument, or a condition property (stringToMatch, executeRegEx, ignoreCase, ignoreColor) if a line number was specified in the second argument |
| new value | The new property value of the condition property specified in the third argument |
| Argument | Usage |
|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| name | The name of the paid sign to edit |
| property / line number | The property to edit for the sign (name, cost, permission, ignoreCase, ignoreColor, matchAnyCondition), or the line of the condition to edit |
| new value / property | The new property value if a property was specified in the second argument, or a condition property (stringToMatch, executeRegEx, ignoreCase, ignoreColor) if a line number was specified in the second argument |
| new value | The new property value of the condition property specified in the third argument |
### /removepaidsigncondition
@ -125,10 +133,10 @@ Removes a paid sign condition from a sign
`/removepaidsigncondition <name (of a paid sign)> <line number>`
| Argument | Usage |
| ----- | ----- |
| name | The name of the paid sign to remove the condition from |
| line number | The line the condition is associated with |
| Argument | Usage |
|-------------|--------------------------------------------------------|
| name | The name of the paid sign to remove the condition from |
| line number | The line the condition is associated with |
### /removepaidsign
@ -136,36 +144,38 @@ Removes a registered paid sign
`/removepaidsign <name (of a paid sign)>`
| Argument | Usage |
| ----- | ----- |
| name | The name of the paid sign to remove |
| Argument | Usage |
|----------|-------------------------------------|
| name | The name of the paid sign to remove |
## Permissions
| Node | Description |
| ------- | ------- |
| paidsigns.* | Grants all paid signs permissions |
| --paidsigns.manage | Grants the permission to add/remove a paid sign |
| --paidsigns.reload | Grants the permissions to reload the plugin |
| Node | Description |
|---------------------------|------------------------------------------------------|
| paidsigns.* | Grants all paid signs permissions |
| --paidsigns.manage | Grants the permission to add/remove a paid sign |
| --paidsigns.reload | Grants the permissions to reload the plugin |
| --paidsigns.paymentexempt | Makes this player exempt from the cost of paid signs |
## Configuration options
| Option | Description |
| ------- | ------- |
| language | The language to use for all messages displayed to players. Currently, only "en" is valid. |
| ignoreCase | Whether to ignore the case (lowercase/uppercase) of the paid sign text. The option can be set on a per-sign basis, but this value is used if not specified. The correct value depends on whether the plugin signs it should match are case-sensitive or not. |
| ignoreColor | Whether to ignore any color or formatting applied to the text when trying to match a paid sign's text. The option can be set on a per-sign basis, but this value is used if not specified. The correct value depends on whether the plugin signs it should match allow coloring or not. |
| refundsEnabled | Whether to enable refunds to the sign creator when a sign detected as a paid sign is broken (payment will always go to the original creator) |
| refundPercentage | The percentage of the paid sign cost to refund (0-100) |
| refundAlways | Whether to refund when signs that players have paid for are broken by anything. This includes tnt, creepers, pistons and similar |
| Option | Description |
|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| language | The language to use for all messages displayed to players. Currently, only "en" is valid. |
| ignoreCase | Whether to ignore the case (lowercase/uppercase) of the paid sign text. The option can be set on a per-sign basis, but this value is used if not specified. The correct value depends on whether the plugin signs it should match are case-sensitive or not. |
| ignoreColor | Whether to ignore any color or formatting applied to the text when trying to match a paid sign's text. The option can be set on a per-sign basis, but this value is used if not specified. The correct value depends on whether the plugin signs it should match allow coloring or not. |
| refundsEnabled | Whether to enable refunds to the sign creator when a sign detected as a paid sign is broken (payment will always go to the original creator) |
| refundPercentage | The percentage of the paid sign cost to refund (0-100) |
| refundAlways | Whether to refund when signs that players have paid for are broken by anything. This includes tnt, creepers, pistons and similar |
## Language customization
All strings, even time units, are customizable. If you place a strings.yml file in the plugin folder, it will take
priority over built-in languages. If you want to change strings, look at PaidSigns/src/main/resources/strings.yml for
the proper keys. All strings have the format: ENUM: "Displayed string". The enum must be identical as it defines which
string you have changed. All strings belonging to a language are beneath the language code and indented with two spaces.
priority over built-in languages. If you want to change strings, look
at [PaidSigns/src/main/resources/strings.yml](https://git.knarcraft.net/EpicKnarvik97/PaidSigns/src/branch/master/src/main/resources/strings.yml)
for the proper keys. All strings have the format: ENUM: "Displayed string". The enum must be identical as it defines
which string you have changed. All strings belonging to a language are beneath the language code and indented with two
spaces.
The easiest way to add a new language is to copy an existing language and paste it into your custom strings.yml and
change strings as necessary. If you don't include all strings, the remaining will use the built-in English translation.

68
pom.xml
View File

@ -6,14 +6,14 @@
<groupId>net.knarcraft</groupId>
<artifactId>paidsigns</artifactId>
<version>1.0.0</version>
<version>1.0.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Paid Signs</name>
<description>Add costs for creating plugin signs</description>
<properties>
<java.version>17</java.version>
<java.version>16</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<url>https://git.knarcraft.net/EpicKnarvik97/PaidSigns</url>
@ -32,7 +32,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
@ -41,6 +41,36 @@
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>net.knarcraft.knarlib</pattern>
<shadedPattern>net.knarcraft.paidsigns.lib.knarlib</shadedPattern>
</relocation>
<relocation>
<pattern>org.jetbrains.annotations</pattern>
<shadedPattern>net.knarcraft.paidsigns.lib.annotations</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>net.knarcraft:knarlib</artifact>
<includes>
<include>net/knarcraft/knarlib/**</include>
</includes>
</filter>
<filter>
<artifact>org.jetbrains:annotations</artifact>
<includes>
<include>org/jetbrains/annotations/**</include>
</includes>
</filter>
<filter>
<excludes>
<exclude>*.MF</exclude>
<exclude>*.yml</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
@ -55,6 +85,10 @@
</build>
<repositories>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
@ -64,29 +98,45 @@
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</repository>
<snapshotRepository>
<id>knarcraft-repo</id>
<url>https://git.knarcraft.net/api/packages/EpicKnarvik97/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.19.2-R0.1-SNAPSHOT</version>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<version>24.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,5 +1,8 @@
package net.knarcraft.paidsigns;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.knarlib.formatting.Translator;
import net.knarcraft.knarlib.util.UpdateChecker;
import net.knarcraft.paidsigns.command.AddCommand;
import net.knarcraft.paidsigns.command.AddConditionCommand;
import net.knarcraft.paidsigns.command.AddConditionTabCompleter;
@ -13,7 +16,7 @@ import net.knarcraft.paidsigns.command.ReloadTabCommand;
import net.knarcraft.paidsigns.command.RemoveConditionCommand;
import net.knarcraft.paidsigns.command.RemoveConditionTabCompleter;
import net.knarcraft.paidsigns.command.RemoveTabCommand;
import net.knarcraft.paidsigns.formatting.Translator;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.listener.SignBreakListener;
import net.knarcraft.paidsigns.listener.SignListener;
import net.knarcraft.paidsigns.manager.EconomyManager;
@ -36,6 +39,8 @@ import org.bukkit.plugin.java.JavaPlugin;
public final class PaidSigns extends JavaPlugin {
private static PaidSigns paidSigns;
private static Translator translator;
private static StringFormatter stringFormatter;
private PaidSignManager signManager;
private String language;
private boolean ignoreCase;
@ -61,11 +66,35 @@ public final class PaidSigns extends JavaPlugin {
return paidSigns;
}
/**
* Gets the translator to use for this plugin
*
* @return <p>The translator to use for this plugin</p>
*/
public static Translator getTranslator() {
return translator;
}
/**
* Gets the string formatter to use for this plugin
*
* @return <p>The string formatter to use for this plugin</p>
*/
public static StringFormatter getStringFormatter() {
return stringFormatter;
}
@Override
public void onEnable() {
setupVault();
loadConfig();
Translator.loadLanguages(language);
//Initialize translator
translator = new Translator();
translator.registerMessageCategory(PaidSignsTranslatableMessage.BOOLEAN_TRUE);
translator.loadLanguages(this.getDataFolder(), "en", language);
stringFormatter = new StringFormatter(this.getDescription().getPrefix(), translator);
signManager = new PaidSignManager(PaidSignManager.loadSigns());
TrackedSignManager.loadTrackedSigns();
@ -74,6 +103,9 @@ public final class PaidSigns extends JavaPlugin {
pluginManager.registerEvents(new SignBreakListener(), this);
registerCommands();
UpdateChecker.checkForUpdate(this, "https://api.spigotmc.org/legacy/update.php?resource=105842",
() -> this.getDescription().getVersion(), null);
}
@Override
@ -86,7 +118,7 @@ public final class PaidSigns extends JavaPlugin {
public void reload() {
this.reloadConfig();
loadConfig();
Translator.loadLanguages(language);
translator.loadLanguages(this.getDataFolder(), "en", language);
signManager = new PaidSignManager(PaidSignManager.loadSigns());
TrackedSignManager.loadTrackedSigns();
}

View File

@ -2,8 +2,7 @@ package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.manager.PaidSignManager;
import net.knarcraft.paidsigns.property.OptionState;
import org.bukkit.command.Command;
@ -30,7 +29,7 @@ public class AddCommand extends TokenizedCommand {
try {
cost = Double.parseDouble(arguments.get(1));
} catch (NumberFormatException exception) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
String permission = "";
@ -71,18 +70,18 @@ public class AddCommand extends TokenizedCommand {
try {
PaidSign sign = new PaidSign(signName, cost, permission, ignoreCase, ignoreColor, matchAnyCondition);
if (manager.getPaidSign(signName) != null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_NAME_DUPLICATE));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_NAME_DUPLICATE);
return false;
}
manager.addPaidSign(sign);
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.SUCCESS_ADDED_PAID_SIGN));
PaidSigns.getStringFormatter().displaySuccessMessage(sender, PaidSignsTranslatableMessage.SUCCESS_ADDED_PAID_SIGN);
return true;
} catch (IOException e) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_EXCEPTION_OCCURRED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_EXCEPTION_OCCURRED);
return false;
} catch (IllegalArgumentException e) {
sender.sendMessage(StringFormatter.replacePlaceholder(StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_INVALID_INPUT), "{input}", e.getMessage()));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSigns.getStringFormatter().replacePlaceholder(
PaidSignsTranslatableMessage.ERROR_INVALID_INPUT, "{input}", e.getMessage()));
}
return false;
}

View File

@ -2,8 +2,7 @@ package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.manager.PaidSignManager;
import net.knarcraft.paidsigns.property.OptionState;
import org.bukkit.command.Command;
@ -30,11 +29,11 @@ public class AddConditionCommand extends TokenizedCommand {
try {
lineNumber = (short) (Short.parseShort(arguments.get(1)) - 1);
if (lineNumber < 0 || lineNumber > 3) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
} catch (NumberFormatException exception) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
String stringToMatch = arguments.get(2);
@ -73,18 +72,17 @@ public class AddConditionCommand extends TokenizedCommand {
PaidSignManager signManager = PaidSigns.getInstance().getSignManager();
PaidSign sign = signManager.getPaidSign(name);
if (sign == null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND);
return false;
}
sign.addCondition(lineNumber, stringToMatch, executeRegEx, ignoreCase, ignoreColor);
try {
signManager.saveSigns();
} catch (IOException e) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_EXCEPTION_OCCURRED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_EXCEPTION_OCCURRED);
return false;
}
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(
TranslatableMessage.SUCCESS_ADDED_PAID_SIGN_CONDITION));
PaidSigns.getStringFormatter().displaySuccessMessage(sender, PaidSignsTranslatableMessage.SUCCESS_ADDED_PAID_SIGN_CONDITION);
return true;
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.utility.TabCompleteHelper;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.paidsigns.utility.PaidSignsTabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -28,17 +29,17 @@ public class AddConditionTabCompleter extends TokenizedTabCompleter {
initializeValues();
}
if (argumentSize == 1) {
return TabCompleteHelper.filterMatchingStartsWith(TabCompleteHelper.getPaidSignNames(), arguments.get(0));
return TabCompletionHelper.filterMatchingStartsWith(PaidSignsTabCompleteHelper.getPaidSignNames(), arguments.get(0));
} else if (argumentSize == 2) {
return TabCompleteHelper.filterMatchingStartsWith(this.lineIndices, arguments.get(1));
return TabCompletionHelper.filterMatchingStartsWith(this.lineIndices, arguments.get(1));
} else if (argumentSize == 3) {
return TabCompleteHelper.filterMatchingStartsWith(stringsToMatch, arguments.get(2));
return TabCompletionHelper.filterMatchingStartsWith(stringsToMatch, arguments.get(2));
} else if (argumentSize == 4) {
return TabCompleteHelper.filterMatchingStartsWith(booleans, arguments.get(3));
return TabCompletionHelper.filterMatchingStartsWith(booleans, arguments.get(3));
} else if (argumentSize == 5) {
return TabCompleteHelper.filterMatchingStartsWith(optionStates, arguments.get(4));
return TabCompletionHelper.filterMatchingStartsWith(optionStates, arguments.get(4));
} else if (argumentSize == 6) {
return TabCompleteHelper.filterMatchingStartsWith(optionStates, arguments.get(5));
return TabCompletionHelper.filterMatchingStartsWith(optionStates, arguments.get(5));
}
return new ArrayList<>();
}
@ -47,10 +48,10 @@ public class AddConditionTabCompleter extends TokenizedTabCompleter {
* Initializes the values available for tab completion
*/
private void initializeValues() {
lineIndices = TabCompleteHelper.getSignLines();
stringsToMatch = TabCompleteHelper.getExampleConditionStrings();
booleans = TabCompleteHelper.getBooleans();
optionStates = TabCompleteHelper.getOptionStates();
lineIndices = PaidSignsTabCompleteHelper.getSignLines();
stringsToMatch = PaidSignsTabCompleteHelper.getExampleConditionStrings();
booleans = PaidSignsTabCompleteHelper.getBooleans();
optionStates = PaidSignsTabCompleteHelper.getOptionStates();
}
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.utility.TabCompleteHelper;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.paidsigns.utility.PaidSignsTabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -29,17 +30,17 @@ public class AddTabCompleter extends TokenizedTabCompleter {
}
if (argumentSize == 1) {
return TabCompleteHelper.filterMatchingStartsWith(names, arguments.get(0));
return TabCompletionHelper.filterMatchingStartsWith(names, arguments.get(0));
} else if (argumentSize == 2) {
return TabCompleteHelper.filterMatchingStartsWith(costs, arguments.get(1));
return TabCompletionHelper.filterMatchingStartsWith(costs, arguments.get(1));
} else if (argumentSize == 3) {
return TabCompleteHelper.tabCompletePermission(arguments.get(arguments.size() - 1));
return PaidSignsTabCompleteHelper.tabCompletePermission(arguments.get(arguments.size() - 1));
} else if (argumentSize == 4) {
return TabCompleteHelper.filterMatchingStartsWith(options, arguments.get(3));
return TabCompletionHelper.filterMatchingStartsWith(options, arguments.get(3));
} else if (argumentSize == 5) {
return TabCompleteHelper.filterMatchingStartsWith(options, arguments.get(4));
return TabCompletionHelper.filterMatchingStartsWith(options, arguments.get(4));
} else if (argumentSize == 6) {
return TabCompleteHelper.filterMatchingStartsWith(booleans, arguments.get(5));
return TabCompletionHelper.filterMatchingStartsWith(booleans, arguments.get(5));
}
return new ArrayList<>();
}
@ -48,10 +49,10 @@ public class AddTabCompleter extends TokenizedTabCompleter {
* Initializes the values available for tab completion
*/
private static void initializeValues() {
names = TabCompleteHelper.getExamplePaidSignNames();
costs = TabCompleteHelper.getCosts();
options = TabCompleteHelper.getOptionStates();
booleans = TabCompleteHelper.getBooleans();
names = PaidSignsTabCompleteHelper.getExamplePaidSignNames();
costs = PaidSignsTabCompleteHelper.getCosts();
options = PaidSignsTabCompleteHelper.getOptionStates();
booleans = PaidSignsTabCompleteHelper.getBooleans();
}
}

View File

@ -3,8 +3,7 @@ package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.container.PaidSignCondition;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.manager.PaidSignManager;
import net.knarcraft.paidsigns.property.OptionState;
import net.knarcraft.paidsigns.property.PaidSignConditionProperty;
@ -31,7 +30,7 @@ public class EditCommand extends TokenizedCommand {
PaidSign sign = PaidSigns.getInstance().getSignManager().getPaidSign(arguments.get(0));
if (sign == null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND);
return false;
}
@ -44,7 +43,7 @@ public class EditCommand extends TokenizedCommand {
return parseGivenProperty(sign, sender);
}
} catch (IOException e) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_EXCEPTION_OCCURRED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_EXCEPTION_OCCURRED);
return false;
}
}
@ -61,7 +60,7 @@ public class EditCommand extends TokenizedCommand {
@NotNull CommandSender sender) throws NumberFormatException {
short signLine = (short) (Short.parseShort(arguments.get(1)) - 1);
if (signLine < 0 || signLine > 3 || sign.getConditions().get(signLine) == null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_NO_SUCH_CONDITION));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_NO_SUCH_CONDITION);
return false;
}
if (argumentSize < 4) {
@ -70,8 +69,7 @@ public class EditCommand extends TokenizedCommand {
PaidSignConditionProperty conditionProperty = PaidSignConditionProperty.getFromString(arguments.get(2));
if (conditionProperty == null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_PROPERTY_NOT_RECOGNIZED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_PROPERTY_NOT_RECOGNIZED);
return false;
}
String value = arguments.get(3);
@ -90,8 +88,7 @@ public class EditCommand extends TokenizedCommand {
@NotNull CommandSender sender) throws IOException {
PaidSignProperty property = PaidSignProperty.getFromString(arguments.get(1));
if (property == null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_PROPERTY_NOT_RECOGNIZED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_PROPERTY_NOT_RECOGNIZED);
return false;
}
String value = arguments.get(2);
@ -99,7 +96,7 @@ public class EditCommand extends TokenizedCommand {
updateProperty(sender, sign, property, value);
return true;
} catch (NumberFormatException exception) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
}
@ -137,7 +134,7 @@ public class EditCommand extends TokenizedCommand {
manager.removePaidSign(sign.getName());
manager.addPaidSign(updatedSign);
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.SUCCESS_UPDATED_PAID_SIGN));
PaidSigns.getStringFormatter().displaySuccessMessage(sender, PaidSignsTranslatableMessage.SUCCESS_UPDATED_PAID_SIGN);
}
/**
@ -173,12 +170,11 @@ public class EditCommand extends TokenizedCommand {
try {
PaidSigns.getInstance().getSignManager().saveSigns();
} catch (IOException e) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_EXCEPTION_OCCURRED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_EXCEPTION_OCCURRED);
return false;
}
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(
TranslatableMessage.SUCCESS_UPDATED_PAID_SIGN_CONDITION));
PaidSigns.getStringFormatter().displaySuccessMessage(sender, PaidSignsTranslatableMessage.SUCCESS_UPDATED_PAID_SIGN_CONDITION);
return true;
}

View File

@ -1,10 +1,11 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.property.PaidSignConditionProperty;
import net.knarcraft.paidsigns.property.PaidSignProperty;
import net.knarcraft.paidsigns.utility.TabCompleteHelper;
import net.knarcraft.paidsigns.utility.PaidSignsTabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -34,7 +35,7 @@ public class EditTabCompleter extends TokenizedTabCompleter {
}
if (argumentSize == 1) {
return TabCompleteHelper.filterMatchingStartsWith(TabCompleteHelper.getPaidSignNames(), arguments.get(0));
return TabCompletionHelper.filterMatchingStartsWith(PaidSignsTabCompleteHelper.getPaidSignNames(), arguments.get(0));
} else if (argumentSize >= 2) {
PaidSign sign = PaidSigns.getInstance().getSignManager().getPaidSign(arguments.get(0));
if (sign != null) {
@ -54,7 +55,7 @@ public class EditTabCompleter extends TokenizedTabCompleter {
if (argumentSize == 2) {
List<String> conditions = getAvailableSignConditions(sign);
conditions.addAll(getPaidSignProperties());
return TabCompleteHelper.filterMatchingStartsWith(conditions, arguments.get(1));
return TabCompletionHelper.filterMatchingStartsWith(conditions, arguments.get(1));
} else if (argumentSize >= 3) {
try {
return tabCompleteSignLine(sign);
@ -79,11 +80,11 @@ public class EditTabCompleter extends TokenizedTabCompleter {
if (signLine < 0 || signLine > 3 || sign.getConditions().get(signLine) == null) {
return new ArrayList<>();
} else if (argumentSize == 3) {
return TabCompleteHelper.filterMatchingStartsWith(getPaidSignConditionProperties(), arguments.get(2));
return TabCompletionHelper.filterMatchingStartsWith(getPaidSignConditionProperties(), arguments.get(2));
} else if (argumentSize == 4) {
PaidSignConditionProperty property = PaidSignConditionProperty.getFromString(arguments.get(2));
if (property != null) {
return TabCompleteHelper.filterMatchingStartsWith(conditionPropertyExampleValues.get(property),
return TabCompletionHelper.filterMatchingStartsWith(conditionPropertyExampleValues.get(property),
arguments.get(3));
}
}
@ -99,9 +100,9 @@ public class EditTabCompleter extends TokenizedTabCompleter {
PaidSignProperty paidSignProperty = PaidSignProperty.getFromString(arguments.get(1));
if (paidSignProperty != null) {
if (paidSignProperty == PaidSignProperty.PERMISSION) {
return TabCompleteHelper.tabCompletePermission(arguments.get(2));
return PaidSignsTabCompleteHelper.tabCompletePermission(arguments.get(2));
} else {
return TabCompleteHelper.filterMatchingStartsWith(propertyExampleValues.get(paidSignProperty),
return TabCompletionHelper.filterMatchingStartsWith(propertyExampleValues.get(paidSignProperty),
arguments.get(2));
}
} else {
@ -154,11 +155,11 @@ public class EditTabCompleter extends TokenizedTabCompleter {
*/
private void initializePropertyExampleValues() {
propertyExampleValues = new HashMap<>();
propertyExampleValues.put(PaidSignProperty.COST, TabCompleteHelper.getCosts());
propertyExampleValues.put(PaidSignProperty.NAME, TabCompleteHelper.getPaidSignNames());
propertyExampleValues.put(PaidSignProperty.IGNORE_CASE, TabCompleteHelper.getOptionStates());
propertyExampleValues.put(PaidSignProperty.IGNORE_COLOR, TabCompleteHelper.getOptionStates());
propertyExampleValues.put(PaidSignProperty.MATCH_ANY_CONDITION, TabCompleteHelper.getBooleans());
propertyExampleValues.put(PaidSignProperty.COST, PaidSignsTabCompleteHelper.getCosts());
propertyExampleValues.put(PaidSignProperty.NAME, PaidSignsTabCompleteHelper.getPaidSignNames());
propertyExampleValues.put(PaidSignProperty.IGNORE_CASE, PaidSignsTabCompleteHelper.getOptionStates());
propertyExampleValues.put(PaidSignProperty.IGNORE_COLOR, PaidSignsTabCompleteHelper.getOptionStates());
propertyExampleValues.put(PaidSignProperty.MATCH_ANY_CONDITION, PaidSignsTabCompleteHelper.getBooleans());
}
/**
@ -167,10 +168,10 @@ public class EditTabCompleter extends TokenizedTabCompleter {
private void initializeConditionPropertyExampleValues() {
conditionPropertyExampleValues = new HashMap<>();
conditionPropertyExampleValues.put(PaidSignConditionProperty.STRING_TO_MATCH,
TabCompleteHelper.getExampleConditionStrings());
conditionPropertyExampleValues.put(PaidSignConditionProperty.IGNORE_COLOR, TabCompleteHelper.getOptionStates());
conditionPropertyExampleValues.put(PaidSignConditionProperty.IGNORE_CASE, TabCompleteHelper.getOptionStates());
conditionPropertyExampleValues.put(PaidSignConditionProperty.EXECUTE_REG_EX, TabCompleteHelper.getBooleans());
PaidSignsTabCompleteHelper.getExampleConditionStrings());
conditionPropertyExampleValues.put(PaidSignConditionProperty.IGNORE_COLOR, PaidSignsTabCompleteHelper.getOptionStates());
conditionPropertyExampleValues.put(PaidSignConditionProperty.IGNORE_CASE, PaidSignsTabCompleteHelper.getOptionStates());
conditionPropertyExampleValues.put(PaidSignConditionProperty.EXECUTE_REG_EX, PaidSignsTabCompleteHelper.getBooleans());
}
}

View File

@ -1,11 +1,10 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.knarlib.formatting.StringFormatter;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.container.PaidSignCondition;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.Translator;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -16,10 +15,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static net.knarcraft.paidsigns.formatting.StringFormatter.getTranslatedErrorMessage;
import static net.knarcraft.paidsigns.formatting.StringFormatter.replacePlaceholder;
import static net.knarcraft.paidsigns.formatting.StringFormatter.replacePlaceholders;
import static net.knarcraft.paidsigns.formatting.StringFormatter.translateBoolean;
import static net.knarcraft.knarlib.formatting.StringFormatter.replacePlaceholder;
import static net.knarcraft.knarlib.formatting.StringFormatter.replacePlaceholders;
/**
* A representation of the command for listing information about paid signs
@ -56,7 +53,7 @@ public class ListCommand extends TokenizedCommand {
Set<String> signNames = PaidSigns.getInstance().getSignManager().getAllPaidSigns().keySet();
List<String> signNameList = new ArrayList<>(signNames);
Collections.sort(signNameList);
String infoFormat = Translator.getTranslatedMessage(TranslatableMessage.PAID_SIGNS_INFO_FORMAT);
String infoFormat = PaidSigns.getTranslator().getTranslatedMessage(PaidSignsTranslatableMessage.PAID_SIGNS_INFO_FORMAT);
//Display up to 10 signs per page
int signsPerPage = 7;
@ -66,19 +63,19 @@ public class ListCommand extends TokenizedCommand {
break;
}
String signName = signNameList.get(signIndex);
signs.append(StringFormatter.replacePlaceholder(infoFormat, "{name}", signName));
signs.append(replacePlaceholder(infoFormat, "{name}", signName));
}
//Display that another page exists, if necessary
boolean hasNextPage = alreadyDisplayed + signsPerPage < signNameList.size();
String nextPagePrompt = "";
if (hasNextPage) {
nextPagePrompt = StringFormatter.replacePlaceholder(Translator.getTranslatedMessage(
TranslatableMessage.PAID_SIGNS_NEXT_PROMPT), "{nextPage}", String.valueOf((pageNumber + 2)));
nextPagePrompt = replacePlaceholder(PaidSigns.getTranslator().getTranslatedMessage(
PaidSignsTranslatableMessage.PAID_SIGNS_NEXT_PROMPT), "{nextPage}", String.valueOf((pageNumber + 2)));
}
sender.sendMessage(StringFormatter.replacePlaceholders(Translator.getTranslatedMessage(
TranslatableMessage.PAID_SIGNS_INFO), new String[]{"{signs}", "{nextPagePrompt}"},
sender.sendMessage(StringFormatter.replacePlaceholders(PaidSigns.getTranslator().getTranslatedMessage(
PaidSignsTranslatableMessage.PAID_SIGNS_INFO), new String[]{"{signs}", "{nextPagePrompt}"},
new String[]{signs.toString(), nextPagePrompt}));
}
@ -91,7 +88,8 @@ public class ListCommand extends TokenizedCommand {
private boolean parsePaidSignSelection(CommandSender sender) {
PaidSign paidSign = PaidSigns.getInstance().getSignManager().getPaidSign(arguments.get(0));
if (paidSign == null) {
sender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSignsTranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND);
return false;
}
if (argumentSize < 2) {
@ -101,16 +99,20 @@ public class ListCommand extends TokenizedCommand {
try {
lineNumber = (short) (Short.parseShort(arguments.get(1)) - 1);
if (lineNumber < 0 || lineNumber > 3) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
} catch (NumberFormatException exception) {
sender.sendMessage(getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
if (!paidSign.getConditions().containsKey(lineNumber)) {
sender.sendMessage(replacePlaceholder(getTranslatedErrorMessage(
TranslatableMessage.ERROR_NO_SUCH_CONDITION), "{line}", String.valueOf(lineNumber)));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSigns.getStringFormatter().replacePlaceholder(
PaidSignsTranslatableMessage.ERROR_NO_SUCH_CONDITION, "{line}",
String.valueOf(lineNumber)));
return false;
}
PaidSignCondition condition = paidSign.getConditions().get(lineNumber);
@ -129,11 +131,11 @@ public class ListCommand extends TokenizedCommand {
*/
private void displayPaidSignCondition(CommandSender sender, String signName, short signLine,
PaidSignCondition condition) {
sender.sendMessage(StringFormatter.replacePlaceholders(Translator.getTranslatedMessage(
TranslatableMessage.PAID_SIGN_CONDITION_INFO), new String[]{"{name}", "{line}", "{match}", "{regex}",
"{case}", "{color}"}, new String[]{signName, String.valueOf(signLine + 1), condition.stringToMatch(),
translateBoolean(condition.executeRegex()), translateBoolean(condition.ignoreCase()),
translateBoolean(condition.ignoreColor())}));
sender.sendMessage(PaidSigns.getStringFormatter().replacePlaceholders(
PaidSignsTranslatableMessage.PAID_SIGN_CONDITION_INFO, new String[]{"{name}", "{line}", "{match}",
"{regex}", "{case}", "{color}"}, new String[]{signName, String.valueOf(signLine + 1),
condition.stringToMatch(), translateBoolean(condition.executeRegex()),
translateBoolean(condition.ignoreCase()), translateBoolean(condition.ignoreColor())}));
}
/**
@ -146,17 +148,32 @@ public class ListCommand extends TokenizedCommand {
Map<Short, PaidSignCondition> signConditions = paidSign.getConditions();
StringBuilder conditions = new StringBuilder();
for (short lineIndex : signConditions.keySet()) {
String format = Translator.getTranslatedMessage(TranslatableMessage.PAID_SIGN_INFO_CONDITION_FORMAT);
String format = PaidSigns.getTranslator().getTranslatedMessage(
PaidSignsTranslatableMessage.PAID_SIGN_INFO_CONDITION_FORMAT);
conditions.append(StringFormatter.replacePlaceholders(format, new String[]{"{line}", "{condition}"},
new String[]{String.valueOf((lineIndex + 1)), signConditions.get(lineIndex).stringToMatch()}));
}
sender.sendMessage(replacePlaceholders(Translator.getTranslatedMessage(
TranslatableMessage.PAID_SIGN_INFO), new String[]{"{name}", "{cost}", "{permission}", "{case}",
sender.sendMessage(replacePlaceholders(PaidSigns.getTranslator().getTranslatedMessage(
PaidSignsTranslatableMessage.PAID_SIGN_INFO), new String[]{"{name}", "{cost}", "{permission}", "{case}",
"{color}", "{any}", "{conditions}"}, new String[]{paidSign.getName(), String.valueOf(paidSign.getCost()),
paidSign.getPermission(), translateBoolean(paidSign.getIgnoreCase()),
translateBoolean(paidSign.getIgnoreColor()), translateBoolean(paidSign.matchAnyCondition()),
conditions.toString()}));
}
/**
* Translates the given boolean value
*
* @param booleanValue <p>The boolean value to translate</p>
* @return <p>The translation of the boolean value</p>
*/
public static String translateBoolean(boolean booleanValue) {
if (booleanValue) {
return PaidSigns.getTranslator().getTranslatedMessage(PaidSignsTranslatableMessage.BOOLEAN_TRUE);
} else {
return PaidSigns.getTranslator().getTranslatedMessage(PaidSignsTranslatableMessage.BOOLEAN_FALSE);
}
}
}

View File

@ -1,8 +1,9 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.utility.TabCompleteHelper;
import net.knarcraft.paidsigns.utility.PaidSignsTabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -22,7 +23,7 @@ public class ListTabCompleter extends TokenizedTabCompleter {
@NotNull String[] args) {
super.onTabComplete(sender, command, alias, args);
if (argumentSize == 1) {
return TabCompleteHelper.filterMatchingStartsWith(TabCompleteHelper.getPaidSignNames(), arguments.get(0));
return TabCompletionHelper.filterMatchingStartsWith(PaidSignsTabCompleteHelper.getPaidSignNames(), arguments.get(0));
} else if (argumentSize == 2) {
PaidSign sign = PaidSigns.getInstance().getSignManager().getPaidSign(arguments.get(0));
if (sign != null) {
@ -30,7 +31,7 @@ public class ListTabCompleter extends TokenizedTabCompleter {
for (Short signLine : sign.getConditions().keySet()) {
availableConditions.add(String.valueOf(signLine + 1));
}
return TabCompleteHelper.filterMatchingStartsWith(availableConditions, arguments.get(1));
return TabCompletionHelper.filterMatchingStartsWith(availableConditions, arguments.get(1));
}
}
return new ArrayList<>();

View File

@ -1,8 +1,7 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
@ -20,7 +19,7 @@ public class ReloadTabCommand implements TabExecutor {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
PaidSigns.getInstance().reload();
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.SUCCESS_RELOADED));
PaidSigns.getStringFormatter().displaySuccessMessage(sender, PaidSignsTranslatableMessage.SUCCESS_RELOADED);
return true;
}

View File

@ -2,8 +2,7 @@ package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.manager.PaidSignManager;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -17,7 +16,8 @@ import java.io.IOException;
public class RemoveConditionCommand extends TokenizedCommand {
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
@NotNull String[] args) {
super.onCommand(sender, command, label, args);
if (argumentSize < 2) {
return false;
@ -28,23 +28,25 @@ public class RemoveConditionCommand extends TokenizedCommand {
try {
lineNumber = (short) (Short.parseShort(arguments.get(1)) - 1);
if (lineNumber < 0 || lineNumber > 3) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
} catch (NumberFormatException exception) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_INVALID_NUMBER));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_INVALID_NUMBER);
return false;
}
PaidSignManager signManager = PaidSigns.getInstance().getSignManager();
PaidSign sign = signManager.getPaidSign(name);
if (sign == null) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSignsTranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND);
return false;
}
if (!sign.getConditions().containsKey(lineNumber)) {
sender.sendMessage(StringFormatter.replacePlaceholder(StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_NO_SUCH_CONDITION), "{line}", String.valueOf((lineNumber + 1))));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSigns.getStringFormatter().replacePlaceholder(
PaidSignsTranslatableMessage.ERROR_NO_SUCH_CONDITION, "{line}",
String.valueOf((lineNumber + 1))));
return false;
}
@ -52,10 +54,10 @@ public class RemoveConditionCommand extends TokenizedCommand {
try {
signManager.saveSigns();
} catch (IOException e) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_EXCEPTION_OCCURRED));
PaidSigns.getStringFormatter().displayErrorMessage(sender, PaidSignsTranslatableMessage.ERROR_EXCEPTION_OCCURRED);
return false;
}
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(TranslatableMessage.SUCCESS_REMOVED_CONDITION));
PaidSigns.getStringFormatter().displaySuccessMessage(sender, PaidSignsTranslatableMessage.SUCCESS_REMOVED_CONDITION);
return true;
}

View File

@ -1,8 +1,9 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.utility.TabCompleteHelper;
import net.knarcraft.paidsigns.utility.PaidSignsTabCompleteHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -28,7 +29,7 @@ public class RemoveConditionTabCompleter extends TokenizedTabCompleter {
}
if (argumentSize == 1) {
return TabCompleteHelper.filterMatchingStartsWith(TabCompleteHelper.getPaidSignNames(), arguments.get(0));
return TabCompletionHelper.filterMatchingStartsWith(PaidSignsTabCompleteHelper.getPaidSignNames(), arguments.get(0));
} else if (argumentSize == 2) {
PaidSign sign = PaidSigns.getInstance().getSignManager().getPaidSign(arguments.get(0));
if (sign != null) {
@ -36,7 +37,7 @@ public class RemoveConditionTabCompleter extends TokenizedTabCompleter {
for (Short signLine : sign.getConditions().keySet()) {
availableConditions.add(String.valueOf(signLine + 1));
}
return TabCompleteHelper.filterMatchingStartsWith(availableConditions, arguments.get(1));
return TabCompletionHelper.filterMatchingStartsWith(availableConditions, arguments.get(1));
}
}
return new ArrayList<>();
@ -46,7 +47,7 @@ public class RemoveConditionTabCompleter extends TokenizedTabCompleter {
* Initializes the values available for tab completion
*/
private void initializeValues() {
lineIndices = TabCompleteHelper.getSignLines();
lineIndices = PaidSignsTabCompleteHelper.getSignLines();
}
}

View File

@ -1,9 +1,8 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.utility.TabCompleteHelper;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.utility.PaidSignsTabCompleteHelper;
import net.knarcraft.paidsigns.utility.Tokenizer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -31,15 +30,16 @@ public class RemoveTabCommand implements TabExecutor {
try {
if (PaidSigns.getInstance().getSignManager().removePaidSign(name)) {
sender.sendMessage(StringFormatter.getTranslatedInfoMessage(
TranslatableMessage.SUCCESS_REMOVED_PAID_SIGN));
PaidSigns.getStringFormatter().displaySuccessMessage(sender,
PaidSignsTranslatableMessage.SUCCESS_REMOVED_PAID_SIGN);
} else {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND));
PaidSigns.getStringFormatter().displaySuccessMessage(sender,
PaidSignsTranslatableMessage.ERROR_PAID_SIGN_NOT_FOUND);
}
return true;
} catch (IOException e) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(TranslatableMessage.ERROR_EXCEPTION_OCCURRED));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSignsTranslatableMessage.ERROR_EXCEPTION_OCCURRED);
}
return false;
}
@ -52,7 +52,7 @@ public class RemoveTabCommand implements TabExecutor {
int argumentSize = args[args.length - 1].isEmpty() ? arguments.size() : arguments.size() - 1;
if (argumentSize < 1) {
return TabCompleteHelper.getPaidSignNames();
return PaidSignsTabCompleteHelper.getPaidSignNames();
} else {
return new ArrayList<>();
}

View File

@ -1,7 +1,7 @@
package net.knarcraft.paidsigns.command;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.utility.Tokenizer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@ -39,8 +39,8 @@ public class TokenizedCommand implements CommandExecutor {
Pattern.compile(regularExpression);
return false;
} catch (PatternSyntaxException exception) {
sender.sendMessage(StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_INVALID_REGULAR_EXPRESSION));
PaidSigns.getStringFormatter().displayErrorMessage(sender,
PaidSignsTranslatableMessage.ERROR_INVALID_REGULAR_EXPRESSION);
return true;
}
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.paidsigns.container;
import net.knarcraft.paidsigns.utility.ColorHelper;
import net.knarcraft.knarlib.property.ColorConversion;
import net.knarcraft.knarlib.util.ColorHelper;
/**
* A condition for deciding if a paid sign matches a sign line
@ -23,8 +24,8 @@ public record PaidSignCondition(String stringToMatch, boolean executeRegex, bool
String stringToMatch = this.stringToMatch;
//Strip color codes if they shouldn't matter
if (this.ignoreColor) {
stringToMatch = ColorHelper.stripColorCodes(stringToMatch);
line = ColorHelper.stripColorCodes(line);
stringToMatch = ColorHelper.stripColorCodes(stringToMatch, ColorConversion.NORMAL);
line = ColorHelper.stripColorCodes(line, ColorConversion.NORMAL);
}
if (this.executeRegex) {
//Match using RegEx

View File

@ -1,13 +1,45 @@
package net.knarcraft.paidsigns.container;
import org.bukkit.block.sign.Side;
import java.util.UUID;
/**
* A representation of a sign placed by a player that matched a paid sign
*
* @param playerId <p>The unique id of the player that created the sign</p>
* @param cost <p>The cost the player paid for creating the sign</p>
* @param playerId <p>The unique id of the player that created the sign</p>
* @param frontCost <p>The cost the player paid for creating the front of the sign</p>
* @param backCost <p>The cost the player paid for creating the back of the sign</p>
*/
public record TrackedSign(UUID playerId, double cost) {
public record TrackedSign(UUID playerId, double frontCost, double backCost) {
/**
* Gets an instance of a tracked sign
*
* @param playerId <p>The unique id of the player that created the sign</p>
* @param side <p>The side the player paid for</p>
* @param cost <p>The cost the player paid for creating the side of the sign</p>
* @return <p>A tracked sign instance</p>
*/
public static TrackedSign getInstance(UUID playerId, Side side, double cost) {
if (side == Side.FRONT) {
return new TrackedSign(playerId, cost, 0);
} else {
return new TrackedSign(playerId, 0, cost);
}
}
/**
* Gets the cost paid for the given side
*
* @param side <p>The side to check the cost for</p>
* @return <p>The cost the player paid</p>
*/
public double getCost(Side side) {
return switch (side) {
case FRONT -> frontCost;
case BACK -> backCost;
};
}
}

View File

@ -1,14 +1,11 @@
package net.knarcraft.paidsigns.formatting;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
/**
* An enum representing all translatable messages
*/
public enum TranslatableMessage {
/**
* The prefix to display in messages
*/
PREFIX,
public enum PaidSignsTranslatableMessage implements TranslatableMessage {
/**
* The message to display when a paid sign is successfully added
@ -139,5 +136,10 @@ public enum TranslatableMessage {
* The error to display if an invalid property is given to the edit command
*/
ERROR_PROPERTY_NOT_RECOGNIZED,
;
@Override
public TranslatableMessage[] getAllMessages() {
return PaidSignsTranslatableMessage.values();
}
}

View File

@ -1,125 +0,0 @@
package net.knarcraft.paidsigns.formatting;
import net.md_5.bungee.api.ChatColor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A formatter for formatting displayed messages
*/
public final class StringFormatter {
private StringFormatter() {
}
/**
* Translates the given boolean value
*
* @param booleanValue <p>The boolean value to translate</p>
* @return <p>The translation of the boolean value</p>
*/
public static String translateBoolean(boolean booleanValue) {
if (booleanValue) {
return Translator.getTranslatedMessage(TranslatableMessage.BOOLEAN_TRUE);
} else {
return Translator.getTranslatedMessage(TranslatableMessage.BOOLEAN_FALSE);
}
}
/**
* Replaces a placeholder in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholder <p>The placeholder to replace</p>
* @param replacement <p>The replacement value</p>
* @return <p>The input string with the placeholder replaced</p>
*/
public static String replacePlaceholder(String input, String placeholder, String replacement) {
return input.replace(placeholder, replacement);
}
/**
* Replaces placeholders in a string
*
* @param input <p>The input string to replace in</p>
* @param placeholders <p>The placeholders to replace</p>
* @param replacements <p>The replacement values</p>
* @return <p>The input string with placeholders replaced</p>
*/
public static String replacePlaceholders(String input, String[] placeholders, String[] replacements) {
for (int i = 0; i < Math.min(placeholders.length, replacements.length); i++) {
input = replacePlaceholder(input, placeholders[i], replacements[i]);
}
return input;
}
/**
* Gets a translated and formatted info message
*
* @param translatableMessage <p>The translatable message to translate and format</p>
* @return <p>The translated and formatted message</p>
*/
public static String getTranslatedInfoMessage(TranslatableMessage translatableMessage) {
return formatInfoMessage(Translator.getTranslatedMessage(translatableMessage));
}
/**
* Gets a translated and formatted error message
*
* @param translatableMessage <p>The translatable message to translate and format</p>
* @return <p>The translated and formatted message</p>
*/
public static String getTranslatedErrorMessage(TranslatableMessage translatableMessage) {
return formatErrorMessage(Translator.getTranslatedMessage(translatableMessage));
}
/**
* Formats an information message by adding the prefix and text color
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
public static String formatInfoMessage(String message) {
return ChatColor.DARK_GREEN + formatMessage(message);
}
/**
* Formats an error message by adding the prefix and text color
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
public static String formatErrorMessage(String message) {
return ChatColor.DARK_RED + formatMessage(message);
}
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
/**
* Formats a message by adding the prefix and text color
*
* @param message <p>The message to format</p>
* @return <p>The formatted message</p>
*/
private static String formatMessage(String message) {
return Translator.getTranslatedMessage(TranslatableMessage.PREFIX) + " " +
ChatColor.RESET + message;
}
}

View File

@ -1,118 +0,0 @@
package net.knarcraft.paidsigns.formatting;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.utility.FileHelper;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* A tool to get strings translated to the correct language
*/
public final class Translator {
private static Map<TranslatableMessage, String> translatedMessages;
private static Map<TranslatableMessage, String> backupTranslatedMessages;
private Translator() {
}
/**
* Loads the languages used by this translator
*/
public static void loadLanguages(String selectedLanguage) {
backupTranslatedMessages = loadTranslatedMessages("en");
translatedMessages = loadCustomTranslatedMessages(selectedLanguage);
if (translatedMessages == null) {
translatedMessages = loadTranslatedMessages(selectedLanguage);
}
}
/**
* Gets a translated version of the given translatable message
*
* @param translatableMessage <p>The message to translate</p>
* @return <p>The translated message</p>
*/
public static String getTranslatedMessage(TranslatableMessage translatableMessage) {
if (translatedMessages == null) {
return "Translated strings not loaded";
}
String translatedMessage;
if (translatedMessages.containsKey(translatableMessage)) {
translatedMessage = translatedMessages.get(translatableMessage);
} else if (backupTranslatedMessages.containsKey(translatableMessage)) {
translatedMessage = backupTranslatedMessages.get(translatableMessage);
} else {
translatedMessage = translatableMessage.toString();
}
return StringFormatter.translateAllColorCodes(translatedMessage);
}
/**
* Loads all translated messages for the given language
*
* @param language <p>The language chosen by the user</p>
* @return <p>A mapping of all strings for the given language</p>
*/
public static Map<TranslatableMessage, String> loadTranslatedMessages(String language) {
try {
BufferedReader reader = FileHelper.getBufferedReaderForInternalFile("/strings.yml");
return loadTranslatableMessages(language, reader);
} catch (FileNotFoundException e) {
PaidSigns.getInstance().getLogger().log(Level.SEVERE, "Unable to load translated messages");
return null;
}
}
/**
* Tries to load translated messages from a custom strings.yml file
*
* @param language <p>The selected language</p>
* @return <p>The loaded translated strings, or null if no custom language file exists</p>
*/
public static Map<TranslatableMessage, String> loadCustomTranslatedMessages(String language) {
File strings = new File(PaidSigns.getInstance().getDataFolder(), "strings.yml");
if (!strings.exists()) {
PaidSigns.getInstance().getLogger().log(Level.FINEST, "Strings file not found");
return null;
}
try {
PaidSigns.getInstance().getLogger().log(Level.INFO, "Loading custom strings...");
return loadTranslatableMessages(language, new BufferedReader(new InputStreamReader(new FileInputStream(strings))));
} catch (FileNotFoundException e) {
PaidSigns.getInstance().getLogger().log(Level.WARNING, "Unable to load custom messages");
return null;
}
}
/**
* Loads translatable messages from the given reader
*
* @param language <p>The selected language</p>
* @param reader <p>The buffered reader to read from</p>
* @return <p>The loaded translated strings</p>
*/
private static Map<TranslatableMessage, String> loadTranslatableMessages(String language, BufferedReader reader) {
Map<TranslatableMessage, String> translatedMessages = new HashMap<>();
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(reader);
for (TranslatableMessage message : TranslatableMessage.values()) {
String translated = configuration.getString(language + "." + message.toString());
if (translated != null) {
translatedMessages.put(message, translated);
}
}
return translatedMessages;
}
}

View File

@ -2,8 +2,8 @@ package net.knarcraft.paidsigns.listener;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.manager.TrackedSignManager;
import net.knarcraft.paidsigns.utility.SignHelper;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -92,9 +92,9 @@ public class SignBreakListener implements Listener {
* @param refund <p>Whether to perform a refund after un-tracking the sign</p>
*/
private void removeTrackedSign(Block block, boolean refund) {
if (block.getState() instanceof Sign) {
if (SignHelper.isSign(block)) {
try {
TrackedSignManager.removeTrackedSign(block.getLocation(), refund);
TrackedSignManager.removeTrackedSign(block.getLocation(), refund, false);
} catch (IOException ignored) {
}
}

View File

@ -1,12 +1,16 @@
package net.knarcraft.paidsigns.listener;
import net.knarcraft.knarlib.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.PaidSign;
import net.knarcraft.paidsigns.container.PaidSignConditionMatch;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.manager.EconomyManager;
import net.knarcraft.paidsigns.manager.TrackedSignManager;
import net.knarcraft.paidsigns.utility.SignHelper;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -18,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
/**
* A listener for listening to registered paid signs
@ -25,12 +30,20 @@ import java.util.Map;
public class SignListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
@SuppressWarnings("unused")
public void onSignChange(SignChangeEvent event) {
if (event.isCancelled() || event.getPlayer().hasPermission("paidsigns.paymentexempt")) {
return;
}
// As signs can be edited now, this event might be triggered on an already registered sign, so unregister
if (SignHelper.isSign(event.getBlock())) {
try {
TrackedSignManager.removeTrackedSign(event.getBlock().getLocation(), true, false,
event.getSide());
} catch (IOException ignored) {
}
}
String[] lines = event.getLines();
Map<String, PaidSign> allPaidSigns = PaidSigns.getInstance().getSignManager().getAllPaidSigns();
@ -116,6 +129,22 @@ public class SignListener implements Listener {
}
performPaidSignTransaction(paidSign, player, event);
Location signLocation = event.getBlock().getLocation();
if (!event.isCancelled()) {
//Immediately refund if a plugin destroyed the sign within 5 ticks of the creation
Bukkit.getScheduler().scheduleSyncDelayedTask(PaidSigns.getInstance(), () -> {
Block block = signLocation.getBlock();
if (!SignHelper.isSign(block)) {
try {
TrackedSignManager.removeTrackedSign(block.getLocation(), true, true);
} catch (IOException e) {
PaidSigns.getInstance().getLogger().log(Level.WARNING, String.format("Unable to save changes " +
"about removed tracked sign at %s", block.getLocation()));
}
}
}, 5);
}
}
/**
@ -129,15 +158,13 @@ public class SignListener implements Listener {
double cost = paidSign.getCost();
boolean canAfford = EconomyManager.canAfford(player, cost);
if (!canAfford) {
player.sendMessage(replaceCost(cost, StringFormatter.getTranslatedErrorMessage(
TranslatableMessage.ERROR_CANNOT_AFFORD)));
PaidSigns.getStringFormatter().displayErrorMessage(player, replaceCost(cost, PaidSignsTranslatableMessage.ERROR_CANNOT_AFFORD));
event.setCancelled(true);
} else {
EconomyManager.withdraw(player, cost);
player.sendMessage(replaceCost(cost, StringFormatter.getTranslatedInfoMessage(
TranslatableMessage.SUCCESS_PAID_FOR_SIGN)));
PaidSigns.getStringFormatter().displaySuccessMessage(player, replaceCost(cost, PaidSignsTranslatableMessage.SUCCESS_PAID_FOR_SIGN));
try {
TrackedSignManager.addTrackedSign(event.getBlock().getLocation(), player.getUniqueId(), cost);
TrackedSignManager.addTrackedSign(event.getBlock().getLocation(), player.getUniqueId(), cost, event.getSide());
} catch (IOException ignored) {
}
}
@ -150,10 +177,9 @@ public class SignListener implements Listener {
* @param message <p>The original message to replace the cost placeholder for</p>
* @return <p>The message with the cost instead of the cost placeholder</p>
*/
private String replaceCost(double cost, String message) {
String unit = EconomyManager.getCurrency(cost != 1);
return String.format(StringFormatter.replacePlaceholders(message, new String[]{"{cost}", "{unit}"},
new String[]{"%.2f", "%s"}), cost, unit);
private String replaceCost(double cost, TranslatableMessage message) {
return PaidSigns.getStringFormatter().replacePlaceholder(message,
"{cost}", EconomyManager.format(cost));
}
}

View File

@ -35,17 +35,13 @@ public final class EconomyManager {
}
/**
* Gets the name of the used currency
* Formats the given amount of currency according to the economy plugin's format
*
* @param plural <p>Whether to get the plural name or the singular name</p>
* @return <p>The name of the used currency</p>
* @param amount <p>The amount of currency to format</p>
* @return <p>The formatted string</p>
*/
public static String getCurrency(boolean plural) {
if (plural) {
return economy.currencyNamePlural();
} else {
return economy.currencyNameSingular();
}
public static String format(double amount) {
return economy.format(amount);
}
/**

View File

@ -2,12 +2,12 @@ package net.knarcraft.paidsigns.manager;
import net.knarcraft.paidsigns.PaidSigns;
import net.knarcraft.paidsigns.container.TrackedSign;
import net.knarcraft.paidsigns.formatting.StringFormatter;
import net.knarcraft.paidsigns.formatting.TranslatableMessage;
import net.knarcraft.paidsigns.formatting.PaidSignsTranslatableMessage;
import net.knarcraft.paidsigns.utility.SignHelper;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
@ -37,10 +37,22 @@ public final class TrackedSignManager {
*
* @param signLocation <p>The location the sign was created at</p>
* @param playerId <p>The unique id of the player that created the sign</p>
* @param cost <p>The cost the player paid</p>
* @param side <p>The side of the sign the player paid for</p>
* @throws IOException <p>If unable to save the tracked signs</p>
*/
public static void addTrackedSign(Location signLocation, UUID playerId, double cost) throws IOException {
trackedSigns.put(signLocation, new TrackedSign(playerId, cost));
public static void addTrackedSign(Location signLocation, UUID playerId, double cost, Side side) throws IOException {
if (trackedSigns.containsKey(signLocation)) {
// If there is already a tracked sign, overwrite it, while updating only the correct cost
TrackedSign oldSign = trackedSigns.get(signLocation);
if (side == Side.FRONT) {
trackedSigns.put(signLocation, new TrackedSign(playerId, cost, oldSign.backCost()));
} else {
trackedSigns.put(signLocation, new TrackedSign(playerId, oldSign.frontCost(), cost));
}
} else {
trackedSigns.put(signLocation, TrackedSign.getInstance(playerId, side, cost));
}
saveTrackedSigns();
}
@ -49,16 +61,64 @@ public final class TrackedSignManager {
*
* @param signLocation <p>The location the sign was removed from</p>
* @param refund <p>Whether to perform a refund after un-tracking the sign</p>
* @param forceRefund <p>Whether to force a refund, even if refunding is disabled</p>
* @param side <p>The side of the sign to un-track</p>
* @throws IOException <p>If unable to save the tracked signs</p>
*/
public static void removeTrackedSign(Location signLocation, boolean refund) throws IOException {
public static void removeTrackedSign(Location signLocation, boolean refund, boolean forceRefund,
Side side) throws IOException {
if (!trackedSigns.containsKey(signLocation)) {
return;
}
TrackedSign trackedSign = trackedSigns.get(signLocation);
Side oppositeSide = getOppositeSide(side);
double oppositeCost = trackedSign.getCost(oppositeSide);
boolean isInUse = oppositeCost > 0;
if (!isInUse) {
trackedSigns.remove(signLocation);
} else {
// If the opposite side has a cost, create a new sign with only that cost
trackedSigns.put(signLocation, TrackedSign.getInstance(trackedSign.playerId(), oppositeSide, oppositeCost));
}
saveTrackedSigns();
if (trackedSign.getCost(side) > 0) {
refund(trackedSign, refund, forceRefund, side);
}
}
/**
* Gets the opposite sign side of the given side
*
* @param side <p>The side to get the opposite of</p>
* @return <p>The opposite sign side</p>
*/
private static Side getOppositeSide(Side side) {
if (side == Side.FRONT) {
return Side.BACK;
} else {
return Side.FRONT;
}
}
/**
* Removes a tracked sign from the manager
*
* @param signLocation <p>The location the sign was removed from</p>
* @param refund <p>Whether to perform a refund after un-tracking the sign</p>
* @param forceRefund <p>Whether to force a refund, even if refunding is disabled</p>
* @throws IOException <p>If unable to save the tracked signs</p>
*/
public static void removeTrackedSign(Location signLocation, boolean refund, boolean forceRefund) throws IOException {
if (!trackedSigns.containsKey(signLocation)) {
return;
}
TrackedSign trackedSign = trackedSigns.get(signLocation);
trackedSigns.remove(signLocation);
saveTrackedSigns();
refund(trackedSign, refund);
refundBothSides(trackedSign, refund, forceRefund);
}
/**
@ -77,6 +137,13 @@ public final class TrackedSignManager {
for (String key : signSection.getKeys(false)) {
loadSign(signSection, key);
}
//Save tracked signs in case some were invalidated after loading
try {
TrackedSignManager.saveTrackedSigns();
} catch (IOException e) {
PaidSigns.getInstance().getLogger().log(Level.WARNING, "Unable to save tracked signs");
}
}
/**
@ -98,15 +165,28 @@ public final class TrackedSignManager {
return;
}
double cost = signSection.getDouble(key + ".cost");
double frontCost;
double backCost = 0;
if (signSection.contains(key + ".cost")) {
// Migrate from single side cost to two side cost
frontCost = signSection.getDouble(key + ".cost");
signSection.set(key + ".cost", null);
} else {
frontCost = signSection.getDouble(key + ".frontCost");
}
if (signSection.contains(key + ".backCost")) {
backCost = signSection.getDouble(key + ".backCost");
}
UUID playerId = UUID.fromString(Objects.requireNonNull(signSection.getString(key + ".playerId")));
TrackedSign trackedSign = new TrackedSign(playerId, cost);
TrackedSign trackedSign = new TrackedSign(playerId, frontCost, backCost);
//Prevent destroyed signs from being tracked indefinitely
if (!(signLocation.getBlock().getState() instanceof Sign)) {
PaidSigns.getInstance().getLogger().log(Level.WARNING, "The sign at " + signLocation + " no longer " +
"exists. Removing from sign tracker. Refunding the player.");
refund(trackedSign, true);
if (!SignHelper.isSign(signLocation.getBlock())) {
Bukkit.getScheduler().scheduleSyncDelayedTask(PaidSigns.getInstance(), () -> {
PaidSigns.getInstance().getLogger().log(Level.WARNING, "The sign at " + signLocation +
" no longer exists. Removing from sign tracker. Refunding the player.");
refundBothSides(trackedSign, true, false);
}, 100);
return;
}
@ -118,7 +198,7 @@ public final class TrackedSignManager {
*
* @throws IOException <p>If unable to write to the data file</p>
*/
private static void saveTrackedSigns() throws IOException {
public static void saveTrackedSigns() throws IOException {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(signsFile);
ConfigurationSection signSection = configuration.createSection("trackedSigns");
@ -126,7 +206,8 @@ public final class TrackedSignManager {
TrackedSign sign = trackedSigns.get(signLocation);
String locationString = Objects.requireNonNull(signLocation.getWorld()).getUID() + "," +
signLocation.getBlockX() + "," + signLocation.getBlockY() + "," + signLocation.getBlockZ();
signSection.set(locationString + ".cost", sign.cost());
signSection.set(locationString + ".frontCost", sign.frontCost());
signSection.set(locationString + ".backCost", sign.backCost());
signSection.set(locationString + ".playerId", sign.playerId().toString());
}
configuration.save(signsFile);
@ -137,19 +218,42 @@ public final class TrackedSignManager {
*
* @param trackedSign <p>The tracked sign to refund for</p>
* @param refund <p>Whether to actually refund</p>
* @param forceRefund <p>Whether to force a refund, even if refunding is disabled</p>
*/
private static void refund(TrackedSign trackedSign, boolean refund) {
if (!PaidSigns.getInstance().areRefundsEnabled() || !refund) {
private static void refund(TrackedSign trackedSign, boolean refund, boolean forceRefund, Side signSide) {
if ((!PaidSigns.getInstance().areRefundsEnabled() || !refund) && !forceRefund) {
return;
}
double cost = trackedSign.getCost(signSide);
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(trackedSign.playerId());
double refundSum = trackedSign.cost() / 100 * PaidSigns.getInstance().getRefundPercentage();
double refundSum;
if (forceRefund) {
//In the case where a refund is forced, the normal refund rate should not apply
refundSum = cost;
} else {
refundSum = cost / 100 * PaidSigns.getInstance().getRefundPercentage();
}
EconomyManager.deposit(offlinePlayer, refundSum);
if (offlinePlayer instanceof Player player) {
player.sendMessage(String.format(StringFormatter.replacePlaceholders(
StringFormatter.getTranslatedInfoMessage(TranslatableMessage.SUCCESS_REFUNDED),
new String[]{"{cost}", "{unit}"}, new String[]{"%.2f", "%s"}), refundSum,
EconomyManager.getCurrency(refundSum != 1)));
PaidSigns.getStringFormatter().displaySuccessMessage(player,
PaidSigns.getStringFormatter().replacePlaceholder(PaidSignsTranslatableMessage.SUCCESS_REFUNDED,
"{cost}", EconomyManager.format(refundSum)));
}
}
/**
* Refunds the costs for both sides of a paid sign
*
* @param trackedSign <p>The tracked sign to refund for</p>
* @param refund <p>Whether to perform a refund after un-tracking the sign</p>
* @param forceRefund <p>Whether to force a refund, even if refunding is disabled</p>
*/
private static void refundBothSides(TrackedSign trackedSign, boolean refund, boolean forceRefund) {
if (trackedSign.frontCost() > 0) {
refund(trackedSign, refund, forceRefund, Side.FRONT);
}
if (trackedSign.backCost() > 0) {
refund(trackedSign, refund, forceRefund, Side.BACK);
}
}

View File

@ -1,43 +0,0 @@
package net.knarcraft.paidsigns.utility;
import net.md_5.bungee.api.ChatColor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A helper class for dealing with colors
*/
public final class ColorHelper {
private ColorHelper() {
}
/**
* Strips all color codes from the given message
*
* @param message <p>The message to strip color codes from</p>
* @return <p>The message without color codes</p>
*/
public static String stripColorCodes(String message) {
return ChatColor.stripColor(translateAllColorCodes(message));
}
/**
* Translates all found color codes to formatting in a string
*
* @param message <p>The string to search for color codes</p>
* @return <p>The message with color codes translated</p>
*/
public static String translateAllColorCodes(String message) {
message = ChatColor.translateAlternateColorCodes('&', message);
Pattern pattern = Pattern.compile("(#[a-fA-F0-9]{6})");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
message = message.replace(matcher.group(), "" + ChatColor.of(matcher.group()));
}
return message;
}
}

View File

@ -1,32 +0,0 @@
package net.knarcraft.paidsigns.utility;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* A helper class for dealing with files
*/
public final class FileHelper {
private FileHelper() {
}
/**
* Gets a buffered reader for
*
* @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>
*/
public static BufferedReader getBufferedReaderForInternalFile(String file) throws FileNotFoundException {
InputStream inputStream = FileHelper.class.getResourceAsStream(file);
if (inputStream == null) {
throw new FileNotFoundException("Unable to read the given file");
}
return new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
}
}

View File

@ -1,9 +1,9 @@
package net.knarcraft.paidsigns.utility;
import net.knarcraft.knarlib.util.TabCompletionHelper;
import net.knarcraft.paidsigns.PaidSigns;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
@ -15,36 +15,15 @@ import java.util.StringJoiner;
/**
* A helper class for providing common tab complete options
*/
public final class TabCompleteHelper {
public final class PaidSignsTabCompleteHelper {
private static List<String> plugins;
private static Map<String, List<String>> permissions;
private TabCompleteHelper() {
private PaidSignsTabCompleteHelper() {
}
/**
* Finds tab complete values that match the start of the typed text
*
* @param values <p>The values to filter</p>
* @param typedText <p>The text the player has started typing</p>
* @return <p>The given string values that start with the player's typed text</p>
*/
public static List<String> filterMatchingStartsWith(@NotNull List<String> values, String typedText) {
//This little trick makes sure tab-completion works for paid sign names
if (!values.isEmpty() && values.get(0).startsWith("\"")) {
typedText = "\"" + typedText;
}
List<String> configValues = new ArrayList<>();
for (String value : values) {
if (value.toLowerCase().startsWith(typedText.toLowerCase())) {
configValues.add(value);
}
}
return configValues;
}
/**
* Gets the available boolean values for tab completion
*
@ -152,7 +131,7 @@ public final class TabCompleteHelper {
output = new ArrayList<>();
} else {
//Filter by the typed text
output = TabCompleteHelper.filterMatchingStartsWith(matchingPermissions, typedNode);
output = TabCompletionHelper.filterMatchingStartsWith(matchingPermissions, typedNode);
}
} else {
output = plugins;

View File

@ -0,0 +1,25 @@
package net.knarcraft.paidsigns.utility;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
/**
* A helper class for dealing with signs
*/
public final class SignHelper {
private SignHelper() {
}
/**
* Checks whether the given block is a sign
*
* @param block <p>The block to check</p>
* @return <p>True if the block is a sign</p>
*/
public static boolean isSign(Block block) {
return block.getState() instanceof Sign;
}
}

View File

@ -1,7 +1,7 @@
name: PaidSigns
version: '${project.version}'
main: net.knarcraft.paidsigns.PaidSigns
api-version: 1.19
api-version: '1.20'
prefix: PaidSigns
depend: [ Vault ]
authors: [ EpicKnarvik97 ]
@ -9,7 +9,7 @@ description: Add costs for creating plugin signs
website: https://git.knarcraft.net/EpicKnarvik97/PaidSigns
commands:
paidsigns:
description: Displays information about paid signs commends
description: Displays information about paid signs commands
usage: /<command>
permission: paidsigns.info
addpaidsign:

View File

@ -1,5 +1,4 @@
en:
PREFIX: "[PaidSigns]"
SUCCESS_ADDED_PAID_SIGN: "&bSuccessfully added new paid sign"
SUCCESS_ADDED_PAID_SIGN_CONDITION: "&bSuccessfully added new paid sign condition"
SUCCESS_UPDATED_PAID_SIGN: "&bSuccessfully updated the paid sign property"
@ -7,8 +6,8 @@ en:
SUCCESS_REMOVED_PAID_SIGN: "&bSuccessfully removed paid sign"
SUCCESS_REMOVED_CONDITION: "&bSuccessfully removed paid sign condition"
SUCCESS_RELOADED: "&bSuccessfully reloaded configuration"
SUCCESS_PAID_FOR_SIGN: "&bYou paid &3{cost} {unit} &bto create the sign"
SUCCESS_REFUNDED: "&bYou were refunded &3{cost} {unit} &bfor your broken sign"
SUCCESS_PAID_FOR_SIGN: "&bYou paid &3{cost} &bto create the sign"
SUCCESS_REFUNDED: "&bYou were refunded &3{cost} &bfor your broken sign"
PAID_SIGNS_INFO: |
&f---&3Paid signs&f---
{signs}{nextPagePrompt}
@ -47,6 +46,6 @@ en:
ERROR_INVALID_INPUT: "&bInvalid input: {input}"
ERROR_PAID_SIGN_NOT_FOUND: "&bNo such paid sign exists"
ERROR_NO_SUCH_CONDITION: "&bThe paid sign you specified has no condition for line {line}"
ERROR_CANNOT_AFFORD: "&bYou cannot afford to create this sign. The cost is {cost} {unit}"
ERROR_CANNOT_AFFORD: "&bYou cannot afford to create this sign. The cost is {cost}"
ERROR_INVALID_REGULAR_EXPRESSION: "&bThe provided regular expression is invalid"
ERROR_PROPERTY_NOT_RECOGNIZED: "&bThe property you tried to change was not recognized"