mirror of
https://github.com/AppleDash/SaneEconomy.git
synced 2024-09-27 14:12:58 +02:00
Compare commits
206 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ed3ccceb1d | ||
|
78d6c70658 | ||
|
2756e0c2d9 | ||
|
bb02d78b88 | ||
|
77ba58e333 | ||
|
c47524f863 | ||
|
afa4e9d36e | ||
|
854c2f5f10 | ||
|
2856305330 | ||
|
eab4b59501 | ||
|
d2fea85ffe | ||
|
5663077718 | ||
|
83d8965c23 | ||
|
fe4fc5018d | ||
|
c269fc1065 | ||
|
2c3fcea269 | ||
|
9fdb09fc79 | ||
|
dfaca9285e | ||
|
e2cc0f3f03 | ||
|
db8970ebbd | ||
|
f22618ebda | ||
|
030911af20 | ||
|
c77d595497 | ||
|
71a83a4139 | ||
|
e42c1fde65 | ||
|
1caee7db9f | ||
|
25c6c2cd95 | ||
|
3c84fe510e | ||
|
71832bfffa | ||
|
867535bb4d | ||
|
6be741b047 | ||
|
ccc7da8fe7 | ||
|
f92c7fddf7 | ||
|
3721f22158 | ||
|
08e87f2779 | ||
|
c51253b63b | ||
|
328e3ad08d | ||
|
b8045511f1 | ||
|
2bfec13b00 | ||
|
f9e8a868e5 | ||
|
be8206ce10 | ||
|
d399e7a6be | ||
|
1f0df591fd | ||
|
5ac6ba1407 | ||
|
9a00cddf24 | ||
|
31d311c229 | ||
|
ca13e9a810 | ||
|
f9440b3f18 | ||
|
01938349cb | ||
|
03cd019763 | ||
|
735c88f839 | ||
|
686b9ef871 | ||
|
c7f58edf44 | ||
|
f54a43437d | ||
|
c3f84698ce | ||
|
5bd417a0fc | ||
|
142b794abf | ||
|
8345b3a478 | ||
|
af3a64c14c | ||
|
96b5de947a | ||
|
684abf3488 | ||
|
d95d55c4f4 | ||
|
39e010a659 | ||
|
4e4eb709fe | ||
|
5574fcbac3 | ||
|
0506af01cc | ||
|
3678f71dc7 | ||
|
94bac07727 | ||
|
078336db8f | ||
|
1545bcacad | ||
|
921734241e | ||
|
36e53b03fb | ||
|
45c921482b | ||
|
00ee138cb5 | ||
|
da16d4474e | ||
|
dc6909af0f | ||
|
d70162c9c3 | ||
|
76e15e3665 | ||
|
6015eb7947 | ||
|
2adb136f3d | ||
|
e84d3772c9 | ||
|
724f0f93f6 | ||
|
ea0dc03057 | ||
|
874e4ed24b | ||
|
3d0d69cea7 | ||
|
c621824b82 | ||
|
6352cce5f3 | ||
|
c0bd36b3e0 | ||
|
2b5c0a3727 | ||
|
c8ba25058e | ||
|
a017083664 | ||
|
2dcc2cb2de | ||
|
40e6b7ad24 | ||
|
3bfecbfc4c | ||
|
742997cd3d | ||
|
38e64d2d9a | ||
|
5f55a623e3 | ||
|
8e0ba1f322 | ||
|
756fb044af | ||
|
b0be08d5dc | ||
|
9d71bb5a21 | ||
|
feb88b4519 | ||
|
8e950cacff | ||
|
ce7e8581c9 | ||
|
8ab8290b77 | ||
|
49950a43d3 | ||
|
cf60c7bbbe | ||
|
56a3904b43 | ||
|
2d15a8075f | ||
|
e015da4c99 | ||
|
0f23767336 | ||
|
3cfdd0185a | ||
|
af1752b85c | ||
|
b78c353093 | ||
|
4c57189e07 | ||
|
343f2a047d | ||
|
642ac49e4a | ||
|
2058b9bd64 | ||
|
96ee933d19 | ||
|
3695d1389b | ||
|
b81d64c2be | ||
|
8627f07163 | ||
|
2ccd25cce4 | ||
|
4df22729aa | ||
|
372ec96381 | ||
|
8a7ceea66e | ||
|
934d92fbf9 | ||
|
0ecfe71938 | ||
|
378a722ae2 | ||
|
510d0e63d0 | ||
|
358d50bcc7 | ||
|
20299a4b2e | ||
|
692f1439d2 | ||
|
e0c1d91ed1 | ||
|
1975ffef0c | ||
|
1a0b1d585d | ||
|
d86233af74 | ||
|
36ffc50fa1 | ||
|
25788446c5 | ||
|
e3a36ccd22 | ||
|
8bef764622 | ||
|
a0d8cb2d5d | ||
|
3670ebaadd | ||
|
e94a767a02 | ||
|
55e58d0ac3 | ||
|
0edc3e5ffe | ||
|
d825c54238 | ||
|
2442261496 | ||
|
9eaab03c50 | ||
|
908b7c956b | ||
|
44c72490a5 | ||
|
bbfd9d31d7 | ||
|
a144667971 | ||
|
8c8c171d3a | ||
|
60fb2e6d93 | ||
|
3f0e0a17e3 | ||
|
38d0823e8e | ||
|
10cac8741a | ||
|
e8c2aa9d07 | ||
|
4fc43a1f99 | ||
|
56a9fa8797 | ||
|
7dc66d143e | ||
|
290d98d26a | ||
|
3ce4666843 | ||
|
5daa5295ba | ||
|
3b3c24db27 | ||
|
ee79c639bc | ||
|
230d50d81b | ||
|
f9b53695d6 | ||
|
7f5740e5f1 | ||
|
cd28df2112 | ||
|
ba10f3666f | ||
|
41339dc3b0 | ||
|
175f9626a1 | ||
|
98f8e66c58 | ||
|
1f26acb49d | ||
|
4a7cac3f57 | ||
|
c7065ea9fa | ||
|
8447f245d5 | ||
|
c63a15dac5 | ||
|
fab5b76b64 | ||
|
0e37ec6e3c | ||
|
493598bc8c | ||
|
c765c40955 | ||
|
b9fbcfcdf5 | ||
|
fc0588281f | ||
|
21cef3c697 | ||
|
34840cbc56 | ||
|
f554013c16 | ||
|
d1b302e779 | ||
|
92350e6d25 | ||
|
f9cf1269fb | ||
|
6642c5b703 | ||
|
9803b795fd | ||
|
97709b0565 | ||
|
3bbca6dfaf | ||
|
1c217be26a | ||
|
66dfba087e | ||
|
8dd2f4e551 | ||
|
accb7a89b8 | ||
|
ca5b2340f7 | ||
|
4b088afec9 | ||
|
eab9e0774b | ||
|
f7dfe11358 | ||
|
3ab99b72e3 | ||
|
8f0267dbac |
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ RemoteSystemsTempFiles/.project
|
||||
out/
|
||||
target/
|
||||
dependency-reduced-pom.xml
|
||||
|
||||
|
@ -3,6 +3,8 @@ SaneEconomy
|
||||
|
||||
Finally, a **sane** economy plugin for Bukkit.
|
||||
|
||||
This project is not dead. It just happens that I am busy and get a lot of difficult feature requests. The code doesn't magically break just by sitting around - even if the last commit was a month ago, the released version of the plugin probably still works fine! :)
|
||||
|
||||
## About
|
||||
|
||||
I was looking for an economy plugin for a server I administrate, and I noticed something quite strange.
|
||||
@ -12,6 +14,13 @@ both in the comments on BukkitDev/SpigotMC, and on GitHub, all with no response
|
||||
|
||||
I decided that it was time for a change. I wanted a working, updated economy plugin for Bukkit, built against the latest API. So I wrote one myself.
|
||||
|
||||
## Components
|
||||
|
||||
* SaneEconomyCore - The main economy provider.
|
||||
* SaneEconomySignShop - A side project written for a specific server. Unsupported.
|
||||
* SaneEconomyMobKills - Another side project for the same server. Unsupported.
|
||||
* SaneEconomyOnlineTime - A replacement for the old plugin TimeIsMoney. Unsupported for now.
|
||||
|
||||
## Development
|
||||
|
||||
We manage dependencies with Maven.
|
||||
|
@ -6,10 +6,10 @@
|
||||
<parent>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomy</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
<version>0</version>
|
||||
</parent>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -18,6 +18,11 @@
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>6.0.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -25,6 +30,7 @@
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
@ -40,7 +46,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@ -50,9 +56,31 @@
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.mcstats.bukkit:metrics</include>
|
||||
<include>org.appledash:sanelib</include>
|
||||
<include>mysql:mysql-connector-java</include>
|
||||
<include>com.zaxxer:HikariCP</include>
|
||||
<include>org.slf4j:slf4j-api</include>
|
||||
<include>org.slf4j:slf4j-nop</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.appledash.sanelib</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomy.shaded.sanelib</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomy.shaded.mysql</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomy.shaded.hikari</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.slf4j</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomy.shaded.slf4j</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@ -65,6 +93,32 @@
|
||||
<outputDirectory>../out/</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-deploy</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>deploy</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<serverId>votuvo</serverId>
|
||||
<nexusUrl>https://nexus.votuvo.com/</nexusUrl>
|
||||
<skipStaging>true</skipStaging>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>votuvo</id>
|
||||
<url>https://nexus.sw4t.net/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</project>
|
||||
|
@ -0,0 +1,33 @@
|
||||
package org.appledash.saneeconomy;
|
||||
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.logger.TransactionLogger;
|
||||
import org.appledash.saneeconomy.vault.VaultHook;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/18/16.
|
||||
* Blackjack is best pony.
|
||||
*
|
||||
* This interface represent's SaneEconomy's public API.
|
||||
* Anything not exposed in some way by this interface should be considered unstable, and may change at any time.
|
||||
*/
|
||||
public interface ISaneEconomy {
|
||||
/**
|
||||
* Get the active EconomyManager
|
||||
* @return EconomyManager
|
||||
*/
|
||||
EconomyManager getEconomyManager();
|
||||
|
||||
/**
|
||||
* Get the active TransactionLogger
|
||||
* @return TransactionLogger, if there is one. Otherwise, Optional.empty()
|
||||
*/
|
||||
Optional<TransactionLogger> getTransactionLogger();
|
||||
|
||||
VaultHook getVaultHook();
|
||||
|
||||
String getLastName(UUID uuid);
|
||||
}
|
@ -1,41 +1,50 @@
|
||||
package org.appledash.saneeconomy;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.appledash.saneeconomy.command.SaneEconomyCommand;
|
||||
import org.appledash.saneeconomy.command.type.*;
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.appledash.saneeconomy.command.*;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.backend.EconomyStorageBackend;
|
||||
import org.appledash.saneeconomy.economy.backend.type.EconomyStorageBackendFlatfile;
|
||||
import org.appledash.saneeconomy.economy.backend.type.EconomyStorageBackendMySQL;
|
||||
import org.appledash.saneeconomy.economy.economable.EconomableGeneric;
|
||||
import org.appledash.saneeconomy.economy.logger.TransactionLogger;
|
||||
import org.appledash.saneeconomy.event.SaneEconomyTransactionEvent;
|
||||
import org.appledash.saneeconomy.listeners.JoinQuitListener;
|
||||
import org.appledash.saneeconomy.updates.GithubVersionChecker;
|
||||
import org.appledash.saneeconomy.utils.I18n;
|
||||
import org.appledash.saneeconomy.utils.SaneEconomyConfiguration;
|
||||
import org.appledash.saneeconomy.vault.VaultHook;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.appledash.sanelib.SanePlugin;
|
||||
import org.appledash.sanelib.command.SaneCommand;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SaneEconomy extends JavaPlugin {
|
||||
public class SaneEconomy extends SanePlugin implements ISaneEconomy {
|
||||
private static SaneEconomy instance;
|
||||
private EconomyManager economyManager;
|
||||
private VaultHook vaultHook;
|
||||
private TransactionLogger transactionLogger;
|
||||
private GithubVersionChecker versionChecker;
|
||||
|
||||
private static final Map<String, SaneEconomyCommand> COMMANDS = new HashMap<String, SaneEconomyCommand>() {{
|
||||
put("balance", new BalanceCommand());
|
||||
put("ecoadmin", new EconomyAdminCommand());
|
||||
put("pay", new PayCommand());
|
||||
put("saneeconomy", new SaneEcoCommand());
|
||||
put("balancetop", new BalanceTopCommand());
|
||||
}};
|
||||
private final Map<String, SaneCommand> commands = new HashMap<String, SaneCommand>() {
|
||||
{
|
||||
this.put("balance", new BalanceCommand(SaneEconomy.this));
|
||||
this.put("ecoadmin", new EconomyAdminCommand(SaneEconomy.this));
|
||||
this.put("pay", new PayCommand(SaneEconomy.this));
|
||||
this.put("saneeconomy", new SaneEcoCommand(SaneEconomy.this));
|
||||
this.put("balancetop", new BalanceTopCommand(SaneEconomy.this));
|
||||
}
|
||||
};
|
||||
|
||||
public SaneEconomy() {
|
||||
instance = this;
|
||||
@ -43,168 +52,179 @@ public class SaneEconomy extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
loadConfig();
|
||||
super.onEnable();
|
||||
|
||||
if (!loadEconomyBackend()) { /* Invalid backend type or connection error of some sort */
|
||||
shutdown();
|
||||
if (!this.loadConfig()) { /* Invalid backend type or connection error of some sort */
|
||||
this.shutdown();
|
||||
return;
|
||||
}
|
||||
|
||||
loadCommands();
|
||||
loadListeners();
|
||||
|
||||
if (getServer().getPluginManager().isPluginEnabled("Vault")) {
|
||||
vaultHook = new VaultHook(this);
|
||||
vaultHook.hook();
|
||||
getLogger().info("Hooked into Vault.");
|
||||
} else {
|
||||
getLogger().info("Not hooking into Vault because it isn't loaded.");
|
||||
if (this.getConfig().getBoolean("locale-override", false)) {
|
||||
Locale.setDefault(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
getServer().getScheduler().scheduleAsyncDelayedTask(this, GithubVersionChecker::checkUpdateAvailable);
|
||||
this.loadCommands();
|
||||
this.loadListeners();
|
||||
|
||||
getServer().getScheduler().runTaskTimerAsynchronously(this, () -> {
|
||||
economyManager.getBackend().reloadTopPlayerBalances();
|
||||
}, 0, (20 * 300) /* Update baltop every 5 minutes */);
|
||||
I18n.getInstance().loadTranslations();
|
||||
if (this.getServer().getPluginManager().isPluginEnabled("Vault")) {
|
||||
this.vaultHook = new VaultHook(this);
|
||||
this.vaultHook.hook();
|
||||
this.getLogger().info("Hooked into Vault.");
|
||||
} else {
|
||||
this.getLogger().info("Not hooking into Vault because it isn't loaded.");
|
||||
}
|
||||
|
||||
if (this.getConfig().getBoolean("update-check", true)) {
|
||||
this.versionChecker = new GithubVersionChecker("SaneEconomyCore", this.getDescription().getVersion().replace("-SNAPSHOT", ""));
|
||||
this.getServer().getScheduler().runTaskAsynchronously(this, this.versionChecker::checkUpdateAvailable);
|
||||
}
|
||||
|
||||
this.getServer().getScheduler().runTaskTimerAsynchronously(this, () -> {
|
||||
this.economyManager.getBackend().reloadTopPlayerBalances();
|
||||
}, 0L, (20L * this.getConfig().getLong("economy.baltop-update-interval", 300L)) /* Update baltop every 5 minutes by default */);
|
||||
|
||||
if (this.getConfig().getBoolean("multi-server-sync", false)) {
|
||||
this.getServer().getPluginManager().registerEvents(new Listener() {
|
||||
@EventHandler
|
||||
public void onTransaction(SaneEconomyTransactionEvent evt) { // Trust me, I'm a doctor.
|
||||
OfflinePlayer[] playersToSync = { evt.getTransaction().getSender().tryCastToPlayer(), evt.getTransaction().getReceiver().tryCastToPlayer() };
|
||||
|
||||
Player fakeSender = Iterables.getFirst(SaneEconomy.this.getServer().getOnlinePlayers(), null);
|
||||
|
||||
if (fakeSender == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Arrays.stream(playersToSync).filter(p -> (p != null) && !p.isOnline()).forEach(p -> {
|
||||
ByteArrayDataOutput bado = ByteStreams.newDataOutput();
|
||||
bado.writeUTF("Forward");
|
||||
bado.writeUTF("ONLINE");
|
||||
bado.writeUTF("SaneEconomy");
|
||||
bado.writeUTF("SyncPlayer");
|
||||
bado.writeUTF(p.getUniqueId().toString());
|
||||
fakeSender.sendPluginMessage(SaneEconomy.this, "BungeeCord", bado.toByteArray());
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", (channel, player, bytes) -> {
|
||||
if (!channel.equals("BungeeCord")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ByteArrayDataInput badi = ByteStreams.newDataInput(bytes);
|
||||
String subChannel = badi.readUTF();
|
||||
|
||||
if (subChannel.equals("SaneEconomy")) {
|
||||
String opCode = badi.readUTF();
|
||||
|
||||
if (opCode.equals("SyncPlayer")) {
|
||||
String playerUuid = badi.readUTF();
|
||||
this.economyManager.getBackend().reloadEconomable(String.format("player:%s", playerUuid), EconomyStorageBackend.EconomableReloadReason.CROSS_SERVER_SYNC);
|
||||
} else {
|
||||
this.getLogger().warning("Invalid OpCode received on SaneEconomy plugin message channel: " + opCode);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (vaultHook != null) {
|
||||
vaultHook.unhook();
|
||||
if (this.vaultHook != null) {
|
||||
this.getLogger().info("Unhooking from Vault.");
|
||||
this.vaultHook.unhook();
|
||||
}
|
||||
|
||||
economyManager.getBackend().waitUntilFlushed();
|
||||
this.flushEconomyManager();
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
File configFile = new File(getDataFolder(), "config.yml");
|
||||
private void flushEconomyManager() {
|
||||
if (this.economyManager != null) {
|
||||
this.getLogger().info("Flushing database...");
|
||||
this.economyManager.getBackend().waitUntilFlushed();
|
||||
|
||||
if (configFile.exists() && getConfig().getBoolean("debug", false)) {
|
||||
getLogger().info("Resetting configuration to default since debug == true.");
|
||||
if (this.economyManager.getBackend() instanceof EconomyStorageBackendMySQL) {
|
||||
((EconomyStorageBackendMySQL) this.economyManager.getBackend()).closeConnections();
|
||||
if (!((EconomyStorageBackendMySQL) this.economyManager.getBackend()).getConnection().getConnection().isFinished()) {
|
||||
this.getLogger().warning("SaneDatabase didn't terminate all threads, something weird is going on?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean loadConfig() {
|
||||
File configFile = new File(this.getDataFolder(), "config.yml");
|
||||
|
||||
if (configFile.exists() && this.getConfig().getBoolean("debug", false)) {
|
||||
this.getLogger().info("Resetting configuration to default since debug == true.");
|
||||
configFile.delete();
|
||||
saveDefaultConfig();
|
||||
reloadConfig();
|
||||
getConfig().set("debug", true);
|
||||
saveConfig();
|
||||
this.saveDefaultConfig();
|
||||
this.reloadConfig();
|
||||
this.getConfig().set("debug", true);
|
||||
this.saveConfig();
|
||||
} else {
|
||||
saveDefaultConfig();
|
||||
reloadConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean loadEconomyBackend() {
|
||||
getLogger().info("Initializing currency...");
|
||||
Currency currency = Currency.fromConfig(getConfig(), "currency");
|
||||
getLogger().info("Initialized currency: " + currency.getPluralName());
|
||||
|
||||
getLogger().info("Initializing economy storage backend...");
|
||||
String backendType = getConfig().getString("backend.type");
|
||||
String oldBackendType = getConfig().getString("old-backend.type", null);
|
||||
|
||||
EconomyStorageBackend backend = loadBackend(backendType, "backend");
|
||||
|
||||
if (backend == null) {
|
||||
getLogger().severe("Failed to load backend!");
|
||||
return false;
|
||||
}
|
||||
|
||||
getLogger().info("Performing initial data load...");
|
||||
backend.reloadDatabase();
|
||||
getLogger().info("Data loaded!");
|
||||
|
||||
if (!Strings.isNullOrEmpty(oldBackendType)) {
|
||||
getLogger().info("Old backend detected, converting... (This may take a minute or two.)");
|
||||
EconomyStorageBackend oldBackend = loadBackend(oldBackendType, "old-backend");
|
||||
if (oldBackend == null) {
|
||||
getLogger().severe("Failed to load old backend!");
|
||||
return false;
|
||||
if (!configFile.exists()) {
|
||||
this.saveDefaultConfig();
|
||||
}
|
||||
|
||||
oldBackend.reloadDatabase();
|
||||
convertBackends(oldBackend, backend);
|
||||
getLogger().info("Data converted, removing old config section.");
|
||||
getConfig().set("old-backend", null);
|
||||
saveConfig();
|
||||
this.reloadConfig();
|
||||
}
|
||||
|
||||
economyManager = new EconomyManager(currency, backend);
|
||||
this.flushEconomyManager(); // If we're reloading the configuration, we flush the old economy manager first
|
||||
|
||||
return true;
|
||||
}
|
||||
SaneEconomyConfiguration config = new SaneEconomyConfiguration(this);
|
||||
|
||||
private EconomyStorageBackend loadBackend(String backendType, String configPrefix) {
|
||||
EconomyStorageBackend backend;
|
||||
this.economyManager = config.loadEconomyBackend();
|
||||
this.transactionLogger = config.loadLogger();
|
||||
|
||||
if (backendType.equalsIgnoreCase("flatfile")) {
|
||||
String backendFileName = getConfig().getString(configPrefix + ".file", "economy.db");
|
||||
File backendFile = new File(getDataFolder(), backendFileName);
|
||||
backend = new EconomyStorageBackendFlatfile(backendFile);
|
||||
getLogger().info("Initialized flatfile backend with file " + backendFile.getAbsolutePath());
|
||||
} else if (backendType.equalsIgnoreCase("mysql")) {
|
||||
String backendHost = getConfig().getString(configPrefix + ".host");
|
||||
int backendPort = getConfig().getInt(configPrefix + ".port", 3306);
|
||||
String backendDb = getConfig().getString(configPrefix + ".database");
|
||||
String backendUser = getConfig().getString(configPrefix + ".username");
|
||||
String backendPass = getConfig().getString(configPrefix + ".password");
|
||||
this.saveConfig();
|
||||
|
||||
String jdbcUrl = String.format("jdbc:mysql://%s:%d/%s", backendHost, backendPort, backendDb);
|
||||
|
||||
EconomyStorageBackendMySQL mySQLBackend = new EconomyStorageBackendMySQL(jdbcUrl, backendUser, backendPass);
|
||||
backend = mySQLBackend;
|
||||
|
||||
getLogger().info("Initialized MySQL backend to host " + backendHost);
|
||||
getLogger().info("Testing connection...");
|
||||
if (!mySQLBackend.testConnection()) {
|
||||
getLogger().severe("MySQL connection failed - cannot continue!");
|
||||
return null;
|
||||
}
|
||||
|
||||
getLogger().info("Connection successful!");
|
||||
} else {
|
||||
getLogger().severe("Unknown storage backend " + backendType + "!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
private void convertBackends(EconomyStorageBackend old, EconomyStorageBackend newer) {
|
||||
old.getAllBalances().forEach((uniqueId, balance) -> {
|
||||
newer.setBalance(new EconomableGeneric(uniqueId), balance);
|
||||
});
|
||||
newer.waitUntilFlushed();
|
||||
return this.economyManager != null;
|
||||
}
|
||||
|
||||
private void loadCommands() {
|
||||
getLogger().info("Initializing commands...");
|
||||
COMMANDS.forEach((name, command) -> getCommand(name).setExecutor(command));
|
||||
getLogger().info("Initialized commands.");
|
||||
this.getLogger().info("Initializing commands...");
|
||||
this.commands.forEach((name, command) -> this.getCommand(name).setExecutor(command));
|
||||
this.getLogger().info("Initialized commands.");
|
||||
}
|
||||
|
||||
private void loadListeners() {
|
||||
getLogger().info("Initializing listeners...");
|
||||
getServer().getPluginManager().registerEvents(new JoinQuitListener(this), this);
|
||||
getLogger().info("Initialized listeners.");
|
||||
this.getLogger().info("Initializing listeners...");
|
||||
this.getServer().getPluginManager().registerEvents(new JoinQuitListener(this), this);
|
||||
this.getLogger().info("Initialized listeners.");
|
||||
}
|
||||
|
||||
private void shutdown(){
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
private void shutdown() {
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
public GithubVersionChecker getVersionChecker() {
|
||||
return this.versionChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active EconomyManager.
|
||||
* Get the active EconomyManager
|
||||
* @return EconomyManager
|
||||
*/
|
||||
@Override
|
||||
public EconomyManager getEconomyManager() {
|
||||
return economyManager;
|
||||
return this.economyManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active TransactionLogger
|
||||
* @return TransactionLogger, if there is one.
|
||||
*/
|
||||
@Override
|
||||
public Optional<TransactionLogger> getTransactionLogger() {
|
||||
return Optional.ofNullable(this.transactionLogger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current plugin instance.
|
||||
* @return Instance
|
||||
*/
|
||||
@Deprecated
|
||||
public static SaneEconomy getInstance() {
|
||||
return instance;
|
||||
}
|
||||
@ -213,7 +233,17 @@ public class SaneEconomy extends JavaPlugin {
|
||||
* Get the logger for the plugin.
|
||||
* @return Plugin logger.
|
||||
*/
|
||||
public static Logger logger(){
|
||||
public static Logger logger() {
|
||||
return instance.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultHook getVaultHook() {
|
||||
return this.vaultHook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastName(UUID uuid) {
|
||||
return this.economyManager.getBackend().getLastName("player:" + uuid.toString());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
package org.appledash.saneeconomy.command;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.utils.PlayerUtils;
|
||||
import org.appledash.sanelib.command.SaneCommand;
|
||||
import org.appledash.sanelib.command.exception.CommandException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.NeedPlayerException;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class BalanceCommand extends SaneCommand {
|
||||
private final SaneEconomy saneEconomy;
|
||||
|
||||
public BalanceCommand(SaneEconomy saneEconomy) {
|
||||
super(saneEconomy);
|
||||
this.saneEconomy = saneEconomy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.balance";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command> [player]"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
String playerIdentifier;
|
||||
String playerName;
|
||||
|
||||
if (args.length == 0) {
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NeedPlayerException();
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
|
||||
playerIdentifier = player.getUniqueId().toString();
|
||||
playerName = player.getDisplayName();
|
||||
} else {
|
||||
playerIdentifier = args[0];
|
||||
playerName = args[0];
|
||||
|
||||
if (!sender.hasPermission("saneeconomy.balance.other")) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "You don't have permission to check the balance of {1}.", playerIdentifier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OfflinePlayer player = PlayerUtils.getOfflinePlayer(playerIdentifier);
|
||||
|
||||
if (player == null) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "That player does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender == player) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Your balance is {1}.", this.saneEconomy.getEconomyManager().getFormattedBalance(Economable.wrap(player)));
|
||||
} else {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Balance for {1} is {2}.", playerName, this.saneEconomy.getEconomyManager().getFormattedBalance(Economable.wrap(player)));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.appledash.saneeconomy.command;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.sanelib.command.SaneCommand;
|
||||
import org.appledash.sanelib.command.exception.CommandException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.TooManyArgumentsException;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class BalanceTopCommand extends SaneCommand {
|
||||
private final SaneEconomy saneEconomy;
|
||||
|
||||
public BalanceTopCommand(SaneEconomy saneEconomy) {
|
||||
super(saneEconomy);
|
||||
this.saneEconomy = saneEconomy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.balancetop";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command>",
|
||||
"/<command> <page>"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length > 1) {
|
||||
throw new TooManyArgumentsException();
|
||||
}
|
||||
|
||||
int nPerPage = 10;
|
||||
int page = 1;
|
||||
|
||||
if (args.length == 1) {
|
||||
try {
|
||||
page = Math.abs(Integer.parseInt(args[0]));
|
||||
} catch (NumberFormatException e) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "{1} is not a valid number.", args[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int offset = (page - 1) * nPerPage;
|
||||
|
||||
Map<String, BigDecimal> topBalances = this.saneEconomy.getEconomyManager().getTopBalances(nPerPage, offset);
|
||||
|
||||
if (topBalances.isEmpty()) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "There aren't enough players to display that page.");
|
||||
return;
|
||||
}
|
||||
|
||||
AtomicInteger index = new AtomicInteger(offset + 1); /* I know it's stupid, but you can't do some_int++ from within the lambda. */
|
||||
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Top {1} players on page {2}:", topBalances.size(), page);
|
||||
|
||||
topBalances.forEach((player, balance) ->
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "[{1:02d}] {2} - {3}",
|
||||
index.getAndIncrement(),
|
||||
player == null ? "<unknown>" : player,
|
||||
this.saneEconomy.getEconomyManager().getCurrency().formatAmount(balance))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,162 @@
|
||||
package org.appledash.saneeconomy.command;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionResult;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.appledash.saneeconomy.utils.PlayerUtils;
|
||||
import org.appledash.sanelib.command.SaneCommand;
|
||||
import org.appledash.sanelib.command.exception.CommandException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.InvalidUsageException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.NeedPlayerException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.TooFewArgumentsException;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class EconomyAdminCommand extends SaneCommand {
|
||||
private final SaneEconomy saneEconomy;
|
||||
|
||||
public EconomyAdminCommand(SaneEconomy saneEconomy) {
|
||||
super(saneEconomy);
|
||||
this.saneEconomy = saneEconomy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.ecoadmin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command> <give/take/set> [player] <amount>"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length < 2) {
|
||||
throw new TooFewArgumentsException();
|
||||
}
|
||||
|
||||
String subCommand = args[0];
|
||||
String sTargetPlayer;
|
||||
String sAmount;
|
||||
|
||||
if (args.length == 2) {
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NeedPlayerException();
|
||||
}
|
||||
|
||||
sTargetPlayer = sender.getName();
|
||||
sAmount = args[1];
|
||||
} else {
|
||||
sTargetPlayer = args[1];
|
||||
sAmount = args[2];
|
||||
}
|
||||
|
||||
OfflinePlayer targetPlayer = PlayerUtils.getOfflinePlayer(sTargetPlayer);
|
||||
|
||||
if (targetPlayer == null) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "That player does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
EconomyManager ecoMan = this.saneEconomy.getEconomyManager();
|
||||
Economable economable = Economable.wrap(targetPlayer);
|
||||
|
||||
BigDecimal amount = NumberUtils.parseAndFilter(ecoMan.getCurrency(), sAmount);
|
||||
|
||||
if (!(subCommand.equalsIgnoreCase("set") && amount.equals(BigDecimal.ZERO)) && amount.compareTo(BigDecimal.ZERO) <= 0) { // If they're setting it to 0 it's fine, otherwise reject numbers under 1.
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "{1} is not a positive number.", ((amount.equals(BigDecimal.ONE.negate())) ? sAmount : String.valueOf(amount)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (subCommand.equalsIgnoreCase("give")) {
|
||||
Transaction transaction = new Transaction(ecoMan.getCurrency(), Economable.wrap(sender), Economable.wrap(targetPlayer), amount, TransactionReason.ADMIN_GIVE);
|
||||
TransactionResult result = ecoMan.transact(transaction);
|
||||
|
||||
BigDecimal newAmount = result.getToBalance();
|
||||
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Added {1} to {2}. Their balance is now {3}.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sTargetPlayer,
|
||||
ecoMan.getCurrency().formatAmount(newAmount)
|
||||
);
|
||||
|
||||
if (this.saneEconomy.getConfig().getBoolean("economy.notify-admin-give") && targetPlayer.isOnline()) {
|
||||
this.saneEconomy.getMessenger().sendMessage((CommandSender) targetPlayer, "{1} has given you {2}. Your balance is now {3}.",
|
||||
sender.getName(),
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
ecoMan.getCurrency().formatAmount(newAmount)
|
||||
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (subCommand.equalsIgnoreCase("take")) {
|
||||
Transaction transaction = new Transaction(ecoMan.getCurrency(), Economable.wrap(targetPlayer), Economable.wrap(sender), amount, TransactionReason.ADMIN_TAKE);
|
||||
TransactionResult result = ecoMan.transact(transaction);
|
||||
|
||||
BigDecimal newAmount = result.getFromBalance();
|
||||
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Took {1} from {2}. Their balance is now {3}.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sTargetPlayer,
|
||||
ecoMan.getCurrency().formatAmount(newAmount)
|
||||
);
|
||||
|
||||
if (this.saneEconomy.getConfig().getBoolean("economy.notify-admin-take") && targetPlayer.isOnline()) {
|
||||
this.saneEconomy.getMessenger().sendMessage((CommandSender) targetPlayer, "{1} has taken {2} from you. Your balance is now {3}.",
|
||||
sender.getName(),
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
ecoMan.getCurrency().formatAmount(newAmount)
|
||||
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (subCommand.equalsIgnoreCase("set")) {
|
||||
BigDecimal oldBal = ecoMan.getBalance(economable);
|
||||
ecoMan.setBalance(economable, amount);
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Balance for {1} set to {2}.", sTargetPlayer, ecoMan.getCurrency().formatAmount(amount));
|
||||
|
||||
this.saneEconomy.getTransactionLogger().ifPresent((logger) -> {
|
||||
// FIXME: This is a silly hack to get it to log.
|
||||
if (oldBal.compareTo(BigDecimal.ZERO) > 0) {
|
||||
logger.logTransaction(new Transaction(
|
||||
ecoMan.getCurrency(), economable, Economable.CONSOLE, oldBal, TransactionReason.ADMIN_TAKE
|
||||
));
|
||||
}
|
||||
|
||||
logger.logTransaction(new Transaction(
|
||||
ecoMan.getCurrency(), Economable.CONSOLE, economable, amount, TransactionReason.ADMIN_GIVE
|
||||
));
|
||||
});
|
||||
|
||||
if (this.saneEconomy.getConfig().getBoolean("economy.notify-admin-set") && targetPlayer.isOnline()) {
|
||||
this.saneEconomy.getMessenger().sendMessage((CommandSender) targetPlayer, "{1} has set your balance to {2}.",
|
||||
sender.getName(),
|
||||
ecoMan.getCurrency().formatAmount(amount)
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidUsageException();
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package org.appledash.saneeconomy.command;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionResult;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.appledash.saneeconomy.utils.PlayerUtils;
|
||||
import org.appledash.sanelib.command.SaneCommand;
|
||||
import org.appledash.sanelib.command.exception.CommandException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.NeedPlayerException;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class PayCommand extends SaneCommand {
|
||||
private final SaneEconomy saneEconomy;
|
||||
|
||||
public PayCommand(SaneEconomy saneEconomy) {
|
||||
super(saneEconomy);
|
||||
this.saneEconomy = saneEconomy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.pay";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/pay <player> <amount>"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length != 2) {
|
||||
throw CommandException.makeArgumentException(2, args.length);
|
||||
}
|
||||
|
||||
/* Doesn't make sense for console to pay a player, and admins paying a player is best done with /ecoadmin give */
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NeedPlayerException();
|
||||
}
|
||||
|
||||
EconomyManager ecoMan = this.saneEconomy.getEconomyManager();
|
||||
Player fromPlayer = (Player) sender;
|
||||
|
||||
String sToPlayer = args[0];
|
||||
OfflinePlayer toPlayer = PlayerUtils.getOfflinePlayer(sToPlayer);
|
||||
|
||||
if (toPlayer == null) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "That player does not exist or has never played before.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (toPlayer.getUniqueId().equals(fromPlayer.getUniqueId())) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "You cannot pay yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.saneEconomy.getConfig().getConfigurationSection("economy").getBoolean("pay-offline-players", true) && !toPlayer.isOnline()) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "You cannot pay an offline player.");
|
||||
return;
|
||||
}
|
||||
|
||||
String sAmount = args[1];
|
||||
BigDecimal amount = NumberUtils.parseAndFilter(ecoMan.getCurrency(), sAmount);
|
||||
|
||||
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "{1} is not a positive number.", ((amount.equals(BigDecimal.ONE.negate())) ? sAmount : String.valueOf(amount)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Perform the actual transfer. False == They didn't have enough money */
|
||||
Transaction transaction = new Transaction(ecoMan.getCurrency(), Economable.wrap(fromPlayer), Economable.wrap(toPlayer), amount, TransactionReason.PLAYER_PAY);
|
||||
TransactionResult result = ecoMan.transact(transaction);
|
||||
|
||||
if (result.getStatus() != TransactionResult.Status.SUCCESS) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "You do not have enough money to transfer {1} to {2}.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sToPlayer
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Inform the relevant parties. */
|
||||
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "You have transferred {1} to {2}.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sToPlayer
|
||||
);
|
||||
|
||||
if (toPlayer.isOnline()) {
|
||||
this.saneEconomy.getMessenger().sendMessage(((CommandSender) toPlayer), "You have received {1} from {2}.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
fromPlayer.getDisplayName()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package org.appledash.saneeconomy.command;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.sanelib.command.SaneCommand;
|
||||
import org.appledash.sanelib.command.exception.CommandException;
|
||||
import org.appledash.sanelib.command.exception.type.usage.InvalidUsageException;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SaneEcoCommand extends SaneCommand {
|
||||
private final SaneEconomy saneEconomy;
|
||||
|
||||
public SaneEcoCommand(SaneEconomy saneEconomy) {
|
||||
super(saneEconomy);
|
||||
this.saneEconomy = saneEconomy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command> reload - Reload everything.",
|
||||
"/<command> reload-database - Reload the database.",
|
||||
"/<command> reload-config - Reload the configuration."
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length != 1) {
|
||||
throw new InvalidUsageException();
|
||||
}
|
||||
|
||||
String subCommand = args[0];
|
||||
|
||||
if (subCommand.equalsIgnoreCase("reload-database")) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Reloading database...");
|
||||
this.saneEconomy.getEconomyManager().getBackend().reloadDatabase();
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Database reloaded.");
|
||||
} else if (subCommand.equalsIgnoreCase("reload-config")) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Reloading configuration...");
|
||||
this.saneEconomy.loadConfig();
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Configuration reloaded.");
|
||||
} else if (subCommand.equalsIgnoreCase("reload")) {
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Reloading configuration and database...");
|
||||
this.saneEconomy.loadConfig();
|
||||
this.saneEconomy.getEconomyManager().getBackend().reloadDatabase();
|
||||
this.saneEconomy.getMessenger().sendMessage(sender, "Configuration and database reloaded.");
|
||||
} else {
|
||||
throw new InvalidUsageException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package org.appledash.saneeconomy.command;
|
||||
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
import org.appledash.saneeconomy.command.exception.type.NoPermissionException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.UsageException;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public abstract class SaneEconomyCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
try {
|
||||
if (!sender.hasPermission(getPermission())) {
|
||||
throw new NoPermissionException();
|
||||
}
|
||||
|
||||
onCommand(sender, args);
|
||||
} catch (UsageException e) {
|
||||
/* Invalid usage in some way, print out exactly what went wrong along with the proper usage. */
|
||||
MessageUtils.sendMessage(sender, e.getMessage());
|
||||
|
||||
for (String s : getUsage()) {
|
||||
MessageUtils.sendMessage(sender, String.format("Usage: %s", s.replace("<command>", label)));
|
||||
}
|
||||
} catch (CommandException e) {
|
||||
MessageUtils.sendMessage(sender, e.getMessage());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permission node required to use the command.
|
||||
* @return Permission node.
|
||||
*/
|
||||
public abstract String getPermission();
|
||||
|
||||
/**
|
||||
* Get the command's usage.
|
||||
* When this is printed, '<command>' will be replaced with the command name.
|
||||
* @return Command usage examples
|
||||
*/
|
||||
public abstract String[] getUsage();
|
||||
|
||||
protected abstract void onCommand(CommandSender sender, String[] args) throws CommandException;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception;
|
||||
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.TooFewArgumentsException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.TooManyArgumentsException;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class CommandException extends Exception {
|
||||
|
||||
/**
|
||||
* Construct the proper CommandException for the given number of expected and actual arguments.
|
||||
* A TooManyArgumentsException is returned if actual > expected, otherwise a TooFewArguemntsException is returned.
|
||||
* @param expectedCount Expected number of arguments
|
||||
* @param actualCount Actual number of arguments
|
||||
* @return The right exception
|
||||
*/
|
||||
public static CommandException makeArgumentException(int expectedCount, int actualCount) {
|
||||
return (actualCount > expectedCount) ? new TooManyArgumentsException() : new TooFewArgumentsException();
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception.type;
|
||||
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class NoPermissionException extends CommandException {
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "You do not have permission to do that.";
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception.type.usage;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class InvalidUsageException extends UsageException {
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Invalid syntax for that command!";
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception.type.usage;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class NeedPlayerException extends UsageException {
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "That command requires a player argument when not run by a player.";
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception.type.usage;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class TooFewArgumentsException extends UsageException {
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Too few arguments for that command!";
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception.type.usage;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class TooManyArgumentsException extends UsageException {
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Too many arguments for that command!";
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.exception.type.usage;
|
||||
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class UsageException extends CommandException {
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.type;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.command.SaneEconomyCommand;
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.NeedPlayerException;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.appledash.saneeconomy.utils.PlayerUtils;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class BalanceCommand extends SaneEconomyCommand {
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.balance";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command> [player]"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
String playerName;
|
||||
|
||||
if (args.length == 0) {
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NeedPlayerException();
|
||||
}
|
||||
|
||||
playerName = sender.getName();
|
||||
} else {
|
||||
playerName = args[0];
|
||||
|
||||
if (!sender.hasPermission("saneeconomy.balance.other")) {
|
||||
MessageUtils.sendMessage(sender, "You don't have permission to check the balance of %s.", playerName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
OfflinePlayer player = PlayerUtils.getOfflinePlayer(playerName);
|
||||
|
||||
if (player == null) {
|
||||
MessageUtils.sendMessage(sender, "That player does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageUtils.sendMessage(sender, "Balance for %s is %s.", playerName, SaneEconomy.getInstance().getEconomyManager().getFormattedBalance(Economable.wrap(player)));
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.type;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.command.SaneEconomyCommand;
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.TooManyArgumentsException;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class BalanceTopCommand extends SaneEconomyCommand {
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.balancetop";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command>"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length > 0) {
|
||||
throw new TooManyArgumentsException();
|
||||
}
|
||||
|
||||
Map<OfflinePlayer, Double> topBalances = SaneEconomy.getInstance().getEconomyManager().getTopPlayerBalances(10);
|
||||
AtomicInteger index = new AtomicInteger(1); /* I know it's stupid, but you can't do some_int++ from within the lambda. */
|
||||
|
||||
MessageUtils.sendMessage(sender, "Top %d players:", topBalances.size());
|
||||
topBalances.forEach((player, balance) -> MessageUtils.sendMessage(sender, "[%02d] %s - %s", index.getAndIncrement(), player.getName(), SaneEconomy.getInstance().getEconomyManager().getCurrency().formatAmount(balance)));
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.type;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.command.SaneEconomyCommand;
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.InvalidUsageException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.NeedPlayerException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.TooFewArgumentsException;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.appledash.saneeconomy.utils.PlayerUtils;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import static org.appledash.saneeconomy.utils.I18n._;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class EconomyAdminCommand extends SaneEconomyCommand {
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.ecoadmin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command> <give/take/set> [player] <amount>"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length < 2) {
|
||||
throw new TooFewArgumentsException();
|
||||
}
|
||||
|
||||
String subCommand = args[0];
|
||||
String sTargetPlayer;
|
||||
String sAmount;
|
||||
|
||||
if (args.length == 2) {
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NeedPlayerException();
|
||||
}
|
||||
|
||||
sTargetPlayer = sender.getName();
|
||||
sAmount = args[1];
|
||||
} else {
|
||||
sTargetPlayer = args[1];
|
||||
sAmount = args[2];
|
||||
}
|
||||
|
||||
OfflinePlayer targetPlayer = PlayerUtils.getOfflinePlayer(sTargetPlayer);
|
||||
|
||||
if (targetPlayer == null) {
|
||||
MessageUtils.sendMessage(sender, _("That player does not exist."));
|
||||
return;
|
||||
}
|
||||
|
||||
EconomyManager ecoMan = SaneEconomy.getInstance().getEconomyManager();
|
||||
Economable economable = Economable.wrap(targetPlayer);
|
||||
|
||||
double amount = NumberUtils.parseAndFilter(ecoMan.getCurrency(), sAmount);
|
||||
|
||||
if (amount <= 0) {
|
||||
MessageUtils.sendMessage(sender, _("%s is not a positive number."), ((amount == -1) ? sAmount : String.valueOf(amount)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (subCommand.equalsIgnoreCase("give")) {
|
||||
double newAmount = ecoMan.addBalance(economable, amount);
|
||||
|
||||
MessageUtils.sendMessage(sender, _("Added %s to %s. Their balance is now %s."),
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sTargetPlayer,
|
||||
ecoMan.getCurrency().formatAmount(newAmount)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subCommand.equalsIgnoreCase("take")) {
|
||||
double newAmount = ecoMan.subtractBalance(economable, amount);
|
||||
|
||||
MessageUtils.sendMessage(sender, _("Took %s from %s. Their balance is now %s."),
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sTargetPlayer,
|
||||
ecoMan.getCurrency().formatAmount(newAmount)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subCommand.equalsIgnoreCase("set")) {
|
||||
ecoMan.setBalance(economable, amount);
|
||||
MessageUtils.sendMessage(sender, _("Balance for %s set to %s."), sTargetPlayer, ecoMan.getCurrency().formatAmount(amount));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidUsageException();
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.type;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.command.SaneEconomyCommand;
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.NeedPlayerException;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*
|
||||
* TODO: Support for paying offline players.
|
||||
*/
|
||||
public class PayCommand extends SaneEconomyCommand {
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.pay";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/pay <player> <amount>"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length != 2) {
|
||||
throw CommandException.makeArgumentException(2, args.length);
|
||||
}
|
||||
|
||||
/* Doesn't make sense for console to pay a player, and admins paying a player is best done with /ecoadmin give */
|
||||
if (!(sender instanceof Player)) {
|
||||
throw new NeedPlayerException();
|
||||
}
|
||||
|
||||
EconomyManager ecoMan = SaneEconomy.getInstance().getEconomyManager();
|
||||
Player fromPlayer = (Player) sender;
|
||||
|
||||
String sToPlayer = args[0];
|
||||
Player toPlayer = Bukkit.getServer().getPlayer(sToPlayer);
|
||||
|
||||
if (toPlayer == null) {
|
||||
MessageUtils.sendMessage(sender, "That player is not online.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (toPlayer.getUniqueId().equals(fromPlayer.getUniqueId())) {
|
||||
MessageUtils.sendMessage(sender, "You cannot pay yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
String sAmount = args[1];
|
||||
double amount = NumberUtils.parseAndFilter(ecoMan.getCurrency(), sAmount);
|
||||
|
||||
if (amount <= 0) {
|
||||
MessageUtils.sendMessage(sender, "%s is not a positive number.", ((amount == -1) ? sAmount : String.valueOf(amount)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Perform the actual transfer. False == They didn't have enough money */
|
||||
boolean result = ecoMan.transfer(Economable.wrap(fromPlayer), Economable.wrap(toPlayer), amount);
|
||||
|
||||
if (!result) {
|
||||
MessageUtils.sendMessage(sender, "You do not have enough money to transfer %s to %s.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sToPlayer
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Inform the relevant parties. */
|
||||
|
||||
MessageUtils.sendMessage(sender, "You have transferred %s to %s.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
sToPlayer
|
||||
);
|
||||
|
||||
MessageUtils.sendMessage(toPlayer, "You have received %s from %s.",
|
||||
ecoMan.getCurrency().formatAmount(amount),
|
||||
fromPlayer.getDisplayName()
|
||||
);
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package org.appledash.saneeconomy.command.type;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.command.SaneEconomyCommand;
|
||||
import org.appledash.saneeconomy.command.exception.CommandException;
|
||||
import org.appledash.saneeconomy.command.exception.type.usage.InvalidUsageException;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SaneEcoCommand extends SaneEconomyCommand {
|
||||
@Override
|
||||
public String getPermission() {
|
||||
return "saneeconomy.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getUsage() {
|
||||
return new String[] {
|
||||
"/<command> reload-database"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCommand(CommandSender sender, String[] args) throws CommandException {
|
||||
if (args.length != 1) {
|
||||
throw new InvalidUsageException();
|
||||
}
|
||||
|
||||
String subCommand = args[0];
|
||||
|
||||
if (subCommand.equalsIgnoreCase("reload-database")) {
|
||||
MessageUtils.sendMessage(sender, "Reloading database...");
|
||||
SaneEconomy.getInstance().getEconomyManager().getBackend().reloadDatabase();
|
||||
MessageUtils.sendMessage(sender, "Database reloaded.");
|
||||
} else {
|
||||
throw new InvalidUsageException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
package org.appledash.saneeconomy.economy;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import com.google.common.base.Strings;
|
||||
import org.appledash.sanelib.messages.MessageUtils;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
@ -14,19 +19,55 @@ public class Currency {
|
||||
private final String nameSingular;
|
||||
private final String namePlural;
|
||||
private final DecimalFormat format;
|
||||
private final String balanceFormat;
|
||||
|
||||
public Currency(String nameSingular, String namePlural, DecimalFormat format) {
|
||||
this(nameSingular, namePlural, format, "{1} {2}");
|
||||
}
|
||||
|
||||
public Currency(String nameSingular, String namePlural, DecimalFormat format, String balanceFormat) {
|
||||
this.nameSingular = nameSingular;
|
||||
this.namePlural = namePlural;
|
||||
this.format = format;
|
||||
this.balanceFormat = balanceFormat;
|
||||
|
||||
this.format.setParseBigDecimal(true);
|
||||
}
|
||||
|
||||
public static Currency fromConfig(FileConfiguration config, String baseNode) {
|
||||
public static Currency fromConfig(ConfigurationSection config) {
|
||||
DecimalFormat format = new DecimalFormat(config.getString("format", "0.00"));
|
||||
|
||||
if (config.getInt("grouping", 0) > 0) {
|
||||
DecimalFormatSymbols symbols = format.getDecimalFormatSymbols();
|
||||
if (symbols.getDecimalSeparator() == ',') { // French
|
||||
symbols.setGroupingSeparator(' ');
|
||||
} else {
|
||||
symbols.setGroupingSeparator(',');
|
||||
}
|
||||
|
||||
String groupingSeparator = config.getString("grouping-separator", null);
|
||||
|
||||
if (!Strings.isNullOrEmpty(groupingSeparator)) {
|
||||
symbols.setGroupingSeparator(groupingSeparator.charAt(0));
|
||||
}
|
||||
|
||||
String decimalSeparator = config.getString("decimal-separator", ".");
|
||||
|
||||
if (!Strings.isNullOrEmpty(decimalSeparator)) {
|
||||
symbols.setDecimalSeparator(decimalSeparator.charAt(0));
|
||||
}
|
||||
|
||||
format.setDecimalFormatSymbols(symbols);
|
||||
format.setGroupingUsed(true);
|
||||
format.setGroupingSize(3);
|
||||
}
|
||||
|
||||
return new Currency(
|
||||
config.getString(String.format("%s.name.singular", baseNode), "dollar"),
|
||||
config.getString(String.format("%s.name.plural", baseNode), "dollars"),
|
||||
new DecimalFormat(config.getString(String.format("%s.format", baseNode), "0.00"))
|
||||
);
|
||||
config.getString("name.singular", "dollar"),
|
||||
config.getString("name.plural", "dollars"),
|
||||
format,
|
||||
config.getString("balance-format", "{1} {2}")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,12 +75,10 @@ public class Currency {
|
||||
* @param amount Money amount.
|
||||
* @return Formatted amount string.
|
||||
*/
|
||||
public String formatAmount(double amount) {
|
||||
if (amount == 1) {
|
||||
return String.format("%s %s", format.format(amount), nameSingular);
|
||||
}
|
||||
|
||||
return String.format("%s %s", format.format(amount), namePlural);
|
||||
public String formatAmount(BigDecimal amount) {
|
||||
return ChatColor.translateAlternateColorCodes('&',
|
||||
MessageUtils.indexedFormat(this.balanceFormat, this.format.format(amount), amount.compareTo(BigDecimal.ONE) == 0 ? this.nameSingular : this.namePlural)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +87,7 @@ public class Currency {
|
||||
* @return Singular name.
|
||||
*/
|
||||
public String getSingularName() {
|
||||
return nameSingular;
|
||||
return this.nameSingular;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,7 +96,7 @@ public class Currency {
|
||||
* @return Plural name.
|
||||
*/
|
||||
public String getPluralName() {
|
||||
return namePlural;
|
||||
return this.namePlural;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,6 +104,6 @@ public class Currency {
|
||||
* @return DecimalFormat instance
|
||||
*/
|
||||
public DecimalFormat getFormat() {
|
||||
return format;
|
||||
return this.format;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
package org.appledash.saneeconomy.economy;
|
||||
|
||||
import org.appledash.saneeconomy.ISaneEconomy;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.backend.EconomyStorageBackend;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.economable.EconomableConsole;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionResult;
|
||||
import org.appledash.saneeconomy.event.SaneEconomyTransactionEvent;
|
||||
import org.appledash.saneeconomy.utils.MapUtil;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
@ -17,12 +25,18 @@ import java.util.UUID;
|
||||
* Represents our EconomyManager, which manages players' balances.
|
||||
*/
|
||||
public class EconomyManager {
|
||||
private final ISaneEconomy saneEconomy;
|
||||
private final Currency currency;
|
||||
private final EconomyStorageBackend backend;
|
||||
private final String serverAccountName;
|
||||
|
||||
private static final BigDecimal REQUIRED_BALANCE_ACCURACY = new BigDecimal("0.0001");
|
||||
|
||||
public EconomyManager(Currency currency, EconomyStorageBackend backend) {
|
||||
public EconomyManager(ISaneEconomy saneEconomy, Currency currency, EconomyStorageBackend backend, String serverAccountName) {
|
||||
this.saneEconomy = saneEconomy;
|
||||
this.currency = currency;
|
||||
this.backend = backend;
|
||||
this.serverAccountName = serverAccountName;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,7 +44,7 @@ public class EconomyManager {
|
||||
* @return Currency
|
||||
*/
|
||||
public Currency getCurrency() {
|
||||
return currency;
|
||||
return this.currency;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,7 +53,7 @@ public class EconomyManager {
|
||||
* @return Formatted balance
|
||||
*/
|
||||
public String getFormattedBalance(Economable player) {
|
||||
return currency.formatAmount(backend.getBalance(player));
|
||||
return this.currency.formatAmount(this.backend.getBalance(player));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +62,7 @@ public class EconomyManager {
|
||||
* @return True if they have used the economy system before, false otherwise
|
||||
*/
|
||||
public boolean accountExists(Economable player) {
|
||||
return backend.accountExists(player);
|
||||
return this.backend.accountExists(player);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,8 +70,12 @@ public class EconomyManager {
|
||||
* @param targetPlayer Player to get balance of
|
||||
* @return Player's balance
|
||||
*/
|
||||
public double getBalance(Economable targetPlayer) {
|
||||
return backend.getBalance(targetPlayer);
|
||||
public BigDecimal getBalance(Economable targetPlayer) {
|
||||
if (EconomableConsole.isConsole(targetPlayer)) {
|
||||
return new BigDecimal(Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
return this.backend.getBalance(targetPlayer);
|
||||
}
|
||||
|
||||
|
||||
@ -67,99 +85,125 @@ public class EconomyManager {
|
||||
* @param requiredBalance How much money we're checking for
|
||||
* @return True if they have requiredBalance or more, false otherwise
|
||||
*/
|
||||
public boolean hasBalance(Economable targetPlayer, double requiredBalance) {
|
||||
return getBalance(targetPlayer) >= requiredBalance;
|
||||
public boolean hasBalance(Economable targetPlayer, BigDecimal requiredBalance) {
|
||||
return (EconomableConsole.isConsole(targetPlayer)) || (hasBalance(this.getBalance(targetPlayer), requiredBalance));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare account balance and required balance to a reasonable degree of accuracy. <br>
|
||||
* <b>Visible for testing</b>
|
||||
*
|
||||
* @param accountBalance account balance
|
||||
* @param requiredBalance required balance
|
||||
* @return true if the account has the required balance to some degree of accuracy, false otherwise
|
||||
*/
|
||||
public boolean hasBalance(BigDecimal accountBalance, BigDecimal requiredBalance) {
|
||||
if (accountBalance.compareTo(requiredBalance) >= 0) {
|
||||
return true;
|
||||
}
|
||||
// Must compare to degree of accuracy
|
||||
// See https://github.com/AppleDash/SaneEconomy/issues/100
|
||||
BigDecimal difference = requiredBalance.subtract(accountBalance);
|
||||
return difference.compareTo(REQUIRED_BALANCE_ACCURACY) < 0; // difference < PRECISION
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to a player's balance.
|
||||
* This does not filter the amount.
|
||||
* @param targetPlayer Player to add to
|
||||
* @param amount Amount to add
|
||||
* @return Player's new balance
|
||||
* @throws IllegalArgumentException If amount is negative
|
||||
*/
|
||||
public double addBalance(Economable targetPlayer, double amount) {
|
||||
amount = NumberUtils.filterAmount(currency, amount);
|
||||
|
||||
if (amount < 0) {
|
||||
throw new IllegalArgumentException("Cannot add a negative amount!");
|
||||
}
|
||||
|
||||
double newAmount = backend.getBalance(targetPlayer) + amount;
|
||||
|
||||
backend.setBalance(targetPlayer, newAmount);
|
||||
|
||||
return newAmount;
|
||||
private void addBalance(Economable targetPlayer, BigDecimal amount) {
|
||||
this.setBalance(targetPlayer, this.backend.getBalance(targetPlayer).add(amount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract from a player's balance.
|
||||
* If the subtraction would result in a negative balance, the balance is instead set to 0.
|
||||
* This does not filter the amount.
|
||||
*
|
||||
* @param targetPlayer Player to subtract from
|
||||
* @param amount Amount to subtract
|
||||
* @return Player's new balance
|
||||
* @throws IllegalArgumentException If amount is negative
|
||||
*/
|
||||
public double subtractBalance(Economable targetPlayer, double amount) {
|
||||
amount = NumberUtils.filterAmount(currency, amount);
|
||||
|
||||
if (amount < 0) {
|
||||
throw new IllegalArgumentException("Cannot subtract a negative amount!");
|
||||
}
|
||||
|
||||
double newAmount = backend.getBalance(targetPlayer) - amount;
|
||||
|
||||
|
||||
/* Subtracting that much would result in a negative balance - don't do that */
|
||||
if (newAmount <= 0.0D) {
|
||||
newAmount = 0.0D;
|
||||
}
|
||||
|
||||
backend.setBalance(targetPlayer, newAmount);
|
||||
|
||||
return newAmount;
|
||||
private void subtractBalance(Economable targetPlayer, BigDecimal amount) {
|
||||
// Ensure we don't go negative.
|
||||
this.setBalance(targetPlayer, this.backend.getBalance(targetPlayer).subtract(amount).max(BigDecimal.ZERO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a player's balance.
|
||||
* Set a player's balance. This does NOT log.
|
||||
* @param targetPlayer Player to set balance of
|
||||
* @param amount Amount to set balance to
|
||||
* @throws IllegalArgumentException If amount is negative
|
||||
*/
|
||||
public void setBalance(Economable targetPlayer, double amount) {
|
||||
amount = NumberUtils.filterAmount(currency, amount);
|
||||
public void setBalance(Economable targetPlayer, BigDecimal amount) {
|
||||
amount = NumberUtils.filterAmount(this.currency, amount);
|
||||
|
||||
if (amount < 0) {
|
||||
throw new IllegalArgumentException("Cannot set balance to a negative value!");
|
||||
if (EconomableConsole.isConsole(targetPlayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
backend.setBalance(targetPlayer, amount);
|
||||
this.backend.setBalance(targetPlayer, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer money from one player to another.
|
||||
* @param fromPlayer Player to transfer from
|
||||
* @param toPlayer Player to transfer to
|
||||
* @param amount Amount to transfer
|
||||
* @return True if success, false if fromPlayer has insufficient funds.
|
||||
* @throws IllegalArgumentException If amount is negative
|
||||
* Perform a transaction - a transfer of funds from one entity to another.
|
||||
* @param transaction Transaction to perform.
|
||||
* @return TransactionResult describing success or failure of the Transaction.
|
||||
*/
|
||||
public boolean transfer(Economable fromPlayer, Economable toPlayer, double amount) {
|
||||
amount = NumberUtils.filterAmount(currency, amount);
|
||||
public TransactionResult transact(Transaction transaction) {
|
||||
Economable sender = transaction.getSender();
|
||||
Economable receiver = transaction.getReceiver();
|
||||
BigDecimal amount = transaction.getAmount(); // This amount is validated and filtered upon creation of Transaction
|
||||
|
||||
if (amount < 0) {
|
||||
throw new IllegalArgumentException("Cannot transfer a negative amount!");
|
||||
if (Bukkit.getServer().getPluginManager() != null) { // Bukkit.getServer().getPluginManager() == null from our JUnit tests.
|
||||
SaneEconomyTransactionEvent evt = new SaneEconomyTransactionEvent(transaction);
|
||||
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
Bukkit.getServer().getPluginManager().callEvent(evt);
|
||||
|
||||
if (evt.isCancelled()) {
|
||||
return new TransactionResult(transaction, TransactionResult.Status.CANCELLED_BY_PLUGIN);
|
||||
}
|
||||
} else {
|
||||
Future<SaneEconomyTransactionEvent> future = Bukkit.getServer().getScheduler().callSyncMethod(SaneEconomy.getInstance(), () -> {
|
||||
Bukkit.getServer().getPluginManager().callEvent(evt);
|
||||
return evt;
|
||||
});
|
||||
|
||||
try {
|
||||
if (future.get().isCancelled()) {
|
||||
return new TransactionResult(transaction, TransactionResult.Status.CANCELLED_BY_PLUGIN);
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Bukkit.getServer().getPluginManager().callEvent(evt);
|
||||
if (evt.isCancelled()) {
|
||||
return new TransactionResult(transaction, TransactionResult.Status.CANCELLED_BY_PLUGIN);
|
||||
}*/
|
||||
}
|
||||
|
||||
if (!hasBalance(fromPlayer, amount)) {
|
||||
return false;
|
||||
if (transaction.isSenderAffected()) { // Sender should have balance taken from them
|
||||
if (!this.hasBalance(sender, amount)) {
|
||||
return new TransactionResult(transaction, TransactionResult.Status.ERR_NOT_ENOUGH_FUNDS);
|
||||
}
|
||||
|
||||
this.subtractBalance(sender, amount);
|
||||
}
|
||||
|
||||
/* Perform the actual transfer. TODO: Maybe return their new balances in some way? */
|
||||
subtractBalance(fromPlayer, amount);
|
||||
addBalance(toPlayer, amount);
|
||||
if (transaction.isReceiverAffected()) { // Receiver should have balance added to them
|
||||
this.addBalance(receiver, amount);
|
||||
}
|
||||
|
||||
return true;
|
||||
this.saneEconomy.getTransactionLogger().ifPresent((logger) -> logger.logTransaction(transaction));
|
||||
|
||||
return new TransactionResult(transaction, this.getBalance(sender), this.getBalance(receiver));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,16 +211,32 @@ public class EconomyManager {
|
||||
* @param amount Maximum number of players to show.
|
||||
* @return Map of OfflinePlayer to Double
|
||||
*/
|
||||
public Map<OfflinePlayer, Double> getTopPlayerBalances(int amount) {
|
||||
Map<UUID, Double> uuidBalances = backend.getTopPlayerBalances(amount);
|
||||
Map<OfflinePlayer, Double> playerBalances = new LinkedHashMap<>();
|
||||
public Map<String, BigDecimal> getTopBalances(int amount, int offset) {
|
||||
LinkedHashMap<String, BigDecimal> playerNamesToBalances = this.backend.getTopBalances();
|
||||
|
||||
uuidBalances.forEach((uuid, balance) -> playerBalances.put(Bukkit.getServer().getOfflinePlayer(uuid), balance));
|
||||
/*uuidBalances.re((uuid, balance) -> {
|
||||
String playerName = this.backend.getLastName(uuid);
|
||||
|
||||
return playerBalances;
|
||||
if (playerName != null) {
|
||||
if ((this.saneEconomy.getVaultHook() == null) || !this.saneEconomy.getVaultHook().hasPermission(offlinePlayer, "saneeconomy.balancetop.hide")) {
|
||||
playerBalances.put(Bukkit.getServer().getOfflinePlayer(uuid), balance);
|
||||
}
|
||||
}
|
||||
});*/
|
||||
|
||||
|
||||
return MapUtil.skipAndTake(playerNamesToBalances, offset, amount);
|
||||
}
|
||||
|
||||
public EconomyStorageBackend getBackend() {
|
||||
return backend;
|
||||
return this.backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the "Server" economy account. This account has an infinite balance and deposits do nothing.
|
||||
* @return Server economy account, or null if none.
|
||||
*/
|
||||
public String getServerAccountName() {
|
||||
return this.serverAccountName;
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ package org.appledash.saneeconomy.economy.backend;
|
||||
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
@ -13,38 +14,43 @@ import java.util.UUID;
|
||||
*/
|
||||
public interface EconomyStorageBackend {
|
||||
/**
|
||||
* Check whether a player has used the economy system before.
|
||||
* @param player Player
|
||||
* Check whether an economable has used the economy system before.
|
||||
* @param economable Economable
|
||||
* @return True if they have, false otherwise.
|
||||
*/
|
||||
boolean accountExists(Economable player);
|
||||
boolean accountExists(Economable economable);
|
||||
|
||||
/**
|
||||
* Get the balance of a player.
|
||||
* @param player Player
|
||||
* @param economable Economable
|
||||
* @return Player's current balance
|
||||
*/
|
||||
double getBalance(Economable player);
|
||||
BigDecimal getBalance(Economable economable);
|
||||
|
||||
/**
|
||||
* Set the balance of a player, overwriting the old balance.
|
||||
* @param player Player
|
||||
* Set the balance of an Economable, overwriting the old balance.
|
||||
* @param economable Economable
|
||||
* @param newBalance Player's new balance
|
||||
*/
|
||||
void setBalance(Economable player, double newBalance);
|
||||
void setBalance(Economable economable, BigDecimal newBalance);
|
||||
|
||||
/**
|
||||
* Get the UUIDs of the players who have the most money, along with how much money they have.
|
||||
* @param amount Maximum number to get.
|
||||
* @return Map of player UUIDs to amounts.
|
||||
*/
|
||||
Map<UUID, Double> getTopPlayerBalances(int amount);
|
||||
LinkedHashMap<String, BigDecimal> getTopBalances();
|
||||
|
||||
/**
|
||||
* Reload this backend's database from disk.
|
||||
*/
|
||||
void reloadDatabase();
|
||||
|
||||
/**
|
||||
* Reload data for just the Economable with the given unique identifier.
|
||||
* @param uniqueIdentifier Unique identifier of Economable to reload data for.
|
||||
*/
|
||||
void reloadEconomable(String uniqueIdentifier, EconomableReloadReason reason);
|
||||
|
||||
/**
|
||||
* Reload this backend's top balances.
|
||||
*/
|
||||
@ -54,10 +60,22 @@ public interface EconomyStorageBackend {
|
||||
* Get the balances of all entities in this database.
|
||||
* @return Map of unique identifiers to balances.
|
||||
*/
|
||||
Map<String, Double> getAllBalances();
|
||||
Map<String, BigDecimal> getAllBalances();
|
||||
|
||||
/**
|
||||
* Wait until all of the data in memory has been written out to disk.
|
||||
*/
|
||||
void waitUntilFlushed();
|
||||
|
||||
/**
|
||||
* Get the last name associated with a unique ID.
|
||||
*
|
||||
* @param uuid Unique ID.
|
||||
* @return Last name, or null if none.
|
||||
*/
|
||||
String getLastName(String uuid);
|
||||
|
||||
enum EconomableReloadReason {
|
||||
CROSS_SERVER_SYNC, PLAYER_JOIN
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,72 @@
|
||||
package org.appledash.saneeconomy.economy.backend.type;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.backend.EconomyStorageBackend;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.utils.MapUtil;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/19/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public abstract class EconomyStorageBackendCaching implements EconomyStorageBackend {
|
||||
protected HashMap<String, Double> balances = new HashMap<>();
|
||||
private Map<UUID, Double> topPlayerBalances = new LinkedHashMap<>();
|
||||
protected Map<String, BigDecimal> balances = new ConcurrentHashMap<>();
|
||||
private LinkedHashMap<String, BigDecimal> topBalances = new LinkedHashMap<>();
|
||||
protected Map<String, String> uuidToName = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean accountExists(Economable economable) {
|
||||
return balances.containsKey(economable.getUniqueIdentifier());
|
||||
return this.balances.containsKey(economable.getUniqueIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getBalance(Economable economable) {
|
||||
if (!accountExists(economable)) {
|
||||
return 0.0D;
|
||||
public BigDecimal getBalance(Economable economable) {
|
||||
if (!this.accountExists(economable)) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
return balances.get(economable.getUniqueIdentifier());
|
||||
return this.balances.get(economable.getUniqueIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<UUID, Double> getTopPlayerBalances(int amount) {
|
||||
return MapUtil.takeFromMap(topPlayerBalances, amount);
|
||||
public LinkedHashMap<String, BigDecimal> getTopBalances() {
|
||||
return this.topBalances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadTopPlayerBalances() {
|
||||
Map<UUID, Double> playerBalances = new HashMap<>();
|
||||
Map<String, BigDecimal> balances = new HashMap<>();
|
||||
|
||||
balances.forEach((identifier, balance) -> {
|
||||
if (identifier.startsWith("player:")) { // FIXME: Come on now...
|
||||
playerBalances.put(UUID.fromString(identifier.substring("player:".length())), balance);
|
||||
}
|
||||
});
|
||||
this.balances.forEach((identifier, balance) ->
|
||||
balances.put(this.uuidToName.get(identifier), balance)
|
||||
);
|
||||
|
||||
topPlayerBalances = MapUtil.sortByValue(playerBalances);
|
||||
this.topBalances = MapUtil.sortByValue(balances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Double> getAllBalances() {
|
||||
return ImmutableMap.copyOf(balances);
|
||||
public Map<String, BigDecimal> getAllBalances() {
|
||||
return ImmutableMap.copyOf(this.balances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadEconomable(String uniqueIdentifier, EconomableReloadReason reason) {
|
||||
if (reason == EconomableReloadReason.CROSS_SERVER_SYNC) {
|
||||
SaneEconomy.logger().warning("Trying to reload a single Economable from backend which does not support this - " + this.getClass().getSimpleName() + ". Recommend switching to MySQL backend for multi-server support.");
|
||||
}
|
||||
|
||||
this.reloadDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastName(String uuid) {
|
||||
return this.uuidToName.get(uuid);
|
||||
}
|
||||
}
|
||||
|
@ -1,109 +0,0 @@
|
||||
package org.appledash.saneeconomy.economy.backend.type;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class EconomyStorageBackendFlatfile extends EconomyStorageBackendCaching {
|
||||
private static final int SCHEMA_VERSION = 2;
|
||||
private final File file;
|
||||
|
||||
public EconomyStorageBackendFlatfile(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadDatabase() {
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
|
||||
int schemaVer = ois.readInt();
|
||||
|
||||
if (schemaVer == 1) {
|
||||
ois.close();
|
||||
loadSchemaVersion1(file);
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaVer != SCHEMA_VERSION) {
|
||||
// ???
|
||||
SaneEconomy.logger().severe("Unrecognized flatfile database version " + schemaVer + ", cannot load database!");
|
||||
return;
|
||||
}
|
||||
|
||||
balances = (HashMap<String, Double>) ois.readObject();
|
||||
|
||||
ois.close();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
SaneEconomy.logger().severe("Failed to load flatfile database!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSchemaVersion1(File file) {
|
||||
SaneEconomy.logger().info("Upgrading flatfile database from version 1.");
|
||||
try {
|
||||
Files.copy(file, new File(file.getParentFile(), file.getName() + "-backup"));
|
||||
SaneEconomy.logger().info("Backed up old flatfile database.");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to back up flatfile database!");
|
||||
}
|
||||
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
|
||||
ois.readInt(); // We already know it's 1.
|
||||
|
||||
|
||||
Map<UUID, Double> oldBalances = (HashMap<UUID, Double>)ois.readObject();
|
||||
oldBalances.forEach((uuid, balance) -> balances.put("player:" + uuid, balance));
|
||||
|
||||
ois.close();
|
||||
|
||||
|
||||
/* Yes, this is kind of bad, but we want to make sure we're loading AND saving the new version of the DB. */
|
||||
saveDatabase();
|
||||
reloadDatabase();
|
||||
} catch (IOException | ClassNotFoundException e) {
|
||||
SaneEconomy.logger().severe("Failed to upgrade flatfile database! Recommend reporting this bug and reverting to an older version of the plugin.");
|
||||
throw new RuntimeException("Failed to upgrade flatfile database!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDatabase() {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
try {
|
||||
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
|
||||
oos.writeInt(SCHEMA_VERSION);
|
||||
oos.writeObject(balances);
|
||||
oos.close();
|
||||
} catch (IOException e) {
|
||||
SaneEconomy.logger().severe("Failed to save flatfile database!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setBalance(Economable player, double newBalance) {
|
||||
balances.put(player.getUniqueIdentifier(), newBalance);
|
||||
saveDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitUntilFlushed() {
|
||||
// Do nothing, database is automatically flushed on every write.
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package org.appledash.saneeconomy.economy.backend.type;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/22/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class EconomyStorageBackendJSON extends EconomyStorageBackendCaching {
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
|
||||
private final File file;
|
||||
|
||||
public EconomyStorageBackendJSON(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBalance(Economable economable, BigDecimal newBalance) {
|
||||
this.balances.put(economable.getUniqueIdentifier(), newBalance);
|
||||
this.uuidToName.put(economable.getUniqueIdentifier(), economable.getName());
|
||||
this.saveDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadDatabase() {
|
||||
if (!this.file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// try to load the old format and convert it
|
||||
// if that fails, load the new format
|
||||
DataHolderOld dataHolder = this.gson.fromJson(new FileReader(this.file), DataHolderOld.class);
|
||||
this.balances = new ConcurrentHashMap<>();
|
||||
this.uuidToName = new ConcurrentHashMap<>(dataHolder.uuidToName);
|
||||
|
||||
dataHolder.balances.forEach((s, bal) ->
|
||||
this.balances.put(s, new BigDecimal(bal))
|
||||
);
|
||||
|
||||
this.saveDatabase();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("Failed to load database!", e);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
DataHolder dataHolder = this.gson.fromJson(new FileReader(this.file), DataHolder.class);
|
||||
this.balances = new ConcurrentHashMap<>(dataHolder.balances);
|
||||
this.uuidToName = new ConcurrentHashMap<>(dataHolder.uuidToName);
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new RuntimeException("Failed to load database!", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitUntilFlushed() {
|
||||
// NOOP - Database is saved on every change.
|
||||
}
|
||||
|
||||
private synchronized void saveDatabase() {
|
||||
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(this.file, false))) {
|
||||
DataHolder dataHolder = new DataHolder(this.balances, this.uuidToName);
|
||||
bufferedWriter.write(this.gson.toJson(dataHolder));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save database", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"FieldMayBeFinal", "CanBeFinal"})
|
||||
private static class DataHolderOld {
|
||||
@SerializedName("balances")
|
||||
private Map<String, Double> balances;
|
||||
@SerializedName("uuidToName")
|
||||
private Map<String, String> uuidToName;
|
||||
|
||||
DataHolderOld(Map<String, Double> balances, Map<String, String> uuidToName) {
|
||||
this.balances = balances;
|
||||
this.uuidToName = uuidToName;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private static class DataHolder {
|
||||
@SerializedName("balances")
|
||||
private Map<String, BigDecimal> balances;
|
||||
@SerializedName("uuidToName")
|
||||
private Map<String, String> uuidToName;
|
||||
|
||||
DataHolder(Map<String, BigDecimal> balances, Map<String, String> uuidToName) {
|
||||
this.balances = balances;
|
||||
this.uuidToName = uuidToName;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +1,88 @@
|
||||
package org.appledash.saneeconomy.economy.backend.type;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.appledash.saneeconomy.utils.database.MySQLConnection;
|
||||
import org.appledash.sanelib.database.DatabaseCredentials;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
|
||||
private final String dbUrl;
|
||||
private final String dbUser;
|
||||
private final String dbPassword;
|
||||
private final Set<String> writingUsers = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
private static final Logger LOGGER = Logger.getLogger("EconomyStorageBackendMySQL");
|
||||
private static final String SANEECONOMY_BALANCES = "saneeconomy_balances";
|
||||
private static final String SANEECONOMY_SCHEMA = "saneeconomy_schema";
|
||||
|
||||
public EconomyStorageBackendMySQL(String dbUrl, String dbUser, String dbPassword) {
|
||||
this.dbUrl = dbUrl;
|
||||
this.dbUser = dbUser;
|
||||
this.dbPassword = dbPassword;
|
||||
|
||||
try {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("No MySQL driver found.");
|
||||
}
|
||||
static {
|
||||
LOGGER.setLevel(Level.FINEST);
|
||||
}
|
||||
private final MySQLConnection dbConn;
|
||||
|
||||
private Connection openConnection() {
|
||||
try {
|
||||
return DriverManager.getConnection(dbUrl, dbUser, dbPassword);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Database unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean testConnection() {
|
||||
try (Connection conn = openConnection()) {
|
||||
createTables();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
public EconomyStorageBackendMySQL(DatabaseCredentials dbCredentials) {
|
||||
this.dbConn = new MySQLConnection(dbCredentials);
|
||||
}
|
||||
|
||||
private void createTables() {
|
||||
try (Connection conn = openConnection()) {
|
||||
try (Connection conn = this.dbConn.openConnection()) {
|
||||
int schemaVersion;
|
||||
if (!checkTableExists("saneeconomy_schema")) {
|
||||
if (checkTableExists("player_balances")) {
|
||||
schemaVersion = 1;
|
||||
} else {
|
||||
schemaVersion = 0;
|
||||
}
|
||||
|
||||
if (!this.checkTableExists(this.dbConn.getTable(SANEECONOMY_SCHEMA))) {
|
||||
schemaVersion = -1;
|
||||
} else {
|
||||
PreparedStatement ps = conn.prepareStatement("SELECT `val` FROM saneeconomy_schema WHERE `key` = 'schema_version'");
|
||||
ps.executeQuery();
|
||||
ResultSet rs = ps.getResultSet();
|
||||
PreparedStatement ps = conn.prepareStatement(String.format("SELECT `val` FROM `%s` WHERE `key` = 'schema_version'", this.dbConn.getTable(SANEECONOMY_SCHEMA)));
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if (!rs.next()) {
|
||||
throw new RuntimeException("Invalid database schema!");
|
||||
}
|
||||
|
||||
schemaVersion = Integer.valueOf(rs.getString("val"));
|
||||
schemaVersion = Integer.parseInt(rs.getString("val"));
|
||||
}
|
||||
|
||||
if (schemaVersion < 2) {
|
||||
if (schemaVersion < 1) {
|
||||
PreparedStatement ps = conn.prepareStatement("CREATE TABLE IF NOT EXISTS `player_balances` (player_uuid CHAR(36), balance DECIMAL(18, 2))");
|
||||
ps.executeUpdate();
|
||||
}
|
||||
conn.prepareStatement("CREATE TABLE IF NOT EXISTS `saneeconomy_schema` (`key` VARCHAR(32) PRIMARY KEY, `val` TEXT)").executeUpdate();
|
||||
upgradeSchema1To2(conn);
|
||||
if (schemaVersion == -1) {
|
||||
conn.prepareStatement(String.format("CREATE TABLE IF NOT EXISTS `%s` (`key` VARCHAR(32) PRIMARY KEY, `val` TEXT)", this.dbConn.getTable(SANEECONOMY_SCHEMA))).executeUpdate();
|
||||
conn.prepareStatement(String.format("REPLACE INTO %s (`key`, `val`) VALUES ('schema_version', 4)", this.dbConn.getTable(SANEECONOMY_SCHEMA))).executeUpdate();
|
||||
conn.prepareStatement(String.format("CREATE TABLE `%s` (unique_identifier VARCHAR(128) PRIMARY KEY, last_name VARCHAR(16), balance TEXT)", this.dbConn.getTable(SANEECONOMY_BALANCES))).executeUpdate();
|
||||
schemaVersion = 4;
|
||||
}
|
||||
|
||||
if (schemaVersion == 2) {
|
||||
conn.prepareStatement(String.format("ALTER TABLE `%s` ADD `last_name` VARCHAR(16)", this.dbConn.getTable(SANEECONOMY_BALANCES))).executeUpdate();
|
||||
conn.prepareStatement(String.format("REPLACE INTO %s (`key`, `val`) VALUES ('schema_version', 3)", this.dbConn.getTable(SANEECONOMY_SCHEMA))).executeUpdate();
|
||||
|
||||
schemaVersion = 3;
|
||||
}
|
||||
|
||||
if (schemaVersion == 3) {
|
||||
conn.prepareStatement(String.format("ALTER TABLE `%s` ADD `balance_new` TEXT", this.dbConn.getTable(SANEECONOMY_BALANCES))).executeUpdate();
|
||||
conn.prepareStatement(String.format("UPDATE `%s` SET balance_new = balance", this.dbConn.getTable(SANEECONOMY_BALANCES))).executeUpdate();
|
||||
conn.prepareStatement(String.format("ALTER TABLE `%s` DROP COLUMN `balance`", this.dbConn.getTable(SANEECONOMY_BALANCES))).executeUpdate();
|
||||
conn.prepareStatement(String.format("ALTER TABLE `%s` CHANGE COLUMN `balance_new` `balance` TEXT", this.dbConn.getTable(SANEECONOMY_BALANCES))).executeUpdate();
|
||||
conn.prepareStatement(String.format("REPLACE INTO %s (`key`, `val`) VALUES ('schema_version', 4)", this.dbConn.getTable(SANEECONOMY_SCHEMA))).executeUpdate();
|
||||
|
||||
schemaVersion = 4;
|
||||
}
|
||||
|
||||
if (schemaVersion != 4) {
|
||||
throw new RuntimeException("Invalid database schema version!");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed to create tables!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void upgradeSchema1To2(Connection conn) throws SQLException {
|
||||
SaneEconomy.logger().info("Upgrading database schema from version 1 to version 2. This might take a little while...");
|
||||
PreparedStatement ps = conn.prepareStatement("REPLACE INTO `saneeconomy_schema` (`key`, `val`) VALUES ('schema_version', '2')");
|
||||
ps.executeUpdate();
|
||||
conn.prepareStatement("CREATE TABLE `saneeconomy_balances` (unique_identifier VARCHAR(128) PRIMARY KEY, balance DECIMAL(18, 2))").executeUpdate();
|
||||
ps = conn.prepareStatement("SELECT * FROM `player_balances`");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
Map<String, Double> oldBalances = new HashMap<>();
|
||||
|
||||
while (rs.next()) {
|
||||
oldBalances.put(rs.getString("player_uuid"), rs.getDouble("balance"));
|
||||
}
|
||||
|
||||
for (Entry<String, Double> e : oldBalances.entrySet()) {
|
||||
ps = conn.prepareStatement("INSERT INTO `saneeconomy_balances` (unique_identifier, balance) VALUES (?, ?)");
|
||||
ps.setString(1, "player:" + e.getKey());
|
||||
ps.setDouble(2, e.getValue());
|
||||
ps.executeUpdate();
|
||||
}
|
||||
reloadDatabase();
|
||||
SaneEconomy.logger().info("Schema upgrade complete!");
|
||||
}
|
||||
|
||||
private boolean checkTableExists(String tableName) {
|
||||
try (Connection conn = openConnection()) {
|
||||
try (Connection conn = this.dbConn.openConnection()) {
|
||||
PreparedStatement ps = conn.prepareStatement("SELECT * FROM information_schema.tables WHERE table_schema = ? AND table_name = ? LIMIT 1");
|
||||
ps.setString(1, dbUrl.substring("jdbc:mysql://".length()).split("/")[1]); // FIXME: There has to be a better way.
|
||||
ps.setString(1, this.dbConn.getCredentials().getDatabaseName());
|
||||
ps.setString(2, tableName);
|
||||
ps.executeQuery();
|
||||
ResultSet rs = ps.getResultSet();
|
||||
@ -125,15 +94,18 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadDatabase() {
|
||||
try (Connection conn = openConnection()) {
|
||||
PreparedStatement ps = conn.prepareStatement("SELECT * FROM `saneeconomy_balances`");
|
||||
public synchronized void reloadDatabase() {
|
||||
this.waitUntilFlushed();
|
||||
this.createTables();
|
||||
try (Connection conn = this.dbConn.openConnection()) {
|
||||
PreparedStatement ps = this.dbConn.prepareStatement(conn, String.format("SELECT * FROM `%s`", this.dbConn.getTable(SANEECONOMY_BALANCES)));
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
balances.clear();
|
||||
this.balances.clear();
|
||||
|
||||
while (rs.next()) {
|
||||
balances.put(rs.getString("unique_identifier"), rs.getDouble("balance"));
|
||||
this.balances.put(rs.getString("unique_identifier"), new BigDecimal(rs.getString("balance")));
|
||||
this.uuidToName.put(rs.getString("unique_identifier"), rs.getString("last_name"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed to reload data from SQL.", e);
|
||||
@ -141,39 +113,39 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setBalance(final Economable economable, final double newBalance) {
|
||||
final double oldBalance = getBalance(economable);
|
||||
balances.put(economable.getUniqueIdentifier(), newBalance);
|
||||
public void setBalance(Economable economable, BigDecimal newBalance) {
|
||||
BigDecimal oldBalance = this.getBalance(economable);
|
||||
this.balances.put(economable.getUniqueIdentifier(), newBalance);
|
||||
this.uuidToName.put(economable.getUniqueIdentifier(), economable.getName());
|
||||
|
||||
Bukkit.getServer().getScheduler().scheduleAsyncDelayedTask(SaneEconomy.getInstance(), () -> {
|
||||
waitForSlot(); // Don't run too many database operations at once.
|
||||
writingUsers.add(economable.getUniqueIdentifier());
|
||||
try (Connection conn = openConnection()) {
|
||||
ensureAccountExists(economable, conn);
|
||||
PreparedStatement statement = conn.prepareStatement("UPDATE `saneeconomy_balances` SET balance = ? WHERE `unique_identifier` = ?");
|
||||
statement.setDouble(1, newBalance);
|
||||
statement.setString(2, economable.getUniqueIdentifier());
|
||||
this.dbConn.executeAsyncOperation("set_balance_" + economable.getUniqueIdentifier(), (conn) -> {
|
||||
try {
|
||||
this.ensureAccountExists(economable, conn);
|
||||
this.dbConn.lockTable(conn, SANEECONOMY_BALANCES);
|
||||
PreparedStatement statement = this.dbConn.prepareStatement(conn, String.format("UPDATE `%s` SET balance = ?, last_name = ? WHERE `unique_identifier` = ?", this.dbConn.getTable(SANEECONOMY_BALANCES)));
|
||||
statement.setString(1, newBalance.toString());
|
||||
statement.setString(2, economable.getName());
|
||||
statement.setString(3, economable.getUniqueIdentifier());
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
/* Roll it back */
|
||||
balances.put(economable.getUniqueIdentifier(), oldBalance);
|
||||
this.dbConn.unlockTables(conn);
|
||||
} catch (Exception e) {
|
||||
this.balances.put(economable.getUniqueIdentifier(), oldBalance);
|
||||
throw new RuntimeException("SQL error has occurred.", e);
|
||||
} finally {
|
||||
writingUsers.remove(economable.getUniqueIdentifier());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private synchronized void ensureAccountExists(Economable economable, Connection conn) throws SQLException {
|
||||
if (!accountExists(economable, conn)) {
|
||||
PreparedStatement statement = conn.prepareStatement("INSERT INTO `saneeconomy_balances` (unique_identifier, balance) VALUES (?, 0.0)");
|
||||
private void ensureAccountExists(Economable economable, Connection conn) throws SQLException {
|
||||
if (!this.accountExists(economable, conn)) {
|
||||
PreparedStatement statement = this.dbConn.prepareStatement(conn, String.format("INSERT INTO `%s` (unique_identifier, last_name, balance) VALUES (?, ?, 0.0)", this.dbConn.getTable(SANEECONOMY_BALANCES)));
|
||||
statement.setString(1, economable.getUniqueIdentifier());
|
||||
statement.setString(2, economable.getName());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized boolean accountExists(Economable economable, Connection conn) throws SQLException {
|
||||
PreparedStatement statement = conn.prepareStatement("SELECT 1 FROM `saneeconomy_balances` WHERE `unique_identifier` = ?");
|
||||
private boolean accountExists(Economable economable, Connection conn) throws SQLException {
|
||||
PreparedStatement statement = this.dbConn.prepareStatement(conn, String.format("SELECT 1 FROM `%s` WHERE `unique_identifier` = ?", this.dbConn.getTable(SANEECONOMY_BALANCES)));
|
||||
statement.setString(1, economable.getUniqueIdentifier());
|
||||
|
||||
ResultSet rs = statement.executeQuery();
|
||||
@ -181,24 +153,34 @@ public class EconomyStorageBackendMySQL extends EconomyStorageBackendCaching {
|
||||
return rs.next();
|
||||
}
|
||||
|
||||
private void waitForSlot() {
|
||||
while (writingUsers.size() >= 5) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitUntilFlushed() {
|
||||
while (!writingUsers.isEmpty()) {
|
||||
this.dbConn.waitUntilFlushed();
|
||||
}
|
||||
|
||||
public MySQLConnection getConnection() {
|
||||
return this.dbConn;
|
||||
}
|
||||
|
||||
public void closeConnections() {
|
||||
this.dbConn.getConnection().cleanup();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void reloadEconomable(String uniqueIdentifier, EconomableReloadReason reason) {
|
||||
this.dbConn.executeAsyncOperation("reload_economable_" + uniqueIdentifier, (conn) -> {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
PreparedStatement ps = conn.prepareStatement(String.format("SELECT balance FROM `%s` WHERE `unique_identifier` = ?", this.dbConn.getTable(SANEECONOMY_BALANCES)));
|
||||
ps.setString(1, uniqueIdentifier);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
this.balances.put(uniqueIdentifier, new BigDecimal(rs.getString("balance")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("SQL error has occured", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,39 @@
|
||||
package org.appledash.saneeconomy.economy.economable;
|
||||
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/19/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public interface Economable {
|
||||
Economable CONSOLE = new EconomableConsole();
|
||||
Economable PLUGIN = new EconomablePlugin();
|
||||
|
||||
String getUniqueIdentifier();
|
||||
String getName();
|
||||
default OfflinePlayer tryCastToPlayer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
static Economable wrap(OfflinePlayer player) {
|
||||
return new EconomablePlayer(player);
|
||||
}
|
||||
|
||||
static Economable wrap(CommandSender sender) {
|
||||
if (sender instanceof OfflinePlayer) {
|
||||
return wrap(((OfflinePlayer) sender));
|
||||
}
|
||||
|
||||
return CONSOLE;
|
||||
}
|
||||
|
||||
static Economable wrap(Player player) {
|
||||
return wrap(((OfflinePlayer) player));
|
||||
}
|
||||
|
||||
static Economable wrap(String identifier) {
|
||||
if (identifier.startsWith("faction-")) {
|
||||
return new EconomableFaction(identifier.replace("faction-", ""));
|
||||
|
@ -0,0 +1,32 @@
|
||||
package org.appledash.saneeconomy.economy.economable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/21/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class EconomableConsole implements Economable {
|
||||
public static final UUID CONSOLE_UUID = new UUID( 0xf88708c237d84a0bL, 0x944259c68e517557L); // Pregenerated for performance
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "CONSOLE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueIdentifier() {
|
||||
return "console:" + CONSOLE_UUID;
|
||||
}
|
||||
|
||||
public static boolean isConsole(Economable economable) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(economable.getUniqueIdentifier().split(":")[1]);
|
||||
|
||||
// Fast comparison + fix for bugs with older databases
|
||||
return economable == Economable.CONSOLE || (uuid.getLeastSignificantBits() == CONSOLE_UUID.getLeastSignificantBits() || uuid.getMostSignificantBits() == CONSOLE_UUID.getMostSignificantBits());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,12 @@ public class EconomableFaction implements Economable {
|
||||
|
||||
@Override
|
||||
public String getUniqueIdentifier() {
|
||||
return "faction:" + factionUuid;
|
||||
return "faction:" + this.factionUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
// FIXME
|
||||
return this.factionUuid;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,11 @@ public class EconomableGeneric implements Economable {
|
||||
|
||||
@Override
|
||||
public String getUniqueIdentifier() {
|
||||
return uniqueIdentifier;
|
||||
return this.uniqueIdentifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.uniqueIdentifier.substring(16); // FIXME: Why 16?
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,18 @@ public class EconomablePlayer implements Economable {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.handle.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueIdentifier() {
|
||||
return "player:" + handle.getUniqueId();
|
||||
return "player:" + this.handle.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflinePlayer tryCastToPlayer() {
|
||||
return this.handle;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package org.appledash.saneeconomy.economy.economable;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/21/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class EconomablePlugin implements Economable {
|
||||
@Override
|
||||
public String getUniqueIdentifier() {
|
||||
return "PLUGIN";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
// FIXME
|
||||
return "PLUGIN";
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package org.appledash.saneeconomy.economy.logger;
|
||||
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 8/15/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public interface TransactionLogger {
|
||||
void logTransaction(Transaction transaction);
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package org.appledash.saneeconomy.economy.logger;
|
||||
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomy.utils.database.MySQLConnection;
|
||||
import org.appledash.sanelib.database.DatabaseCredentials;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/20/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class TransactionLoggerMySQL implements TransactionLogger {
|
||||
private final MySQLConnection dbConn;
|
||||
|
||||
public TransactionLoggerMySQL(DatabaseCredentials credentials) {
|
||||
this.dbConn = new MySQLConnection(credentials);
|
||||
}
|
||||
|
||||
private void logGeneric(String from, String to, BigDecimal change, TransactionReason reason) {
|
||||
this.dbConn.executeAsyncOperation("log_transaction", (conn) -> {
|
||||
try {
|
||||
PreparedStatement ps = conn.prepareStatement(String.format("INSERT INTO `%s` (`source`, `destination`, `amount`, `reason`) VALUES (?, ?, ?, ?)", this.dbConn.getTable("transaction_logs")));
|
||||
ps.setString(1, from);
|
||||
ps.setString(2, to);
|
||||
ps.setString(3, change.toString());
|
||||
ps.setString(4, reason.toString());
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Error occurred logging addition", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean testConnection() {
|
||||
if (this.dbConn.testConnection()) {
|
||||
this.createTables();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createTables() {
|
||||
try (Connection conn = this.dbConn.openConnection()) {
|
||||
PreparedStatement ps = conn.prepareStatement(String.format("CREATE TABLE IF NOT EXISTS `%s` (`source` VARCHAR(128), `destination` VARCHAR(128), `amount` DECIMAL(18, 2), `reason` VARCHAR(128), `logged` TIMESTAMP NOT NULL default CURRENT_TIMESTAMP)", this.dbConn.getTable("transaction_logs")));
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed to create transaction logger tables", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logTransaction(Transaction transaction) {
|
||||
this.logGeneric(transaction.getSender().getUniqueIdentifier(), transaction.getReceiver().getUniqueIdentifier(), transaction.getAmount(), transaction.getReason());
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package org.appledash.saneeconomy.economy.transaction;
|
||||
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.economable.EconomableConsole;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason.AffectedParties;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/21/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class Transaction {
|
||||
private final Economable sender;
|
||||
private final Economable receiver;
|
||||
private final BigDecimal amount;
|
||||
private final TransactionReason reason;
|
||||
|
||||
public Transaction(Currency currency, Economable sender, Economable receiver, BigDecimal amount, TransactionReason reason) {
|
||||
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new IllegalArgumentException("Cannot transact a zero or negative amount!");
|
||||
}
|
||||
|
||||
|
||||
this.sender = sender;
|
||||
this.receiver = receiver;
|
||||
this.amount = NumberUtils.filterAmount(currency, amount);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public Economable getSender() {
|
||||
return this.sender;
|
||||
}
|
||||
|
||||
public Economable getReceiver() {
|
||||
return this.receiver;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public TransactionReason getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
public boolean isSenderAffected() {
|
||||
if (EconomableConsole.isConsole(this.sender)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this.reason.getAffectedParties() == AffectedParties.SENDER) || (this.reason.getAffectedParties() == AffectedParties.BOTH);
|
||||
}
|
||||
|
||||
public boolean isReceiverAffected() {
|
||||
if (EconomableConsole.isConsole(this.receiver)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this.reason.getAffectedParties() == AffectedParties.RECEIVER) || (this.reason.getAffectedParties() == AffectedParties.BOTH);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.appledash.saneeconomy.economy.transaction;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 8/15/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public enum TransactionReason {
|
||||
/**
|
||||
* A player paying another player.
|
||||
*/
|
||||
PLAYER_PAY(AffectedParties.BOTH),
|
||||
/**
|
||||
* An admin giving a player money.
|
||||
*/
|
||||
ADMIN_GIVE(AffectedParties.RECEIVER),
|
||||
ADMIN_TAKE(AffectedParties.SENDER),
|
||||
/**
|
||||
* Another plugin using the API.
|
||||
*/
|
||||
PLUGIN_GIVE(AffectedParties.RECEIVER),
|
||||
PLUGIN_TAKE(AffectedParties.SENDER),
|
||||
/**
|
||||
* Initial starting balance on join.
|
||||
*/
|
||||
STARTING_BALANCE(AffectedParties.RECEIVER),
|
||||
/**
|
||||
* Used in unit tests.
|
||||
*/
|
||||
TEST_GIVE(AffectedParties.RECEIVER),
|
||||
TEST_TAKE(AffectedParties.SENDER);
|
||||
|
||||
private final AffectedParties affectedParties;
|
||||
|
||||
TransactionReason(AffectedParties affectedParties) {
|
||||
this.affectedParties = affectedParties;
|
||||
}
|
||||
|
||||
public AffectedParties getAffectedParties() {
|
||||
return this.affectedParties;
|
||||
}
|
||||
|
||||
public enum AffectedParties {
|
||||
SENDER,
|
||||
RECEIVER,
|
||||
BOTH
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.appledash.saneeconomy.economy.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/21/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class TransactionResult {
|
||||
private final Transaction transaction;
|
||||
private final BigDecimal fromBalance;
|
||||
private final BigDecimal toBalance;
|
||||
private Status status;
|
||||
|
||||
public TransactionResult(Transaction transaction, BigDecimal fromBalance, BigDecimal toBalance) {
|
||||
this.transaction = transaction;
|
||||
this.fromBalance = fromBalance;
|
||||
this.toBalance = toBalance;
|
||||
this.status = Status.SUCCESS;
|
||||
}
|
||||
|
||||
public TransactionResult(Transaction transaction, Status status) {
|
||||
this(transaction, BigDecimal.ONE.negate(), BigDecimal.ONE.negate());
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return this.transaction;
|
||||
}
|
||||
|
||||
public BigDecimal getFromBalance() {
|
||||
return this.fromBalance;
|
||||
}
|
||||
|
||||
public BigDecimal getToBalance() {
|
||||
return this.toBalance;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
SUCCESS("Success."),
|
||||
CANCELLED_BY_PLUGIN("That transaction was cancelled by a plugin."), // In theory this message should never be shown.
|
||||
ERR_NOT_ENOUGH_FUNDS("Not enough money is available for you to complete that transaction.");
|
||||
|
||||
private final String message;
|
||||
|
||||
Status(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package org.appledash.saneeconomy.event;
|
||||
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/5/17.
|
||||
* Blackjack is best pony.
|
||||
*
|
||||
* This event is called whenever a Transaction occurs in the plugin. If you cancel this event, the transaction will be cancelled as well.
|
||||
*/
|
||||
public class SaneEconomyTransactionEvent extends Event implements Cancellable {
|
||||
private static final HandlerList handlerList = new HandlerList();
|
||||
private final Transaction transaction;
|
||||
private boolean isCancelled;
|
||||
|
||||
public SaneEconomyTransactionEvent(Transaction transaction) {
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlerList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.isCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.isCancelled = cancelled;
|
||||
}
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return this.transaction;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlerList;
|
||||
}
|
||||
}
|
@ -1,14 +1,21 @@
|
||||
package org.appledash.saneeconomy.listeners;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.backend.EconomyStorageBackend;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomy.updates.GithubVersionChecker;
|
||||
import org.appledash.saneeconomy.utils.MessageUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
@ -23,18 +30,29 @@ public class JoinQuitListener implements Listener {
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent evt) {
|
||||
Player player = evt.getPlayer();
|
||||
Economable economable = Economable.wrap(player);
|
||||
double startBalance = plugin.getConfig().getDouble("economy.start-balance", 0.0D);
|
||||
Economable economable = Economable.wrap((OfflinePlayer) player);
|
||||
BigDecimal startBalance = BigDecimal.valueOf(this.plugin.getConfig().getDouble("economy.start-balance", 0.0D));
|
||||
|
||||
/* A starting balance is configured AND they haven't been given it yet. */
|
||||
if ((startBalance > 0) && !plugin.getEconomyManager().accountExists(economable)) {
|
||||
plugin.getEconomyManager().setBalance(economable, startBalance);
|
||||
MessageUtils.sendMessage(player, "You've been issued a starting balance of %s!", plugin.getEconomyManager().getCurrency().formatAmount(startBalance));
|
||||
if ((startBalance.compareTo(BigDecimal.ZERO) > 0) && !this.plugin.getEconomyManager().accountExists(economable)) {
|
||||
this.plugin.getEconomyManager().transact(new Transaction(
|
||||
this.plugin.getEconomyManager().getCurrency(), Economable.CONSOLE, economable, startBalance, TransactionReason.STARTING_BALANCE
|
||||
));
|
||||
if (this.plugin.getConfig().getBoolean("economy.notify-start-balance", true)) {
|
||||
this.plugin.getMessenger().sendMessage(player, "You've been issued a starting balance of {1}!", this.plugin.getEconomyManager().getCurrency().formatAmount(startBalance));
|
||||
}
|
||||
}
|
||||
|
||||
/* Update notification */
|
||||
if (player.hasPermission("saneeconomy.update-notify") && GithubVersionChecker.isUpdateAvailable()) {
|
||||
MessageUtils.sendMessage(player, "An update is available! The currently-installed version is %s, but the newest available is %s. Please go to %s to update!", plugin.getDescription().getVersion(), GithubVersionChecker.getNewestVersion(), GithubVersionChecker.DOWNLOAD_URL);
|
||||
if ((this.plugin.getVersionChecker() != null) && player.hasPermission("saneeconomy.update-notify") && this.plugin.getVersionChecker().isUpdateAvailable()) {
|
||||
this.plugin.getMessenger().sendMessage(player, "An update is available! The currently-installed version is {1}, but the newest available is {2}. Please go to {3} to update!", this.plugin.getDescription().getVersion(), this.plugin.getVersionChecker().getNewestVersion(), GithubVersionChecker.DOWNLOAD_URL);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerLogin(AsyncPlayerPreLoginEvent evt) {
|
||||
Bukkit.getServer().getScheduler().runTaskAsynchronously(this.plugin, () -> {
|
||||
this.plugin.getEconomyManager().getBackend().reloadEconomable(String.format("player:%s", evt.getUniqueId()), EconomyStorageBackend.EconomableReloadReason.PLAYER_JOIN); // TODO: If servers start to lag when lots of people join, this is why.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.utils.WebUtils;
|
||||
|
||||
/**
|
||||
@ -14,18 +13,25 @@ import org.appledash.saneeconomy.utils.WebUtils;
|
||||
public class GithubVersionChecker {
|
||||
public static final String DOWNLOAD_URL = "https://github.com/AppleDash/SaneEconomy/releases";
|
||||
private static final String RELEASES_URL = "https://api.github.com/repos/AppleDash/SaneEconomy/releases";
|
||||
private static boolean updateChecked;
|
||||
private static boolean updateAvailable;
|
||||
private static String newestVersion;
|
||||
private static String newestFound;
|
||||
|
||||
public static void checkUpdateAvailable() {
|
||||
private boolean updateChecked;
|
||||
private boolean updateAvailable;
|
||||
private final String pluginName;
|
||||
private final String currentVersion;
|
||||
|
||||
public GithubVersionChecker(String pluginName, String currentVersion) {
|
||||
this.pluginName = pluginName;
|
||||
this.currentVersion = currentVersion;
|
||||
}
|
||||
|
||||
public void checkUpdateAvailable() {
|
||||
String jsonContent = WebUtils.getContents(RELEASES_URL);
|
||||
|
||||
JsonArray array = (JsonArray)new JsonParser().parse(jsonContent);
|
||||
|
||||
int currentVersion = releaseToInt(SaneEconomy.getInstance().getDescription().getVersion());
|
||||
int newestVersion = -1;
|
||||
// JsonObject newestObj = null;
|
||||
String currentVersion = this.currentVersion;
|
||||
String newestFound = null;
|
||||
|
||||
for (JsonElement elem : array) {
|
||||
if (elem instanceof JsonObject) {
|
||||
@ -36,30 +42,30 @@ public class GithubVersionChecker {
|
||||
continue;
|
||||
}
|
||||
|
||||
String versionStr = releaseObj.get("tag_name").getAsString();
|
||||
int version = releaseToInt(versionStr);
|
||||
String releaseName = releaseObj.get("name").getAsString().split(" ")[0];
|
||||
|
||||
if (version > newestVersion) {
|
||||
newestVersion = version;
|
||||
GithubVersionChecker.newestVersion = versionStr;
|
||||
// newestObj = releaseObj;
|
||||
if (!releaseName.equalsIgnoreCase(this.pluginName)) { // Not for this plugin.
|
||||
continue;
|
||||
}
|
||||
|
||||
String versionStr = releaseObj.get("tag_name").getAsString();
|
||||
|
||||
if (VersionComparer.isSemVerGreaterThan(newestFound, versionStr)) {
|
||||
newestFound = versionStr;
|
||||
GithubVersionChecker.newestFound = versionStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateChecked = true;
|
||||
updateAvailable = newestVersion > currentVersion;
|
||||
this.updateChecked = true;
|
||||
this.updateAvailable = VersionComparer.isSemVerGreaterThan(currentVersion, newestFound);
|
||||
}
|
||||
|
||||
private static int releaseToInt(String release) {
|
||||
return Integer.valueOf(release.trim().replace(".", ""));
|
||||
public boolean isUpdateAvailable() {
|
||||
return this.updateChecked && this.updateAvailable;
|
||||
}
|
||||
|
||||
public static boolean isUpdateAvailable() {
|
||||
return updateChecked && updateAvailable;
|
||||
}
|
||||
|
||||
public static String getNewestVersion() {
|
||||
return newestVersion;
|
||||
public String getNewestVersion() {
|
||||
return newestFound;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,35 @@
|
||||
package org.appledash.saneeconomy.updates;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/15/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public final class VersionComparer {
|
||||
private VersionComparer() {
|
||||
}
|
||||
|
||||
public static boolean isSemVerGreaterThan(String first, String second) {
|
||||
if (first == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (second == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] firstParts = intifyParts(first);
|
||||
int[] secondParts = intifyParts(second);
|
||||
|
||||
return computeInt(secondParts) > computeInt(firstParts);
|
||||
}
|
||||
|
||||
private static int[] intifyParts(String version) {
|
||||
String[] firstParts = version.split("\\.");
|
||||
|
||||
return new int[] { Integer.parseInt(firstParts[0]), Integer.parseInt(firstParts[1]), Integer.parseInt(firstParts[2]) };
|
||||
}
|
||||
|
||||
private static int computeInt(int[] parts) {
|
||||
return (parts[0] * 1000000) + (parts[1] * 1000) + parts[2];
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package org.appledash.saneeconomy.utils;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 8/5/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class I18n {
|
||||
private static final I18n INSTANCE = new I18n(SaneEconomy.getInstance());
|
||||
private final SaneEconomy plugin;
|
||||
private final Map<String, String> translations = new HashMap<>();
|
||||
|
||||
private I18n(SaneEconomy plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void loadTranslations() {
|
||||
File configFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
YamlConfiguration configJar = YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getResourceAsStream("/messages.yml")));
|
||||
|
||||
if (configFile.exists()) { // Attempt to merge any new keys from the JAR's messages.yml into the copy in the plugin's data folder
|
||||
YamlConfiguration configFileYaml = YamlConfiguration.loadConfiguration(configFile);
|
||||
for (Map jarMap : configJar.getMapList("messages")) {
|
||||
boolean has = false;
|
||||
String key = jarMap.get("message").toString();
|
||||
|
||||
for (Map fileMap : configFileYaml.getMapList("messages")) {
|
||||
if (fileMap.get("message").toString().equals(key)) {
|
||||
has = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has) { // Folder messages.yml does not have this key, add it.
|
||||
List<Map> map = new ArrayList<>(configFileYaml.getMapList("messages"));
|
||||
map.add(ImmutableMap.of("message", key));
|
||||
configFileYaml.set("messages", map);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
configFileYaml.save(configFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save translations file.", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
configJar.save(configFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save initial translations file.", e);
|
||||
}
|
||||
}
|
||||
|
||||
YamlConfiguration configFileYaml = YamlConfiguration.loadConfiguration(configFile);
|
||||
configFileYaml.getMapList("messages").stream().filter(map -> map.containsKey("translation")).forEach(map -> {
|
||||
translations.put(map.get("message").toString(), map.get("translation").toString());
|
||||
});
|
||||
}
|
||||
|
||||
private String translate(String input) {
|
||||
return translations.containsKey(input) ? translations.get(input) : input;
|
||||
}
|
||||
|
||||
public static String _(String s) {
|
||||
return INSTANCE.translate(s);
|
||||
}
|
||||
|
||||
public static I18n getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
@ -6,30 +6,74 @@ import java.util.*;
|
||||
* Created by appledash on 7/11/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class MapUtil {
|
||||
/* Originally found on StackOverflow: http://stackoverflow.com/a/2581754/1849152 */
|
||||
public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
|
||||
List<Map.Entry<K, V>> list =
|
||||
new LinkedList<>(map.entrySet());
|
||||
Collections.sort(list, (o1, o2) -> -((o1.getValue()).compareTo(o2.getValue())));
|
||||
public final class MapUtil {
|
||||
private MapUtil() {
|
||||
}
|
||||
|
||||
Map<K, V> result = new LinkedHashMap<>();
|
||||
/* Originally found on StackOverflow: http://stackoverflow.com/a/2581754/1849152 */
|
||||
public static <K, V extends Comparable<? super V>> LinkedHashMap<K, V> sortByValue(Map<K, V> map) {
|
||||
List<Map.Entry<K, V>> list =
|
||||
new LinkedList<>(map.entrySet());
|
||||
|
||||
list.sort((o1, o2) -> -((o1.getValue()).compareTo(o2.getValue())));
|
||||
|
||||
LinkedHashMap<K, V> result = new LinkedHashMap<>();
|
||||
for (Map.Entry<K, V> entry : list) {
|
||||
result.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <K, V> Map<K, V> takeFromMap(Map<K, V> map, int amount) {
|
||||
Map<K, V> newMap = new LinkedHashMap<>();
|
||||
/**
|
||||
* "Skip" the given number of items in a LinkedHashMap and return a new LinkedHashMap with the remaining items.
|
||||
* @param map Map
|
||||
* @param nSkip Number of items to skip
|
||||
* @return New LinkedHashMap, may be empty.
|
||||
*/
|
||||
@SuppressWarnings("CollectionDeclaredAsConcreteClass")
|
||||
public static <K, V> LinkedHashMap<K, V> skip(LinkedHashMap<K, V> map, int nSkip) {
|
||||
LinkedHashMap<K, V> newMap = new LinkedHashMap<>();
|
||||
|
||||
if (map.size() <= nSkip) {
|
||||
return newMap;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
if (newMap.size() > amount) {
|
||||
break;
|
||||
if (i >= nSkip) {
|
||||
newMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
newMap.put(entry.getKey(), entry.getValue());
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
public static <K, V> LinkedHashMap<K, V> take(LinkedHashMap<K, V> map, int nTake) {
|
||||
LinkedHashMap<K, V> newMap = new LinkedHashMap<>();
|
||||
|
||||
if (map.size() <= nTake) {
|
||||
return map;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (Map.Entry<K, V> entry : map.entrySet()) {
|
||||
if (i >= nTake) {
|
||||
break;
|
||||
}
|
||||
|
||||
newMap.put(entry.getKey(), entry.getValue());
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return newMap;
|
||||
}
|
||||
|
||||
public static <K, V> LinkedHashMap<K, V> skipAndTake(LinkedHashMap<K, V> map, int nSkip, int nTake) {
|
||||
return take(skip(map, nSkip), nTake);
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
package org.appledash.saneeconomy.utils;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import static org.appledash.saneeconomy.utils.I18n._;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 6/13/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class MessageUtils {
|
||||
/**
|
||||
* Send a formatted chat message to the given target.
|
||||
* This message will have the prefix defined in SaneEconomy's config file.
|
||||
* @param target Target CommandSender
|
||||
* @param fmt String#format format
|
||||
* @param args String#format args
|
||||
*/
|
||||
public static void sendMessage(CommandSender target, String fmt, Object... args) {
|
||||
fmt = _(fmt);
|
||||
String prefix = ChatColor.translateAlternateColorCodes('&', SaneEconomy.getInstance().getConfig().getString("chat.prefix", ""));
|
||||
target.sendMessage(prefix + String.format(fmt, (Object[])args));
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package org.appledash.saneeconomy.utils;
|
||||
import com.google.common.base.Strings;
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
|
||||
@ -10,10 +12,13 @@ import java.text.ParseException;
|
||||
* Created by AppleDash on 6/14/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class NumberUtils {
|
||||
private static final double INVALID_DOUBLE = -1;
|
||||
public final class NumberUtils {
|
||||
private static final BigDecimal INVALID_DOUBLE = BigDecimal.ONE.negate();
|
||||
|
||||
public static double parsePositiveDouble(String sDouble) {
|
||||
private NumberUtils() {
|
||||
}
|
||||
|
||||
public static BigDecimal parsePositiveDouble(String sDouble) {
|
||||
if (Strings.isNullOrEmpty(sDouble)) {
|
||||
return INVALID_DOUBLE;
|
||||
}
|
||||
@ -24,30 +29,54 @@ public class NumberUtils {
|
||||
return INVALID_DOUBLE;
|
||||
}
|
||||
|
||||
double doub;
|
||||
BigDecimal doub;
|
||||
|
||||
try {
|
||||
doub = NumberFormat.getInstance().parse(sDouble).doubleValue();
|
||||
doub = (BigDecimal) constructDecimalFormat().parseObject(sDouble);
|
||||
} catch (ParseException | NumberFormatException e) {
|
||||
return INVALID_DOUBLE;
|
||||
}
|
||||
|
||||
if (doub < 0) {
|
||||
if (doub.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return INVALID_DOUBLE;
|
||||
}
|
||||
|
||||
/*if (Double.isInfinite(doub) || Double.isNaN(doub)) {
|
||||
return INVALID_DOUBLE;
|
||||
}*/
|
||||
|
||||
return doub;
|
||||
}
|
||||
|
||||
public static double filterAmount(Currency currency, double amount) {
|
||||
public static BigDecimal filterAmount(Currency currency, BigDecimal amount) {
|
||||
try {
|
||||
return NumberFormat.getInstance().parse(currency.getFormat().format(amount)).doubleValue();
|
||||
return (BigDecimal) constructDecimalFormat().parse(currency.getFormat().format(amount.abs()));
|
||||
} catch (ParseException e) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
public static double parseAndFilter(Currency currency, String sDouble) {
|
||||
public static BigDecimal parseAndFilter(Currency currency, String sDouble) {
|
||||
return filterAmount(currency, parsePositiveDouble(sDouble));
|
||||
}
|
||||
|
||||
public static boolean equals(BigDecimal left, BigDecimal right) {
|
||||
if (left == null) {
|
||||
throw new NullPointerException("left == null");
|
||||
}
|
||||
|
||||
if (right == null) {
|
||||
throw new NullPointerException("right == null");
|
||||
}
|
||||
|
||||
return left.compareTo(right) == 0;
|
||||
}
|
||||
|
||||
private static DecimalFormat constructDecimalFormat() {
|
||||
DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance();
|
||||
|
||||
decimalFormat.setParseBigDecimal(true);
|
||||
|
||||
return decimalFormat;
|
||||
}
|
||||
}
|
||||
|
@ -3,28 +3,59 @@ package org.appledash.saneeconomy.utils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/19/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class PlayerUtils {
|
||||
public final class PlayerUtils {
|
||||
private PlayerUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an online or offline player from Bukkit.
|
||||
* This is guaranteed to be a player who has played before, but is not guaranteed to be currently online.
|
||||
* @param playerName The player's name
|
||||
* @param playerNameOrUUID The player's name or UUID
|
||||
* @return OfflinePlayer object, or null if never played
|
||||
*/
|
||||
public static OfflinePlayer getOfflinePlayer(String playerName) {
|
||||
OfflinePlayer player = Bukkit.getServer().getPlayer(playerName);
|
||||
public static OfflinePlayer getOfflinePlayer(String playerNameOrUUID) {
|
||||
OfflinePlayer player = tryGetFromUUID(playerNameOrUUID);
|
||||
|
||||
if (player == null) {
|
||||
player = Bukkit.getServer().getOfflinePlayer(playerName);
|
||||
if ((player != null) && (player.hasPlayedBefore() || player.isOnline())) {
|
||||
return player;
|
||||
}
|
||||
|
||||
if ((player != null) && !player.hasPlayedBefore()) {
|
||||
//noinspection ReuseOfLocalVariable
|
||||
player = Bukkit.getServer().getPlayer(playerNameOrUUID);
|
||||
|
||||
if (player == null) {
|
||||
player = Bukkit.getServer().getOfflinePlayer(playerNameOrUUID);
|
||||
}
|
||||
|
||||
if ((player != null) && (!player.hasPlayedBefore() && !player.isOnline())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
private static OfflinePlayer tryGetFromUUID(String possibleUUID) {
|
||||
UUID uuid;
|
||||
OfflinePlayer player;
|
||||
|
||||
try {
|
||||
uuid = UUID.fromString(possibleUUID);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return null;
|
||||
}
|
||||
|
||||
player = Bukkit.getServer().getPlayer(uuid);
|
||||
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
return Bukkit.getServer().getOfflinePlayer(uuid);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,164 @@
|
||||
package org.appledash.saneeconomy.utils;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.backend.EconomyStorageBackend;
|
||||
import org.appledash.saneeconomy.economy.backend.type.EconomyStorageBackendJSON;
|
||||
import org.appledash.saneeconomy.economy.backend.type.EconomyStorageBackendMySQL;
|
||||
import org.appledash.saneeconomy.economy.economable.EconomableGeneric;
|
||||
import org.appledash.saneeconomy.economy.logger.TransactionLogger;
|
||||
import org.appledash.saneeconomy.economy.logger.TransactionLoggerMySQL;
|
||||
import org.appledash.sanelib.database.DatabaseCredentials;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/18/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class SaneEconomyConfiguration {
|
||||
private final Logger logger;
|
||||
private final SaneEconomy saneEconomy;
|
||||
private final Configuration rootConfig;
|
||||
|
||||
public SaneEconomyConfiguration(SaneEconomy saneEconomy) {
|
||||
this.saneEconomy = saneEconomy;
|
||||
this.rootConfig = saneEconomy.getConfig();
|
||||
this.logger = saneEconomy.getLogger();
|
||||
}
|
||||
|
||||
public EconomyManager loadEconomyBackend() {
|
||||
this.logger.info("Initializing currency...");
|
||||
Currency currency = Currency.fromConfig(this.rootConfig.getConfigurationSection("currency"));
|
||||
this.logger.info("Initialized currency: " + currency.getPluralName());
|
||||
|
||||
this.logger.info("Initializing economy storage backend...");
|
||||
|
||||
EconomyStorageBackend backend = this.loadBackend(this.rootConfig.getConfigurationSection("backend"));
|
||||
|
||||
if (backend == null) {
|
||||
this.logger.severe("Failed to load backend!");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.logger.info("Performing initial data load...");
|
||||
backend.reloadDatabase();
|
||||
this.logger.info("Data loaded!");
|
||||
|
||||
if (!Strings.isNullOrEmpty(this.rootConfig.getString("old-backend.type", null))) {
|
||||
this.logger.info("Old backend detected, converting... (This may take a minute or two.)");
|
||||
EconomyStorageBackend oldBackend = this.loadBackend(this.rootConfig.getConfigurationSection("old-backend"));
|
||||
if (oldBackend == null) {
|
||||
this.logger.severe("Failed to load old backend!");
|
||||
return null;
|
||||
}
|
||||
|
||||
oldBackend.reloadDatabase();
|
||||
this.convertBackends(oldBackend, backend);
|
||||
this.logger.info("Data converted, removing old config section.");
|
||||
this.rootConfig.set("old-backend", null);
|
||||
}
|
||||
|
||||
return new EconomyManager(this.saneEconomy, currency, backend, this.rootConfig.getString("economy.server-account", null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an EconomyStorageBackend using the information in the given ConfigurationSection.
|
||||
* @param config ConfigurationSection to read connection parameters from
|
||||
* @return Constructed EconomyStorageBackend, or null if something inappropriate happened.
|
||||
*/
|
||||
private EconomyStorageBackend loadBackend(ConfigurationSection config) {
|
||||
EconomyStorageBackend backend;
|
||||
String backendType = config.getString("type");
|
||||
|
||||
if (backendType.equalsIgnoreCase("json")) {
|
||||
String backendFileName = config.getString("file", "economy.json");
|
||||
File backendFile = new File(this.saneEconomy.getDataFolder(), backendFileName);
|
||||
backend = new EconomyStorageBackendJSON(backendFile);
|
||||
this.logger.info("Initialized JSON backend with file " + backendFile.getAbsolutePath());
|
||||
} else if (backendType.equalsIgnoreCase("mysql")) {
|
||||
EconomyStorageBackendMySQL mySQLBackend = new EconomyStorageBackendMySQL(this.loadCredentials(config));
|
||||
|
||||
backend = mySQLBackend;
|
||||
|
||||
this.logger.info("Initialized MySQL backend.");
|
||||
this.logger.info("Testing connection...");
|
||||
if (!mySQLBackend.getConnection().testConnection()) {
|
||||
this.logger.severe("MySQL connection failed - cannot continue!");
|
||||
return null;
|
||||
}
|
||||
|
||||
this.logger.info("Connection successful!");
|
||||
} else {
|
||||
this.logger.severe("Unknown storage backend " + backendType + "!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert one EconomyStorageBackend to another.
|
||||
* Right now, this just consists of converting all player balances. Data in the old backend is kept.
|
||||
* Why is this in here?
|
||||
* @param oldBackend Old backend
|
||||
* @param newBackend New backend
|
||||
*/
|
||||
private void convertBackends(EconomyStorageBackend oldBackend, EconomyStorageBackend newBackend) {
|
||||
oldBackend.getAllBalances().forEach((uniqueId, balance) ->
|
||||
newBackend.setBalance(new EconomableGeneric(uniqueId), balance)
|
||||
);
|
||||
|
||||
newBackend.waitUntilFlushed();
|
||||
}
|
||||
|
||||
public TransactionLogger loadLogger() {
|
||||
if (!this.rootConfig.getBoolean("log-transactions", false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.logger.info("Attempting to load transaction logger...");
|
||||
|
||||
if (this.rootConfig.getConfigurationSection("logger-database") == null) {
|
||||
this.logger.severe("No transaction logger database defined, cannot possibly connect!");
|
||||
return null;
|
||||
}
|
||||
|
||||
DatabaseCredentials credentials = this.loadCredentials(this.rootConfig.getConfigurationSection("logger-database"));
|
||||
|
||||
TransactionLoggerMySQL transactionLogger = new TransactionLoggerMySQL(credentials);
|
||||
|
||||
if (transactionLogger.testConnection()) {
|
||||
this.logger.info("Initialized MySQL transaction logger.");
|
||||
return transactionLogger;
|
||||
}
|
||||
|
||||
this.logger.severe("Failed to connect to MySQL database for transaction logger!");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load database host, port, username, password, and db name from a ConfigurationSection
|
||||
* @param config ConfigurationSection containing the right fields.
|
||||
* @return DatabaseCredentials with the information from the config.
|
||||
*/
|
||||
private DatabaseCredentials loadCredentials(ConfigurationSection config) {
|
||||
String databaseType = config.getString("type", "mysql");
|
||||
String backendHost = config.getString("host");
|
||||
int backendPort = config.getInt("port", 3306);
|
||||
String backendDb = config.getString("database");
|
||||
String backendUser = config.getString("username");
|
||||
String backendPass = config.getString("password");
|
||||
String tablePrefix = config.getString("table_prefix", "");
|
||||
boolean useSsl = config.getBoolean("use_ssl", false);
|
||||
|
||||
return new DatabaseCredentials(
|
||||
databaseType, backendHost, backendPort, backendUser, backendPass, backendDb, tablePrefix, useSsl
|
||||
);
|
||||
}
|
||||
}
|
@ -11,23 +11,27 @@ import java.net.URL;
|
||||
* Created by appledash on 7/11/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class WebUtils {
|
||||
public final class WebUtils {
|
||||
private WebUtils() {
|
||||
}
|
||||
|
||||
public static String getContents(String url) {
|
||||
try {
|
||||
String out = "";
|
||||
StringBuilder out = new StringBuilder();
|
||||
URL uri = new URL(url);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(uri.openConnection().getInputStream()));
|
||||
String line;
|
||||
|
||||
//noinspection NestedAssignment
|
||||
while ((line = br.readLine()) != null) {
|
||||
out += line + "\n";
|
||||
out.append(line).append("\n");
|
||||
}
|
||||
|
||||
return out;
|
||||
br.close();
|
||||
|
||||
return out.toString();
|
||||
} catch (IOException e) {
|
||||
SaneEconomy.logger().warning("Failed to get contents of URL " + url);
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Failed to get URL contents!");
|
||||
throw new RuntimeException("Failed to get URL contents!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,124 @@
|
||||
package org.appledash.saneeconomy.utils.database;
|
||||
|
||||
import org.appledash.sanelib.database.DatabaseCredentials;
|
||||
import org.appledash.sanelib.database.SaneDatabase;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/19/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class MySQLConnection {
|
||||
private static final Logger LOGGER = Logger.getLogger("MySQLConnection");
|
||||
public static final int FIVE_SECONDS = 5000;
|
||||
private final DatabaseCredentials dbCredentials;
|
||||
private final SaneDatabase saneDatabase;
|
||||
private boolean canLockTables = true;
|
||||
|
||||
public MySQLConnection(DatabaseCredentials dbCredentials) {
|
||||
this.dbCredentials = dbCredentials;
|
||||
this.saneDatabase = new SaneDatabase(dbCredentials);
|
||||
}
|
||||
|
||||
public Connection openConnection() {
|
||||
try {
|
||||
return this.saneDatabase.getConnection();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Database unavailable.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException {
|
||||
PreparedStatement preparedStatement = conn.prepareStatement(sql);
|
||||
|
||||
preparedStatement.setQueryTimeout(this.dbCredentials.getQueryTimeout()); // 5 second timeout
|
||||
|
||||
return preparedStatement;
|
||||
}
|
||||
|
||||
public boolean testConnection() {
|
||||
try (Connection ignored = this.openConnection()) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void lockTable(Connection conn, String tableName) throws SQLException {
|
||||
if (!this.canLockTables) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
conn.prepareStatement("LOCK TABLE " + this.getTable(tableName) + " WRITE").execute();
|
||||
this.canLockTables = true;
|
||||
} catch (SQLException e) {
|
||||
if (this.canLockTables) {
|
||||
LOGGER.warning("Your MySQL user does not have privileges to LOCK TABLES - this may cause issues if you are running this plugin with the same database on multiple servers.");
|
||||
}
|
||||
|
||||
this.canLockTables = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void unlockTables(Connection conn) throws SQLException {
|
||||
if (!this.canLockTables) {
|
||||
return;
|
||||
}
|
||||
|
||||
conn.prepareStatement("UNLOCK TABLES").execute();
|
||||
}
|
||||
|
||||
public void executeAsyncOperation(String tag, Consumer<Connection> callback) {
|
||||
this.saneDatabase.runDatabaseOperationAsync(tag, () -> this.doExecuteAsyncOperation(1, callback));
|
||||
}
|
||||
|
||||
// This is a bit weird because it has to account for recursion...
|
||||
private void doExecuteAsyncOperation(int levels, Consumer<Connection> callback) {
|
||||
try (Connection conn = this.openConnection()) {
|
||||
callback.accept(conn);
|
||||
} catch (Exception e) {
|
||||
if (levels > this.dbCredentials.getMaxRetries()) {
|
||||
throw new RuntimeException("This shouldn't happen (database error)", e);
|
||||
}
|
||||
|
||||
LOGGER.severe("An internal SQL error has occured, trying up to " + (5 - levels) + " more times...");
|
||||
e.printStackTrace();
|
||||
levels++;
|
||||
this.doExecuteAsyncOperation(levels, callback);
|
||||
}
|
||||
}
|
||||
|
||||
public DatabaseCredentials getCredentials() {
|
||||
return this.dbCredentials;
|
||||
}
|
||||
|
||||
public String getTable(String tableName) {
|
||||
return this.dbCredentials.getTablePrefix() + tableName;
|
||||
}
|
||||
|
||||
public void waitUntilFlushed() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
while (!this.saneDatabase.areAllTransactionsDone()) {
|
||||
if ((System.currentTimeMillis() - startTime) > FIVE_SECONDS) {
|
||||
LOGGER.warning("Took too long to flush all transactions - something has probably hung :(");
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SaneDatabase getConnection() {
|
||||
return this.saneDatabase;
|
||||
}
|
||||
}
|
@ -5,9 +5,13 @@ import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.economy.EconomyResponse;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionResult;
|
||||
import org.appledash.saneeconomy.utils.PlayerUtils;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -37,7 +41,7 @@ public class EconomySaneEconomy implements Economy {
|
||||
|
||||
@Override
|
||||
public String format(double v) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().getCurrency().formatAmount(v);
|
||||
return SaneEconomy.getInstance().getEconomyManager().getCurrency().formatAmount(new BigDecimal(v));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -51,16 +55,8 @@ public class EconomySaneEconomy implements Economy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAccount(String playerName) {
|
||||
Economable economable;
|
||||
if (validatePlayer(playerName)) {
|
||||
economable = Economable.wrap(Bukkit.getPlayer(playerName));
|
||||
} else {
|
||||
economable = Economable.wrap(playerName);
|
||||
}
|
||||
|
||||
return SaneEconomy.getInstance().getEconomyManager().accountExists(economable);
|
||||
|
||||
public boolean hasAccount(String target) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().accountExists(this.makeEconomable(target));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -69,129 +65,121 @@ public class EconomySaneEconomy implements Economy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAccount(String playerName, String worldName) {
|
||||
return hasAccount(playerName);
|
||||
public boolean hasAccount(String target, String worldName) {
|
||||
return this.hasAccount(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAccount(OfflinePlayer offlinePlayer, String worldName) {
|
||||
return hasAccount(offlinePlayer);
|
||||
return this.hasAccount(offlinePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBalance(String playerName) {
|
||||
Economable economable;
|
||||
if (validatePlayer(playerName)) {
|
||||
economable = Economable.wrap(Bukkit.getPlayer(playerName));
|
||||
} else {
|
||||
economable = Economable.wrap(playerName);
|
||||
}
|
||||
|
||||
return SaneEconomy.getInstance().getEconomyManager().getBalance(economable);
|
||||
public double getBalance(String target) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().getBalance(this.makeEconomable(target)).doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBalance(OfflinePlayer offlinePlayer) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().getBalance(Economable.wrap(offlinePlayer));
|
||||
return SaneEconomy.getInstance().getEconomyManager().getBalance(Economable.wrap(offlinePlayer)).doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBalance(String playerName, String worldName) {
|
||||
return getBalance(playerName);
|
||||
public double getBalance(String target, String worldName) {
|
||||
return this.getBalance(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBalance(OfflinePlayer offlinePlayer, String worldName) {
|
||||
return getBalance(offlinePlayer);
|
||||
return this.getBalance(offlinePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String playerName, double v) {
|
||||
Economable economable;
|
||||
if (validatePlayer(playerName)) {
|
||||
economable = Economable.wrap(Bukkit.getPlayer(playerName));
|
||||
} else {
|
||||
economable = Economable.wrap(playerName);
|
||||
public boolean has(String target, double amount) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().hasBalance(this.makeEconomable(target), new BigDecimal(amount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(OfflinePlayer offlinePlayer, double amount) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().hasBalance(Economable.wrap(offlinePlayer), new BigDecimal(amount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String target, String worldName, double amount) {
|
||||
return this.has(target, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(OfflinePlayer offlinePlayer, String worldName, double amount) {
|
||||
return this.has(offlinePlayer, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse withdrawPlayer(String target, double amount) {
|
||||
if (amount == 0) {
|
||||
return new EconomyResponse(amount, this.getBalance(target), EconomyResponse.ResponseType.SUCCESS, "");
|
||||
}
|
||||
|
||||
return SaneEconomy.getInstance().getEconomyManager().hasBalance(economable, v);
|
||||
return this.transact(new Transaction(
|
||||
SaneEconomy.getInstance().getEconomyManager().getCurrency(), this.makeEconomable(target), Economable.PLUGIN, new BigDecimal(amount), TransactionReason.PLUGIN_TAKE
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(OfflinePlayer offlinePlayer, double v) {
|
||||
return SaneEconomy.getInstance().getEconomyManager().hasBalance(Economable.wrap(offlinePlayer), v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String playerName, String worldName, double v) {
|
||||
return has(playerName, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(OfflinePlayer offlinePlayer, String worldName, double v) {
|
||||
return has(offlinePlayer, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse withdrawPlayer(String playerName, double v) {
|
||||
Economable economable;
|
||||
if (validatePlayer(playerName)) {
|
||||
economable = Economable.wrap(Bukkit.getPlayer(playerName));
|
||||
} else {
|
||||
economable = Economable.wrap(playerName);
|
||||
public EconomyResponse withdrawPlayer(OfflinePlayer offlinePlayer, double amount) {
|
||||
if (amount == 0) {
|
||||
return new EconomyResponse(amount, this.getBalance(offlinePlayer), EconomyResponse.ResponseType.SUCCESS, "");
|
||||
}
|
||||
|
||||
if (!has(playerName, v)) {
|
||||
return new EconomyResponse(v, getBalance(playerName), EconomyResponse.ResponseType.FAILURE, "Insufficient funds.");
|
||||
if (!this.has(offlinePlayer, amount)) {
|
||||
return new EconomyResponse(amount, this.getBalance(offlinePlayer), EconomyResponse.ResponseType.FAILURE, "Insufficient funds.");
|
||||
}
|
||||
|
||||
return new EconomyResponse(v, SaneEconomy.getInstance().getEconomyManager().subtractBalance(economable, v), EconomyResponse.ResponseType.SUCCESS, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse withdrawPlayer(OfflinePlayer offlinePlayer, double v) {
|
||||
if (!has(offlinePlayer, v)) {
|
||||
return new EconomyResponse(v, getBalance(offlinePlayer), EconomyResponse.ResponseType.FAILURE, "Insufficient funds.");
|
||||
}
|
||||
|
||||
return new EconomyResponse(v, SaneEconomy.getInstance().getEconomyManager().subtractBalance(Economable.wrap(offlinePlayer), v), EconomyResponse.ResponseType.SUCCESS, null);
|
||||
return this.transact(new Transaction(
|
||||
SaneEconomy.getInstance().getEconomyManager().getCurrency(), Economable.wrap(offlinePlayer), Economable.PLUGIN, new BigDecimal(amount), TransactionReason.PLUGIN_TAKE
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse withdrawPlayer(String playerName, String worldName, double v) {
|
||||
return withdrawPlayer(playerName, v);
|
||||
return this.withdrawPlayer(playerName, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse withdrawPlayer(OfflinePlayer offlinePlayer, String s, double v) {
|
||||
return withdrawPlayer(offlinePlayer, v);
|
||||
return this.withdrawPlayer(offlinePlayer, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse depositPlayer(String playerName, double v) {
|
||||
Economable economable;
|
||||
if (validatePlayer(playerName)) {
|
||||
economable = Economable.wrap(Bukkit.getPlayer(playerName));
|
||||
} else {
|
||||
economable = Economable.wrap(playerName);
|
||||
public EconomyResponse depositPlayer(String target, double amount) {
|
||||
if (amount == 0) {
|
||||
return new EconomyResponse(amount, this.getBalance(target), EconomyResponse.ResponseType.SUCCESS, "");
|
||||
}
|
||||
|
||||
return new EconomyResponse(v, SaneEconomy.getInstance().getEconomyManager().addBalance(economable, v), EconomyResponse.ResponseType.SUCCESS, null);
|
||||
return this.transact(new Transaction(
|
||||
SaneEconomy.getInstance().getEconomyManager().getCurrency(), Economable.PLUGIN, this.makeEconomable(target), new BigDecimal(amount), TransactionReason.PLUGIN_GIVE
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse depositPlayer(OfflinePlayer offlinePlayer, double v) {
|
||||
return new EconomyResponse(v, SaneEconomy.getInstance().getEconomyManager().addBalance(Economable.wrap(offlinePlayer), v), EconomyResponse.ResponseType.SUCCESS, null);
|
||||
if (v == 0) {
|
||||
return new EconomyResponse(v, this.getBalance(offlinePlayer), EconomyResponse.ResponseType.SUCCESS, "");
|
||||
}
|
||||
|
||||
return this.transact(new Transaction(
|
||||
SaneEconomy.getInstance().getEconomyManager().getCurrency(), Economable.PLUGIN, Economable.wrap(offlinePlayer), new BigDecimal(v), TransactionReason.PLUGIN_GIVE
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse depositPlayer(String playerName, String worldName, double v) {
|
||||
return depositPlayer(playerName, v);
|
||||
return this.depositPlayer(playerName, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyResponse depositPlayer(OfflinePlayer offlinePlayer, String worldName, double v) {
|
||||
return depositPlayer(offlinePlayer, v);
|
||||
return this.depositPlayer(offlinePlayer, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -275,6 +263,28 @@ public class EconomySaneEconomy implements Economy {
|
||||
}
|
||||
|
||||
private boolean validatePlayer(String playerName) {
|
||||
return Bukkit.getServer().getPlayer(playerName) != null;
|
||||
return PlayerUtils.getOfflinePlayer(playerName) != null;
|
||||
}
|
||||
|
||||
private Economable makeEconomable(String input) {
|
||||
if (input.equals(SaneEconomy.getInstance().getEconomyManager().getServerAccountName())) {
|
||||
return Economable.CONSOLE;
|
||||
}
|
||||
|
||||
if (this.validatePlayer(input)) {
|
||||
return Economable.wrap(PlayerUtils.getOfflinePlayer(input));
|
||||
}
|
||||
|
||||
return Economable.wrap(input);
|
||||
}
|
||||
|
||||
private EconomyResponse transact(Transaction transaction) {
|
||||
TransactionResult result = SaneEconomy.getInstance().getEconomyManager().transact(transaction);
|
||||
|
||||
if (result.getStatus() == TransactionResult.Status.SUCCESS) {
|
||||
return new EconomyResponse(transaction.getAmount().doubleValue(), result.getToBalance().doubleValue(), EconomyResponse.ResponseType.SUCCESS, null);
|
||||
}
|
||||
|
||||
return new EconomyResponse(0, 0, EconomyResponse.ResponseType.FAILURE, result.getStatus().toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
package org.appledash.saneeconomy.vault;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.permission.Permission;
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
/**
|
||||
@ -18,10 +22,20 @@ public class VaultHook {
|
||||
}
|
||||
|
||||
public void hook() {
|
||||
Bukkit.getServicesManager().register(Economy.class, provider, plugin, ServicePriority.Normal);
|
||||
Bukkit.getServicesManager().register(Economy.class, this.provider, this.plugin, ServicePriority.Normal);
|
||||
}
|
||||
|
||||
public void unhook() {
|
||||
Bukkit.getServicesManager().unregister(Economy.class, provider);
|
||||
Bukkit.getServicesManager().unregister(Economy.class, this.provider);
|
||||
}
|
||||
|
||||
public boolean hasPermission(OfflinePlayer offlinePlayer, String permNode) {
|
||||
RegisteredServiceProvider<Permission> rsp = this.plugin.getServer().getServicesManager().getRegistration(Permission.class);
|
||||
|
||||
if ((offlinePlayer == null) || (offlinePlayer.getUniqueId() == null) || Strings.isNullOrEmpty(offlinePlayer.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (rsp != null) && rsp.getProvider().playerHas(null, offlinePlayer, permNode);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,34 @@
|
||||
# Settings pertaining to the backend. Check the wiki for valid values here.
|
||||
backend:
|
||||
type: flatfile
|
||||
type: json
|
||||
|
||||
# Settings pertaining to the currency.
|
||||
currency:
|
||||
name:
|
||||
name: # Currency name as displayed to players.
|
||||
singular: dollar
|
||||
plural: dollars
|
||||
balance-format: '{1} {2}' # The format for currency amounts when displayed to players. {1} = amount, {2} = currency name, properly pluralized
|
||||
# I have no idea what any of these do but try tweaking them if weird things happen regarding currency amounts and you're not in Canada or the USA.
|
||||
format: '0.00'
|
||||
grouping: 3
|
||||
grouping-separator: ','
|
||||
decimal-separator: '.'
|
||||
|
||||
chat:
|
||||
prefix: '&b[&9Economy&b]&r '
|
||||
prefix: '&b[&9Economy&b]&r ' # Prefix for chat messages sent by the plugin. Include a space at the end.
|
||||
|
||||
economy:
|
||||
start-balance: 0.0
|
||||
start-balance: 0.0 # If this is greater than zero, players will be given this amount on their first join.
|
||||
notify-start-balance: true # Whether to send players a message when they receive their starting balance.
|
||||
server-account: '$SERVER$' # Economy operations on an account with this name will appear to have an infinite balance.
|
||||
baltop-update-interval: 300 # How often, in seconds, to reload the content of /baltop from the database.
|
||||
notify-admin-give: false # Whether to notify players when /ecoadmin give is used on them.
|
||||
notify-admin-take: false # Whether to notify players when /ecoadmin take is used on them.
|
||||
notify-admin-set: false # Whether to notify players when /ecoadmin set is used on them.
|
||||
pay-offline-players: true # Whether to allow paying offline players or not.
|
||||
|
||||
debug: false
|
||||
multi-server-sync: false # Experimental balance syncing without player rejoins, across BungeeCord networks.
|
||||
update-check: true # Whether to check for updates to the plugin and notify admins about them.
|
||||
locale-override: false # Whether to force the server's locale to be en_US.
|
||||
debug: false # Debug mode, leave this set to false.
|
||||
metrics: true # Whether to send anonymous metrics about the plugin's usage back to the developers, useful for fixing crashes.
|
||||
|
@ -1,23 +1,46 @@
|
||||
# How to use this file:
|
||||
# Add a translation to the messages you wish to change, like this:
|
||||
# - message: That player is not online.
|
||||
# translation: Ce joueur est pas en ligne.
|
||||
# translation: Ce joueur n'est pas en ligne.
|
||||
# To be clear: DO NOT change 'message'. Simply add a new, indented 'translation' line below it, with the changed message.
|
||||
# Colors can also be used, by means of prefixing color codes with an '&' symbol.
|
||||
# The order of placeholders can be changed. Anything after the :, like the '02d' in {1:02d} is a Java String.format specifier. If you don't know what that is, I recommend leaving it as-is.
|
||||
# IMPORTANT: If your translation has a colon ( : ) character inside of it, you must enclose the entire part after "translation: " in single quotes ( ' ).
|
||||
# If this file doesn't work for some reason, check your console for errors with "SnakeYAML" included in them.
|
||||
####################################################################################################
|
||||
########## READ ABOVE IF YOU INTEND TO EDIT THIS FILE, BEFORE ASKING FOR HELP! #####################
|
||||
####################################################################################################
|
||||
messages:
|
||||
- message: You don't have permission to check the balance of %s.
|
||||
- message: That player is not online.
|
||||
- message: You cannot pay yourself.
|
||||
- message: "Usage: %s"
|
||||
- message: That player does not exist.
|
||||
- message: Balance for %s is %s.
|
||||
- message: "Top %d players:"
|
||||
- message: That player does not exist.
|
||||
- message: "%s is not a positive number."
|
||||
- message: Added %s to %s. Their balance is now %s.
|
||||
- message: Took %s from %s. Their balance is now %s.
|
||||
- message: Balance for %s set to %s
|
||||
- message: You do not have enough money to transfer %s to %s.
|
||||
- message: You have transferred %s to %s.
|
||||
- message: You have received %s from %s.
|
||||
- message: Reloading database...
|
||||
- message: Database reloaded.
|
||||
- message: You've been issued a starting balance of %s!
|
||||
- message: "You don't have permission to check the balance of {1}."
|
||||
- message: "That player is not online."
|
||||
- message: "You cannot pay yourself."
|
||||
- message: "Usage: {1}"
|
||||
- message: "Your balance is {1}."
|
||||
- message: "Balance for {1} is {2}."
|
||||
- message: "Top {1} players on page {2}:"
|
||||
- message: "That player does not exist."
|
||||
- message: "{1} is not a positive number."
|
||||
- message: "Added {1} to {2}. Their balance is now {3}."
|
||||
- message: "Took {1} from {2}. Their balance is now {3}."
|
||||
- message: "Balance for {1} set to {2}."
|
||||
- message: "{1} has given you {2}. Your balance is now {3}."
|
||||
- message: "{1} has taken {2} from you. Your balance is now {3}."
|
||||
- message: "{1} has set your balance to {2}."
|
||||
- message: "You do not have enough money to transfer {1} to {2}."
|
||||
- message: "You have transferred {1} to {2}."
|
||||
- message: "You have received {1} from {2}."
|
||||
- message: "Reloading database..."
|
||||
- message: "Database reloaded."
|
||||
- message: "You've been issued a starting balance of {1}!"
|
||||
- message: "{1} is not a valid number."
|
||||
- message: "There aren't enough players to display that page."
|
||||
- message: "[{1:02d}] {2} - {3}"
|
||||
- message: "Too few arguments for that command!"
|
||||
- message: "Usage: {1}"
|
||||
- message: "/pay <player> <amount>"
|
||||
- message: "/<command> [player]"
|
||||
- message: "/<command>"
|
||||
- message: "/<command> <page>"
|
||||
- message: "/<command> <give/take/set> [player] <amount>"
|
||||
- message: "/<command> reload-database"
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
name: SaneEconomy
|
||||
author: AppleDash
|
||||
main: org.appledash.saneeconomy.SaneEconomy
|
||||
version: 0.7.1
|
||||
version: ${project.version}
|
||||
# api-version: '1.14'
|
||||
load: STARTUP
|
||||
softdepend: [Vault]
|
||||
commands:
|
||||
balance:
|
||||
@ -49,3 +51,20 @@ permissions:
|
||||
saneeconomy.balancetop:
|
||||
description: Allows you to view the players on the server who have the most money.
|
||||
default: true
|
||||
saneeconomy.balancetop.hide:
|
||||
description: Players with this node are hidden from /baltop
|
||||
default: false
|
||||
saneeconomy.update-notify:
|
||||
description: Allows you to be notified of updates to the plugin on join.
|
||||
default: op
|
||||
saneeconomy.*:
|
||||
children:
|
||||
saneeconomy.balance: true
|
||||
saneeconomy.balance.other: true
|
||||
saneeconomy.ecoadmin: true
|
||||
saneeconomy.pay: true
|
||||
saneeconomy.admin: true
|
||||
saneeconomy.balancetop: true
|
||||
saneeconomy.balancetop.hide: false
|
||||
saneeconomy.update-notify: true
|
||||
default: op
|
||||
|
@ -4,6 +4,7 @@ import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
@ -14,7 +15,7 @@ public class CurrencyTest {
|
||||
@Test
|
||||
public void testCurrencyFormat() {
|
||||
Currency currency = new Currency("test dollar", "test dollars", new DecimalFormat("0.00"));
|
||||
Assert.assertEquals(currency.formatAmount(1.0D), "1.00 test dollar");
|
||||
Assert.assertEquals(currency.formatAmount(1337.0D), "1337.00 test dollars");
|
||||
Assert.assertEquals(currency.formatAmount(new BigDecimal("1.0")), "1.00 test dollar");
|
||||
Assert.assertEquals(currency.formatAmount(new BigDecimal("1337.0")), "1337.00 test dollars");
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ public class EconomableTest {
|
||||
@Test
|
||||
public void testWrapFaction() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
Economable economable = Economable.wrap(String.format("faction-%s", uuid.toString()));
|
||||
Economable economable = Economable.wrap(String.format("faction-%s", uuid));
|
||||
Assert.assertEquals(economable.getClass(), EconomableFaction.class);
|
||||
Assert.assertEquals(economable.getUniqueIdentifier(), String.format("faction:%s", uuid.toString()));
|
||||
Assert.assertEquals(economable.getUniqueIdentifier(), String.format("faction:%s", uuid));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,65 +1,142 @@
|
||||
package org.appledash.saneeconomy.test;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionResult;
|
||||
import org.appledash.saneeconomy.test.mock.MockEconomyStorageBackend;
|
||||
import org.appledash.saneeconomy.test.mock.MockOfflinePlayer;
|
||||
import org.appledash.saneeconomy.test.mock.MockSaneEconomy;
|
||||
import org.appledash.saneeconomy.test.util.SaneEcoAssert;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 7/29/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class EconomyManagerTest {
|
||||
private EconomyManager economyManager;
|
||||
|
||||
@Before
|
||||
public void setupEconomyManager() {
|
||||
this.economyManager = new EconomyManager(new MockSaneEconomy(),
|
||||
new Currency("test dollar", "test dollars", new DecimalFormat("0.00")),
|
||||
new MockEconomyStorageBackend(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEconomyManager() {
|
||||
EconomyManager economyManager = new EconomyManager(new Currency("test dollar", "test dollars", new DecimalFormat("0.00")), new MockEconomyStorageBackend());
|
||||
Economable playerOne = Economable.wrap(new MockOfflinePlayer("One"));
|
||||
Economable playerTwo = Economable.wrap(new MockOfflinePlayer("Two"));
|
||||
|
||||
// Accounts should not exist
|
||||
Assert.assertFalse(economyManager.accountExists(playerOne));
|
||||
Assert.assertFalse(economyManager.accountExists(playerTwo));
|
||||
Assert.assertEquals(economyManager.getBalance(playerOne), 0.0D, 0.0);
|
||||
Assert.assertEquals(economyManager.getBalance(playerTwo), 0.0D, 0.0);
|
||||
Assert.assertFalse(this.economyManager.accountExists(playerOne));
|
||||
Assert.assertFalse(this.economyManager.accountExists(playerTwo));
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ZERO, this.economyManager.getBalance(playerOne));
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ZERO, this.economyManager.getBalance(playerTwo));
|
||||
|
||||
economyManager.setBalance(playerOne, 100.0D);
|
||||
this.economyManager.setBalance(playerOne, new BigDecimal("100.0"));
|
||||
|
||||
// Now one should have an account, but two should not
|
||||
Assert.assertTrue(economyManager.accountExists(playerOne));
|
||||
Assert.assertFalse(economyManager.accountExists(playerTwo));
|
||||
Assert.assertTrue(this.economyManager.accountExists(playerOne));
|
||||
Assert.assertFalse(this.economyManager.accountExists(playerTwo));
|
||||
|
||||
// One should have balance, two should not
|
||||
Assert.assertEquals(economyManager.getBalance(playerOne), 100.0, 0.0);
|
||||
Assert.assertEquals(economyManager.getBalance(playerTwo), 0.0, 0.0);
|
||||
SaneEcoAssert.assertEquals(new BigDecimal("100.00"), this.economyManager.getBalance(playerOne));
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ZERO, this.economyManager.getBalance(playerTwo));
|
||||
|
||||
// One should be able to transfer to two
|
||||
Assert.assertTrue(economyManager.transfer(playerOne, playerTwo, 50.0));
|
||||
Assert.assertSame(this.economyManager.transact(new Transaction(this.economyManager.getCurrency(), playerOne, playerTwo, new BigDecimal("50.0"), TransactionReason.PLAYER_PAY)).getStatus(), TransactionResult.Status.SUCCESS);
|
||||
|
||||
// One should now have only 50 left, two should have 50 now
|
||||
Assert.assertEquals(economyManager.getBalance(playerOne), 50.0, 0.0);
|
||||
Assert.assertEquals(economyManager.getBalance(playerTwo), 50.0, 0.0);
|
||||
SaneEcoAssert.assertEquals("Player one should have 50 dollars", new BigDecimal("50.00"), this.economyManager.getBalance(playerOne));
|
||||
SaneEcoAssert.assertEquals("Player two should have 50 dollars", new BigDecimal("50.00"), this.economyManager.getBalance(playerTwo));
|
||||
|
||||
// Ensure that balance addition and subtraction works...
|
||||
Assert.assertEquals(economyManager.subtractBalance(playerOne, 25.0), 25.0, 0.0);
|
||||
Assert.assertEquals(economyManager.addBalance(playerOne, 25.0), 50.0, 0.0);
|
||||
Assert.assertEquals(economyManager.subtractBalance(playerTwo, Double.MAX_VALUE), 0.0, 0.0);
|
||||
SaneEcoAssert.assertEquals(new BigDecimal("25.00"), this.economyManager.transact(
|
||||
new Transaction(this.economyManager.getCurrency(), playerOne, Economable.CONSOLE, new BigDecimal("25.00"), TransactionReason.TEST_TAKE)
|
||||
).getFromBalance());
|
||||
|
||||
SaneEcoAssert.assertEquals(new BigDecimal("50.00"), this.economyManager.transact(
|
||||
new Transaction(this.economyManager.getCurrency(), Economable.CONSOLE, playerOne, new BigDecimal("25.00"), TransactionReason.TEST_GIVE)
|
||||
).getToBalance());
|
||||
|
||||
Assert.assertEquals(TransactionResult.Status.ERR_NOT_ENOUGH_FUNDS, this.economyManager.transact(
|
||||
new Transaction(this.economyManager.getCurrency(), playerTwo, Economable.CONSOLE, new BigDecimal(Double.MAX_VALUE), TransactionReason.TEST_TAKE)
|
||||
).getStatus());
|
||||
|
||||
// Ensure that hasBalance works
|
||||
Assert.assertTrue(economyManager.hasBalance(playerOne, 50.0));
|
||||
Assert.assertFalse(economyManager.hasBalance(playerOne, 51.0));
|
||||
|
||||
|
||||
Assert.assertTrue(this.economyManager.hasBalance(playerOne, new BigDecimal("50.00")));
|
||||
Assert.assertFalse(this.economyManager.hasBalance(playerOne, new BigDecimal("51.00")));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testNegativeBalance() {
|
||||
EconomyManager economyManager = new EconomyManager(new Currency("test dollar", "test dollars", new DecimalFormat("0.00")), new MockEconomyStorageBackend());
|
||||
Economable economable = Economable.wrap(new MockOfflinePlayer("Bob"));
|
||||
economyManager.setBalance(economable, -1.0);
|
||||
@Test
|
||||
public void testTopBalances() {
|
||||
Random random = new Random();
|
||||
List<Economable> economables = new ArrayList<>(10);
|
||||
Set<String> names = new HashSet<>();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Economable economable = Economable.wrap(new MockOfflinePlayer("Dude" + i));
|
||||
names.add("Dude" + i);
|
||||
economables.add(economable);
|
||||
this.economyManager.setBalance(economable, new BigDecimal(random.nextInt(1000)));
|
||||
}
|
||||
|
||||
this.economyManager.getBackend().reloadTopPlayerBalances();
|
||||
|
||||
List<BigDecimal> javaSortedBalances = economables.stream().map(this.economyManager::getBalance).sorted((left, right) -> -left.compareTo(right)).collect(Collectors.toList());
|
||||
List<BigDecimal> ecoManTopBalances = ImmutableList.copyOf(this.economyManager.getTopBalances(10, 0).values());
|
||||
|
||||
Assert.assertTrue("List is not correctly sorted!", this.areListsEqual(javaSortedBalances, ecoManTopBalances));
|
||||
Assert.assertEquals("Wrong number of top balances!", 5, this.economyManager.getTopBalances(5, 0).size());
|
||||
|
||||
this.economyManager.getTopBalances(10, 0).keySet().forEach(name -> Assert.assertTrue("Returned name in top balances not valid!", names.contains(name)));
|
||||
}
|
||||
|
||||
private <T> boolean areListsEqual(List<T> first, List<T> second) {
|
||||
if (first.size() != second.size()) {
|
||||
throw new IllegalArgumentException("Lists must be same length (first=" + first.size() + ", second=" + second.size() + ")");
|
||||
}
|
||||
|
||||
for (int i = 0; i < first.size(); i++) {
|
||||
if (!first.get(i).equals(second.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasRequiredBalance() {
|
||||
for (int n = 0; n < 20; n++) { // in the absence of Junit 5's @RepeatedTest
|
||||
|
||||
BigDecimal bigDecimal = randomBigDecimal();
|
||||
// We MUST modify the BigDecimal in some way otherwise the test will always succeed
|
||||
// See https://github.com/AppleDash/SaneEconomy/issues/100
|
||||
for (int m = 0; m < 20; m++) {
|
||||
bigDecimal = bigDecimal.add(randomBigDecimal()).subtract(randomBigDecimal());
|
||||
}
|
||||
//
|
||||
|
||||
Assert.assertTrue("Account must have required balance despite loss of precision (repeat " + n + ")",
|
||||
economyManager.hasBalance(bigDecimal, new BigDecimal(bigDecimal.doubleValue())));
|
||||
}
|
||||
}
|
||||
|
||||
private static BigDecimal randomBigDecimal() {
|
||||
return new BigDecimal(ThreadLocalRandom.current().nextDouble());
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package org.appledash.saneeconomy.test;
|
||||
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.appledash.saneeconomy.test.util.SaneEcoAssert;
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -16,19 +18,21 @@ public class NumberUtilsTest {
|
||||
@Test
|
||||
public void testParsePositive() {
|
||||
// Valid input
|
||||
Assert.assertEquals(NumberUtils.parsePositiveDouble("69.0"), 69.0, 0.0);
|
||||
SaneEcoAssert.assertEquals(new BigDecimal("69.0"), NumberUtils.parsePositiveDouble("69.0"));
|
||||
// Valid but not positive
|
||||
Assert.assertEquals(NumberUtils.parsePositiveDouble("-10.0"), -1.0, 0.0);
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ONE.negate(), NumberUtils.parsePositiveDouble("-10.0"));
|
||||
// Invalid
|
||||
Assert.assertEquals(NumberUtils.parsePositiveDouble("nan"), -1.0, 0.0);
|
||||
Assert.assertEquals(NumberUtils.parsePositiveDouble("ponies"), -1.0, 0.0);
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ONE.negate(), NumberUtils.parsePositiveDouble("nan"));
|
||||
SaneEcoAssert.assertEquals(BigDecimal.ONE.negate(), NumberUtils.parsePositiveDouble("ponies"));
|
||||
// Infinite
|
||||
// TODO: Not needed with BigDecimal? Assert.assertEquals(BigDecimal.ONE.negate(), NumberUtils.parsePositiveDouble("1E1000000000"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilter() {
|
||||
Currency currency = new Currency(null, null, new DecimalFormat("0.00"));
|
||||
|
||||
Assert.assertEquals(NumberUtils.filterAmount(currency, 1337.420D), 1337.42, 0.0);
|
||||
SaneEcoAssert.assertEquals(new BigDecimal("1337.42"), NumberUtils.filterAmount(currency, new BigDecimal("1337.420")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -36,7 +40,7 @@ public class NumberUtilsTest {
|
||||
Locale old = Locale.getDefault();
|
||||
Locale.setDefault(Locale.FRANCE);
|
||||
try {
|
||||
testFilter();
|
||||
this.testFilter();
|
||||
} catch (Throwable e) {
|
||||
Locale.setDefault(old);
|
||||
throw e;
|
||||
@ -44,4 +48,14 @@ public class NumberUtilsTest {
|
||||
Locale.setDefault(old);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigDecimalEquals() {
|
||||
BigDecimal one = new BigDecimal("100.0");
|
||||
BigDecimal two = new BigDecimal("100.00");
|
||||
BigDecimal three = new BigDecimal("100.1");
|
||||
|
||||
Assert.assertTrue("100.0 should equal 100.00", NumberUtils.equals(one, two));
|
||||
Assert.assertFalse("100.0 should not equal 100.1", NumberUtils.equals(one, three));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package org.appledash.saneeconomy.test;
|
||||
|
||||
import org.appledash.saneeconomy.updates.VersionComparer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/15/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class VersionComparerTest {
|
||||
@Test
|
||||
public void testVersionComparer() {
|
||||
Assert.assertTrue(VersionComparer.isSemVerGreaterThan("0.12.6", "1.0.0"));
|
||||
Assert.assertFalse(VersionComparer.isSemVerGreaterThan("2.0.0", "1.0.0"));
|
||||
Assert.assertTrue(VersionComparer.isSemVerGreaterThan("0.1.0", "0.2.0"));
|
||||
Assert.assertTrue(VersionComparer.isSemVerGreaterThan("1.0.0", "2.0.0"));
|
||||
Assert.assertFalse(VersionComparer.isSemVerGreaterThan("0.12.6", "0.5.7"));
|
||||
}
|
||||
}
|
@ -3,18 +3,26 @@ package org.appledash.saneeconomy.test.mock;
|
||||
import org.appledash.saneeconomy.economy.backend.type.EconomyStorageBackendCaching;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by AppleDash on 7/29/2016.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class MockEconomyStorageBackend extends EconomyStorageBackendCaching {
|
||||
@Override
|
||||
public void setBalance(Economable player, double newBalance) {
|
||||
balances.put(player.getUniqueIdentifier(), newBalance);
|
||||
public void setBalance(Economable player, BigDecimal newBalance) {
|
||||
this.balances.put(player.getUniqueIdentifier(), newBalance);
|
||||
this.uuidToName.put(player.getUniqueIdentifier(), player.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadDatabase() {
|
||||
System.out.println("Reloading mock economy database (doing nothing).");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitUntilFlushed() {
|
||||
// Null op
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,10 @@ public class MockOfflinePlayer implements OfflinePlayer {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
|
||||
public MockOfflinePlayer(UUID uuid, String name) {
|
||||
private MockOfflinePlayer(UUID uuid, String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
MockServer.getInstance().addOfflinePlayer(this);
|
||||
}
|
||||
|
||||
public MockOfflinePlayer(String name) {
|
||||
@ -33,12 +34,12 @@ public class MockOfflinePlayer implements OfflinePlayer {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -46,11 +47,6 @@ public class MockOfflinePlayer implements OfflinePlayer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBanned(boolean banned) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted() {
|
||||
return false;
|
||||
|
@ -0,0 +1,35 @@
|
||||
package org.appledash.saneeconomy.test.mock;
|
||||
|
||||
import org.appledash.saneeconomy.ISaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.logger.TransactionLogger;
|
||||
import org.appledash.saneeconomy.vault.VaultHook;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 9/18/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class MockSaneEconomy implements ISaneEconomy {
|
||||
@Override
|
||||
public EconomyManager getEconomyManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TransactionLogger> getTransactionLogger() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultHook getVaultHook() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastName(UUID uuid) {
|
||||
return uuid.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,628 @@
|
||||
package org.appledash.saneeconomy.test.mock;
|
||||
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.boss.*;
|
||||
import org.bukkit.command.CommandException;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.help.HelpMap;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.bukkit.loot.LootTable;
|
||||
import org.bukkit.map.MapView;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.ServicesManager;
|
||||
import org.bukkit.plugin.messaging.Messenger;
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scoreboard.ScoreboardManager;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/15/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
public class MockServer implements Server {
|
||||
public static MockServer instance;
|
||||
|
||||
public static MockServer getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new MockServer();
|
||||
Bukkit.setServer(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
private final Logger logger = Logger.getLogger("MockServer");
|
||||
private final Map<UUID, OfflinePlayer> offlinePlayers = new HashMap<>();
|
||||
|
||||
public void addOfflinePlayer(OfflinePlayer offlinePlayer) {
|
||||
this.offlinePlayers.put(offlinePlayer.getUniqueId(), offlinePlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "SaneEconomy Mock Server";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "0.1.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBukkitVersion() {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Player> getOnlinePlayers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewDistance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorldType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getGenerateStructures() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllowEnd() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllowNether() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWhitelist() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelist(boolean b) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<OfflinePlayer> getWhitelistedPlayers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadWhitelist() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int broadcastMessage(String s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUpdateFolder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getUpdateFolderFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getConnectionThrottle() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTicksPerAnimalSpawns() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTicksPerMonsterSpawns() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayerExact(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Player> matchPlayer(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer(UUID uuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginManager getPluginManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitScheduler getScheduler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServicesManager getServicesManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<World> getWorlds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World createWorld(WorldCreator worldCreator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unloadWorld(String s, boolean b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unloadWorld(World world, boolean b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getWorld(UUID uuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapView getMap(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MapView createMap(World world) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createExplorerMap(World world, Location location, StructureType structureType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack createExplorerMap(World world, Location location, StructureType structureType, int radius, boolean findUnexplored) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadData() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginCommand getPluginCommand(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePlayers() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchCommand(CommandSender commandSender, String s) throws CommandException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addRecipe(Recipe recipe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Recipe> getRecipesFor(ItemStack itemStack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Recipe> recipeIterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearRecipes() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetRecipes() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getCommandAliases() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnRadius() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpawnRadius(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getOnlineMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllowFlight() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHardcore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int broadcast(String s, String s1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflinePlayer getOfflinePlayer(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflinePlayer getOfflinePlayer(UUID uuid) {
|
||||
return this.offlinePlayers.get(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getIPBans() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void banIP(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbanIP(String s) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<OfflinePlayer> getBannedPlayers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BanList getBanList(BanList.Type type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<OfflinePlayer> getOperators() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameMode getDefaultGameMode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultGameMode(GameMode gameMode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsoleCommandSender getConsoleSender() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getWorldContainer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OfflinePlayer[] getOfflinePlayers() {
|
||||
return new OfflinePlayer[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Messenger getMessenger() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpMap getHelpMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder inventoryHolder, InventoryType inventoryType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder inventoryHolder, InventoryType inventoryType, String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder inventoryHolder, int i) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory createInventory(InventoryHolder inventoryHolder, int i, String s) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Merchant createMerchant(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMonsterSpawnLimit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnimalSpawnLimit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWaterAnimalSpawnLimit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAmbientSpawnLimit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimaryThread() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMotd() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShutdownMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Warning.WarningState getWarningState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemFactory getItemFactory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScoreboardManager getScoreboardManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedServerIcon getServerIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedServerIcon loadServerIcon(File file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedServerIcon loadServerIcon(BufferedImage bufferedImage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIdleTimeout(int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIdleTimeout() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator.ChunkData createChunkData(World world) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBar createBossBar(String s, BarColor barColor, BarStyle barStyle, BarFlag... barFlags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyedBossBar createBossBar(NamespacedKey key, String title, BarColor color, BarStyle style, BarFlag... flags) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<KeyedBossBar> getBossBars() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyedBossBar getBossBar(NamespacedKey key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeBossBar(NamespacedKey key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity(UUID uuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Advancement getAdvancement(NamespacedKey namespacedKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Advancement> advancementIterator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(Material material) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(Material material, Consumer<BlockData> consumer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(String data) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockData createBlockData(Material material, String data) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Keyed> Tag<T> getTag(String registry, NamespacedKey tag, Class<T> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Keyed> Iterable<Tag<T>> getTags(String registry, Class<T> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LootTable getLootTable(NamespacedKey key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> selectEntities(CommandSender sender, String selector) throws IllegalArgumentException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnsafeValues getUnsafe() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spigot spigot() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(Plugin plugin, String s, byte[] bytes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getListeningPluginChannels() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package org.appledash.saneeconomy.test.util;
|
||||
|
||||
import org.appledash.saneeconomy.utils.NumberUtils;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public final class SaneEcoAssert {
|
||||
private SaneEcoAssert() {
|
||||
}
|
||||
|
||||
public static void assertEquals(BigDecimal left, BigDecimal right) {
|
||||
Assert.assertTrue(String.format("%s != %s", left.toPlainString(), right.toPlainString()), NumberUtils.equals(left, right));
|
||||
}
|
||||
|
||||
public static void assertEquals(String message, BigDecimal left, BigDecimal right) {
|
||||
Assert.assertTrue(message, NumberUtils.equals(left, right));
|
||||
}
|
||||
}
|
76
SaneEconomyMobKills/pom.xml
Normal file
76
SaneEconomyMobKills/pom.xml
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>SaneEconomy</artifactId>
|
||||
<groupId>org.appledash</groupId>
|
||||
<version>0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>SaneEconomyMobKills</artifactId>
|
||||
<version>0.1.3-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.appledash:sanelib</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.appledash.sanelib</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomymobkills.shaded.sanelib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<outputDirectory>../out/</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,55 @@
|
||||
package org.appledash.saneeconomymobkills;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomymobkills.listeners.EntityDamageListener;
|
||||
import org.appledash.sanelib.SanePlugin;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by appledash on 12/27/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SaneEconomyMobKills extends SanePlugin {
|
||||
private SaneEconomy saneEconomy;
|
||||
private final Map<String, Double> killAmounts = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.saneEconomy = (SaneEconomy) this.getServer().getPluginManager().getPlugin("SaneEconomy");
|
||||
super.onEnable();
|
||||
|
||||
YamlConfiguration amountsConfig;
|
||||
|
||||
if (!(new File(this.getDataFolder(), "amounts.yml").exists())) {
|
||||
amountsConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getResourceAsStream("/amounts.yml")));
|
||||
try {
|
||||
amountsConfig.save(new File(this.getDataFolder(), "amounts.yml"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save amounts.yml to plugin data folder!");
|
||||
}
|
||||
} else {
|
||||
amountsConfig = YamlConfiguration.loadConfiguration(new File(this.getDataFolder(), "amounts.yml"));
|
||||
}
|
||||
|
||||
for (String entityTypeName : amountsConfig.getKeys(false)) {
|
||||
double value = amountsConfig.getDouble(entityTypeName);
|
||||
this.killAmounts.put(entityTypeName, value);
|
||||
}
|
||||
|
||||
this.getServer().getPluginManager().registerEvents(new EntityDamageListener(this), this);
|
||||
}
|
||||
|
||||
public SaneEconomy getSaneEconomy() {
|
||||
return this.saneEconomy;
|
||||
}
|
||||
|
||||
public Map<String, Double> getKillAmounts() {
|
||||
return this.killAmounts;
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package org.appledash.saneeconomymobkills.listeners;
|
||||
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomymobkills.SaneEconomyMobKills;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 12/27/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class EntityDamageListener implements Listener {
|
||||
private final SaneEconomyMobKills plugin;
|
||||
private final Map<Integer, Map<UUID, Double>> damageDealt = new HashMap<>();
|
||||
|
||||
public EntityDamageListener(SaneEconomyMobKills plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEntityDamage(EntityDamageByEntityEvent evt) {
|
||||
if (!(evt.getDamager() instanceof Player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player damager = ((Player) evt.getDamager());
|
||||
Entity damagee = evt.getEntity();
|
||||
|
||||
if (!damager.hasPermission("saneeconomy.mobkills.use")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.plugin.getKillAmounts().containsKey(this.getEntityType(damagee))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<UUID, Double> damageDoneToThisEntity = new HashMap<>();
|
||||
|
||||
if (this.damageDealt.containsKey(damagee.getEntityId())) {
|
||||
damageDoneToThisEntity = this.damageDealt.get(damagee.getEntityId());
|
||||
} else {
|
||||
this.damageDealt.put(damagee.getEntityId(), damageDoneToThisEntity);
|
||||
}
|
||||
|
||||
double totalDamageDealt = 0;
|
||||
|
||||
if (damageDoneToThisEntity.containsKey(damager.getUniqueId())) {
|
||||
totalDamageDealt += damageDoneToThisEntity.get(damager.getUniqueId());
|
||||
}
|
||||
|
||||
totalDamageDealt += evt.getDamage();
|
||||
|
||||
damageDoneToThisEntity.put(damager.getUniqueId(), totalDamageDealt);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEntityDeath(EntityDeathEvent evt) {
|
||||
LivingEntity entity = evt.getEntity();
|
||||
|
||||
if (!this.damageDealt.containsKey(entity.getEntityId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<UUID, Double> damageDoneToThisEntity = this.damageDealt.get(entity.getEntityId());
|
||||
double totalDmg = entity.getMaxHealth();//sumValues(damageDoneToThisEntity);
|
||||
|
||||
for (Map.Entry<UUID, Double> entry : damageDoneToThisEntity.entrySet()) {
|
||||
double thisDmg = entry.getValue();
|
||||
double thisPercent = (thisDmg / totalDmg) * 100.0D;
|
||||
double thisAmount = this.plugin.getKillAmounts().get(this.getEntityType(entity)) * (thisPercent / 100);
|
||||
OfflinePlayer offlinePlayer = Bukkit.getServer().getOfflinePlayer(entry.getKey());
|
||||
|
||||
if (offlinePlayer.isOnline()) {
|
||||
Player player = Bukkit.getServer().getPlayer(offlinePlayer.getUniqueId());
|
||||
this.plugin.getMessenger().sendMessage(player, "You have been awarded {1} for doing {2:.2f}% of the damage required to kill that {3}!", this.plugin.getSaneEconomy().getEconomyManager().getCurrency().formatAmount(BigDecimal.valueOf(thisAmount)), thisPercent, entity.getName());
|
||||
}
|
||||
|
||||
this.plugin.getSaneEconomy().getEconomyManager().transact(new Transaction(
|
||||
this.plugin.getSaneEconomy().getEconomyManager().getCurrency(), Economable.PLUGIN, Economable.wrap(offlinePlayer), BigDecimal.valueOf(thisAmount), TransactionReason.PLUGIN_GIVE
|
||||
));
|
||||
}
|
||||
|
||||
this.damageDealt.remove(evt.getEntity().getEntityId());
|
||||
}
|
||||
|
||||
private String getEntityType(Entity entity) {
|
||||
EntityType entityType = entity.getType();
|
||||
|
||||
if ((entityType == EntityType.SKELETON) && (((Skeleton) entity).getSkeletonType() == Skeleton.SkeletonType.WITHER)) {
|
||||
return "WITHER_SKELETON";
|
||||
}
|
||||
|
||||
if ((entityType == EntityType.GUARDIAN) && ((Guardian) entity).isElder()) {
|
||||
return "ELDER_GUARDIAN";
|
||||
}
|
||||
|
||||
return entityType.toString();
|
||||
}
|
||||
}
|
20
SaneEconomyMobKills/src/main/resources/amounts.yml
Normal file
20
SaneEconomyMobKills/src/main/resources/amounts.yml
Normal file
@ -0,0 +1,20 @@
|
||||
ZOMBIE: 2.0
|
||||
SKELETON: 3.0
|
||||
SPIDER: 1.0
|
||||
CREEPER: 3.0
|
||||
SLIME: 0.2
|
||||
SILVERFISH: 1.2
|
||||
CAVE_SPIDER: 1.5
|
||||
WITCH: 5.0
|
||||
PIG_ZOMBIE: 3.0
|
||||
BLAZE: 5.5
|
||||
GHAST: 7.0
|
||||
MAGMA_CUBE: 0.4
|
||||
WITHER_SKELETON: 5.5
|
||||
ENDERMAN: 4.0
|
||||
ENDERMITE: 1.0
|
||||
SHULKER: 2.0
|
||||
GUARDIAN: 2.5
|
||||
ELDER_GUARDIAN: 50.0
|
||||
WITHER: 150.0
|
||||
ENDER_DRAGON: 1000.0
|
6
SaneEconomyMobKills/src/main/resources/plugin.yml
Normal file
6
SaneEconomyMobKills/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,6 @@
|
||||
name: SaneEconomyMobKills
|
||||
description: A plugin to give players experience when they kill mobs.
|
||||
version: ${project.version}
|
||||
author: AppleDash
|
||||
main: org.appledash.saneeconomymobkills.SaneEconomyMobKills
|
||||
depend: [SaneEconomy]
|
77
SaneEconomyOnlineTime/pom.xml
Normal file
77
SaneEconomyOnlineTime/pom.xml
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>SaneEconomy</artifactId>
|
||||
<groupId>org.appledash</groupId>
|
||||
<version>0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>SaneEconomyOnlineTime</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.appledash:sanelib</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.appledash.sanelib</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomysignshop.shaded.sanelib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<outputDirectory>../out/</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,47 @@
|
||||
package org.appledash.saneeconomy.onlinetime;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/13/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class Payout {
|
||||
private final int secondsInterval;
|
||||
private final BigDecimal amount;
|
||||
private final String message;
|
||||
private String permission;
|
||||
private final long reportInterval;
|
||||
|
||||
public Payout(int secondsInterval, double amount, String message, long reportInterval) {
|
||||
this.secondsInterval = secondsInterval;
|
||||
this.amount = BigDecimal.valueOf(amount);
|
||||
this.message = message;
|
||||
this.reportInterval = reportInterval;
|
||||
}
|
||||
|
||||
public int getSecondsInterval() {
|
||||
return this.secondsInterval;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return this.amount;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
public static Payout fromConfigMap(Map<?, ?> values) {
|
||||
return new Payout(Integer.parseInt(String.valueOf(values.get("seconds"))), Double.parseDouble(String.valueOf(values.get("amount"))), String.valueOf(values.get("message")), Long.parseLong(String.valueOf(values.get("report_interval"))));
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return this.permission;
|
||||
}
|
||||
|
||||
public long getReportInterval() {
|
||||
return this.reportInterval;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.appledash.saneeconomy.onlinetime;
|
||||
|
||||
import org.appledash.saneeconomy.SaneEconomy;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.sanelib.SanePlugin;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by appledash on 7/13/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class SaneEconomyOnlineTime extends SanePlugin implements Listener {
|
||||
private final Map<UUID, Long> onlineSeconds = new HashMap<>();
|
||||
private final Map<UUID, BigDecimal> reportingAmounts = new HashMap<>();
|
||||
private final List<Payout> payouts = new ArrayList<>();
|
||||
private SaneEconomy saneEconomy;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
super.onEnable();
|
||||
this.saneEconomy = (SaneEconomy) this.getServer().getPluginManager().getPlugin("SaneEconomy");
|
||||
this.saveDefaultConfig();
|
||||
|
||||
this.getConfig().getMapList("payouts").forEach(map -> {
|
||||
this.payouts.add(Payout.fromConfigMap(map));
|
||||
});
|
||||
|
||||
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> {
|
||||
for (Player player : this.getServer().getOnlinePlayers()) {
|
||||
long onlineSeconds = this.onlineSeconds.getOrDefault(player.getUniqueId(), 0L);
|
||||
|
||||
onlineSeconds++;
|
||||
|
||||
this.onlineSeconds.put(player.getUniqueId(), onlineSeconds);
|
||||
|
||||
for (Payout payout : this.payouts) {
|
||||
if (payout.getPermission() != null && !player.hasPermission(payout.getPermission())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((onlineSeconds % payout.getSecondsInterval()) == 0) {
|
||||
if (this.reportingAmounts.containsKey(player.getUniqueId())) {
|
||||
this.reportingAmounts.put(player.getUniqueId(), this.reportingAmounts.get(player.getUniqueId()).add(payout.getAmount()));
|
||||
} else {
|
||||
this.reportingAmounts.put(player.getUniqueId(), payout.getAmount());
|
||||
}
|
||||
|
||||
this.saneEconomy.getEconomyManager().transact(new Transaction(this.saneEconomy.getEconomyManager().getCurrency(), Economable.PLUGIN, Economable.wrap(player), payout.getAmount(), TransactionReason.PLUGIN_GIVE));
|
||||
}
|
||||
|
||||
if ((onlineSeconds % payout.getReportInterval()) == 0) {
|
||||
this.getMessenger().sendMessage(player, payout.getMessage(), this.saneEconomy.getEconomyManager().getCurrency().formatAmount(this.reportingAmounts.getOrDefault(player.getUniqueId(), BigDecimal.ZERO)), payout.getReportInterval());
|
||||
this.reportingAmounts.put(player.getUniqueId(), BigDecimal.ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}, 0, 20);
|
||||
|
||||
this.getServer().getPluginManager().registerEvents(this, this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent evt) {
|
||||
this.onlineSeconds.remove(evt.getPlayer().getUniqueId());
|
||||
this.reportingAmounts.remove(evt.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
9
SaneEconomyOnlineTime/src/main/resources/config.yml
Normal file
9
SaneEconomyOnlineTime/src/main/resources/config.yml
Normal file
@ -0,0 +1,9 @@
|
||||
chat:
|
||||
prefix: '&b[&OnlineTime&b]&r '
|
||||
silent: false
|
||||
|
||||
payouts:
|
||||
- seconds: 10
|
||||
amount: 1000
|
||||
message: 'You have been paid {1} in the past {2} seconds of being online!'
|
||||
report_interval: 30
|
6
SaneEconomyOnlineTime/src/main/resources/plugin.yml
Normal file
6
SaneEconomyOnlineTime/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,6 @@
|
||||
name: SaneEconomyOnlineTime
|
||||
main: org.appledash.saneeconomy.onlinetime.SaneEconomyOnlineTime
|
||||
description: Pays players for being online!
|
||||
author: AppleDash
|
||||
version: ${project.version}
|
||||
depend: [SaneEconomy]
|
76
SaneEconomySignShop/pom.xml
Normal file
76
SaneEconomySignShop/pom.xml
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>SaneEconomy</artifactId>
|
||||
<groupId>org.appledash</groupId>
|
||||
<version>0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>SaneEconomySignShop</artifactId>
|
||||
<version>0.1.8-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.appledash</groupId>
|
||||
<artifactId>SaneEconomyCore</artifactId>
|
||||
<version>0.17.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.3</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.appledash:sanelib</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.appledash.sanelib</pattern>
|
||||
<shadedPattern>org.appledash.saneeconomysignshop.shaded.sanelib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<outputDirectory>../out/</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,67 @@
|
||||
package org.appledash.saneeconomysignshop;
|
||||
|
||||
import org.appledash.saneeconomy.ISaneEconomy;
|
||||
import org.appledash.saneeconomysignshop.listeners.BreakListener;
|
||||
import org.appledash.saneeconomysignshop.listeners.InteractListener;
|
||||
import org.appledash.saneeconomysignshop.listeners.SignChangeListener;
|
||||
import org.appledash.saneeconomysignshop.signshop.SignShopManager;
|
||||
import org.appledash.saneeconomysignshop.signshop.storage.SignShopStorageJSON;
|
||||
import org.appledash.saneeconomysignshop.util.ItemDatabase;
|
||||
import org.appledash.saneeconomysignshop.util.LimitManager;
|
||||
import org.appledash.sanelib.SanePlugin;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/2/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SaneEconomySignShop extends SanePlugin {
|
||||
private ISaneEconomy saneEconomy;
|
||||
private final SignShopManager signShopManager = new SignShopManager(new SignShopStorageJSON(new File(this.getDataFolder(), "shops.json")));
|
||||
private final LimitManager limitManager = new LimitManager();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (!this.getServer().getPluginManager().isPluginEnabled("SaneEconomy")) {
|
||||
this.getLogger().severe("SaneEconomy is not enabled on this server - something is wrong here!");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
super.onEnable();
|
||||
|
||||
ItemDatabase.initItemDB();
|
||||
|
||||
this.saneEconomy = (ISaneEconomy) this.getServer().getPluginManager().getPlugin("SaneEconomy");
|
||||
|
||||
// If it's stupid but it works... it's probably still stupid.
|
||||
this.getLogger().info(String.format("Hooked into SaneEconomy version %s.", ((Plugin) this.saneEconomy).getDescription().getVersion()));
|
||||
|
||||
this.saveDefaultConfig();
|
||||
|
||||
this.limitManager.loadLimits(YamlConfiguration.loadConfiguration(new InputStreamReader(this.getClass().getResourceAsStream("/limits.yml")))); // Always load from JAR
|
||||
this.signShopManager.loadSignShops();
|
||||
|
||||
this.getServer().getScheduler().scheduleSyncRepeatingTask(this, this.limitManager::incrementLimitsHourly, 0, 20 * 60 * 60);
|
||||
|
||||
this.getServer().getPluginManager().registerEvents(new SignChangeListener(this), this);
|
||||
this.getServer().getPluginManager().registerEvents(new InteractListener(this), this);
|
||||
this.getServer().getPluginManager().registerEvents(new BreakListener(this), this);
|
||||
}
|
||||
|
||||
public SignShopManager getSignShopManager() {
|
||||
return this.signShopManager;
|
||||
}
|
||||
|
||||
public ISaneEconomy getSaneEconomy() {
|
||||
return this.saneEconomy;
|
||||
}
|
||||
|
||||
public LimitManager getLimitManager() {
|
||||
return this.limitManager;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package org.appledash.saneeconomysignshop.listeners;
|
||||
|
||||
import org.appledash.saneeconomysignshop.SaneEconomySignShop;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/16/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class BreakListener implements Listener {
|
||||
private final SaneEconomySignShop plugin;
|
||||
|
||||
public BreakListener(SaneEconomySignShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent evt) {
|
||||
this.plugin.getSignShopManager().getSignShop(evt.getBlock().getLocation()).ifPresent((shop) -> {
|
||||
if (!evt.getPlayer().hasPermission("saneeconomy.signshop.destroy.admin")) {
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "You may not destroy that!");
|
||||
evt.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getSignShopManager().removeSignShop(shop);
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "Sign shop destroyed!");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
package org.appledash.saneeconomysignshop.listeners;
|
||||
|
||||
import org.appledash.saneeconomy.economy.EconomyManager;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionResult;
|
||||
import org.appledash.saneeconomysignshop.SaneEconomySignShop;
|
||||
import org.appledash.saneeconomysignshop.signshop.ShopTransaction;
|
||||
import org.appledash.saneeconomysignshop.signshop.ShopTransaction.TransactionDirection;
|
||||
import org.appledash.saneeconomysignshop.signshop.SignShop;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/3/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class InteractListener implements Listener {
|
||||
private static final Logger LOGGER = Logger.getLogger("SignShop");
|
||||
private final SaneEconomySignShop plugin;
|
||||
|
||||
public InteractListener(SaneEconomySignShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent evt) {
|
||||
if (!evt.getPlayer().hasPermission("saneeconomy.signshop.use")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.getHand() != EquipmentSlot.HAND) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((evt.getPlayer().getInventory().getItemInMainHand() != null) && (evt.getPlayer().getInventory().getItemInMainHand().getType() == Material.DIAMOND_AXE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((evt.getAction() != Action.RIGHT_CLICK_BLOCK) && (evt.getAction() != Action.LEFT_CLICK_BLOCK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<SignShop> shopOptional = this.plugin.getSignShopManager().getSignShop(evt.getClickedBlock().getLocation());
|
||||
|
||||
if (!shopOptional.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SignShop shop = shopOptional.get();
|
||||
|
||||
// Buy
|
||||
if (evt.getAction() == Action.RIGHT_CLICK_BLOCK) {
|
||||
evt.setCancelled(true);
|
||||
if (!shop.canBuy()) {
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "This shop does not permit buying.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.doBuy(shop, evt.getPlayer());
|
||||
}
|
||||
|
||||
// Sell
|
||||
if (evt.getAction() == Action.LEFT_CLICK_BLOCK) {
|
||||
evt.setCancelled(true);
|
||||
if (!shop.canSell()) {
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "This shop does not permit selling.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.doSell(shop, evt.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
private void doBuy(SignShop shop, Player player) {
|
||||
EconomyManager ecoMan = this.plugin.getSaneEconomy().getEconomyManager();
|
||||
int quantity = player.isSneaking() ? 1 : shop.getQuantity();
|
||||
|
||||
ShopTransaction shopTransaction = shop.makeTransaction(ecoMan.getCurrency(), player, TransactionDirection.BUY, quantity);
|
||||
|
||||
/* No buy limits for now!
|
||||
if (!plugin.getLimitManager().shouldAllowTransaction(shopTransaction)) {
|
||||
MessageUtils.sendMessage(player, "You have reached your buying limit for the time being. Try back in an hour or so.");
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getLimitManager().setRemainingLimit(player, ShopTransaction.TransactionDirection.BUY, shop.getItem(), plugin.getLimitManager().getRemainingLimit(player, ShopTransaction.TransactionDirection.BUY, shop.getItem()) - quantity);
|
||||
*/
|
||||
|
||||
Transaction ecoTransaction = shopTransaction.makeEconomyTransaction();
|
||||
TransactionResult result = ecoMan.transact(ecoTransaction);
|
||||
|
||||
if (result.getStatus() != TransactionResult.Status.SUCCESS) {
|
||||
this.plugin.getMessenger().sendMessage(player, "An error occurred attempting to perform that transaction: {1}", result.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack stack = new ItemStack(shop.getItemStack()); /* Clone it so we don't modify the stack size in the shop */
|
||||
stack.setAmount(quantity);
|
||||
player.getInventory().addItem(stack);
|
||||
|
||||
this.plugin.getMessenger().sendMessage(player, "You have bought {1} {2} for {3}.", quantity, shop.getItemStack().getType().name(), ecoMan.getCurrency().formatAmount(shopTransaction.getPrice()));
|
||||
LOGGER.info(String.format("%s just bought %d %s for %s.", player.getName(), quantity, shop.getItemStack().getType().name(), ecoMan.getCurrency().formatAmount(shopTransaction.getPrice())));
|
||||
}
|
||||
|
||||
private void doSell(SignShop shop, Player player) { // TODO: Selling enchanted items
|
||||
EconomyManager ecoMan = this.plugin.getSaneEconomy().getEconomyManager();
|
||||
int quantity = player.isSneaking() ? 1 : shop.getQuantity();
|
||||
BigDecimal price = shop.getSellPrice(quantity);
|
||||
|
||||
if (!player.getInventory().containsAtLeast(new ItemStack(shop.getItemStack()), quantity)) {
|
||||
this.plugin.getMessenger().sendMessage(player, "You do not have {1} {2}!", quantity, shop.getItemStack().getType().name());
|
||||
return;
|
||||
}
|
||||
|
||||
ShopTransaction shopTransaction = shop.makeTransaction(ecoMan.getCurrency(), player, TransactionDirection.SELL, quantity);
|
||||
|
||||
if (!this.plugin.getLimitManager().shouldAllowTransaction(shopTransaction)) {
|
||||
this.plugin.getMessenger().sendMessage(player, "You have reached your selling limit for the time being. Try back in an hour or so.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.getLimitManager().setRemainingLimit(player, TransactionDirection.SELL, shop.getItem(), this.plugin.getLimitManager().getRemainingLimit(player, TransactionDirection.SELL, shop.getItem()) - quantity);
|
||||
|
||||
|
||||
ItemStack stack = new ItemStack(shop.getItemStack()); /* Clone it so we don't modify the stack size in the shop */
|
||||
stack.setAmount(quantity);
|
||||
player.getInventory().removeItem(stack); // FIXME: This does not remove items with damage values that were detected by contains()
|
||||
|
||||
ecoMan.transact(shopTransaction.makeEconomyTransaction());
|
||||
|
||||
this.plugin.getMessenger().sendMessage(player, "You have sold {1} {2} for {3}.", quantity, shop.getItemStack().getType().name(), ecoMan.getCurrency().formatAmount(price));
|
||||
LOGGER.info(String.format("%s just sold %d %s for %s.", player.getName(), quantity, shop.getItemStack().getType().name(), ecoMan.getCurrency().formatAmount(price)));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package org.appledash.saneeconomysignshop.listeners;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.appledash.saneeconomysignshop.SaneEconomySignShop;
|
||||
import org.appledash.saneeconomysignshop.signshop.SignShop;
|
||||
import org.appledash.saneeconomysignshop.util.ItemDatabase;
|
||||
import org.appledash.saneeconomysignshop.util.ItemDatabase.InvalidItemException;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/2/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SignChangeListener implements Listener {
|
||||
private final SaneEconomySignShop plugin;
|
||||
|
||||
public SignChangeListener(SaneEconomySignShop plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onSignChange(SignChangeEvent evt) {
|
||||
if (!evt.getPlayer().hasPermission("saneeconomy.signshop.create.admin")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsedSignShop pss = this.parseSignShop(evt);
|
||||
|
||||
if (pss.error != null) {
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "Cannot create shop: {1}", pss.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pss.shop == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SignShop signShop = pss.shop;
|
||||
this.plugin.getSignShopManager().addSignShop(signShop);
|
||||
evt.setLine(0, ChatColor.translateAlternateColorCodes('&', this.plugin.getConfig().getString("admin-shop-title")));
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "Sign shop created!");
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "Item: {1} x {2}", signShop.getQuantity(), signShop.getItemStack());
|
||||
|
||||
if (signShop.canBuy()) { // The player be buying from the shop, not the other way around.
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "Will sell to players for {1}.",
|
||||
this.plugin.getSaneEconomy().getEconomyManager().getCurrency().formatAmount(signShop.getBuyPrice())
|
||||
);
|
||||
}
|
||||
|
||||
if (signShop.canSell()) { // The player be selling to the shop, not the other way around.
|
||||
this.plugin.getMessenger().sendMessage(evt.getPlayer(), "Will buy from players for {1}.",
|
||||
this.plugin.getSaneEconomy().getEconomyManager().getCurrency().formatAmount(signShop.getSellPrice())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private ParsedSignShop parseSignShop(SignChangeEvent evt) {
|
||||
String[] lines = evt.getLines();
|
||||
Player player = evt.getPlayer();
|
||||
Location location = evt.getBlock().getLocation();
|
||||
|
||||
if ((lines[0] == null) || !lines[0].equalsIgnoreCase(this.plugin.getConfig().getString("admin-shop-trigger"))) { // First line must contain the trigger
|
||||
return new ParsedSignShop();
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(lines[1])) { // Second line must contain an item name
|
||||
return new ParsedSignShop("No item name specified.");
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(lines[2])) { // Second line must contain buy/sell prices
|
||||
return new ParsedSignShop("No buy/sell price(s) specified.");
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(lines[3])) { // Third line must contain item amount.
|
||||
return new ParsedSignShop("No item amount specified.");
|
||||
}
|
||||
|
||||
String itemName = lines[1];
|
||||
String buySellRaw = lines[2];
|
||||
String amountRaw = lines[3];
|
||||
|
||||
ItemStack itemStack;
|
||||
try {
|
||||
itemStack = ItemDatabase.parseGive(itemName);
|
||||
} catch (InvalidItemException e) {
|
||||
return new ParsedSignShop("Invalid item name or ID specified.");
|
||||
}
|
||||
|
||||
Matcher m = Pattern.compile("(B:(?<buy>[0-9.]+))?[ ]*(S:(?<sell>[0-9.]+))?").matcher(buySellRaw.trim());
|
||||
|
||||
if (!m.matches()) {
|
||||
return new ParsedSignShop("Invalid buy/sell prices specified.");
|
||||
}
|
||||
|
||||
double buy = Strings.isNullOrEmpty(m.group("buy")) ? -1.0 : Double.parseDouble(m.group("buy"));
|
||||
double sell = Strings.isNullOrEmpty(m.group("sell")) ? -1.0 : Double.parseDouble(m.group("sell"));
|
||||
|
||||
if ((buy == -1) && (sell == -1)) {
|
||||
return new ParsedSignShop("Buy and sell amounts for this shop are both invalid.");
|
||||
}
|
||||
|
||||
int itemAmount;
|
||||
|
||||
try {
|
||||
itemAmount = Integer.parseInt(amountRaw);
|
||||
|
||||
if (itemAmount <= 0) {
|
||||
throw new NumberFormatException();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return new ParsedSignShop("Item amount is not a positive integer.");
|
||||
}
|
||||
|
||||
return new ParsedSignShop(new SignShop(player.getUniqueId(), location, itemStack, itemAmount, buy, sell));
|
||||
}
|
||||
|
||||
private static final class ParsedSignShop {
|
||||
private SignShop shop;
|
||||
private String error;
|
||||
|
||||
private ParsedSignShop(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
private ParsedSignShop() {
|
||||
|
||||
}
|
||||
|
||||
private ParsedSignShop(SignShop shop) {
|
||||
this.shop = shop;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package org.appledash.saneeconomysignshop.signshop;
|
||||
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.appledash.saneeconomy.economy.economable.Economable;
|
||||
import org.appledash.saneeconomy.economy.transaction.Transaction;
|
||||
import org.appledash.saneeconomy.economy.transaction.TransactionReason;
|
||||
import org.appledash.saneeconomysignshop.util.ItemInfo;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/1/17.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class ShopTransaction {
|
||||
private final Currency currency;
|
||||
// Direction is always what the player is doing. BUY = player is buying from shop.
|
||||
private final TransactionDirection direction;
|
||||
private final Player player;
|
||||
private final ItemInfo item;
|
||||
private final int quantity;
|
||||
private final BigDecimal price;
|
||||
|
||||
public ShopTransaction(Currency currency, TransactionDirection direction, Player player, ItemInfo item, int quantity, BigDecimal price) {
|
||||
this.currency = currency;
|
||||
this.direction = direction;
|
||||
this.player = player;
|
||||
this.item = item;
|
||||
this.quantity = quantity;
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public TransactionDirection getDirection() {
|
||||
return this.direction;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public ItemInfo getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
public int getQuantity() {
|
||||
return this.quantity;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
public Transaction makeEconomyTransaction() {
|
||||
if (this.direction == TransactionDirection.BUY) {
|
||||
return new Transaction(this.currency, Economable.wrap(this.player), Economable.PLUGIN, this.price, TransactionReason.PLUGIN_TAKE);
|
||||
} else {
|
||||
return new Transaction(this.currency, Economable.PLUGIN, Economable.wrap(this.player), this.price, TransactionReason.PLUGIN_GIVE);
|
||||
}
|
||||
}
|
||||
|
||||
public enum TransactionDirection {
|
||||
BUY, SELL
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package org.appledash.saneeconomysignshop.signshop;
|
||||
|
||||
import org.appledash.saneeconomy.economy.Currency;
|
||||
import org.appledash.saneeconomysignshop.signshop.ShopTransaction.TransactionDirection;
|
||||
import org.appledash.saneeconomysignshop.util.ItemInfo;
|
||||
import org.appledash.saneeconomysignshop.util.SerializableLocation;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/2/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SignShop implements Serializable {
|
||||
private final UUID ownerUuid;
|
||||
private final SerializableLocation location;
|
||||
private final ItemInfo item;
|
||||
private final int quantity;
|
||||
private final BigDecimal buyPrice;
|
||||
private final BigDecimal sellPrice;
|
||||
|
||||
public SignShop(UUID ownerUuid, Location location, ItemStack item, int quantity, double buyPrice, double sellPrice) {
|
||||
if ((ownerUuid == null) || (location == null) || (item == null)) {
|
||||
throw new IllegalArgumentException("ownerUuid, location, and item must not be null.");
|
||||
}
|
||||
|
||||
if (quantity < 1) {
|
||||
throw new IllegalArgumentException("Quantity must be greater than zero.");
|
||||
}
|
||||
|
||||
this.ownerUuid = ownerUuid;
|
||||
this.location = new SerializableLocation(location);
|
||||
this.item = new ItemInfo(item);
|
||||
this.quantity = quantity;
|
||||
this.buyPrice = BigDecimal.valueOf(buyPrice);
|
||||
this.sellPrice = BigDecimal.valueOf(sellPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the in-world Location of this SignShop
|
||||
* @return Location
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return this.location.getBukkitLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of item this SignShop is selling
|
||||
* @return Material representing item/block type
|
||||
*/
|
||||
public ItemStack getItemStack() {
|
||||
return this.item.toItemStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ItemInfo for the item this SignShop is selling
|
||||
* @return ItemInfo representing the type and quantity of item
|
||||
*/
|
||||
public ItemInfo getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price that the player can buy this item from the server for
|
||||
* @return Buy price for this.getQuantity() items
|
||||
*/
|
||||
public BigDecimal getBuyPrice() {
|
||||
return this.buyPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price that the player can sell this item to the server for
|
||||
* @return Buy price for this.getQuantity() items
|
||||
*/
|
||||
public BigDecimal getSellPrice() {
|
||||
return this.sellPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price that the player can buy a specific number of this item from the server for.
|
||||
* Scales based on the defined price for the defined quantity.
|
||||
* @param quantity Quantity of items to price
|
||||
* @return Price to buy that number of items at this shop
|
||||
*/
|
||||
public BigDecimal getBuyPrice(int quantity) {
|
||||
return this.buyPrice.multiply(BigDecimal.valueOf((double) quantity / this.quantity)); // TODO: Is this okay?
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price that the player can sell a specific number of this item to the server for.
|
||||
* Scales based on the defined price for the defined quantity.
|
||||
* @param quantity Quantity of items to price
|
||||
* @return Price to sell that number of items at this shop
|
||||
*/
|
||||
public BigDecimal getSellPrice(int quantity) {
|
||||
return this.sellPrice.multiply(BigDecimal.valueOf((double) quantity / this.quantity)); // TODO: Is this okay?
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if anyone can buy items from this shop
|
||||
* @return True if they can, false if they can't
|
||||
*/
|
||||
public boolean canBuy() {
|
||||
return this.buyPrice.compareTo(BigDecimal.ZERO) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if anyone can sell items to this shop
|
||||
* @return True if they can, false if they can't
|
||||
*/
|
||||
public boolean canSell() {
|
||||
return this.sellPrice.compareTo(BigDecimal.ZERO) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UUID of the player that created this SignShop
|
||||
* @return UUID
|
||||
*/
|
||||
public UUID getOwnerUuid() {
|
||||
return this.ownerUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of items that this shop will sell
|
||||
* @return Number of items
|
||||
*/
|
||||
public int getQuantity() {
|
||||
return this.quantity;
|
||||
}
|
||||
|
||||
public ShopTransaction makeTransaction(Currency currency, Player player, TransactionDirection direction, int quantity) {
|
||||
return new ShopTransaction(currency, direction, player, this.item, quantity, (direction == TransactionDirection.BUY) ? this.getBuyPrice(quantity) : this.getSellPrice(quantity));
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.appledash.saneeconomysignshop.signshop;
|
||||
|
||||
import org.appledash.saneeconomysignshop.signshop.storage.SignShopStorage;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/2/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SignShopManager {
|
||||
private final SignShopStorage storage;
|
||||
|
||||
public SignShopManager(SignShopStorage storage) {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public void loadSignShops() {
|
||||
this.storage.loadSignShops();
|
||||
}
|
||||
|
||||
public void addSignShop(SignShop signShop) {
|
||||
this.storage.putSignShop(signShop);
|
||||
}
|
||||
|
||||
public void removeSignShop(SignShop signShop) {
|
||||
this.storage.removeSignShop(signShop);
|
||||
}
|
||||
|
||||
public Optional<SignShop> getSignShop(Location location) {
|
||||
return Optional.ofNullable(this.storage.getSignShops().get(location));
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.appledash.saneeconomysignshop.signshop.storage;
|
||||
|
||||
import org.appledash.saneeconomysignshop.signshop.SignShop;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/6/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public interface SignShopStorage {
|
||||
void loadSignShops();
|
||||
void putSignShop(SignShop signShop);
|
||||
void removeSignShop(SignShop signShop);
|
||||
Map<Location, SignShop> getSignShops();
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package org.appledash.saneeconomysignshop.signshop.storage;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.appledash.saneeconomysignshop.signshop.SignShop;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/18/17.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class SignShopStorageJSON implements SignShopStorage {
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
|
||||
private final File storageFile;
|
||||
private final Map<Location, SignShop> cachedSignShops = new HashMap<>();
|
||||
|
||||
public SignShopStorageJSON(File storageFile) {
|
||||
this.storageFile = storageFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadSignShops() {
|
||||
if (!this.storageFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
List<SignShop> tempShops = this.gson.fromJson(new FileReader(this.storageFile), new TypeToken<List<SignShop>>() {} .getType());
|
||||
tempShops.forEach((shop) -> this.cachedSignShops.put(shop.getLocation(), shop));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalStateException("This shouldn't happen - the file " + this.storageFile.getAbsolutePath() + " disappeared while we were trying to read it!", e);
|
||||
}
|
||||
|
||||
this.saveSignShops();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putSignShop(SignShop signShop) {
|
||||
this.cachedSignShops.put(signShop.getLocation(), signShop);
|
||||
this.saveSignShops();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSignShop(SignShop signShop) {
|
||||
this.cachedSignShops.remove(signShop.getLocation());
|
||||
this.saveSignShops();
|
||||
}
|
||||
|
||||
private synchronized void saveSignShops() {
|
||||
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(this.storageFile, false))) {
|
||||
bufferedWriter.write(this.gson.toJson(ImmutableList.copyOf(this.cachedSignShops.values())));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to save sign shops!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Location, SignShop> getSignShops() {
|
||||
return ImmutableMap.copyOf(this.cachedSignShops);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/1/17.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class DefaultHashMap<K, V> extends HashMap<K, V> {
|
||||
private final KeyBasedSupplier<K, V> defaultSupplier;
|
||||
|
||||
public DefaultHashMap(Supplier<V> defaultSupplier) {
|
||||
this((k) -> defaultSupplier.get());
|
||||
}
|
||||
|
||||
public DefaultHashMap(KeyBasedSupplier<K, V> supplier) {
|
||||
this.defaultSupplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public V get(Object key) {
|
||||
V value = super.get(key);
|
||||
|
||||
if (value == null) {
|
||||
value = this.defaultSupplier.get((K)key);
|
||||
this.put((K) key, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public interface KeyBasedSupplier<K, V> {
|
||||
V get(K k);
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Created by appledash on 8/3/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public final class ItemDatabase {
|
||||
private static Map<String, Pair<Integer, Short>> itemMap = new HashMap<>();
|
||||
|
||||
private ItemDatabase() {
|
||||
}
|
||||
|
||||
public static void initItemDB() {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(ItemDatabase.class.getResourceAsStream("/items.csv")))) {
|
||||
String line;
|
||||
|
||||
//noinspection NestedAssignment
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.startsWith("#") || !line.contains(",")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] split = line.split(",");
|
||||
String name = split[0];
|
||||
int id = Integer.parseInt(split[1]);
|
||||
short damage = Short.parseShort(split[2]);
|
||||
|
||||
itemMap.put(name.toLowerCase(), Pair.of(id, damage));
|
||||
}
|
||||
|
||||
itemMap = ImmutableMap.copyOf(itemMap);
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
throw new RuntimeException("Failed to initialize item database!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Pair<Material, Short>> getIDAndDamageForName(String name) {
|
||||
if (Material.getMaterial(name) != null) {
|
||||
return Optional.of(Pair.of(Material.getMaterial(name), (short) 0));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
// TODO
|
||||
//return Optional.ofNullable(itemMap.get(name.toLowerCase()));
|
||||
}
|
||||
|
||||
public static ItemStack parseGive(String rawItemName) throws InvalidItemException {
|
||||
String itemName;
|
||||
short damage;
|
||||
|
||||
if (rawItemName.contains(":")) {
|
||||
String[] splitItemName = rawItemName.split(":");
|
||||
itemName = splitItemName[0];
|
||||
if (splitItemName.length == 1) { // They just typed 'tnt:'
|
||||
damage = 0;
|
||||
} else { // They typed 'tnt:something'
|
||||
try {
|
||||
damage = Short.parseShort(splitItemName[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidItemException("Damage value must be a number.");
|
||||
}
|
||||
}
|
||||
} else { // No damage value
|
||||
itemName = rawItemName;
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
Optional<Material> materialOptional = parseMaterialFromName(itemName);
|
||||
|
||||
if (!materialOptional.isPresent()) {
|
||||
Optional<Pair<Material, Short>> parsedItem = getIDAndDamageForName(normalizeItemName(itemName));
|
||||
if (!parsedItem.isPresent()) {
|
||||
throw new InvalidItemException("Item by that name does not exist.");
|
||||
}
|
||||
|
||||
if (damage == 0) {
|
||||
damage = parsedItem.get().getRight();
|
||||
}
|
||||
|
||||
return new ItemStack(parsedItem.get().getLeft(), 1, damage);
|
||||
}
|
||||
|
||||
return new ItemStack(materialOptional.get(), 1, damage);
|
||||
|
||||
}
|
||||
|
||||
private static Optional<Material> parseMaterialFromName(String materialName) {
|
||||
for (Material mat : Material.values()) {
|
||||
if (normalizeItemName(mat.name()).equals(normalizeItemName(materialName))) {
|
||||
return Optional.of(mat);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static String normalizeItemName(String itemName) {
|
||||
return itemName.toLowerCase().replace("_", "").replace(" ", "");
|
||||
}
|
||||
|
||||
public static class InvalidItemException extends Exception {
|
||||
public InvalidItemException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Created by appledash on 11/3/16.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class ItemInfo implements Serializable {
|
||||
private final Material material;
|
||||
private final short damage;
|
||||
private final int amount;
|
||||
|
||||
public ItemInfo(ItemStack stack) {
|
||||
this(stack.getType(), stack.getDurability(), stack.getAmount());
|
||||
}
|
||||
|
||||
public ItemInfo(Material material, short damage, int amount) {
|
||||
this.material = material;
|
||||
this.damage = damage;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public ItemStack toItemStack() {
|
||||
return new ItemStack(this.material, this.amount, this.damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ItemInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemInfo other = ((ItemInfo) o);
|
||||
|
||||
return (other.material == this.material) && (other.damage == this.damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.material, this.damage);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/1/17.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class ItemLimits {
|
||||
// The default limit for items that have no limit.
|
||||
public static final ItemLimits DEFAULT = new ItemLimits(10, 1);
|
||||
|
||||
private final int limit;
|
||||
private final int hourlyGain;
|
||||
|
||||
public ItemLimits(int limit, int hourlyGain) {
|
||||
this.limit = limit;
|
||||
this.hourlyGain = hourlyGain;
|
||||
}
|
||||
|
||||
public int getHourlyGain() {
|
||||
return this.hourlyGain;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return this.limit;
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
import org.appledash.saneeconomysignshop.signshop.ShopTransaction;
|
||||
import org.appledash.saneeconomysignshop.signshop.ShopTransaction.TransactionDirection;
|
||||
import org.appledash.saneeconomysignshop.util.ItemDatabase.InvalidItemException;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/1/17.
|
||||
* Blackjack is still best pony.
|
||||
*/
|
||||
public class LimitManager {
|
||||
private static final Logger LOGGER = Logger.getLogger("LimitManager");
|
||||
// private final Map<ItemInfo, ItemLimits> buyItemLimits = new DefaultHashMap<ItemInfo, ItemLimits>(() -> ItemLimits.DEFAULT);
|
||||
private final Map<ItemInfo, ItemLimits> sellItemLimits = new DefaultHashMap<>(() -> ItemLimits.DEFAULT);
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private final Map<UUID, Map<ItemInfo, Integer>> sellPlayerLimits = new DefaultHashMap<>(() -> new DefaultHashMap<>((info) -> this.sellItemLimits.get(info).getLimit()));
|
||||
// private final Map<TransactionDirection, Map<ItemInfo, ItemLimits>> itemLimits = new DefaultHashMap<>(() -> new DefaultHashMap<>(() -> ItemLimits.DEFAULT));
|
||||
// This is a slightly complex data structure. It works like this:
|
||||
// It's a map of (limit types to (maps of players to (maps of materials to the remaining limit))).
|
||||
// All the TransactionDirections defaults to an empty map, which defaults to an empty map, which defaults to 0.
|
||||
// @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
// private final Map<TransactionDirection, Map<UUID, Map<ItemInfo, Integer>>> playerLimits = new DefaultHashMap<>(() -> new DefaultHashMap<>(() -> new DefaultHashMap<>(() -> 0)));
|
||||
|
||||
public int getRemainingLimit(Player player, TransactionDirection type, ItemInfo stack) {
|
||||
if (type == TransactionDirection.SELL) {
|
||||
return this.sellPlayerLimits.get(player.getUniqueId()).get(stack);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Don't know how to get limits for that TransactionDirection!");
|
||||
}
|
||||
|
||||
public void setRemainingLimit(Player player, TransactionDirection type, ItemInfo stack, int limit) {
|
||||
if (type == TransactionDirection.SELL) {
|
||||
if (this.sellPlayerLimits.get(player.getUniqueId()).get(stack) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
limit = Math.min(limit, this.sellItemLimits.get(stack).getLimit());
|
||||
limit = Math.max(0, limit);
|
||||
|
||||
this.sellPlayerLimits.get(player.getUniqueId()).put(stack, limit);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Don't know how to set limits for that TransactionDirection!");
|
||||
}
|
||||
|
||||
public boolean shouldAllowTransaction(ShopTransaction transaction) {
|
||||
// System.out.printf("Limit: %d, quantity: %d\n", limit, transaction.getQuantity());
|
||||
return this.getRemainingLimit(transaction.getPlayer(), transaction.getDirection(), transaction.getItem()) >= transaction.getQuantity();
|
||||
}
|
||||
|
||||
public void incrementLimitsHourly() {
|
||||
// For every limit type
|
||||
// For every player
|
||||
// For every limit
|
||||
// Increment limit by the limit for the specific direction and item.
|
||||
|
||||
this.sellPlayerLimits.forEach((playerUuid, itemToLimit) -> {
|
||||
Map<ItemInfo, Integer> newLimits = new HashMap<>();
|
||||
|
||||
itemToLimit.forEach((itemInfo, currentLimit) ->
|
||||
newLimits.put(itemInfo, currentLimit + (this.sellItemLimits.get(itemInfo).getHourlyGain())));
|
||||
|
||||
itemToLimit.putAll(newLimits);
|
||||
});
|
||||
}
|
||||
|
||||
public void loadLimits(ConfigurationSection config) {
|
||||
for (Map<?, ?> map : config.getMapList("sell")) {
|
||||
String itemName = String.valueOf(map.get("item"));
|
||||
int sellLimit = Integer.parseInt(String.valueOf(map.get("limit")));
|
||||
int hourlyGain = Integer.parseInt(String.valueOf(map.get("gain")));
|
||||
ItemStack stack;
|
||||
|
||||
try {
|
||||
stack = ItemDatabase.parseGive(itemName);
|
||||
} catch (InvalidItemException e) {
|
||||
LOGGER.warning(String.format("You tried to load the item '%s' in limits.yml, but I have no idea what that is.", map.get("item")));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ItemInfo itemInfo = new ItemInfo(stack);
|
||||
|
||||
this.sellItemLimits.put(itemInfo, new ItemLimits(sellLimit, hourlyGain));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
/**
|
||||
* Created by appledash on 1/18/17.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class Pair<K, V> {
|
||||
private final K left;
|
||||
private final V right;
|
||||
|
||||
public Pair(K left, V right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
public K getLeft() {
|
||||
return this.left;
|
||||
}
|
||||
|
||||
public V getRight() {
|
||||
return this.right;
|
||||
}
|
||||
|
||||
public static <K, V> Pair<K, V> of(K k, V v) {
|
||||
return new Pair<>(k, v);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.appledash.saneeconomysignshop.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by appledash on 10/17/16.
|
||||
* Blackjack is best pony.
|
||||
*/
|
||||
public class SerializableLocation implements Serializable {
|
||||
private final double x;
|
||||
private final double y;
|
||||
private final double z;
|
||||
private final float yaw;
|
||||
private final float pitch;
|
||||
private final UUID worldUuid;
|
||||
|
||||
public SerializableLocation(Location location) {
|
||||
this.x = location.getX();
|
||||
this.y = location.getY();
|
||||
this.z = location.getZ();
|
||||
this.yaw = location.getYaw();
|
||||
this.pitch = location.getPitch();
|
||||
this.worldUuid = location.getWorld().getUID();
|
||||
}
|
||||
|
||||
public Location getBukkitLocation() {
|
||||
return new Location(Bukkit.getServer().getWorld(this.worldUuid), this.x, this.y, this.z, this.yaw, this.pitch);
|
||||
}
|
||||
}
|
4
SaneEconomySignShop/src/main/resources/config.yml
Normal file
4
SaneEconomySignShop/src/main/resources/config.yml
Normal file
@ -0,0 +1,4 @@
|
||||
admin-shop-trigger: '[Shop]'
|
||||
admin-shop-title: '&8[&6Shop&8]&r'
|
||||
chat:
|
||||
prefix: '&b[&9Shops&b]&r '
|
8349
SaneEconomySignShop/src/main/resources/items.csv
Normal file
8349
SaneEconomySignShop/src/main/resources/items.csv
Normal file
File diff suppressed because it is too large
Load Diff
449
SaneEconomySignShop/src/main/resources/limits.yml
Normal file
449
SaneEconomySignShop/src/main/resources/limits.yml
Normal file
@ -0,0 +1,449 @@
|
||||
sell:
|
||||
- item: APPLE
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: CARROT_ITEM
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: MELON
|
||||
limit: 6400
|
||||
gain: 640
|
||||
- item: POTATO
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: BEETROOT
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: PUMPKIN
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: MELON_BLOCK
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: BROWN_MUSHROOM
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_MUSHROOM
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: BREAD
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: FISH:0
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: FISH:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: FISH:2
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: FISH:3
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: RABBIT
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RAW_BEEF
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: MUTTON
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: PORKCHOP
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RAW_CHICKEN
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: DYE:3
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: EGG
|
||||
limit: 480
|
||||
gain: 48
|
||||
- item: WHEAT
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: SUGAR
|
||||
limit: 1600
|
||||
gain: 160
|
||||
|
||||
- item: SAPLING:0
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SAPLING:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SAPLING:2
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SAPLING:3
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SAPLING:4
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SAPLING:5
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: DEADBUSH
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LEAVES:0
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: LEAVES:1
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: LEAVES:2
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: LEAVES:3
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: VINE
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: TALLGRASS:1
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: TALLGRASS:2
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:4
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:5
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:6
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:7
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:3
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:2
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:1
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:8
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: RED_FLOWER:0
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: YELLOW_FLOWER
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: DOUBLE_PLANT:1
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: DOUBLE_PLANT:0
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: DOUBLE_PLANT:5
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: DOUBLE_PLANT:4
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: WATERLILY
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: REEDS
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: CACTUS
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SEEDS
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: PUMPKIN_SEEDS
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: MELON_SEEDS
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: BEETROOT_SEEDS
|
||||
limit: 1600
|
||||
gain: 160
|
||||
|
||||
- item: GRASS
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: DIRT:0
|
||||
limit: 4800
|
||||
gain: 480
|
||||
- item: DIRT:1
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: DIRT:2
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: MYCELIUM
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SAND:0
|
||||
limit: 4800
|
||||
gain: 480
|
||||
- item: SAND:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SANDSTONE
|
||||
limit: 3200
|
||||
gain: 320
|
||||
- item: RED_SANDSTONE
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: STONE:0
|
||||
limit: 6400
|
||||
gain: 640
|
||||
- item: COBBLESTONE
|
||||
limit: 6400
|
||||
gain: 640
|
||||
- item: STONE:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: STONE:3
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: STONE:5
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: GRAVEL
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: CLAY
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: HARDENED_CLAY
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: BRICK_BLOCK
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LOG:0
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LOG:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LOG:2
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LOG:3
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LOG2:0
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: LOG2:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: GLASS
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SNOW
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: PACKED_ICE
|
||||
limit: 640
|
||||
gain: 64
|
||||
- item: OBSIDIAN
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: NETHER_BRICK
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: QUARTZ_BLOCK
|
||||
limit: 1280
|
||||
gain: 128
|
||||
- item: SOUL_SAND
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: GLOWSTONE
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: SEA_LANTERN
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: PRISMARINE:0
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: PRISMARINE:1
|
||||
limit: 1600
|
||||
gain: 160
|
||||
- item: PRISMARINE:2
|
||||
limit: 1600
|
||||
gain: 160
|
||||
|
||||
- item: COAL
|
||||
limit: 6400
|
||||
gain: 320
|
||||
- item: IRON_INGOT
|
||||
limit: 2560
|
||||
gain: 64
|
||||
- item: GOLD_INGOT
|
||||
limit: 1280
|
||||
gain: 32
|
||||
- item: DIAMOND
|
||||
limit: 640
|
||||
gain: 32
|
||||
- item: REDSTONE
|
||||
limit: 3200
|
||||
gain: 160
|
||||
- item: DYE:4
|
||||
limit: 3200
|
||||
gain: 160
|
||||
- item: EMERALD
|
||||
limit: 1280
|
||||
gain: 64
|
||||
- item: QUARTZ
|
||||
limit: 1600
|
||||
gain: 100
|
||||
- item: PAPER
|
||||
limit: 1600
|
||||
gain: 100
|
||||
- item: FEATHER
|
||||
limit: 1600
|
||||
gain: 100
|
||||
- item: LEATHER
|
||||
limit: 1600
|
||||
gain: 100
|
||||
- item: RABBIT_FOOT
|
||||
limit: 1600
|
||||
gain: 100
|
||||
- item: FLINT
|
||||
limit: 1600
|
||||
gain: 100
|
||||
- item: ROTTEN_FLESH
|
||||
limit: 2560
|
||||
gain: 128
|
||||
- item: BONE
|
||||
limit: 2560
|
||||
gain: 128
|
||||
- item: STRING
|
||||
limit: 2560
|
||||
gain: 128
|
||||
- item: SPIDER_EYE
|
||||
limit: 2560
|
||||
gain: 128
|
||||
- item: GUNPOWDER
|
||||
limit: 2560
|
||||
gain: 125
|
||||
- item: SLIME_BALL
|
||||
limit: 2560
|
||||
gain: 128
|
||||
- item: ENDER_PEARL
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: BLAZE_ROD
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: GHAST_TEAR
|
||||
limit: 640
|
||||
gain: 32
|
||||
|
||||
- item: WOOL:14
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:1
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:4
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:5
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:13
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:9
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:3
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:11
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:10
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:2
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:6
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:12
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:0
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:8
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:7
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: WOOL:15
|
||||
limit: 1600
|
||||
gain: 64
|
||||
- item: STAINED_CLAY:14
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:1
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:4
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:5
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:13
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:9
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:3
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:11
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:10
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:2
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:6
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:12
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:0
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:8
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:7
|
||||
limit: 1600
|
||||
gain: 80
|
||||
- item: STAINED_CLAY:15
|
||||
limit: 1600
|
||||
gain: 80
|
5
SaneEconomySignShop/src/main/resources/plugin.yml
Normal file
5
SaneEconomySignShop/src/main/resources/plugin.yml
Normal file
@ -0,0 +1,5 @@
|
||||
name: SaneEconomySignShop
|
||||
main: org.appledash.saneeconomysignshop.SaneEconomySignShop
|
||||
author: AppleDash
|
||||
version: ${project.version}
|
||||
depend: [SaneEconomy]
|
9
astyle.conf
Normal file
9
astyle.conf
Normal file
@ -0,0 +1,9 @@
|
||||
--mode=java
|
||||
--style=java # Bracket formatting
|
||||
--indent=spaces=4
|
||||
--indent-switches
|
||||
--pad-oper
|
||||
--pad-header
|
||||
--add-brackets
|
||||
--suffix=none # Do not backup original file
|
||||
--lineend=linux
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user