├── src └── main │ ├── resources │ ├── config.yml │ ├── plugin.yml │ └── lang │ │ ├── zh_CN.yml │ │ └── en_US.yml │ └── java │ └── cat │ └── nyaa │ └── nyaabank │ ├── database │ ├── enums │ │ ├── AccountType.java │ │ ├── InterestType.java │ │ ├── BankStatus.java │ │ └── TransactionType.java │ ├── tables │ │ ├── BankAccount.java │ │ ├── PartialRecord.java │ │ ├── BankRegistration.java │ │ ├── SignRegistration.java │ │ └── TransactionLog.java │ ├── DatabaseManager.java │ └── CycleManager.java │ ├── I18n.java │ ├── NyaaBank.java │ ├── Configuration.java │ ├── signs │ ├── ChatInputCallbacks.java │ ├── SignHelper.java │ └── SignListener.java │ ├── BankManagementCommands.java │ ├── CommonAction.java │ └── CommandHandler.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── Jenkinsfile ├── LICENSE ├── .gitignore ├── gradlew.bat └── gradlew /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NyaaCat/NyaaBank/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/enums/AccountType.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.enums; 2 | 3 | public enum AccountType { 4 | BANK, 5 | PLAYER; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/enums/InterestType.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.enums; 2 | 3 | public enum InterestType { 4 | SIMPLE, 5 | COMPOUND; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/enums/BankStatus.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.enums; 2 | 3 | public enum BankStatus { 4 | PENDING, // Hasn't established 5 | ACTIVE, // Operating normally 6 | BANKRUPT;// Bankrupted 7 | } 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NyaaBank [![Build Status](https://ci.nyaacat.com/job/NyaaBank/badge/icon)](https://ci.nyaacat.com/job/NyaaBank/) 2 | Simplified realistic banking system for players to setup their private bank. Designed for NyaaCat server. 3 | 4 | ## Dependencies 5 | - NyaaCore 6 | - Vault 7 | 8 | ## Version History 9 | - 7.1.x: Minecraft 1.15.1, since build 65 10 | - 7.0.x: Minecraft 1.14.4, since build 63 11 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | stages { 4 | stage('Build') { 5 | tools { 6 | jdk "jdk16" 7 | } 8 | steps { 9 | sh 'gradle publish' 10 | } 11 | } 12 | } 13 | 14 | post { 15 | always { 16 | archiveArtifacts artifacts: 'build/libs/*.jar', fingerprint: true 17 | cleanWs() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/enums/TransactionType.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.enums; 2 | 3 | 4 | // NOTE: Add new values to TransactionLog:: 5 | public enum TransactionType { 6 | DEPOSIT, // from PLAYER to BANK, PARTIAL_ID in EXTRA 7 | WITHDRAW, // from BANK to PLAYER 8 | LOAN, // from BANK to PLAYER, PARTIAL_ID in EXTRA 9 | REPAY, // from PLAYER to BANK 10 | INTEREST_DEPOSIT, // from BANK to PLAYER 11 | INTEREST_LOAN, // from PLAYER to BANK 12 | PARTIAL_MOVE, // from PLAYER to BANK, PARTIAL_ID in EXTRA 13 | VAULT_CHANGE, // from PLAYER to BANK 14 | COMMISSION, // from PLAYER to BANK 15 | QUERY; // from PLAYER to {random stuff}, detail in EXTRA 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/I18n.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank; 2 | 3 | import cat.nyaa.nyaacore.LanguageRepository; 4 | import org.bukkit.plugin.java.JavaPlugin; 5 | 6 | public class I18n extends LanguageRepository { 7 | private static I18n instance; 8 | private final NyaaBank plugin; 9 | private final String lang; 10 | 11 | @Override 12 | protected JavaPlugin getPlugin() { 13 | return plugin; 14 | } 15 | 16 | @Override 17 | protected String getLanguage() { 18 | return lang; 19 | } 20 | 21 | public I18n(NyaaBank plugin, String lang) { 22 | instance = this; 23 | this.plugin = plugin; 24 | this.lang = lang; 25 | load(); 26 | } 27 | 28 | public static String format(String key, Object... args) { 29 | return instance.getFormatted(key, args); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 NyaaCat Community 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/tables/BankAccount.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.tables; 2 | 3 | import cat.nyaa.nyaacore.orm.annotations.Column; 4 | import cat.nyaa.nyaacore.orm.annotations.Table; 5 | 6 | import java.util.UUID; 7 | 8 | @Table("bank_accounts") 9 | public class BankAccount { 10 | public static final String N_ACCOUNT_ID = "account_id"; 11 | @Column(name = N_ACCOUNT_ID, primary = true) 12 | public UUID accountId; 13 | 14 | public static final String N_BANK_ID = "bank_id"; 15 | @Column(name = N_BANK_ID) 16 | public UUID bankId; 17 | 18 | public static final String N_PLAYER_ID = "player_id"; 19 | @Column(name = N_PLAYER_ID) 20 | public UUID playerId; 21 | 22 | 23 | public static final String N_DEPOSIT = "deposit"; 24 | @Column(name = N_DEPOSIT) 25 | public Double deposit; 26 | 27 | public static final String N_DEPOSIT_INTEREST = "deposit_interest"; 28 | @Column(name = N_DEPOSIT_INTEREST) 29 | public Double deposit_interest; 30 | 31 | public static final String N_LOAN = "loan"; 32 | @Column(name = N_LOAN) 33 | public Double loan; 34 | 35 | public static final String N_LOAN_INTEREST = "loan_interest"; 36 | @Column(name = N_LOAN_INTEREST) 37 | public Double loan_interest; 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/tables/PartialRecord.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.tables; 2 | 3 | import cat.nyaa.nyaabank.database.enums.TransactionType; 4 | import cat.nyaa.nyaacore.orm.annotations.Column; 5 | import cat.nyaa.nyaacore.orm.annotations.Table; 6 | 7 | import java.time.Instant; 8 | import java.util.UUID; 9 | 10 | /* New deposit or loan */ 11 | @Table("partial_transactions") 12 | public class PartialRecord { 13 | // Data column names 14 | public static final String N_CAPITAL = "capital"; 15 | public static final String N_TRANSACTION_ID = "transaction_id"; 16 | public static final String N_BANK_ID = "bank_id"; 17 | public static final String N_PLAYER_ID = "player_id"; 18 | public static final String N_START_DATE = "start_date"; 19 | public static final String N_TRANSACTION_TYPE = "transaction_type"; 20 | 21 | @Column(name = N_TRANSACTION_ID, primary = true) 22 | public UUID transactionId; 23 | 24 | @Column(name = N_BANK_ID) 25 | public UUID bankId; 26 | 27 | @Column(name = N_PLAYER_ID) 28 | public UUID playerId; 29 | 30 | @Column(name = N_CAPITAL) 31 | public Double capital; 32 | 33 | @Column(name = N_TRANSACTION_TYPE) 34 | public TransactionType type; // deposit or loan 35 | 36 | public Instant startDate; // Stored as Unix timestamp ms 37 | 38 | @Column(name = N_START_DATE) 39 | public Long getStartDate() { 40 | return startDate.toEpochMilli(); 41 | } 42 | 43 | public void setStartDate(Long startDate) { 44 | this.startDate = Instant.ofEpochMilli(startDate); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/workspace.xml 8 | .idea/tasks.xml 9 | .idea/dictionaries 10 | .idea/vcs.xml 11 | .idea/jsLibraryMappings.xml 12 | 13 | # Sensitive or high-churn files: 14 | .idea/dataSources.ids 15 | .idea/dataSources.xml 16 | .idea/dataSources.local.xml 17 | .idea/sqlDataSources.xml 18 | .idea/dynamic.xml 19 | .idea/uiDesigner.xml 20 | 21 | # Gradle: 22 | .idea/gradle.xml 23 | .idea/libraries 24 | 25 | # Mongo Explorer plugin: 26 | .idea/mongoSettings.xml 27 | 28 | ## File-based project format: 29 | *.iws 30 | 31 | ## Plugin-specific files: 32 | 33 | # IntelliJ 34 | /out/ 35 | 36 | # mpeltonen/sbt-idea plugin 37 | .idea_modules/ 38 | 39 | # JIRA plugin 40 | atlassian-ide-plugin.xml 41 | 42 | # Crashlytics plugin (for Android Studio and IntelliJ) 43 | com_crashlytics_export_strings.xml 44 | crashlytics.properties 45 | crashlytics-build.properties 46 | fabric.properties 47 | ### Example user template template 48 | ### Example user template 49 | 50 | # IntelliJ project files 51 | .idea 52 | *.iml 53 | out 54 | gen### Java template 55 | *.class 56 | 57 | # Mobile Tools for Java (J2ME) 58 | .mtj.tmp/ 59 | 60 | # Package Files # 61 | *.jar 62 | *.war 63 | *.ear 64 | 65 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 66 | hs_err_pid* 67 | 68 | .DS_Store 69 | .gradle 70 | build 71 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/NyaaBank.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank; 2 | 3 | import cat.nyaa.nyaabank.database.CycleManager; 4 | import cat.nyaa.nyaabank.database.DatabaseManager; 5 | import cat.nyaa.nyaabank.signs.SignListener; 6 | import cat.nyaa.nyaacore.utils.VaultUtils; 7 | import net.milkbowl.vault.economy.Economy; 8 | import org.bukkit.event.HandlerList; 9 | import org.bukkit.plugin.java.JavaPlugin; 10 | 11 | public class NyaaBank extends JavaPlugin { 12 | public static NyaaBank instance = null; 13 | public Configuration cfg = null; 14 | public I18n i18n; 15 | public DatabaseManager dbm; 16 | public CommandHandler commandHandler; 17 | public Economy eco; 18 | public CycleManager cycle; 19 | public SignListener signListener; 20 | 21 | @Override 22 | public void onEnable() { 23 | instance = this; 24 | cfg = new Configuration(this); 25 | cfg.load(); 26 | i18n = new I18n(this, cfg.language); 27 | dbm = new DatabaseManager(this); 28 | commandHandler = new CommandHandler(this, i18n); 29 | eco = VaultUtils.getVaultEconomy(); 30 | cycle = new CycleManager(this); 31 | signListener = new SignListener(this); 32 | getCommand("nyaabank").setExecutor(commandHandler); 33 | } 34 | 35 | @Override 36 | public void onDisable() { 37 | HandlerList.unregisterAll(this); 38 | getServer().getScheduler().cancelTasks(this); 39 | getCommand("nyaabank").setExecutor(null); 40 | cfg.save(); 41 | } 42 | 43 | // Reload plugin simply disable the plugin without saving config 44 | public void doReload() { 45 | HandlerList.unregisterAll(this); 46 | getServer().getScheduler().cancelTasks(this); 47 | getCommand("nyaabank").setExecutor(null); 48 | dbm.disconnect(); 49 | i18n.load(); 50 | onEnable(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: NyaaBank 2 | main: cat.nyaa.nyaabank.NyaaBank 3 | description: "Simplified realistic banking system for players to setup their private bank. Designed for NyaaCat server." 4 | version: ${version} 5 | depend: [Vault, NyaaCore] 6 | softdepend: [] 7 | authors: [RecursiveG] 8 | website: "https://github.com/NyaaCat/NyaaBank" 9 | api-version: 1.13 10 | 11 | commands: 12 | nyaabank: 13 | description: The command for NyaaBank 14 | aliases: nb 15 | permission: nb.command 16 | permission-message: "You do not have the required permission: " 17 | usage: "/ [SubCommand...] [Arguments...] or / help" 18 | 19 | permissions: 20 | nb.command: 21 | description: Run bank command 22 | default: true 23 | nb.user: 24 | description: Default permissions for normal player 25 | default: true 26 | children: 27 | nb.deposit: true 28 | nb.withdraw: true 29 | nb.loan: true 30 | nb.repay: true 31 | nb.list_my: true 32 | 33 | nb.banker: 34 | description: Default permissions for bankers 35 | default: true 36 | children: 37 | nb.bank_list: true 38 | nb.bank_info: true 39 | nb.bank_interest: true 40 | nb.bank_customers: true 41 | nb.bank_vault: true 42 | 43 | nb.sign_create: true 44 | nb.sign_break: true 45 | 46 | nb.admin: 47 | description: Permissions for the god 48 | default: op 49 | children: 50 | nb.bank_list_admin: true 51 | nb.bank_info_admin: true 52 | nb.bank_interest_admin: true 53 | nb.bank_customers_admin: true 54 | nb.bank_vault_admin: true 55 | 56 | nb.list_my_admin: true 57 | nb.sign_create_admin: true 58 | nb.sign_break_admin: true 59 | 60 | nb.create_bank: true 61 | nb.force_bankrupt: true 62 | nb.top: true 63 | nb.reload: true 64 | 65 | nb.tansaction_cmds: 66 | description: Allow players to interact with bank from command 67 | default: op 68 | children: 69 | nb.deposite_cmd: true 70 | nb.withdraw_cmd: true 71 | nb.loan_cmd: true 72 | nb.repay_cmd: true 73 | 74 | nb.debug: 75 | description: Access to all dev/debug functions 76 | default: op # change to false when releasing 77 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/Configuration.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank; 2 | 3 | import cat.nyaa.nyaacore.configuration.PluginConfigure; 4 | import cat.nyaa.nyaacore.orm.backends.BackendConfig; 5 | import org.bukkit.plugin.java.JavaPlugin; 6 | 7 | public class Configuration extends PluginConfigure { 8 | private final NyaaBank plugin; 9 | 10 | /* Basic plugin & database config */ 11 | @Serializable 12 | public String language = "en_US"; 13 | @Serializable(name = "interest_cycle") 14 | public long interestCycle = 30L * 24 * 60 * 60 * 1000; // ms 15 | @Serializable(name = "interest_cycle_offset") 16 | public long interestCycleOffset = 0L; // ms 17 | 18 | /* Interest limit */ 19 | @Serializable(name = "saving_interest_limit.enabled") 20 | public boolean savingInterestLimitEnabled = false; 21 | @Serializable(name = "saving_interest_limit.min") 22 | public double savingInterestLimitMin = -1D; // hundred percent 23 | @Serializable(name = "saving_interest_limit.max") 24 | public double savingInterestLimitMax = 2D; // hundred percent 25 | @Serializable(name = "debit_interest_limit.enabled") 26 | public boolean debitInterestLimitEnabled = false; 27 | @Serializable(name = "debit_interest_limit.min") 28 | public double debitInterestLimitMin = 0D; // hundred percent 29 | @Serializable(name = "debit_interest_limit.max") 30 | public double debitInterestLimitMax = 5D; // hundred percent 31 | 32 | @Serializable(name = "sign.sign_magic") 33 | public String signMagic = "[BANK]"; 34 | @Serializable(name = "sign.color_active") 35 | public String signColorActive = "§l§a"; 36 | @Serializable(name = "sign.color_bankrupt") 37 | public String signColorBankrupt = "§l§c"; 38 | @Serializable(name = "sign.timeout") 39 | public long signTimeout = 7; // seconds 40 | 41 | @Serializable 42 | public BackendConfig database = BackendConfig.sqliteBackend("nyaabank.db"); 43 | 44 | /* Data stored in config file. Do not edit these entries manually */ 45 | @Serializable(name = "data.last_check_point") 46 | public long lastCheckPoint = -1L; // unix timestamp ms. negative if no check point have been reached. 47 | 48 | public Configuration(NyaaBank pl) { 49 | this.plugin = pl; 50 | } 51 | 52 | @Override 53 | protected JavaPlugin getPlugin() { 54 | return plugin; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/tables/BankRegistration.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.tables; 2 | 3 | import cat.nyaa.nyaabank.database.enums.BankStatus; 4 | import cat.nyaa.nyaabank.database.enums.InterestType; 5 | import cat.nyaa.nyaacore.orm.annotations.Column; 6 | import cat.nyaa.nyaacore.orm.annotations.Table; 7 | 8 | import java.time.Instant; 9 | import java.util.UUID; 10 | 11 | /* Information about the bank */ 12 | @Table("bank_registration") 13 | public class BankRegistration { 14 | public static final String N_BANK_ID = "bank_id"; 15 | @Column(name = N_BANK_ID, primary = true) 16 | public UUID bankId; 17 | 18 | public static final String N_OWNER_ID = "owner_id"; 19 | @Column(name = N_OWNER_ID) 20 | public UUID ownerId; 21 | 22 | public static final String N_ID_NUMBER = "id_number"; 23 | @Column(name = N_ID_NUMBER) 24 | public Long idNumber; // short identifier for human input 25 | 26 | public static final String N_BANK_NAME = "bank_name"; 27 | @Column(name = N_BANK_NAME) 28 | public String name; 29 | 30 | public static final String N_REGISTERED_CAPITAL = "registered_capital"; 31 | @Column(name = N_REGISTERED_CAPITAL) 32 | public Double registered_capital; 33 | 34 | public static final String N_INTEREST_RATE_SAVING = "interest_rate_saving"; 35 | @Column(name = N_INTEREST_RATE_SAVING) 36 | public Double savingInterest; // default saving interest, hundred percent 37 | 38 | public static final String N_INTEREST_RATE_DEBIT = "interest_rate_debit"; 39 | @Column(name = N_INTEREST_RATE_DEBIT) 40 | public Double debitInterest; // default loan interest, hundred percent 41 | 42 | public static final String N_INTEREST_RATE_SAVING_NEXT = "interest_rate_saving_next"; 43 | @Column(name = N_INTEREST_RATE_SAVING_NEXT) 44 | public Double savingInterestNext; // saving interest for next cycle, hundred percent 45 | 46 | public static final String N_INTEREST_RATE_DEBIT_NEXT = "interest_rate_debit_next"; 47 | @Column(name = N_INTEREST_RATE_DEBIT_NEXT) 48 | public Double debitInterestNext; // loan interest for next cycle, hundred percent 49 | 50 | public static final String N_ESTABLISH_DATE = "establish_date"; 51 | @Column(name = N_ESTABLISH_DATE) 52 | public Instant establishDate; // Stored as human readable string 53 | 54 | public static final String N_BANK_STATUS = "bank_status"; 55 | @Column(name = N_BANK_STATUS) 56 | public BankStatus status; 57 | 58 | public static final String N_INTEREST_TYPE = "interest_type"; 59 | @Column(name = N_INTEREST_TYPE) 60 | public InterestType interestType; 61 | 62 | public static final String N_INTEREST_TYPE_NEXT = "interest_type_next"; 63 | @Column(name = N_INTEREST_TYPE_NEXT) 64 | public InterestType interestTypeNext; 65 | } 66 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/tables/SignRegistration.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.tables; 2 | 3 | import cat.nyaa.nyaabank.database.enums.TransactionType; 4 | import cat.nyaa.nyaacore.orm.annotations.Column; 5 | import cat.nyaa.nyaacore.orm.annotations.Table; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.Location; 8 | 9 | import java.util.UUID; 10 | 11 | /* Information about the signs in world map */ 12 | @Table("sign_registration") 13 | public class SignRegistration { 14 | // Data column names 15 | public static final String N_LOAN_AMOUNT = "loan_amount"; 16 | public static final String N_COMMISSION_FEE = "commission_fee"; 17 | public static final String N_SIGN_ID = "sign_id"; 18 | public static final String N_BANK_ID = "bank_id"; 19 | public static final String N_SIGN_TYPE = "sign_type"; 20 | public static final String N_LOCATION_WORLD_NAME = "location_world_name"; 21 | public static final String N_LOCATION_X = "location_x"; 22 | public static final String N_LOCATION_Y = "location_y"; 23 | public static final String N_LOCATION_Z = "location_z"; 24 | 25 | @Column(name = N_SIGN_ID, primary = true) 26 | public UUID signId; 27 | 28 | @Column(name = N_BANK_ID) 29 | public UUID bankId; 30 | 31 | @Column(name = N_SIGN_TYPE) 32 | public TransactionType type; // DEPOSIT/WITHDRAW/LOAN/REPAY 33 | 34 | @Column(name = N_LOAN_AMOUNT) 35 | public Double loanAmount; // for LOAN sign only 36 | 37 | @Column(name = N_COMMISSION_FEE) 38 | public Double commissionFee; // for WITHDRAW & REPAY only 39 | 40 | public Location location; 41 | 42 | @Column(name = N_LOCATION_WORLD_NAME) 43 | public String getWorldName() { 44 | return location.getWorld().getName(); 45 | } 46 | 47 | public void setWorldName(String worldName) { 48 | if (location == null) { 49 | location = new Location(Bukkit.getWorld(worldName), 0, 0, 0); 50 | } else { 51 | location.setWorld(Bukkit.getWorld(worldName)); 52 | } 53 | } 54 | 55 | @Column(name = N_LOCATION_X) 56 | public Long getCoordinateX() { 57 | return (long) location.getBlockX(); 58 | } 59 | 60 | public void setCoordinateX(Long x) { 61 | if (location == null) { 62 | location = new Location(Bukkit.getWorlds().get(0), x, 0, 0); 63 | } else { 64 | location.setX(x); 65 | } 66 | } 67 | 68 | @Column(name = N_LOCATION_Y) 69 | public Long getCoordinateY() { 70 | return (long) location.getBlockY(); 71 | } 72 | 73 | public void setCoordinateY(Long y) { 74 | if (location == null) { 75 | location = new Location(Bukkit.getWorlds().get(0), 0, y, 0); 76 | } else { 77 | location.setY(y); 78 | } 79 | } 80 | 81 | @Column(name = N_LOCATION_Z) 82 | public Long getCoordinateZ() { 83 | return (long) location.getBlockZ(); 84 | } 85 | 86 | public void setCoordinateZ(Long z) { 87 | if (location == null) { 88 | location = new Location(Bukkit.getWorlds().get(0), 0, 0, z); 89 | } else { 90 | location.setZ(z); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/signs/ChatInputCallbacks.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.signs; 2 | 3 | import cat.nyaa.nyaabank.I18n; 4 | import cat.nyaa.nyaabank.NyaaBank; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.EventPriority; 9 | import org.bukkit.event.HandlerList; 10 | import org.bukkit.event.Listener; 11 | import org.bukkit.event.player.AsyncPlayerChatEvent; 12 | import org.bukkit.scheduler.BukkitRunnable; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.UUID; 17 | 18 | class ChatInputCallbacks { 19 | interface InputCallback { 20 | void onDoubleInput(Player p, double input, boolean isAll); 21 | } 22 | 23 | class InputTimeoutTimer extends BukkitRunnable { 24 | private final UUID playerId; 25 | 26 | InputTimeoutTimer(UUID id) { 27 | playerId = id; 28 | } 29 | 30 | @Override 31 | public void run() { 32 | Player p = Bukkit.getPlayer(playerId); 33 | if (p != null) { 34 | p.sendMessage(I18n.format("user.sign.input_timeout")); 35 | } 36 | callbacks.remove(playerId); 37 | timers.remove(playerId); 38 | if (callbacks.isEmpty() && listener != null) { 39 | HandlerList.unregisterAll(listener); 40 | listener = null; 41 | } 42 | } 43 | } 44 | 45 | class InputListener implements Listener { 46 | @EventHandler(priority = EventPriority.LOW) 47 | public void onPlayerInput(AsyncPlayerChatEvent ev) { 48 | UUID playerId = ev.getPlayer().getUniqueId(); 49 | if (!callbacks.containsKey(playerId)) return; 50 | 51 | 52 | ev.setCancelled(true); 53 | String msg = ev.getMessage(); 54 | if ("ALL".equalsIgnoreCase(msg) || "CONFIRM".equalsIgnoreCase(msg)) { 55 | callbacks.get(playerId).onDoubleInput(ev.getPlayer(), -1, true); 56 | } else { 57 | Double number = null; 58 | try { 59 | number = Double.parseDouble(msg); 60 | if (Double.isInfinite(number) || Double.isNaN(number)) 61 | number = null; 62 | } catch (NumberFormatException ex) { 63 | number = null; 64 | } 65 | if (number == null) { 66 | ev.getPlayer().sendMessage(I18n.format("user.sign.invalid_number")); 67 | } else { 68 | callbacks.get(playerId).onDoubleInput(ev.getPlayer(), number, false); 69 | } 70 | } 71 | 72 | if (timers.containsKey(playerId)) { 73 | timers.get(playerId).cancel(); 74 | } 75 | callbacks.remove(playerId); 76 | timers.remove(playerId); 77 | assert (listener == this); 78 | if (callbacks.isEmpty()) { 79 | HandlerList.unregisterAll(listener); 80 | listener = null; 81 | } 82 | } 83 | } 84 | 85 | final Map callbacks = new HashMap<>(); 86 | private final Map timers = new HashMap<>(); 87 | private InputListener listener = null; 88 | private final NyaaBank plugin; 89 | 90 | ChatInputCallbacks(NyaaBank plugin) { 91 | this.plugin = plugin; 92 | } 93 | 94 | public void register(UUID player, InputCallback callback) { 95 | callbacks.put(player, callback); 96 | InputTimeoutTimer timer = new InputTimeoutTimer(player); 97 | timer.runTaskLater(plugin, plugin.cfg.signTimeout * 20); 98 | timers.put(player, timer); 99 | if (listener == null) { 100 | listener = new InputListener(); 101 | plugin.getServer().getPluginManager().registerEvents(listener, plugin); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/tables/TransactionLog.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database.tables; 2 | 3 | import cat.nyaa.nyaabank.database.DatabaseManager; 4 | import cat.nyaa.nyaabank.database.enums.AccountType; 5 | import cat.nyaa.nyaabank.database.enums.TransactionType; 6 | import cat.nyaa.nyaacore.orm.annotations.Column; 7 | import cat.nyaa.nyaacore.orm.annotations.Table; 8 | 9 | import java.time.Instant; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.UUID; 13 | 14 | @Table("transaction_log") 15 | public class TransactionLog { 16 | // Data column names 17 | public static final String N_ID = "id"; 18 | public static final String N_CAPITAL = "capital"; 19 | public static final String N_EXTRA = "extra"; 20 | public static final String N_TIME = "time"; 21 | public static final String N_FROM_ID = "from_id"; 22 | public static final String N_TO_ID = "to_id"; 23 | public static final String N_FROM_TYPE = "from_type"; 24 | public static final String N_TO_TYPE = "to_type"; 25 | public static final String N_TRANSACTION_TYPE = "transaction_type"; 26 | 27 | @Column(name = N_ID, primary = true) 28 | public Integer id; 29 | 30 | @Column(name = N_TIME) 31 | private Instant time; 32 | @Column(name = N_FROM_ID) 33 | private UUID from; 34 | @Column(name = N_TO_ID) 35 | private UUID to; 36 | @Column(name = N_CAPITAL) 37 | public Double capital; 38 | @Column(name = N_FROM_TYPE) 39 | public AccountType fromType; 40 | @Column(name = N_TO_TYPE) 41 | public AccountType toType; 42 | @Column(name = N_TRANSACTION_TYPE) 43 | public TransactionType type; 44 | @Column(name = N_EXTRA) 45 | public String extra; 46 | 47 | 48 | public TransactionLog() { 49 | 50 | } 51 | 52 | private DatabaseManager dbm = null; 53 | 54 | public TransactionLog(DatabaseManager dbm, TransactionType ttype) { 55 | this.dbm = dbm; 56 | switch (ttype) { 57 | case DEPOSIT: // from PLAYER to BANK, PARTIAL_ID in EXTRA 58 | case REPAY: // from PLAYER to BANK 59 | case INTEREST_LOAN: // from PLAYER to BANK 60 | case PARTIAL_MOVE: // from PLAYER to BANK, PARTIAL_ID in EXTRA 61 | case VAULT_CHANGE: 62 | case COMMISSION: 63 | case QUERY: // from PLAYER to {random stuff}, detail in EXTRA 64 | fromType = AccountType.PLAYER; 65 | toType = AccountType.BANK; 66 | break; 67 | case WITHDRAW: // from BANK to PLAYER 68 | case LOAN: // from BANK to PLAYER, PARTIAL_ID in EXTRA 69 | case INTEREST_DEPOSIT: // from BANK to PLAYER 70 | fromType = AccountType.BANK; 71 | toType = AccountType.PLAYER; 72 | break; 73 | default: 74 | throw new IllegalArgumentException("Unknown Transaction Type"); 75 | } 76 | this.type = ttype; 77 | this.extra = ""; 78 | } 79 | 80 | public TransactionLog from(UUID id) { 81 | this.from = id; 82 | return this; 83 | } 84 | 85 | public TransactionLog to(UUID id) { 86 | this.to = id; 87 | return this; 88 | } 89 | 90 | public TransactionLog capital(Double capital) { 91 | this.capital = capital; 92 | return this; 93 | } 94 | 95 | private Map extraMap = null; 96 | 97 | // TODO extra document 98 | public TransactionLog extra(String key, String value) { 99 | if (extraMap == null) extraMap = new HashMap<>(); 100 | extraMap.put(key, value); 101 | return this; 102 | } 103 | 104 | public void insert() { 105 | if (dbm == null) throw new IllegalStateException("assert(dbm != null)"); 106 | time = Instant.now(); 107 | id = null; 108 | extra = ""; 109 | if (extraMap != null) { 110 | for (String key : extraMap.keySet()) { 111 | if (extra.length() > 0) extra += ", "; 112 | extra += String.format("\"%s\": \"%s\"", key, extraMap.get(key)); 113 | } 114 | } 115 | extra = "{" + extra + "}"; 116 | dbm.tableTransactionLog.insert(this); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/resources/lang/zh_CN.yml: -------------------------------------------------------------------------------- 1 | command: 2 | reg: 3 | name_duplicate: 指定的银行名称已存在 4 | established: (#%d) 银行 `%s&r` 创建成功,ID 为 {%s} 5 | not_enough_capital: 玩家没有足够的现金用于创建银行 6 | top: 7 | list_item: '%3d. %s&r(%.2f) {%s}' 8 | list_my: 9 | list_item: 银行:%s&r 存款:%.2f 贷款:%.2f 10 | empty_list: 你尚未开立任何银行账户 11 | bankrupt: 12 | player_not_found: 找不到指定的玩家 13 | bank_not_found: 找不到指定的银行,或结果不唯一 14 | reload: 15 | complete: 重新加载成功 16 | bank_list: 17 | list_player: === %s 的银行 === 18 | list_all: === 所有银行列表 === 19 | list_item: (#%d) %s {所有者:%s,银行ID:%s} 20 | list_empty: 暂无已注册的银行 21 | bank_info: 22 | no_such_bank: 找不到指定的银行,或结果不唯一 23 | only_self: 你没有权限执行此操作 24 | info: '银行名称 :%s 25 | 26 | 银行ID :%s 27 | 28 | 所有者名称 :%s 29 | 30 | 所有者ID :%s 31 | 32 | 创立时间 :%s 33 | 34 | 当前状态 :%s 35 | 36 | 总行注册地点:%.2f 37 | 38 | 储蓄利率 :%f%% 39 | 40 | 贷款利率 :%f%% 41 | 42 | 利息类型 :%s 43 | 44 | 下期存款利率:%f%% 45 | 46 | 下期贷款利率:%f%% 47 | 48 | 下期利息类型:%s 49 | 50 | ' 51 | bank_interest: 52 | no_such_bank: 找不到指定的银行,或结果不唯一 53 | only_self: 你没有权限执行此操作 54 | rate_out_of_range: 利率超出允许的范围:[%.2f, %.2f] 55 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 56 | bank_customers: 57 | no_such_bank: 找不到指定的银行,或结果不唯一 58 | only_self: 你没有权限执行此操作 59 | customer_record: '%s:存款【%.2f】贷款【%.2f】' 60 | no_customer: 暂无客户记录 61 | bank_vault: 62 | no_such_bank: 找不到指定的银行,或结果不唯一 63 | only_self: 你没有权限执行此操作 64 | vault_insufficient: 此银行的库存金没有足够的余额 65 | player_insufficient: 你没有足够的现金 66 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 67 | user: 68 | deposit: 69 | invalid_amount: 存款金额无效 70 | not_enough_money: 你没有足够的现金 71 | bank_not_found: 找不到指定的银行,或结果不唯一 72 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 73 | withdraw: 74 | invalid_amount: 取款金额无效 75 | bank_not_found: 找不到指定的银行,或结果不唯一 76 | not_enough_deposit: 取款金额超出了存款余额 77 | bank_run: 银行没有足够的资金 78 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 79 | loan: 80 | invalid_amount: 贷款金额无效 81 | bank_not_found: 找不到指定的银行,或结果不唯一 82 | has_loan: 你尚未清偿上期贷款,暂不能申请新的贷款 83 | not_enough_money_bank: 银行没有足够的资金 84 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 85 | repay: 86 | invalid_amount: 偿还金额无效 87 | bank_not_found: 找不到指定的银行,或结果不唯一 88 | no_need_to_pay: 数值超出了应偿还金额的上限 89 | not_enough_money: 你没有足够的现金 90 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 91 | sign: 92 | invalid_sign: 该银行业务牌无效 93 | invalid_number: 数值无效 94 | input_timeout: 输入超时,操作已取消 95 | loan_cancelled: 已取消贷款操作 96 | create_success: 银行业务牌创建成功 97 | create_fail: 设置银行业务牌失败 98 | input_accepted: 交易已接受。 99 | break_no_permission: 你不能破坏这个银行业务牌 100 | break_success: 撤销银行业务牌成功 101 | use_no_permission: 你没有权限使用这个银行业务牌 102 | bankrupted: 不能对指定的银行执行此操作,因为此银行已破产 103 | text_saving: 储蓄业务 104 | text_withdraw: 取款业务 105 | text_loan: 贷款业务 106 | text_repay: 偿还贷款 107 | hint_saving: 利率:%.2f%% 108 | hint_withdraw: 手续费:%.2f 109 | hint_loan: '%.2f/%.2f%%' 110 | hint_repay: 手续费:%.2f 111 | input_prompt_deposit: 请在 %d 秒内输入你要存入的金额 112 | input_prompt_withdraw: 请在 %d 秒内输入你要取出的金额(或输入 ALL 取出全部存款) 113 | input_prompt_loan: 请在 %d 秒内输入 CONFIRM 来确认此操作 114 | input_prompt_repay: 请在 %d 秒内输入你要偿还的金额(或输入 ALL 偿还全部贷款) 115 | manual: 116 | no_description: 无描述 117 | no_usage: 无用法说明 118 | help: 119 | description: 显示帮助信息 120 | usage: /nb [子命令] help 121 | bank: 122 | description: 管理银行 123 | usage: /nb bank [子命令] [参数] 124 | list: 125 | description: 列出银行 126 | usage: /nb bank list [玩家名称] 127 | info: 128 | desctiption: 显示银行信息 129 | usage: /nb bank info <银行ID> 130 | interest: 131 | description: 修改利率。 132 | usage: /nb bank interest <银行ID> [SAVING|LOAN|TYPE] 133 | customers: 134 | description: 列出所有客户 135 | usage: /nb bank customers <银行ID> 136 | vault: 137 | description: 将款项存入银行库存金 138 | usage: /nb bank vault <金额> <银行ID> 139 | reg: 140 | description: 注册新银行,并指定百分比值的利息 141 | usage: /nb reg <所有者名称> <银行名称> <银行总部所在地> <存款利息> <贷款利息> <利息类型:SIMPLE|COMPOUND> 142 | top: 143 | description: 按总部所在地列出银行 144 | usage: /nb top 145 | bankrupt: 146 | description: 将玩家或银行设置为破产状态 147 | usage: /nb bankrupt <玩家名称|银行ID> 148 | my: 149 | description: 列出个人账户信息 150 | usage: /nb my [玩家名称] 151 | deposit: 152 | description: 向某家银行存款 153 | usage: /nb deposit <金额> <银行ID> 154 | withdraw: 155 | description: 从某家银行取款 156 | usage: /nb withdraw <金额|ALL> <银行ID> 157 | loan: 158 | description: 向银行贷款 159 | usage: /nb loan <金额> <银行ID> 160 | repay: 161 | description: 偿还贷款 162 | usage: /nb repay <金额|ALL> <银行ID> 163 | reload: 164 | description: 重新加载插件 165 | usage: /nb reload 166 | _check: 167 | description: 【警告!仅供调试使用】强制运行Checkpoint Routine 168 | usage: /nb _check 169 | _benchmark: 170 | description: 【警告!执行此命令将销毁数据】测试数据库性能 171 | usage: /nb _benchmark 172 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/DatabaseManager.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database; 2 | 3 | import cat.nyaa.nyaabank.NyaaBank; 4 | import cat.nyaa.nyaabank.database.enums.TransactionType; 5 | import cat.nyaa.nyaabank.database.tables.*; 6 | import cat.nyaa.nyaacore.orm.*; 7 | import cat.nyaa.nyaacore.orm.backends.IConnectedDatabase; 8 | import cat.nyaa.nyaacore.orm.backends.ITypedTable; 9 | 10 | import java.sql.SQLException; 11 | import java.util.List; 12 | import java.util.UUID; 13 | 14 | public class DatabaseManager { 15 | private final NyaaBank plugin; 16 | public final IConnectedDatabase db; 17 | public final ITypedTable tableBankAccount; 18 | public final ITypedTable tableBankRegistration; 19 | public final ITypedTable tablePartialRecord; 20 | public final ITypedTable tableSignRegistration; 21 | public final ITypedTable tableTransactionLog; 22 | 23 | public DatabaseManager(NyaaBank plugin) { 24 | this.plugin = plugin; 25 | try { 26 | db = DatabaseUtils.connect(plugin, plugin.cfg.database); 27 | tableBankAccount = db.getTable(BankAccount.class); 28 | tableBankRegistration = db.getTable(BankRegistration.class); 29 | tablePartialRecord = db.getTable(PartialRecord.class); 30 | tableSignRegistration = db.getTable(SignRegistration.class); 31 | tableTransactionLog = db.getTable(TransactionLog.class); 32 | } catch (ClassNotFoundException | SQLException ex) { 33 | throw new RuntimeException("Failed to connect to database", ex); 34 | } 35 | } 36 | 37 | public void disconnect() { 38 | try { 39 | db.close(); 40 | } catch (SQLException ex) { 41 | throw new RuntimeException(ex); 42 | } 43 | } 44 | 45 | /** 46 | * get unique bank registeration from partial bank UUID 47 | * null returned if bank not unique or not exists 48 | * 49 | * @param partialUUID part of the uuid, including dash 50 | * @return unique bank 51 | */ 52 | public BankRegistration getUniqueBank(String partialUUID) { 53 | if (partialUUID == null || partialUUID.isEmpty()) return null; 54 | return tableBankRegistration.selectUniqueUnchecked( 55 | new WhereClause(BankRegistration.N_BANK_ID, " LIKE ", "%" + partialUUID + "%") 56 | ); 57 | } 58 | 59 | public BankRegistration getUniqueBank(UUID bankId) { 60 | if (bankId == null) return null; 61 | return tableBankRegistration.selectUniqueUnchecked(WhereClause.EQ(BankRegistration.N_BANK_ID, bankId)); 62 | } 63 | 64 | /** 65 | * Get player's bank account 66 | * return null if not found 67 | */ 68 | public BankAccount getAccount(UUID bankId, UUID playerId) { 69 | BankAccount account = null; 70 | 71 | List l = tableBankAccount.select( 72 | new WhereClause() 73 | .whereEq(BankAccount.N_BANK_ID, bankId.toString()) 74 | .whereEq(BankAccount.N_PLAYER_ID, playerId.toString()) 75 | ); 76 | 77 | if (!l.isEmpty()) { 78 | if (l.size() > 1) { 79 | plugin.getLogger().severe("Duplicated account: bankid:" + 80 | bankId.toString() + " playerid:" + playerId.toString()); 81 | } 82 | account = l.get(0); 83 | } 84 | return account; 85 | } 86 | 87 | public List getPartialRecords(UUID bankId, UUID playerId, TransactionType type) { 88 | return tablePartialRecord.select(new WhereClause() 89 | .whereEq(PartialRecord.N_BANK_ID, bankId.toString()) 90 | .whereEq(PartialRecord.N_PLAYER_ID, playerId.toString()) 91 | .whereEq(PartialRecord.N_TRANSACTION_TYPE, type) 92 | ); 93 | } 94 | 95 | /** 96 | * Get total deposit: deposit+interest+partial 97 | * 98 | * @param bankId bank id 99 | * @param playerId player id 100 | * @return total deposit 101 | */ 102 | public double getTotalDeposit(UUID bankId, UUID playerId) { 103 | double ret = 0; 104 | BankAccount account = getAccount(bankId, playerId); 105 | if (account != null) { 106 | ret += account.deposit + account.deposit_interest; 107 | } 108 | for (PartialRecord rec : getPartialRecords(bankId, playerId, TransactionType.DEPOSIT)) { 109 | ret += rec.capital; 110 | } 111 | return ret; 112 | } 113 | 114 | /** 115 | * Get total loan: loan+interest+partial 116 | * 117 | * @param bankId bank id 118 | * @param playerId player id 119 | * @return total loan 120 | */ 121 | public double getTotalLoan(UUID bankId, UUID playerId) { 122 | double ret = 0; 123 | BankAccount account = getAccount(bankId, playerId); 124 | if (account != null) { 125 | ret += account.loan + account.loan_interest; 126 | } 127 | for (PartialRecord rec : getPartialRecords(bankId, playerId, TransactionType.LOAN)) { 128 | ret += rec.capital; 129 | } 130 | return ret; 131 | } 132 | 133 | /** 134 | * Return next unused bank id number 135 | */ 136 | public long getNextIdNumber() { 137 | Long dbMaxId = tableBankRegistration.selectSingleton(String.format("MAX(%s)", BankRegistration.N_ID_NUMBER), DataTypeMapping.LongConverter.INSTANCE); 138 | return dbMaxId == null ? 1 : dbMaxId + 1; 139 | } 140 | 141 | public BankRegistration getBankByIdNumber(long idNumber) { 142 | try { 143 | return tableBankRegistration.selectUnique(WhereClause.EQ(BankRegistration.N_ID_NUMBER, idNumber)); 144 | } catch (NonUniqueResultException ex) { 145 | return null; 146 | } 147 | } 148 | 149 | /* return a new log entry */ 150 | public TransactionLog log(TransactionType type) { 151 | return new TransactionLog(this, type); 152 | } 153 | 154 | public RollbackGuard acquireRollbackGuard() { 155 | return new RollbackGuard(db); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/resources/lang/en_US.yml: -------------------------------------------------------------------------------- 1 | command: 2 | reg: 3 | name_duplicate: "Bank name already exists" 4 | established: "(#%d) Bank `%s&r` established. ID={%s}" 5 | not_enough_capital: "The player has not that much money" 6 | top: 7 | list_item: "%3d. %s&r(%.2f) {%s}" 8 | list_my: 9 | list_item: "Bank: %s&r Deposit: %.2f Loan: %.2f" 10 | empty_list: "No bank account" 11 | bankrupt: 12 | player_not_found: "Player not found" 13 | bank_not_found: "Bank not found or not unique" 14 | reload: 15 | complete: "Reload Completed" 16 | 17 | bank_list: 18 | list_player: "=== %s's banks ===" 19 | list_all: "=== All banks ===" 20 | list_item: "(#%d) %s {Owner: %s, bankId: %s}" 21 | list_empty: "No registered bank" 22 | bank_info: 23 | no_such_bank: "Bank not found or not unique" 24 | only_self: "It's none of your business" 25 | info: | 26 | Bank Name: %s 27 | Bank Id: %s 28 | Owner Name: %s 29 | Owner Id: %s 30 | Established Date: %s 31 | Status: %s 32 | Registered Capital: %.2f 33 | Deposit Interest Rate: %f%% 34 | Loan Interest Rate: %f%% 35 | Interest Type: %s 36 | Next Deposit Interest Rate: %f%% 37 | Next Loan Interest Rate: %f%% 38 | Next Interest Type: %s 39 | bank_interest: 40 | no_such_bank: "Bank not found or not unique" 41 | only_self: "It's none of your business" 42 | rate_out_of_range: "Interest Rate out of range: [%.2f, %.2f]" 43 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 44 | bank_customers: 45 | no_such_bank: "Bank not found or not unique" 46 | only_self: "It's none of your business" 47 | customer_record: "%s: Deposit=%.2f, Loan=%.2f" 48 | no_customer: "No customer to be listed" 49 | bank_vault: 50 | no_such_bank: "Bank not found or not unique" 51 | only_self: "It's none of your business" 52 | vault_insufficient: "Bank vault do not have that much money" 53 | player_insufficient: "You do not have that much money" 54 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 55 | 56 | user: 57 | deposit: 58 | invalid_amount: "Invalid amount" 59 | not_enough_money: "Not enough money" 60 | bank_not_found: "Bank not found or not unique" 61 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 62 | withdraw: 63 | invalid_amount: "Invalid amount" 64 | bank_not_found: "Bank not found or not unique" 65 | not_enough_deposit: "You do not have that much deposit" 66 | bank_run: "This bank do not have that much money" 67 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 68 | loan: 69 | invalid_amount: "Invalid amount" 70 | bank_not_found: "Bank not found or not unique" 71 | has_loan: "You haven't cleared your previous loan" 72 | not_enough_money_bank: "This bank does not have that much money" 73 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 74 | repay: 75 | invalid_amount: "Invalid amount" 76 | bank_not_found: "Bank not found or not unique" 77 | no_need_to_pay: "You do not need to pay that much money" 78 | not_enough_money: "You do not have that much money" 79 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 80 | sign: 81 | invalid_sign: "This is an invalid bank sign" 82 | invalid_number: "This is not an valid number" 83 | input_timeout: "You didn't type a number in time. Action cancelled" 84 | loan_cancelled: "Loan cancelled" 85 | create_success: "Bank sign created" 86 | create_fail: "Failed to setup bank sign" 87 | input_accepted: "Transaction accepted." 88 | break_no_permission: "You cannot break this sign" 89 | break_success: "You broke the bank sign" 90 | use_no_permission: "You do not have permission to use this sign" 91 | bankrupted: "You cannot interact with this bank cause it's bankrupted" 92 | 93 | text_saving: "Saving Service" 94 | text_withdraw: "Withdraw" 95 | text_loan: "Loan Service" 96 | text_repay: "Repay" 97 | 98 | hint_saving: "Interest %.2f%%" 99 | hint_withdraw: "Fee: %.2f" 100 | hint_loan: "%.2f/%.2f%%" 101 | hint_repay: "Fee: %.2f" 102 | 103 | input_prompt_deposit: "Type the amount of money you want to deposit in %d seconds" 104 | input_prompt_withdraw: "Type the amount of money you want to withdraw (or `ALL`) in %d seconds" 105 | input_prompt_loan: "Type `CONFIRM` in %d seconds to confirm the transaction" 106 | input_prompt_repay: "Type the amount of money you want to repay (or `ALL`) in %d seconds" 107 | 108 | manual: 109 | no_description: "No description" 110 | no_usage: "No usage" 111 | 112 | help: 113 | description: "Show help message" 114 | usage: "/nb [subCommands...] help" 115 | 116 | bank: 117 | description: "Manage your banks" 118 | usage: "/nb bank [subCommand] [args...]" 119 | 120 | list: 121 | description: "List the banks" 122 | usage: "/nb bank list [playerName]" 123 | 124 | info: 125 | desctiption: "Show the info about one bank" 126 | usage: "/nb bank info " 127 | 128 | interest: 129 | description: "Change interest rate." 130 | usage: "/nb bank interest [SAVING|LOAN|TYPE] " 131 | 132 | customers: 133 | description: "List all customers" 134 | usage: "/nb bank customers " 135 | 136 | vault: 137 | description: "Deposit some money into bank vault" 138 | usage: "/nb bank vault " 139 | 140 | reg: 141 | description: "Register a bank, use hundred percent interest" 142 | usage: "/nb reg " 143 | 144 | top: 145 | description: "List banks by capital" 146 | usage: "/nb top" 147 | 148 | bankrupt: 149 | description: "Set someone as bankrupt" 150 | usage: "/nb bankrupt " 151 | 152 | my: 153 | description: "List my accounts" 154 | usage: "/nb my [player-name]" 155 | 156 | deposit: 157 | description: "Make deposit to some bank" 158 | usage: "/nb deposit " 159 | 160 | withdraw: 161 | description: "Withdraw money from bank" 162 | usage: "/nb withdraw " 163 | 164 | loan: 165 | description: "Loan money from bank" 166 | usage: "/nb loan " 167 | 168 | repay: 169 | description: "Repay the loan" 170 | usage: "/nb repay " 171 | 172 | reload: 173 | description: "Reload plugin" 174 | usage: "/nb reload" 175 | 176 | _check: 177 | description: "WARN: debug only. Force run checkpoint routine" 178 | usage: "/nb _check" 179 | 180 | _benchmark: 181 | description: "WARN: data will be DESTROYED. Test database performance" 182 | usage: "/nb _benchmark" 183 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/signs/SignHelper.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.signs; 2 | 3 | import cat.nyaa.nyaabank.I18n; 4 | import cat.nyaa.nyaabank.NyaaBank; 5 | import cat.nyaa.nyaabank.database.enums.BankStatus; 6 | import cat.nyaa.nyaabank.database.enums.TransactionType; 7 | import cat.nyaa.nyaabank.database.tables.BankRegistration; 8 | import cat.nyaa.nyaabank.database.tables.SignRegistration; 9 | import cat.nyaa.nyaacore.orm.WhereClause; 10 | import org.bukkit.ChatColor; 11 | import org.bukkit.Location; 12 | import org.bukkit.block.Block; 13 | import org.bukkit.block.Sign; 14 | import org.bukkit.scheduler.BukkitRunnable; 15 | 16 | import java.util.List; 17 | import java.util.UUID; 18 | 19 | public final class SignHelper { 20 | 21 | public static boolean stringEqIgnoreColor(String s1, String s2, boolean ignoreCase) { 22 | if (s1 == null && s2 == null) return true; 23 | if (s1 == null || s2 == null) return false; 24 | String a = ChatColor.stripColor(s1); 25 | String b = ChatColor.stripColor(s2); 26 | if (ignoreCase) { 27 | return a.equalsIgnoreCase(b); 28 | } else { 29 | return a.equals(b); 30 | } 31 | } 32 | 33 | public static Double parseDouble(String s) { 34 | Double d = null; 35 | try { 36 | d = Double.parseDouble(s); 37 | } catch (NumberFormatException ex) { 38 | return null; 39 | } 40 | if (Double.isInfinite(d) || Double.isNaN(d)) 41 | return null; 42 | return d; 43 | } 44 | 45 | /** 46 | * get the registered sign at the given location 47 | * if no sign registered, return null 48 | */ 49 | public static SignRegistration getSign(NyaaBank plugin, Location location) { 50 | List l = plugin.dbm.tableSignRegistration.select(new WhereClause() 51 | .whereEq(SignRegistration.N_LOCATION_WORLD_NAME, location.getWorld().getName()) 52 | .whereEq(SignRegistration.N_LOCATION_X, location.getBlockX()) 53 | .whereEq(SignRegistration.N_LOCATION_Y, location.getBlockY()) 54 | .whereEq(SignRegistration.N_LOCATION_Z, location.getBlockZ()) 55 | ); 56 | if (l.size() > 1 || l.size() <= 0) return null; 57 | return l.get(0); 58 | } 59 | 60 | public static boolean isSignBlock(Block b) { 61 | if (b == null) return false; 62 | return b.getType().name().endsWith("_SIGN"); 63 | } 64 | 65 | /** 66 | * update the sign according to the sign registeration 67 | * IllegalArgumentException thrown if not valid sign block. 68 | */ 69 | public static void updateSignBlock(NyaaBank plugin, Location signLocation, SignRegistration signReg) { 70 | if (signLocation == null || signLocation.getWorld() == null || signLocation.getBlock() == null || 71 | !isSignBlock(signLocation.getBlock())) { 72 | throw new IllegalArgumentException("Not a valid sign block"); 73 | } 74 | BankRegistration bank = plugin.dbm.getUniqueBank(signReg.bankId); 75 | if (bank == null) { 76 | throw new IllegalArgumentException("Invalid bank"); 77 | } 78 | Sign sign = (Sign) signLocation.getBlock().getState(); 79 | sign.setLine(0, plugin.cfg.signMagic); 80 | sign.setLine(1, (bank.status == BankStatus.BANKRUPT ? plugin.cfg.signColorBankrupt : plugin.cfg.signColorActive) 81 | + bank.name); 82 | switch (signReg.type) { 83 | case LOAN: 84 | sign.setLine(2, I18n.format("user.sign.text_loan")); 85 | sign.setLine(3, I18n.format("user.sign.hint_loan", signReg.loanAmount, bank.debitInterest)); 86 | break; 87 | case DEPOSIT: 88 | sign.setLine(2, I18n.format("user.sign.text_saving")); 89 | sign.setLine(3, I18n.format("user.sign.hint_saving", bank.savingInterest)); 90 | break; 91 | case WITHDRAW: 92 | sign.setLine(2, I18n.format("user.sign.text_withdraw")); 93 | sign.setLine(3, I18n.format("user.sign.hint_withdraw", signReg.commissionFee)); 94 | break; 95 | case REPAY: 96 | sign.setLine(2, I18n.format("user.sign.text_repay")); 97 | sign.setLine(3, I18n.format("user.sign.hint_repay", signReg.commissionFee)); 98 | break; 99 | default: 100 | throw new IllegalArgumentException("Invalid sign type"); 101 | } 102 | sign.update(); 103 | } 104 | 105 | /** 106 | * Parse the sign at the given location 107 | * If signReg given, info is stored into it, otherwise a new SignRegistration is created 108 | * signReg or new object is returned, null is returned if errors occur 109 | * The sign itself is not altered. 110 | */ 111 | public static SignRegistration parseSign(NyaaBank plugin, String[] lines, SignRegistration signReg, Location signLoc) { 112 | if (signReg == null) { 113 | signReg = new SignRegistration(); 114 | signReg.signId = UUID.randomUUID(); 115 | } 116 | String magic = lines[0]; 117 | if (!stringEqIgnoreColor(magic, plugin.cfg.signMagic, true) && 118 | !stringEqIgnoreColor(magic, SignListener.SIGN_MAGIC_FALLBACK, true)) { 119 | return null; 120 | } 121 | BankRegistration bank = null; 122 | try { 123 | long idNumber = Long.parseLong(lines[1]); 124 | bank = plugin.dbm.getBankByIdNumber(idNumber); 125 | } catch (NumberFormatException ex) { 126 | bank = null; 127 | } 128 | if (bank == null) return null; 129 | signReg.bankId = bank.bankId; 130 | signReg.commissionFee = 0D; 131 | signReg.location = signLoc.clone(); 132 | signReg.loanAmount = -1D; // to meet DB not null constraint 133 | String srv = lines[2].toUpperCase(); 134 | switch (srv) { 135 | case "DEPOSIT": 136 | signReg.type = TransactionType.DEPOSIT; 137 | break; 138 | case "WITHDRAW": 139 | signReg.type = TransactionType.WITHDRAW; 140 | signReg.commissionFee = parseDouble(lines[3]); 141 | if (signReg.commissionFee == null) return null; 142 | break; 143 | case "LOAN": 144 | signReg.type = TransactionType.LOAN; 145 | signReg.loanAmount = parseDouble(lines[3]); 146 | if (signReg.loanAmount == null) return null; 147 | break; 148 | case "REPAY": 149 | signReg.type = TransactionType.REPAY; 150 | signReg.commissionFee = parseDouble(lines[3]); 151 | if (signReg.commissionFee == null) return null; 152 | break; 153 | default: 154 | return null; 155 | } 156 | return signReg; 157 | } 158 | 159 | /** 160 | * Update all signs in the given list. 161 | * One sign per tick, so it may take several ticks to complete the request 162 | */ 163 | public static void batchUpdateSign(NyaaBank plugin, List signList) { 164 | if (signList == null || signList.size() == 0) return; 165 | new BukkitRunnable() { 166 | int idx = 0; 167 | boolean cancelled = false; 168 | 169 | @Override 170 | public void run() { 171 | if (cancelled) { 172 | this.cancel(); 173 | return; 174 | } 175 | SignRegistration sr = signList.get(idx); 176 | try { 177 | updateSignBlock(plugin, sr.location, sr); 178 | } catch (IllegalArgumentException ex) { // remove invalid sign registrations 179 | plugin.dbm.tableSignRegistration.delete(WhereClause.EQ(SignRegistration.N_SIGN_ID, sr.signId)); 180 | ex.printStackTrace(); 181 | } 182 | idx++; 183 | if (idx >= signList.size()) { 184 | this.cancel(); 185 | cancelled = true; 186 | } 187 | } 188 | }.runTaskTimer(plugin, 1L, 1L); 189 | } 190 | 191 | /** 192 | * Update all signs of the given bank 193 | */ 194 | public static void batchUpdateSign(NyaaBank plugin, BankRegistration bank) { 195 | batchUpdateSign(plugin, plugin.dbm.tableSignRegistration.select(WhereClause.EQ(SignRegistration.N_BANK_ID, bank.bankId))); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/BankManagementCommands.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank; 2 | 3 | import cat.nyaa.nyaabank.database.enums.BankStatus; 4 | import cat.nyaa.nyaabank.database.enums.InterestType; 5 | import cat.nyaa.nyaabank.database.enums.TransactionType; 6 | import cat.nyaa.nyaabank.database.tables.BankAccount; 7 | import cat.nyaa.nyaabank.database.tables.BankRegistration; 8 | import cat.nyaa.nyaabank.database.tables.PartialRecord; 9 | import cat.nyaa.nyaacore.LanguageRepository; 10 | import cat.nyaa.nyaacore.cmdreceiver.Arguments; 11 | import cat.nyaa.nyaacore.cmdreceiver.BadCommandException; 12 | import cat.nyaa.nyaacore.cmdreceiver.CommandReceiver; 13 | import cat.nyaa.nyaacore.cmdreceiver.SubCommand; 14 | import cat.nyaa.nyaacore.orm.WhereClause; 15 | import com.google.common.collect.HashMultimap; 16 | import com.google.common.collect.Multimap; 17 | import org.bukkit.Bukkit; 18 | import org.bukkit.OfflinePlayer; 19 | import org.bukkit.command.CommandSender; 20 | import org.bukkit.entity.Player; 21 | 22 | import java.util.*; 23 | import java.util.stream.Collectors; 24 | import java.util.stream.Stream; 25 | 26 | public class BankManagementCommands extends CommandReceiver { 27 | private final NyaaBank plugin; 28 | 29 | public BankManagementCommands(Object plugin, LanguageRepository i18n) { 30 | super((NyaaBank) plugin, i18n); 31 | this.plugin = (NyaaBank) plugin; 32 | } 33 | 34 | @Override 35 | public String getHelpPrefix() { 36 | return "bank"; 37 | } 38 | 39 | /** 40 | * Get unique bank considering sender's permission 41 | * If sender is not player, ownership check is skipped 42 | * 43 | * @param sender who invoked the command 44 | * @param idNumber bank idNumber 45 | * @param noBankLang language key to be print when no bank is found 46 | * @param adminPermission permission required to skip the ownership check 47 | * @param permissionLang language key for wrong ownership 48 | * @return the bank 49 | */ 50 | private BankRegistration getBankWithPermission(CommandSender sender, long idNumber, String noBankLang, 51 | String adminPermission, String permissionLang) { 52 | BankRegistration bank = plugin.dbm.getBankByIdNumber(idNumber); 53 | if (bank == null) throw new BadCommandException(noBankLang); 54 | if (sender instanceof Player) { 55 | if (!((Player) sender).getUniqueId().equals(bank.ownerId)) { 56 | if (!sender.hasPermission(adminPermission)) { 57 | throw new BadCommandException(permissionLang); 58 | } 59 | } 60 | } 61 | return bank; 62 | } 63 | 64 | @SubCommand(value = "list", permission = "nb.bank_list") 65 | public void listBanks(CommandSender sender, Arguments args) { 66 | if (sender.hasPermission("nb.bank_list_admin")) { // OPs 67 | String playerName = args.next(); 68 | List banks; 69 | if (playerName == null) { 70 | banks = plugin.dbm.tableBankRegistration.select(WhereClause.EMPTY); 71 | msg(sender, "command.bank_list.list_all"); 72 | } else { 73 | UUID id = plugin.getServer().getOfflinePlayer(playerName).getUniqueId(); 74 | banks = plugin.dbm.tableBankRegistration.select(WhereClause.EQ(BankRegistration.N_OWNER_ID, id)); 75 | msg(sender, "command.bank_list.list_player", playerName); 76 | } 77 | if (banks.size() <= 0) { 78 | msg(sender, "command.bank_list.list_empty"); 79 | return; 80 | } 81 | banks.sort(Comparator.comparing(a -> a.ownerId)); 82 | for (BankRegistration r : banks) { 83 | OfflinePlayer p = plugin.getServer().getOfflinePlayer(r.ownerId); 84 | msg(sender, "command.bank_list.list_item", r.idNumber, r.name, p.getName(), r.bankId.toString()); 85 | } 86 | } else { // players 87 | Player p = asPlayer(sender); 88 | List banks = plugin.dbm.tableBankRegistration.select(WhereClause.EQ(BankRegistration.N_OWNER_ID, p.getUniqueId())); 89 | 90 | msg(sender, "command.bank_list.list_player", p.getName()); 91 | if (banks.size() <= 0) { 92 | msg(sender, "command.bank_list.list_empty"); 93 | return; 94 | } 95 | 96 | banks.sort(Comparator.comparing(a -> a.name)); 97 | for (BankRegistration r : banks) { 98 | msg(sender, "command.bank_list.list_item", r.idNumber, r.name, p.getName(), r.bankId.toString()); 99 | } 100 | } 101 | } 102 | 103 | @SubCommand(value = "info", permission = "nb.bank_info") 104 | public void bankInfo(CommandSender sender, Arguments args) { 105 | if (args.top() == null) throw new BadCommandException(); 106 | BankRegistration bank = getBankWithPermission(sender, args.nextInt(), 107 | "command.bank_info.no_such_bank", 108 | "nb.bank_info_admin", 109 | "command.bank_info.only_self"); 110 | 111 | String ownerName = "UNKNOWN"; 112 | if (Bukkit.getOfflinePlayer(bank.ownerId) != null) { 113 | if (Bukkit.getOfflinePlayer(bank.ownerId).getName() != null) { 114 | ownerName = Bukkit.getOfflinePlayer(bank.ownerId).getName(); 115 | } 116 | } 117 | msg(sender, "command.bank_info.info", 118 | bank.name, 119 | bank.bankId.toString(), 120 | ownerName, 121 | bank.ownerId.toString(), 122 | bank.establishDate.toString(), 123 | bank.status.name(), 124 | bank.registered_capital, 125 | bank.savingInterest, 126 | bank.debitInterest, 127 | bank.interestType.name(), 128 | bank.savingInterestNext, 129 | bank.debitInterestNext, 130 | bank.interestTypeNext.name()); 131 | } 132 | 133 | @SubCommand(value = "interest", permission = "nb.bank_interest") 134 | public void changeInterestInfo(CommandSender sender, Arguments args) { 135 | int idNumber = args.nextInt(); 136 | String op = args.next(); // OPERATION: SAVING/LOAN/TYPE 137 | 138 | BankRegistration bank = getBankWithPermission(sender, idNumber, "command.bank_interest.no_such_bank", 139 | "nb.bank_interest_admin", "command.bank_interest.only_self"); 140 | if (bank.status == BankStatus.BANKRUPT) throw new BadCommandException("command.bank_interest.bankrupted"); 141 | op = op.toUpperCase(); 142 | switch (op) { 143 | case "SAVING": { 144 | double newSaving = args.nextDouble(); 145 | if (plugin.cfg.savingInterestLimitEnabled) { 146 | if (plugin.cfg.savingInterestLimitMax < newSaving || 147 | plugin.cfg.savingInterestLimitMin > newSaving) { 148 | throw new BadCommandException("command.bank_interest.rate_out_of_range", 149 | plugin.cfg.savingInterestLimitMin, 150 | plugin.cfg.savingInterestLimitMax); 151 | } 152 | } 153 | bank.savingInterestNext = newSaving; 154 | plugin.dbm.tableBankRegistration.update(bank, WhereClause.EQ(BankRegistration.N_BANK_ID, bank.bankId), BankRegistration.N_INTEREST_RATE_SAVING_NEXT); 155 | break; 156 | } 157 | case "LOAN": { 158 | double newLoan = args.nextDouble(); 159 | if (plugin.cfg.debitInterestLimitEnabled) { 160 | if (plugin.cfg.debitInterestLimitMax < newLoan || 161 | plugin.cfg.debitInterestLimitMin > newLoan) { 162 | throw new BadCommandException("command.bank_interest.rate_out_of_range", 163 | plugin.cfg.debitInterestLimitMin, 164 | plugin.cfg.debitInterestLimitMax); 165 | } 166 | } 167 | bank.debitInterestNext = newLoan; 168 | plugin.dbm.tableBankRegistration.update(bank, WhereClause.EQ(BankRegistration.N_BANK_ID, bank.bankId), BankRegistration.N_INTEREST_RATE_DEBIT_NEXT); 169 | break; 170 | } 171 | case "TYPE": { 172 | InterestType newType = args.nextEnum(InterestType.class); 173 | bank.interestTypeNext = newType; 174 | plugin.dbm.tableBankRegistration.update(bank, WhereClause.EQ(BankRegistration.N_BANK_ID, bank.bankId), BankRegistration.N_INTEREST_TYPE_NEXT); 175 | break; 176 | } 177 | default: 178 | throw new BadCommandException(); 179 | } 180 | } 181 | 182 | @SubCommand(value = "customers", permission = "nb.bank_customers") 183 | public void listCustomers(CommandSender sender, Arguments args) { 184 | BankRegistration bank = getBankWithPermission(sender, args.nextInt(), "command.bank_customers.no_such_bank", 185 | "nb.bank_customers_admin", "command.bank_customers.only_self"); 186 | 187 | Map accounts = new HashMap<>(); 188 | Multimap partials = HashMultimap.create(); 189 | for (BankAccount a : plugin.dbm.tableBankAccount.select(WhereClause.EQ(BankAccount.N_BANK_ID, bank.bankId))) { 190 | if (accounts.containsKey(a.playerId)) { 191 | plugin.getLogger().severe(String.format("Multiple accounts bank-id=%s, player-id=%s", 192 | bank.bankId.toString(), a.playerId)); 193 | } 194 | accounts.put(a.playerId, a); 195 | } 196 | for (PartialRecord p : plugin.dbm.tablePartialRecord.select(WhereClause.EQ(PartialRecord.N_BANK_ID, bank.bankId))) { 197 | partials.put(p.playerId, p); 198 | } 199 | 200 | List ids = Stream.concat(accounts.keySet().stream(), partials.keySet().stream()) 201 | .distinct().sorted().collect(Collectors.toList()); 202 | for (UUID playerId : ids) { 203 | double saving = 0; 204 | if (accounts.containsKey(playerId)) 205 | saving += accounts.get(playerId).deposit + accounts.get(playerId).deposit_interest; 206 | for (PartialRecord p : partials.get(playerId)) 207 | saving += p.type == TransactionType.DEPOSIT ? p.capital : 0; 208 | double loan = 0; 209 | if (accounts.containsKey(playerId)) 210 | loan += accounts.get(playerId).loan + accounts.get(playerId).loan_interest; 211 | for (PartialRecord p : partials.get(playerId)) 212 | loan += p.type == TransactionType.LOAN ? p.capital : 0; 213 | String id = playerId.toString(); 214 | if (Bukkit.getPlayer(playerId) != null && Bukkit.getPlayer(playerId).getName() != null) { 215 | id = Bukkit.getPlayer(playerId).getName(); 216 | } 217 | msg(sender, "command.bank_customers.customer_record", id, saving, loan); 218 | } 219 | if (ids.size() <= 0) { 220 | msg(sender, "command.bank_customers.no_customer"); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/signs/SignListener.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.signs; 2 | 3 | import cat.nyaa.nyaabank.CommonAction; 4 | import cat.nyaa.nyaabank.I18n; 5 | import cat.nyaa.nyaabank.NyaaBank; 6 | import cat.nyaa.nyaabank.database.enums.BankStatus; 7 | import cat.nyaa.nyaabank.database.enums.TransactionType; 8 | import cat.nyaa.nyaabank.database.tables.BankRegistration; 9 | import cat.nyaa.nyaabank.database.tables.SignRegistration; 10 | import cat.nyaa.nyaacore.orm.WhereClause; 11 | import org.bukkit.ChatColor; 12 | import org.bukkit.OfflinePlayer; 13 | import org.bukkit.block.Sign; 14 | import org.bukkit.entity.Player; 15 | import org.bukkit.event.EventHandler; 16 | import org.bukkit.event.EventPriority; 17 | import org.bukkit.event.Listener; 18 | import org.bukkit.event.block.Action; 19 | import org.bukkit.event.block.BlockBreakEvent; 20 | import org.bukkit.event.block.SignChangeEvent; 21 | import org.bukkit.event.player.PlayerInteractEvent; 22 | import org.bukkit.scheduler.BukkitRunnable; 23 | 24 | import static cat.nyaa.nyaabank.signs.SignHelper.stringEqIgnoreColor; 25 | 26 | public class SignListener implements Listener { 27 | public final static String SIGN_MAGIC_FALLBACK = "[BANK]"; 28 | private final ChatInputCallbacks callbacks; 29 | private final NyaaBank plugin; 30 | 31 | public SignListener(NyaaBank plugin) { 32 | this.plugin = plugin; 33 | plugin.getServer().getPluginManager().registerEvents(this, plugin); 34 | callbacks = new ChatInputCallbacks(plugin); 35 | } 36 | 37 | /** 38 | * Apply commission fee, move from player to bank. 39 | * amount can be negative 40 | */ 41 | private void applyCommission(Player player, BankRegistration bank, double amount) { 42 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 43 | if (amount > 0) { 44 | plugin.eco.withdrawPlayer(player, amount); 45 | plugin.eco.depositPlayer(banker, amount); 46 | plugin.dbm.log(TransactionType.COMMISSION).from(player.getUniqueId()).to(bank.bankId) 47 | .capital(amount).insert(); 48 | } else if (amount < 0) { 49 | plugin.eco.depositPlayer(player, -amount); 50 | plugin.eco.withdrawPlayer(banker, amount); 51 | plugin.dbm.log(TransactionType.COMMISSION).from(player.getUniqueId()).to(bank.bankId) 52 | .capital(amount).insert(); 53 | } // do nothing if zero commission 54 | } 55 | 56 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 57 | public void onPlayerRightClickSign(PlayerInteractEvent ev) { 58 | if (!ev.hasBlock()) return; 59 | if (ev.getAction() != Action.RIGHT_CLICK_BLOCK) return; 60 | if (!SignHelper.isSignBlock(ev.getClickedBlock())) return; 61 | Sign sign = (Sign) ev.getClickedBlock().getState(); 62 | String magicLine = ChatColor.stripColor(sign.getLine(0)); 63 | if (stringEqIgnoreColor(magicLine, plugin.cfg.signMagic, false) || 64 | stringEqIgnoreColor(magicLine, SIGN_MAGIC_FALLBACK, false)) { 65 | Player p = ev.getPlayer(); 66 | if (callbacks.callbacks.containsKey(p.getUniqueId())) return; 67 | SignRegistration sr = SignHelper.getSign(plugin, ev.getClickedBlock().getLocation()); 68 | if (sr == null) { 69 | p.sendMessage(I18n.format("user.sign.invalid_sign")); 70 | return; 71 | } 72 | BankRegistration bank = plugin.dbm.getUniqueBank(sr.bankId.toString()); 73 | if (bank == null) { 74 | p.sendMessage(I18n.format("user.sign.invalid_sign")); 75 | return; 76 | } 77 | if (bank.status == BankStatus.BANKRUPT) { 78 | p.sendMessage(I18n.format("user.sign.bankrupted")); 79 | return; 80 | } 81 | if (sr.type == TransactionType.LOAN && sr.loanAmount <= 0) { 82 | p.sendMessage(I18n.format("user.sign.invalid_sign")); 83 | return; 84 | } 85 | switch (sr.type) { 86 | case DEPOSIT: 87 | if (!p.hasPermission("nb.deposit")) { 88 | p.sendMessage(I18n.format("user.sign.use_no_permission")); 89 | break; 90 | } 91 | p.sendMessage(I18n.format("user.sign.input_prompt_deposit", plugin.cfg.signTimeout)); 92 | callbacks.register(ev.getPlayer().getUniqueId(), 93 | new ChatInputCallbacks.InputCallback() { 94 | @Override 95 | public void onDoubleInput(Player p, double input, boolean isAll) { 96 | try { 97 | if (isAll) 98 | throw new CommonAction.TransactionException("user.sign.invalid_number"); 99 | CommonAction.deposit(plugin, p, bank, input); 100 | p.sendMessage(I18n.format("user.sign.input_accepted")); 101 | } catch (CommonAction.TransactionException ex) { 102 | p.sendMessage(I18n.format(ex.getMessage())); 103 | } 104 | } 105 | }); 106 | break; 107 | case WITHDRAW: 108 | if (!p.hasPermission("nb.withdraw")) { 109 | p.sendMessage(I18n.format("user.sign.use_no_permission")); 110 | break; 111 | } 112 | p.sendMessage(I18n.format("user.sign.input_prompt_withdraw", plugin.cfg.signTimeout)); 113 | callbacks.register(ev.getPlayer().getUniqueId(), 114 | new ChatInputCallbacks.InputCallback() { 115 | @Override 116 | public void onDoubleInput(Player p, double input, boolean isAll) { 117 | try { 118 | CommonAction.withdraw(plugin, p, bank, input, isAll); 119 | applyCommission(p, bank, sr.commissionFee); 120 | p.sendMessage(I18n.format("user.sign.input_accepted")); 121 | 122 | } catch (CommonAction.TransactionException ex) { 123 | p.sendMessage(I18n.format(ex.getMessage())); 124 | } 125 | } 126 | }); 127 | break; 128 | case LOAN: 129 | if (!p.hasPermission("nb.loan")) { 130 | p.sendMessage(I18n.format("user.sign.use_no_permission")); 131 | break; 132 | } 133 | p.sendMessage(I18n.format("user.sign.input_prompt_loan", plugin.cfg.signTimeout)); 134 | callbacks.register(ev.getPlayer().getUniqueId(), 135 | new ChatInputCallbacks.InputCallback() { 136 | @Override 137 | public void onDoubleInput(Player p, double input, boolean isAll) { 138 | try { 139 | if (!isAll) 140 | throw new CommonAction.TransactionException("user.sign.loan_cancelled"); 141 | CommonAction.loan(plugin, p, bank, sr.loanAmount); 142 | p.sendMessage(I18n.format("user.sign.input_accepted")); 143 | } catch (CommonAction.TransactionException ex) { 144 | p.sendMessage(I18n.format(ex.getMessage())); 145 | } 146 | } 147 | }); 148 | break; 149 | case REPAY: 150 | if (!p.hasPermission("nb.repay")) { 151 | p.sendMessage(I18n.format("user.sign.use_no_permission")); 152 | break; 153 | } 154 | p.sendMessage(I18n.format("user.sign.input_prompt_repay", plugin.cfg.signTimeout)); 155 | callbacks.register(ev.getPlayer().getUniqueId(), 156 | new ChatInputCallbacks.InputCallback() { 157 | @Override 158 | public void onDoubleInput(Player p, double input, boolean isAll) { 159 | try { 160 | CommonAction.repay(plugin, p, bank, input, isAll); 161 | applyCommission(p, bank, sr.commissionFee); 162 | p.sendMessage(I18n.format("user.sign.input_accepted")); 163 | } catch (CommonAction.TransactionException ex) { 164 | p.sendMessage(I18n.format(ex.getMessage())); 165 | } 166 | } 167 | }); 168 | break; 169 | default: 170 | p.sendMessage(I18n.format("user.sign.invalid_sign")); 171 | return; // do not update if an invalid sign 172 | } 173 | new BukkitRunnable() { 174 | @Override 175 | public void run() { 176 | SignHelper.updateSignBlock(plugin, ev.getClickedBlock().getLocation(), sr); 177 | } 178 | }.runTaskLater(plugin, 1L); // update block after click; 179 | } 180 | } 181 | 182 | @EventHandler(ignoreCancelled = true) 183 | public void onPlayerCreateSign(SignChangeEvent ev) { 184 | if (!ev.getPlayer().hasPermission("nb.sign_create")) return; 185 | String firstLine = ev.getLine(0); 186 | if (!SignHelper.stringEqIgnoreColor(firstLine, SIGN_MAGIC_FALLBACK, true) && 187 | !SignHelper.stringEqIgnoreColor(firstLine, plugin.cfg.signMagic, true)) 188 | return; // not a bank sign 189 | SignRegistration sr = SignHelper.getSign(plugin, ev.getBlock().getLocation()); 190 | boolean newSign = false; 191 | if (sr == null) newSign = true; 192 | sr = SignHelper.parseSign(plugin, ev.getLines(), sr, ev.getBlock().getLocation()); 193 | if (sr == null) { 194 | ev.getPlayer().sendMessage(I18n.format("user.sign.create_fail")); 195 | return; 196 | } 197 | if (!ev.getPlayer().hasPermission("nb.sign_create_admin")) { // skip bank owner check for admins 198 | BankRegistration bank = plugin.dbm.getUniqueBank(sr.bankId); 199 | if (bank == null || !ev.getPlayer().getUniqueId().equals(bank.ownerId)) { 200 | ev.getPlayer().sendMessage(I18n.format("user.sign.create_fail")); 201 | return; 202 | } 203 | } 204 | if (newSign) { 205 | ; 206 | plugin.dbm.tableSignRegistration.insert(sr); 207 | } else { 208 | ; 209 | plugin.dbm.tableSignRegistration.update(sr, WhereClause.EQ(SignRegistration.N_SIGN_ID, sr.signId)); 210 | } 211 | ev.getPlayer().sendMessage(I18n.format("user.sign.create_success")); 212 | 213 | // update sign next tick 214 | final SignRegistration fsr = sr; 215 | new BukkitRunnable() { 216 | @Override 217 | public void run() { 218 | SignHelper.updateSignBlock(plugin, ev.getBlock().getLocation(), fsr); 219 | } 220 | }.runTaskLater(plugin, 1); 221 | } 222 | 223 | @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) 224 | public void onPlayerBreakSign(BlockBreakEvent ev) { 225 | if (!SignHelper.isSignBlock(ev.getBlock())) return; 226 | SignRegistration sr = SignHelper.getSign(plugin, ev.getBlock().getLocation()); 227 | if (sr == null) return; 228 | if (!ev.getPlayer().hasPermission("nb.sign_break")) { 229 | ev.getPlayer().sendMessage(I18n.format("user.sign.break_no_permission")); 230 | ev.setCancelled(true); 231 | return; 232 | } 233 | if (!ev.getPlayer().hasPermission("nb.sign_break_admin")) { // skip owner check for admin 234 | BankRegistration bank = plugin.dbm.getUniqueBank(sr.bankId); 235 | if (bank != null && !ev.getPlayer().getUniqueId().equals(bank.ownerId)) { 236 | ev.getPlayer().sendMessage(I18n.format("user.sign.break_no_permission")); 237 | ev.setCancelled(true); 238 | return; 239 | } 240 | } 241 | plugin.dbm.tableSignRegistration.delete(WhereClause.EQ(SignRegistration.N_SIGN_ID, sr.signId)); 242 | ev.getPlayer().sendMessage(I18n.format("user.sign.break_success")); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/CommonAction.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank; 2 | 3 | import cat.nyaa.nyaabank.database.enums.BankStatus; 4 | import cat.nyaa.nyaabank.database.enums.TransactionType; 5 | import cat.nyaa.nyaabank.database.tables.BankAccount; 6 | import cat.nyaa.nyaabank.database.tables.BankRegistration; 7 | import cat.nyaa.nyaabank.database.tables.PartialRecord; 8 | import cat.nyaa.nyaacore.orm.WhereClause; 9 | import org.bukkit.OfflinePlayer; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.time.Instant; 13 | import java.util.List; 14 | import java.util.UUID; 15 | 16 | public final class CommonAction { 17 | public static class TransactionException extends Exception { 18 | public TransactionException(String msg) { 19 | super(msg); 20 | } 21 | 22 | @Override 23 | public String getMessage() { 24 | return super.getMessage(); 25 | } 26 | } 27 | 28 | public static void deposit(NyaaBank plugin, Player player, BankRegistration bank, 29 | double amount) throws TransactionException { 30 | if (amount <= 0) throw new TransactionException("user.deposit.invalid_amount"); 31 | if (!plugin.eco.has(player, amount)) throw new TransactionException("user.deposit.not_enough_money"); 32 | if (bank == null) throw new TransactionException("user.deposit.bank_not_found"); 33 | if (bank.status == BankStatus.BANKRUPT) throw new TransactionException("user.deposit.bankrupted"); 34 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 35 | 36 | plugin.eco.withdrawPlayer(player, amount); 37 | PartialRecord partial = new PartialRecord(); 38 | partial.transactionId = UUID.randomUUID(); 39 | partial.bankId = bank.bankId; 40 | partial.playerId = player.getUniqueId(); 41 | partial.capital = amount; 42 | partial.type = TransactionType.DEPOSIT; 43 | partial.startDate = Instant.now(); 44 | plugin.dbm.tablePartialRecord.insert(partial); 45 | plugin.dbm.log(TransactionType.DEPOSIT) 46 | .from(player.getUniqueId()) 47 | .to(bank.bankId) 48 | .capital(amount) 49 | .extra("partialId", partial.transactionId.toString()) 50 | .insert(); 51 | plugin.eco.depositPlayer(banker, amount); 52 | } 53 | 54 | public static void withdraw(NyaaBank plugin, Player player, BankRegistration bank, 55 | double amount, boolean withdrawAll) throws TransactionException { 56 | if (bank == null) throw new TransactionException("user.withdraw.bank_not_found"); 57 | if (bank.status == BankStatus.BANKRUPT) throw new TransactionException("user.withdraw.bankrupted"); 58 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 59 | double totalDeposit = plugin.dbm.getTotalDeposit(bank.bankId, player.getUniqueId()); 60 | if (withdrawAll) { 61 | if (!plugin.eco.has(banker, totalDeposit)) throw new TransactionException("user.withdraw.bank_run"); 62 | BankAccount account = plugin.dbm.getAccount(bank.bankId, player.getUniqueId()); 63 | if (account != null) { 64 | account.deposit = 0D; 65 | account.deposit_interest = 0D; 66 | plugin.dbm.tableBankAccount.update( 67 | account, 68 | WhereClause.EQ(BankAccount.N_ACCOUNT_ID, account.accountId), 69 | BankAccount.N_DEPOSIT, BankAccount.N_DEPOSIT_INTEREST 70 | ); 71 | } 72 | plugin.dbm.tablePartialRecord.delete( 73 | new WhereClause() 74 | .whereEq(PartialRecord.N_BANK_ID, bank.bankId) 75 | .whereEq(PartialRecord.N_PLAYER_ID, player.getUniqueId()) 76 | .whereEq(PartialRecord.N_TRANSACTION_TYPE, TransactionType.DEPOSIT) 77 | ); 78 | plugin.eco.withdrawPlayer(banker, totalDeposit); 79 | plugin.dbm.log(TransactionType.WITHDRAW).from(bank.bankId).to(player.getUniqueId()) 80 | .capital(totalDeposit).insert(); 81 | plugin.eco.depositPlayer(player, totalDeposit); 82 | } else { 83 | if (amount <= 0) throw new TransactionException("user.withdraw.invalid_amount"); 84 | if (amount > totalDeposit) throw new TransactionException("user.withdraw.not_enough_deposit"); 85 | if (!plugin.eco.has(banker, amount)) throw new TransactionException("user.withdraw.bank_run"); 86 | double realAmount = 0; 87 | List l = plugin.dbm.getPartialRecords(bank.bankId, player.getUniqueId(), TransactionType.DEPOSIT); 88 | l.sort((a, b) -> a.capital.equals(b.capital) ? 0 : (a.capital < b.capital ? -1 : 1)); 89 | int idx = 0; 90 | while (amount > 0 && idx < l.size()) { 91 | PartialRecord r = l.get(idx); 92 | if (amount > r.capital) { 93 | amount -= r.capital; 94 | realAmount += r.capital; 95 | plugin.dbm.tablePartialRecord.delete(WhereClause.EQ(PartialRecord.N_TRANSACTION_ID, r.transactionId)); 96 | idx++; 97 | } else { 98 | realAmount += amount; 99 | r.capital -= amount; 100 | amount = -1; 101 | plugin.dbm.tablePartialRecord.update(r, WhereClause.EQ(PartialRecord.N_TRANSACTION_ID, r.transactionId), PartialRecord.N_CAPITAL); 102 | } 103 | } 104 | if (amount > 0) { 105 | BankAccount account = plugin.dbm.getAccount(bank.bankId, player.getUniqueId()); 106 | if (amount > account.deposit + account.deposit_interest) { 107 | realAmount += account.deposit + account.deposit_interest; 108 | account.deposit = 0D; 109 | account.deposit_interest = 0D; 110 | } else if (amount > account.deposit_interest) { 111 | account.deposit -= amount - account.deposit_interest; 112 | account.deposit_interest = 0D; 113 | realAmount += amount; 114 | amount = 0D; 115 | } else { 116 | account.deposit_interest -= amount; 117 | realAmount += amount; 118 | amount = 0D; 119 | } 120 | plugin.dbm.tableBankAccount.update(account, WhereClause.EQ(BankAccount.N_ACCOUNT_ID, account.accountId), BankAccount.N_DEPOSIT, BankAccount.N_DEPOSIT_INTEREST); 121 | } 122 | plugin.eco.withdrawPlayer(banker, realAmount); 123 | plugin.dbm.log(TransactionType.WITHDRAW).from(bank.bankId).to(player.getUniqueId()) 124 | .capital(realAmount).insert(); 125 | plugin.eco.depositPlayer(player, realAmount); 126 | } 127 | } 128 | 129 | public static void loan(NyaaBank plugin, Player player, BankRegistration bank, 130 | double amount) throws TransactionException { 131 | if (amount <= 0) throw new TransactionException("user.loan.invalid_amount"); 132 | if (bank == null) throw new TransactionException("user.loan.bank_not_found"); 133 | if (bank.status == BankStatus.BANKRUPT) throw new TransactionException("user.loan.bankrupted"); 134 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 135 | if (!plugin.eco.has(banker, amount)) throw new TransactionException("user.loan.not_enough_money_bank"); 136 | BankAccount account = plugin.dbm.getAccount(bank.bankId, player.getUniqueId()); 137 | if (account != null && (account.loan > 0 || account.loan_interest > 0)) { 138 | throw new TransactionException("user.loan.has_loan"); 139 | } 140 | 141 | int loan_count = plugin.dbm.tablePartialRecord.count(new WhereClause() 142 | .whereEq(PartialRecord.N_PLAYER_ID, player.getUniqueId()) 143 | .whereEq(PartialRecord.N_BANK_ID, bank.bankId) 144 | .whereEq(PartialRecord.N_TRANSACTION_TYPE, TransactionType.LOAN) 145 | ); 146 | if (loan_count > 0) { 147 | throw new TransactionException("user.loan.has_loan"); 148 | } 149 | PartialRecord partial = new PartialRecord(); 150 | partial.transactionId = UUID.randomUUID(); 151 | partial.bankId = bank.bankId; 152 | partial.playerId = player.getUniqueId(); 153 | partial.capital = amount; 154 | partial.type = TransactionType.LOAN; 155 | partial.startDate = Instant.now(); 156 | plugin.dbm.tablePartialRecord.insert(partial); 157 | plugin.eco.withdrawPlayer(banker, amount); 158 | plugin.eco.depositPlayer(player, amount); 159 | plugin.dbm.log(TransactionType.LOAN) 160 | .to(player.getUniqueId()) 161 | .from(bank.bankId) 162 | .capital(amount) 163 | .extra("partialId", partial.transactionId.toString()) 164 | .insert(); 165 | } 166 | 167 | public static void repay(NyaaBank plugin, Player player, BankRegistration bank, 168 | double amount, boolean repayAll) throws TransactionException { 169 | if (bank == null) throw new TransactionException("user.repay.bank_not_found"); 170 | if (bank.status == BankStatus.BANKRUPT) throw new TransactionException("user.repay.bankrupted"); 171 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 172 | 173 | double totalLoan = plugin.dbm.getTotalLoan(bank.bankId, player.getUniqueId()); 174 | if (repayAll) { 175 | if (!plugin.eco.has(player, totalLoan)) throw new TransactionException("user.repay.not_enough_money"); 176 | BankAccount account = plugin.dbm.getAccount(bank.bankId, player.getUniqueId()); 177 | if (account != null) { 178 | account.loan = 0D; 179 | account.loan_interest = 0D; 180 | plugin.dbm.tableBankAccount.update(account, WhereClause.EQ(BankAccount.N_BANK_ID, account.accountId), BankAccount.N_LOAN, BankAccount.N_LOAN_INTEREST); 181 | } 182 | plugin.dbm.tablePartialRecord.delete(new WhereClause() 183 | .whereEq(PartialRecord.N_BANK_ID, bank.bankId) 184 | .whereEq(PartialRecord.N_PLAYER_ID, player.getUniqueId()) 185 | .whereEq(PartialRecord.N_TRANSACTION_TYPE, TransactionType.LOAN) 186 | ); 187 | plugin.eco.depositPlayer(banker, totalLoan); 188 | plugin.dbm.log(TransactionType.REPAY).to(bank.bankId).from(player.getUniqueId()) 189 | .capital(totalLoan).insert(); 190 | plugin.eco.withdrawPlayer(player, totalLoan); 191 | } else { 192 | if (amount <= 0) throw new TransactionException("user.repay.invalid_amount"); 193 | if (amount > totalLoan) throw new TransactionException("user.repay.no_need_to_pay"); 194 | if (!plugin.eco.has(player, amount)) throw new TransactionException("user.repay.not_enough_money"); 195 | double realAmount = 0; 196 | List l = plugin.dbm.getPartialRecords(bank.bankId, player.getUniqueId(), TransactionType.LOAN); 197 | l.sort((a, b) -> a.capital.compareTo(b.capital)); 198 | int idx = 0; 199 | while (amount > 0 && idx < l.size()) { 200 | PartialRecord r = l.get(idx); 201 | if (amount > r.capital) { 202 | amount -= r.capital; 203 | realAmount += r.capital; 204 | plugin.dbm.tablePartialRecord.delete(WhereClause.EQ(PartialRecord.N_TRANSACTION_ID, r.transactionId)); 205 | idx++; 206 | } else { 207 | realAmount += amount; 208 | r.capital -= amount; 209 | amount = -1; 210 | plugin.dbm.tablePartialRecord.update(r, WhereClause.EQ(PartialRecord.N_TRANSACTION_ID, r.transactionId), PartialRecord.N_CAPITAL); 211 | } 212 | } 213 | if (amount > 0) { 214 | BankAccount account = plugin.dbm.getAccount(bank.bankId, player.getUniqueId()); 215 | if (amount > account.loan + account.loan_interest) { 216 | realAmount += account.loan + account.loan_interest; 217 | account.loan = 0D; 218 | account.loan_interest = 0D; 219 | } else if (amount > account.loan_interest) { 220 | account.loan -= amount - account.loan_interest; 221 | account.loan_interest = 0D; 222 | realAmount += amount; 223 | amount = 0D; 224 | } else { 225 | account.loan_interest -= amount; 226 | realAmount += amount; 227 | amount = 0D; 228 | } 229 | plugin.dbm.tableBankAccount.update(account, WhereClause.EQ(BankAccount.N_ACCOUNT_ID, account.accountId), BankAccount.N_LOAN, BankAccount.N_LOAN_INTEREST); 230 | } 231 | plugin.eco.depositPlayer(banker, realAmount); 232 | plugin.dbm.log(TransactionType.REPAY).to(bank.bankId).from(player.getUniqueId()) 233 | .capital(realAmount).insert(); 234 | plugin.eco.withdrawPlayer(player, realAmount); 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/database/CycleManager.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank.database; 2 | 3 | import cat.nyaa.nyaabank.NyaaBank; 4 | import cat.nyaa.nyaabank.database.enums.BankStatus; 5 | import cat.nyaa.nyaabank.database.tables.BankAccount; 6 | import cat.nyaa.nyaabank.database.tables.BankRegistration; 7 | import cat.nyaa.nyaabank.database.tables.PartialRecord; 8 | import cat.nyaa.nyaabank.signs.SignHelper; 9 | import cat.nyaa.nyaacore.orm.RollbackGuard; 10 | import cat.nyaa.nyaacore.orm.WhereClause; 11 | import org.bukkit.OfflinePlayer; 12 | import org.bukkit.scheduler.BukkitRunnable; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.UUID; 17 | 18 | import static cat.nyaa.nyaabank.database.enums.TransactionType.*; 19 | 20 | public class CycleManager { 21 | private final NyaaBank plugin; 22 | 23 | public CycleManager(NyaaBank plugin) { 24 | this.plugin = plugin; 25 | onEnabled(); 26 | } 27 | 28 | private class CheckPointTask extends BukkitRunnable { 29 | CheckPointTask(long delay_ms) { 30 | long delay_ticks = delay_ms / 50 + 1; // ms to ticks 31 | this.runTaskLater(plugin, delay_ticks); 32 | } 33 | 34 | @Override 35 | public void run() { 36 | 37 | long len = plugin.cfg.interestCycle; 38 | long offset = plugin.cfg.interestCycleOffset; 39 | long now = System.currentTimeMillis() - offset; 40 | 41 | long idxB = Math.floorDiv(now, len); 42 | 43 | // schedule next timer 44 | long idxC = idxB + 1; 45 | long nextCheckpoint = idxC * len + offset + 1; 46 | long delay = nextCheckpoint - now; 47 | new CheckPointTask(delay); 48 | 49 | // update db 50 | updateDatabaseInterests(idxB * len + offset, len); 51 | 52 | // update lastCheckPoint 53 | plugin.cfg.lastCheckPoint = idxB * len + offset + 1; 54 | plugin.cfg.save(); 55 | 56 | try { 57 | SignHelper.batchUpdateSign(plugin, 58 | plugin.dbm.tableSignRegistration.select(WhereClause.EMPTY)); 59 | } catch (Exception e) { 60 | throw e; 61 | } 62 | // TODO we may not need to update all signs 63 | } 64 | } 65 | 66 | public long getNextCheckpoint() { 67 | long len = plugin.cfg.interestCycle; 68 | long offset = plugin.cfg.interestCycleOffset; 69 | long now = System.currentTimeMillis() - offset; 70 | long idxB = Math.floorDiv(now, len); 71 | long idxC = idxB + 1; 72 | return idxC * len + offset; 73 | } 74 | 75 | /** 76 | * 1. check any missed check points 77 | * 2. setup timer to next check point 78 | */ 79 | private void onEnabled() { 80 | long len = plugin.cfg.interestCycle; 81 | long offset = plugin.cfg.interestCycleOffset; 82 | long last = plugin.cfg.lastCheckPoint - offset; 83 | long now = System.currentTimeMillis() - offset; 84 | 85 | /* compute # of missed check points */ 86 | if (last >= 0) { 87 | long idxA = Math.floorDiv(last, len) + 1; 88 | long idxB = Math.floorDiv(now, len); 89 | for (long i = idxA; i <= idxB; i++) { 90 | updateDatabaseInterests(i * len + offset, len); // immediately compute missed check points 91 | } 92 | plugin.cfg.lastCheckPoint = idxB * len + offset + 1; 93 | } else { 94 | long idxB = Math.floorDiv(now, len); 95 | plugin.cfg.lastCheckPoint = idxB * len + offset + 1; 96 | } 97 | plugin.cfg.save(); 98 | 99 | /* setup next checkpoint timer */ 100 | long idxC = Math.floorDiv(now, len) + 1; 101 | long nextCheckpoint = idxC * len + offset + 1; 102 | long delay = nextCheckpoint - now; 103 | this.new CheckPointTask(delay); 104 | } 105 | 106 | /** 107 | * Actually check   update the database. 108 | * Since the timer won't tick exactly precise, there might be several seconds error. 109 | * Thus designatedTimestamp is used and the function acts like it's been called 110 | * exactly at that time. 111 | *

112 | * It compute the interest for deposit   loan in BankAccounts 113 | * It compute the interest for deposit   loan in PartialRecord 114 | * It updates interest := interestNext 115 | * It won't change the lastCheckPoint in config file. 116 | * 117 | * @param designatedTimestamp unix timestamp ms 118 | * @param cycleLength unix timestamp ms 119 | */ 120 | public void updateDatabaseInterests(long designatedTimestamp, long cycleLength) { 121 | // TODO maybe we can write this as a big SQL query? 122 | // TODO run in another thread? 123 | try (RollbackGuard guard = new RollbackGuard(plugin.dbm.db)) { 124 | Map> accountMap = new HashMap<>(); // Map> 125 | Map bankMap = new HashMap<>(); // Map 126 | for (BankAccount a : plugin.dbm.tableBankAccount.select(WhereClause.EMPTY)) { 127 | if (!accountMap.containsKey(a.bankId)) accountMap.put(a.bankId, new HashMap<>()); 128 | accountMap.get(a.bankId).put(a.playerId, a); 129 | } 130 | for (BankRegistration r : plugin.dbm.tableBankRegistration.select(WhereClause.EMPTY)) { 131 | bankMap.put(r.bankId, r); 132 | } 133 | // compute BankAccounts 134 | for (UUID bankId : accountMap.keySet()) { 135 | if (bankMap.get(bankId).status == BankStatus.BANKRUPT) continue; // skip bankrupted banks 136 | for (UUID playerId : accountMap.get(bankId).keySet()) { 137 | BankAccount account = accountMap.get(bankId).get(playerId); 138 | BankRegistration bank = bankMap.get(bankId); 139 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 140 | 141 | // Deposit interest 142 | double deposit_interest = 0; 143 | switch (bank.interestType) { 144 | case SIMPLE: 145 | deposit_interest = account.deposit * bank.savingInterest / 100D; 146 | break; 147 | case COMPOUND: 148 | deposit_interest = (account.deposit + account.deposit_interest) * bank.savingInterest / 100D; 149 | } 150 | deposit_interest = Math.round(deposit_interest * 1000D) / 1000D; // round to 10^-3 151 | 152 | if (deposit_interest >= 0) { 153 | account.deposit_interest += deposit_interest; 154 | } else if (deposit_interest + account.deposit_interest > 0) { // negative interest i.e. money transferred from player to bank 155 | account.deposit_interest += deposit_interest; 156 | } else { 157 | account.deposit += deposit_interest + account.deposit_interest; 158 | account.deposit_interest = 0D; 159 | } 160 | plugin.dbm.log(INTEREST_DEPOSIT).from(bankId).to(playerId).capital(deposit_interest).insert(); 161 | 162 | // Loan interest 163 | double loan_interest = 0; 164 | switch (bank.interestType) { 165 | case SIMPLE: 166 | loan_interest = account.loan * bank.debitInterest / 100D; 167 | break; 168 | case COMPOUND: 169 | loan_interest = (account.loan + account.loan_interest) * bank.debitInterest / 100D; 170 | } 171 | loan_interest = Math.round(loan_interest * 1000D) / 1000D; 172 | 173 | if (loan_interest >= 0) { 174 | account.loan_interest += loan_interest; 175 | } else if (loan_interest + account.loan_interest > 0) { // negative interest i.e. money transferred from bank to player 176 | account.loan_interest += loan_interest; 177 | } else { 178 | account.loan += loan_interest + account.loan_interest; 179 | account.loan_interest = 0D; 180 | } 181 | plugin.dbm.log(INTEREST_LOAN).from(playerId).to(bankId).capital(loan_interest).insert(); 182 | } 183 | } 184 | 185 | // compute Partial Records 186 | for (PartialRecord partial : plugin.dbm.tablePartialRecord.select(WhereClause.EMPTY)) { 187 | BankRegistration bank = bankMap.get(partial.bankId); 188 | if (bank.status == BankStatus.BANKRUPT) continue; // skip bankrupted banks 189 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 190 | BankAccount account = null; 191 | if (accountMap.containsKey(partial.bankId)) { 192 | account = accountMap.get(partial.bankId).get(partial.playerId); 193 | } 194 | boolean newAccount = false; 195 | if (account == null) { 196 | newAccount = true; 197 | account = new BankAccount(); 198 | account.accountId = UUID.randomUUID(); 199 | account.bankId = partial.bankId; 200 | account.playerId = partial.playerId; 201 | account.deposit = 0D; 202 | account.deposit_interest = 0D; 203 | account.loan = 0D; 204 | account.loan_interest = 0D; 205 | } 206 | 207 | switch (partial.type) { 208 | case DEPOSIT: { // Deposit interest 209 | double deposit_interest = partial.capital * bank.savingInterest / 100D; 210 | deposit_interest *= (designatedTimestamp - partial.startDate.toEpochMilli()) / (double) cycleLength; 211 | deposit_interest = Math.round(deposit_interest * 1000D) / 1000D; 212 | 213 | if (deposit_interest >= 0) { 214 | account.deposit += partial.capital; 215 | account.deposit_interest += deposit_interest; 216 | plugin.dbm.log(INTEREST_DEPOSIT).from(partial.bankId).to(partial.playerId).capital(deposit_interest) 217 | .extra("partialId", partial.transactionId.toString()).insert(); 218 | plugin.dbm.log(PARTIAL_MOVE).from(partial.playerId).to(partial.bankId).capital(partial.capital) 219 | .extra("partialId", partial.transactionId.toString()) 220 | .extra("target", "DEPOSIT").insert(); 221 | } else if (partial.capital + deposit_interest > 0) { // negative interest i.e. money transferred from player to bank 222 | account.deposit += partial.capital + deposit_interest; 223 | plugin.dbm.log(INTEREST_DEPOSIT).from(partial.bankId).to(partial.playerId).capital(deposit_interest) 224 | .extra("partialId", partial.transactionId.toString()).insert(); 225 | plugin.dbm.log(PARTIAL_MOVE).from(partial.playerId).to(partial.bankId).capital(partial.capital + deposit_interest) 226 | .extra("partialId", partial.transactionId.toString()) 227 | .extra("target", "DEPOSIT").insert(); 228 | } else { // bank take all the money 229 | plugin.dbm.log(INTEREST_DEPOSIT).from(partial.bankId).to(partial.playerId).capital(-partial.capital) 230 | .extra("partialId", partial.transactionId.toString()).insert(); 231 | } 232 | break; 233 | } 234 | case LOAN: { // Loan interest 235 | double loan_interest = partial.capital * bank.debitInterest / 100D; 236 | loan_interest *= (designatedTimestamp - partial.startDate.toEpochMilli()) / (double) cycleLength; 237 | loan_interest = Math.round(loan_interest * 1000D) / 1000D; 238 | 239 | if (loan_interest >= 0) { 240 | account.loan += partial.capital; 241 | account.loan_interest += loan_interest; 242 | plugin.dbm.log(INTEREST_LOAN).from(partial.playerId).to(partial.bankId).capital(loan_interest) 243 | .extra("partialId", partial.transactionId.toString()).insert(); 244 | plugin.dbm.log(PARTIAL_MOVE).from(partial.playerId).to(partial.bankId).capital(partial.capital) 245 | .extra("partialId", partial.transactionId.toString()) 246 | .extra("target", "LOAN").insert(); 247 | } else if (partial.capital + loan_interest > 0) { // negative interest i.e. money transferred from bank to player 248 | account.loan += partial.capital + loan_interest; 249 | plugin.dbm.log(INTEREST_LOAN).from(partial.playerId).to(partial.bankId).capital(loan_interest) 250 | .extra("partialId", partial.transactionId.toString()).insert(); 251 | plugin.dbm.log(PARTIAL_MOVE).from(partial.playerId).to(partial.bankId).capital(partial.capital + loan_interest) 252 | .extra("partialId", partial.transactionId.toString()) 253 | .extra("target", "LOAN").insert(); 254 | } else { 255 | plugin.dbm.log(INTEREST_LOAN).from(partial.playerId).to(partial.bankId).capital(-partial.capital) 256 | .extra("partialId", partial.transactionId.toString()).insert(); 257 | // give you the money, nothing need to be done. 258 | } 259 | break; 260 | } 261 | } 262 | 263 | // insert new account to table 264 | if (newAccount) { 265 | if (!accountMap.containsKey(account.bankId)) 266 | accountMap.put(account.bankId, new HashMap<>()); 267 | accountMap.get(account.bankId).put(account.playerId, account); 268 | plugin.dbm.tableBankAccount.insert(account); 269 | } 270 | } 271 | 272 | // update interest := interestNext 273 | for (BankRegistration bank : bankMap.values()) { 274 | if (bank.status == BankStatus.BANKRUPT) continue; // skip bankrupted banks 275 | bank.debitInterest = bank.debitInterestNext; 276 | bank.savingInterest = bank.savingInterestNext; 277 | bank.interestType = bank.interestTypeNext; 278 | } 279 | 280 | // write to database 281 | for (BankRegistration bank : bankMap.values()) { 282 | plugin.dbm.tableBankRegistration.update(bank, WhereClause.EQ(BankRegistration.N_BANK_ID, bank.bankId)); 283 | } 284 | for (Map m : accountMap.values()) { 285 | for (BankAccount account : m.values()) { 286 | plugin.dbm.tableBankAccount.update(account, WhereClause.EQ(BankAccount.N_ACCOUNT_ID, account.accountId)); 287 | } 288 | } 289 | plugin.dbm.tablePartialRecord.delete(WhereClause.EMPTY); 290 | 291 | // Transaction finish 292 | guard.commit(); 293 | } catch (Exception ex) { 294 | ex.printStackTrace(); 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/main/java/cat/nyaa/nyaabank/CommandHandler.java: -------------------------------------------------------------------------------- 1 | package cat.nyaa.nyaabank; 2 | 3 | import cat.nyaa.nyaabank.database.enums.BankStatus; 4 | import cat.nyaa.nyaabank.database.enums.InterestType; 5 | import cat.nyaa.nyaabank.database.enums.TransactionType; 6 | import cat.nyaa.nyaabank.database.tables.BankAccount; 7 | import cat.nyaa.nyaabank.database.tables.BankRegistration; 8 | import cat.nyaa.nyaabank.database.tables.PartialRecord; 9 | import cat.nyaa.nyaabank.signs.SignHelper; 10 | import cat.nyaa.nyaacore.LanguageRepository; 11 | import cat.nyaa.nyaacore.cmdreceiver.Arguments; 12 | import cat.nyaa.nyaacore.cmdreceiver.BadCommandException; 13 | import cat.nyaa.nyaacore.cmdreceiver.CommandReceiver; 14 | import cat.nyaa.nyaacore.cmdreceiver.SubCommand; 15 | import cat.nyaa.nyaacore.orm.RollbackGuard; 16 | import cat.nyaa.nyaacore.orm.WhereClause; 17 | import org.bukkit.ChatColor; 18 | import org.bukkit.OfflinePlayer; 19 | import org.bukkit.command.CommandSender; 20 | import org.bukkit.entity.Player; 21 | import org.bukkit.scheduler.BukkitRunnable; 22 | 23 | import java.time.Instant; 24 | import java.util.*; 25 | 26 | import static cat.nyaa.nyaabank.database.enums.TransactionType.REPAY; 27 | import static cat.nyaa.nyaabank.database.enums.TransactionType.WITHDRAW; 28 | import static cat.nyaa.nyaacore.orm.WhereClause.EMPTY; 29 | import static cat.nyaa.nyaacore.orm.WhereClause.EQ; 30 | 31 | public class CommandHandler extends CommandReceiver { 32 | @Override 33 | public String getHelpPrefix() { 34 | return ""; 35 | } 36 | 37 | private final NyaaBank plugin; 38 | 39 | @SubCommand("bank") 40 | BankManagementCommands bankCommand; 41 | 42 | public CommandHandler(NyaaBank plugin, LanguageRepository i18n) { 43 | super(plugin, i18n); 44 | this.plugin = plugin; 45 | } 46 | 47 | @SubCommand(value = "reg", permission = "nb.create_bank") 48 | public void createBank(CommandSender sender, Arguments args) { 49 | String playerName = args.next(); 50 | String bankName = args.next(); 51 | double capital = args.nextDouble(); 52 | double savingInter = args.nextDouble(); 53 | double debitInter = args.nextDouble(); 54 | InterestType interestType = args.nextEnum(InterestType.class); 55 | 56 | if (playerName == null || bankName == null || capital < 0) { 57 | throw new BadCommandException("manual.reg.usage"); 58 | } 59 | bankName = ChatColor.translateAlternateColorCodes('&', bankName); 60 | List q = plugin.dbm.tableBankRegistration.select(EMPTY); 61 | for (BankRegistration b : q) { 62 | if (SignHelper.stringEqIgnoreColor(bankName, b.name, true)) { 63 | msg(sender, "command.reg.name_duplicate"); 64 | return; 65 | } 66 | } 67 | 68 | OfflinePlayer p = plugin.getServer().getPlayer(playerName); 69 | if (p == null) { 70 | p = plugin.getServer().getOfflinePlayer(playerName); 71 | } 72 | 73 | if (!plugin.eco.has(p, capital)) { 74 | msg(sender, "command.reg.not_enough_capital"); 75 | return; 76 | } 77 | plugin.eco.withdrawPlayer(p, capital); 78 | BankRegistration reg = new BankRegistration(); 79 | reg.bankId = UUID.randomUUID(); 80 | reg.idNumber = plugin.dbm.getNextIdNumber(); // TODO print idNumber EVERYWHERE 81 | reg.name = bankName; 82 | reg.ownerId = p.getUniqueId(); 83 | reg.registered_capital = capital; 84 | reg.establishDate = Instant.now(); 85 | reg.status = BankStatus.ACTIVE; 86 | reg.interestType = interestType; 87 | reg.interestTypeNext = interestType; 88 | reg.savingInterest = savingInter; 89 | reg.savingInterestNext = savingInter; 90 | reg.debitInterest = debitInter; 91 | reg.debitInterestNext = debitInter; 92 | plugin.dbm.tableBankRegistration.insert(reg); 93 | msg(sender, "command.reg.established", reg.idNumber, reg.name, reg.bankId.toString()); 94 | } 95 | 96 | @SubCommand(value = "top", permission = "nb.top") 97 | public void topBanks(CommandSender sender, Arguments args) { 98 | // TODO use actual capital instead of registered_capital 99 | List l = plugin.dbm.tableBankRegistration.select(EMPTY); 100 | l.sort((a, b) -> a.registered_capital.compareTo(b.registered_capital)); 101 | for (int i = l.size(); i >= 1; i--) { 102 | BankRegistration b = l.get(i - 1); 103 | msg(sender, "command.top.list_item", i, b.name, b.registered_capital, b.bankId); 104 | } 105 | } 106 | 107 | @SubCommand(value = "my", permission = "nb.list_my") 108 | public void listMyAccounts(CommandSender sender, Arguments args) { 109 | UUID pid = null; 110 | if (sender.hasPermission("nb.list_my_admin") && args.top() != null) { 111 | pid = plugin.getServer().getOfflinePlayer(args.next()).getUniqueId(); 112 | } 113 | if (pid == null) pid = asPlayer(sender).getUniqueId(); 114 | List accounts = plugin.dbm.tableBankAccount.select(WhereClause.EQ(BankAccount.N_PLAYER_ID, pid)); 115 | List partials = plugin.dbm.tablePartialRecord.select(WhereClause.EQ(PartialRecord.N_PLAYER_ID, pid)); 116 | 117 | Map deposit = new HashMap<>(); 118 | Map loan = new HashMap<>(); 119 | for (BankAccount b : accounts) { 120 | deposit.put(b.bankId, deposit.getOrDefault(b.bankId, 0D) + b.deposit + b.deposit_interest); 121 | loan.put(b.bankId, loan.getOrDefault(b.bankId, 0D) + b.loan + b.loan_interest); 122 | } 123 | for (PartialRecord p : partials) { 124 | if (p.type == TransactionType.DEPOSIT) { 125 | deposit.put(p.bankId, deposit.getOrDefault(p.bankId, 0D) + p.capital); 126 | } else if (p.type == TransactionType.LOAN) { 127 | loan.put(p.bankId, loan.getOrDefault(p.bankId, 0D) + p.capital); 128 | } 129 | } 130 | 131 | Set bankIds = new HashSet<>(); 132 | bankIds.addAll(deposit.keySet()); 133 | bankIds.addAll(loan.keySet()); 134 | for (UUID bankId : bankIds) { 135 | BankRegistration bank = plugin.dbm.getUniqueBank(bankId.toString()); 136 | msg(sender, "command.list_my.list_item", bank.name, 137 | deposit.getOrDefault(bankId, 0D), loan.getOrDefault(bankId, 0D)); 138 | } 139 | if (bankIds.isEmpty()) { 140 | msg(sender, "command.list_my.empty_list"); 141 | } 142 | } 143 | 144 | // FIXME: extra codes required to properly rollback `plugin.eco' changes 145 | @SubCommand(value = "bankrupt", permission = "nb.force_bankrupt") 146 | public void forceBankrupt(CommandSender sender, Arguments args) { 147 | if (args.top() == null) throw new BadCommandException(); 148 | String type = args.next().toLowerCase(); 149 | if (args.top() == null) throw new BadCommandException(); 150 | if ("player".equals(type)) { 151 | String id = args.next(); 152 | OfflinePlayer p = plugin.getServer().getOfflinePlayer(id); 153 | if (p == null || p.getUniqueId() == null) { 154 | throw new BadCommandException("command.bankrupt.player_not_found"); 155 | } 156 | UUID pid = p.getUniqueId(); 157 | try (RollbackGuard guard = plugin.dbm.acquireRollbackGuard()) { 158 | List partials = plugin.dbm.tablePartialRecord.select(EQ(PartialRecord.N_PLAYER_ID, pid)); 159 | List accounts = plugin.dbm.tableBankAccount.select(EQ(BankAccount.N_PLAYER_ID, pid)); 160 | 161 | Map cachedBanks = new HashMap<>(); 162 | for (PartialRecord r : partials) { 163 | BankRegistration bank = cachedBanks.get(r.bankId); 164 | if (bank == null) { 165 | bank = plugin.dbm.getUniqueBank(r.bankId); 166 | cachedBanks.put(r.bankId, bank); 167 | } 168 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 169 | if (r.type == TransactionType.LOAN) { 170 | plugin.eco.withdrawPlayer(p, r.capital); 171 | plugin.eco.depositPlayer(banker, r.capital); 172 | plugin.dbm.log(REPAY).from(pid).to(bank.bankId).capital(r.capital) 173 | .extra("partialId", r.transactionId.toString()) 174 | .extra("bankrupt", "PLAYER").insert(); 175 | } else if (r.type == TransactionType.DEPOSIT) { 176 | plugin.eco.depositPlayer(p, r.capital); 177 | plugin.eco.withdrawPlayer(banker, r.capital); 178 | plugin.dbm.log(WITHDRAW).to(pid).from(bank.bankId).capital(r.capital) 179 | .extra("partialId", r.transactionId.toString()) 180 | .extra("bankrupt", "PLAYER").insert(); 181 | } 182 | } 183 | for (BankAccount r : accounts) { 184 | BankRegistration bank = cachedBanks.get(r.bankId); 185 | if (bank == null) { 186 | bank = plugin.dbm.getUniqueBank(r.bankId); 187 | cachedBanks.put(r.bankId, bank); 188 | } 189 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 190 | double netDeposit = r.deposit + r.deposit_interest - r.loan - r.loan_interest; 191 | if (netDeposit < 0) { // player owe bank 192 | plugin.eco.withdrawPlayer(p, -netDeposit); 193 | plugin.eco.depositPlayer(banker, netDeposit); 194 | plugin.dbm.log(REPAY).from(pid).to(bank.bankId).capital(-netDeposit) 195 | .extra("bankrupt", "PLAYER").insert(); 196 | } else { // bank owe player 197 | plugin.eco.depositPlayer(p, netDeposit); 198 | plugin.eco.withdrawPlayer(banker, netDeposit); 199 | plugin.dbm.log(WITHDRAW).to(pid).from(bank.bankId).capital(netDeposit) 200 | .extra("bankrupt", "PLAYER").insert(); 201 | } 202 | } 203 | plugin.dbm.tablePartialRecord.delete(EQ(PartialRecord.N_PLAYER_ID, pid)); 204 | plugin.dbm.tableBankAccount.delete(EQ(PartialRecord.N_PLAYER_ID, pid)); 205 | guard.commit(); 206 | } catch (Exception ex) { 207 | throw new RuntimeException(ex); 208 | } 209 | 210 | } else if ("bank".equals(type)) { 211 | /* STEP 1: Force acquire all loan 212 | * STEP 2: Return all deposits 213 | * STEP 3: Clear registered_capital and balance with banker 214 | * STEP 4: Update database to BANKRUPT & update signs 215 | */ 216 | try (RollbackGuard guard = plugin.dbm.acquireRollbackGuard()) { 217 | BankRegistration bank = plugin.dbm.getBankByIdNumber(args.nextInt()); 218 | if (bank == null) throw new BadCommandException("command.bankrupt.bank_not_found"); 219 | OfflinePlayer banker = plugin.getServer().getOfflinePlayer(bank.ownerId); 220 | 221 | // STEP 1 & 2 222 | List partials = plugin.dbm.tablePartialRecord.select(EQ(PartialRecord.N_BANK_ID, bank.bankId)); 223 | List accounts = plugin.dbm.tableBankAccount.select(EQ(BankAccount.N_BANK_ID, bank.bankId)); 224 | 225 | for (PartialRecord r : partials) { 226 | if (r.type == TransactionType.LOAN) { 227 | plugin.eco.depositPlayer(banker, r.capital); 228 | plugin.eco.withdrawPlayer(plugin.getServer().getOfflinePlayer(r.playerId), r.capital); 229 | plugin.dbm.log(REPAY).from(r.playerId).to(bank.bankId).capital(r.capital) 230 | .extra("partialId", r.transactionId.toString()) 231 | .extra("bankrupt", "BANK").insert(); 232 | } else if (r.type == TransactionType.DEPOSIT) { 233 | plugin.eco.withdrawPlayer(banker, r.capital); 234 | plugin.eco.depositPlayer(plugin.getServer().getOfflinePlayer(r.playerId), r.capital); 235 | plugin.dbm.log(WITHDRAW).to(r.playerId).from(bank.bankId).capital(r.capital) 236 | .extra("partialId", r.transactionId.toString()) 237 | .extra("bankrupt", "BANK").insert(); 238 | } 239 | } 240 | for (BankAccount r : accounts) { 241 | double netDeposit = r.deposit + r.deposit_interest - r.loan - r.loan_interest; 242 | if (netDeposit < 0) { // player oew bank 243 | plugin.eco.depositPlayer(banker, -netDeposit); 244 | plugin.eco.withdrawPlayer(plugin.getServer().getOfflinePlayer(r.playerId), -netDeposit); 245 | plugin.dbm.log(REPAY).from(r.playerId).to(bank.bankId).capital(-netDeposit) 246 | .extra("bankrupt", "BANK").insert(); 247 | } else { 248 | plugin.eco.withdrawPlayer(banker, netDeposit); 249 | plugin.eco.depositPlayer(plugin.getServer().getOfflinePlayer(r.playerId), netDeposit); 250 | plugin.dbm.log(WITHDRAW).to(r.playerId).from(bank.bankId).capital(netDeposit) 251 | .extra("bankrupt", "BANK").insert(); 252 | } 253 | } 254 | 255 | plugin.dbm.tablePartialRecord.delete(EQ(PartialRecord.N_BANK_ID, bank.bankId)); 256 | plugin.dbm.tableBankAccount.delete(EQ(PartialRecord.N_BANK_ID, bank.bankId)); 257 | 258 | // STEP 3 259 | plugin.eco.depositPlayer(banker, bank.registered_capital); 260 | 261 | // STEP 4 262 | bank.status = BankStatus.BANKRUPT; 263 | plugin.dbm.tableBankRegistration.update(bank, EQ(BankRegistration.N_BANK_ID, bank.bankId)); 264 | SignHelper.batchUpdateSign(plugin, bank); 265 | guard.commit(); 266 | } catch (Exception ex) { 267 | throw new RuntimeException(ex); 268 | } 269 | } else { 270 | throw new BadCommandException(); 271 | } 272 | } 273 | 274 | @SubCommand(value = "deposit", permission = "nb.deposit_cmd") 275 | public void commandDeposit(CommandSender sender, Arguments args) { 276 | Player p = asPlayer(sender); 277 | Double amount = args.nextDouble(); 278 | if (amount <= 0) throw new BadCommandException("user.deposit.invalid_amount"); 279 | if (!plugin.eco.has(p, amount)) throw new BadCommandException("user.deposit.not_enough_money"); 280 | BankRegistration bank = plugin.dbm.getBankByIdNumber(args.nextInt()); 281 | if (bank == null) throw new BadCommandException("user.deposit.bank_not_found"); 282 | 283 | try { 284 | CommonAction.deposit(plugin, p, bank, amount); 285 | } catch (CommonAction.TransactionException ex) { 286 | throw new BadCommandException(ex.getMessage()); 287 | } 288 | } 289 | 290 | @SubCommand(value = "withdraw", permission = "nb.withdraw_cmd") 291 | public void commandWithdraw(CommandSender sender, Arguments args) { 292 | Player p = asPlayer(sender); 293 | String amountS = args.top(); 294 | if (amountS == null) throw new BadCommandException(); 295 | double amount; 296 | boolean withdrawAll; 297 | if ("ALL".equals(amountS.toUpperCase())) { 298 | withdrawAll = true; 299 | amount = -1; 300 | args.next(); 301 | } else { 302 | withdrawAll = false; 303 | amount = args.nextDouble(); 304 | } 305 | BankRegistration bank = plugin.dbm.getBankByIdNumber(args.nextInt()); 306 | if (bank == null) throw new BadCommandException("user.withdraw.bank_not_found"); 307 | 308 | try { 309 | CommonAction.withdraw(plugin, p, bank, amount, withdrawAll); 310 | } catch (CommonAction.TransactionException ex) { 311 | throw new BadCommandException(ex.getMessage()); 312 | } 313 | } 314 | 315 | @SubCommand(value = "loan", permission = "nb.loan_cmd") 316 | public void commandLoan(CommandSender sender, Arguments args) { 317 | Player p = asPlayer(sender); 318 | double amount = args.nextDouble(); 319 | if (amount <= 0) throw new BadCommandException("user.loan.invalid_amount"); 320 | BankRegistration bank = plugin.dbm.getBankByIdNumber(args.nextInt()); 321 | if (bank == null) throw new BadCommandException("user.loan.bank_not_found"); 322 | 323 | try { 324 | CommonAction.loan(plugin, p, bank, amount); 325 | } catch (CommonAction.TransactionException ex) { 326 | throw new BadCommandException(ex.getMessage()); 327 | } 328 | } 329 | 330 | @SubCommand(value = "repay", permission = "nb.repay_cmd") 331 | public void commandRepay(CommandSender sender, Arguments args) { 332 | Player p = asPlayer(sender); 333 | String amountS = args.top(); 334 | if (amountS == null) throw new BadCommandException(); 335 | double amount; 336 | boolean repayAll; 337 | if ("ALL".equals(amountS.toUpperCase())) { 338 | repayAll = true; 339 | amount = -1; 340 | args.next(); 341 | } else { 342 | repayAll = false; 343 | amount = args.nextDouble(); 344 | } 345 | BankRegistration bank = plugin.dbm.getBankByIdNumber(args.nextInt()); 346 | if (bank == null) throw new BadCommandException("user.repay.bank_not_found"); 347 | 348 | try { 349 | CommonAction.repay(plugin, p, bank, amount, repayAll); 350 | } catch (CommonAction.TransactionException ex) { 351 | throw new BadCommandException(ex.getMessage()); 352 | } 353 | } 354 | 355 | @SubCommand(value = "reload", permission = "nb.reload") 356 | public void commandReload(CommandSender sender, Arguments args) { 357 | new BukkitRunnable() { 358 | @Override 359 | public void run() { 360 | plugin.doReload(); 361 | sender.sendMessage(I18n.format("command.reload.complete")); 362 | } 363 | }.runTaskLater(plugin, 1L); 364 | } 365 | 366 | @SubCommand(value = "_check", permission = "nb.debug") // for debug only 367 | public void forceCheckPoint(CommandSender sender, Arguments args) { 368 | sender.sendMessage(String.format("Cycle: %.2f%%", ((double) (plugin.cycle.getNextCheckpoint() - System.currentTimeMillis())) / (double) plugin.cfg.interestCycle * 100D)); 369 | plugin.cycle.updateDatabaseInterests(plugin.cycle.getNextCheckpoint(), plugin.cfg.interestCycle); 370 | SignHelper.batchUpdateSign(plugin, plugin.dbm.tableSignRegistration.select(EMPTY)); 371 | } 372 | 373 | @SubCommand(value = "_benchmark", permission = "nb.debug") // for debug only 374 | public void checkPointDbBenchmark(CommandSender sender, Arguments args) { // WARN: will destroy database 375 | final int NUM_BANK = 10; 376 | final int NUM_ACCOUT = 50; 377 | sender.sendMessage(String.format("#Bank: %d\n#Account per bank: %d", NUM_BANK, NUM_ACCOUT)); 378 | sender.sendMessage("Inserting data ..."); 379 | long startTime = System.currentTimeMillis(); 380 | plugin.dbm.tableBankRegistration.delete(EMPTY); 381 | plugin.dbm.tablePartialRecord.delete(EMPTY); 382 | plugin.dbm.tableBankAccount.delete(EMPTY); 383 | List banks = new ArrayList<>(); 384 | 385 | for (int i = 0; i < NUM_BANK; i++) { 386 | BankRegistration reg = new BankRegistration(); 387 | reg.bankId = UUID.randomUUID(); 388 | reg.name = "Test bank " + Integer.toString(i); 389 | reg.idNumber = (long) i; 390 | reg.ownerId = UUID.randomUUID(); 391 | reg.registered_capital = 10000D; 392 | reg.establishDate = Instant.now(); 393 | reg.status = BankStatus.ACTIVE; 394 | reg.interestType = InterestType.COMPOUND; 395 | reg.interestTypeNext = reg.interestType; 396 | reg.savingInterest = 0.1D; 397 | reg.savingInterestNext = reg.savingInterest; 398 | reg.debitInterest = 0.1D; 399 | reg.debitInterestNext = reg.debitInterest; 400 | plugin.dbm.tableBankRegistration.insert(reg); 401 | banks.add(reg); 402 | } 403 | 404 | for (BankRegistration b : banks) { 405 | for (int i = 0; i < NUM_ACCOUT; i++) { 406 | PartialRecord partial = new PartialRecord(); 407 | partial.transactionId = UUID.randomUUID(); 408 | partial.bankId = b.bankId; 409 | partial.playerId = UUID.randomUUID(); 410 | partial.capital = 1000D; 411 | partial.type = TransactionType.DEPOSIT; 412 | partial.startDate = Instant.now(); 413 | plugin.dbm.tablePartialRecord.insert(partial); 414 | } 415 | } 416 | 417 | long endTime = System.currentTimeMillis(); 418 | sender.sendMessage(String.format("Finished in %.2fs", (endTime - startTime) / 1000D)); 419 | 420 | sender.sendMessage("Round #1 ..."); 421 | startTime = System.currentTimeMillis(); 422 | plugin.cycle.updateDatabaseInterests(System.currentTimeMillis(), plugin.cfg.interestCycle); 423 | endTime = System.currentTimeMillis(); 424 | sender.sendMessage(String.format("Finished in %.2fs", (endTime - startTime) / 1000D)); 425 | 426 | sender.sendMessage("Round #2 ..."); 427 | startTime = System.currentTimeMillis(); 428 | plugin.cycle.updateDatabaseInterests(System.currentTimeMillis(), plugin.cfg.interestCycle); 429 | endTime = System.currentTimeMillis(); 430 | sender.sendMessage(String.format("Finished in %.2fs", (endTime - startTime) / 1000D)); 431 | 432 | plugin.dbm.tableBankRegistration.delete(EMPTY); 433 | plugin.dbm.tablePartialRecord.delete(EMPTY); 434 | plugin.dbm.tableBankAccount.delete(EMPTY); 435 | } 436 | } 437 | --------------------------------------------------------------------------------