9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-22 08:09:20 +00:00

Compare commits

..

49 Commits
2.2.2 ... 2.2.5

Author SHA1 Message Date
William
516b163e07 use AndJam 1.0.2 for now. 2023-06-09 10:22:10 +01:00
William
c123b15708 Bump AndJam to 1.0.3 2023-06-09 10:19:42 +01:00
William
29f8d8bf50 Bump dependencies 2023-06-09 10:12:21 +01:00
dependabot[bot]
4f65cf49ef Bump commons-io:commons-io from 2.11.0 to 2.12.0 (#134)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-05-28 10:28:38 +01:00
William
27f9e24dfb [ci skip] Fix missing closing perenthesis 2023-05-17 17:01:35 +01:00
William
2cbe39f158 [ci skip] Update songoda link to craftaro 2023-05-17 17:01:09 +01:00
William
5840f61571 Fix tests 2023-05-07 23:17:23 +01:00
William
6bd12dc000 Update DesertWell to 2.0.2, improve adventure API usage 2023-05-07 23:15:50 +01:00
William
1afbd3753a Merge remote-tracking branch 'origin/master'
# Conflicts:
#	.github/workflows/java_ci.yml
#	build.gradle
2023-05-07 23:02:05 +01:00
William
38a063420b Add license headers 2023-05-07 23:00:12 +01:00
William
0fae3484a1 Add workflow files, test reporting, maven publishing, docs, bump version 2023-05-07 22:59:19 +01:00
William
7bb4bff485 Make case conversion operations use the English locale 2023-05-07 22:50:37 +01:00
William
c5f5cd702e Update gradle-build-action 2023-05-01 14:58:10 +01:00
dependabot[bot]
3ced2cc0af Bump org.junit.jupiter:junit-jupiter-api from 5.9.2 to 5.9.3 (#128)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-01 14:56:48 +01:00
dependabot[bot]
73e1840574 Bump org.junit.jupiter:junit-jupiter-engine from 5.9.2 to 5.9.3 (#129)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-05-01 13:50:40 +01:00
dependabot[bot]
d1ca56af1f Bump dev.triumphteam:triumph-gui from 3.1.4 to 3.1.5 (#130)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-01 13:50:25 +01:00
dependabot[bot]
55211e30c5 Bump org.ajoberstar.grgit from 5.0.0 to 5.2.0 (#126)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-04-26 00:25:20 +01:00
evlad
2c1a38f2c9 Disable all projectiles while player is in sync (#127) 2023-04-26 00:21:04 +01:00
小蔡
0c7e052d44 Update zh-tw.yml (#124) 2023-04-20 22:20:24 +01:00
William
54cc11fce0 [ci skip] Update README.md 2023-04-17 11:21:40 +01:00
William
3645fa01ec [ci skip] Wrap README header in header tag 2023-04-17 11:11:38 +01:00
dependabot[bot]
e20a0da845 Bump net.kyori:adventure-api from 4.13.0 to 4.13.1 (#120)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 18:02:22 +01:00
William
7ed7d0a29e Add additional cast checking to PDC tag fetching, cast via complex type (#118) 2023-04-05 12:53:05 +01:00
William278
e7e7995e4e Bump dependencies 2023-04-05 12:03:13 +01:00
William278
af57cfcf70 Fix log method not logging throwables 2023-03-28 15:37:04 +01:00
William
f1ac9b5e04 [ci skip] Update README 2023-03-13 13:44:25 +00:00
William
a9b1070725 Encapsulate Settings, tweak keys, add empty dead player save option, close #73 2023-03-10 21:17:15 +00:00
William
5a000add98 Fix deadlock on busy servers with small thread pools, close #100 2023-03-10 21:00:15 +00:00
dependabot[bot]
aec2836d1e Bump com.github.plan-player-analytics:Plan from 5.5.2254 to 5.5.2272 (#108)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-03-10 20:56:14 +00:00
William
84e2ea3904 gradle: Update to Gradle 8.0.2, bump shadow to 8.1.0 2023-03-10 20:54:57 +00:00
dependabot[bot]
4f669170c2 Bump org.jetbrains:annotations from 24.0.0 to 24.0.1 (#107)
Bumps [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations) from 24.0.0 to 24.0.1.
- [Release notes](https://github.com/JetBrains/java-annotations/releases)
- [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md)
- [Commits](https://github.com/JetBrains/java-annotations/commits)

---
updated-dependencies:
- dependency-name: org.jetbrains:annotations
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-05 12:45:59 +00:00
dependabot[bot]
8ea8c7b7ba Bump com.github.plan-player-analytics:Plan from 5.5.2208 to 5.5.2254 (#104)
Bumps [com.github.plan-player-analytics:Plan](https://github.com/plan-player-analytics/Plan) from 5.5.2208 to 5.5.2254.
- [Release notes](https://github.com/plan-player-analytics/Plan/releases)
- [Commits](https://github.com/plan-player-analytics/Plan/compare/5.5.2208...5.5.2254)

---
updated-dependencies:
- dependency-name: com.github.plan-player-analytics:Plan
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-02-28 02:50:03 +00:00
dependabot[bot]
acab4ae58a Bump net.william278:DesertWell from 1.1 to 1.1.1 (#102)
Bumps [net.william278:DesertWell](https://github.com/WiIIiam278/DesertWell) from 1.1 to 1.1.1.
- [Release notes](https://github.com/WiIIiam278/DesertWell/releases)
- [Commits](https://github.com/WiIIiam278/DesertWell/compare/1.1...1.1.1)

---
updated-dependencies:
- dependency-name: net.william278:DesertWell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-28 02:49:35 +00:00
Ceddix
ea822b0f4b fixed some issues I made (#101) 2023-02-23 02:26:52 +00:00
dependabot[bot]
24a9974ff7 Bump com.github.plan-player-analytics:Plan from 5.4.1690 to 5.5.2208 (#99)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-02-22 12:59:51 +00:00
dependabot[bot]
222a9871e0 Bump dev.dejvokep:boosted-yaml from 1.3 to 1.3.1 (#98)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William <will27528@gmail.com>
2023-02-22 12:59:37 +00:00
dependabot[bot]
0ce9d2ce74 Bump dev.triumphteam:triumph-gui from 3.1.3 to 3.1.4 (#97)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-22 12:58:27 +00:00
William278
cde500a123 Bump to 2.2.4 2023-02-18 13:30:36 +00:00
William278
f15790030f Update dependencies 2023-02-18 13:29:12 +00:00
William278
ce3350c6fa Fix shutdown crash with PDC (rollback switch to using task) 2023-02-18 13:24:49 +00:00
William
4288742052 Update dependencies, close #85, close #86, close #87, close #88, close #89 2023-02-13 13:29:52 +00:00
William
8205b9c169 Create dependabot.yml and funding.yml 2023-02-13 13:20:27 +00:00
William
1d7f6a8d8b refactor: Use mappings class for PDC tag type handling 2023-02-12 19:14:46 +00:00
William
3425c97245 Bump to v2.2.3 2023-02-12 18:18:29 +00:00
evlad
2d1d8f1ab6 Sync lock: Cancel item frame interaction, add command blacklist (#84)
Co-authored-by: William <will27528@gmail.com>
2023-02-12 18:08:46 +00:00
William
f322d31b03 [ci skip] Correct typos in README 2023-02-12 15:43:26 +00:00
William278
368e665ac3 [ci skip] Update README 2023-01-27 11:46:31 +00:00
William278
922eb2f19a Merge remote-tracking branch 'origin/master'
# Conflicts:
#	README.md
2023-01-27 11:45:50 +00:00
William278
23e0123004 Update README and banner 2023-01-27 11:45:04 +00:00
122 changed files with 3292 additions and 666 deletions

7
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
# Dependabot configuration file for GitHub
version: 2
updates:
- package-ecosystem: "gradle" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

3
.github/funding.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# Funding metadata for GitHub
github: WiIIiam278
custom: https://buymeacoff.ee/william278

37
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
# Builds, tests the project with Gradle
name: CI Tests
on:
push:
branches: [ 'master' ]
paths-ignore:
- 'docs/**'
- 'workflows/**'
- 'README.md'
permissions:
contents: read
checks: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build test publish
env:
SNAPSHOTS_MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
SNAPSHOTS_MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: success() || failure() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/test/TEST-*.xml'

View File

@@ -1,32 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: Java CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 16
uses: actions/setup-java@v3
with:
java-version: '16'
distribution: 'temurin'
- name: Build with Gradle
uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
with:
arguments: test

24
.github/workflows/pr_tests.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
# Carry out tests on pull requests
name: PR Tests
on:
pull_request:
branches: [ 'master' ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Test Pull Request
uses: gradle/gradle-build-action@v2
with:
arguments: test

33
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
# Builds, tests and publishes to maven when a release is published
name: Release Tests
on:
release:
types: [ published ]
permissions:
contents: read
checks: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: build test publish
env:
RELEASES_MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
RELEASES_MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
- name: Publish Test Report
uses: mikepenz/action-junit-report@v3
if: success() || failure() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/test/TEST-*.xml'

28
.github/workflows/update_docs.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
# Update the GitHub Wiki documentation when a push is made to docs/
name: Update Docs
on:
push:
branches: [ 'master' ]
paths:
- 'docs/**'
- 'workflows/**'
tags-ignore:
- '*'
permissions:
contents: write
jobs:
deploy-wiki:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Code'
uses: actions/checkout@v3
- name: 'Push Changes to Wiki'
uses: Andrew-Chen-Wang/github-wiki-action@v3
env:
WIKI_DIR: 'docs/'
GH_TOKEN: ${{ github.token }}
GH_MAIL: 'actions@github.com'
GH_NAME: 'github-actions[bot]'

10
HEADER Normal file
View File

@@ -0,0 +1,10 @@
This file is part of HuskSync by William278. Do not redistribute!
Copyright (c) William278 <will27528@gmail.com>
All rights reserved.
This source code is provided as reference to licensed individuals that have purchased the HuskSync
plugin once from any of the official sources it is provided. The availability of this code does
not grant you the rights to modify, re-distribute, compile or redistribute this source code or
"plugin" outside this intended purpose. This license does not cover libraries developed by third
parties that are utilised in the plugin.

View File

@@ -1,36 +1,60 @@
# [![HuskSync Banner](images/banner-graphic.png)](https://github.com/WiIIiam278/HuskSync)
[![GitHub CI](https://img.shields.io/github/actions/workflow/status/WiIIiam278/HuskSync/java_ci.yml?branch=master&logo=github)](https://github.com/WiIIiam278/HuskSync/actions/workflows/java_ci.yml)
[![JitPack API](https://img.shields.io/jitpack/version/net.william278/HuskSync?color=%2300fb9a&label=api&logo=gradle)](https://jitpack.io/#net.william278/HuskSync)
[![Support Discord](https://img.shields.io/discord/818135932103557162.svg?label=&logo=discord&logoColor=fff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/tVYhJfyDWG)
[Documentation, Guides & API](https://william278.net/docs/husksync) · [Resource Page](https://www.spigotmc.org/resources/husksync.97144/) · [Bug Reports](https://github.com/WiIIiam278/HuskSync/issues)
<!--suppress ALL -->
<p align="center">
<img src="images/banner.png" alt="HuskSync" />
<a href="https://github.com/WiIIiam278/HuskSync/actions/workflows/ci.yml">
<img src="https://img.shields.io/github/actions/workflow/status/WiIIiam278/HuskSync/ci.yml?branch=master&logo=github"/>
</a>
<a href="https://jitpack.io/#net.william278/HuskSync">
<img src="https://img.shields.io/jitpack/version/net.william278/HuskSync?color=%2300fb9a&label=api&logo=gradle" />
</a>
<a href="https://discord.gg/tVYhJfyDWG">
<img src="https://img.shields.io/discord/818135932103557162.svg?label=&logo=discord&logoColor=fff&color=7389D8&labelColor=6A7EC2" />
</a>
<br/>
<b>
<a href="https://www.spigotmc.org/resources/husksync.97144/">Spigot</a>
</b>
<b>
<a href="https://william278.net/docs/husksync/setup">Setup</a>
</b>
<b>
<a href="https://william278.net/docs/husksync/">Docs</a>
</b>
<b>
<a href="https://github.com/WiIIiam278/HuskSync/issues">Issues</a>
</b>
</p>
<br/>
**HuskSync** is a modern, cross-server player data synchronisation system that enables the comprehensive synchronisation of your user's data across multiple proxied servers. It does this by making use of Redis and MySQL to optimally cache data while players change servers.
## Features
![Data snapshot viewer](images/data-snapshot-viewer.png)
**⭐ Seamless synchronisation** &mdash; Utilises optimised Redis caching when players change server to sync player data super quickly for a seamless experience.
- Synchronise inventories, ender chests, advancements, statistics, experience points, health, max health, hunger, saturation, potion effects, persistent data container tags, game mode, location and more across multiple proxied servers.
- Create and manage "snapshot" backups of user data and roll back users to previous states on-the-fly. (`/userdata`)
- Preview, list, delete, restore & pin user data snapshots in-game with an intuitive menu.
- Examine the contents of player's inventories and ender chests on-the-fly. (`/inventory`, `/enderchest`)
- Hooks with your [Player Analytics](https://github.com/plan-player-analytics/Plan) web panel to provide an overview of user data.
- Supports segregating synchronisation across multiple distinct clusters on one network.
**⭐ Complete player synchronisation** &mdash; Sync inventories, Ender Chests, health, hunger, effects, advancements, statistics, locked maps & [more](https://william278.net/docs/husksync/sync-features)—no data left behind!
## Requirements
* A MySQL Database (v8.0+).
* A Redis Database (v5.0+)
* Any number of proxied Spigot servers (Minecraft v1.16.5+, Java 16+)
**⭐ Backup, restore & rotate** &mdash; Something gone wrong? Restore players back to a previous data state. Rotate and manage data snapshots in-game!
**⭐ Import existing data** &mdash; Import your MySQLPlayerDataBridge data—or from your existing world data! No server reset needed!
**⭐ Works great with Plan** &mdash; Stay in touch with your community through HuskSync analytics on your Plan web panel.
**⭐ Extensible API & open-source** &mdash; Need more? Extend the plugin with the Developer API. Or, submit a pull request through our code bounty system!
**Ready?** [It's syncing time!](https://william278.net/docs/husksync/setup)
## Setup
1. Place the plugin jar file in the `/plugins/` directory of each Spigot server. You do not need to install HuskSync as a proxy plugin.
Requires a MySQL (v8.0+) database, a Redis (v5.0+) server and any number of Spigot-based 1.16.5+ Minecraft servers, running Java 16+.
1. Place the plugin jar file in the /plugins/ directory of each Spigot server. You do not need to install HuskSync as a proxy plugin.
2. Start, then stop every server to let HuskSync generate the config file.
3. Navigate to the HuskSync config file on each server (`~/plugins/HuskSync/config.yml`) and fill in both the MySQL and Redis database credentials.
3. Navigate to the HuskSync config file on each server (~/plugins/HuskSync/config.yml) and fill in both the MySQL and Redis database credentials.
4. Start every server again and synchronization will begin.
## Building
To build HuskSync, simply run the following in the root of the repository:
```
```bash
./gradlew clean build
```
@@ -40,25 +64,20 @@ HuskSync is a premium resource. This source code is provided as reference only f
- [License](https://github.com/WiIIiam278/HuskSync/blob/master/LICENSE)
## Contributing
A code bounty program is in place for HuskSync, where developers making significant code contributions to HuskSync may be entitled to a license at my discretion to use HuskSync in commercial contexts without having to purchase the resource. Please read the information for contributors in the LICENSE file before submitting a pull request.
A code bounty program is in place for HuskSync, where developers making significant code contributions to HuskSync may be entitled to a license at my discretion to use HuskSync in commercial contexts without having to purchase the resource. Please read the information for contributors in the LICENSE file before submitting a pull request.
## Translation
## Translations
Translations of the plugin locales are welcome to help make the plugin more accessible. Please submit a pull request with your translations as a `.yml` file.
- [Locales Directory](https://github.com/WiIIiam278/HuskSync/tree/master/common/src/main/resources/locales)
- [English Locales](https://github.com/WiIIiam278/HuskSync/tree/master/common/src/main/resources/locales/en-gb.yml)
## bStats
This plugin uses bStats to provide me with metrics about its usage:
- [bStats Metrics](https://bstats.org/plugin/bukkit/HuskSync%20-%20Bukkit/13140)
You can turn metric collection off by navigating to `~/plugins/bStats/config.yml` and editing the config to disable plugin metrics.
- [Locales Directory](https://github.com/WiIIiam278/HuskSync/tree/master/common/src/main/resources/languages)
- [English Locales](https://github.com/WiIIiam278/HuskSync/tree/master/common/src/main/resources/languages/en-gb.yml)
## Links
- [Documentation, Guides & API](https://william278.net/docs/husksync)
- [Resource Page](https://www.spigotmc.org/resources/husksync.97144/)
- [Bug Reports](https://github.com/WiIIiam278/HuskSync/issues)
- [Discord Support](https://discord.gg/tVYhJfyDWG) (Proof of purchase required)
- [Docs](https://william278.net/docs/husksync/) &mdash; Read the plugin documentation!
- [Spigot](https://www.spigotmc.org/resources/husksync.97144/) &mdash; View the Spigot resource page (Also: [Polymart](https://polymart.org/resource/husksync.1634), [Craftaro](https://craftaro.com/marketplace/product/husksync.406))
- [Issues](https://github.com/WiIIiam278/HuskSync/issues) &mdash; File a bug report or feature request
- [Discord](https://discord.gg/tVYhJfyDWG) &mdash; Get help, ask questions (Proof of purchase required)
- [bStats](https://bstats.org/plugin/bukkit/HuskSync%20-%20Bukkit/13140) &mdash; View plugin metrics
---
&copy; [William278](https://william278.net/), 2022. All rights reserved.
&copy; [William278](https://william278.net/), 2023. All rights reserved.

View File

@@ -1,15 +1,19 @@
plugins {
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'org.ajoberstar.grgit' version '5.0.0'
id 'java'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'org.cadixdev.licenser' version '0.6.1' apply false
id 'org.ajoberstar.grgit' version '5.2.0'
id 'maven-publish'
id 'java'
}
group 'net.william278'
version "$ext.plugin_version-${versionMetadata()}"
version "$ext.plugin_version${versionMetadata()}"
description "$ext.plugin_description"
defaultTasks 'licenseFormat', 'build'
ext {
set 'version', version.toString()
set 'description', description.toString()
set 'jedis_version', jedis_version.toString()
set 'mysql_driver_version', mysql_driver_version.toString()
set 'snappy_version', snappy_version.toString()
@@ -20,6 +24,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
allprojects {
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'org.cadixdev.licenser'
apply plugin: 'java'
compileJava.options.encoding = 'UTF-8'
@@ -37,17 +42,24 @@ allprojects {
maven { url 'https://repo.mattstudios.me/artifactory/public/' }
maven { url 'https://jitpack.io' }
maven { url 'https://libraries.minecraft.net/' }
maven { url 'https://william278.net/releases/' }
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
}
test {
useJUnitPlatform()
}
license {
header = rootProject.file('HEADER')
include '**/*.java'
newLine = true
}
processResources {
filter ReplaceTokens as Class, beginToken: '${', endToken: '}',
tokens: rootProject.ext.properties
@@ -58,6 +70,10 @@ subprojects {
version rootProject.version
archivesBaseName = "${rootProject.name}-${project.name.capitalize()}"
jar {
from '../LICENSE'
}
if (['bukkit', 'plugin'].contains(project.name)) {
shadowJar {
destinationDirectory.set(file("$rootDir/target"))
@@ -79,6 +95,35 @@ subprojects {
shadowJar.dependsOn(sourcesJar, javadocJar)
publishing {
repositories {
if (System.getenv("RELEASES_MAVEN_USERNAME") != null) {
maven {
name = "william278-releases"
url = "https://repo.william278.net/releases"
credentials {
username = System.getenv("RELEASES_MAVEN_USERNAME")
password = System.getenv("RELEASES_MAVEN_PASSWORD")
}
authentication {
basic(BasicAuthentication)
}
}
}
if (System.getenv("SNAPSHOTS_MAVEN_USERNAME") != null) {
maven {
name = "william278-snapshots"
url = "https://repo.william278.net/snapshots"
credentials {
username = System.getenv("SNAPSHOTS_MAVEN_USERNAME")
password = System.getenv("SNAPSHOTS_MAVEN_PASSWORD")
}
authentication {
basic(BasicAuthentication)
}
}
}
}
publications {
mavenJava(MavenPublication) {
groupId = 'net.william278'
@@ -101,8 +146,15 @@ logger.lifecycle("Building HuskSync ${version} by William278")
@SuppressWarnings('GrMethodMayBeStatic')
def versionMetadata() {
if (grgit == null) {
return System.getenv("GITHUB_RUN_NUMBER") ? 'build.' + System.getenv("GITHUB_RUN_NUMBER") : 'unknown'
// Get if there is a tag for this commit
def tag = grgit.tag.list().find { it.commit.id == grgit.head().id }
if (tag != null) {
return ''
}
return grgit.head().abbreviatedId + (grgit.status().clean ? '' : '-indev')
// Otherwise, get the last commit hash and if it's a clean head
if (grgit == null) {
return '-' + System.getenv("GITHUB_RUN_NUMBER") ? 'build.' + System.getenv("GITHUB_RUN_NUMBER") : 'unknown'
}
return '-' + grgit.head().abbreviatedId + (grgit.status().clean ? '' : '-indev')
}

View File

@@ -1,22 +1,22 @@
dependencies {
implementation project(path: ':common')
implementation 'org.bstats:bstats-bukkit:3.0.0'
implementation 'org.bstats:bstats-bukkit:3.0.2'
implementation 'net.william278:mpdbdataconverter:1.0.1'
implementation 'net.william278:hsldataconverter:1.0'
implementation 'net.william278:MapDataAPI:1.0.2'
implementation 'net.william278:AndJam:1.0.2'
implementation 'me.lucko:commodore:2.2'
implementation 'net.kyori:adventure-platform-bukkit:4.1.2'
implementation 'dev.triumphteam:triumph-gui:3.1.3'
implementation 'net.kyori:adventure-platform-bukkit:4.3.0'
implementation 'dev.triumphteam:triumph-gui:3.1.5'
compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'commons-io:commons-io:2.11.0'
compileOnly 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
compileOnly 'dev.dejvokep:boosted-yaml:1.3'
compileOnly 'commons-io:commons-io:2.13.0'
compileOnly 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT'
compileOnly 'dev.dejvokep:boosted-yaml:1.3.1'
compileOnly 'com.zaxxer:HikariCP:5.0.1'
compileOnly 'redis.clients:jedis:' + jedis_version
compileOnly 'net.william278:DesertWell:1.1'
compileOnly 'net.william278:Annotaml:2.0'
compileOnly 'net.william278:DesertWell:2.0.4'
compileOnly 'net.william278:Annotaml:2.0.1'
compileOnly 'net.william278:AdvancementAPI:97a9583413'
}

View File

@@ -1,8 +1,21 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.william278.annotaml.Annotaml;
import net.william278.desertwell.Version;
import net.william278.desertwell.util.Version;
import net.william278.husksync.command.BukkitCommand;
import net.william278.husksync.command.BukkitCommandType;
import net.william278.husksync.command.Permission;
@@ -92,7 +105,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
}
// Prepare data adapter
if (settings.compressData) {
if (settings.doCompressData()) {
dataAdapter = new CompressedDataAdapter();
} else {
dataAdapter = new JsonDataAdapter();
@@ -117,7 +130,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
log(Level.INFO, "Successfully established a connection to the database");
} else {
throw new HuskSyncInitializationException("Failed to establish a connection to the database. " +
"Please check the supplied database credentials in the config file");
"Please check the supplied database credentials in the config file");
}
// Prepare redis connection
@@ -128,7 +141,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
log(Level.INFO, "Successfully established a connection to the Redis server");
} else {
throw new HuskSyncInitializationException("Failed to establish a connection to the Redis server. " +
"Please check the supplied Redis credentials in the config file");
"Please check the supplied Redis credentials in the config file");
}
// Register events
@@ -169,12 +182,12 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
}
// Check for updates
if (settings.checkForUpdates) {
if (settings.doCheckForUpdates()) {
log(Level.INFO, "Checking for updates...");
getLatestVersionIfOutdated().thenAccept(newestVersion ->
newestVersion.ifPresent(newVersion -> log(Level.WARNING,
"An update is available for HuskSync, v" + newVersion
+ " (Currently running v" + getPluginVersion() + ")")));
+ " (Currently running v" + getPluginVersion() + ")")));
}
} catch (HuskSyncInitializationException exception) {
log(Level.SEVERE, """
@@ -265,7 +278,11 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
@Override
public void log(@NotNull Level level, @NotNull String message, @NotNull Throwable... throwable) {
getLogger().log(level, message, throwable);
if (throwable.length > 0) {
getLogger().log(level, message, throwable[0]);
} else {
getLogger().log(level, message);
}
}
@NotNull
@@ -277,7 +294,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
@NotNull
@Override
public Version getMinecraftVersion() {
return Version.fromMinecraftVersionString(Bukkit.getBukkitVersion());
return Version.fromString(Bukkit.getBukkitVersion());
}
/**
@@ -304,8 +321,8 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
// Load locales from language preset default
final Locales languagePresets = Annotaml.create(Locales.class,
Objects.requireNonNull(getResource("locales/" + settings.language + ".yml"))).get();
this.locales = Annotaml.create(new File(getDataFolder(), "messages_" + settings.language + ".yml"),
Objects.requireNonNull(getResource("locales/" + settings.getLanguage() + ".yml"))).get();
this.locales = Annotaml.create(new File(getDataFolder(), "messages_" + settings.getLanguage() + ".yml"),
languagePresets).get();
return true;
} catch (IOException | NullPointerException | InvocationTargetException | IllegalAccessException |

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.api;
import net.william278.husksync.BukkitHuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import me.lucko.commodore.CommodoreProvider;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import me.lucko.commodore.CommodoreProvider;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import net.william278.husksync.BukkitHuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.bukkit.inventory.ItemStack;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import net.william278.husksync.BukkitHuskSync;

View File

@@ -0,0 +1,67 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Optional;
public record BukkitPersistentTypeMapping<T, Z>(PersistentDataTagType type, PersistentDataType<T, Z> bukkitType) {
public static final BukkitPersistentTypeMapping<?, ?>[] PRIMITIVE_TYPE_MAPPINGS = new BukkitPersistentTypeMapping<?, ?>[]{
new BukkitPersistentTypeMapping<>(PersistentDataTagType.BYTE, PersistentDataType.BYTE),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.SHORT, PersistentDataType.SHORT),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.INTEGER, PersistentDataType.INTEGER),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.LONG, PersistentDataType.LONG),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.FLOAT, PersistentDataType.FLOAT),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.DOUBLE, PersistentDataType.DOUBLE),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.STRING, PersistentDataType.STRING),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.BYTE_ARRAY, PersistentDataType.BYTE_ARRAY),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.INTEGER_ARRAY, PersistentDataType.INTEGER_ARRAY),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.LONG_ARRAY, PersistentDataType.LONG_ARRAY),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.TAG_CONTAINER_ARRAY, PersistentDataType.TAG_CONTAINER_ARRAY),
new BukkitPersistentTypeMapping<>(PersistentDataTagType.TAG_CONTAINER, PersistentDataType.TAG_CONTAINER)
};
public BukkitPersistentTypeMapping(@NotNull PersistentDataTagType type, @NotNull PersistentDataType<T, Z> bukkitType) {
this.type = type;
this.bukkitType = bukkitType;
}
@NotNull
public PersistentDataTag<Z> getContainerValue(@NotNull PersistentDataContainer container, @NotNull NamespacedKey key) throws NullPointerException {
return new PersistentDataTag<>(type, Objects.requireNonNull(container.get(key, bukkitType)));
}
public void setContainerValue(@NotNull PersistentDataContainerData container, @NotNull Player player, @NotNull NamespacedKey key) throws NullPointerException {
container.getTagValue(key.toString(), bukkitType.getComplexType())
.ifPresent(value -> player.getPersistentDataContainer().set(key, bukkitType, value));
}
public static Optional<BukkitPersistentTypeMapping<?, ?>> getMapping(@NotNull PersistentDataTagType type) {
for (BukkitPersistentTypeMapping<?, ?> mapping : PRIMITIVE_TYPE_MAPPINGS) {
if (mapping.type().equals(type)) {
return Optional.of(mapping);
}
}
return Optional.empty();
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import net.william278.husksync.BukkitHuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.data.DataSaveCause;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.BukkitHuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.data.DataSaveCause;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.BukkitHuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.data.UserData;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import org.bukkit.entity.Player;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.listener;
import net.william278.husksync.config.Settings;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.listener;
import net.william278.husksync.BukkitHuskSync;
@@ -8,7 +21,6 @@ import net.william278.husksync.data.ItemData;
import net.william278.husksync.player.BukkitPlayer;
import net.william278.husksync.player.OnlineUser;
import org.bukkit.Bukkit;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
@@ -22,20 +34,26 @@ import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class BukkitEventListener extends EventListener implements BukkitJoinEventListener, BukkitQuitEventListener,
BukkitDeathEventListener, Listener {
protected final List<String> blacklistedCommands;
public BukkitEventListener(@NotNull BukkitHuskSync huskSync) {
super(huskSync);
this.blacklistedCommands = huskSync.getSettings().getBlacklistedCommandsWhileLocked();
Bukkit.getServer().getPluginManager().registerEvents(this, huskSync);
}
@@ -70,7 +88,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
}
// Handle saving player data snapshots on death
if (!plugin.getSettings().saveOnDeath) return;
if (!plugin.getSettings().doSaveOnDeath()) return;
// Truncate the drops list to the inventory size and save the player's inventory
final int maxInventorySize = BukkitInventoryMap.INVENTORY_SLOT_COUNT;
@@ -84,7 +102,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
@EventHandler(ignoreCancelled = true)
public void onWorldSave(@NotNull WorldSaveEvent event) {
// Handle saving player data snapshots when the world saves
if (!plugin.getSettings().saveOnWorldSave) return;
if (!plugin.getSettings().doSaveOnWorldSave()) return;
CompletableFuture.runAsync(() -> super.saveOnWorldSave(event.getWorld().getPlayers()
.stream().map(BukkitPlayer::adapt)
@@ -99,7 +117,7 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onProjectileLaunch(@NotNull ProjectileLaunchEvent event) {
final Projectile projectile = event.getEntity();
if (projectile.getShooter() instanceof Player player && projectile.getType() == EntityType.TRIDENT) {
if (projectile.getShooter() instanceof Player player) {
event.setCancelled(cancelPlayerEvent(player.getUniqueId()));
}
}
@@ -121,6 +139,11 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerInteractEntity(@NotNull PlayerInteractEntityEvent event) {
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockPlace(@NotNull BlockPlaceEvent event) {
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
@@ -150,4 +173,14 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPermissionCommand(@NotNull PlayerCommandPreprocessEvent event) {
String[] commandArgs = event.getMessage().substring(1).split(" ");
String commandLabel = commandArgs[0].toLowerCase(Locale.ENGLISH);
if (blacklistedCommands.contains(commandLabel)) {
event.setCancelled(cancelPlayerEvent(event.getPlayer().getUniqueId()));
}
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.listener;
import net.william278.husksync.config.Settings;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.listener;
import net.william278.husksync.config.Settings;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.migrator;
import com.zaxxer.hikari.HikariDataSource;
@@ -37,11 +50,11 @@ public class LegacyMigrator extends Migrator {
public LegacyMigrator(@NotNull HuskSync plugin) {
super(plugin);
this.hslConverter = HSLConverter.getInstance();
this.sourceHost = plugin.getSettings().mySqlHost;
this.sourcePort = plugin.getSettings().mySqlPort;
this.sourceUsername = plugin.getSettings().mySqlUsername;
this.sourcePassword = plugin.getSettings().mySqlPassword;
this.sourceDatabase = plugin.getSettings().mySqlDatabase;
this.sourceHost = plugin.getSettings().getMySqlHost();
this.sourcePort = plugin.getSettings().getMySqlPort();
this.sourceUsername = plugin.getSettings().getMySqlUsername();
this.sourcePassword = plugin.getSettings().getMySqlPassword();
this.sourceDatabase = plugin.getSettings().getMySqlDatabase();
this.sourcePlayersTable = "husksync_players";
this.sourceDataTable = "husksync_data";
this.minecraftVersion = plugin.getMinecraftVersion().toString();
@@ -66,7 +79,7 @@ public class LegacyMigrator extends Migrator {
connectionPool.setJdbcUrl(jdbcUrl);
connectionPool.setUsername(sourceUsername);
connectionPool.setPassword(sourcePassword);
connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase());
connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase(Locale.ENGLISH));
plugin.log(Level.INFO, "Downloading raw data from the legacy database (this might take a while)...");
final List<LegacyData> dataToMigrate = new ArrayList<>();
@@ -141,7 +154,7 @@ public class LegacyMigrator extends Migrator {
@Override
public void handleConfigurationCommand(@NotNull String[] args) {
if (args.length == 2) {
if (switch (args[0].toLowerCase()) {
if (switch (args[0].toLowerCase(Locale.ENGLISH)) {
case "host" -> {
this.sourceHost = args[1];
yield true;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.migrator;
import com.zaxxer.hikari.HikariDataSource;
@@ -17,6 +30,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -42,11 +56,11 @@ public class MpdbMigrator extends Migrator {
public MpdbMigrator(@NotNull BukkitHuskSync plugin, @NotNull Plugin mySqlPlayerDataBridge) {
super(plugin);
this.mpdbConverter = MPDBConverter.getInstance(mySqlPlayerDataBridge);
this.sourceHost = plugin.getSettings().mySqlHost;
this.sourcePort = plugin.getSettings().mySqlPort;
this.sourceUsername = plugin.getSettings().mySqlUsername;
this.sourcePassword = plugin.getSettings().mySqlPassword;
this.sourceDatabase = plugin.getSettings().mySqlDatabase;
this.sourceHost = plugin.getSettings().getMySqlHost();
this.sourcePort = plugin.getSettings().getMySqlPort();
this.sourceUsername = plugin.getSettings().getMySqlUsername();
this.sourcePassword = plugin.getSettings().getMySqlPassword();
this.sourceDatabase = plugin.getSettings().getMySqlDatabase();
this.sourceInventoryTable = "mpdb_inventory";
this.sourceEnderChestTable = "mpdb_enderchest";
this.sourceExperienceTable = "mpdb_experience";
@@ -73,7 +87,7 @@ public class MpdbMigrator extends Migrator {
connectionPool.setJdbcUrl(jdbcUrl);
connectionPool.setUsername(sourceUsername);
connectionPool.setPassword(sourcePassword);
connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase());
connectionPool.setPoolName((getIdentifier() + "_migrator_pool").toUpperCase(Locale.ENGLISH));
plugin.log(Level.INFO, "Downloading raw data from the MySQLPlayerDataBridge database (this might take a while)...");
final List<MpdbData> dataToMigrate = new ArrayList<>();
@@ -137,7 +151,7 @@ public class MpdbMigrator extends Migrator {
@Override
public void handleConfigurationCommand(@NotNull String[] args) {
if (args.length == 2) {
if (switch (args[0].toLowerCase()) {
if (switch (args[0].toLowerCase(Locale.ENGLISH)) {
case "host" -> {
this.sourceHost = args[1];
yield true;

View File

@@ -1,14 +1,26 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.player;
import de.themoep.minedown.adventure.MineDown;
import de.themoep.minedown.adventure.MineDownParser;
import dev.triumphteam.gui.builder.gui.StorageBuilder;
import dev.triumphteam.gui.guis.Gui;
import dev.triumphteam.gui.guis.StorageGui;
import net.kyori.adventure.audience.Audience;
import net.roxeez.advancement.display.FrameType;
import net.william278.andjam.Toast;
import net.william278.desertwell.Version;
import net.william278.desertwell.util.Version;
import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*;
@@ -19,9 +31,10 @@ import org.bukkit.attribute.Attribute;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
@@ -36,28 +49,14 @@ import java.util.logging.Level;
* Bukkit implementation of an {@link OnlineUser}
*/
public class BukkitPlayer extends OnlineUser {
private static final PersistentDataType<?, ?>[] PRIMITIVE_PERSISTENT_DATA_TYPES = new PersistentDataType<?, ?>[]{
PersistentDataType.BYTE,
PersistentDataType.SHORT,
PersistentDataType.INTEGER,
PersistentDataType.LONG,
PersistentDataType.FLOAT,
PersistentDataType.DOUBLE,
PersistentDataType.STRING,
PersistentDataType.BYTE_ARRAY,
PersistentDataType.INTEGER_ARRAY,
PersistentDataType.LONG_ARRAY,
PersistentDataType.TAG_CONTAINER_ARRAY,
PersistentDataType.TAG_CONTAINER};
private final BukkitHuskSync plugin;
private final Player player;
private final Audience audience;
private BukkitPlayer(@NotNull Player player) {
super(player.getUniqueId(), player.getName());
this.plugin = BukkitHuskSync.getInstance();
this.player = player;
this.audience = BukkitHuskSync.getInstance().getAudiences().player(player);
}
@NotNull
@@ -106,11 +105,11 @@ public class BukkitPlayer extends OnlineUser {
if (statusData.health != currentHealth) {
final double healthToSet = currentHealth > currentMaxHealth ? currentMaxHealth : statusData.health;
final double maxHealth = currentMaxHealth;
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
try {
player.setHealth(Math.min(healthToSet, maxHealth));
} catch (IllegalArgumentException e) {
BukkitHuskSync.getInstance().getLogger().log(Level.WARNING,
plugin.getLogger().log(Level.WARNING,
"Failed to set health of player " + player.getName() + " to " + healthToSet);
}
});
@@ -125,7 +124,7 @@ public class BukkitPlayer extends OnlineUser {
}
player.setHealthScaled(statusData.healthScale != 0D);
} catch (IllegalArgumentException e) {
BukkitHuskSync.getInstance().getLogger().log(Level.WARNING,
plugin.getLogger().log(Level.WARNING,
"Failed to set health scale of player " + player.getName() + " to " + statusData.healthScale);
}
}
@@ -143,11 +142,11 @@ public class BukkitPlayer extends OnlineUser {
player.setExp(statusData.expProgress);
}
if (settings.getSynchronizationFeature(Settings.SynchronizationFeature.GAME_MODE)) {
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () ->
Bukkit.getScheduler().runTask(plugin, () ->
player.setGameMode(GameMode.valueOf(statusData.gameMode)));
}
if (settings.getSynchronizationFeature(Settings.SynchronizationFeature.LOCATION)) {
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
if (statusData.isFlying) {
player.setAllowFlight(true);
player.setFlying(true);
@@ -160,7 +159,11 @@ public class BukkitPlayer extends OnlineUser {
@Override
public CompletableFuture<ItemData> getInventory() {
return BukkitSerializer.serializeItemStackArray(player.getInventory().getContents())
final PlayerInventory inventory = player.getInventory();
if (inventory.isEmpty()) {
return CompletableFuture.completedFuture(ItemData.empty());
}
return BukkitSerializer.serializeItemStackArray(inventory.getContents())
.thenApply(ItemData::new);
}
@@ -168,7 +171,7 @@ public class BukkitPlayer extends OnlineUser {
public CompletableFuture<Void> setInventory(@NotNull ItemData itemData) {
return BukkitSerializer.deserializeInventory(itemData.serializedItems).thenApplyAsync(contents -> {
final CompletableFuture<Void> inventorySetFuture = new CompletableFuture<>();
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
player.setItemOnCursor(null);
player.getInventory().setContents(contents.getContents());
player.updateInventory();
@@ -180,7 +183,11 @@ public class BukkitPlayer extends OnlineUser {
@Override
public CompletableFuture<ItemData> getEnderChest() {
return BukkitSerializer.serializeItemStackArray(player.getEnderChest().getContents())
final Inventory enderChest = player.getEnderChest();
if (enderChest.isEmpty()) {
return CompletableFuture.completedFuture(ItemData.empty());
}
return BukkitSerializer.serializeItemStackArray(enderChest.getContents())
.thenApply(ItemData::new);
}
@@ -188,7 +195,7 @@ public class BukkitPlayer extends OnlineUser {
public CompletableFuture<Void> setEnderChest(@NotNull ItemData enderChestData) {
return BukkitSerializer.deserializeItemStackArray(enderChestData.serializedItems).thenApplyAsync(contents -> {
final CompletableFuture<Void> enderChestSetFuture = new CompletableFuture<>();
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
player.getEnderChest().setContents(contents);
enderChestSetFuture.complete(null);
});
@@ -207,7 +214,7 @@ public class BukkitPlayer extends OnlineUser {
return BukkitSerializer.deserializePotionEffectArray(potionEffectData.serializedPotionEffects)
.thenApplyAsync(effects -> {
final CompletableFuture<Void> potionEffectsSetFuture = new CompletableFuture<>();
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
for (PotionEffect effect : player.getActivePotionEffects()) {
player.removePotionEffect(effect.getType());
}
@@ -245,7 +252,7 @@ public class BukkitPlayer extends OnlineUser {
@Override
public CompletableFuture<Void> setAdvancements(@NotNull List<AdvancementData> advancementData) {
return CompletableFuture.runAsync(() -> Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
return CompletableFuture.runAsync(() -> Bukkit.getScheduler().runTask(plugin, () -> {
// Temporarily disable advancement announcing if needed
boolean announceAdvancementUpdate = false;
@@ -277,20 +284,20 @@ public class BukkitPlayer extends OnlineUser {
record.completedCriteria.keySet().stream()
.filter(criterion -> !playerProgress.getAwardedCriteria().contains(criterion))
.forEach(criterion -> {
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(),
Bukkit.getScheduler().runTask(plugin,
() -> player.getAdvancementProgress(advancement).awardCriteria(criterion));
correctExperience.set(true);
});
// Revoke all criteria that the player does have but should not
new ArrayList<>(playerProgress.getAwardedCriteria()).stream().filter(criterion -> !record.completedCriteria.containsKey(criterion))
.forEach(criterion -> Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(),
.forEach(criterion -> Bukkit.getScheduler().runTask(plugin,
() -> player.getAdvancementProgress(advancement).revokeCriteria(criterion)));
},
// Revoke the criteria as the player shouldn't have any
() -> new ArrayList<>(playerProgress.getAwardedCriteria()).forEach(criterion ->
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(),
Bukkit.getScheduler().runTask(plugin,
() -> player.getAdvancementProgress(advancement).revokeCriteria(criterion))));
// Update the player's experience in case the advancement changed that
@@ -302,7 +309,7 @@ public class BukkitPlayer extends OnlineUser {
}
// Re-enable announcing advancements (back on main thread again)
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
if (finalAnnounceAdvancementUpdate) {
player.getWorld().setGameRule(GameRule.ANNOUNCE_ADVANCEMENTS, true);
}
@@ -372,7 +379,7 @@ public class BukkitPlayer extends OnlineUser {
try {
player.setStatistic(Statistic.valueOf(statistic), statisticsData.untypedStatistics.get(statistic));
} catch (IllegalArgumentException e) {
BukkitHuskSync.getInstance().getLogger().log(Level.WARNING,
plugin.getLogger().log(Level.WARNING,
"Failed to set generic statistic " + statistic + " for " + username);
}
}
@@ -384,7 +391,7 @@ public class BukkitPlayer extends OnlineUser {
player.setStatistic(Statistic.valueOf(statistic), Material.valueOf(blockMaterial),
statisticsData.blockStatistics.get(statistic).get(blockMaterial));
} catch (IllegalArgumentException e) {
BukkitHuskSync.getInstance().getLogger().log(Level.WARNING,
plugin.getLogger().log(Level.WARNING,
"Failed to set " + blockMaterial + " statistic " + statistic + " for " + username);
}
}
@@ -397,7 +404,7 @@ public class BukkitPlayer extends OnlineUser {
player.setStatistic(Statistic.valueOf(statistic), Material.valueOf(itemMaterial),
statisticsData.itemStatistics.get(statistic).get(itemMaterial));
} catch (IllegalArgumentException e) {
BukkitHuskSync.getInstance().getLogger().log(Level.WARNING,
plugin.getLogger().log(Level.WARNING,
"Failed to set " + itemMaterial + " statistic " + statistic + " for " + username);
}
}
@@ -410,7 +417,7 @@ public class BukkitPlayer extends OnlineUser {
player.setStatistic(Statistic.valueOf(statistic), EntityType.valueOf(entityType),
statisticsData.entityStatistics.get(statistic).get(entityType));
} catch (IllegalArgumentException e) {
BukkitHuskSync.getInstance().getLogger().log(Level.WARNING,
plugin.getLogger().log(Level.WARNING,
"Failed to set " + entityType + " statistic " + statistic + " for " + username);
}
}
@@ -438,7 +445,7 @@ public class BukkitPlayer extends OnlineUser {
.valueOf(locationData.worldEnvironment)).findFirst().ifPresent(bukkitWorld::set);
}
if (bukkitWorld.get() != null) {
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> {
Bukkit.getScheduler().runTask(plugin, () -> {
player.teleport(new Location(bukkitWorld.get(),
locationData.x, locationData.y, locationData.z,
locationData.yaw, locationData.pitch), PlayerTeleportEvent.TeleportCause.PLUGIN);
@@ -450,65 +457,24 @@ public class BukkitPlayer extends OnlineUser {
@Override
public CompletableFuture<PersistentDataContainerData> getPersistentDataContainer() {
final Map<String, PersistentDataTag<?>> persistentDataMap = new HashMap<>();
final PersistentDataContainer container = player.getPersistentDataContainer();
return CompletableFuture.supplyAsync(() -> {
final PersistentDataContainer container = player.getPersistentDataContainer();
if (container.isEmpty()) {
return new PersistentDataContainerData(new HashMap<>());
}
final HashMap<String, PersistentDataTag<?>> persistentDataMap = new HashMap<>();
for (final NamespacedKey key : container.getKeys()) {
PersistentDataType<?, ?> type = null;
for (PersistentDataType<?, ?> dataType : PRIMITIVE_PERSISTENT_DATA_TYPES) {
if (container.has(key, dataType)) {
container.getKeys().forEach(key -> {
BukkitPersistentTypeMapping<?, ?> type = null;
for (BukkitPersistentTypeMapping<?, ?> dataType : BukkitPersistentTypeMapping.PRIMITIVE_TYPE_MAPPINGS) {
if (container.has(key, dataType.bukkitType())) {
type = dataType;
break;
}
}
if (type != null) {
// This is absolutely disgusting code and needs to be swiftly put out of its misery with a refactor
final Class<?> primitiveType = type.getPrimitiveType();
if (String.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.STRING,
Objects.requireNonNull(container.get(key, PersistentDataType.STRING))));
} else if (int.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.INTEGER,
Objects.requireNonNull(container.get(key, PersistentDataType.INTEGER))));
} else if (double.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.DOUBLE,
Objects.requireNonNull(container.get(key, PersistentDataType.DOUBLE))));
} else if (float.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.FLOAT,
Objects.requireNonNull(container.get(key, PersistentDataType.FLOAT))));
} else if (long.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.LONG,
Objects.requireNonNull(container.get(key, PersistentDataType.LONG))));
} else if (short.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.SHORT,
Objects.requireNonNull(container.get(key, PersistentDataType.SHORT))));
} else if (byte.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.BYTE,
Objects.requireNonNull(container.get(key, PersistentDataType.BYTE))));
} else if (byte[].class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.BYTE_ARRAY,
Objects.requireNonNull(container.get(key, PersistentDataType.BYTE_ARRAY))));
} else if (int[].class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.INTEGER_ARRAY,
Objects.requireNonNull(container.get(key, PersistentDataType.INTEGER_ARRAY))));
} else if (long[].class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.LONG_ARRAY,
Objects.requireNonNull(container.get(key, PersistentDataType.LONG_ARRAY))));
} else if (PersistentDataContainer.class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.TAG_CONTAINER,
Objects.requireNonNull(container.get(key, PersistentDataType.TAG_CONTAINER))));
} else if (PersistentDataContainer[].class.equals(primitiveType)) {
persistentDataMap.put(key.toString(), new PersistentDataTag<>(BukkitPersistentDataTagType.TAG_CONTAINER_ARRAY,
Objects.requireNonNull(container.get(key, PersistentDataType.TAG_CONTAINER_ARRAY))));
}
persistentDataMap.put(key.toString(), type.getContainerValue(container, key));
}
}
});
return new PersistentDataContainerData(persistentDataMap);
}).exceptionally(throwable -> {
BukkitHuskSync.getInstance().log(Level.WARNING,
plugin.log(Level.WARNING,
"Could not read " + player.getName() + "'s persistent data map, skipping!");
throwable.printStackTrace();
return new PersistentDataContainerData(new HashMap<>());
@@ -523,61 +489,29 @@ public class BukkitPlayer extends OnlineUser {
container.getTags().forEach(keyString -> {
final NamespacedKey key = NamespacedKey.fromString(keyString);
if (key != null) {
// Set a tag with the given key and value. This is crying out for a refactor.
container.getTagType(keyString).ifPresentOrElse(dataType -> {
switch (dataType) {
case BYTE -> container.getTagValue(keyString, byte.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.BYTE, value));
case SHORT -> container.getTagValue(keyString, short.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.SHORT, value));
case INTEGER -> container.getTagValue(keyString, int.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.INTEGER, value));
case LONG -> container.getTagValue(keyString, long.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.LONG, value));
case FLOAT -> container.getTagValue(keyString, float.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.FLOAT, value));
case DOUBLE -> container.getTagValue(keyString, double.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.DOUBLE, value));
case STRING -> container.getTagValue(keyString, String.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.STRING, value));
case BYTE_ARRAY -> container.getTagValue(keyString, byte[].class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.BYTE_ARRAY, value));
case INTEGER_ARRAY -> container.getTagValue(keyString, int[].class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.INTEGER_ARRAY, value));
case LONG_ARRAY -> container.getTagValue(keyString, long[].class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.LONG_ARRAY, value));
case TAG_CONTAINER ->
container.getTagValue(keyString, PersistentDataContainer.class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.TAG_CONTAINER, value));
case TAG_CONTAINER_ARRAY ->
container.getTagValue(keyString, PersistentDataContainer[].class).ifPresent(
value -> player.getPersistentDataContainer().set(key,
PersistentDataType.TAG_CONTAINER_ARRAY, value));
}
}, () -> BukkitHuskSync.getInstance().log(Level.WARNING,
"Could not set " + player.getName() + "'s persistent data key " + keyString +
" as it has an invalid type. Skipping!"));
container.getTagType(keyString)
.flatMap(BukkitPersistentTypeMapping::getMapping)
.ifPresentOrElse(mapping -> mapping.setContainerValue(container, player, key),
() -> plugin.log(Level.WARNING,
"Could not set " + player.getName() + "'s persistent data key " + keyString +
" as it has an invalid type. Skipping!"));
}
});
}).exceptionally(throwable -> {
BukkitHuskSync.getInstance().log(Level.WARNING,
plugin.log(Level.WARNING,
"Could not write " + player.getName() + "'s persistent data map, skipping!");
throwable.printStackTrace();
return null;
});
}
@Override
@NotNull
public Audience getAudience() {
return plugin.getAudiences().player(player);
}
@Override
public boolean isOffline() {
try {
@@ -591,7 +525,7 @@ public class BukkitPlayer extends OnlineUser {
@NotNull
@Override
public Version getMinecraftVersion() {
return Version.fromMinecraftVersionString(Bukkit.getBukkitVersion());
return Version.fromString(Bukkit.getBukkitVersion());
}
@Override
@@ -640,7 +574,7 @@ public class BukkitPlayer extends OnlineUser {
});
// Display the GUI (synchronously; on the main server thread)
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> gui.open(player));
Bukkit.getScheduler().runTask(plugin, () -> gui.open(player));
}).exceptionally(throwable -> {
// Handle exceptions
updatedData.completeExceptionally(throwable);
@@ -654,19 +588,12 @@ public class BukkitPlayer extends OnlineUser {
return player.getHealth() <= 0;
}
@Override
public void sendActionBar(@NotNull MineDown mineDown) {
audience.sendActionBar(mineDown
.disable(MineDownParser.Option.SIMPLE_FORMATTING)
.replace().toComponent());
}
@Override
public void sendToast(@NotNull MineDown title, @NotNull MineDown description,
@NotNull String iconMaterial, @NotNull String backgroundType) {
try {
final Material material = Material.matchMaterial(iconMaterial);
Toast.builder(BukkitHuskSync.getInstance())
Toast.builder(plugin)
.setTitle(title.toComponent())
.setDescription(description.toComponent())
.setIcon(material != null ? material : Material.BARRIER)
@@ -678,13 +605,6 @@ public class BukkitPlayer extends OnlineUser {
}
}
@Override
public void sendMessage(@NotNull MineDown mineDown) {
audience.sendMessage(mineDown
.disable(MineDownParser.Option.SIMPLE_FORMATTING)
.replace().toComponent());
}
/**
* Returns a {@link Player}'s maximum health, minus any health boost effects
*
@@ -706,7 +626,7 @@ public class BukkitPlayer extends OnlineUser {
@Override
public boolean isLocked() {
return BukkitHuskSync.getInstance().getLockedPlayers().contains(player.getUniqueId());
return plugin.getLockedPlayers().contains(player.getUniqueId());
}
@Override

View File

@@ -1,29 +1,29 @@
name: HuskSync
version: ${version}
main: net.william278.husksync.BukkitHuskSync
name: 'HuskSync'
version: '${version}'
main: 'net.william278.husksync.BukkitHuskSync'
api-version: 1.16
author: William278
description: 'A modern, cross-server player data synchronization system'
author: 'William278'
description: '${description}'
website: 'https://william278.net'
softdepend:
- MysqlPlayerDataBridge
- Plan
- 'MysqlPlayerDataBridge'
- 'Plan'
libraries:
- 'redis.clients:jedis:${jedis_version}'
- 'mysql:mysql-connector-java:${mysql_driver_version}'
- 'com.mysql:mysql-connector-j:${mysql_driver_version}'
- 'org.xerial.snappy:snappy-java:${snappy_version}'
- 'org.apache.commons:commons-text:${commons_text_version}'
commands:
husksync:
usage: '/husksync <update/info/reload/migrate>'
usage: '/<command> <update/info/reload/migrate>'
description: 'Manage the HuskSync plugin'
userdata:
usage: '/userdata <view/list/delete/restore/pin/dump> <username> [version_uuid]'
usage: '/<command> <view/list/delete/restore/pin/dump> <username> [version_uuid]'
description: 'View, manage & restore player userdata'
inventory:
usage: '/inventory <username> [version_uuid]'
usage: '/<command> <username> [version_uuid]'
description: 'View & edit a player''s inventory'
enderchest:
usage: '/enderchest <username> [version_uuid]'
usage: '/<command> <username> [version_uuid]'
description: 'View & edit a player''s Ender Chest'

View File

@@ -1,29 +1,28 @@
dependencies {
implementation 'commons-io:commons-io:2.11.0'
implementation 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT'
implementation 'net.kyori:adventure-api:4.11.0'
implementation 'com.google.code.gson:gson:2.10'
implementation 'dev.dejvokep:boosted-yaml:1.3'
implementation 'net.william278:Annotaml:2.0'
implementation 'net.william278:DesertWell:1.1'
implementation 'commons-io:commons-io:2.13.0'
implementation 'de.themoep:minedown-adventure:1.7.2-SNAPSHOT'
implementation 'net.kyori:adventure-api:4.14.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'dev.dejvokep:boosted-yaml:1.3.1'
implementation 'net.william278:Annotaml:2.0.1'
implementation 'net.william278:DesertWell:2.0.4'
implementation 'net.william278:PagineDown:1.1'
implementation('com.zaxxer:HikariCP:5.0.1') {
exclude module: 'slf4j-api'
}
compileOnly 'org.jetbrains:annotations:23.0.0'
compileOnly 'com.github.plan-player-analytics:Plan:5.4.1690'
compileOnly 'org.jetbrains:annotations:24.0.1'
compileOnly 'com.github.plan-player-analytics:Plan:5.5.2272'
compileOnly 'redis.clients:jedis:' + jedis_version
compileOnly 'org.xerial.snappy:snappy-java:' + snappy_version
compileOnly 'org.apache.commons:commons-text:' + commons_text_version
testImplementation 'org.xerial.snappy:snappy-java:1.1.8.4'
testImplementation 'com.github.plan-player-analytics:Plan:5.4.1690'
testImplementation 'com.github.plan-player-analytics:Plan:5.5.2272'
testImplementation 'redis.clients:jedis:' + jedis_version
testImplementation 'org.xerial.snappy:snappy-java:' + snappy_version
testImplementation 'org.apache.commons:commons-text:' + commons_text_version
testCompileOnly 'dev.dejvokep:boosted-yaml:1.3'
testCompileOnly 'org.jetbrains:annotations:23.0.0'
testCompileOnly 'dev.dejvokep:boosted-yaml:1.3.1'
testCompileOnly 'org.jetbrains:annotations:24.0.1'
}
shadowJar {

View File

@@ -1,6 +1,20 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync;
import net.william278.desertwell.UpdateChecker;
import net.william278.desertwell.util.UpdateChecker;
import net.william278.desertwell.util.Version;
import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataAdapter;
@@ -9,7 +23,6 @@ import net.william278.husksync.event.EventCannon;
import net.william278.husksync.migrator.Migrator;
import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.redis.RedisManager;
import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@@ -126,7 +139,7 @@ public interface HuskSync {
* @param throwable a throwable to log
*/
default void debug(@NotNull String message, @NotNull Throwable... throwable) {
if (getSettings().debugLogging) {
if (getSettings().doDebugLogging()) {
log(Level.INFO, "[DEBUG] " + message, throwable);
}
}
@@ -153,14 +166,14 @@ public interface HuskSync {
* @return a {@link CompletableFuture} returning the latest {@link Version} if the current one is out-of-date
*/
default CompletableFuture<Optional<Version>> getLatestVersionIfOutdated() {
final UpdateChecker updateChecker = UpdateChecker.create(getPluginVersion(), SPIGOT_RESOURCE_ID);
return updateChecker.isUpToDate().thenApply(upToDate -> {
if (upToDate) {
return Optional.empty();
} else {
return Optional.of(updateChecker.getLatestVersion().join());
}
});
return UpdateChecker.builder()
.currentVersion(getPluginVersion())
.endpoint(UpdateChecker.Endpoint.SPIGOT)
.resource(Integer.toString(SPIGOT_RESOURCE_ID)).build()
.check()
.thenApply(checked -> checked.isUpToDate()
? Optional.empty()
: Optional.of(checked.getLatestVersion()));
}
/**

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.api;
import net.william278.husksync.HuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import net.william278.husksync.HuskSync;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import de.themoep.minedown.adventure.MineDown;
@@ -12,6 +25,7 @@ import org.jetbrains.annotations.NotNull;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -31,7 +45,7 @@ public class EnderChestCommand extends CommandBase implements TabCompletable {
.ifPresent(player::sendMessage);
return;
}
plugin.getDatabase().getUserByName(args[0].toLowerCase()).thenAccept(optionalUser ->
plugin.getDatabase().getUserByName(args[0].toLowerCase(Locale.ENGLISH)).thenAccept(optionalUser ->
optionalUser.ifPresentOrElse(user -> {
if (args.length == 2) {
// View user data by specified UUID

View File

@@ -1,16 +1,28 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import de.themoep.minedown.adventure.MineDown;
import net.william278.desertwell.AboutMenu;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.william278.desertwell.about.AboutMenu;
import net.william278.husksync.HuskSync;
import net.william278.husksync.migrator.Migrator;
import net.william278.husksync.player.OnlineUser;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -21,30 +33,32 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
public HuskSyncCommand(@NotNull HuskSync implementor) {
super("husksync", Permission.COMMAND_HUSKSYNC, implementor);
this.aboutMenu = AboutMenu.create("HuskSync")
.withDescription("A modern, cross-server player data synchronization system")
.withVersion(implementor.getPluginVersion())
.addAttribution("Author",
AboutMenu.Credit.of("William278").withDescription("Click to visit website").withUrl("https://william278.net"))
.addAttribution("Contributors",
AboutMenu.Credit.of("HarvelsX").withDescription("Code"),
AboutMenu.Credit.of("HookWoods").withDescription("Code"))
.addAttribution("Translators",
AboutMenu.Credit.of("Namiu").withDescription("Japanese (ja-jp)"),
AboutMenu.Credit.of("anchelthe").withDescription("Spanish (es-es)"),
AboutMenu.Credit.of("Melonzio").withDescription("Spanish (es-es)"),
AboutMenu.Credit.of("Ceddix").withDescription("German (de-de)"),
AboutMenu.Credit.of("Pukejoy_1").withDescription("Bulgarian (bg-bg)"),
AboutMenu.Credit.of("mateusneresrb").withDescription("Brazilian Portuguese (pt-br)"),
AboutMenu.Credit.of("小蔡").withDescription("Traditional Chinese (zh-tw)"),
AboutMenu.Credit.of("Ghost-chu").withDescription("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("DJelly4K").withDescription("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("Thourgard").withDescription("Ukrainian (uk-ua)"),
AboutMenu.Credit.of("xF3d3").withDescription("Italian (it-it)"))
.addButtons(
AboutMenu.Link.of("https://william278.net/docs/husksync").withText("Documentation").withIcon(""),
AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").withText("Issues").withIcon("").withColor("#ff9f0f"),
AboutMenu.Link.of("https://discord.gg/tVYhJfyDWG").withText("Discord").withIcon("").withColor("#6773f5"));
this.aboutMenu = AboutMenu.builder()
.title(Component.text("HuskSync"))
.description(Component.text("A modern, cross-server player data synchronization system"))
.version(implementor.getPluginVersion())
.credits("Author",
AboutMenu.Credit.of("William278").description("Click to visit website").url("https://william278.net"))
.credits("Contributors",
AboutMenu.Credit.of("HarvelsX").description("Code"),
AboutMenu.Credit.of("HookWoods").description("Code"))
.credits("Translators",
AboutMenu.Credit.of("Namiu").description("Japanese (ja-jp)"),
AboutMenu.Credit.of("anchelthe").description("Spanish (es-es)"),
AboutMenu.Credit.of("Melonzio").description("Spanish (es-es)"),
AboutMenu.Credit.of("Ceddix").description("German (de-de)"),
AboutMenu.Credit.of("Pukejoy_1").description("Bulgarian (bg-bg)"),
AboutMenu.Credit.of("mateusneresrb").description("Brazilian Portuguese (pt-br)"),
AboutMenu.Credit.of("小蔡").description("Traditional Chinese (zh-tw)"),
AboutMenu.Credit.of("Ghost-chu").description("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("DJelly4K").description("Simplified Chinese (zh-cn)"),
AboutMenu.Credit.of("Thourgard").description("Ukrainian (uk-ua)"),
AboutMenu.Credit.of("xF3d3").description("Italian (it-it)"))
.buttons(
AboutMenu.Link.of("https://william278.net/docs/husksync").text("Documentation").icon(""),
AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").text("Issues").icon("").color(TextColor.color(0xff9f0f)),
AboutMenu.Link.of("https://discord.gg/tVYhJfyDWG").text("Discord").icon("").color(TextColor.color(0x6773f5)))
.build();
}
@Override
@@ -53,7 +67,7 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
sendAboutMenu(player);
return;
}
switch (args[0].toLowerCase()) {
switch (args[0].toLowerCase(Locale.ENGLISH)) {
case "update", "version" -> {
if (!player.hasPermission(Permission.COMMAND_HUSKSYNC_UPDATE.node)) {
plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage);
@@ -91,7 +105,7 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
plugin.log(Level.INFO, "Console usage: \"husksync <update/about/reload/migrate>\"");
return;
}
switch (args[0].toLowerCase()) {
switch (args[0].toLowerCase(Locale.ENGLISH)) {
case "update", "version" -> plugin.getLatestVersionIfOutdated().thenAccept(newestVersion ->
newestVersion.ifPresentOrElse(newVersion -> plugin.log(Level.WARNING,
"An update is available for HuskSync, v" + newVersion
@@ -165,6 +179,6 @@ public class HuskSyncCommand extends CommandBase implements TabCompletable, Cons
plugin.getLocales().getLocale("error_no_permission").ifPresent(player::sendMessage);
return;
}
player.sendMessage(aboutMenu.toMineDown());
player.sendMessage(aboutMenu.toComponent());
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import de.themoep.minedown.adventure.MineDown;
@@ -12,6 +25,7 @@ import org.jetbrains.annotations.NotNull;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -31,7 +45,7 @@ public class InventoryCommand extends CommandBase implements TabCompletable {
.ifPresent(player::sendMessage);
return;
}
plugin.getDatabase().getUserByName(args[0].toLowerCase()).thenAccept(optionalUser ->
plugin.getDatabase().getUserByName(args[0].toLowerCase(Locale.ENGLISH)).thenAccept(optionalUser ->
optionalUser.ifPresentOrElse(user -> {
if (args.length == 2) {
// View user data by specified UUID

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,18 +1,28 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.command;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSaveCause;
import net.william278.husksync.data.UserData;
import net.william278.husksync.util.DataSnapshotList;
import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.util.DataDumper;
import net.william278.husksync.util.DataSnapshotList;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -34,7 +44,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
return;
}
switch (args[0].toLowerCase()) {
switch (args[0].toLowerCase(Locale.ENGLISH)) {
case "view" -> {
if (args.length < 2) {
plugin.getLocales().getLocale("error_invalid_syntax",
@@ -47,7 +57,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
try {
final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser
.ifPresentOrElse(user -> plugin.getDatabase().getUserData(user, versionUuid)
.thenAccept(data -> data.ifPresentOrElse(
@@ -63,7 +73,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
}
} else {
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser
.ifPresentOrElse(user -> plugin.getDatabase().getCurrentUserData(user)
.thenAccept(latestData -> latestData.ifPresentOrElse(
@@ -87,7 +97,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
}
final String username = args[1];
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user).thenAccept(dataList -> {
// Check if there is data to display
@@ -133,7 +143,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
try {
final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().deleteUserData(user, versionUuid).thenAccept(deleted -> {
if (deleted) {
@@ -172,7 +182,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
try {
final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(data -> {
if (data.isEmpty()) {
@@ -219,7 +229,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
try {
final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(
optionalUserData -> optionalUserData.ifPresentOrElse(userData -> {
@@ -267,7 +277,7 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
try {
final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase()
.getUserByName(username.toLowerCase())
.getUserByName(username.toLowerCase(Locale.ENGLISH))
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(
optionalUserData -> optionalUserData.ifPresentOrElse(userData -> {

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.config;
import de.themoep.minedown.adventure.MineDown;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.config;
import net.william278.annotaml.YamlComment;
@@ -5,8 +18,7 @@ import net.william278.annotaml.YamlFile;
import net.william278.annotaml.YamlKey;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Map;
import java.util.*;
/**
* Plugin settings, read from config.yml
@@ -18,126 +30,266 @@ import java.util.Map;
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┣╸ Information: https://william278.net/project/husksync
┗╸ Documentation: https://william278.net/docs/husksync""",
versionField = "config_version", versionNumber = 3)
versionField = "config_version", versionNumber = 4)
public class Settings {
// Top-level settings
public String language = "en-gb";
@YamlKey("language")
private String language = "en-gb";
@YamlKey("check_for_updates")
public boolean checkForUpdates = true;
private boolean checkForUpdates = true;
@YamlKey("cluster_id")
public String clusterId = "";
private String clusterId = "";
@YamlKey("debug_logging")
public boolean debugLogging = false;
private boolean debugLogging = false;
// Database settings
@YamlComment("Database connection settings")
@YamlKey("database.credentials.host")
public String mySqlHost = "localhost";
private String mySqlHost = "localhost";
@YamlKey("database.credentials.port")
public int mySqlPort = 3306;
private int mySqlPort = 3306;
@YamlKey("database.credentials.database")
public String mySqlDatabase = "HuskSync";
private String mySqlDatabase = "HuskSync";
@YamlKey("database.credentials.username")
public String mySqlUsername = "root";
private String mySqlUsername = "root";
@YamlKey("database.credentials.password")
public String mySqlPassword = "pa55w0rd";
private String mySqlPassword = "pa55w0rd";
@YamlKey("database.credentials.parameters")
public String mySqlConnectionParameters = "?autoReconnect=true&useSSL=false";
private String mySqlConnectionParameters = "?autoReconnect=true&useSSL=false";
@YamlComment("MySQL connection pool properties")
@YamlKey("database.connection_pool.maximum_pool_size")
public int mySqlConnectionPoolSize = 10;
private int mySqlConnectionPoolSize = 10;
@YamlKey("database.connection_pool.minimum_idle")
public int mySqlConnectionPoolIdle = 10;
private int mySqlConnectionPoolIdle = 10;
@YamlKey("database.connection_pool.maximum_lifetime")
public long mySqlConnectionPoolLifetime = 1800000;
private long mySqlConnectionPoolLifetime = 1800000;
@YamlKey("database.connection_pool.keepalive_time")
public long mySqlConnectionPoolKeepAlive = 0;
private long mySqlConnectionPoolKeepAlive = 0;
@YamlKey("database.connection_pool.connection_timeout")
public long mySqlConnectionPoolTimeout = 5000;
private long mySqlConnectionPoolTimeout = 5000;
@YamlKey("database.table_names")
public Map<String, String> tableNames = TableName.getDefaults();
@NotNull
public String getTableName(@NotNull TableName tableName) {
return tableNames.getOrDefault(tableName.name().toLowerCase(), tableName.defaultName);
}
private Map<String, String> tableNames = TableName.getDefaults();
// Redis settings
@YamlComment("Redis connection settings")
@YamlKey("redis.credentials.host")
public String redisHost = "localhost";
private String redisHost = "localhost";
@YamlKey("redis.credentials.port")
public int redisPort = 6379;
private int redisPort = 6379;
@YamlKey("redis.credentials.password")
public String redisPassword = "";
private String redisPassword = "";
@YamlKey("redis.use_ssl")
public boolean redisUseSsl = false;
private boolean redisUseSsl = false;
// Synchronization settings
@YamlComment("Synchronization settings")
@YamlKey("synchronization.max_user_data_snapshots")
public int maxUserDataSnapshots = 5;
private int maxUserDataSnapshots = 5;
@YamlKey("synchronization.save_on_world_save")
public boolean saveOnWorldSave = true;
private boolean saveOnWorldSave = true;
@YamlKey("synchronization.save_on_death")
public boolean saveOnDeath = false;
private boolean saveOnDeath = false;
@YamlKey("synchronization.save_empty_drops_on_death")
private boolean saveEmptyDropsOnDeath = true;
@YamlKey("synchronization.compress_data")
public boolean compressData = true;
private boolean compressData = true;
@YamlKey("synchronization.notification_display_slot")
public NotificationDisplaySlot notificationDisplaySlot = NotificationDisplaySlot.ACTION_BAR;
private NotificationDisplaySlot notificationDisplaySlot = NotificationDisplaySlot.ACTION_BAR;
@YamlKey("synchronization.save_dead_player_inventories")
public boolean saveDeadPlayerInventories = true;
@YamlKey("synchronization.synchronise_dead_players_changing_server")
private boolean synchroniseDeadPlayersChangingServer = true;
@YamlKey("synchronization.network_latency_milliseconds")
public int networkLatencyMilliseconds = 500;
private int networkLatencyMilliseconds = 500;
@YamlKey("synchronization.features")
public Map<String, Boolean> synchronizationFeatures = SynchronizationFeature.getDefaults();
private Map<String, Boolean> synchronizationFeatures = SynchronizationFeature.getDefaults();
public boolean getSynchronizationFeature(@NotNull SynchronizationFeature feature) {
return synchronizationFeatures.getOrDefault(feature.name().toLowerCase(), feature.enabledByDefault);
}
@YamlKey("synchronization.blacklisted_commands_while_locked")
private List<String> blacklistedCommandsWhileLocked = new ArrayList<>();
@YamlKey("synchronization.event_priorities")
public Map<String, String> synchronizationEventPriorities = EventType.getDefaults();
private Map<String, String> synchronizationEventPriorities = EventType.getDefaults();
// Zero-args constructor for instantiation via Annotaml
public Settings() {
}
@NotNull
public String getLanguage() {
return language;
}
public boolean doCheckForUpdates() {
return checkForUpdates;
}
@NotNull
public String getClusterId() {
return clusterId;
}
public boolean doDebugLogging() {
return debugLogging;
}
@NotNull
public String getMySqlHost() {
return mySqlHost;
}
public int getMySqlPort() {
return mySqlPort;
}
@NotNull
public String getMySqlDatabase() {
return mySqlDatabase;
}
@NotNull
public String getMySqlUsername() {
return mySqlUsername;
}
@NotNull
public String getMySqlPassword() {
return mySqlPassword;
}
@NotNull
public String getMySqlConnectionParameters() {
return mySqlConnectionParameters;
}
@NotNull
public String getTableName(@NotNull TableName tableName) {
return tableNames.getOrDefault(tableName.name().toLowerCase(Locale.ENGLISH), tableName.defaultName);
}
public int getMySqlConnectionPoolSize() {
return mySqlConnectionPoolSize;
}
public int getMySqlConnectionPoolIdle() {
return mySqlConnectionPoolIdle;
}
public long getMySqlConnectionPoolLifetime() {
return mySqlConnectionPoolLifetime;
}
public long getMySqlConnectionPoolKeepAlive() {
return mySqlConnectionPoolKeepAlive;
}
public long getMySqlConnectionPoolTimeout() {
return mySqlConnectionPoolTimeout;
}
@NotNull
public String getRedisHost() {
return redisHost;
}
public int getRedisPort() {
return redisPort;
}
@NotNull
public String getRedisPassword() {
return redisPassword;
}
public boolean isRedisUseSsl() {
return redisUseSsl;
}
public int getMaxUserDataSnapshots() {
return maxUserDataSnapshots;
}
public boolean doSaveOnWorldSave() {
return saveOnWorldSave;
}
public boolean doSaveOnDeath() {
return saveOnDeath;
}
public boolean doSaveEmptyDropsOnDeath() {
return saveEmptyDropsOnDeath;
}
public boolean doCompressData() {
return compressData;
}
@NotNull
public NotificationDisplaySlot getNotificationDisplaySlot() {
return notificationDisplaySlot;
}
public boolean isSynchroniseDeadPlayersChangingServer() {
return synchroniseDeadPlayersChangingServer;
}
public int getNetworkLatencyMilliseconds() {
return networkLatencyMilliseconds;
}
@NotNull
public Map<String, Boolean> getSynchronizationFeatures() {
return synchronizationFeatures;
}
public boolean getSynchronizationFeature(@NotNull SynchronizationFeature feature) {
return getSynchronizationFeatures().getOrDefault(feature.name().toLowerCase(Locale.ENGLISH), feature.enabledByDefault);
}
@NotNull
public List<String> getBlacklistedCommandsWhileLocked() {
return blacklistedCommandsWhileLocked;
}
@NotNull
public EventPriority getEventPriority(@NotNull Settings.EventType eventType) {
try {
return EventPriority.valueOf(synchronizationEventPriorities.get(eventType.name().toLowerCase()));
return EventPriority.valueOf(synchronizationEventPriorities.get(eventType.name().toLowerCase(Locale.ENGLISH)));
} catch (IllegalArgumentException e) {
e.printStackTrace();
return EventPriority.NORMAL;
}
}
/**
* Represents the names of tables in the database
*/
@@ -153,7 +305,7 @@ public class Settings {
@NotNull
private Map.Entry<String, String> toEntry() {
return Map.entry(name().toLowerCase(), defaultName);
return Map.entry(name().toLowerCase(Locale.ENGLISH), defaultName);
}
@SuppressWarnings("unchecked")
@@ -213,7 +365,7 @@ public class Settings {
@NotNull
private Map.Entry<String, Boolean> toEntry() {
return Map.entry(name().toLowerCase(), enabledByDefault);
return Map.entry(name().toLowerCase(Locale.ENGLISH), enabledByDefault);
}
@SuppressWarnings("unchecked")
@@ -241,7 +393,7 @@ public class Settings {
@NotNull
private Map.Entry<String, String> toEntry() {
return Map.entry(name().toLowerCase(), defaultPriority.name());
return Map.entry(name().toLowerCase(Locale.ENGLISH), defaultPriority.name());
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;

View File

@@ -1,35 +0,0 @@
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
/**
* Represents the type of a {@link PersistentDataTag}
*/
public enum BukkitPersistentDataTagType {
BYTE,
SHORT,
INTEGER,
LONG,
FLOAT,
DOUBLE,
STRING,
BYTE_ARRAY,
INTEGER_ARRAY,
LONG_ARRAY,
TAG_CONTAINER_ARRAY,
TAG_CONTAINER;
public static Optional<BukkitPersistentDataTagType> getDataType(@NotNull String typeName) {
for (BukkitPersistentDataTagType type : values()) {
if (type.name().equalsIgnoreCase(typeName)) {
return Optional.of(type);
}
}
return Optional.empty();
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
/**

View File

@@ -1,11 +1,26 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import net.william278.husksync.api.BaseHuskSyncAPI;
import net.william278.husksync.config.Locales;
import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.api.BaseHuskSyncAPI;
import net.william278.husksync.player.User;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
/**
* Identifies the cause of a player data save.
*
@@ -103,7 +118,7 @@ public enum DataSaveCause {
@NotNull
public String getDisplayName() {
return Locales.truncate(name().toLowerCase(), 10);
return Locales.truncate(name().toLowerCase(Locale.ENGLISH), 10);
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;
@@ -32,4 +45,13 @@ public class ItemData {
protected ItemData() {
}
/**
* Check if the item data is empty
*
* @return {@code true} if the item data is empty; {@code false} otherwise
*/
public boolean isEmpty() {
return serializedItems.isEmpty();
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.GsonBuilder;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;
@@ -18,7 +31,7 @@ public class PersistentDataContainerData {
@SerializedName("persistent_data_map")
protected Map<String, PersistentDataTag<?>> persistentDataMap;
public PersistentDataContainerData(@NotNull final Map<String, PersistentDataTag<?>> persistentDataMap) {
public PersistentDataContainerData(@NotNull Map<String, PersistentDataTag<?>> persistentDataMap) {
this.persistentDataMap = persistentDataMap;
}
@@ -26,17 +39,23 @@ public class PersistentDataContainerData {
protected PersistentDataContainerData() {
}
public <T> Optional<T> getTagValue(@NotNull final String tagName, @NotNull Class<T> tagClass) {
if (persistentDataMap.containsKey(tagName)) {
return Optional.of(tagClass.cast(persistentDataMap.get(tagName).value));
public <T> Optional<T> getTagValue(@NotNull String tagName, @NotNull Class<T> tagClass) {
if (!persistentDataMap.containsKey(tagName)) {
return Optional.empty();
}
return Optional.empty();
// If the tag cannot be cast to the specified class, return an empty optional
final boolean canCast = tagClass.isAssignableFrom(persistentDataMap.get(tagName).value.getClass());
if (!canCast) {
return Optional.empty();
}
return Optional.of(tagClass.cast(persistentDataMap.get(tagName).value));
}
public Optional<BukkitPersistentDataTagType> getTagType(@NotNull final String tagType) {
public Optional<PersistentDataTagType> getTagType(@NotNull String tagType) {
if (persistentDataMap.containsKey(tagType)) {
return BukkitPersistentDataTagType.getDataType(persistentDataMap.get(tagType).type);
return PersistentDataTagType.getDataType(persistentDataMap.get(tagType).type);
}
return Optional.empty();
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;
@@ -19,16 +32,17 @@ public class PersistentDataTag<T> {
*/
public T value;
public PersistentDataTag(@NotNull BukkitPersistentDataTagType type, @NotNull T value) {
public PersistentDataTag(@NotNull PersistentDataTagType type, @NotNull T value) {
this.type = type.name();
this.value = value;
}
@SuppressWarnings("unused")
private PersistentDataTag() {
}
public Optional<BukkitPersistentDataTagType> getType() {
return BukkitPersistentDataTagType.getDataType(type);
public Optional<PersistentDataTagType> getType() {
return PersistentDataTagType.getDataType(type);
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
/**
* Represents the type of a {@link PersistentDataTag}
*/
public enum PersistentDataTagType {
BYTE,
SHORT,
INTEGER,
LONG,
FLOAT,
DOUBLE,
STRING,
BYTE_ARRAY,
INTEGER_ARRAY,
LONG_ARRAY,
TAG_CONTAINER_ARRAY,
TAG_CONTAINER;
public static Optional<PersistentDataTagType> getDataType(@NotNull String typeName) {
for (PersistentDataTagType type : values()) {
if (type.name().equalsIgnoreCase(typeName)) {
return Optional.of(type);
}
}
return Optional.empty();
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import net.william278.husksync.config.Settings;

View File

@@ -1,7 +1,20 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import com.google.gson.annotations.SerializedName;
import net.william278.desertwell.Version;
import net.william278.desertwell.util.Version;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import net.william278.husksync.command.Permission;
@@ -7,10 +20,7 @@ import net.william278.husksync.player.User;
import org.jetbrains.annotations.NotNull;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.*;
/**
* Represents a uniquely versioned and timestamped snapshot of a user's data, including why it was saved.
@@ -57,7 +67,7 @@ public record UserDataSnapshot(@NotNull UUID versionUUID, @NotNull Date versionT
if (pinned()) {
locales.getLocale("data_manager_pinned").ifPresent(user::sendMessage);
}
locales.getLocale("data_manager_cause", cause().name().toLowerCase().replaceAll("_", " "))
locales.getLocale("data_manager_cause", cause().name().toLowerCase(Locale.ENGLISH).replaceAll("_", " "))
.ifPresent(user::sendMessage);
// User status data, if present in the snapshot
@@ -67,7 +77,7 @@ public record UserDataSnapshot(@NotNull UUID versionUUID, @NotNull Date versionT
Integer.toString((int) statusData.maxHealth),
Integer.toString(statusData.hunger),
Integer.toString(statusData.expLevel),
statusData.gameMode.toLowerCase()))
statusData.gameMode.toLowerCase(Locale.ENGLISH)))
.ifPresent(user::sendMessage);
// Advancement and statistic data, if both are present in the snapshot

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.database;
import net.william278.husksync.HuskSync;
@@ -114,10 +127,9 @@ public abstract class Database {
* <b>(Internal)</b> Prune user data for a given user to the maximum value as configured.
*
* @param user The user to prune data for
* @return A future returning void when complete
* @implNote Data snapshots marked as {@code pinned} are exempt from rotation
*/
protected abstract CompletableFuture<Void> rotateUserData(@NotNull User user);
protected abstract void rotateUserData(@NotNull User user);
/**
* Deletes a specific {@link UserDataSnapshot} entry for a user from the database, by its UUID.

View File

@@ -1,9 +1,25 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.database;
import com.zaxxer.hikari.HikariDataSource;
import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*;
import net.william278.husksync.data.DataAdaptionException;
import net.william278.husksync.data.DataSaveCause;
import net.william278.husksync.data.UserData;
import net.william278.husksync.data.UserDataSnapshot;
import net.william278.husksync.event.DataSaveEvent;
import net.william278.husksync.player.User;
import org.jetbrains.annotations.NotNull;
@@ -52,17 +68,17 @@ public class MySqlDatabase extends Database {
public MySqlDatabase(@NotNull HuskSync plugin) {
super(plugin);
final Settings settings = plugin.getSettings();
this.mySqlHost = settings.mySqlHost;
this.mySqlPort = settings.mySqlPort;
this.mySqlDatabaseName = settings.mySqlDatabase;
this.mySqlUsername = settings.mySqlUsername;
this.mySqlPassword = settings.mySqlPassword;
this.mySqlConnectionParameters = settings.mySqlConnectionParameters;
this.hikariMaximumPoolSize = settings.mySqlConnectionPoolSize;
this.hikariMinimumIdle = settings.mySqlConnectionPoolIdle;
this.hikariMaximumLifetime = settings.mySqlConnectionPoolLifetime;
this.hikariKeepAliveTime = settings.mySqlConnectionPoolKeepAlive;
this.hikariConnectionTimeOut = settings.mySqlConnectionPoolTimeout;
this.mySqlHost = settings.getMySqlHost();
this.mySqlPort = settings.getMySqlPort();
this.mySqlDatabaseName = settings.getMySqlDatabase();
this.mySqlUsername = settings.getMySqlUsername();
this.mySqlPassword = settings.getMySqlPassword();
this.mySqlConnectionParameters = settings.getMySqlConnectionParameters();
this.hikariMaximumPoolSize = settings.getMySqlConnectionPoolSize();
this.hikariMinimumIdle = settings.getMySqlConnectionPoolIdle();
this.hikariMaximumLifetime = settings.getMySqlConnectionPoolLifetime();
this.hikariKeepAliveTime = settings.getMySqlConnectionPoolKeepAlive();
this.hikariConnectionTimeOut = settings.getMySqlConnectionPoolTimeout();
}
/**
@@ -297,27 +313,25 @@ public class MySqlDatabase extends Database {
}
@Override
protected CompletableFuture<Void> rotateUserData(@NotNull User user) {
return CompletableFuture.runAsync(() -> {
final List<UserDataSnapshot> unpinnedUserData = getUserData(user).join().stream()
.filter(dataSnapshot -> !dataSnapshot.pinned()).toList();
if (unpinnedUserData.size() > plugin.getSettings().maxUserDataSnapshots) {
try (Connection connection = getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
DELETE FROM `%user_data_table%`
WHERE `player_uuid`=?
AND `pinned` IS FALSE
ORDER BY `timestamp` ASC
LIMIT %entry_count%;""".replace("%entry_count%",
Integer.toString(unpinnedUserData.size() - plugin.getSettings().maxUserDataSnapshots))))) {
statement.setString(1, user.uuid.toString());
statement.executeUpdate();
}
} catch (SQLException e) {
plugin.log(Level.SEVERE, "Failed to prune user data from the database", e);
protected void rotateUserData(@NotNull User user) {
final List<UserDataSnapshot> unpinnedUserData = getUserData(user).join().stream()
.filter(dataSnapshot -> !dataSnapshot.pinned()).toList();
if (unpinnedUserData.size() > plugin.getSettings().getMaxUserDataSnapshots()) {
try (Connection connection = getConnection()) {
try (PreparedStatement statement = connection.prepareStatement(formatStatementTables("""
DELETE FROM `%user_data_table%`
WHERE `player_uuid`=?
AND `pinned` IS FALSE
ORDER BY `timestamp` ASC
LIMIT %entry_count%;""".replace("%entry_count%",
Integer.toString(unpinnedUserData.size() - plugin.getSettings().getMaxUserDataSnapshots()))))) {
statement.setString(1, user.uuid.toString());
statement.executeUpdate();
}
} catch (SQLException e) {
plugin.log(Level.SEVERE, "Failed to prune user data from the database", e);
}
});
}
}
@Override
@@ -362,7 +376,8 @@ public class MySqlDatabase extends Database {
plugin.log(Level.SEVERE, "Failed to set user data in the database", e);
}
}
}).thenRun(() -> rotateUserData(user).join());
this.rotateUserData(user);
});
}
@Override

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
public interface CancellableEvent extends Event {

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.data.DataSaveCause;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import java.util.concurrent.CompletableFuture;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.data.DataSaveCause;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.player.OnlineUser;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
import net.william278.husksync.data.UserData;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.event;
public interface SyncCompleteEvent extends PlayerEvent {

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.hook;
import com.djrapitops.plan.extension.CallEvents;
@@ -16,6 +29,7 @@ import net.william278.husksync.player.User;
import org.jetbrains.annotations.NotNull;
import java.util.Date;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -212,7 +226,7 @@ public class PlanDataExtension implements DataExtension {
plugin.getDatabase().getUserData(user).join().forEach(versionedUserData -> dataSnapshotsTable.addRow(
versionedUserData.versionTimestamp().getTime(),
versionedUserData.versionUUID().toString().split("-")[0],
versionedUserData.cause().name().toLowerCase().replaceAll("_", " "),
versionedUserData.cause().name().toLowerCase(Locale.ENGLISH).replaceAll("_", " "),
versionedUserData.pinned() ? PINNED_HTML_STRING + "Pinned" : "Unpinned"
)));
return dataSnapshotsTable.build();

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.hook;
import com.djrapitops.plan.capability.CapabilityService;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.listener;
import de.themoep.minedown.adventure.MineDown;
@@ -60,7 +73,7 @@ public abstract class EventListener {
CompletableFuture.runAsync(() -> {
try {
// Hold reading data for the network latency threshold, to ensure the source server has set the redis key
Thread.sleep(Math.max(0, plugin.getSettings().networkLatencyMilliseconds));
Thread.sleep(Math.max(0, plugin.getSettings().getNetworkLatencyMilliseconds()));
} catch (InterruptedException e) {
plugin.log(Level.SEVERE, "An exception occurred handling a player join", e);
} finally {
@@ -124,7 +137,7 @@ public abstract class EventListener {
*/
private void handleSynchronisationCompletion(@NotNull OnlineUser user, boolean succeeded) {
if (succeeded) {
switch (plugin.getSettings().notificationDisplaySlot) {
switch (plugin.getSettings().getNotificationDisplaySlot()) {
case CHAT -> plugin.getLocales().getLocale("synchronisation_complete")
.ifPresent(user::sendMessage);
case ACTION_BAR -> plugin.getLocales().getLocale("synchronisation_complete")
@@ -179,7 +192,7 @@ public abstract class EventListener {
* @param usersInWorld a list of users in the world that is being saved
*/
protected final void saveOnWorldSave(@NotNull List<OnlineUser> usersInWorld) {
if (disabling || !plugin.getSettings().saveOnWorldSave) {
if (disabling || !plugin.getSettings().doSaveOnWorldSave()) {
return;
}
usersInWorld.stream()
@@ -196,7 +209,8 @@ public abstract class EventListener {
* @param drops The items that this user would have dropped
*/
protected void saveOnPlayerDeath(@NotNull OnlineUser user, @NotNull ItemData drops) {
if (disabling || !plugin.getSettings().saveOnDeath || lockedPlayers.contains(user.uuid) || user.isNpc()) {
if (disabling || !plugin.getSettings().doSaveOnDeath() || lockedPlayers.contains(user.uuid) || user.isNpc()
|| (!plugin.getSettings().doSaveEmptyDropsOnDeath() && drops.isEmpty())) {
return;
}

View File

@@ -1,8 +1,21 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.migrator;
import net.william278.husksync.HuskSync;
import org.jetbrains.annotations.NotNull;
import net.william278.husksync.data.UserData;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture;

View File

@@ -1,17 +1,30 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.player;
import de.themoep.minedown.adventure.MineDown;
import net.william278.desertwell.Version;
import de.themoep.minedown.adventure.MineDownParser;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.william278.desertwell.util.Version;
import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*;
import net.william278.husksync.event.PreSyncEvent;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
@@ -43,12 +56,12 @@ public abstract class OnlineUser extends User {
public final CompletableFuture<Void> setStatus(@NotNull StatusData statusData,
@NotNull List<StatusDataFlag> statusDataFlags) {
final Settings settings = new Settings();
settings.synchronizationFeatures.put(Settings.SynchronizationFeature.HEALTH.name().toLowerCase(), statusDataFlags.contains(StatusDataFlag.SET_HEALTH));
settings.synchronizationFeatures.put(Settings.SynchronizationFeature.MAX_HEALTH.name().toLowerCase(), statusDataFlags.contains(StatusDataFlag.SET_MAX_HEALTH));
settings.synchronizationFeatures.put(Settings.SynchronizationFeature.HUNGER.name().toLowerCase(), statusDataFlags.contains(StatusDataFlag.SET_HUNGER));
settings.synchronizationFeatures.put(Settings.SynchronizationFeature.EXPERIENCE.name().toLowerCase(), statusDataFlags.contains(StatusDataFlag.SET_EXPERIENCE));
settings.synchronizationFeatures.put(Settings.SynchronizationFeature.INVENTORIES.name().toLowerCase(), statusDataFlags.contains(StatusDataFlag.SET_SELECTED_ITEM_SLOT));
settings.synchronizationFeatures.put(Settings.SynchronizationFeature.LOCATION.name().toLowerCase(), statusDataFlags.contains(StatusDataFlag.SET_GAME_MODE) || statusDataFlags.contains(StatusDataFlag.SET_FLYING));
settings.getSynchronizationFeatures().put(Settings.SynchronizationFeature.HEALTH.name().toLowerCase(Locale.ENGLISH), statusDataFlags.contains(StatusDataFlag.SET_HEALTH));
settings.getSynchronizationFeatures().put(Settings.SynchronizationFeature.MAX_HEALTH.name().toLowerCase(Locale.ENGLISH), statusDataFlags.contains(StatusDataFlag.SET_MAX_HEALTH));
settings.getSynchronizationFeatures().put(Settings.SynchronizationFeature.HUNGER.name().toLowerCase(Locale.ENGLISH), statusDataFlags.contains(StatusDataFlag.SET_HUNGER));
settings.getSynchronizationFeatures().put(Settings.SynchronizationFeature.EXPERIENCE.name().toLowerCase(Locale.ENGLISH), statusDataFlags.contains(StatusDataFlag.SET_EXPERIENCE));
settings.getSynchronizationFeatures().put(Settings.SynchronizationFeature.INVENTORIES.name().toLowerCase(Locale.ENGLISH), statusDataFlags.contains(StatusDataFlag.SET_SELECTED_ITEM_SLOT));
settings.getSynchronizationFeatures().put(Settings.SynchronizationFeature.LOCATION.name().toLowerCase(Locale.ENGLISH), statusDataFlags.contains(StatusDataFlag.SET_GAME_MODE) || statusDataFlags.contains(StatusDataFlag.SET_FLYING));
return setStatus(statusData, settings);
}
@@ -59,8 +72,7 @@ public abstract class OnlineUser extends User {
* @param settings settings, containing information about which features should be synced
* @return a future returning void when complete
*/
public abstract CompletableFuture<Void> setStatus(@NotNull StatusData statusData,
@NotNull Settings settings);
public abstract CompletableFuture<Void> setStatus(@NotNull StatusData statusData, @NotNull Settings settings);
/**
* Get the player's inventory {@link ItemData} contents
@@ -183,19 +195,44 @@ public abstract class OnlineUser extends User {
@NotNull
public abstract Version getMinecraftVersion();
/**
* Get the player's adventure {@link Audience}
*
* @return the player's {@link Audience}
*/
@NotNull
public abstract Audience getAudience();
/**
* Send a message to this player
*
* @param component the {@link Component} message to send
*/
public void sendMessage(@NotNull Component component) {
getAudience().sendMessage(component);
}
/**
* Dispatch a MineDown-formatted message to this player
*
* @param mineDown the parsed {@link MineDown} to send
*/
public abstract void sendMessage(@NotNull MineDown mineDown);
public void sendMessage(@NotNull MineDown mineDown) {
sendMessage(mineDown
.disable(MineDownParser.Option.SIMPLE_FORMATTING)
.replace().toComponent());
}
/**
* Dispatch a MineDown-formatted action bar message to this player
*
* @param mineDown the parsed {@link MineDown} to send
*/
public abstract void sendActionBar(@NotNull MineDown mineDown);
public void sendActionBar(@NotNull MineDown mineDown) {
getAudience().sendActionBar(mineDown
.disable(MineDownParser.Option.SIMPLE_FORMATTING)
.replace().toComponent());
}
/**
* Dispatch a toast message to this player
@@ -248,7 +285,7 @@ public abstract class OnlineUser extends User {
public final CompletableFuture<Boolean> setData(@NotNull UserData data, @NotNull HuskSync plugin) {
return CompletableFuture.supplyAsync(() -> {
// Prevent synchronising user data from newer versions of Minecraft
if (Version.fromMinecraftVersionString(data.getMinecraftVersion()).compareTo(plugin.getMinecraftVersion()) > 0) {
if (Version.fromString(data.getMinecraftVersion()).compareTo(plugin.getMinecraftVersion()) > 0) {
plugin.log(Level.SEVERE, "Cannot set data for " + username +
" because the Minecraft version of their user data (" + data.getMinecraftVersion() +
") is newer than the server's Minecraft version (" + plugin.getMinecraftVersion() + ").");
@@ -325,7 +362,7 @@ public abstract class OnlineUser extends User {
if (!isOffline()) {
final Settings settings = plugin.getSettings();
if (settings.getSynchronizationFeature(Settings.SynchronizationFeature.INVENTORIES)) {
if (isDead() && settings.saveDeadPlayerInventories) {
if (isDead() && settings.isSynchroniseDeadPlayersChangingServer()) {
plugin.debug("Player " + username + " is dead, so their inventory will be set to empty.");
add(CompletableFuture.runAsync(() -> builder.setInventory(ItemData.empty())));
} else {

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.player;
import org.jetbrains.annotations.NotNull;

View File

@@ -1,7 +1,22 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.redis;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
public enum RedisKeyType {
CACHE(60 * 60 * 24),
DATA_UPDATE(10),
@@ -15,6 +30,6 @@ public enum RedisKeyType {
@NotNull
public String getKeyPrefix() {
return RedisManager.KEY_NAMESPACE.toLowerCase() + ":" + RedisManager.clusterId.toLowerCase() + ":" + name().toLowerCase();
return RedisManager.KEY_NAMESPACE.toLowerCase(Locale.ENGLISH) + ":" + RedisManager.clusterId.toLowerCase(Locale.ENGLISH) + ":" + name().toLowerCase(Locale.ENGLISH);
}
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.redis;
import de.themoep.minedown.adventure.MineDown;
@@ -33,13 +46,13 @@ public class RedisManager extends JedisPubSub {
public RedisManager(@NotNull HuskSync plugin) {
this.plugin = plugin;
clusterId = plugin.getSettings().clusterId;
clusterId = plugin.getSettings().getClusterId();
// Set redis credentials
this.redisHost = plugin.getSettings().redisHost;
this.redisPort = plugin.getSettings().redisPort;
this.redisPassword = plugin.getSettings().redisPassword;
this.redisUseSsl = plugin.getSettings().redisUseSsl;
this.redisHost = plugin.getSettings().getRedisHost();
this.redisPort = plugin.getSettings().getRedisPort();
this.redisPassword = plugin.getSettings().getRedisPassword();
this.redisUseSsl = plugin.getSettings().isRedisUseSsl();
// Configure the jedis pool
this.jedisPoolConfig = new JedisPoolConfig();
@@ -91,7 +104,7 @@ public class RedisManager extends JedisPubSub {
final UserData userData = plugin.getDataAdapter().fromBytes(redisMessage.data);
user.setData(userData, plugin).thenAccept(succeeded -> {
if (succeeded) {
switch (plugin.getSettings().notificationDisplaySlot) {
switch (plugin.getSettings().getNotificationDisplaySlot()) {
case CHAT -> plugin.getLocales().getLocale("data_update_complete")
.ifPresent(user::sendMessage);
case ACTION_BAR -> plugin.getLocales().getLocale("data_update_complete")

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.redis;
import com.google.gson.GsonBuilder;

View File

@@ -1,8 +1,22 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.redis;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
public enum RedisMessageType {
@@ -11,8 +25,8 @@ public enum RedisMessageType {
@NotNull
public String getMessageChannel() {
return RedisManager.KEY_NAMESPACE.toLowerCase() + ":" + RedisManager.clusterId.toLowerCase()
+ ":" + name().toLowerCase();
return RedisManager.KEY_NAMESPACE.toLowerCase(Locale.ENGLISH) + ":" + RedisManager.clusterId.toLowerCase(Locale.ENGLISH)
+ ":" + name().toLowerCase(Locale.ENGLISH);
}
public static Optional<RedisMessageType> getTypeFromChannel(@NotNull String messageChannel) {

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.util;
import com.google.gson.JsonObject;
@@ -13,6 +26,7 @@ import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.StringJoiner;
import java.util.logging.Level;
@@ -164,7 +178,7 @@ public class DataDumper {
return new StringJoiner("_")
.add(user.username)
.add(new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(dataSnapshot.versionTimestamp()))
.add(dataSnapshot.cause().name().toLowerCase())
.add(dataSnapshot.cause().name().toLowerCase(Locale.ENGLISH))
.add(dataSnapshot.versionUUID().toString().split("-")[0])
+ ".json";
}

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.util;
import net.william278.husksync.config.Locales;

View File

@@ -1,6 +1,6 @@
synchronisation_complete: '[⏵ Daten synchronisiert!](#00fb9a)'
synchronisation_failed: '[⏵ Ein Fehler ist beim Synchronisieren deiner Daten aufgetreten! Bitte kontaktiere einen Administrator.](#ff7e5e)'
reload_complete: '[HuskSync](#00fb9a bold) [| Die Konfigurations- und Sprachdateien wurden neu geladen.'
reload_complete: '[HuskSync](#00fb9a bold) [| Die Konfigurations- und Sprachdateien wurden neu geladen.](#00fb9a)'
error_invalid_syntax: '[Fehler:](#ff3300) [Falsche Syntax. Nutze: %1%](#ff7e5e)'
error_invalid_player: '[Fehler:](#ff3300) [Es konnte kein Spieler mit diesem Namen gefunden werden.](#ff7e5e)'
error_no_permission: '[Fehler:](#ff3300) [Du hast nicht die benötigten Berechtigungen um diesen Befehl auszuführen](#ff7e5e)'
@@ -14,19 +14,19 @@ inventory_viewer_opened: '[Du siehst den Schnappschuss des Inventares von](#00fb
ender_chest_viewer_opened: '[Du siehst den Schnappschuss der Endertruhe von](#00fb9a) [%1%](#00fb9a bold) [von ⌚ %2%](#00fb9a)'
data_update_complete: '[🔔 Deine Daten wurden aktualisiert!](#00fb9a)'
data_update_failed: '[🔔 Ein Fehler ist beim Aktualisieren deiner Daten aufgetreten! Bitte kontaktiere einen Administrator.](#ff7e5e)'
data_manager_title: '[Du siehst den Nutzerdaten-Schnappschuss](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für [%3%](#00fb9a bold show_text=&7Spieler-UUID:\n&8%4%)[:](#00fb9a)'
data_manager_title: '[Du siehst den Nutzerdaten-Schnappschuss](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für %3%](#00fb9a bold show_text=&7Spieler-UUID:\n&8%4%)[:](#00fb9a)'
data_manager_timestamp: '[⌚ %1%](#ffc43b-#f5c962 show_text=&7Versions-Zeitstempel:\n&8Zeitpunkt der Speicherung der Daten)'
data_manager_pinned: '[※ Schnappschuss angeheftet](#d8ff2b show_text=&7Angeheftet:\n&8Dieser Nutzerdaten-Schnappschuss wird nicht automatisch rotiert.)'
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7Speicherungsgrund:\n&8Der Grund für das Speichern der Daten)\n'
data_manager_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7Lebenspunkte) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7Hungerpunkte) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7XP-Level) [🏹 %5%](dark_aqua show_text=&7Spielmodus)'
data_manager_advancements_statistics: '[⭐ Erfolge: %1%](color=#ffc43b-#f5c962 show_text=&7Erfolge in denen du Fortschritt gemacht hast:\n&8%2%) [⌛ Spielzeit: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7Deine verbrachte Zeit im Spiel\n&8⚠ Basierend auf Spielstatistiken)\n'
data_manager_item_buttons: '[View:](gray) [[🪣 Inventar…]](color=#a17b5f-#f5b98c show_text=&7Klicke zum Ansehen run_command=/inventory %1% %2%) [[⌀ Endertruhe…]](#b649c4-#d254ff show_text=&7Klicke zum Ansehen run_command=/enderchest %1% %2%)'
data_manager_management_buttons: '[Verwalte:](gray) [[❌ Löschen…]](#ff3300 show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss zu löschen.\n&8Dies betrifft nicht die aktuellen Nutzerdaten.\n&#ff3300&⚠ Dieser Schritt kann nicht rückgängig gemacht werden! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Wiederherstellen…]](#00fb9a show_text=&7Klicke, um die Nutzerdaten wiederherzustellen.\n&8Dies wird die Nutzerdaten auf den Stand des Schnappschusses setzen.\n&#ff3300&⚠ Die aktuellen Nutzerdaten von %1% werden überschrieben! suggest_command=/husksync:userdata restore %1% %2%) [[※ Anheften/Loslösen…]](#d8ff2b show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss anzuheften oder loszulösen\n&8Angeheftete Nutzerdaten-Schnappschüsse werden nicht automatisch rotiert run_command=/userdata pin %1% %2%)'
data_manager_item_buttons: '[Sehen:](gray) [[🪣 Inventar…]](color=#a17b5f-#f5b98c show_text=&7Klicke zum Ansehen run_command=/inventory %1% %2%) [[⌀ Endertruhe…]](#b649c4-#d254ff show_text=&7Klicke zum Ansehen run_command=/enderchest %1% %2%)'
data_manager_management_buttons: '[Verwalten:](gray) [[❌ Löschen…]](#ff3300 show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss zu löschen.\n&8Dies betrifft nicht die aktuellen Nutzerdaten.\n&#ff3300&⚠ Dieser Schritt kann nicht rückgängig gemacht werden! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ Wiederherstellen…]](#00fb9a show_text=&7Klicke, um die Nutzerdaten wiederherzustellen.\n&8Dies wird die Nutzerdaten auf den Stand des Schnappschusses setzen.\n&#ff3300&⚠ Die aktuellen Nutzerdaten von %1% werden überschrieben! suggest_command=/husksync:userdata restore %1% %2%) [[※ Anheften/Loslösen…]](#d8ff2b show_text=&7Klicke, um diesen Nutzerdaten-Schnappschuss anzuheften oder loszulösen\n&8Angeheftete Nutzerdaten-Schnappschüsse werden nicht automatisch rotiert run_command=/userdata pin %1% %2%)'
data_manager_system_buttons: '[System:](gray) [[⏷ Daten-Dump…]](dark_gray show_text=&7Klicke, um diesen rohen Nutzerdaten-Schnappschuss in eine Datei zu speichern.\n&8Daten-Dumps können unter ~/plugins/HuskSync/dumps/ gefunden werden. run_command=/husksync:userdata dump %1% %2% file) [[☂ Web-Dump…]](dark_gray show_text=&7Klicke, um diesen rohen Nutzerdaten-Schnappschuss auf den mc-logs Service hochzuladen.\n&8Du erhältst dann eine URL, die die Daten enthält. run_command=/husksync:userdata dump %1% %2% web)'
data_manager_advancements_preview_remaining: '&7und %1% weitere…'
data_list_title: '[Nutzerdaten-Schnappschüsse von %1%:](#00fb9a) [(%2%-%3% von](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_list_item: '[%1%](gray show_text=&7Daten-Schnappschuss %3% run_command=/userdata view %6% %4%) [%7%](#d8ff2b show_text=&7Angeheftet:\n&8Angeheftete Schnappschüsse werden nicht automatisch rotiert. run_command=/userdata view %6% %4%) [%2%](color=#ffc43b-#f5c962 show_text=&7Versions-Zeitstempel:&7\n&8Zeitpunkt der Speicherung der Daten run_command=/userdata view %6% %4%) [⚡ %3%](color=#62a9f5-#7ab8fa show_text=&7Versions-UUID:&7\n&8%4% run_command=/userdata view %6% %4%) [⚑ %5%](#23a825-#36f539 show_text=&7Speicherungsgrund:\n&8Der Grund für das Speichern der Daten run_command=/userdata view %6% %4%)'
data_deleted: '[❌ Nutzerdaten-Schnappschuss erfolgreich gelöscht](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)'
data_deleted: '[❌ Nutzerdaten-Schnappschuss erfolgreich gelöscht](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Player UUID:\n&8%4%)'
data_restored: '[⏪ Erfgreich wiederhergestellt](#00fb9a) [Aktuelle Nutzerdaten des Schnappschusses von %1%](#00fb9a show_text=&7Spieler-UUID:\n&8%2%) [%3%.](#00fb9a show_text=&7Versions-UUID:\n&8%4%)'
data_pinned: '[※ Nutzerdaten-Schnappschuss erfolgreich angepinnt](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Spieler-UUID:\n&8%4%)'
data_unpinned: '[※ Nutzerdaten-Schnappschuss erfolgreich losgelöst](#00fb9a) [%1%](#00fb9a show_text=&7Versions-UUID:\n&8%2%) [für](#00fb9a) [%3%.](#00fb9a show_text=&7Spieler-UUID:\n&8%4%)'

View File

@@ -20,22 +20,22 @@ data_manager_pinned: '[※ 被標記的快照](#d8ff2b show_text=&7標記:\n&8
data_manager_cause: '[⚑ %1%](#23a825-#36f539 show_text=&7保存原因:\n&8保存此快照的原因)\n'
data_manager_status: '[%1%](red)[/](gray)[%2%](red)[×](gray)[❤](red show_text=&7血量) [%3%](yellow)[×](gray)[🍖](yellow show_text=&7飽食度) [ʟᴠ](green)[.](gray)[%4%](green show_text=&7經驗等級) [🏹 %5%](dark_aqua show_text=&7遊戲模式)'
data_manager_advancements_statistics: '[⭐ 成就: %1%](color=#ffc43b-#f5c962 show_text=&7已獲得的成就:\n&8%2%) [⌛ 遊戲時間: %3%ʜʀs](color=#62a9f5-#7ab8fa show_text=&7遊戲內的遊玩時間\n&8⚠ 根據遊戲內統計)\n'
data_manager_item_buttons: '[View:](gray) [[🪣 背包…]](color=#a17b5f-#f5b98c show_text=&7點擊查看 run_command=/inventory %1% %2%) [[⌀ 終界箱…]](#b649c4-#d254ff show_text=&7點擊查看 run_command=/enderchest %1% %2%)'
data_manager_item_buttons: '[查看:](gray) [[🪣 背包…]](color=#a17b5f-#f5b98c show_text=&7點擊查看 run_command=/inventory %1% %2%) [[⌀ 終界箱…]](#b649c4-#d254ff show_text=&7點擊查看 run_command=/enderchest %1% %2%)'
data_manager_management_buttons: '[管理:](gray) [[❌ 刪除…]](#ff3300 show_text=&7點擊刪除這個快照\n&8這不會影像目前玩家的資料\n&#ff3300&⚠ 此操作不能取消! suggest_command=/husksync:userdata delete %1% %2%) [[⏪ 恢復…]](#00fb9a show_text=&7點擊將玩家資料覆蓋為此快照\n&8這將導致玩家的資料會被此快照覆蓋\n&#ff3300&⚠ %1% 當前的資料將被覆蓋! suggest_command=/husksync:userdata restore %1% %2%) [[※ 標記…]](#d8ff2b show_text=&7點擊切換標記狀態\n&8被標記的快照將不會自動輪換更新 run_command=/userdata pin %1% %2%)'
data_manager_system_buttons: '[System:](gray) [[⏷ File Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to a file.\n&8Data dumps can be found in ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ Web Dump…]](dark_gray show_text=&7Click to dump this raw user data snapshot to the mc-logs service\n&8You will be provided with a URL containing the data. run_command=/husksync:userdata dump %1% %2% web)'
data_manager_system_buttons: '[系統:](gray) [[⏷ 本地轉存…]](dark_gray show_text=&7點擊將此玩家資料快照轉存到本地文件中\n&8轉存的資料可以在以下路徑找到 ~/plugins/HuskSync/dumps/ run_command=/husksync:userdata dump %1% %2% file) [[☂ 雲端轉存…]](dark_gray show_text=&7點擊將此玩家資料快照轉存到 mc-logs 服務\n&8您將獲得一個包含資料的 URL. run_command=/husksync:userdata dump %1% %2% web)'
data_manager_advancements_preview_remaining: '&7還有 %1% …'
data_list_title: '[%1%''s user data snapshots:](#00fb9a) [(%2%-%3% of](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_list_title: '[%1% 的玩家資料快照:](#00fb9a) [(%2%-%3% ](#00fb9a) [%4%](#00fb9a bold)[)](#00fb9a)\n'
data_list_item: '[%1%](gray show_text=&7快照名稱: %3% run_command=/userdata view %6% %4%) [%7%](#d8ff2b show_text=&7標記:\n&8被標記的快照不會自動輪換更新 run_command=/userdata view %6% %4%) [%2%](color=#ffc43b-#f5c962 show_text=&7時間戳:&7\n&8資料保存時間 run_command=/userdata view %6% %4%) [⚡ %3%](color=#62a9f5-#7ab8fa show_text=&7Version UUID:&7\n&8%4% run_command=/userdata view %6% %4%) [⚑ %5%](#23a825-#36f539 show_text=&7保存原因:\n&8保存此快照的原因 run_command=/userdata view %6% %4%)'
data_deleted: '[❌ 成功刪除:](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)'
data_restored: '[⏪ 成功將玩家](#00fb9a) [%1%](#00fb9a show_text=&7玩家 UUID:\n&8%2%)[的資料恢復為 快照:](#00fb9a) [%3%.](#00fb9a show_text=&7Version UUID:\n&8%4%)'
data_pinned: '[※ 成功標記](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [的快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%)'
data_unpinned: '[※ 成功解除](#00fb9a) [%3%](#00fb9a show_text=&7玩家 UUID:\n&8%4%) [快照:](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [的標記](#00fb9a)'
data_dumped: '[☂ Successfully dumped the user data snapshot %1% for %2% to:](#00fb9a) &7%3%'
list_footer: '\n%1%[Page](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%'
list_previous_page_button: '[◀](white show_text=&7View previous page run_command=%2% %1%) '
list_next_page_button: ' [▶](white show_text=&7View next page run_command=%2% %1%)'
list_page_jumpers: '(%1%)'
list_page_jumper_button: '[%1%](show_text=&7Jump to page %1% run_command=%2% %1%)'
data_dumped: '[☂ 成功將 %2% 資料快照 %1% 儲存至:](#00fb9a) &7%3%'
list_footer: \n%1%[頁面](#00fb9a) [%2%](#00fb9a)/[%3%](#00fb9a)%4% %5%
list_previous_page_button: '[◀](white show_text=&7查看上一頁 run_command=%2% %1%) '
list_next_page_button: ' [▶](white show_text=&7查看下一頁 run_command=%2% %1%)'
list_page_jumpers: (%1%)
list_page_jumper_button: '[%1%](show_text=&7跳至第 %1% run_command=%2% %1%)'
list_page_jumper_current_page: '[%1%](#00fb9a)'
list_page_jumper_separator: ' '
list_page_jumper_group_separator: '…'
list_page_jumper_group_separator:

View File

@@ -1,6 +1,19 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync;
import net.william278.desertwell.Version;
import net.william278.desertwell.util.Version;
import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataAdapter;

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.data;
import net.william278.husksync.DummyHuskSync;
@@ -62,13 +75,13 @@ public class DataAdaptionTests {
private String getTestSerializedPersistentDataContainer() {
final HashMap<String, PersistentDataTag<?>> persistentDataTest = new HashMap<>();
persistentDataTest.put("husksync:byte_test", new PersistentDataTag<>(BukkitPersistentDataTagType.BYTE, 0x01));
persistentDataTest.put("husksync:double_test", new PersistentDataTag<>(BukkitPersistentDataTagType.DOUBLE, 2d));
persistentDataTest.put("husksync:string_test", new PersistentDataTag<>(BukkitPersistentDataTagType.STRING, "test"));
persistentDataTest.put("husksync:int_test", new PersistentDataTag<>(BukkitPersistentDataTagType.INTEGER, 3));
persistentDataTest.put("husksync:long_test", new PersistentDataTag<>(BukkitPersistentDataTagType.LONG, 4L));
persistentDataTest.put("husksync:float_test", new PersistentDataTag<>(BukkitPersistentDataTagType.FLOAT, 5f));
persistentDataTest.put("husksync:short_test", new PersistentDataTag<>(BukkitPersistentDataTagType.SHORT, 6));
persistentDataTest.put("husksync:byte_test", new PersistentDataTag<>(PersistentDataTagType.BYTE, 0x01));
persistentDataTest.put("husksync:double_test", new PersistentDataTag<>(PersistentDataTagType.DOUBLE, 2d));
persistentDataTest.put("husksync:string_test", new PersistentDataTag<>(PersistentDataTagType.STRING, "test"));
persistentDataTest.put("husksync:int_test", new PersistentDataTag<>(PersistentDataTagType.INTEGER, 3));
persistentDataTest.put("husksync:long_test", new PersistentDataTag<>(PersistentDataTagType.LONG, 4L));
persistentDataTest.put("husksync:float_test", new PersistentDataTag<>(PersistentDataTagType.FLOAT, 5f));
persistentDataTest.put("husksync:short_test", new PersistentDataTag<>(PersistentDataTagType.SHORT, 6));
final PersistentDataContainerData persistentDataContainerData = new PersistentDataContainerData(persistentDataTest);
final DataAdapter dataAdapter = new JsonDataAdapter();

View File

@@ -1,3 +1,16 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.hook;
import com.djrapitops.plan.extension.extractor.ExtensionExtractor;

View File

@@ -1,9 +1,23 @@
/*
* This file is part of HuskSync by William278. Do not redistribute!
*
* Copyright (c) William278 <will27528@gmail.com>
* All rights reserved.
*
* This source code is provided as reference to licensed individuals that have purchased the HuskSync
* plugin once from any of the official sources it is provided. The availability of this code does
* not grant you the rights to modify, re-distribute, compile or redistribute this source code or
* "plugin" outside this intended purpose. This license does not cover libraries developed by third
* parties that are utilised in the plugin.
*/
package net.william278.husksync.player;
import de.themoep.minedown.adventure.MineDown;
import net.kyori.adventure.audience.Audience;
import net.william278.desertwell.util.Version;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*;
import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -129,7 +143,13 @@ public class DummyPlayer extends OnlineUser {
@NotNull
@Override
public Version getMinecraftVersion() {
return Version.fromMinecraftVersionString("1.19-beta123456");
return Version.fromString("1.19-beta123456");
}
@Override
@NotNull
public Audience getAudience() {
return Audience.empty();
}
@Override

12
docs/API-Events.md Normal file
View File

@@ -0,0 +1,12 @@
HuskSync provides three API events your plugin can listen to when certain parts of the data synchronisation process are performed. These events deal in HuskSync class types, so you may want to familiarize yourself with the [API basics](API) first. Two of the events can be cancelled (thus aborting the synchronisation process at certain stages) and some of the events expose methods letting you affect their outcome (such as modifying the data that is saved during the process).
Consult the Javadocs for more information -- and don't forget to register your listener when listening for these event calls. Please note that carrying out expensive blocking operations during these events is strongly discouraged as this may affect plugin performance.
## List of API Events
| Bukkit Event class | Cancellable | Description |
|---------------------------|:-----------:|---------------------------------------------------------------------------------------------|
| `BukkitDataSaveEvent` | ✅ | Called when player data snapshot is created, saved and cached due to a DataSaveCause |
| `BukkitPreSync` | ✅ | Called before a player has their data updated from the cache or database, just after login |
| `BukkitSyncCompleteEvent` | ❌ | Called once a player has completed their data synchronisation on login successfully&dagger; |
&dagger;This can also fire when a user's data is updated while the player is logged in; i.e. when an admin rolls back the user, updates their inventory or Ender Chest through the respective commands, or when an API call is made forcing the user to have their data updated.

77
docs/API.md Normal file
View File

@@ -0,0 +1,77 @@
[![HuskSync banner](https://raw.githubusercontent.com/WiIIiam278/HuskSync/master/images/banner.png)](https://github.com/WiIIiam278/HuskSync)
# HuskSync API v2
![](https://jitpack.io/v/WiIIiam278/HuskSync.svg)
The HuskSync API provides methods for retrieving and updating user data, as well as a number of events for tracking when user data is synced and saved.
The API is distributed via [JitPack](https://jitpack.io/#net.william278/HuskSync). Please note that the HuskSync v1 API is not compatible.
(Some) javadocs are also available to view on Jitpack [here](https://javadoc.jitpack.io/net/william278/HuskSync/latest/javadoc/).
## Table of contents
1. Adding the API to your project
2. Adding HuskSync as a dependency
3. Next steps
## API Introduction
### 1.1 Setup with Maven
<details>
<summary>Maven setup information</summary>
- Add the repository to your `pom.xml` as per below.
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
```
- Add the dependency to your `pom.xml` as per below. Replace `version` with the latest version of HuskSync (without the v): ![Latest version](https://img.shields.io/github/v/tag/WiIIiam278/HuskSync?color=%23282828&label=%20&style=flat-square)
```xml
<dependency>
<groupId>net.william278</groupId>
<artifactId>HuskSync</artifactId>
<version>version</version>
<scope>provided</scope>
</dependency>
```
</details>
### 1.2 Setup with Gradle
<details>
<summary>Gradle setup information</summary>
- Add the dependency like so to your `build.gradle`:
```groovy
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
```
- Add the dependency as per below. Replace `version` with the latest version of HuskSync (without the v): ![Latest version](https://img.shields.io/github/v/tag/WiIIiam278/HuskSync?color=%23282828&label=%20&style=flat-square)
```groovy
dependencies {
compileOnly 'net.william278:HuskSync:version'
}
```
</details>
### 2. Adding HuskSync as a dependency
- Add HuskSync to your `softdepend` (if you want to optionally use HuskSync) or `depend` (if your plugin relies on HuskSync) section in `plugin.yml` of your project.
```yaml
name: MyPlugin
version: 1.0
main: net.william278.myplugin.MyPlugin
author: William278
description: 'A plugin that hooks with the HuskSync API!'
softdepend: # Or, use 'depend' here
- HuskSync
```
### 3. Next steps
Now that you've got everything ready, you can start doing stuff with the HuskSync API!
- [[UserData API]] &mdash; Get data snapshots and update current user data
- [[API Events]] &mdash; Listen to, cancel and modify the result of data synchronization events

17
docs/Commands.md Normal file
View File

@@ -0,0 +1,17 @@
This page contains a table of HuskSync commands and their required permission nodes.
| Command | Description | Permission |
|-----------------------------------------------|--------------------------------------|--------------------------------------|
| `/husksync` | Use `/husksync` subcommands | `husksync.command.husksync` |
| `/husksync info` | View plugin information | `husksync.command.husksync info` |
| `/husksync reload` | Reload config & message files | `husksync.command.husksync.reload` |
| `/husksync update` | Check if an update is available | `husksync.command.husksync.update` |
| `/husksync migrate <migrator> [args]` | Migrate user data | _Console-only_ |
| `/userdata view <username> [version_uuid]` | View a snapshot of user data | `husksync.command.userdata` |
| `/userdata restore <username> <version_uuid>` | Restore a snapshot of user data | `husksync.command.userdata.manage` |
| `/userdata delete <username> <version_uuid>` | Delete a snapshot of user data | `husksync.command.userdata.manage` |
| `/userdata pin <username> <version_uuid>` | Pin a snapshot of user data | `husksync.command.userdata.manage` |
| `/inventory <username> [version_uuid]` | View a user's inventory contents | `husksync.command.inventory`&dagger; |
| `/enderchest <username> [version_uuid]` | View a user's ender chest contents | `husksync.command.enderchest`&dagger;|
&dagger; The respective `husksync.command.inventory.edit` and `husksync.command.enderchest.edit` permission node is required to be able to edit a user's inventory/ender chest using the interface.

78
docs/Config-File.md Normal file
View File

@@ -0,0 +1,78 @@
This page contains the configuration file reference for HuskSync. The config file is located in `/plugins/HuskSync/config.yml`
## Example config
<details>
<summary>config.yml</summary>
```yaml
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ HuskSync Config ┃
# ┃ Developed by William278 ┃
# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
# ┣╸ Information: https://william278.net/project/husksync
# ┗╸ Documentation: https://william278.net/docs/husksync
language: en-gb
check_for_updates: true
cluster_id: ''
debug_logging: false
database:
credentials:
# Database connection settings
host: localhost
port: 3306
database: HuskSync
username: root
password: pa55w0rd
parameters: ?autoReconnect=true&useSSL=false
connection_pool:
# MySQL connection pool properties
maximum_pool_size: 10
minimum_idle: 10
maximum_lifetime: 1800000
keepalive_time: 0
connection_timeout: 5000
table_names:
users: husksync_users
user_data: husksync_user_data
redis:
credentials:
# Redis connection settings
host: localhost
port: 6379
password: ''
use_ssl: false
synchronization:
# Synchronization settings
max_user_data_snapshots: 5
save_on_world_save: true
save_on_death: false
save_empty_drops_on_death: true
compress_data: true
notification_display_slot: ACTION_BAR
synchronise_dead_players_changing_server: true
network_latency_milliseconds: 500
features:
health: true
statistics: true
persistent_data_container: false
hunger: true
ender_chests: true
advancements: true
location: false
game_mode: true
potion_effects: true
locked_maps: false
inventories: true
max_health: true
experience: true
blacklisted_commands_while_locked: []
event_priorities:
join_listener: LOWEST
quit_listener: LOWEST
death_listener: NORMAL
```
</details>
## Messages files
You can customize the plugin locales, too, by editing your `messages-xx-xx.yml` file. This file is formatted using [MineDown syntax](https://github.com/Phoenix616/MineDown). For more information, see [[Translations]].

45
docs/Data-Rotation.md Normal file
View File

@@ -0,0 +1,45 @@
HuskSync provides options for backing up and automatically rotating user data. That way, if something goes wrong, you can restore a user from a previous snapshot of user data.
## Snapshots
HuskSync creates what is known as "Snapshots" of a user's data whenever it saves data.
Each user data snapshot has:
- a unique ID
- a timestamp (when it was created)
- a save cause (why it was created)
- a flag to indicate if the snapshot has been pinned (preventing it from being rotated)
By default, HuskSync will store 5 user data snapshots about a user (including their latest snapshot). After that, when a new user snapshot is set, the oldest snapshot will automatically be deleted. You can change the number of snapshots to keep by changing the `max_user_data_snapshots` setting in the `config.yml` file (minimum 1).
Pinned user data snapshots are exempt from being rotated and can only be deleted manually in-game.
## Viewing user data
To view a list of a user's snapshots, use `/userdata list <username>`. Their most recent snapshots will be listed from the database, from newest to oldest. You can click the buttons to navigate through their pages.
[![Data snapshot list](https://raw.githubusercontent.com/WiIIiam278/HuskSync/master/images/data-snapshot-list.png)](#)
Snapshots marked with a star after the number have been pinned. Hover over it to view more information.
You can then click on the items in the list in chat to view an overview of each snapshot. Alternatively, to view a user's most recent data snapshot, use `/userdata view <username>`.
[![Data snapshot viewer](https://raw.githubusercontent.com/WiIIiam278/HuskSync/master/images/data-snapshot-viewer.png)](#)
## Managing user data
You can use the "Manage" buttons to manage user data. These buttons will only appear if you have the userdata command manage permission. (See [[Commands]])
- Click on "Delete" to remove the data
- Click on "Restore" to restore the user data. If the user is online, their items and stats will be updated, otherwise they will be set to this data upon next login.
- Click on "Pin" to pin the user data. An indicator will appear in the data viewer and list marking the snapshot as being pinned.
## Save causes
Data save causes, marked with a 🚩 flag, indicate what caused the data to be saved.
- **disconnect**: Indicates data saved when a player disconnected from the server (either to change servers, or to log off)
- **world save**: Indicates data saved when the world saved. This can be turned off in `config.yml` by setting `save_on_world_save` to false under `synchronization`.
- **server shutdown**: Indicates data saved when the server shut down
- **inventory command**: Indicates data was saved by editing inventory contents via the `/inventory` command
- **enderchest command**: Indicates data was saved by editing Ender Chest contents via the `/enderchest` command
- **backup restore**: Indicates data was saved by restoring it from a previous version
- **api**: Indicates data was saved by an [[API]] call
- **mpdb migration**: Indicates data was saved from being imported from MySQLPlayerDataBridge (See [[MPDB Migration]])
- **legacy migration**: Indicates data was saved from being imported from a legacy version (v1.x - See [[Legacy Migration]])
- **unknown**: Indicates data was saved by an unknown cause.

Some files were not shown because too many files have changed in this diff Show More