mirror of
https://github.com/SimplexDevelopment/FeelingLucky.git
synced 2025-07-04 19:16:41 +00:00
Compare commits
36 Commits
Alpha-RC01
...
1.2.0-RC01
Author | SHA1 | Date | |
---|---|---|---|
c3d781f5b6 | |||
e6fe9e904e | |||
c383b2c546 | |||
94b9e12f45 | |||
eb80523edc | |||
355b612732 | |||
67734f3f89 | |||
4232842749 | |||
010fd76031 | |||
c50b222586 | |||
17f83bd9f2 | |||
10d7a4ed98 | |||
73e5be91eb | |||
07c4e5d50c | |||
e13ca55adf | |||
fbd8d10461 | |||
a4d71b2c0b | |||
bb9bdcdf03 | |||
1bf2a818ec | |||
0488e1d6b1 | |||
330fd278b7 | |||
6119180b0b | |||
fe620952bc | |||
5b1fb352bb | |||
b424a83062 | |||
077ed05d74 | |||
8e70e1de73 | |||
899768819e | |||
ea50032a89 | |||
057450c585 | |||
d08b240ff9 | |||
cbc57d5971 | |||
247a2fafc0 | |||
ffdafda69c | |||
0383bfeaf3 | |||
93ee1e2c43 |
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
|
||||||
|
|
||||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -19,6 +19,12 @@
|
|||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.rar
|
*.rar
|
||||||
|
|
||||||
|
# Compiler Files #
|
||||||
|
build/
|
||||||
|
.idea/
|
||||||
|
.gradle/
|
||||||
|
FeelingLucky.iml
|
||||||
|
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
replay_pid*
|
replay_pid*
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +0,0 @@
|
|||||||
#Wed Feb 09 22:19:17 CST 2022
|
|
||||||
gradle.version=7.3.3
|
|
Binary file not shown.
Binary file not shown.
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
6
.idea/compiler.xml
generated
6
.idea/compiler.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<bytecodeTargetLevel target="17" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
17
.idea/gradle.xml
generated
17
.idea/gradle.xml
generated
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
||||||
<component name="GradleSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<GradleProjectSettings>
|
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</GradleProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
54
.idea/inspectionProfiles/Project_Default.xml
generated
54
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,54 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoCipherInsecureAsymmetricCryptographicAlgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoKeyAgreementGuideonApprovedCryptographicAlgorithm" enabled="true" level="SENSEI MARKED INFO" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoKeyAgreementInsecureCryptographicAlgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoKeyPairGenerationApprovedStandardCryptographicAlgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoKeyPairGenerationInsecureCryptographicAlgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoKeyPairGenerationNonStandardCryptographicAlgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoSignatureApprovedHashingAlgorithm" enabled="true" level="SENSEI MARKED INFO" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoSignatureInsecureHashingAlgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_CryptoSignatureNonStandardHashingAlgorithm" enabled="true" level="SENSEI WARNING" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataInjectionParameterizeLDAPFiltersDirContextsearch" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidbruteforcingUsesufficientlylongkeysizeskeyGenerator" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatekeypairgenerationalgorithminsecure" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatekeypairgenerationalgorithmnotrecommended" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithmDESfamily" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithmHmacfamily" enabled="true" level="SENSEI WARNING" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithmHmacfamily1" enabled="true" level="SENSEI WARNING" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithmOtheralgorithms" enabled="true" level="SENSEI MARKED INFO" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithminsecureSecretKeyFactory" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithmnotrecommendedSecretKeyFactory" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUseappropriatesecretkeygenerationalgorithmotherSecretKeyFactory" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUsestrongsymmetriccryptographicalgorithm" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUsesufficientlylongkeysizeskeyGeneratorbadvalue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUsesufficientlylongkeysizeskeyPairGenerator" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-CryptographyAvoidcryptographicweaknessUsesufficientlylongkeysizeskeyPairGeneratorbadvalue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_DataProtection-SecureDataStorageAvoiddataexposureUseCipherinsteadofNullCipher" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidCodeInjectionUseSafeConstructor1stargumentoftypeConstructor" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidCodeInjectionUseSafeConstructorargumentsbutnoConstructorargument" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidCodeInjectionUseSafeConstructornoarguments" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidSQLInjectionUseParameterizedQueriesPreparedStatement" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidSQLInjectionUseParameterizedQueriesStatement" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidXMLInjectionUsesetFeature" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidXMLInjectionUsesetSchema" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InjectionAvoidXMLInjectionsetFeaturewithbadvalue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InputValidationAvoidXXEDonotsetDocumentBuilderFactoryexternal-parameter-entitiestotrue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InputValidationAvoidXXEDonotsetDocumentBuilderFactoryload-external-dtdtotrue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InputValidationAvoidXXEDonotsetDocumentBuilderFactorysetExpandEntityReferencestotrue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InputValidationAvoidXXEDonotsetDocumentBuilderFactorysetXIncludeAwaretotrue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_InputValidationAvoidXXEDonotsetXMLInputFactoryPropertytotrue" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_PortabilityFlawAvoidlocaledependentcomparisonsequalsaftercaseconversion" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_TLSWeakEncryptionInsecureVersion" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_TLSWeakEncryptionOutdatedVersion" enabled="true" level="SENSEI WARNING" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesDocumentBuilderFactorysetExpandEntityReferencestofalse" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesDocumentBuilderFactorysetFeaturedissallow-doctype-decl" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesDocumentBuilderFactorysetFeaturedissallow-doctype-declwrongboolean" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesDocumentBuilderFactorysetFeatureexternal-parameter-entitiesshouldbesetfirst" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesDocumentBuilderFactorysetFeatureload-external-dtd" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesDocumentBuilderFactorysetXIncludeAware" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesXMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
<inspection_tool class="fcd36335-ac86-4bef-8d30-062d8aae0364_XMLExternalEntitiesXMLInputFactory.SUPPORT_DTD" enabled="true" level="SENSEI ERROR" enabled_by_default="true" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
45
.idea/jarRepositories.xml
generated
45
.idea/jarRepositories.xml
generated
@ -1,45 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RemoteRepositoriesConfiguration">
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="central" />
|
|
||||||
<option name="name" value="Maven Central repository" />
|
|
||||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="jboss.community" />
|
|
||||||
<option name="name" value="JBoss Community repository" />
|
|
||||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="MavenRepo" />
|
|
||||||
<option name="name" value="MavenRepo" />
|
|
||||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="papermc-repo" />
|
|
||||||
<option name="name" value="papermc-repo" />
|
|
||||||
<option name="url" value="https://papermc.io/repo/repository/maven-public/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="sonatype" />
|
|
||||||
<option name="name" value="sonatype" />
|
|
||||||
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="sonatype" />
|
|
||||||
<option name="name" value="sonatype" />
|
|
||||||
<option name="url" value="file:/$PROJECT_DIR$/%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20url%20=%20uri(%22https:/s01.oss.sonatype.org/service/local/staging/deploy/maven2/%22)%0A" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="sonatype" />
|
|
||||||
<option name="name" value="sonatype" />
|
|
||||||
<option name="url" value="https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" />
|
|
||||||
</remote-repository>
|
|
||||||
<remote-repository>
|
|
||||||
<option name="id" value="sonatype" />
|
|
||||||
<option name="name" value="sonatype" />
|
|
||||||
<option name="url" value="https://s01.oss.sonatype.org/content/groups/public/" />
|
|
||||||
</remote-repository>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
12
.idea/misc.xml
generated
12
.idea/misc.xml
generated
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="EntryPointsManager">
|
|
||||||
<list size="1">
|
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
|
|
||||||
</list>
|
|
||||||
</component>
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="openjdk-17" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
13
.idea/modules/Crumb.main.iml
generated
13
.idea/modules/Crumb.main.iml
generated
@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module version="4">
|
|
||||||
<component name="FacetManager">
|
|
||||||
<facet type="minecraft" name="Minecraft">
|
|
||||||
<configuration>
|
|
||||||
<autoDetectTypes>
|
|
||||||
<platformType>PAPER</platformType>
|
|
||||||
<platformType>ADVENTURE</platformType>
|
|
||||||
</autoDetectTypes>
|
|
||||||
</configuration>
|
|
||||||
</facet>
|
|
||||||
</component>
|
|
||||||
</module>
|
|
124
.idea/uiDesigner.xml
generated
124
.idea/uiDesigner.xml
generated
@ -1,124 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Palette2">
|
|
||||||
<group name="Swing">
|
|
||||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
|
||||||
</item>
|
|
||||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
|
||||||
<initial-values>
|
|
||||||
<property name="text" value="Button" />
|
|
||||||
</initial-values>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
|
||||||
<initial-values>
|
|
||||||
<property name="text" value="RadioButton" />
|
|
||||||
</initial-values>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
|
||||||
<initial-values>
|
|
||||||
<property name="text" value="CheckBox" />
|
|
||||||
</initial-values>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
|
||||||
<initial-values>
|
|
||||||
<property name="text" value="Label" />
|
|
||||||
</initial-values>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
|
||||||
<preferred-size width="150" height="-1" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
|
||||||
<preferred-size width="150" height="-1" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
|
||||||
<preferred-size width="150" height="-1" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
|
||||||
<preferred-size width="150" height="50" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
|
||||||
<preferred-size width="150" height="50" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
|
||||||
<preferred-size width="150" height="50" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
|
||||||
<preferred-size width="150" height="50" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
|
||||||
<preferred-size width="150" height="50" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
|
||||||
<preferred-size width="150" height="50" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
|
||||||
<preferred-size width="200" height="200" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
|
||||||
<preferred-size width="200" height="200" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
|
||||||
<preferred-size width="-1" height="20" />
|
|
||||||
</default-constraints>
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
|
||||||
</item>
|
|
||||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
|
||||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
|
||||||
</item>
|
|
||||||
</group>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="JAVA_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
28
README.md
Normal file
28
README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# <center>FeelingLucky Beta v1.0.2</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.
|
||||||
|
|
||||||
|
## <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, and support for Spigot will not be added in the future.</center></span>
|
||||||
|
|
||||||
|
### <center>Note: If you are migrating from an Alpha build, the plugin configuration folder will need to be regenerated.</center>
|
||||||
|
|
17
build.gradle
17
build.gradle
@ -3,22 +3,16 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = 'io.github.simplex'
|
group = 'io.github.simplex'
|
||||||
version = '1.0-SNAPSHOT'
|
version = '1.2.0-RC01'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
maven { url = uri("https://s01.oss.sonatype.org/content/groups/public/") }
|
||||||
name = 'papermc-repo'
|
maven { url = uri("https://papermc.io/repo/repository/maven-public/")}
|
||||||
url = 'https://papermc.io/repo/repository/maven-public/'
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
name = 'sonatype'
|
|
||||||
url = 'https://s01.oss.sonatype.org/content/groups/public/'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly 'io.papermc.paper:paper-api:1.18.1-R0.1-SNAPSHOT'
|
compileOnly("io.papermc.paper:paper-api:1.19-R0.1-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
def targetJavaVersion = 17
|
def targetJavaVersion = 17
|
||||||
@ -29,6 +23,9 @@ java {
|
|||||||
if (JavaVersion.current() < javaVersion) {
|
if (JavaVersion.current() < javaVersion) {
|
||||||
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
|
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withSourcesJar();
|
||||||
|
withJavadocJar();
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
name: Crumb
|
|
||||||
version: '1.0-SNAPSHOT'
|
|
||||||
main: io.github.simplex.crumb.Crumb
|
|
||||||
api-version: 1.18
|
|
Binary file not shown.
@ -1,2 +0,0 @@
|
|||||||
Manifest-Version: 1.0
|
|
||||||
|
|
@ -1 +1 @@
|
|||||||
rootProject.name = 'Crumb'
|
rootProject.name = 'FeelingLucky'
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package io.github.simplex.api;
|
package io.github.simplex.api;
|
||||||
|
|
||||||
import org.bukkit.attribute.Attribute;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
public interface LuckContainer extends Serializable {
|
public interface LuckContainer extends Serializable {
|
||||||
Attribute asAttribute();
|
|
||||||
|
|
||||||
double getNumber();
|
|
||||||
|
|
||||||
boolean isMatch(double number);
|
boolean isMatch(double number);
|
||||||
|
|
||||||
@ -18,5 +14,5 @@ public interface LuckContainer extends Serializable {
|
|||||||
|
|
||||||
Player associatedPlayer();
|
Player associatedPlayer();
|
||||||
|
|
||||||
double baseValue();
|
double getValue();
|
||||||
}
|
}
|
||||||
|
75
src/main/java/io/github/simplex/lib/ItemBuilder.java
Normal file
75
src/main/java/io/github/simplex/lib/ItemBuilder.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package io.github.simplex.lib;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class ItemBuilder {
|
||||||
|
private final ItemStack stack;
|
||||||
|
private final ItemMeta meta;
|
||||||
|
|
||||||
|
public ItemBuilder(Material material) {
|
||||||
|
this.stack = new ItemStack(material);
|
||||||
|
this.meta = stack.getItemMeta();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static ItemBuilder of(Material material) {
|
||||||
|
return new ItemBuilder(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
public static boolean isTool(Material m) {
|
||||||
|
String name = m.getKey().getKey();
|
||||||
|
|
||||||
|
return m.equals(Material.SHEARS)
|
||||||
|
|| name.endsWith("bow")
|
||||||
|
|| name.endsWith("pickaxe")
|
||||||
|
|| name.endsWith("axe")
|
||||||
|
|| name.endsWith("sword")
|
||||||
|
|| name.endsWith("shovel")
|
||||||
|
|| name.endsWith("hoe");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemBuilder setName(String name) {
|
||||||
|
meta.displayName(MiniComponent.of(name).send());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemBuilder setAmount(int amount) {
|
||||||
|
stack.setAmount(amount);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemBuilder setLore(String... lore) {
|
||||||
|
List<Component> components = new ArrayList<>();
|
||||||
|
Arrays.stream(lore).forEach(entry -> components.add(MiniComponent.of(entry).send()));
|
||||||
|
meta.lore(components);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack build() {
|
||||||
|
stack.setItemMeta(meta);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component component(String content, TextColor color, TextDecoration decoration) {
|
||||||
|
return Component.empty().content(content).decorate(decoration).color(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component component(String content, TextColor color) {
|
||||||
|
return Component.empty().content(content).color(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Component component(String content) {
|
||||||
|
return Component.empty().content(content);
|
||||||
|
}
|
||||||
|
}
|
21
src/main/java/io/github/simplex/lib/Messages.java
Normal file
21
src/main/java/io/github/simplex/lib/Messages.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package io.github.simplex.lib;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
private final Component message;
|
||||||
|
|
||||||
|
Messages(Component message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component get() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
59
src/main/java/io/github/simplex/lib/MiniComponent.java
Normal file
59
src/main/java/io/github/simplex/lib/MiniComponent.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package io.github.simplex.lib;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.TextColor;
|
||||||
|
import net.kyori.adventure.text.format.TextDecoration;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
public class MiniComponent {
|
||||||
|
private final String content;
|
||||||
|
private TextDecoration decoration = null;
|
||||||
|
private TextColor color = null;
|
||||||
|
|
||||||
|
public MiniComponent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static MiniComponent of(String content) {
|
||||||
|
return new MiniComponent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static Component info(String content) {
|
||||||
|
return new MiniComponent(content).color(ChatColor.GREEN).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static Component warn(String content) {
|
||||||
|
return new MiniComponent(content).color(ChatColor.YELLOW).decorate(TextDecoration.ITALIC).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static Component err(String content) {
|
||||||
|
return new MiniComponent(content).color(ChatColor.RED).decorate(TextDecoration.BOLD).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MiniComponent decorate(TextDecoration decoration) {
|
||||||
|
this.decoration = decoration;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MiniComponent color(ChatColor color) {
|
||||||
|
this.color = TextColor.color(color.asBungee().getColor().getRGB());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component send() {
|
||||||
|
if (color == null) {
|
||||||
|
if (decoration == null) return Component.empty().content(content);
|
||||||
|
|
||||||
|
return Component.empty().content(content).decoration(decoration, TextDecoration.State.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoration == null) return Component.empty().content(content).color(color);
|
||||||
|
|
||||||
|
return Component.empty().content(content).decorate(decoration).color(color);
|
||||||
|
}
|
||||||
|
}
|
89
src/main/java/io/github/simplex/luck/Config.java
Normal file
89
src/main/java/io/github/simplex/luck/Config.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package io.github.simplex.luck;
|
||||||
|
|
||||||
|
import io.github.simplex.luck.listener.AbstractListener;
|
||||||
|
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.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
public class Config extends YamlConfiguration {
|
||||||
|
private final FeelingLucky plugin;
|
||||||
|
private final 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");
|
||||||
|
}};
|
||||||
|
private File configFile;
|
||||||
|
|
||||||
|
public Config(FeelingLucky plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
File dataFolder = plugin.getDataFolder();
|
||||||
|
if (!dataFolder.exists()) dataFolder.mkdirs();
|
||||||
|
File configFile = new File(dataFolder, "config.yml");
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
SneakyWorker.sneakyTry(configFile::createNewFile);
|
||||||
|
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(this.configFile.getPath()));
|
||||||
|
newFile.createNewFile();
|
||||||
|
plugin.saveResource("config.yml", true);
|
||||||
|
});
|
||||||
|
this.configFile = newFile;
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
SneakyWorker.sneakyTry(() -> save(configFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() {
|
||||||
|
SneakyWorker.sneakyTry(() -> load(configFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractListener.Rarity getRarity(String name) {
|
||||||
|
return AbstractListener.Rarity.valueOf(getString(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getChance(String path) {
|
||||||
|
return getDouble(path);
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +1,109 @@
|
|||||||
package io.github.simplex.luck;
|
package io.github.simplex.luck;
|
||||||
|
|
||||||
|
import io.github.simplex.luck.listener.*;
|
||||||
import io.github.simplex.luck.player.PlayerConfig;
|
import io.github.simplex.luck.player.PlayerConfig;
|
||||||
import io.github.simplex.luck.player.PlayerHandler;
|
import io.github.simplex.luck.player.PlayerHandler;
|
||||||
import org.bukkit.Bukkit;
|
import io.github.simplex.luck.util.LuckCMD;
|
||||||
|
import io.github.simplex.luck.util.SneakyWorker;
|
||||||
|
import io.github.simplex.luck.util.SpecialFootItem;
|
||||||
|
import io.github.simplex.metrics.Metrics;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public final class FeelingLucky extends JavaPlugin {
|
public final class FeelingLucky extends JavaPlugin {
|
||||||
private static final Map<UUID, PlayerConfig> configMap = new HashMap<>();
|
private final Map<UUID, PlayerConfig> configMap = new HashMap<>();
|
||||||
public PlayerHandler handler;
|
private final File playerDirectory = new File(getDataFolder(), "players");
|
||||||
|
private final SpecialFootItem specialFootItem = new SpecialFootItem();
|
||||||
|
|
||||||
public static Map<UUID, PlayerConfig> getConfigMap() {
|
private PlayerHandler handler;
|
||||||
|
private Config config;
|
||||||
|
|
||||||
|
public Map<UUID, PlayerConfig> getConfigMap() {
|
||||||
return configMap;
|
return configMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
Bukkit.getLogger().info("Initializing the PlayerHandler...");
|
getLogger().info("Initializing metrics...");
|
||||||
|
new Metrics(this, 15054);
|
||||||
|
getLogger().info("Metrics loaded. Initializing the PlayerHandler...");
|
||||||
handler = new PlayerHandler(this);
|
handler = new PlayerHandler(this);
|
||||||
Bukkit.getLogger().info("Initialization complete! Attempting to register the handler...");
|
getLogger().info("Initialization complete! Attempting to register the Listeners...");
|
||||||
this.getServer().getPluginManager().registerEvents(handler, this);
|
registerListeners();
|
||||||
Bukkit.getLogger().info("Registration complete! Attempting to load all player configuration files...");
|
getLogger().info("Registration complete! Attempting to load all player configuration files...");
|
||||||
|
loadPlayerConfigurations();
|
||||||
|
getLogger().info("Attempting to load the main configuration...");
|
||||||
|
config = new Config(this);
|
||||||
|
getLogger().info("Main Config loaded successfully! Attempting to load the Luck command...");
|
||||||
|
new LuckCMD(this);
|
||||||
|
getLogger().info("Successfully loaded the Luck command!");
|
||||||
|
|
||||||
File[] files = getDataFolder().listFiles();
|
getLogger().info("Successfully initialized!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
getLogger().info("Saving all player configurations...");
|
||||||
|
configMap.values().forEach(PlayerConfig::save);
|
||||||
|
getLogger().info("Complete! Saving the main config...");
|
||||||
|
config.save();
|
||||||
|
getLogger().info("Complete! Goodbye! :)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlayerConfigurations() {
|
||||||
|
if (!playerDirectory.exists()) {
|
||||||
|
getLogger().info("No directory exists. Creating...");
|
||||||
|
playerDirectory.mkdirs();
|
||||||
|
getLogger().info("Created new directory \"FeelingLucky/players\".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] files = playerDirectory.listFiles();
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
Arrays.stream(files).forEach(file -> {
|
Arrays.stream(files).forEach(file -> {
|
||||||
UUID uuid = UUID.fromString(file.getName().split("\\.")[0]);
|
UUID uuid = UUID.fromString(file.getName().split("\\.")[0]);
|
||||||
configMap.put(uuid, PlayerConfig.loadFrom(file));
|
configMap.put(uuid, PlayerConfig.initFrom(this, file));
|
||||||
});
|
});
|
||||||
configMap.forEach((u, pc) -> pc.load());
|
configMap.forEach((u, pc) -> pc.load());
|
||||||
getLogger().info("Successfully loaded all configurations!");
|
getLogger().info("Successfully loaded all configurations!");
|
||||||
} else {
|
} else {
|
||||||
getLogger().info("There are no player configurations to load.");
|
getLogger().info("There are no player configurations to load.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Bukkit.getLogger().info("Successfully initialized!");
|
private void registerListeners() {
|
||||||
|
try {
|
||||||
|
Class<?>[] listeners = SneakyWorker.getClasses(AbstractListener.class.getPackage().getName());
|
||||||
|
Arrays.stream(listeners).forEach(l -> {
|
||||||
|
if (AbstractListener.class.isAssignableFrom(l)) {
|
||||||
|
if (l.equals(AbstractListener.class)) return;
|
||||||
|
|
||||||
|
SneakyWorker.sneakyTry(() -> l.getDeclaredConstructor(FeelingLucky.class).newInstance(this));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException | ClassNotFoundException ex) {
|
||||||
|
getLogger().severe(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerHandler getHandler() {
|
||||||
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
@NotNull
|
||||||
Bukkit.getLogger().info("Saving all player configurations...");
|
public Config getConfig() {
|
||||||
configMap.forEach((u, pc) -> pc.save());
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpecialFootItem getFoot() {
|
||||||
|
return specialFootItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
package io.github.simplex.luck;
|
|
||||||
|
|
||||||
import io.github.simplex.luck.player.Luck;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Item;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
|
|
||||||
public class SneakyWorker {
|
|
||||||
public static void sneakyTry(SneakyTry sneakyTry) {
|
|
||||||
try {
|
|
||||||
sneakyTry.tryThis();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
String sb = "An error of type: "
|
|
||||||
+ ex.getClass().getSimpleName()
|
|
||||||
+ " has occurred. A cause will be printed. \n\n"
|
|
||||||
+ ex.getCause();
|
|
||||||
Bukkit.getLogger().severe(sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void quietTry(SneakyTry sneakyTry) {
|
|
||||||
try {
|
|
||||||
sneakyTry.tryThis();
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void move(Item item) {
|
|
||||||
ItemStack stack = item.getItemStack();
|
|
||||||
int rng = (Luck.RNG().nextInt(2, 5)) + stack.getAmount();
|
|
||||||
stack.setAmount(rng);
|
|
||||||
item.setItemStack(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface SneakyTry {
|
|
||||||
void tryThis() throws Exception;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,37 @@
|
|||||||
|
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 org.bukkit.event.Listener;
|
||||||
|
|
||||||
|
public abstract class AbstractListener implements Listener {
|
||||||
|
protected final FeelingLucky plugin;
|
||||||
|
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 boolean doesQualify(String name, double luck) {
|
||||||
|
return switch (config.getRarity(name)) {
|
||||||
|
case HIGH -> luck > config.getChance("high_rarity_chance");
|
||||||
|
case MED -> luck > config.getChance("medium_rarity_chance");
|
||||||
|
case LOW -> luck > config.getChance("low_rarity_chance");
|
||||||
|
case NONE -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Rarity {
|
||||||
|
HIGH,
|
||||||
|
MED,
|
||||||
|
LOW,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
import io.github.simplex.luck.util.SneakyWorker;
|
||||||
|
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) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void extraBlockDrops(BlockDropItemEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Luck luck = getHandler().getLuckContainer(player);
|
||||||
|
List<Item> items = event.getItems();
|
||||||
|
if (luck.quickRNG(luck.getValue()) && doesQualify("block_drops", luck.getValue())) {
|
||||||
|
event.getItems().clear();
|
||||||
|
event.getItems().addAll(items.stream().map(SneakyWorker::move).toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
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 org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.data.Ageable;
|
||||||
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
public final class BonemealFullCrop extends AbstractListener {
|
||||||
|
public BonemealFullCrop(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void bonemealFullCrop(PlayerInteractEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Action action = event.getAction();
|
||||||
|
ItemStack bonemeal = ItemBuilder.of(Material.BONE_MEAL).build();
|
||||||
|
Luck luck = getHandler().getLuckContainer(player);
|
||||||
|
|
||||||
|
ItemStack handItem = event.getItem();
|
||||||
|
if (handItem == null) return;
|
||||||
|
|
||||||
|
Block block = event.getClickedBlock();
|
||||||
|
if (block == null) return;
|
||||||
|
|
||||||
|
BlockData data = block.getBlockData();
|
||||||
|
|
||||||
|
if (action.isRightClick()
|
||||||
|
&& handItem.isSimilar(bonemeal)
|
||||||
|
&& (data instanceof Ageable crop)
|
||||||
|
&& 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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
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.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
|
|
||||||
|
public final class CheatDeath extends AbstractListener {
|
||||||
|
public CheatDeath(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
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.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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
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) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
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.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()) {
|
||||||
|
enchMap.replace(particular, particular.getMaxLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
enchMap.replace(particular, enchMap.get(particular) + rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FeelingLucky plugin() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/io/github/simplex/luck/listener/ExpBoost.java
Normal file
27
src/main/java/io/github/simplex/luck/listener/ExpBoost.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent;
|
||||||
|
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) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
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.getValue()) && doesQualify("experience", luck.getValue())) {
|
||||||
|
orb.setExperience(rounded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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())) {
|
||||||
|
e.setDamage(nextDmg);
|
||||||
|
player.sendMessage(MiniComponent.info("Your luck has increased your damage output!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
src/main/java/io/github/simplex/luck/listener/HideCheck.java
Normal file
59
src/main/java/io/github/simplex/luck/listener/HideCheck.java
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
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.Entity;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class HideCheck extends AbstractListener {
|
||||||
|
public Map<Player, List<Entity>> entityMapList = new HashMap<>();
|
||||||
|
|
||||||
|
public HideCheck(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void initPlayerMaps(PlayerJoinEvent event) {
|
||||||
|
entityMapList.put(event.getPlayer(), List.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void checkTargeting(EntityTargetLivingEntityEvent event) {
|
||||||
|
if (event.getTarget() instanceof Player player) {
|
||||||
|
if (event.getEntity() instanceof LivingEntity entity) {
|
||||||
|
List<Entity> buffer = entityMapList.get(player).isEmpty() ?
|
||||||
|
new ArrayList<>() : entityMapList.get(player);
|
||||||
|
buffer.add(entity);
|
||||||
|
entityMapList.replace(player, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()) && !luck.isMarked(player)) {
|
||||||
|
entityMapList.get(player).forEach(e -> {
|
||||||
|
e.getTrackedPlayers().remove(player);
|
||||||
|
});
|
||||||
|
player.sendMessage(MiniComponent.info("Your luck has hidden you from sight."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void removePlayerOnLeave(PlayerQuitEvent event) {
|
||||||
|
entityMapList.remove(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
81
src/main/java/io/github/simplex/luck/listener/IllOmen.java
Normal file
81
src/main/java/io/github/simplex/luck/listener/IllOmen.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
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.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.EntityPotionEffectEvent;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
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) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void reconnectCheck(PlayerJoinEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
PotionEffectType type = PotionEffectType.BAD_OMEN;
|
||||||
|
Luck luck = getHandler().getLuckContainer(player);
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
luck.restore();
|
||||||
|
player.sendMessage("The -25% debuff to your luck has been removed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
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) {
|
||||||
|
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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
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.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)) {
|
||||||
|
luck.restore();
|
||||||
|
player.sendMessage("The -25% debuff to your luck has been removed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void disconnectCheck(PlayerQuitEvent event) {
|
||||||
|
if (event.getPlayer().hasPotionEffect(PotionEffectType.BAD_OMEN)) {
|
||||||
|
Luck luck = plugin.getHandler().getLuckContainer(event.getPlayer());
|
||||||
|
if (luck.cached(event.getPlayer())) {
|
||||||
|
luck.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/main/java/io/github/simplex/luck/listener/ItemDrops.java
Normal file
74
src/main/java/io/github/simplex/luck/listener/ItemDrops.java
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
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 {
|
||||||
|
private final Map<UUID, Player> entityPlayerMap = new HashMap<>();
|
||||||
|
private boolean canAffect = false;
|
||||||
|
|
||||||
|
public ItemDrops(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void checkForPreItemDrop(EntityDamageByEntityEvent event) {
|
||||||
|
if (!(event.getEntity() instanceof LivingEntity entity)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(event.getDamager() instanceof Player player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(entity.getHealth() <= 0.0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entityPlayerMap.put(entity.getUniqueId(), player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void checkForDroppedItems(EntityDeathEvent event) {
|
||||||
|
if (event.getEntity() instanceof Player) {
|
||||||
|
canAffect = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canAffect = event.getDrops().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void itemDrops(EntityDropItemEvent event) {
|
||||||
|
Entity entity = event.getEntity();
|
||||||
|
|
||||||
|
if (entityPlayerMap.get(entity.getUniqueId()) == null) 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.getValue()) && doesQualify("item_drops", luck.getValue())) {
|
||||||
|
int rng = Luck.RNG().nextInt(2, 5);
|
||||||
|
amount += rng;
|
||||||
|
stack.setAmount(amount);
|
||||||
|
event.getItemDrop().setItemStack(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/main/java/io/github/simplex/luck/listener/JumpBoost.java
Normal file
26
src/main/java/io/github/simplex/luck/listener/JumpBoost.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
if (luck.quickRNG(luck.getValue()) && !luck.isMarked(player)) {
|
||||||
|
player.setVelocity(new Vector(0, 2, 0));
|
||||||
|
player.sendMessage(MiniComponent.info("Your luck has boosted your jump height!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/main/java/io/github/simplex/luck/listener/OreVein.java
Normal file
51
src/main/java/io/github/simplex/luck/listener/OreVein.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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.Tag;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class OreVein extends AbstractListener {
|
||||||
|
|
||||||
|
public OreVein(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void playerMine(BlockBreakEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||||
|
if (luck.quickRNG(luck.getValue()) && event.getBlock().isValidTool(player.getInventory().getItemInMainHand())) {
|
||||||
|
getOresInArea(event.getBlock()).forEach(Block::breakNaturally);
|
||||||
|
player.sendMessage(MiniComponent.info("Your luck has let you mine all the blocks with one swing."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<Block> getOresInArea(Block block) {
|
||||||
|
Stream.Builder<Block> streamBuilder = Stream.builder();
|
||||||
|
Location start = block.getLocation();
|
||||||
|
World world = block.getWorld();
|
||||||
|
Stream<Tag<Material>> materialStream = Stream.of(Tag.COAL_ORES, Tag.COPPER_ORES, Tag.DIAMOND_ORES, Tag.GOLD_ORES, Tag.IRON_ORES, Tag.EMERALD_ORES, Tag.LAPIS_ORES, Tag.REDSTONE_ORES);
|
||||||
|
for (int x = start.getBlockX() - 15; x <= start.getBlockX() + 15; x++) {
|
||||||
|
for (int y = start.getBlockY() - 15; y <= start.getBlockY() + 15; y++) {
|
||||||
|
for (int z = start.getBlockZ() - 15; z <= start.getBlockZ() + 15; z++) {
|
||||||
|
Location location = new Location(world, x, y, z);
|
||||||
|
Material blockType = location.getBlock().getType();
|
||||||
|
if (materialStream.anyMatch(o -> o.isTagged(blockType))) {
|
||||||
|
streamBuilder.add(location.getBlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return streamBuilder.build().filter(b -> b.getType().equals(block.getType()));
|
||||||
|
}
|
||||||
|
}
|
@ -1,207 +1,80 @@
|
|||||||
package io.github.simplex.luck.listener;
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
import io.github.simplex.lib.PotionEffectBuilder;
|
import io.github.simplex.lib.MiniComponent;
|
||||||
import io.github.simplex.luck.FeelingLucky;
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
import io.github.simplex.luck.ListBox;
|
|
||||||
import io.github.simplex.luck.SneakyWorker;
|
|
||||||
import io.github.simplex.luck.player.Luck;
|
import io.github.simplex.luck.player.Luck;
|
||||||
import io.github.simplex.luck.player.PlayerHandler;
|
import io.github.simplex.luck.util.CooldownTimer;
|
||||||
import net.kyori.adventure.text.Component;
|
import io.github.simplex.luck.util.SpecialFootItem;
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.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.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
import org.bukkit.event.block.BlockDropItemEvent;
|
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
|
|
||||||
import org.bukkit.event.entity.EntityDropItemEvent;
|
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.potion.PotionEffect;
|
|
||||||
import org.bukkit.potion.PotionEffectType;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.Objects;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public record PlayerListener(FeelingLucky plugin) implements Listener {
|
public final class PlayerListener extends AbstractListener {
|
||||||
|
private final CooldownTimer timer;
|
||||||
|
|
||||||
private static final Map<UUID, Player> entityPlayerMap = new HashMap<>();
|
public PlayerListener(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
@EventHandler
|
this.timer = new CooldownTimer();
|
||||||
public void takeDamage(EntityDamageEvent event) {
|
|
||||||
Entity entity = event.getEntity();
|
|
||||||
if (!(entity instanceof Player)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Player player = (Player) event.getEntity();
|
|
||||||
Luck luck = PlayerHandler.getLuckContainer(player);
|
|
||||||
if (ListBox.acceptedCauses.contains(event.getCause())) {
|
|
||||||
if (luck.notDefault()) {
|
|
||||||
double percentage = luck.getPercentage();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a player's luck stat is a negative number, or they are "marked",
|
|
||||||
* this will trigger instead of the regular luck spin.
|
|
||||||
*/
|
|
||||||
if (percentage < 0 || PlayerHandler.isMarked(player)) {
|
|
||||||
percentage = Math.abs(percentage);
|
|
||||||
if (luck.quickRNG(percentage)) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
player.damage(event.getDamage() * 2);
|
|
||||||
player.sendMessage(Component.empty().content("You were unlucky and took double damage."));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (luck.quickRNG(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 a player's luck stat is a negative number, or they are "marked",
|
|
||||||
* this will trigger instead of the regular luck spin.
|
|
||||||
*/
|
|
||||||
if (percentage < 0 || PlayerHandler.isMarked(player)) {
|
|
||||||
percentage = Math.abs(percentage);
|
|
||||||
if (luck.quickRNG(percentage)) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
player.addPotionEffect(PotionEffectBuilder.newEffect().type(ListBox.potionEffects.get(Luck.RNG().nextInt(ListBox.potionEffects.size() - 1))).amplifier(Luck.RNG().nextInt(1, 5)).duration(Luck.RNG().nextInt(1, 120)).create());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (luck.quickRNG(percentage)) {
|
|
||||||
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."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void extraBlockDrops(BlockDropItemEvent event) {
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
Luck luck = PlayerHandler.getLuckContainer(player);
|
|
||||||
List<Item> items = event.getItems();
|
|
||||||
if (luck.quickRNG(luck.getPercentage())) {
|
|
||||||
items.forEach(SneakyWorker::move);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void checkForPreItemDrop(EntityDamageByEntityEvent event) {
|
|
||||||
if (!(event.getEntity() instanceof LivingEntity entity)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(event.getDamager() instanceof Player player)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(entity.getHealth() <= 0.0)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity instanceof Witch witch) {
|
|
||||||
if (Luck.quickRNG2(33.0)) {
|
|
||||||
Location location = witch.getLocation();
|
|
||||||
World world = location.getWorld();
|
|
||||||
Item item = world.dropItemNaturally(location, new ItemStack(Material.RABBIT_FOOT, 1));
|
|
||||||
new EntityDropItemEvent(witch, item).callEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entityPlayerMap.put(entity.getUniqueId(), player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void itemDrops(EntityDropItemEvent event) {
|
|
||||||
Entity entity = event.getEntity();
|
|
||||||
|
|
||||||
if (entityPlayerMap.get(entity.getUniqueId()) == null) return;
|
|
||||||
|
|
||||||
Player player = entityPlayerMap.get(entity.getUniqueId());
|
|
||||||
Luck luck = PlayerHandler.getLuckContainer(player);
|
|
||||||
Item item = event.getItemDrop();
|
|
||||||
ItemStack stack = item.getItemStack();
|
|
||||||
int amount = stack.getAmount();
|
|
||||||
if (luck.quickRNG(luck.getPercentage())) {
|
|
||||||
int rng = Luck.RNG().nextInt(2, 5);
|
|
||||||
amount += rng;
|
|
||||||
stack.setAmount(amount);
|
|
||||||
event.getItemDrop().setItemStack(stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void restoreHunger(PlayerItemConsumeEvent event) {
|
|
||||||
ItemStack item = event.getItem();
|
|
||||||
Luck luck = PlayerHandler.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)) {
|
|
||||||
event.getPlayer().setExhaustion(event.getPlayer().getExhaustion() + 2);
|
|
||||||
event.getPlayer().addPotionEffect(effect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void rabbitFoot(PlayerInteractEvent event) {
|
public void rabbitFoot(PlayerInteractEvent event) {
|
||||||
Action action = event.getAction();
|
Action action = event.getAction();
|
||||||
ItemStack foot = new ItemStack(Material.RABBIT_FOOT);
|
ItemStack foot = new ItemStack(Material.RABBIT_FOOT);
|
||||||
|
SpecialFootItem special = plugin.getFoot();
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
Luck luck = PlayerHandler.getLuckContainer(player);
|
Luck luck = getHandler().getLuckContainer(player);
|
||||||
if (action.isRightClick() && player.getInventory().getItemInMainHand().isSimilar(foot)) {
|
|
||||||
|
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."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.isRightClick() && player.getInventory().getItemInMainHand().getType().equals(foot.getType())) {
|
||||||
|
if (foot.getItemMeta().equals(special.meta()) || foot.equals(special.get())) {
|
||||||
|
luck.setMultiplier(luck.multiplier() + 0.1);
|
||||||
|
player.sendMessage(MiniComponent.info("Your luck multiplier has increased by 0.1!"));
|
||||||
|
}
|
||||||
double rng = Luck.RNG().nextDouble(2.0, 5.0);
|
double rng = Luck.RNG().nextDouble(2.0, 5.0);
|
||||||
player.getInventory().remove(player.getInventory().getItemInMainHand());
|
player.getInventory().remove(player.getInventory().getItemInMainHand());
|
||||||
luck.addTo(rng);
|
luck.addTo(rng);
|
||||||
|
plugin.getHandler().updatePlayer(player, luck);
|
||||||
player.sendMessage(Component.empty().content("Your luck has been increased by " + rng + " points."));
|
timer.setCooldown(player.getUniqueId(), System.currentTimeMillis());
|
||||||
|
player.sendMessage(MiniComponent.info("Your luck has been increased by " + rng + " points."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void witchesBrew(EntityDamageByEntityEvent event) {
|
public void luckDecrease(EntityDamageByEntityEvent event) {
|
||||||
Entity eTEMP = event.getDamager();
|
Entity eTEMP = event.getDamager();
|
||||||
Entity pTEMP = event.getEntity();
|
Entity pTEMP = event.getEntity();
|
||||||
DamageCause cause = event.getCause();
|
EntityDamageEvent.DamageCause cause = event.getCause();
|
||||||
|
|
||||||
if (!(pTEMP instanceof Player player)) {
|
if (!(pTEMP instanceof Player player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(eTEMP instanceof Witch)) {
|
if (!(eTEMP instanceof Witch) || !(eTEMP instanceof Guardian)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Luck luck = PlayerHandler.getLuckContainer(player);
|
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||||
if (cause.equals(DamageCause.MAGIC) || cause.equals(DamageCause.POISON)) {
|
if (cause.equals(EntityDamageEvent.DamageCause.MAGIC) || cause.equals(EntityDamageEvent.DamageCause.POISON)) {
|
||||||
if (luck.quickRNG(33.0)) {
|
if (luck.quickRNG(33.0)) {
|
||||||
luck.takeFrom(5.0);
|
luck.takeFrom(5.0);
|
||||||
plugin.handler.updatePlayer(player, luck);
|
plugin.getHandler().updatePlayer(player, luck);
|
||||||
|
player.sendMessage(MiniComponent.warn("Your luck has been decreased by 5 points!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 io.github.simplex.luck.util.ListBox;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RandomEffect extends AbstractListener {
|
||||||
|
public RandomEffect(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
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);
|
||||||
|
player.sendMessage(MiniComponent.info("Thanks to luck, a random positive potion effect has been applied to you."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import io.github.simplex.lib.PotionEffectBuilder;
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
import io.github.simplex.luck.util.ListBox;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
|
||||||
|
public class RestoreHunger extends AbstractListener {
|
||||||
|
public RestoreHunger(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
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.getValue();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import io.github.simplex.lib.PotionEffectBuilder;
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
import io.github.simplex.luck.util.ListBox;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
|
|
||||||
|
public class TakeDamage extends AbstractListener {
|
||||||
|
public TakeDamage(FeelingLucky plugin) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void takeDamage(EntityDamageEvent event) {
|
||||||
|
Entity entity = event.getEntity();
|
||||||
|
if (!(entity instanceof Player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player player = (Player) event.getEntity();
|
||||||
|
Luck luck = getHandler().getLuckContainer(player);
|
||||||
|
if (ListBox.acceptedCauses.contains(event.getCause())) {
|
||||||
|
if (luck.notDefault()) {
|
||||||
|
double percentage = luck.getValue();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a player's luck stat is a negative number, or they are "marked",
|
||||||
|
* this will trigger instead of the regular luck spin.
|
||||||
|
*/
|
||||||
|
if (percentage < 0 || luck.isMarked(player)) {
|
||||||
|
percentage = Math.abs(percentage);
|
||||||
|
if (luck.quickRNG(percentage)) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
player.damage(event.getDamage() * 2);
|
||||||
|
player.sendMessage(Component.empty().content("You were unlucky and took double damage."));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.getValue();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If a player's luck stat is a negative number, or they are "marked",
|
||||||
|
* this will trigger instead of the regular luck spin.
|
||||||
|
*/
|
||||||
|
if (percentage < 0 || luck.isMarked(player)) {
|
||||||
|
percentage = Math.abs(percentage);
|
||||||
|
if (luck.quickRNG(percentage)) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
player.addPotionEffect(PotionEffectBuilder.newEffect().type(ListBox.potionEffects.get(Luck.RNG().nextInt(ListBox.potionEffects.size() - 1))).amplifier(Luck.RNG().nextInt(1, 5)).duration(Luck.RNG().nextInt(1, 120)).create());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (luck.quickRNG(percentage) && doesQualify("take_damage", percentage)) {
|
||||||
|
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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
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 org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.inventory.CraftItemEvent;
|
||||||
|
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) {
|
||||||
|
super(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void unbreakableTool(CraftItemEvent event) {
|
||||||
|
CraftingInventory inventory = event.getInventory();
|
||||||
|
ItemStack stack = inventory.getResult();
|
||||||
|
|
||||||
|
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.getValue()) && doesQualify("unbreakable", luck.getValue())) {
|
||||||
|
meta.setUnbreakable(true);
|
||||||
|
stack.setItemMeta(meta);
|
||||||
|
inventory.setResult(stack);
|
||||||
|
player.sendMessage(MiniComponent.info("By the grace of Luck you have crafted an unbreakable tool!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
package io.github.simplex.luck.listener;
|
||||||
|
|
||||||
|
import io.github.simplex.lib.ItemBuilder;
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
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.player.PlayerInteractAtEntityEvent;
|
||||||
|
import org.bukkit.inventory.MerchantRecipe;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class VillagerInventory extends AbstractListener {
|
||||||
|
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()
|
||||||
|
));
|
||||||
|
recipe.setDemand(8);
|
||||||
|
recipe.setPriceMultiplier(1.25F);
|
||||||
|
recipe.setVillagerExperience(25);
|
||||||
|
recipe.setSpecialPrice(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void addRabbitFootToVillagerInventory(PlayerInteractAtEntityEvent event) {
|
||||||
|
if (event.getRightClicked() instanceof Villager vil) {
|
||||||
|
if (!vil.getProfession().equals(Villager.Profession.BUTCHER)) return;
|
||||||
|
|
||||||
|
List<MerchantRecipe> recipeList = new ArrayList<>(vil.getRecipes());
|
||||||
|
if (recipeList.contains(recipe)) return;
|
||||||
|
|
||||||
|
Luck luck = plugin.getHandler().getLuckContainer(event.getPlayer());
|
||||||
|
if (luck == null) return;
|
||||||
|
|
||||||
|
if (luck.quickRNG(luck.getValue())) {
|
||||||
|
recipeList.add(recipe);
|
||||||
|
vil.setRecipes(recipeList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,37 @@
|
|||||||
package io.github.simplex.luck.player;
|
package io.github.simplex.luck.player;
|
||||||
|
|
||||||
import io.github.simplex.api.LuckContainer;
|
import io.github.simplex.api.LuckContainer;
|
||||||
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.attribute.Attribute;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.SplittableRandom;
|
import java.util.*;
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public class Luck implements LuckContainer {
|
public class Luck implements LuckContainer {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final double multiplier;
|
|
||||||
private final double BASE_VALUE;
|
|
||||||
private final PlayerLuckChangeEvent event;
|
private final PlayerLuckChangeEvent event;
|
||||||
|
private final FeelingLucky plugin;
|
||||||
|
private final List<Player> markedPlayers = new ArrayList<>();
|
||||||
|
private final Map<Player, Double> cache = new HashMap<>();
|
||||||
|
private double BASE_VALUE;
|
||||||
|
private double multiplier;
|
||||||
|
private double tempSave;
|
||||||
|
|
||||||
public Luck(Player player) {
|
public Luck(FeelingLucky plugin, Player player) {
|
||||||
this(player, 1.0);
|
this(plugin, player, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Luck(Player player, double multiplier) {
|
public Luck(FeelingLucky plugin, Player player, double multiplier) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.multiplier = multiplier;
|
this.multiplier = multiplier;
|
||||||
BASE_VALUE = player.getAttribute(Attribute.GENERIC_LUCK).getDefaultValue();
|
this.plugin = plugin;
|
||||||
event = new PlayerLuckChangeEvent(player);
|
BASE_VALUE = plugin.getConfigMap().get(player.getUniqueId()).getConfig().getDouble("luck");
|
||||||
|
tempSave = BASE_VALUE;
|
||||||
|
|
||||||
|
event = new PlayerLuckChangeEvent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true,
|
@Contract(pure = true,
|
||||||
@ -33,35 +40,43 @@ public class Luck implements LuckContainer {
|
|||||||
return new SplittableRandom();
|
return new SplittableRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean quickRNG2(double percentage) {
|
public static boolean quickRNGnoMultiplier(double value) {
|
||||||
double rng;
|
double rng;
|
||||||
if (percentage >= 100.0) {
|
if (value >= 1024.0) {
|
||||||
rng = 100.0; // 100% chance to trigger, obviously;
|
rng = 1024.0; // 100% chance to trigger, obviously;
|
||||||
} else {
|
} else {
|
||||||
rng = RNG().nextDouble(0.0, 99.0);
|
rng = RNG().nextDouble(0.0, 1024.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (percentage >= rng);
|
double actual = Math.round((rng / 1024.0) * 100);
|
||||||
|
|
||||||
|
return (value >= actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public FeelingLucky getPlugin() {
|
||||||
public Attribute asAttribute() {
|
return plugin;
|
||||||
return Attribute.GENERIC_LUCK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void markPlayer(Player player) {
|
||||||
public double getNumber() {
|
markedPlayers.add(player);
|
||||||
return associatedPlayer().getAttribute(asAttribute()).getValue();
|
}
|
||||||
|
|
||||||
|
public void unmarkPlayer(Player player) {
|
||||||
|
markedPlayers.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMarked(Player player) {
|
||||||
|
return markedPlayers.contains(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMatch(double number) {
|
public boolean isMatch(double number) {
|
||||||
return getNumber() == number;
|
return getValue() == number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isClose(double number, int range) {
|
public boolean isClose(double number, int range) {
|
||||||
return ((getNumber() - range <= number) && (number <= getNumber() + range));
|
return ((getValue() - range <= number) && (number <= getValue() + range));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -74,63 +89,100 @@ public class Luck implements LuckContainer {
|
|||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean quickRNG(double percentage) {
|
public boolean quickRNG(double value) {
|
||||||
double rng;
|
double rng;
|
||||||
if (percentage >= 100.0) {
|
if (value >= 1024.0) {
|
||||||
rng = 100.0; // 100% chance to trigger, obviously;
|
rng = 1024.0; // 100% chance to trigger, obviously;
|
||||||
} else {
|
} else {
|
||||||
rng = RNG().nextDouble(0.0, 99.0);
|
rng = RNG().nextDouble(0.0, 1024.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double actual = Math.round((rng / 1024) * 100);
|
||||||
|
|
||||||
if (multiplier() > 1.0) {
|
if (multiplier() > 1.0) {
|
||||||
return ((percentage * multiplier()) >= rng);
|
return ((value * multiplier()) >= actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (percentage >= rng);
|
return (value >= actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
setValue(getDefaultValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double baseValue() {
|
public double getValue() {
|
||||||
return BASE_VALUE;
|
return BASE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double defaultValue() {
|
public void setValue(double value) {
|
||||||
return player.getAttribute(Attribute.GENERIC_LUCK).getDefaultValue();
|
BASE_VALUE = value;
|
||||||
|
plugin.getConfigMap().get(associatedPlayer().getUniqueId()).setLuck(value);
|
||||||
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setValue(double value) {
|
public void setMultiplier(double multiplier) {
|
||||||
player.getAttribute(Attribute.GENERIC_LUCK).setBaseValue(value);
|
this.multiplier = multiplier;
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
plugin.getConfigMap().get(associatedPlayer().getUniqueId()).setMultiplier(multiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cache() {
|
||||||
|
if (cache.containsKey(player)) {
|
||||||
|
cache.replace(player, getValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.put(player, getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cached(Player player) {
|
||||||
|
return cache.containsKey(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restore() {
|
||||||
|
if (!cache.containsKey(player)) {
|
||||||
|
plugin.getLogger().info("Nothing to restore!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(cache.get(player));
|
||||||
|
cache.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDefaultValue() {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTo(double value) {
|
public void addTo(double value) {
|
||||||
setValue(baseValue() + value);
|
if (value >= 1024.0 || (getValue() + value) >= 1024.0) {
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
setValue(1024.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setValue(getValue() + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void takeFrom(double value) {
|
public void takeFrom(double value) {
|
||||||
setValue(baseValue() - value);
|
if (value <= -1024.0 || (getValue() - value) <= -1024.0) {
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
setValue(-1024.0);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
setValue(getValue() + value);
|
||||||
public double getPercentage() {
|
|
||||||
return baseValue() - defaultValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean notDefault() {
|
public boolean notDefault() {
|
||||||
return baseValue() != defaultValue();
|
return getValue() != getDefaultValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean lessThan(double value) {
|
public boolean lessThan(double value) {
|
||||||
return baseValue() < value;
|
return getValue() < value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean greaterThan(double value) {
|
public boolean greaterThan(double value) {
|
||||||
return baseValue() > value;
|
return getValue() > value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "" + baseValue();
|
return "" + getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package io.github.simplex.luck.player;
|
package io.github.simplex.luck.player;
|
||||||
|
|
||||||
import io.github.simplex.luck.FeelingLucky;
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
import io.github.simplex.luck.SneakyWorker;
|
import io.github.simplex.luck.util.SneakyWorker;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@ -10,19 +13,23 @@ import java.io.BufferedWriter;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PlayerConfig extends YamlConfiguration {
|
public class PlayerConfig {
|
||||||
private final File configFile;
|
private final File configFile;
|
||||||
|
private final OfflinePlayer player;
|
||||||
private volatile YamlConfiguration config;
|
private volatile YamlConfiguration config;
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public PlayerConfig(FeelingLucky plugin, Player player) {
|
public PlayerConfig(FeelingLucky plugin, Player player) {
|
||||||
File dataFolder = plugin.getDataFolder();
|
this.player = player;
|
||||||
|
if (!plugin.getDataFolder().exists()) plugin.getDataFolder().mkdirs();
|
||||||
|
File dataFolder = new File(plugin.getDataFolder(), "players");
|
||||||
if (!dataFolder.exists()) dataFolder.mkdirs();
|
if (!dataFolder.exists()) dataFolder.mkdirs();
|
||||||
File file = new File(dataFolder, player.getUniqueId() + ".yml");
|
File file = new File(dataFolder, player.getUniqueId() + ".yml");
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
String name = "username: " + player.getName();
|
String name = "username: " + player.getName();
|
||||||
String luck = "luck: " + PlayerHandler.getLuckContainer(player).defaultValue();
|
String luck = "luck: " + 0;
|
||||||
String multiplier = "multiplier: " + 1.0;
|
String multiplier = "multiplier: " + 1.0;
|
||||||
|
|
||||||
SneakyWorker.sneakyTry(() -> {
|
SneakyWorker.sneakyTry(() -> {
|
||||||
@ -38,26 +45,27 @@ public class PlayerConfig extends YamlConfiguration {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
configFile = file;
|
configFile = file;
|
||||||
config = loadConfiguration(configFile);
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
String tempUsername = config.getString("username");
|
String tempUsername = config.getString("username");
|
||||||
|
|
||||||
if (tempUsername != null && tempUsername.equalsIgnoreCase("replace")) {
|
if (tempUsername == null) {
|
||||||
config.set("username", player.getName());
|
config.set("username", player.getName());
|
||||||
config.set("luck", PlayerHandler.getLuckContainer(player).defaultValue());
|
config.set("luck", plugin.getHandler().getLuckContainer(player).getDefaultValue());
|
||||||
config.set("multiplier", "1.0");
|
config.set("multiplier", "1.0");
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PlayerConfig(File file) {
|
protected PlayerConfig(FeelingLucky plugin, File file) {
|
||||||
this.configFile = file;
|
this.configFile = file;
|
||||||
config = loadConfiguration(configFile);
|
this.player = Bukkit.getOfflinePlayer(UUID.fromString(file.getName().split("\\.")[0]));
|
||||||
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("_ -> new")
|
@Contract("_, _ -> new")
|
||||||
public static PlayerConfig loadFrom(File file) {
|
public static PlayerConfig initFrom(FeelingLucky plugin, File file) {
|
||||||
return new PlayerConfig(file);
|
return new PlayerConfig(plugin, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save() {
|
public void save() {
|
||||||
@ -65,7 +73,31 @@ public class PlayerConfig extends YamlConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void load() {
|
public void load() {
|
||||||
SneakyWorker.sneakyTry(() -> config = loadConfiguration(configFile));
|
SneakyWorker.sneakyTry(() -> config = YamlConfiguration.loadConfiguration(configFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
save();
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfflinePlayer getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String name) {
|
||||||
|
config.set("username", name);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLuck(double luck) {
|
||||||
|
config.set("luck", luck);
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMultiplier(double multiplier) {
|
||||||
|
config.set("multiplier", multiplier);
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public YamlConfiguration getConfig() {
|
public YamlConfiguration getConfig() {
|
||||||
|
@ -1,63 +1,61 @@
|
|||||||
package io.github.simplex.luck.player;
|
package io.github.simplex.luck.player;
|
||||||
|
|
||||||
import io.github.simplex.luck.FeelingLucky;
|
import io.github.simplex.luck.FeelingLucky;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerLoginEvent;
|
import org.bukkit.event.player.PlayerLoginEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public record PlayerHandler(FeelingLucky plugin) implements Listener {
|
public class PlayerHandler implements Listener {
|
||||||
private static final Map<Player, Luck> playerLuckMap = new HashMap<>();
|
public final FeelingLucky plugin;
|
||||||
private static final List<Player> markedPlayers = new ArrayList<>();
|
private final Map<Player, Luck> playerLuckMap = new HashMap<>();
|
||||||
|
|
||||||
public static Luck getLuckContainer(Player player) {
|
public PlayerHandler(FeelingLucky plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(this, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Luck getLuckContainer(Player player) {
|
||||||
return playerLuckMap.get(player);
|
return playerLuckMap.get(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void markPlayer(Player player) {
|
|
||||||
markedPlayers.add(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void unmarkPlayer(Player player) {
|
|
||||||
markedPlayers.remove(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isMarked(Player player) {
|
|
||||||
return markedPlayers.contains(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void initializePlayer(PlayerLoginEvent event) {
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
PlayerConfig config = FeelingLucky.getConfigMap().get(player.getUniqueId());
|
|
||||||
|
|
||||||
if (config == null) {
|
|
||||||
config = new PlayerConfig(plugin, player);
|
|
||||||
FeelingLucky.getConfigMap().put(player.getUniqueId(), config);
|
|
||||||
}
|
|
||||||
|
|
||||||
String username = config.getString("username");
|
|
||||||
double luck = config.getDouble("luck");
|
|
||||||
double multiplier = config.getDouble("multiplier");
|
|
||||||
|
|
||||||
if (!player.getName().equalsIgnoreCase(username)) {
|
|
||||||
config.set("username", player.getName());
|
|
||||||
config.save();
|
|
||||||
config.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
Luck container = new Luck(player, multiplier);
|
|
||||||
container.setValue(luck);
|
|
||||||
|
|
||||||
playerLuckMap.put(player, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updatePlayer(Player player, Luck luck) {
|
public void updatePlayer(Player player, Luck luck) {
|
||||||
playerLuckMap.replace(player, luck);
|
playerLuckMap.replace(player, luck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void initializePlayer(PlayerLoginEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
PlayerConfig playerConfig = plugin.getConfigMap().get(player.getUniqueId());
|
||||||
|
|
||||||
|
if (playerConfig == null) {
|
||||||
|
playerConfig = new PlayerConfig(plugin, player);
|
||||||
|
plugin.getConfigMap().put(player.getUniqueId(), playerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
String username = playerConfig.getConfig().getString("username");
|
||||||
|
double luck = playerConfig.getConfig().getDouble("luck");
|
||||||
|
double multiplier = playerConfig.getConfig().getDouble("multiplier");
|
||||||
|
|
||||||
|
if (!player.getName().equalsIgnoreCase(username)) {
|
||||||
|
playerConfig.getConfig().set("username", player.getName());
|
||||||
|
playerConfig.save();
|
||||||
|
playerConfig.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
Luck container = new Luck(plugin, player, multiplier);
|
||||||
|
container.setValue(luck);
|
||||||
|
|
||||||
|
playerLuckMap.put(player, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void clearContainer(PlayerQuitEvent event) {
|
||||||
|
playerLuckMap.remove(event.getPlayer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.simplex.luck.player;
|
package io.github.simplex.luck.player;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
import org.bukkit.event.player.PlayerEvent;
|
import org.bukkit.event.player.PlayerEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -8,16 +7,16 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class PlayerLuckChangeEvent extends PlayerEvent {
|
public class PlayerLuckChangeEvent extends PlayerEvent {
|
||||||
public final HandlerList handlerList = new HandlerList();
|
public final HandlerList handlerList = new HandlerList();
|
||||||
|
|
||||||
public PlayerLuckChangeEvent(@NotNull Player who) {
|
public PlayerLuckChangeEvent(@NotNull Luck luck) {
|
||||||
super(who);
|
super(luck.associatedPlayer());
|
||||||
Luck luck = PlayerHandler.getLuckContainer(who);
|
|
||||||
if (luck.lessThan(0.0) && !PlayerHandler.isMarked(who)) {
|
if (luck.lessThan(0.0) && !luck.isMarked(luck.associatedPlayer())) {
|
||||||
PlayerHandler.markPlayer(who);
|
luck.markPlayer(luck.associatedPlayer());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (luck.greaterThan(0.0) && PlayerHandler.isMarked(who)) {
|
if (luck.greaterThan(0.0) && luck.isMarked(luck.associatedPlayer())) {
|
||||||
PlayerHandler.unmarkPlayer(who);
|
luck.unmarkPlayer(luck.associatedPlayer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
src/main/java/io/github/simplex/luck/util/CooldownTimer.java
Normal file
35
src/main/java/io/github/simplex/luck/util/CooldownTimer.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package io.github.simplex.luck.util;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class CooldownTimer {
|
||||||
|
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) {
|
||||||
|
cooldowns.remove(playerUUID);
|
||||||
|
} else {
|
||||||
|
cooldowns.put(playerUUID, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCooldown(UUID uuid) {
|
||||||
|
return cooldowns.getOrDefault(uuid, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long remaining(Player player) {
|
||||||
|
long timeLeft = System.currentTimeMillis() - getCooldown(player.getUniqueId());
|
||||||
|
return TimeUnit.MILLISECONDS.toSeconds(timeLeft) - DEFAULT_COOLDOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onCooldown(Player player) {
|
||||||
|
long remaining = System.currentTimeMillis() - getCooldown(player.getUniqueId());
|
||||||
|
return (!(TimeUnit.MILLISECONDS.toSeconds(remaining) >= DEFAULT_COOLDOWN));
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package io.github.simplex.luck;
|
package io.github.simplex.luck.util;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
@ -6,6 +6,7 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ListBox {
|
public class ListBox {
|
||||||
@ -36,6 +37,25 @@ public class ListBox {
|
|||||||
add(PotionEffectType.WEAKNESS);
|
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<>() {{
|
public static final List<ItemStack> foods = new ArrayList<>() {{
|
||||||
add(new ItemStack(Material.COOKED_BEEF));
|
add(new ItemStack(Material.COOKED_BEEF));
|
||||||
add(new ItemStack(Material.COOKED_CHICKEN));
|
add(new ItemStack(Material.COOKED_CHICKEN));
|
205
src/main/java/io/github/simplex/luck/util/LuckCMD.java
Normal file
205
src/main/java/io/github/simplex/luck/util/LuckCMD.java
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package io.github.simplex.luck.util;
|
||||||
|
|
||||||
|
import io.github.simplex.lib.Messages;
|
||||||
|
import io.github.simplex.lib.MiniComponent;
|
||||||
|
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.*;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
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");
|
||||||
|
register(plugin.getServer().getCommandMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 == 3) {
|
||||||
|
if ((sender instanceof ConsoleCommandSender) || sender.hasPermission("luck.admin")) {
|
||||||
|
if (args[0].equalsIgnoreCase("reload") && args[1].equalsIgnoreCase("-p")) {
|
||||||
|
Player player = Bukkit.getPlayer(args[2]);
|
||||||
|
if (player == null) {
|
||||||
|
sender.sendMessage(Messages.NO_PLAYER.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UUID uuid = player.getUniqueId();
|
||||||
|
PlayerConfig config = plugin.getConfigMap().get(uuid);
|
||||||
|
config.reload();
|
||||||
|
sender.sendMessage(MiniComponent.info("Successfully reloaded " + player.getName() + "'s configuration file."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = Bukkit.getPlayer(args[1]);
|
||||||
|
double amount = Double.parseDouble(args[2]);
|
||||||
|
|
||||||
|
if (player == null) {
|
||||||
|
sender.sendMessage(Messages.NO_PLAYER.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount > 1024.0 || amount < -1024.0) {
|
||||||
|
sender.sendMessage(Messages.OUT_OF_BOUNDS.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||||
|
PlayerConfig config = plugin.getConfigMap().get(player.getUniqueId());
|
||||||
|
|
||||||
|
switch (args[0]) {
|
||||||
|
case "set" -> {
|
||||||
|
luck.setValue(amount);
|
||||||
|
plugin.getHandler().updatePlayer(player, luck);
|
||||||
|
config.setLuck(luck.getValue());
|
||||||
|
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
|
||||||
|
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."));
|
||||||
|
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."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(Messages.NO_PERMISSION.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")) {
|
||||||
|
Player player = Bukkit.getPlayer(args[1]);
|
||||||
|
|
||||||
|
if (player == null) {
|
||||||
|
sender.sendMessage(Messages.NO_PLAYER.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||||
|
sender.sendMessage(MiniComponent.info("Luck stat for " + args[1] + ": " + luck.getValue()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args[0].equalsIgnoreCase("reset")) {
|
||||||
|
Player player = Bukkit.getPlayer(args[1]);
|
||||||
|
|
||||||
|
if (player == null) {
|
||||||
|
sender.sendMessage(Messages.NO_PLAYER.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Luck luck = plugin.getHandler().getLuckContainer(player);
|
||||||
|
PlayerConfig config = plugin.getConfigMap().get(player.getUniqueId());
|
||||||
|
luck.reset();
|
||||||
|
plugin.getHandler().updatePlayer(player, luck);
|
||||||
|
config.setLuck(luck.getValue());
|
||||||
|
sender.sendMessage(MiniComponent.info("Successfully reset " + args[1] + "'s Luck stat."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(Messages.NO_PERMISSION.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.length == 1) {
|
||||||
|
if (args[0].equalsIgnoreCase("reload") && sender.hasPermission("luck.admin")) {
|
||||||
|
plugin.getConfigMap().values().forEach(PlayerConfig::reload);
|
||||||
|
sender.sendMessage(MiniComponent.info("Player configurations reloaded."));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.getValue()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (sender instanceof ConsoleCommandSender) {
|
||||||
|
sender.sendMessage(Messages.NOT_FROM_CONSOLE.get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
|
||||||
|
List<String> completions = new ArrayList<>() {{
|
||||||
|
add("info");
|
||||||
|
}};
|
||||||
|
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.stream().filter(n -> n.startsWith(args[0])).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (args.length == 3 && playerNames.contains(args[1])) {
|
||||||
|
switch (args[0]) {
|
||||||
|
case "give":
|
||||||
|
case "take":
|
||||||
|
case "set":
|
||||||
|
return List.of("amount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
82
src/main/java/io/github/simplex/luck/util/SneakyWorker.java
Normal file
82
src/main/java/io/github/simplex/luck/util/SneakyWorker.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package io.github.simplex.luck.util;
|
||||||
|
|
||||||
|
import io.github.simplex.luck.player.Luck;
|
||||||
|
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 {
|
||||||
|
sneakyTry.tryThis();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
String sb = "An error of type: "
|
||||||
|
+ ex.getClass().getSimpleName()
|
||||||
|
+ " has occurred. A cause will be printed. \n\n"
|
||||||
|
+ ex.getCause();
|
||||||
|
Bukkit.getLogger().severe(sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void quietTry(SneakyTry sneakyTry) {
|
||||||
|
try {
|
||||||
|
sneakyTry.tryThis();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
void tryThis() throws Exception;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.simplex.luck.util;
|
||||||
|
|
||||||
|
import io.github.simplex.lib.ItemBuilder;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
public class SpecialFootItem {
|
||||||
|
public final ItemStack stack;
|
||||||
|
|
||||||
|
public 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 0.1.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack get() {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemMeta meta() {
|
||||||
|
return stack.getItemMeta();
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/main/resources/config.yml
Normal file
26
src/main/resources/config.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# These entries are for the minimum amount of luck required for the event to actually trigger.
|
||||||
|
# These work in addition to the RNG generator,
|
||||||
|
# meaning both the RNG and this condition must be met for the event to trigger.
|
||||||
|
# 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.0
|
||||||
|
medium_rarity_chance: 128.0
|
||||||
|
low_rarity_chance: 64.0
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# These entries are case-sensitive.
|
||||||
|
|
||||||
|
block_drops: LOW
|
||||||
|
bonemeal: MED
|
||||||
|
cheat_death: MED
|
||||||
|
enchanting: HIGH
|
||||||
|
experience: HIGH
|
||||||
|
item_drops: LOW
|
||||||
|
random_effect: HIGH
|
||||||
|
restore_hunger: NONE
|
||||||
|
take_damage: MED
|
||||||
|
unbreakable: HIGH
|
Reference in New Issue
Block a user