mirror of
https://github.com/SimplexDevelopment/FeelingLucky.git
synced 2025-04-03 15:53:15 +00:00
Compare commits
37 Commits
Beta-20220
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
0cccf50a85 | ||
|
cd2c90dc7a | ||
|
74cbcbcdb0 | ||
|
447e1ba391 | ||
|
c4e29ff728 | ||
|
32a9435e30 | ||
|
d282c2e982 | ||
|
0ad60075da | ||
|
661b7bd6a7 | ||
|
639bf09e48 | ||
|
8b3486a269 | ||
|
94b6067f97 | ||
|
0b07bd9da2 | ||
|
400687733f | ||
|
c7a168ede1 | ||
|
fda004a3c8 | ||
|
0c82515f43 | ||
|
c3d781f5b6 | ||
|
e6fe9e904e | ||
|
c383b2c546 | ||
|
94b9e12f45 | ||
|
eb80523edc | ||
|
355b612732 | ||
|
67734f3f89 | ||
|
4232842749 | ||
|
010fd76031 | ||
|
c50b222586 | ||
|
17f83bd9f2 | ||
|
10d7a4ed98 | ||
|
73e5be91eb | ||
|
07c4e5d50c | ||
|
e13ca55adf | ||
|
fbd8d10461 | ||
|
a4d71b2c0b | ||
|
bb9bdcdf03 | ||
|
1bf2a818ec | ||
|
0488e1d6b1 |
32
.github/workflows/codacy-analysis.yml
vendored
Normal file
32
.github/workflows/codacy-analysis.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Codacy Security Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "main" ]
|
||||
pull_request:
|
||||
branches: [ "master", "main" ]
|
||||
|
||||
jobs:
|
||||
codacy-security-scan:
|
||||
name: Codacy Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@main
|
||||
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@master
|
||||
with:
|
||||
output: results.sarif
|
||||
format: sarif
|
||||
# Adjust severity of non-security issues
|
||||
gh-code-scanning-compat: true
|
||||
# Force 0 exit code to allow SARIF file generation
|
||||
# This will handover control about PR rejection to the GitHub side
|
||||
max-allowed-issues: 2147483647
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@main
|
||||
with:
|
||||
sarif_file: results.sarif
|
77
.github/workflows/codeql-analysis.yml
vendored
Normal file
77
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '25 16 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'java' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
- name: Codacy Coverage Reporter
|
||||
uses: codacy/codacy-coverage-reporter-action@v1.3.0
|
||||
|
||||
|
29
README.md
Normal file
29
README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# <center>FeelingLucky v1.3.0 - A luck driven mechanics plugin.</center>
|
||||
|
||||
## <center><u><span style="color:blue">Plugin Description:</u></center>
|
||||
|
||||
### <center><u><span style="color:cyan">For All Users:</u></center>
|
||||
|
||||
<b>FeelingLucky</b> is a mechanics plugin designed to expand upon the Luck attribute which Minecraft provides.
|
||||
Each user is assigned a tangible Luck stat, which can be viewed by using <b><span style="color:violet">/luck info</color></b>.
|
||||
Users can <i>increase</i> their luck stat by using a rabbit's foot,
|
||||
or increase their luck and their luck multiplier by consuming a special rabbits foot purchased from a Butcher villager.
|
||||
<b><span style="color:red">Beware</b> though, if you take damage from guardian lasers or a witch's potion,
|
||||
there's a chance your luck will <i>decrease</i> instead.
|
||||
|
||||
### <center><u><span style="color:pink">For Administrators:</u></center>
|
||||
Admins can modify values in the configuration file, as well as modify individual user's luck stat.
|
||||
Admins can set, reset, add to, and take from player's luck stat.
|
||||
Admins can also reload the main configuration, as well as individual and all player configurations.
|
||||
For this, the command is <b><span style="color:violet">/luck reload -m</color></b> for the main config,
|
||||
<b><span style="color:violet">/luck reload</color></b> to reload all player configurations, and <b><span style="color:violet">/luck reload -p <i>PLAYER_NAME</i></span></b> to reload individual player configuration files.
|
||||
Server owners and/or individuals with console access can run /rgc to regenerate the main configuration file in the case that there are values missing, corrupted, or invalid.
|
||||
|
||||
## <center><u><span style="color:blue">Server Requirements:</u></center>
|
||||
|
||||
In order to run <b>FeelingLucky</b> v<b>1.1.0</b>, the latest version of Paper or Spigot is required.
|
||||
|
||||
#### <center><span style="color:red">Note: Paper is REQUIRED for this plugin to run. Spigot is not supported, however Spigot support is currently in progress.</center></span>
|
||||
|
||||
### <center>Note: If you are migrating from an Alpha build, the plugin configuration folder will need to be regenerated.</center>
|
||||
|
15
build.gradle
15
build.gradle
@ -3,22 +3,17 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'io.github.simplex'
|
||||
version = 'Alpha-1.0'
|
||||
version = '1.3.0'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = 'papermc-repo'
|
||||
url = 'https://papermc.io/repo/repository/maven-public/'
|
||||
}
|
||||
maven {
|
||||
name = 'sonatype'
|
||||
url = 'https://s01.oss.sonatype.org/content/groups/public/'
|
||||
}
|
||||
maven { url = uri("https://s01.oss.sonatype.org/content/groups/public/") }
|
||||
maven { url = uri("https://papermc.io/repo/repository/maven-public/")}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT'
|
||||
compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
|
||||
implementation 'redis.clients:jedis:3.7.0'
|
||||
}
|
||||
|
||||
def targetJavaVersion = 17
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
@ -1,23 +1,68 @@
|
||||
package io.github.simplex.api;
|
||||
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import java.io.Serializable;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The LuckContainer interface represents a container for player luck.
|
||||
* It provides methods to get and set the verbosity of the luck container,
|
||||
* check if a number matches or is close to the luck value, get the multiplier,
|
||||
* get the associated player, and get the luck value.
|
||||
*
|
||||
* This interface is Serializable, which means it can be written to a stream
|
||||
* and restored.
|
||||
*/
|
||||
public interface LuckContainer extends Serializable {
|
||||
|
||||
Attribute asAttribute();
|
||||
/**
|
||||
* Checks if the luck container is verbose.
|
||||
*
|
||||
* @return true if the luck container is verbose, false otherwise.
|
||||
*/
|
||||
boolean isVerbose();
|
||||
|
||||
double getNumber();
|
||||
/**
|
||||
* Sets the verbosity of the luck container.
|
||||
*
|
||||
* @param verbose the verbosity to set.
|
||||
*/
|
||||
void setVerbose(boolean verbose);
|
||||
|
||||
/**
|
||||
* Checks if a number matches the luck value.
|
||||
*
|
||||
* @param number the number to check.
|
||||
* @return true if the number matches the luck value, false otherwise.
|
||||
*/
|
||||
boolean isMatch(double number);
|
||||
|
||||
/**
|
||||
* Checks if a number is close to the luck value within a certain range.
|
||||
*
|
||||
* @param number the number to check.
|
||||
* @param range the range within which the number is considered close.
|
||||
* @return true if the number is close to the luck value, false otherwise.
|
||||
*/
|
||||
boolean isClose(double number, int range);
|
||||
|
||||
/**
|
||||
* Gets the multiplier of the luck container.
|
||||
*
|
||||
* @return the multiplier.
|
||||
*/
|
||||
double multiplier();
|
||||
|
||||
/**
|
||||
* Gets the player associated with the luck container.
|
||||
*
|
||||
* @return the associated player.
|
||||
*/
|
||||
Player associatedPlayer();
|
||||
|
||||
/**
|
||||
* Gets the luck value of the luck container.
|
||||
*
|
||||
* @return the luck value.
|
||||
*/
|
||||
double getValue();
|
||||
}
|
||||
}
|
@ -1,21 +1,25 @@
|
||||
package io.github.simplex.lib;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
|
||||
public enum Messages {
|
||||
public enum Messages
|
||||
{
|
||||
|
||||
NOT_FROM_CONSOLE(MiniComponent.err("This command may only be used in game.")),
|
||||
NO_PERMISSION(MiniComponent.err("You do not have permission to use this command.")),
|
||||
NO_PLAYER(MiniComponent.warn("That player cannot be found.")),
|
||||
OUT_OF_BOUNDS(MiniComponent.err("Number must be between -1024.0 and 1024.0"));
|
||||
OUT_OF_BOUNDS(MiniComponent.err("Number must be between -1024.0 and 1024.0")),
|
||||
VERBOSE_DISABLED(MiniComponent.err("Verbose mode is currently disabled globally."));
|
||||
|
||||
private final Component message;
|
||||
private final ComponentLike message;
|
||||
|
||||
Messages(Component message) {
|
||||
Messages(ComponentLike message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Component get() {
|
||||
public ComponentLike get()
|
||||
{
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package io.github.simplex.lib;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MiniComponent {
|
||||
private final String content;
|
||||
@ -21,17 +23,17 @@ public class MiniComponent {
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static Component info(String content) {
|
||||
public static ComponentLike info(String content) {
|
||||
return new MiniComponent(content).color(ChatColor.GREEN).send();
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static Component warn(String content) {
|
||||
public static ComponentLike warn(String content) {
|
||||
return new MiniComponent(content).color(ChatColor.YELLOW).decorate(TextDecoration.ITALIC).send();
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static Component err(String content) {
|
||||
public static ComponentLike err(String content) {
|
||||
return new MiniComponent(content).color(ChatColor.RED).decorate(TextDecoration.BOLD).send();
|
||||
}
|
||||
|
||||
@ -45,7 +47,7 @@ public class MiniComponent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Component send() {
|
||||
public @Nullable Component send() {
|
||||
if (color == null) {
|
||||
if (decoration == null) return Component.empty().content(content);
|
||||
|
||||
|
@ -1,91 +1,217 @@
|
||||
package io.github.simplex.luck;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.listener.AbstractListener;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import io.github.simplex.luck.util.SneakyWorker;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Config extends YamlConfiguration {
|
||||
private final FeelingLucky plugin;
|
||||
private File configFile;
|
||||
import io.github.simplex.sql.SQLType;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public Config(FeelingLucky plugin) {
|
||||
this.plugin = plugin;
|
||||
public class Config extends YamlConfiguration
|
||||
{
|
||||
private final Map<String, Object> configEntries = new HashMap<>()
|
||||
{{
|
||||
put("high_rarity_chance", 512.0);
|
||||
put("medium_rarity_chance", 128.0);
|
||||
put("low_rarity_chance", 64.0);
|
||||
put("global_verbosity", true);
|
||||
put("block_drops", "LOW");
|
||||
put("bonemeal", "MED");
|
||||
put("cheat_death", "MED");
|
||||
put("enchanting", "HIGH");
|
||||
put("experience", "HIGH");
|
||||
put("give_damage", "LOW");
|
||||
put("hide_check", "MED");
|
||||
put("item_drops", "LOW");
|
||||
put("jump_boost", "MED");
|
||||
put("ore_vein", "HIGH");
|
||||
put("random_effect", "HIGH");
|
||||
put("restore_hunger", "NONE");
|
||||
put("take_damage", "MED");
|
||||
put("unbreakable", "HIGH");
|
||||
}};
|
||||
private final File configFile;
|
||||
|
||||
public Config(FeelingLucky plugin)
|
||||
{
|
||||
File dataFolder = plugin.getDataFolder();
|
||||
if (!dataFolder.exists()) dataFolder.mkdirs();
|
||||
if (dataFolder.mkdirs())
|
||||
{
|
||||
plugin.getLogger().info("Created new data folder. Writing new configuration file...");
|
||||
plugin.saveResource("config.yml", true);
|
||||
}
|
||||
|
||||
File configFile = new File(dataFolder, "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
SneakyWorker.sneakyTry(configFile::createNewFile);
|
||||
if (!configFile.exists())
|
||||
{
|
||||
plugin.getLogger().info("No configuration file exists. Creating a new one...");
|
||||
plugin.saveResource("config.yml", true);
|
||||
}
|
||||
|
||||
this.configFile = configFile;
|
||||
load();
|
||||
|
||||
if (validateIntegrity()) {
|
||||
File newFile = new File(plugin.getDataFolder(), "config.yml");
|
||||
SneakyWorker.sneakyTry(() -> {
|
||||
Files.delete(Path.of(plugin.getDataFolder().getPath()));
|
||||
newFile.createNewFile();
|
||||
plugin.saveResource("config.yml", true);
|
||||
});
|
||||
this.configFile = newFile;
|
||||
if (validateIntegrity(this.configFile))
|
||||
{
|
||||
load();
|
||||
}
|
||||
else
|
||||
{
|
||||
configEntries.forEach(super::set);
|
||||
Logs.warn("Your configuration file is missing keys. " +
|
||||
"\nPlease use /rgc in the console to regenerate the config file. " +
|
||||
"\nAlternatively, delete the config.yml and restart your server. " +
|
||||
"\nIt is safe to ignore this, as default values will be used." +
|
||||
"\nHowever, it is highly recommended to regenerate the configuration.");
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
public void save()
|
||||
{
|
||||
SneakyWorker.sneakyTry(() -> save(configFile));
|
||||
}
|
||||
|
||||
public void load() {
|
||||
public void load()
|
||||
{
|
||||
SneakyWorker.sneakyTry(() -> load(configFile));
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
public void reload()
|
||||
{
|
||||
save();
|
||||
load();
|
||||
}
|
||||
|
||||
public boolean validateIntegrity() {
|
||||
for (String key : getKeys(false)) {
|
||||
if (!configEntries.contains(key)) {
|
||||
plugin.getLogger().severe("The contents of your configuration file is corrupted! Regenerating a new configuration file...");
|
||||
return true;
|
||||
public boolean validateIntegrity(@NotNull File fromDisk)
|
||||
{
|
||||
YamlConfiguration disk = YamlConfiguration.loadConfiguration(fromDisk);
|
||||
if (disk.getKeys(true).isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = true;
|
||||
|
||||
for (String key : configEntries.keySet())
|
||||
{
|
||||
if (!disk.getKeys(false).contains(key))
|
||||
{
|
||||
if (result)
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
public AbstractListener.Rarity getRarity(String name) {
|
||||
public AbstractListener.Rarity getRarity(String name)
|
||||
{
|
||||
return AbstractListener.Rarity.valueOf(getString(name));
|
||||
}
|
||||
|
||||
public double getChance(String path) {
|
||||
public double getChance(String path)
|
||||
{
|
||||
return getDouble(path);
|
||||
}
|
||||
|
||||
private List<String> configEntries = new ArrayList<>() {{
|
||||
add("high_rarity_chance");
|
||||
add("medium_rarity_chance");
|
||||
add("low_rarity_chance");
|
||||
add("block_drops");
|
||||
add("bonemeal");
|
||||
add("cheat_death");
|
||||
add("enchanting");
|
||||
add("experience");
|
||||
add("item_drops");
|
||||
add("random_effect");
|
||||
add("restore_hunger");
|
||||
add("take_damage");
|
||||
add("unbreakable");
|
||||
}};
|
||||
public boolean isVerboseGlobal() {
|
||||
return getBoolean("global_verbosity");
|
||||
}
|
||||
|
||||
public SQLType getSQLType() {
|
||||
return SQLType.fromString(Objects.requireNonNull(getString("database_type")));
|
||||
}
|
||||
|
||||
public SQLiteWrapper getSQLite() {
|
||||
return new SQLiteWrapper();
|
||||
}
|
||||
|
||||
public RedisWrapper getRedis() {
|
||||
return new RedisWrapper();
|
||||
}
|
||||
|
||||
public MySQLWrapper getMySQL() {
|
||||
return new MySQLWrapper();
|
||||
}
|
||||
|
||||
public final class SQLiteWrapper {
|
||||
private final String path;
|
||||
|
||||
public SQLiteWrapper() {
|
||||
this.path = getString("sqlite.path");
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public final class RedisWrapper {
|
||||
private final String host;
|
||||
private final String port;
|
||||
private final String password;
|
||||
private final int database;
|
||||
|
||||
public RedisWrapper() {
|
||||
this.host = getString("redis.host");
|
||||
this.port = getString("redis.port");
|
||||
this.password = getString("redis.password");
|
||||
this.database = getInt("redis.database");
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public int getDatabase() {
|
||||
return database;
|
||||
}
|
||||
}
|
||||
|
||||
public final class MySQLWrapper {
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final String database;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public MySQLWrapper() {
|
||||
this.host = getString("mysql.host");
|
||||
this.port = getInt("mysql.port");
|
||||
this.database = getString("mysql.database");
|
||||
this.username = getString("mysql.username");
|
||||
this.password = getString("mysql.password");
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,39 +3,65 @@ package io.github.simplex.luck;
|
||||
import io.github.simplex.luck.listener.*;
|
||||
import io.github.simplex.luck.player.PlayerConfig;
|
||||
import io.github.simplex.luck.player.PlayerHandler;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import io.github.simplex.luck.util.LuckCMD;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import io.github.simplex.luck.util.RegenerateConfigCMD;
|
||||
import io.github.simplex.luck.util.SpecialFootItem;
|
||||
import io.github.simplex.metrics.Metrics;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.github.simplex.sql.MySQL;
|
||||
import io.github.simplex.sql.Redis;
|
||||
import io.github.simplex.sql.SQLType;
|
||||
import io.github.simplex.sql.SQLite;
|
||||
import net.kyori.adventure.chat.ChatType;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class FeelingLucky extends JavaPlugin {
|
||||
private final Map<UUID, PlayerConfig> configMap = new HashMap<>();
|
||||
|
||||
private final File playerDirectory = new File(getDataFolder(), "players");
|
||||
private final SpecialFootItem specialFootItem = new SpecialFootItem();
|
||||
private final ChatType.Bound bind = ChatType.CHAT.bind(Component.text(getName()));
|
||||
private PlayerHandler handler;
|
||||
private Config config;
|
||||
|
||||
private MySQL mysql;
|
||||
private SQLite sqlite;
|
||||
private Redis Redis;
|
||||
|
||||
private boolean shouldLoadPhysical = false;
|
||||
|
||||
public Map<UUID, PlayerConfig> getConfigMap() {
|
||||
return configMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getLogger().info("Initializing the PlayerHandler...");
|
||||
handler = new PlayerHandler(this);
|
||||
getLogger().info("Initializing metrics...");
|
||||
new Metrics(this, 15054);
|
||||
getLogger().info("Metrics loaded. Initializing SQL...");
|
||||
initSQL();
|
||||
getLogger().info("Initialization complete! Attempting to register the Listeners...");
|
||||
registerListeners();
|
||||
getLogger().info("Registration complete! Attempting to load all player configuration files...");
|
||||
getLogger().info("Registration complete! Attempting to load all saved player configurations...");
|
||||
loadPlayerConfigurations();
|
||||
getLogger().info("Player configurations loaded! Initializing PlayerHandler...");
|
||||
handler = new PlayerHandler(this);
|
||||
getLogger().info("Attempting to load the main configuration...");
|
||||
config = new Config(this);
|
||||
getLogger().info("Main Config loaded successfully! Attempting to load the Luck command...");
|
||||
getLogger().info("Main Config loaded successfully! Loading commands...");
|
||||
new LuckCMD(this);
|
||||
getLogger().info("Successfully loaded the Luck command!");
|
||||
new RegenerateConfigCMD(this);
|
||||
getLogger().info("Successfully loaded all commands!");
|
||||
|
||||
getLogger().info("Successfully initialized!");
|
||||
}
|
||||
@ -49,12 +75,89 @@ public final class FeelingLucky extends JavaPlugin {
|
||||
getLogger().info("Complete! Goodbye! :)");
|
||||
}
|
||||
|
||||
private void initSQL() {
|
||||
switch (config.getSQLType()) {
|
||||
case MYSQL -> {
|
||||
try {
|
||||
mysql = new MySQL(this);
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Failed to initialize MySQL. Falling back to standard plugin configuration.");
|
||||
Logs.error(e);
|
||||
shouldLoadPhysical(true);
|
||||
}
|
||||
}
|
||||
case SQLITE -> {
|
||||
try {
|
||||
sqlite = new SQLite(this);
|
||||
} catch (SQLException e) {
|
||||
getLogger().severe("Failed to initialize SQLite. Falling back to standard plugin configuration.");
|
||||
Logs.error(e);
|
||||
shouldLoadPhysical(true);
|
||||
}
|
||||
}
|
||||
case REDIS -> {
|
||||
Redis = new Redis(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveToSQL() {
|
||||
switch (config.getSQLType()) {
|
||||
case MYSQL -> {
|
||||
mysql.savePlayers();
|
||||
}
|
||||
case SQLITE -> {
|
||||
try {
|
||||
sqlite.savePlayers();
|
||||
} catch (Exception e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
case REDIS -> {
|
||||
Redis = new Redis(this);
|
||||
Redis.savePlayers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPlayersFromSQL() {
|
||||
switch (config.getSQLType()) {
|
||||
case MYSQL -> {
|
||||
mysql.loadPlayers();
|
||||
}
|
||||
case SQLITE -> {
|
||||
try {
|
||||
sqlite.loadPlayers();
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
case REDIS -> {
|
||||
Redis.loadPlayers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadPlayerConfigurations() {
|
||||
File[] files = getDataFolder().listFiles();
|
||||
if (!playerDirectory.exists()) {
|
||||
getLogger().info("No directory exists. Creating...");
|
||||
playerDirectory.mkdirs();
|
||||
getLogger().info("Created new directory \"FeelingLucky/players\".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.getSQLType() != SQLType.NONE && !shouldLoadPhysical()) {
|
||||
loadPlayersFromSQL();
|
||||
getLogger().info("Successfully loaded all configurations from SQL!");
|
||||
return;
|
||||
}
|
||||
|
||||
File[] files = playerDirectory.listFiles();
|
||||
if (files != null) {
|
||||
Arrays.stream(files).forEach(file -> {
|
||||
Arrays.stream(files).forEach(file ->
|
||||
{
|
||||
UUID uuid = UUID.fromString(file.getName().split("\\.")[0]);
|
||||
configMap.put(uuid, PlayerConfig.loadFrom(this, file));
|
||||
configMap.put(uuid, PlayerConfig.initFromFile(this, file));
|
||||
});
|
||||
configMap.forEach((u, pc) -> pc.load());
|
||||
getLogger().info("Successfully loaded all configurations!");
|
||||
@ -69,9 +172,14 @@ public final class FeelingLucky extends JavaPlugin {
|
||||
new CheatDeath(this);
|
||||
new EnchantmentBoost(this);
|
||||
new ExpBoost(this);
|
||||
new GiveDamage(this);
|
||||
new HideCheck(this);
|
||||
new IllOmen(this);
|
||||
new ItemDrops(this);
|
||||
new JumpBoost(this);
|
||||
// new OreVein(this); (Currently unstable & unsafe).
|
||||
new PlayerListener(this);
|
||||
new RandomEffect(this);
|
||||
new RestoreHunger(this);
|
||||
new TakeDamage(this);
|
||||
new UnbreakableTool(this);
|
||||
@ -81,10 +189,30 @@ public final class FeelingLucky extends JavaPlugin {
|
||||
public PlayerHandler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public SpecialFootItem getFoot() {
|
||||
return specialFootItem;
|
||||
}
|
||||
|
||||
private void shouldLoadPhysical(boolean state) {
|
||||
shouldLoadPhysical = state;
|
||||
}
|
||||
|
||||
private boolean shouldLoadPhysical() {
|
||||
return shouldLoadPhysical;
|
||||
}
|
||||
|
||||
public CommandMap getCommandMap() {
|
||||
return getServer().getCommandMap();
|
||||
}
|
||||
|
||||
public ChatType.Bound bind() {
|
||||
return bind;
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,35 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.luck.Config;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.PlayerHandler;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractListener implements Listener {
|
||||
protected final FeelingLucky plugin;
|
||||
protected final Map<AbstractListener, Rarity> listenerRarityMap = new HashMap<>();
|
||||
protected final Config config;
|
||||
|
||||
public AbstractListener(FeelingLucky plugin) {
|
||||
this.plugin = plugin;
|
||||
this.config = plugin.getConfig();
|
||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
protected PlayerHandler getHandler() {
|
||||
return plugin.getHandler();
|
||||
}
|
||||
|
||||
public void register(AbstractListener listener) {
|
||||
plugin.getServer().getPluginManager().registerEvents(listener, plugin);
|
||||
}
|
||||
|
||||
public boolean doesQualify(String name, double luck) {
|
||||
return switch (plugin.getConfig().getRarity(name)) {
|
||||
case HIGH -> luck > plugin.getConfig().getChance("high_rarity_chance");
|
||||
case MED -> luck > plugin.getConfig().getChance("medium_rarity_chance");
|
||||
case LOW -> luck > plugin.getConfig().getChance("low_rarity_chance");
|
||||
case NONE -> true;
|
||||
};
|
||||
}
|
||||
|
||||
public enum Rarity {
|
||||
HIGH,
|
||||
MED,
|
||||
@ -30,17 +37,7 @@ public abstract class AbstractListener implements Listener {
|
||||
NONE
|
||||
}
|
||||
|
||||
public boolean doesQualify(String name, double luck) {
|
||||
switch (config.getRarity(name)) {
|
||||
case HIGH:
|
||||
if (luck < config.getChance("high_rarity_chance")) return false;
|
||||
case MED:
|
||||
if (luck < config.getChance("medium_rarity_chance")) return false;
|
||||
case LOW:
|
||||
if (luck < config.getChance("low_rarity_chance")) return false;
|
||||
case NONE:
|
||||
return true;
|
||||
}
|
||||
throw new IllegalArgumentException("The value for the listener rarity is not a recognized rarity value.");
|
||||
public Audience asAudience(final Player player) {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,35 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import io.github.simplex.luck.util.SneakyWorker;
|
||||
import java.util.List;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.BlockDropItemEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class BlockDrops extends AbstractListener {
|
||||
public BlockDrops(FeelingLucky plugin) {
|
||||
public final class BlockDrops extends AbstractListener
|
||||
{
|
||||
public BlockDrops(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void extraBlockDrops(BlockDropItemEvent event) {
|
||||
public void extraBlockDrops(BlockDropItemEvent event)
|
||||
{
|
||||
Player player = event.getPlayer();
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
List<Item> items = event.getItems();
|
||||
if (luck.quickRNG(luck.getPercentage()) && doesQualify("block_drops", luck.getPercentage())) {
|
||||
items.forEach(SneakyWorker::move);
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("block_drops", luck.getValue()))
|
||||
{
|
||||
event.getItems().addAll(items.stream().map(SneakyWorker::move).toList());
|
||||
}
|
||||
|
||||
if (luck.isVerbose())
|
||||
asAudience(player).sendMessage(MiniComponent.info("You got lucky and received extra drops!"));
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import io.github.simplex.lib.ItemBuilder;
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.Ageable;
|
||||
@ -17,11 +18,13 @@ import org.bukkit.inventory.ItemStack;
|
||||
public final class BonemealFullCrop extends AbstractListener {
|
||||
public BonemealFullCrop(FeelingLucky plugin) {
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void bonemealFullCrop(PlayerInteractEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Audience pAud = player;
|
||||
Action action = event.getAction();
|
||||
ItemStack bonemeal = ItemBuilder.of(Material.BONE_MEAL).build();
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
@ -37,12 +40,17 @@ public final class BonemealFullCrop extends AbstractListener {
|
||||
if (action.isRightClick()
|
||||
&& handItem.isSimilar(bonemeal)
|
||||
&& (data instanceof Ageable crop)
|
||||
&& luck.quickRNG(luck.getPercentage())
|
||||
&& doesQualify("bonemeal", luck.getPercentage())) {
|
||||
&& luck.quickRNG(luck.getValue())
|
||||
&& doesQualify("bonemeal", luck.getValue())) {
|
||||
crop.setAge(crop.getMaximumAge());
|
||||
data.merge(crop);
|
||||
block.setBlockData(data);
|
||||
player.sendMessage(MiniComponent.info("You got lucky and your crops grew to maturity."));
|
||||
|
||||
if (luck.isVerbose()) {
|
||||
asAudience(player).sendMessage(MiniComponent.info("Your luck has caused your crop to become ready for" +
|
||||
" " +
|
||||
"harvest!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,25 +3,35 @@ package io.github.simplex.luck.listener;
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
|
||||
public final class CheatDeath extends AbstractListener {
|
||||
public CheatDeath(FeelingLucky plugin) {
|
||||
public final class CheatDeath extends AbstractListener
|
||||
{
|
||||
public CheatDeath(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void cheatDeath(PlayerDeathEvent event) {
|
||||
public void cheatDeath(PlayerDeathEvent event)
|
||||
{
|
||||
Player player = event.getPlayer();
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
double absorption = Math.round(Luck.RNG().nextDouble(5.0, 10.0));
|
||||
if (luck.quickRNG(luck.getPercentage()) && doesQualify("cheat_death", luck.getPercentage())) {
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("cheat_death", luck.getValue()))
|
||||
{
|
||||
event.setCancelled(true);
|
||||
player.setHealth(1.0);
|
||||
player.setAbsorptionAmount(absorption);
|
||||
player.sendMessage(MiniComponent.of("You got lucky and cheated death!").send());
|
||||
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(MiniComponent.info("You got lucky and cheated death!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,52 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.enchantment.EnchantItemEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class EnchantmentBoost extends AbstractListener {
|
||||
public EnchantmentBoost(FeelingLucky plugin) {
|
||||
public final class EnchantmentBoost extends AbstractListener
|
||||
{
|
||||
public EnchantmentBoost(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void enchantItem(EnchantItemEvent event) {
|
||||
public void enchantItem(EnchantItemEvent event)
|
||||
{
|
||||
Map<Enchantment, Integer> enchMap = event.getEnchantsToAdd();
|
||||
List<Enchantment> enchList = enchMap.keySet().stream().toList();
|
||||
Player player = event.getEnchanter();
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getPercentage()) && doesQualify("enchanting", luck.getPercentage())) {
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("enchanting", luck.getValue()))
|
||||
{
|
||||
Enchantment particular = enchList.get(Luck.RNG().nextInt(enchList.size()));
|
||||
int rng = Luck.RNG().nextInt(1, 5);
|
||||
|
||||
if ((enchMap.get(particular) + rng) > particular.getMaxLevel()) {
|
||||
if ((enchMap.get(particular) + rng) > particular.getMaxLevel())
|
||||
{
|
||||
enchMap.replace(particular, particular.getMaxLevel());
|
||||
}
|
||||
|
||||
enchMap.replace(particular, enchMap.get(particular) + rng);
|
||||
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(
|
||||
MiniComponent.info("Your luck has given you an extra random enchantment."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FeelingLucky plugin() {
|
||||
public FeelingLucky plugin()
|
||||
{
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,38 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent;
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import org.bukkit.entity.ExperienceOrb;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
||||
public final class ExpBoost extends AbstractListener {
|
||||
public ExpBoost(FeelingLucky plugin) {
|
||||
public final class ExpBoost extends AbstractListener
|
||||
{
|
||||
public ExpBoost(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void boostExperienceGain(PlayerPickupExperienceEvent event) {
|
||||
public void boostExperienceGain(PlayerPickupExperienceEvent event)
|
||||
{
|
||||
ExperienceOrb orb = event.getExperienceOrb();
|
||||
int n = orb.getExperience();
|
||||
int math = (5 * n ^ 2) / (2 * n + 4);
|
||||
int rounded = Math.round(math);
|
||||
Player player = event.getPlayer();
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getPercentage()) && doesQualify("experience", luck.getPercentage())) {
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("experience", luck.getValue()))
|
||||
{
|
||||
orb.setExperience(rounded);
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(
|
||||
MiniComponent.info("Your luck has given you extra experience!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
|
||||
public class GiveDamage extends AbstractListener
|
||||
{
|
||||
public GiveDamage(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void playerAttack(EntityDamageByEntityEvent e)
|
||||
{
|
||||
if ((e.getDamager() instanceof Player player)
|
||||
&& (e.getEntity() instanceof LivingEntity))
|
||||
{
|
||||
double nextDmg = e.getDamage() + Luck.RNG().nextDouble(1.0, 5.0);
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("give_damage", luck.getValue()))
|
||||
{
|
||||
e.setDamage(nextDmg);
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(
|
||||
MiniComponent.info("Your luck gave you a critical hit!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
src/main/java/io/github/simplex/luck/listener/HideCheck.java
Normal file
49
src/main/java/io/github/simplex/luck/listener/HideCheck.java
Normal file
@ -0,0 +1,49 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Monster;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||
|
||||
public class HideCheck extends AbstractListener
|
||||
{
|
||||
public HideCheck(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void checkForSneak(PlayerToggleSneakEvent event)
|
||||
{
|
||||
Player player = event.getPlayer();
|
||||
if (player.isSneaking())
|
||||
return;
|
||||
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("hide_check", luck.getValue()))
|
||||
{
|
||||
player.getNearbyEntities(25, 25, 25)
|
||||
.stream()
|
||||
.filter(e -> e instanceof Monster)
|
||||
.map(e -> (Monster) e)
|
||||
.forEach(m ->
|
||||
{
|
||||
final LivingEntity target = m.getTarget();
|
||||
if (target != null && target.getUniqueId().equals(player.getUniqueId()))
|
||||
{
|
||||
m.setTarget(null);
|
||||
}
|
||||
});
|
||||
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(MiniComponent.info("Your luck has hidden you from sight."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,69 +11,92 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
public class IllOmen extends AbstractListener {
|
||||
public IllOmen(FeelingLucky plugin) {
|
||||
public class IllOmen extends AbstractListener
|
||||
{
|
||||
public IllOmen(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void reconnectCheck(PlayerJoinEvent event) {
|
||||
public void reconnectCheck(PlayerJoinEvent event)
|
||||
{
|
||||
Player player = event.getPlayer();
|
||||
PotionEffectType type = PotionEffectType.BAD_OMEN;
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
|
||||
if (player.hasPotionEffect(type)) {
|
||||
if (player.hasPotionEffect(type))
|
||||
{
|
||||
luck.cache();
|
||||
double maths = luck.getValue() - (luck.getValue() * 0.25);
|
||||
luck.setValue(maths);
|
||||
player.sendMessage(MiniComponent.info("A -25% debuff has been applied to your luck from the Bad Omen status effect."));
|
||||
} else if (luck.cached(player) && !player.hasPotionEffect(type)) {
|
||||
asAudience(player).sendMessage(MiniComponent.info("A -25% debuff has been applied to your luck from the " +
|
||||
"Bad Omen " +
|
||||
"status effect."));
|
||||
}
|
||||
else if (luck.cached(player) && !player.hasPotionEffect(type))
|
||||
{
|
||||
luck.restore();
|
||||
player.sendMessage("The -25% debuff to your luck has been removed.");
|
||||
asAudience(player).sendMessage(MiniComponent.info("The -25% debuff to your luck has been removed."));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void effectApplyCheck(EntityPotionEffectEvent event) {
|
||||
public void effectApplyCheck(EntityPotionEffectEvent event)
|
||||
{
|
||||
EntityPotionEffectEvent.Cause cause = EntityPotionEffectEvent.Cause.PATROL_CAPTAIN;
|
||||
EntityPotionEffectEvent.Action added = EntityPotionEffectEvent.Action.ADDED;
|
||||
EntityPotionEffectEvent.Action changed = EntityPotionEffectEvent.Action.CHANGED;
|
||||
|
||||
if (event.getCause().equals(cause) && (event.getAction().equals(added) || event.getAction().equals(changed))) {
|
||||
if (event.getEntity() instanceof Player player) {
|
||||
if (event.getCause().equals(cause) && (event.getAction().equals(added) || event.getAction().equals(changed)))
|
||||
{
|
||||
if (event.getEntity() instanceof Player player)
|
||||
{
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
luck.cache();
|
||||
double maths = luck.getValue() - (luck.getValue() * 0.25);
|
||||
luck.setValue(maths);
|
||||
player.sendMessage(MiniComponent.warn("A -25% debuff has been applied to your luck from the Bad Omen status effect."));
|
||||
asAudience(player).sendMessage(
|
||||
MiniComponent.warn("A -25% debuff has been applied to your luck from the Bad Omen status effect."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void effectRemoveCheck(EntityPotionEffectEvent event) {
|
||||
public void effectRemoveCheck(EntityPotionEffectEvent event)
|
||||
{
|
||||
PotionEffect old = event.getOldEffect();
|
||||
EntityPotionEffectEvent.Action cleared = EntityPotionEffectEvent.Action.CLEARED;
|
||||
EntityPotionEffectEvent.Action removed = EntityPotionEffectEvent.Action.REMOVED;
|
||||
|
||||
if (old == null) return;
|
||||
if (old == null)
|
||||
return;
|
||||
|
||||
if (old.getType().equals(PotionEffectType.BAD_OMEN) && (event.getAction().equals(cleared) || event.getAction().equals(removed))) {
|
||||
if ((event.getEntity() instanceof Player player)) {
|
||||
if (old.getType().equals(PotionEffectType.BAD_OMEN) && (event.getAction().equals(cleared) || event.getAction()
|
||||
.equals(
|
||||
removed)))
|
||||
{
|
||||
if ((event.getEntity() instanceof Player player))
|
||||
{
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
if (luck.cached(player)) {
|
||||
if (luck.cached(player))
|
||||
{
|
||||
luck.restore();
|
||||
player.sendMessage("The -25% debuff to your luck has been removed.");
|
||||
asAudience(player).sendMessage(MiniComponent.info("The -25% debuff to your luck has been removed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void disconnectCheck(PlayerQuitEvent event) {
|
||||
if (event.getPlayer().hasPotionEffect(PotionEffectType.BAD_OMEN)) {
|
||||
public void disconnectCheck(PlayerQuitEvent event)
|
||||
{
|
||||
if (event.getPlayer().hasPotionEffect(PotionEffectType.BAD_OMEN))
|
||||
{
|
||||
Luck luck = plugin.getHandler().getLuckContainer(event.getPlayer());
|
||||
if (luck.cached(event.getPlayer())) {
|
||||
if (luck.cached(event.getPlayer()))
|
||||
{
|
||||
luck.restore();
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,47 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
import org.bukkit.event.entity.EntityDropItemEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ItemDrops extends AbstractListener {
|
||||
public class ItemDrops extends AbstractListener
|
||||
{
|
||||
private final Map<UUID, Player> entityPlayerMap = new HashMap<>();
|
||||
private boolean canAffect = false;
|
||||
|
||||
public ItemDrops(FeelingLucky plugin) {
|
||||
public ItemDrops(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void checkForPreItemDrop(EntityDamageByEntityEvent event) {
|
||||
if (!(event.getEntity() instanceof LivingEntity entity)) {
|
||||
public void checkForPreItemDrop(EntityDamageByEntityEvent event)
|
||||
{
|
||||
if (!(event.getEntity() instanceof LivingEntity entity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(event.getDamager() instanceof Player player)) {
|
||||
if (!(event.getDamager() instanceof Player player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(entity.getHealth() <= 0.0)) {
|
||||
if (entity.getHealth() > 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -42,8 +49,10 @@ public class ItemDrops extends AbstractListener {
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void checkForDroppedItems(EntityDeathEvent event) {
|
||||
if (event.getEntity() instanceof Player) {
|
||||
public void checkForDroppedItems(EntityDeathEvent event)
|
||||
{
|
||||
if (event.getEntity() instanceof Player)
|
||||
{
|
||||
canAffect = false;
|
||||
return;
|
||||
}
|
||||
@ -52,23 +61,32 @@ public class ItemDrops extends AbstractListener {
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void itemDrops(EntityDropItemEvent event) {
|
||||
public void itemDrops(EntityDropItemEvent event)
|
||||
{
|
||||
Entity entity = event.getEntity();
|
||||
|
||||
if (entityPlayerMap.get(entity.getUniqueId()) == null) return;
|
||||
if (entityPlayerMap.get(entity.getUniqueId()) == null)
|
||||
return;
|
||||
|
||||
if (!canAffect) return;
|
||||
if (!canAffect)
|
||||
return;
|
||||
|
||||
Player player = entityPlayerMap.get(entity.getUniqueId());
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
Item item = event.getItemDrop();
|
||||
ItemStack stack = item.getItemStack();
|
||||
int amount = stack.getAmount();
|
||||
if (luck.quickRNG(luck.getPercentage()) && doesQualify("item_drops", luck.getPercentage())) {
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("item_drops", luck.getValue()))
|
||||
{
|
||||
int rng = Luck.RNG().nextInt(2, 5);
|
||||
amount += rng;
|
||||
stack.setAmount(amount);
|
||||
event.getItemDrop().setItemStack(stack);
|
||||
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(MiniComponent.info("Your luck earned you some extra loot!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
31
src/main/java/io/github/simplex/luck/listener/JumpBoost.java
Normal file
31
src/main/java/io/github/simplex/luck/listener/JumpBoost.java
Normal file
@ -0,0 +1,31 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerJumpEvent;
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
public class JumpBoost extends AbstractListener {
|
||||
public JumpBoost(FeelingLucky plugin) {
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void detectJumping(PlayerJumpEvent event) {
|
||||
Player player = event.getPlayer(); // Player is never null; they're in game and jumping.
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
Vector velocity = player.getVelocity().clone();
|
||||
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("jump_boost", luck.getValue())) {
|
||||
player.setVelocity(velocity.multiply(2.5));
|
||||
|
||||
if (luck.isVerbose()) {
|
||||
asAudience(player).sendMessage(MiniComponent.info("Your luck gave you an extra boost to your jump!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
src/main/java/io/github/simplex/luck/listener/OreVein.java
Normal file
77
src/main/java/io/github/simplex/luck/listener/OreVein.java
Normal file
@ -0,0 +1,77 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public class OreVein extends AbstractListener {
|
||||
|
||||
public OreVein(FeelingLucky plugin) {
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void playerMine(@NotNull BlockBreakEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("ore_vein", luck.getValue()) && event.getBlock().isValidTool(player.getInventory().getItemInMainHand())) {
|
||||
Material minedBlockType = event.getBlock().getType();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
getOresInArea(event.getBlock(), minedBlockType).forEach(Block::breakNaturally);
|
||||
player.sendMessage(MiniComponent.info("Your luck has let you mine all the blocks with one swing."));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Block> getOresInArea(@NotNull Block block, Material minedBlockType) {
|
||||
Set<Block> blocks = new HashSet<>();
|
||||
Queue<Block> queue = new LinkedList<>();
|
||||
Set<Location> visited = new HashSet<>();
|
||||
Location initialLocation = block.getLocation();
|
||||
|
||||
queue.add(block);
|
||||
visited.add(initialLocation);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
Block currentBlock = queue.poll();
|
||||
Location currentLocation = currentBlock.getLocation();
|
||||
|
||||
// Check if the current block is within the maximum radius from the initial block
|
||||
if (initialLocation.distance(currentLocation) > 16) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks.add(currentBlock);
|
||||
|
||||
for (BlockFace face : BlockFace.values()) {
|
||||
Block neighbour = currentBlock.getRelative(face);
|
||||
Location neighbourLocation = neighbour.getLocation();
|
||||
|
||||
if (!visited.contains(neighbourLocation) && neighbour.getType().equals(minedBlockType)) {
|
||||
queue.add(neighbour);
|
||||
visited.add(neighbourLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
}
|
@ -5,15 +5,12 @@ import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import io.github.simplex.luck.util.CooldownTimer;
|
||||
import io.github.simplex.luck.util.SpecialFootItem;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Guardian;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Witch;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
@ -28,33 +25,36 @@ public final class PlayerListener extends AbstractListener {
|
||||
public PlayerListener(FeelingLucky plugin) {
|
||||
super(plugin);
|
||||
this.timer = new CooldownTimer();
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void rabbitFoot(PlayerInteractEvent event) {
|
||||
Action action = event.getAction();
|
||||
ItemStack foot = new ItemStack(Material.RABBIT_FOOT);
|
||||
SpecialFootItem special = new SpecialFootItem();
|
||||
SpecialFootItem special = plugin.getFoot();
|
||||
Player player = event.getPlayer();
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
|
||||
if (timer.onCooldown(player)) {
|
||||
player.sendMessage(MiniComponent.err("That feature can only be used once every 30 seconds."));
|
||||
player.sendMessage(MiniComponent.info("You have " + timer.remaining(player) + " seconds remaining."));
|
||||
asAudience(player).sendMessage(MiniComponent.err("That feature can only be used once every 30 seconds."));
|
||||
asAudience(player).sendMessage(MiniComponent.info("You have " + timer.remaining(player) + " seconds " +
|
||||
"remaining."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.isRightClick() && player.getInventory().getItemInMainHand().isSimilar(foot)) {
|
||||
if (action.isRightClick() && player.getInventory().getItemInMainHand().getType().equals(foot.getType())) {
|
||||
if (foot.getItemMeta().equals(special.meta()) || foot.equals(special.get())) {
|
||||
luck.setMultiplier(luck.multiplier() + 1);
|
||||
player.sendMessage(MiniComponent.info("Your luck multiplier has increased by 1!"));
|
||||
luck.setMultiplier(luck.multiplier() + 0.1);
|
||||
asAudience(player).sendMessage(MiniComponent.info("Your luck multiplier has increased by 0.1!"));
|
||||
}
|
||||
double rng = Luck.RNG().nextDouble(2.0, 5.0);
|
||||
rng = Math.round(rng);
|
||||
player.getInventory().remove(player.getInventory().getItemInMainHand());
|
||||
luck.addTo(rng);
|
||||
plugin.getHandler().updatePlayer(player, luck);
|
||||
timer.setCooldown(player.getUniqueId(), System.currentTimeMillis());
|
||||
player.sendMessage(MiniComponent.info("Your luck has been increased by " + rng + " points."));
|
||||
asAudience(player).sendMessage(MiniComponent.info("Your luck has been increased by " + rng + " points."));
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,28 +77,8 @@ public final class PlayerListener extends AbstractListener {
|
||||
if (luck.quickRNG(33.0)) {
|
||||
luck.takeFrom(5.0);
|
||||
plugin.getHandler().updatePlayer(player, luck);
|
||||
player.sendMessage(MiniComponent.warn("Your luck has been decreased by 5 points!"));
|
||||
asAudience(player).sendMessage(MiniComponent.warn("Your luck has been decreased by 5 points!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (PlayerListener) obj;
|
||||
return Objects.equals(this.plugin, that.plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlayerListener[" +
|
||||
"plugin=" + plugin + ']';
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import io.github.simplex.luck.util.ListBox;
|
||||
import java.util.List;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
|
||||
public class RandomEffect extends AbstractListener
|
||||
{
|
||||
public RandomEffect(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void giveRandomEffect(PlayerRespawnEvent respawn)
|
||||
{
|
||||
Player player = respawn.getPlayer();
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
|
||||
List<PotionEffect> effectList = ListBox.positiveEffects.stream().map(m -> m.createEffect(120, 5)).toList();
|
||||
int size = effectList.size();
|
||||
PotionEffect random = effectList.get(Luck.RNG().nextInt(size - 1));
|
||||
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("random_effect", luck.getValue()))
|
||||
{
|
||||
player.addPotionEffect(random);
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(
|
||||
MiniComponent.info("Thanks to luck, a random positive potion effect has " +
|
||||
"been applied to you."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void giveRandomEffect(PlayerTeleportEvent tp)
|
||||
{
|
||||
Player player = tp.getPlayer();
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
|
||||
List<PotionEffect> effectList = ListBox.positiveEffects.stream().map(m -> m.createEffect(120, 5)).toList();
|
||||
int size = effectList.size();
|
||||
PotionEffect random = effectList.get(Luck.RNG().nextInt(size - 1));
|
||||
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("random_effect", luck.getValue()))
|
||||
{
|
||||
player.addPotionEffect(random);
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player).sendMessage(
|
||||
MiniComponent.info("Thanks to luck, a random positive potion effect has been applied to you."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.lib.PotionEffectBuilder;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
@ -10,26 +11,44 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
public class RestoreHunger extends AbstractListener {
|
||||
public RestoreHunger(FeelingLucky plugin) {
|
||||
public class RestoreHunger extends AbstractListener
|
||||
{
|
||||
public RestoreHunger(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void restoreHunger(PlayerItemConsumeEvent event) {
|
||||
public void restoreHunger(PlayerItemConsumeEvent event)
|
||||
{
|
||||
ItemStack item = event.getItem();
|
||||
Luck luck = getHandler().getLuckContainer(event.getPlayer());
|
||||
PotionEffect effect = PotionEffectBuilder.newEffect().type(PotionEffectType.SATURATION).amplifier(2).duration(10).particles(false).create();
|
||||
if (luck.notDefault()) {
|
||||
double percentage = luck.getPercentage();
|
||||
ListBox.foods.forEach(food -> {
|
||||
if (item.isSimilar(food)) {
|
||||
if (luck.quickRNG(percentage) && doesQualify("restore_hunger", percentage)) {
|
||||
event.getPlayer().setExhaustion(event.getPlayer().getExhaustion() + 2);
|
||||
event.getPlayer().addPotionEffect(effect);
|
||||
}
|
||||
}
|
||||
});
|
||||
PotionEffect effect = PotionEffectBuilder.newEffect()
|
||||
.type(PotionEffectType.SATURATION)
|
||||
.amplifier(2)
|
||||
.duration(10)
|
||||
.particles(false)
|
||||
.create();
|
||||
if (luck.notDefault())
|
||||
{
|
||||
double percentage = luck.getValue();
|
||||
ListBox.foods.forEach(food ->
|
||||
{
|
||||
if (item.isSimilar(food) && (luck.quickRNG(percentage) && doesQualify(
|
||||
"restore_hunger", percentage)))
|
||||
{
|
||||
event.getPlayer().setExhaustion(event.getPlayer().getExhaustion() + 2);
|
||||
event.getPlayer().addPotionEffect(effect);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(event.getPlayer())
|
||||
.sendMessage(MiniComponent.info("Your luck has restored your hunger a little more."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.simplex.luck.listener;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.lib.PotionEffectBuilder;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
@ -13,6 +14,7 @@ import org.bukkit.event.entity.EntityDamageEvent;
|
||||
public class TakeDamage extends AbstractListener {
|
||||
public TakeDamage(FeelingLucky plugin) {
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@ -23,9 +25,8 @@ public class TakeDamage extends AbstractListener {
|
||||
}
|
||||
Player player = (Player) event.getEntity();
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
if (ListBox.acceptedCauses.contains(event.getCause())) {
|
||||
if (luck.notDefault()) {
|
||||
double percentage = luck.getPercentage();
|
||||
if (ListBox.acceptedCauses.contains(event.getCause()) && (luck.notDefault())) {
|
||||
double percentage = luck.getValue();
|
||||
|
||||
/*
|
||||
* If a player's luck stat is a negative number, or they are "marked",
|
||||
@ -36,7 +37,7 @@ public class TakeDamage extends AbstractListener {
|
||||
if (luck.quickRNG(percentage)) {
|
||||
event.setCancelled(true);
|
||||
player.damage(event.getDamage() * 2);
|
||||
player.sendMessage(Component.empty().content("You were unlucky and took double damage."));
|
||||
asAudience(player).sendMessage(MiniComponent.warn("You were unlucky and took double damage!"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -44,14 +45,12 @@ public class TakeDamage extends AbstractListener {
|
||||
if (luck.quickRNG(percentage) && doesQualify("take_damage", percentage)) {
|
||||
event.setCancelled(true);
|
||||
player.damage(event.getDamage() / 2);
|
||||
player.sendMessage(Component.empty().content("You got lucky and took less damage."));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ListBox.sideCauses.contains(event.getCause())) {
|
||||
if (luck.notDefault()) {
|
||||
double percentage = luck.getPercentage();
|
||||
if (ListBox.sideCauses.contains(event.getCause()) && (luck.notDefault())) {
|
||||
double percentage = luck.getValue();
|
||||
|
||||
/*
|
||||
* If a player's luck stat is a negative number, or they are "marked",
|
||||
@ -70,9 +69,10 @@ public class TakeDamage extends AbstractListener {
|
||||
event.setCancelled(true);
|
||||
player.getActivePotionEffects().removeIf(p -> ListBox.potionEffects.contains(p.getType()));
|
||||
player.setFireTicks(0);
|
||||
player.sendMessage(Component.empty().content("You got lucky and your afflictions were cured."));
|
||||
asAudience(player).sendMessage(MiniComponent.info("You got lucky and your afflictions were cured" +
|
||||
"."));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,29 +11,41 @@ import org.bukkit.inventory.CraftingInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
public class UnbreakableTool extends AbstractListener {
|
||||
public UnbreakableTool(FeelingLucky plugin) {
|
||||
public class UnbreakableTool extends AbstractListener
|
||||
{
|
||||
public UnbreakableTool(FeelingLucky plugin)
|
||||
{
|
||||
super(plugin);
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void unbreakableTool(CraftItemEvent event) {
|
||||
public void unbreakableTool(CraftItemEvent event)
|
||||
{
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
ItemStack stack = inventory.getResult();
|
||||
|
||||
if (stack == null) return;
|
||||
if (stack == null)
|
||||
return;
|
||||
ItemMeta meta = stack.getItemMeta();
|
||||
|
||||
if (ItemBuilder.isTool(stack.getType())) {
|
||||
if (event.getWhoClicked() instanceof Player player) {
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getPercentage()) && doesQualify("unbreakable", luck.getPercentage())) {
|
||||
meta.setUnbreakable(true);
|
||||
stack.setItemMeta(meta);
|
||||
inventory.setResult(stack);
|
||||
player.sendMessage(MiniComponent.info("By the grace of Luck you have crafted an unbreakable tool!"));
|
||||
if (ItemBuilder.isTool(stack.getType()) && (event.getWhoClicked() instanceof Player player))
|
||||
{
|
||||
Luck luck = getHandler().getLuckContainer(player);
|
||||
if (luck.quickRNG(luck.getValue()) && doesQualify("unbreakable", luck.getValue()))
|
||||
{
|
||||
meta.setUnbreakable(true);
|
||||
stack.setItemMeta(meta);
|
||||
inventory.setResult(stack);
|
||||
|
||||
if (luck.isVerbose())
|
||||
{
|
||||
asAudience(player)
|
||||
.sendMessage(
|
||||
MiniComponent.info("By the grace of Luck you have crafted an unbreakable tool!"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import io.github.simplex.luck.util.SpecialFootItem;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
||||
import org.bukkit.inventory.MerchantRecipe;
|
||||
|
||||
@ -16,12 +15,14 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class VillagerInventory extends AbstractListener {
|
||||
private final SpecialFootItem foot = new SpecialFootItem();
|
||||
private final MerchantRecipe recipe = new MerchantRecipe(foot.get(), 0, 2, true);
|
||||
private final MerchantRecipe recipe;
|
||||
|
||||
public VillagerInventory(FeelingLucky plugin) {
|
||||
super(plugin);
|
||||
|
||||
SpecialFootItem foot = plugin.getFoot();
|
||||
this.recipe = new MerchantRecipe(foot.get(), 0, 2, true);
|
||||
|
||||
recipe.setIngredients(Arrays.asList(
|
||||
ItemBuilder.of(Material.EMERALD).build(),
|
||||
ItemBuilder.of(Material.RABBIT_HIDE).build()
|
||||
@ -30,6 +31,8 @@ public class VillagerInventory extends AbstractListener {
|
||||
recipe.setPriceMultiplier(1.25F);
|
||||
recipe.setVillagerExperience(25);
|
||||
recipe.setSpecialPrice(4);
|
||||
|
||||
register(this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@ -43,7 +46,7 @@ public class VillagerInventory extends AbstractListener {
|
||||
Luck luck = plugin.getHandler().getLuckContainer(event.getPlayer());
|
||||
if (luck == null) return;
|
||||
|
||||
if (luck.quickRNG(luck.getPercentage())) {
|
||||
if (luck.quickRNG(luck.getValue())) {
|
||||
recipeList.add(recipe);
|
||||
vil.setRecipes(recipeList);
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
package io.github.simplex.luck.player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DynamicConfig {
|
||||
private UUID playerUUID;
|
||||
private String username;
|
||||
private double luckValue;
|
||||
private boolean isVerbose;
|
||||
private double multiplier;
|
||||
|
||||
public String getPlayerUUID() {
|
||||
return playerUUID.toString();
|
||||
}
|
||||
|
||||
public void setPlayerUUID(UUID playerUUID) {
|
||||
this.playerUUID = playerUUID;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public double getLuckValue() {
|
||||
return luckValue;
|
||||
}
|
||||
|
||||
public void setLuckValue(double luckValue) {
|
||||
this.luckValue = luckValue;
|
||||
}
|
||||
|
||||
public boolean isVerbose() {
|
||||
return isVerbose;
|
||||
}
|
||||
|
||||
public void setVerbose(boolean isVerbose) {
|
||||
this.isVerbose = isVerbose;
|
||||
}
|
||||
|
||||
public double getMultiplier() {
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
public void setMultiplier(double multiplier) {
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
}
|
@ -3,12 +3,17 @@ package io.github.simplex.luck.player;
|
||||
import io.github.simplex.api.LuckContainer;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public class Luck implements LuckContainer {
|
||||
@ -20,6 +25,7 @@ public class Luck implements LuckContainer {
|
||||
private double BASE_VALUE;
|
||||
private double multiplier;
|
||||
private double tempSave;
|
||||
private boolean verbose;
|
||||
|
||||
public Luck(FeelingLucky plugin, Player player) {
|
||||
this(plugin, player, 1.0);
|
||||
@ -35,15 +41,21 @@ public class Luck implements LuckContainer {
|
||||
event = new PlayerLuckChangeEvent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates a new instance of a pseudorandom number generator based off entropy provided by the operating system.
|
||||
* This will allow for a much purer randomization, due to entropy being different for each call.
|
||||
*
|
||||
* @return A new instance of SecureRandom. Each time this method is called a new instance is created to provide maximum variation with entropic calculations.
|
||||
*/
|
||||
@Contract(pure = true,
|
||||
value = "-> new")
|
||||
public static @NotNull SplittableRandom RNG() {
|
||||
return new SplittableRandom();
|
||||
public static @NotNull SecureRandom RNG() {
|
||||
return new SecureRandom(SecureRandom.getSeed(20));
|
||||
}
|
||||
|
||||
public static boolean quickRNGnoMultiplier(double percentage) {
|
||||
public static boolean quickRNGnoMultiplier(double value) {
|
||||
double rng;
|
||||
if (percentage >= 100.0) {
|
||||
if (value >= 1024.0) {
|
||||
rng = 1024.0; // 100% chance to trigger, obviously;
|
||||
} else {
|
||||
rng = RNG().nextDouble(0.0, 1024.0);
|
||||
@ -51,7 +63,11 @@ public class Luck implements LuckContainer {
|
||||
|
||||
double actual = Math.round((rng / 1024.0) * 100);
|
||||
|
||||
return (percentage >= actual);
|
||||
return (value >= actual);
|
||||
}
|
||||
|
||||
public boolean playerHasLuckPE() {
|
||||
return player.hasPotionEffect(PotionEffectType.LUCK);
|
||||
}
|
||||
|
||||
public FeelingLucky getPlugin() {
|
||||
@ -71,23 +87,30 @@ public class Luck implements LuckContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attribute asAttribute() {
|
||||
return Attribute.GENERIC_LUCK;
|
||||
public void setVerbose(final boolean verbose) {
|
||||
if (!plugin.getConfig().isVerboseGlobal())
|
||||
return;
|
||||
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getNumber() {
|
||||
return associatedPlayer().getAttribute(asAttribute()).getValue();
|
||||
public boolean isVerbose() {
|
||||
if (plugin.getConfig().isVerboseGlobal()) {
|
||||
return verbose;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMatch(double number) {
|
||||
return getNumber() == number;
|
||||
return getValue() == number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClose(double number, int range) {
|
||||
return ((getNumber() - range <= number) && (number <= getNumber() + range));
|
||||
return ((getValue() - range <= number) && (number <= getValue() + range));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,21 +123,33 @@ public class Luck implements LuckContainer {
|
||||
return player;
|
||||
}
|
||||
|
||||
public boolean quickRNG(double percentage) {
|
||||
/**
|
||||
* Quickly calculate whether or not the player has enough luck to trigger the condition.
|
||||
*
|
||||
* @param value The players luck value.
|
||||
* @return True if the player meets the criteria, false if they do not.
|
||||
*/
|
||||
public boolean quickRNG(double value) {
|
||||
double rng;
|
||||
if (percentage >= 100.0) {
|
||||
if (value >= 1024.0) {
|
||||
rng = 1024.0; // 100% chance to trigger, obviously;
|
||||
} else {
|
||||
rng = RNG().nextDouble(0.0, 1024.0);
|
||||
}
|
||||
|
||||
AtomicReference<Double> multiplier = new AtomicReference<>(multiplier());
|
||||
double actual = Math.round((rng / 1024) * 100);
|
||||
double newVal = Math.round((value / 1024) * 100);
|
||||
|
||||
if (multiplier() > 1.0) {
|
||||
return ((percentage * multiplier()) >= actual);
|
||||
if (playerHasLuckPE()) {
|
||||
player.getActivePotionEffects()
|
||||
.stream()
|
||||
.filter(p -> p.getType().equals(PotionEffectType.LUCK))
|
||||
.findFirst()
|
||||
.ifPresent(p -> multiplier.updateAndGet(v -> (v + p.getAmplifier())));
|
||||
}
|
||||
|
||||
return (percentage >= actual);
|
||||
return ((newVal * multiplier.get()) >= actual);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@ -128,7 +163,6 @@ public class Luck implements LuckContainer {
|
||||
|
||||
public void setValue(double value) {
|
||||
BASE_VALUE = value;
|
||||
player.getAttribute(Attribute.GENERIC_LUCK).setBaseValue(value);
|
||||
plugin.getConfigMap().get(associatedPlayer().getUniqueId()).setLuck(value);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
}
|
||||
@ -162,7 +196,7 @@ public class Luck implements LuckContainer {
|
||||
}
|
||||
|
||||
public double getDefaultValue() {
|
||||
return player.getAttribute(Attribute.GENERIC_LUCK).getDefaultValue();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void addTo(double value) {
|
||||
@ -181,10 +215,6 @@ public class Luck implements LuckContainer {
|
||||
setValue(getValue() + value);
|
||||
}
|
||||
|
||||
public double getPercentage() {
|
||||
return getValue() - getDefaultValue();
|
||||
}
|
||||
|
||||
public boolean notDefault() {
|
||||
return getValue() != getDefaultValue();
|
||||
}
|
||||
|
@ -1,106 +1,209 @@
|
||||
package io.github.simplex.luck.player;
|
||||
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.util.SneakyWorker;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerConfig {
|
||||
public class PlayerConfig
|
||||
{
|
||||
private final File configFile;
|
||||
private final OfflinePlayer player;
|
||||
private volatile YamlConfiguration config;
|
||||
private final FeelingLucky plugin;
|
||||
private YamlConfiguration config;
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public PlayerConfig(FeelingLucky plugin, Player player) {
|
||||
|
||||
public PlayerConfig(FeelingLucky plugin, Player player)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.player = player;
|
||||
if (!plugin.getDataFolder().exists()) plugin.getDataFolder().mkdirs();
|
||||
File dataFolder = new File(plugin.getDataFolder(), "players");
|
||||
if (!dataFolder.exists()) dataFolder.mkdirs();
|
||||
File file = new File(dataFolder, player.getUniqueId() + ".yml");
|
||||
if (!file.exists()) {
|
||||
String name = "username: " + player.getName();
|
||||
String luck = "luck: " + player.getAttribute(Attribute.GENERIC_LUCK).getDefaultValue();
|
||||
String multiplier = "multiplier: " + 1.0;
|
||||
|
||||
SneakyWorker.sneakyTry(() -> {
|
||||
file.createNewFile();
|
||||
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(file, StandardCharsets.UTF_8));
|
||||
writer.write(name);
|
||||
writer.newLine();
|
||||
writer.write(luck);
|
||||
writer.newLine();
|
||||
writer.write(multiplier);
|
||||
writer.close();
|
||||
});
|
||||
}
|
||||
configFile = file;
|
||||
configFile = configFile(plugin, player);
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
String tempUsername = config.getString("username");
|
||||
|
||||
if (tempUsername == null) {
|
||||
if (tempUsername == null)
|
||||
{
|
||||
config.set("username", player.getName());
|
||||
config.set("luck", plugin.getHandler().getLuckContainer(player).getDefaultValue());
|
||||
config.set("multiplier", "1.0");
|
||||
config.set("verbose", plugin.getConfig().isVerboseGlobal());
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
protected PlayerConfig(FeelingLucky plugin, File file) {
|
||||
protected PlayerConfig(FeelingLucky plugin, File file)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.configFile = file;
|
||||
this.player = Bukkit.getOfflinePlayer(UUID.fromString(file.getName().split("\\.")[0]));
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
}
|
||||
|
||||
protected PlayerConfig(FeelingLucky plugin, DynamicConfig user)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.player = Bukkit.getOfflinePlayer(UUID.fromString(user.getPlayerUUID()));
|
||||
configFile = configFile(plugin, player);
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
}
|
||||
|
||||
@Contract("_, _ -> new")
|
||||
public static PlayerConfig loadFrom(FeelingLucky plugin, File file) {
|
||||
public static PlayerConfig initFromFile(FeelingLucky plugin, File file)
|
||||
{
|
||||
return new PlayerConfig(plugin, file);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
SneakyWorker.sneakyTry(() -> config.save(configFile));
|
||||
public static PlayerConfig fromDynamicConfig(FeelingLucky plugin, DynamicConfig config)
|
||||
{
|
||||
PlayerConfig playerConfig = new PlayerConfig(plugin, config);
|
||||
playerConfig.setUsername(config.getPlayerUUID());
|
||||
playerConfig.setLuck(config.getLuckValue());
|
||||
playerConfig.setMultiplier(config.getMultiplier());
|
||||
playerConfig.setVerbose(config.isVerbose());
|
||||
return playerConfig;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
SneakyWorker.sneakyTry(() -> config = YamlConfiguration.loadConfiguration(configFile));
|
||||
public DynamicConfig toDynamicConfig()
|
||||
{
|
||||
DynamicConfig dynamicConfig = new DynamicConfig();
|
||||
dynamicConfig.setPlayerUUID(player.getUniqueId());
|
||||
dynamicConfig.setLuckValue(getLuck());
|
||||
dynamicConfig.setVerbose(isVerbose());
|
||||
dynamicConfig.setMultiplier(getMultiplier());
|
||||
return dynamicConfig;
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
@NotNull
|
||||
private File configFile(FeelingLucky plugin, OfflinePlayer player)
|
||||
{
|
||||
if (!plugin.getDataFolder().exists())
|
||||
plugin.getDataFolder().mkdirs();
|
||||
File dataFolder = new File(plugin.getDataFolder(), "players");
|
||||
if (!dataFolder.exists())
|
||||
dataFolder.mkdirs();
|
||||
File file = new File(dataFolder, player.getUniqueId() + ".yml");
|
||||
if (!file.exists())
|
||||
{
|
||||
try
|
||||
{
|
||||
file.createNewFile();
|
||||
final YamlConfiguration v0 = new YamlConfiguration();
|
||||
v0.set("username", player.getName());
|
||||
v0.set("luck", 0);
|
||||
v0.set("multiplier", 1.0);
|
||||
v0.set("verbose", plugin.getConfig().isVerboseGlobal());
|
||||
v0.save(file);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logs.error(ex);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
try
|
||||
{
|
||||
config.save(configFile);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logs.error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void load()
|
||||
{
|
||||
try
|
||||
{
|
||||
config.load(configFile);
|
||||
}
|
||||
catch (IOException | InvalidConfigurationException ex)
|
||||
{
|
||||
Logs.error(ex);
|
||||
|
||||
Logs.warn("Attempting to reinitialize variable... this is dangerous!");
|
||||
|
||||
try
|
||||
{
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
}
|
||||
catch (IllegalArgumentException th)
|
||||
{
|
||||
Logs.error(th);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void reload()
|
||||
{
|
||||
save();
|
||||
load();
|
||||
}
|
||||
|
||||
public OfflinePlayer getPlayer() {
|
||||
public OfflinePlayer getPlayer()
|
||||
{
|
||||
return player;
|
||||
}
|
||||
|
||||
public void setUsername(String name) {
|
||||
config.set("username", name);
|
||||
save();
|
||||
public String getUsername() {
|
||||
return config.getString("username");
|
||||
}
|
||||
|
||||
public void setLuck(double luck) {
|
||||
public double getLuck()
|
||||
{
|
||||
return config.getDouble("luck");
|
||||
}
|
||||
|
||||
public void setLuck(double luck)
|
||||
{
|
||||
config.set("luck", luck);
|
||||
save();
|
||||
reload();
|
||||
}
|
||||
|
||||
public void setMultiplier(double multiplier) {
|
||||
public double getMultiplier()
|
||||
{
|
||||
return config.getDouble("multiplier");
|
||||
}
|
||||
|
||||
public void setMultiplier(double multiplier)
|
||||
{
|
||||
config.set("multiplier", multiplier);
|
||||
save();
|
||||
reload();
|
||||
}
|
||||
|
||||
public YamlConfiguration getConfig() {
|
||||
public boolean isVerbose()
|
||||
{
|
||||
return config.getBoolean("verbose");
|
||||
}
|
||||
|
||||
public void setVerbose(final boolean verbose)
|
||||
{
|
||||
config.set("verbose", verbose);
|
||||
reload();
|
||||
}
|
||||
|
||||
public void setUsername(String name)
|
||||
{
|
||||
config.set("username", name);
|
||||
reload();
|
||||
}
|
||||
|
||||
public YamlConfiguration getConfig()
|
||||
{
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.simplex.luck.player;
|
||||
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.sql.SQLType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@ -38,9 +39,10 @@ public class PlayerHandler implements Listener {
|
||||
plugin.getConfigMap().put(player.getUniqueId(), playerConfig);
|
||||
}
|
||||
|
||||
String username = playerConfig.getConfig().getString("username");
|
||||
double luck = playerConfig.getConfig().getDouble("luck");
|
||||
double multiplier = playerConfig.getConfig().getDouble("multiplier");
|
||||
String username = playerConfig.getUsername();
|
||||
double luck = playerConfig.getLuck();
|
||||
double multiplier = playerConfig.getMultiplier();
|
||||
boolean verbose = plugin.getConfig().isVerboseGlobal() && playerConfig.isVerbose();
|
||||
|
||||
if (!player.getName().equalsIgnoreCase(username)) {
|
||||
playerConfig.getConfig().set("username", player.getName());
|
||||
@ -49,6 +51,7 @@ public class PlayerHandler implements Listener {
|
||||
}
|
||||
|
||||
Luck container = new Luck(plugin, player, multiplier);
|
||||
container.setVerbose(verbose);
|
||||
container.setValue(luck);
|
||||
|
||||
playerLuckMap.put(player, container);
|
||||
|
@ -1,7 +1,5 @@
|
||||
package io.github.simplex.luck.util;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -10,9 +8,8 @@ import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CooldownTimer {
|
||||
private final Map<UUID, Long> cooldowns = new HashMap<>();
|
||||
|
||||
public static final long DEFAULT_COOLDOWN = 30L;
|
||||
private final Map<UUID, Long> cooldowns = new HashMap<>();
|
||||
|
||||
public void setCooldown(UUID playerUUID, long time) {
|
||||
if (time < 1) {
|
||||
|
@ -6,6 +6,7 @@ import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ListBox {
|
||||
@ -36,6 +37,25 @@ public class ListBox {
|
||||
add(PotionEffectType.WEAKNESS);
|
||||
}};
|
||||
|
||||
public static final List<PotionEffectType> positiveEffects = new ArrayList<>() {{
|
||||
add(PotionEffectType.DAMAGE_RESISTANCE);
|
||||
add(PotionEffectType.DOLPHINS_GRACE);
|
||||
add(PotionEffectType.INCREASE_DAMAGE);
|
||||
add(PotionEffectType.ABSORPTION);
|
||||
add(PotionEffectType.SATURATION);
|
||||
add(PotionEffectType.FIRE_RESISTANCE);
|
||||
add(PotionEffectType.WATER_BREATHING);
|
||||
add(PotionEffectType.SPEED);
|
||||
add(PotionEffectType.SLOW_FALLING);
|
||||
add(PotionEffectType.REGENERATION);
|
||||
add(PotionEffectType.NIGHT_VISION);
|
||||
add(PotionEffectType.LUCK);
|
||||
add(PotionEffectType.JUMP);
|
||||
add(PotionEffectType.INVISIBILITY);
|
||||
add(PotionEffectType.HEALTH_BOOST);
|
||||
add(PotionEffectType.FAST_DIGGING);
|
||||
}};
|
||||
|
||||
public static final List<ItemStack> foods = new ArrayList<>() {{
|
||||
add(new ItemStack(Material.COOKED_BEEF));
|
||||
add(new ItemStack(Material.COOKED_CHICKEN));
|
||||
|
52
src/main/java/io/github/simplex/luck/util/Logs.java
Normal file
52
src/main/java/io/github/simplex/luck/util/Logs.java
Normal file
@ -0,0 +1,52 @@
|
||||
package io.github.simplex.luck.util;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Logs
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger("FeelingLucky");
|
||||
|
||||
private Logs()
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static void info(String message)
|
||||
{
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
public static void warn(String message)
|
||||
{
|
||||
logger.warn(message);
|
||||
}
|
||||
|
||||
public static void warn(String message, Throwable th)
|
||||
{
|
||||
logger.warn(message, th);
|
||||
}
|
||||
|
||||
public static void warn(Throwable th)
|
||||
{
|
||||
final String msg = ExceptionUtils.getRootCauseMessage(th);
|
||||
logger.warn(msg);
|
||||
}
|
||||
|
||||
public static void error(String message)
|
||||
{
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
public static void error(String message, Throwable th)
|
||||
{
|
||||
logger.error(message, th);
|
||||
}
|
||||
|
||||
public static void error(Throwable th)
|
||||
{
|
||||
final String msg = ExceptionUtils.getRootCauseMessage(th);
|
||||
logger.error(msg, th);
|
||||
}
|
||||
}
|
@ -6,10 +6,7 @@ import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.Luck;
|
||||
import io.github.simplex.luck.player.PlayerConfig;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -18,19 +15,23 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LuckCMD extends Command implements TabCompleter {
|
||||
public class LuckCMD extends Command implements TabCompleter, PluginIdentifiableCommand {
|
||||
private final FeelingLucky plugin;
|
||||
|
||||
public LuckCMD(FeelingLucky plugin) {
|
||||
super("luck", "FeelingLucky main command.", "/<command> <info | set | reset | give | take> [player] [amount]", List.of());
|
||||
this.plugin = plugin;
|
||||
setPermission("luck.default");
|
||||
plugin.getServer().getCommandMap().register("luck", "FeelingLucky", this);
|
||||
plugin.getCommandMap().register("luck", "FeelingLucky", this);
|
||||
plugin.getLogger().info("Successfully registered command: Luck");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
if (args.length < 1 || args.length > 3) return false;
|
||||
if (args.length < 1 || args.length > 3) {
|
||||
sender.sendMessage(this.getUsage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.length == 3) {
|
||||
if ((sender instanceof ConsoleCommandSender) || sender.hasPermission("luck.admin")) {
|
||||
@ -68,21 +69,21 @@ public class LuckCMD extends Command implements TabCompleter {
|
||||
luck.setValue(amount);
|
||||
plugin.getHandler().updatePlayer(player, luck);
|
||||
config.setLuck(luck.getValue());
|
||||
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
|
||||
sender.sendMessage(MiniComponent.info("Successfully set " + args[1] + "'s Luck stat to " + amount + "."));
|
||||
return true;
|
||||
}
|
||||
case "give" -> {
|
||||
luck.addTo(amount);
|
||||
plugin.getHandler().updatePlayer(player, luck);
|
||||
config.setLuck(luck.getValue());
|
||||
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
|
||||
sender.sendMessage(MiniComponent.info("Successfully gave " + args[1] + " " + amount + " points of luck!"));
|
||||
return true;
|
||||
}
|
||||
case "take" -> {
|
||||
luck.takeFrom(amount);
|
||||
plugin.getHandler().updatePlayer(player, luck);
|
||||
config.setLuck(luck.getValue());
|
||||
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
|
||||
sender.sendMessage(MiniComponent.info("Successfully took " + amount + " points of luck from " + args[1]));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -95,7 +96,9 @@ public class LuckCMD extends Command implements TabCompleter {
|
||||
if (args.length == 2) {
|
||||
if ((sender instanceof ConsoleCommandSender) || sender.hasPermission("luck.admin")) {
|
||||
if (args[0].equalsIgnoreCase("reload") && args[1].equalsIgnoreCase("-m")) {
|
||||
|
||||
plugin.getConfig().reload();
|
||||
sender.sendMessage(MiniComponent.info("Configuration successfully reloaded."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("info")) {
|
||||
@ -111,6 +114,23 @@ public class LuckCMD extends Command implements TabCompleter {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("verbose") && sender instanceof Player player) {
|
||||
|
||||
if (!plugin.getConfig().isVerboseGlobal()) {
|
||||
player.sendMessage(Messages.VERBOSE_DISABLED.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
final boolean a1 = Boolean.parseBoolean(args[1]);
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
PlayerConfig config = plugin.getConfigMap().get(player.getUniqueId());
|
||||
luck.setVerbose(a1);
|
||||
plugin.getHandler().updatePlayer(player, luck);
|
||||
config.setVerbose(a1);
|
||||
sender.sendMessage(MiniComponent.info("Verbose mode set to " + a1 + "."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("reset")) {
|
||||
Player player = Bukkit.getPlayer(args[1]);
|
||||
|
||||
@ -143,7 +163,7 @@ public class LuckCMD extends Command implements TabCompleter {
|
||||
if ((sender instanceof Player player) && player.hasPermission("luck.default")) {
|
||||
if (args[0].equalsIgnoreCase("info")) {
|
||||
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||
player.sendMessage(MiniComponent.info("Your Luck: " + luck.getPercentage()));
|
||||
player.sendMessage(MiniComponent.info("Your Luck: " + luck.getValue()));
|
||||
return true;
|
||||
}
|
||||
} else if (sender instanceof ConsoleCommandSender) {
|
||||
@ -160,33 +180,47 @@ public class LuckCMD extends Command implements TabCompleter {
|
||||
List<String> completions = new ArrayList<>() {{
|
||||
add("info");
|
||||
}};
|
||||
List<String> playerNames = new ArrayList<>() {{
|
||||
Bukkit.getOnlinePlayers().forEach(p -> add(p.getName()));
|
||||
}};
|
||||
List<String> adminCommands = List.of("set", "reset", "give", "take");
|
||||
List<String> playerNames = Bukkit.getOnlinePlayers().stream().map(Player::getName).toList();
|
||||
List<String> adminCommands = List.of("set", "reset", "give", "take", "reload");
|
||||
|
||||
if ((sender instanceof ConsoleCommandSender) || sender.hasPermission("luck.admin")) {
|
||||
completions.addAll(adminCommands);
|
||||
return completions;
|
||||
return completions.stream().filter(n -> n.startsWith(args[0])).toList();
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("info") && sender.hasPermission("luck.admin")) {
|
||||
return playerNames;
|
||||
}
|
||||
if (adminCommands.contains(args[0])
|
||||
&& sender.hasPermission("luck.admin")) {
|
||||
if (args.length == 2) {
|
||||
switch (args[0]) {
|
||||
case "info":
|
||||
case "reset":
|
||||
return playerNames.stream().filter(n -> n.startsWith(args[1])).toList();
|
||||
case "reload":
|
||||
return List.of("-m", "-p");
|
||||
}
|
||||
}
|
||||
|
||||
if (completions.contains(args[1]) && sender.hasPermission("luck.admin")) {
|
||||
switch (args[0]) {
|
||||
case "info":
|
||||
case "reset":
|
||||
case "reload":
|
||||
return new ArrayList<>();
|
||||
case "give":
|
||||
case "take":
|
||||
case "set":
|
||||
return List.of("amount");
|
||||
if (args.length == 3 && playerNames.contains(args[1])) {
|
||||
switch (args[0]) {
|
||||
case "give":
|
||||
case "take":
|
||||
case "set":
|
||||
return List.of("amount");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return completions;
|
||||
if (args[0].equalsIgnoreCase("reload")
|
||||
&& args[1].equalsIgnoreCase("-p")
|
||||
&& sender.hasPermission("luck.admin") && (args.length == 3)) {
|
||||
return playerNames.stream().filter(n -> n.startsWith(args[2])).toList();
|
||||
}
|
||||
|
||||
return completions.stream().filter(n -> n.startsWith(args[0])).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FeelingLucky getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
package io.github.simplex.luck.util;
|
||||
|
||||
import io.github.simplex.lib.MiniComponent;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import org.bukkit.command.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RegenerateConfigCMD extends Command implements TabCompleter, PluginIdentifiableCommand {
|
||||
private final FeelingLucky plugin;
|
||||
|
||||
public RegenerateConfigCMD(FeelingLucky plugin) {
|
||||
super("rgc", "Regenerate this plugin's config file.", "/<command>", List.of());
|
||||
this.plugin = plugin;
|
||||
setPermission("luck.rgc");
|
||||
plugin.getCommandMap().register("rgc", "FeelingLucky", this);
|
||||
plugin.getLogger().info("Successfully registered command: RGC.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
if (!(sender instanceof ConsoleCommandSender)) {
|
||||
sender.sendMessage(MiniComponent.err("This command can only be used through console access."));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.saveResource("config.yml", true);
|
||||
plugin.getConfig().load();
|
||||
plugin.getLogger().info("Configuration regenerated.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FeelingLucky getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
@ -5,6 +5,13 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
public class SneakyWorker {
|
||||
public static void sneakyTry(SneakyTry sneakyTry) {
|
||||
try {
|
||||
@ -14,7 +21,7 @@ public class SneakyWorker {
|
||||
+ ex.getClass().getSimpleName()
|
||||
+ " has occurred. A cause will be printed. \n\n"
|
||||
+ ex.getCause();
|
||||
Bukkit.getLogger().severe(sb);
|
||||
Logs.error(sb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,11 +32,48 @@ public class SneakyWorker {
|
||||
}
|
||||
}
|
||||
|
||||
public static void move(Item item) {
|
||||
public static Class<?>[] getClasses(String packageName) throws ClassNotFoundException, IOException {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
assert classLoader != null;
|
||||
String path = packageName.replace(".", "/");
|
||||
Enumeration<URL> resources = classLoader.getResources(path);
|
||||
List<File> dirs = new ArrayList<>();
|
||||
while (resources.hasMoreElements()) {
|
||||
URL resource = resources.nextElement();
|
||||
dirs.add(new File(resource.getFile()));
|
||||
}
|
||||
ArrayList<Class<?>> classes = new ArrayList<>();
|
||||
for (File directory : dirs) {
|
||||
classes.addAll(findClasses(directory, packageName));
|
||||
}
|
||||
return classes.toArray(new Class<?>[0]);
|
||||
}
|
||||
|
||||
private static List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
|
||||
List<Class<?>> classes = new ArrayList<>();
|
||||
if (!directory.exists()) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
File[] files = directory.listFiles();
|
||||
assert files != null;
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
assert !file.getName().contains(".");
|
||||
classes.addAll(findClasses(file, packageName + "." + file.getName().substring(0, file.getName().length() - 6)));
|
||||
} else if (file.getName().endsWith(".class")) {
|
||||
classes.add(Class.forName(packageName + "." + file.getName().substring(0, file.getName().length() - 6)));
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public static Item move(Item item) {
|
||||
ItemStack stack = item.getItemStack();
|
||||
int rng = (Luck.RNG().nextInt(2, 5)) + stack.getAmount();
|
||||
stack.setAmount(rng);
|
||||
item.setItemStack(stack);
|
||||
return item;
|
||||
}
|
||||
|
||||
public interface SneakyTry {
|
||||
|
@ -12,7 +12,7 @@ public class SpecialFootItem {
|
||||
stack = ItemBuilder.of(Material.RABBIT_FOOT)
|
||||
.setName("Enhanced Rabbit Foot")
|
||||
.setAmount(1).setLore("A strange energy radiates from within.",
|
||||
"This item will increase your luck multiplier by one.")
|
||||
"This item will increase your luck multiplier by 0.1.")
|
||||
.build();
|
||||
}
|
||||
|
||||
|
863
src/main/java/io/github/simplex/metrics/Metrics.java
Normal file
863
src/main/java/io/github/simplex/metrics/Metrics.java
Normal file
@ -0,0 +1,863 @@
|
||||
/*
|
||||
* This Metrics class was auto-generated and can be copied into your project if you are
|
||||
* not using a build tool like Gradle or Maven for dependency management.
|
||||
*
|
||||
* IMPORTANT: You are not allowed to modify this class, except changing the package.
|
||||
*
|
||||
* Unallowed modifications include but are not limited to:
|
||||
* - Remove the option for users to opt-out
|
||||
* - Change the frequency for data submission
|
||||
* - Obfuscate the code (every obfucator should allow you to make an exception for specific files)
|
||||
* - Reformat the code (if you use a linter, add an exception)
|
||||
*
|
||||
* Violations will result in a ban of your plugin and account from bStats.
|
||||
*/
|
||||
package io.github.simplex.metrics;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class Metrics {
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
private final MetricsBase metricsBase;
|
||||
|
||||
/**
|
||||
* Creates a new Metrics instance.
|
||||
*
|
||||
* @param plugin Your plugin instance.
|
||||
* @param serviceId The id of the service. It can be found at <a
|
||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(JavaPlugin plugin, int serviceId) {
|
||||
this.plugin = plugin;
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
if (!config.isSet("serverUuid")) {
|
||||
config.addDefault("enabled", true);
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
config.addDefault("logFailedRequests", false);
|
||||
config.addDefault("logSentData", false);
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
// Inform the server owners about bStats
|
||||
config
|
||||
.options()
|
||||
.header(
|
||||
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
||||
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
||||
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
||||
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
||||
+ "anonymous.")
|
||||
.copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// Load the data
|
||||
boolean enabled = config.getBoolean("enabled", true);
|
||||
String serverUUID = config.getString("serverUuid");
|
||||
boolean logErrors = config.getBoolean("logFailedRequests", false);
|
||||
boolean logSentData = config.getBoolean("logSentData", false);
|
||||
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
metricsBase =
|
||||
new MetricsBase(
|
||||
"bukkit",
|
||||
serverUUID,
|
||||
serviceId,
|
||||
enabled,
|
||||
this::appendPlatformData,
|
||||
this::appendServiceData,
|
||||
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
||||
plugin::isEnabled,
|
||||
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
|
||||
(message) -> this.plugin.getLogger().log(Level.INFO, message),
|
||||
logErrors,
|
||||
logSentData,
|
||||
logResponseStatusText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
metricsBase.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
||||
builder.appendField("playerAmount", getPlayerAmount());
|
||||
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
|
||||
builder.appendField("bukkitVersion", Bukkit.getVersion());
|
||||
builder.appendField("bukkitName", Bukkit.getName());
|
||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
||||
builder.appendField("osName", System.getProperty("os.name"));
|
||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
||||
}
|
||||
|
||||
private void appendServiceData(JsonObjectBuilder builder) {
|
||||
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
|
||||
}
|
||||
|
||||
private int getPlayerAmount() {
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed from an array to a collection,
|
||||
// This fixes java.lang.NoSuchMethodError:
|
||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
return onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
// Just use the new method if the reflection failed
|
||||
return Bukkit.getOnlinePlayers().size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetricsBase {
|
||||
|
||||
/** The version of the Metrics class. */
|
||||
public static final String METRICS_VERSION = "3.0.0";
|
||||
|
||||
private static final ScheduledExecutorService scheduler =
|
||||
Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics"));
|
||||
|
||||
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
|
||||
|
||||
private final String platform;
|
||||
|
||||
private final String serverUuid;
|
||||
|
||||
private final int serviceId;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
|
||||
|
||||
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
|
||||
|
||||
private final Consumer<Runnable> submitTaskConsumer;
|
||||
|
||||
private final Supplier<Boolean> checkServiceEnabledSupplier;
|
||||
|
||||
private final BiConsumer<String, Throwable> errorLogger;
|
||||
|
||||
private final Consumer<String> infoLogger;
|
||||
|
||||
private final boolean logErrors;
|
||||
|
||||
private final boolean logSentData;
|
||||
|
||||
private final boolean logResponseStatusText;
|
||||
|
||||
private final Set<CustomChart> customCharts = new HashSet<>();
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
/**
|
||||
* Creates a new MetricsBase class instance.
|
||||
*
|
||||
* @param platform The platform of the service.
|
||||
* @param serviceId The id of the service.
|
||||
* @param serverUuid The server uuid.
|
||||
* @param enabled Whether or not data sending is enabled.
|
||||
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all platform-specific data.
|
||||
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
||||
* appends all service-specific data.
|
||||
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
|
||||
* used to delegate the data collection to a another thread to prevent errors caused by
|
||||
* concurrency. Can be {@code null}.
|
||||
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
|
||||
* @param errorLogger A consumer that accepts log message and an error.
|
||||
* @param infoLogger A consumer that accepts info log messages.
|
||||
* @param logErrors Whether or not errors should be logged.
|
||||
* @param logSentData Whether or not the sent data should be logged.
|
||||
* @param logResponseStatusText Whether or not the response status text should be logged.
|
||||
*/
|
||||
public MetricsBase(
|
||||
String platform,
|
||||
String serverUuid,
|
||||
int serviceId,
|
||||
boolean enabled,
|
||||
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
|
||||
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
|
||||
Consumer<Runnable> submitTaskConsumer,
|
||||
Supplier<Boolean> checkServiceEnabledSupplier,
|
||||
BiConsumer<String, Throwable> errorLogger,
|
||||
Consumer<String> infoLogger,
|
||||
boolean logErrors,
|
||||
boolean logSentData,
|
||||
boolean logResponseStatusText) {
|
||||
this.platform = platform;
|
||||
this.serverUuid = serverUuid;
|
||||
this.serviceId = serviceId;
|
||||
this.enabled = enabled;
|
||||
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
|
||||
this.appendServiceDataConsumer = appendServiceDataConsumer;
|
||||
this.submitTaskConsumer = submitTaskConsumer;
|
||||
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
|
||||
this.errorLogger = errorLogger;
|
||||
this.infoLogger = infoLogger;
|
||||
this.logErrors = logErrors;
|
||||
this.logSentData = logSentData;
|
||||
this.logResponseStatusText = logResponseStatusText;
|
||||
checkRelocation();
|
||||
if (enabled) {
|
||||
// WARNING: Removing the option to opt-out will get your plugin banned from bStats
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
this.customCharts.add(chart);
|
||||
}
|
||||
|
||||
private void startSubmitting() {
|
||||
final Runnable submitTask =
|
||||
() -> {
|
||||
if (!enabled || !checkServiceEnabledSupplier.get()) {
|
||||
// Submitting data or service is disabled
|
||||
scheduler.shutdown();
|
||||
return;
|
||||
}
|
||||
if (submitTaskConsumer != null) {
|
||||
submitTaskConsumer.accept(this::submitData);
|
||||
} else {
|
||||
this.submitData();
|
||||
}
|
||||
};
|
||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution
|
||||
// of requests on the
|
||||
// bStats backend. To circumvent this problem, we introduce some randomness into the initial
|
||||
// and second delay.
|
||||
// WARNING: You must not modify and part of this Metrics class, including the submit delay or
|
||||
// frequency!
|
||||
// WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it!
|
||||
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
||||
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
||||
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
||||
scheduler.scheduleAtFixedRate(
|
||||
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void submitData() {
|
||||
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
|
||||
appendPlatformDataConsumer.accept(baseJsonBuilder);
|
||||
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
|
||||
appendServiceDataConsumer.accept(serviceJsonBuilder);
|
||||
JsonObjectBuilder.JsonObject[] chartData =
|
||||
customCharts.stream()
|
||||
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
|
||||
.filter(Objects::nonNull)
|
||||
.toArray(JsonObjectBuilder.JsonObject[]::new);
|
||||
serviceJsonBuilder.appendField("id", serviceId);
|
||||
serviceJsonBuilder.appendField("customCharts", chartData);
|
||||
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
|
||||
baseJsonBuilder.appendField("serverUUID", serverUuid);
|
||||
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
|
||||
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
|
||||
scheduler.execute(
|
||||
() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Could not submit bStats metrics data", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
|
||||
if (logSentData) {
|
||||
infoLogger.accept("Sent bStats metrics data: " + data.toString());
|
||||
}
|
||||
String url = String.format(REPORT_URL, platform);
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
|
||||
connection.setDoOutput(true);
|
||||
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try (BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
if (logResponseStatusText) {
|
||||
infoLogger.accept("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks that the class was properly relocated. */
|
||||
private void checkRelocation() {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null
|
||||
|| !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little
|
||||
// "trick" ... :D
|
||||
final String defaultPackage =
|
||||
new String(new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'});
|
||||
final String examplePackage =
|
||||
new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure no one just copy & pastes the example and uses the wrong package
|
||||
// names
|
||||
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|
||||
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given string.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped string.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
valuesBuilder.appendField(entry.getKey(), new int[] {entry.getValue()});
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class CustomChart {
|
||||
|
||||
private final String chartId;
|
||||
|
||||
protected CustomChart(String chartId) {
|
||||
if (chartId == null) {
|
||||
throw new IllegalArgumentException("chartId must not be null");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
public JsonObjectBuilder.JsonObject getRequestJsonObject(
|
||||
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
|
||||
JsonObjectBuilder builder = new JsonObjectBuilder();
|
||||
builder.appendField("chartId", chartId);
|
||||
try {
|
||||
JsonObjectBuilder.JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
builder.appendField("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logErrors) {
|
||||
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
|
||||
}
|
||||
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
// Skip this invalid
|
||||
continue;
|
||||
}
|
||||
allSkipped = false;
|
||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
return new JsonObjectBuilder().appendField("value", value).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extremely simple JSON builder.
|
||||
*
|
||||
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
|
||||
* for its use-case.
|
||||
*/
|
||||
public static class JsonObjectBuilder {
|
||||
|
||||
private StringBuilder builder = new StringBuilder();
|
||||
|
||||
private boolean hasAtLeastOneField = false;
|
||||
|
||||
public JsonObjectBuilder() {
|
||||
builder.append("{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a null field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendNull(String key) {
|
||||
appendFieldUnescaped(key, "null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("JSON value must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer field to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param value The value of the field.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int value) {
|
||||
appendFieldUnescaped(key, String.valueOf(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param object The object.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject object) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException("JSON object must not be null");
|
||||
}
|
||||
appendFieldUnescaped(key, object.toString());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a string array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The string array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, String[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values)
|
||||
.map(value -> "\"" + escape(value) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an integer array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, int[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an object array to the JSON.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param values The integer array.
|
||||
* @return A reference to this object.
|
||||
*/
|
||||
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
|
||||
if (values == null) {
|
||||
throw new IllegalArgumentException("JSON values must not be null");
|
||||
}
|
||||
String escapedValues =
|
||||
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
|
||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a field to the object.
|
||||
*
|
||||
* @param key The key of the field.
|
||||
* @param escapedValue The escaped value of the field.
|
||||
*/
|
||||
private void appendFieldUnescaped(String key, String escapedValue) {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("JSON key must not be null");
|
||||
}
|
||||
if (hasAtLeastOneField) {
|
||||
builder.append(",");
|
||||
}
|
||||
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
|
||||
hasAtLeastOneField = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the JSON string and invalidates this builder.
|
||||
*
|
||||
* @return The built JSON string.
|
||||
*/
|
||||
public JsonObject build() {
|
||||
if (builder == null) {
|
||||
throw new IllegalStateException("JSON has already been built");
|
||||
}
|
||||
JsonObject object = new JsonObject(builder.append("}").toString());
|
||||
builder = null;
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
|
||||
*
|
||||
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
|
||||
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
|
||||
*
|
||||
* @param value The value to escape.
|
||||
* @return The escaped value.
|
||||
*/
|
||||
private static String escape(String value) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == '"') {
|
||||
builder.append("\\\"");
|
||||
} else if (c == '\\') {
|
||||
builder.append("\\\\");
|
||||
} else if (c <= '\u000F') {
|
||||
builder.append("\\u000").append(Integer.toHexString(c));
|
||||
} else if (c <= '\u001F') {
|
||||
builder.append("\\u00").append(Integer.toHexString(c));
|
||||
} else {
|
||||
builder.append(c);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A super simple representation of a JSON object.
|
||||
*
|
||||
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
|
||||
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
|
||||
* JsonObject)}.
|
||||
*/
|
||||
public static class JsonObject {
|
||||
|
||||
private final String value;
|
||||
|
||||
private JsonObject(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
94
src/main/java/io/github/simplex/sql/MySQL.java
Normal file
94
src/main/java/io/github/simplex/sql/MySQL.java
Normal file
@ -0,0 +1,94 @@
|
||||
package io.github.simplex.sql;
|
||||
|
||||
import io.github.simplex.luck.Config;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.DynamicConfig;
|
||||
import io.github.simplex.luck.player.PlayerConfig;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import io.github.simplex.sql.users.MySQLUser;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MySQL {
|
||||
|
||||
private final Connection connection;
|
||||
private final FeelingLucky plugin;
|
||||
|
||||
public MySQL(FeelingLucky plugin) throws SQLException {
|
||||
this.plugin = plugin;
|
||||
|
||||
Config.MySQLWrapper mySQLWrapper = plugin.getConfig().getMySQL();
|
||||
String host = mySQLWrapper.getHost();
|
||||
int port = mySQLWrapper.getPort();
|
||||
String database = mySQLWrapper.getDatabase();
|
||||
String username = mySQLWrapper.getUsername();
|
||||
String password = mySQLWrapper.getPassword();
|
||||
|
||||
connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database, username, password);
|
||||
if (connection != null) {
|
||||
try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE IF NOT EXISTS PlayerConfig (playerUUID VARCHAR(36), username VARCHAR(16), luckValue DOUBLE, isVerbose BOOLEAN, multiplier DOUBLE)")) {
|
||||
statement.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void loadPlayers() {
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM PlayerConfig");
|
||||
ResultSet resultSet = statement.executeQuery()) {
|
||||
|
||||
while (resultSet.next()) {
|
||||
String playerUUID = resultSet.getString("playerUUID");
|
||||
MySQLUser mySQLUser = new MySQLUser(connection, UUID.fromString(playerUUID));
|
||||
DynamicConfig userConfig = mySQLUser.loadUserConfig();
|
||||
PlayerConfig playerConfig = PlayerConfig.fromDynamicConfig(plugin, userConfig);
|
||||
plugin.getConfigMap().put(UUID.fromString(playerUUID), playerConfig);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void savePlayer(final PlayerConfig playerConfig) {
|
||||
MySQLUser mySQLUser = new MySQLUser(connection, playerConfig.getPlayer().getUniqueId());
|
||||
mySQLUser.saveUserConfig(playerConfig.toDynamicConfig());
|
||||
}
|
||||
|
||||
public void loadPlayer(final UUID playerUUID) {
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM PlayerConfig WHERE playerUUID = ?")) {
|
||||
statement.setString(1, playerUUID.toString());
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
if (resultSet.next()) {
|
||||
MySQLUser mySQLUser = new MySQLUser(connection, playerUUID);
|
||||
DynamicConfig userConfig = mySQLUser.loadUserConfig();
|
||||
PlayerConfig playerConfig = PlayerConfig.fromDynamicConfig(plugin, userConfig);
|
||||
plugin.getConfigMap().put(playerUUID, playerConfig);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void savePlayers() {
|
||||
for (UUID playerUUID : plugin.getConfigMap().keySet()) {
|
||||
PlayerConfig playerConfig = plugin.getConfigMap().get(playerUUID);
|
||||
MySQLUser mySQLUser = new MySQLUser(connection, playerUUID);
|
||||
mySQLUser.saveUserConfig(playerConfig.toDynamicConfig());
|
||||
}
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
src/main/java/io/github/simplex/sql/Redis.java
Normal file
77
src/main/java/io/github/simplex/sql/Redis.java
Normal file
@ -0,0 +1,77 @@
|
||||
package io.github.simplex.sql;
|
||||
|
||||
import io.github.simplex.luck.Config;
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.DynamicConfig;
|
||||
import io.github.simplex.luck.player.PlayerConfig;
|
||||
import io.github.simplex.sql.users.RedisUser;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Redis {
|
||||
|
||||
private final redis.clients.jedis.Jedis jedis;
|
||||
private final FeelingLucky plugin;
|
||||
|
||||
public Redis(final FeelingLucky plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
final Config.RedisWrapper redisWrapper = plugin.getConfig().getRedis();
|
||||
final String host = redisWrapper.getHost();
|
||||
final String password = redisWrapper.getPassword();
|
||||
final String port = redisWrapper.getPort();
|
||||
final int database = redisWrapper.getDatabase();
|
||||
|
||||
jedis = new redis.clients.jedis.Jedis(host, Integer.parseInt(port));
|
||||
jedis.auth(password);
|
||||
jedis.select(database);
|
||||
}
|
||||
|
||||
public redis.clients.jedis.Jedis getJedis() {
|
||||
return jedis;
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
jedis.close();
|
||||
}
|
||||
|
||||
public void loadPlayers() {
|
||||
Set<String> playerUUIDs = jedis.keys("*");
|
||||
for (String playerUUID : playerUUIDs) {
|
||||
RedisUser redisUser = new RedisUser(jedis, UUID.fromString(playerUUID));
|
||||
DynamicConfig userConfig = redisUser.loadUserConfig();
|
||||
|
||||
// Create a new PlayerConfig instance with the loaded data
|
||||
PlayerConfig playerConfig = PlayerConfig.fromDynamicConfig(plugin, userConfig);
|
||||
|
||||
// Add the PlayerConfig instance to the map
|
||||
plugin.getConfigMap().put(UUID.fromString(playerUUID), playerConfig);
|
||||
}
|
||||
}
|
||||
|
||||
public void savePlayers() {
|
||||
for (UUID playerUUID : plugin.getConfigMap().keySet()) {
|
||||
PlayerConfig playerConfig = plugin.getConfigMap().get(playerUUID);
|
||||
RedisUser redisUser = new RedisUser(jedis, playerUUID);
|
||||
redisUser.saveUserConfig(playerConfig.toDynamicConfig());
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPlayer(UUID playerUUID) {
|
||||
RedisUser redisUser = new RedisUser(jedis, playerUUID);
|
||||
DynamicConfig userConfig = redisUser.loadUserConfig();
|
||||
|
||||
// Create a new PlayerConfig instance with the loaded data
|
||||
PlayerConfig playerConfig = PlayerConfig.fromDynamicConfig(plugin, userConfig);
|
||||
|
||||
// Add the PlayerConfig instance to the map
|
||||
plugin.getConfigMap().put(playerUUID, playerConfig);
|
||||
}
|
||||
|
||||
public void savePlayer(UUID playerUUID) {
|
||||
PlayerConfig playerConfig = plugin.getConfigMap().get(playerUUID);
|
||||
RedisUser redisUser = new RedisUser(jedis, playerUUID);
|
||||
redisUser.saveUserConfig(playerConfig.toDynamicConfig());
|
||||
}
|
||||
}
|
17
src/main/java/io/github/simplex/sql/SQLType.java
Normal file
17
src/main/java/io/github/simplex/sql/SQLType.java
Normal file
@ -0,0 +1,17 @@
|
||||
package io.github.simplex.sql;
|
||||
|
||||
public enum SQLType {
|
||||
SQLITE,
|
||||
MYSQL,
|
||||
REDIS,
|
||||
NONE;
|
||||
|
||||
public static SQLType fromString(String type) {
|
||||
return switch (type.toLowerCase()) {
|
||||
case "sqlite" -> SQLITE;
|
||||
case "mysql" -> MYSQL;
|
||||
case "redis" -> REDIS;
|
||||
default -> NONE;
|
||||
};
|
||||
}
|
||||
}
|
83
src/main/java/io/github/simplex/sql/SQLite.java
Normal file
83
src/main/java/io/github/simplex/sql/SQLite.java
Normal file
@ -0,0 +1,83 @@
|
||||
package io.github.simplex.sql;
|
||||
|
||||
import io.github.simplex.luck.FeelingLucky;
|
||||
import io.github.simplex.luck.player.DynamicConfig;
|
||||
import io.github.simplex.luck.player.PlayerConfig;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import io.github.simplex.sql.users.SQLiteUser;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SQLite {
|
||||
|
||||
private final Connection connection;
|
||||
private final FeelingLucky plugin;
|
||||
|
||||
public SQLite(final FeelingLucky plugin) throws SQLException {
|
||||
this.plugin = plugin;
|
||||
final String databaseFilePath = plugin.getConfig().getSQLite().getPath();
|
||||
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFilePath);
|
||||
if (connection != null) createTable();
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void closeConnection() throws SQLException {
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void createTable() throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE IF NOT EXISTS PlayerConfig (playerUUID VARCHAR(36), username VARCHAR(16), luckValue DOUBLE, isVerbose BOOLEAN, multiplier DOUBLE)")) {
|
||||
statement.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPlayers() throws SQLException {
|
||||
try (PreparedStatement statement = connection.prepareStatement("SELECT playerUUID FROM PlayerConfig");
|
||||
ResultSet resultSet = statement.executeQuery()) {
|
||||
|
||||
while (resultSet.next()) {
|
||||
UUID playerUUID = UUID.fromString(resultSet.getString("playerUUID"));
|
||||
SQLiteUser sqliteUser = new SQLiteUser(connection, playerUUID);
|
||||
DynamicConfig userConfig = sqliteUser.loadUserConfig();
|
||||
|
||||
// Create a new PlayerConfig instance with the loaded data
|
||||
PlayerConfig playerConfig = PlayerConfig.fromDynamicConfig(plugin, userConfig);
|
||||
|
||||
// Add the PlayerConfig instance to the map
|
||||
plugin.getConfigMap().put(playerUUID, playerConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void savePlayers() throws SQLException {
|
||||
for (UUID playerUUID : plugin.getConfigMap().keySet()) {
|
||||
PlayerConfig playerConfig = plugin.getConfigMap().get(playerUUID);
|
||||
SQLiteUser sqliteUser = new SQLiteUser(connection, playerUUID);
|
||||
sqliteUser.saveUserConfig(playerConfig.toDynamicConfig());
|
||||
}
|
||||
}
|
||||
|
||||
public void loadPlayer(UUID playerUUID) throws SQLException {
|
||||
SQLiteUser sqliteUser = new SQLiteUser(connection, playerUUID);
|
||||
DynamicConfig userConfig = sqliteUser.loadUserConfig();
|
||||
|
||||
// Create a new PlayerConfig instance with the loaded data
|
||||
PlayerConfig playerConfig = PlayerConfig.fromDynamicConfig(plugin, userConfig);
|
||||
|
||||
// Add the PlayerConfig instance to the map
|
||||
plugin.getConfigMap().put(playerUUID, playerConfig);
|
||||
}
|
||||
|
||||
public void savePlayer(UUID playerUUID) throws SQLException {
|
||||
PlayerConfig playerConfig = plugin.getConfigMap().get(playerUUID);
|
||||
SQLiteUser sqliteUser = new SQLiteUser(connection, playerUUID);
|
||||
sqliteUser.saveUserConfig(playerConfig.toDynamicConfig());
|
||||
}
|
||||
}
|
67
src/main/java/io/github/simplex/sql/users/MySQLUser.java
Normal file
67
src/main/java/io/github/simplex/sql/users/MySQLUser.java
Normal file
@ -0,0 +1,67 @@
|
||||
package io.github.simplex.sql.users;
|
||||
|
||||
import io.github.simplex.luck.player.DynamicConfig;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MySQLUser {
|
||||
|
||||
private final Connection connection;
|
||||
private final String playerUUID;
|
||||
|
||||
public MySQLUser(Connection connection, UUID playerUUID) {
|
||||
this.connection = connection;
|
||||
this.playerUUID = playerUUID.toString();
|
||||
}
|
||||
|
||||
public DynamicConfig loadUserConfig() {
|
||||
DynamicConfig config = new DynamicConfig();
|
||||
try {
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM PlayerConfig WHERE playerUUID = ?"
|
||||
);
|
||||
statement.setString(1, playerUUID);
|
||||
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
|
||||
if (resultSet.next()) {
|
||||
config.setPlayerUUID(UUID.fromString(resultSet.getString("uuid")));
|
||||
config.setUsername(Bukkit.getOfflinePlayer(UUID.fromString(resultSet.getString("uuid"))).getName());
|
||||
config.setLuckValue(resultSet.getInt("luckValue"));
|
||||
config.setVerbose(resultSet.getBoolean("isVerbose"));
|
||||
config.setMultiplier(resultSet.getDouble("multiplier"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void saveUserConfig(DynamicConfig config) {
|
||||
try {
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO PlayerConfig (playerUUID, username, luckValue, isVerbose, multiplier) VALUES (?, ?, ?, ?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE username = ?, luckValue = ?, isVerbose = ?, multiplier = ?"
|
||||
);
|
||||
statement.setString(1, playerUUID);
|
||||
statement.setString(2, config.getUsername());
|
||||
statement.setDouble(3, config.getLuckValue());
|
||||
statement.setBoolean(4, config.isVerbose());
|
||||
statement.setDouble(5, config.getMultiplier());
|
||||
statement.setString(6, config.getUsername());
|
||||
statement.setDouble(7, config.getLuckValue());
|
||||
statement.setBoolean(8, config.isVerbose());
|
||||
statement.setDouble(9, config.getMultiplier());
|
||||
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
}
|
37
src/main/java/io/github/simplex/sql/users/RedisUser.java
Normal file
37
src/main/java/io/github/simplex/sql/users/RedisUser.java
Normal file
@ -0,0 +1,37 @@
|
||||
package io.github.simplex.sql.users;
|
||||
|
||||
import io.github.simplex.luck.player.DynamicConfig;
|
||||
import org.bukkit.Bukkit;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class RedisUser {
|
||||
|
||||
private final Jedis jedis;
|
||||
private final String playerUUID;
|
||||
|
||||
public RedisUser(Jedis jedis, UUID playerUUID) {
|
||||
this.jedis = jedis;
|
||||
this.playerUUID = playerUUID.toString();
|
||||
}
|
||||
|
||||
public DynamicConfig loadUserConfig() {
|
||||
DynamicConfig config = new DynamicConfig();
|
||||
config.setPlayerUUID(UUID.fromString(jedis.hget(playerUUID, "uuid")));
|
||||
config.setUsername(Bukkit.getOfflinePlayer(UUID.fromString(jedis.hget(playerUUID, "username"))).getName());
|
||||
config.setLuckValue(Integer.parseInt(jedis.hget(playerUUID, "luckValue")));
|
||||
config.setVerbose(Boolean.parseBoolean(jedis.hget(playerUUID, "isVerbose")));
|
||||
config.setMultiplier(Double.parseDouble(jedis.hget(playerUUID, "multiplier")));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public void saveUserConfig(DynamicConfig config) {
|
||||
jedis.hset(playerUUID, "uuid", config.getPlayerUUID());
|
||||
jedis.hset(playerUUID, "username", config.getUsername());
|
||||
jedis.hset(playerUUID, "luckValue", String.valueOf(config.getLuckValue()));
|
||||
jedis.hset(playerUUID, "isVerbose", String.valueOf(config.isVerbose()));
|
||||
jedis.hset(playerUUID, "multiplier", String.valueOf(config.getMultiplier()));
|
||||
}
|
||||
}
|
65
src/main/java/io/github/simplex/sql/users/SQLiteUser.java
Normal file
65
src/main/java/io/github/simplex/sql/users/SQLiteUser.java
Normal file
@ -0,0 +1,65 @@
|
||||
package io.github.simplex.sql.users;
|
||||
|
||||
import io.github.simplex.luck.player.DynamicConfig;
|
||||
import io.github.simplex.luck.util.Logs;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SQLiteUser {
|
||||
|
||||
private final Connection connection;
|
||||
private final String playerUUID;
|
||||
|
||||
public SQLiteUser(Connection connection, UUID playerUUID) {
|
||||
this.connection = connection;
|
||||
this.playerUUID = playerUUID.toString();
|
||||
}
|
||||
|
||||
public DynamicConfig loadUserConfig() {
|
||||
DynamicConfig config = new DynamicConfig();
|
||||
try {
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM PlayerConfig WHERE playerUUID = ?"
|
||||
);
|
||||
statement.setString(1, playerUUID);
|
||||
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
|
||||
if (resultSet.next()) {
|
||||
config.setPlayerUUID(UUID.fromString(resultSet.getString("playerUUID")));
|
||||
config.setUsername(
|
||||
Bukkit.getOfflinePlayer(
|
||||
UUID.fromString(
|
||||
resultSet.getString("playerUUID")))
|
||||
.getName());
|
||||
config.setLuckValue(resultSet.getDouble("luckValue"));
|
||||
config.setVerbose(resultSet.getBoolean("isVerbose"));
|
||||
config.setMultiplier(resultSet.getDouble("multiplier"));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public void saveUserConfig(DynamicConfig config) {
|
||||
try {
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO PlayerConfig (playerUUID, username, luckValue, isVerbose, multiplier) VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
statement.setString(1, playerUUID);
|
||||
statement.setString(2, config.getUsername());
|
||||
statement.setDouble(3, config.getLuckValue());
|
||||
statement.setBoolean(4, config.isVerbose());
|
||||
statement.setDouble(5, config.getMultiplier());
|
||||
statement.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
Logs.error(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,23 +4,57 @@
|
||||
# These values must be in the form of doubles, as listed below.
|
||||
# The maximum amount of luck that can be attributed is 1024.0, and the minimum is -1024.0
|
||||
|
||||
high_rarity_chance: 512
|
||||
medium_rarity_chance: 128
|
||||
low_rarity_chance: 64
|
||||
high_rarity_chance: 512.0
|
||||
medium_rarity_chance: 128.0
|
||||
low_rarity_chance: 64.0
|
||||
|
||||
# Change whether verbosity is enabled globally or not.
|
||||
# If the verbosity is set to true, then all players will have the option to turn it off for themselves only.
|
||||
# If the verbosity is set to false, then players will not have the option to turn it on.
|
||||
global_verbosity: true
|
||||
|
||||
# The following entries are for the rarity level of each event trigger.
|
||||
# This will determine which rarity chance to use which ensures players
|
||||
# The following values are accepted: NONE, LOW, MED, HIGH
|
||||
# - None implies that there is no rarity chance attributed to that feature.
|
||||
#- None implies that there is no rarity chance attributed to that feature.
|
||||
# These entries are case-sensitive.
|
||||
|
||||
block_drops: LOW
|
||||
bonemeal: LOW
|
||||
bone_meal: MED
|
||||
cheat_death: MED
|
||||
enchanting: HIGH
|
||||
experience: HIGH
|
||||
give_damage: LOW
|
||||
hide_check: MED
|
||||
item_drops: LOW
|
||||
jump_boost: MED
|
||||
ore_vein: HIGH
|
||||
random_effect: HIGH
|
||||
restore_hunger: MED
|
||||
restore_hunger: NONE
|
||||
take_damage: MED
|
||||
unbreakable: HIGH
|
||||
unbreakable: HIGH
|
||||
|
||||
# This section is for Database storage.
|
||||
# If you wish to use a database, you must fill out the following information.
|
||||
|
||||
#database_types: MYSQL, SQLITE, REDIS, NONE
|
||||
database_type: NONE
|
||||
|
||||
# MySQL Database
|
||||
mysql:
|
||||
host: localhost
|
||||
port: 3306
|
||||
database: minecraft
|
||||
username: root
|
||||
password: password
|
||||
|
||||
# SQLite Database
|
||||
sqlite:
|
||||
path: database.db
|
||||
|
||||
# Redis Database
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password: password
|
||||
database: 0
|
||||
|
Loading…
x
Reference in New Issue
Block a user