Skip to content

Commit

Permalink
Paper plugin support, save player itemsToKeep rather than drops if no…
Browse files Browse the repository at this point in the history
…t empty (WiIIiam278#179)

* Paper plugin support, save itemsToKeep if present, close WiIIiam278#172

* Fixup wrong packages, suppress a warning

* Update docs, add settings for death saving, reorganise config slightly

* Improve default server name lookup

* docs: Add note on Unsupported Versions

* docs: Minor Sync Modes tweaks
  • Loading branch information
WiIIiam278 authored Oct 9, 2023
1 parent 6d9e68a commit 7db3ed6
Show file tree
Hide file tree
Showing 19 changed files with 380 additions and 55 deletions.
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ subprojects {
from '../LICENSE'
}

if (['bukkit', 'plugin'].contains(project.name)) {
if (['paper'].contains(project.name)) {
compileJava.options.release.set 17
}

if (['bukkit', 'paper', 'plugin'].contains(project.name)) {
shadowJar {
destinationDirectory.set(file("$rootDir/target"))
archiveClassifier.set('')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ public void onEnable() {
});

// Register events
initialize("events", (plugin) -> this.eventListener = new BukkitEventListener(this));
initialize("events", (plugin) -> this.eventListener = createEventListener());

// Register commands
initialize("commands", (plugin) -> BukkitCommand.Type.registerCommands(this));
Expand Down Expand Up @@ -209,6 +209,11 @@ public void onDisable() {
log(Level.INFO, "Successfully disabled HuskSync v" + getPluginVersion());
}

@NotNull
protected BukkitEventListener createEventListener() {
return new BukkitEventListener(this);
}

@Override
@NotNull
public Set<OnlineUser> getOnlineUsers() {
Expand Down Expand Up @@ -259,6 +264,7 @@ public void setDataSyncer(@NotNull DataSyncer dataSyncer) {

@NotNull
@Override
@SuppressWarnings("unchecked")
public Map<Identifier, Serializer<? extends Data>> getSerializers() {
return serializers;
}
Expand Down
10 changes: 8 additions & 2 deletions common/src/main/java/net/william278/husksync/HuskSync.java
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,16 @@ default void debug(@NotNull String message, @NotNull Throwable... throwable) {
default void loadConfigs() {
try {
// Load settings
setSettings(Annotaml.create(new File(getDataFolder(), "config.yml"), Settings.class).get());
setSettings(Annotaml.create(
new File(getDataFolder(), "config.yml"),
Settings.class
).get());

// Load server name
setServer(Annotaml.create(new File(getDataFolder(), "server.yml"), Server.class).get());
setServer(Annotaml.create(
new File(getDataFolder(), "server.yml"),
Server.getDefault(this)
).get());

// Load locales from language preset default
final Locales languagePresets = Annotaml.create(
Expand Down
42 changes: 32 additions & 10 deletions common/src/main/java/net/william278/husksync/config/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@

package net.william278.husksync.config;

import net.william278.annotaml.Annotaml;
import net.william278.annotaml.YamlFile;
import net.william278.annotaml.YamlKey;
import net.william278.husksync.HuskSync;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.nio.file.Path;
import java.util.List;

/**
* Represents a server on a proxied network.
Expand All @@ -37,26 +41,44 @@
┗╸ If you join it using /server alpha, then set it to 'alpha' (case-sensitive)""")
public class Server {

@YamlKey("name")
private String serverName;

private Server(@NotNull String serverName) {
this.serverName = serverName;
}

@SuppressWarnings("unused")
private Server() {
}

@NotNull
public static Server getDefault(@NotNull HuskSync plugin) {
return new Server(getDefaultServerName(plugin));
}

/**
* Default server identifier.
* Find a sensible default name for the server name property
*/
@NotNull
public static String getDefaultServerName() {
private static String getDefaultServerName(@NotNull HuskSync plugin) {
try {
// Fetch server default from supported plugins if present
for (String s : List.of("HuskHomes", "HuskTowns")) {
final File serverFile = Path.of(plugin.getDataFolder().getParent(), s, "server.yml").toFile();
if (serverFile.exists()) {
return Annotaml.create(serverFile, Server.class).get().getName();
}
}

// Fetch server default from user dir name
final Path serverDirectory = Path.of(System.getProperty("user.dir"));
return serverDirectory.getFileName().toString().trim();
} catch (Exception e) {
} catch (Throwable e) {
return "server";
}
}

@YamlKey("name")
private String serverName = getDefaultServerName();

@SuppressWarnings("unused")
private Server() {
}

@Override
public boolean equals(@NotNull Object other) {
// If the name of this server matches another, the servers are the same.
Expand Down
39 changes: 28 additions & 11 deletions common/src/main/java/net/william278/husksync/config/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,21 @@ public class Settings {
private boolean saveOnWorldSave = true;

@YamlComment("Whether to create a snapshot for users when they die (containing their death drops)")
@YamlKey("synchronization.save_on_death")
@YamlKey("synchronization.save_on_death.enabled")
private boolean saveOnDeath = false;

@YamlComment("Whether to save empty death drops for users when they die")
@YamlKey("synchronization.save_empty_drops_on_death")
private boolean saveEmptyDropsOnDeath = true;
@YamlComment("What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). "
+ " Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server.")
@YamlKey("synchronization.save_on_death.items_to_save")
private DeathItemsMode deathItemsMode = DeathItemsMode.DROPS;

@YamlComment("Should a death snapshot still be created even if the items to save on the player's death are empty?")
@YamlKey("synchronization.save_on_death.save_empty_items")
private boolean saveEmptyDeathItems = true;

@YamlComment("Whether dead players who log out and log in to a different server should have their items saved.")
@YamlKey("synchronization.save_on_death.sync_dead_players_changing_server")
private boolean synchronizeDeadPlayersChangingServer = true;

@YamlComment("Whether to use the snappy data compression algorithm. Keep on unless you know what you're doing")
@YamlKey("synchronization.compress_data")
Expand All @@ -188,11 +197,6 @@ public class Settings {
@YamlKey("synchronization.synchronize_max_health")
private boolean synchronizeMaxHealth = true;

@YamlComment("Whether dead players who log out and log in to a different server should have their items saved. "
+ "You may need to modify this if you're using the keepInventory gamerule.")
@YamlKey("synchronization.synchronize_dead_players_changing_server")
private boolean synchronizeDeadPlayersChangingServer = true;

@YamlComment("If using the DELAY sync method, how long should this server listen for Redis key data updates before "
+ "pulling data from the database instead (i.e., if the user did not change servers).")
@YamlKey("synchronization.network_latency_milliseconds")
Expand Down Expand Up @@ -341,8 +345,13 @@ public boolean doSaveOnDeath() {
return saveOnDeath;
}

public boolean doSaveEmptyDropsOnDeath() {
return saveEmptyDropsOnDeath;
@NotNull
public DeathItemsMode getDeathItemsMode() {
return deathItemsMode;
}

public boolean doSaveEmptyDeathItems() {
return saveEmptyDeathItems;
}

public boolean doCompressData() {
Expand Down Expand Up @@ -397,6 +406,14 @@ public EventListener.Priority getEventPriority(@NotNull EventListener.ListenerTy
}
}

/**
* Represents the mode of saving items on death
*/
public enum DeathItemsMode {
DROPS,
ITEMS_TO_KEEP
}

/**
* Represents the names of tables in the database
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,16 @@ protected final void saveOnWorldSave(@NotNull List<OnlineUser> usersInWorld) {
* Handles the saving of data when a player dies
*
* @param user The user who died
* @param drops The items that this user would have dropped
* @param items The items that should be saved for this user on their death
*/
protected void saveOnPlayerDeath(@NotNull OnlineUser user, @NotNull Data.Items drops) {
protected void saveOnPlayerDeath(@NotNull OnlineUser user, @NotNull Data.Items items) {
if (plugin.isDisabling() || !plugin.getSettings().doSaveOnDeath() || plugin.isLocked(user.getUuid())
|| user.isNpc() || (!plugin.getSettings().doSaveEmptyDropsOnDeath() && drops.isEmpty())) {
|| user.isNpc() || (!plugin.getSettings().doSaveEmptyDeathItems() && items.isEmpty())) {
return;
}

final DataSnapshot.Packed snapshot = user.createSnapshot(DataSnapshot.SaveCause.DEATH);
snapshot.edit(plugin, (data -> data.getInventory().ifPresent(inventory -> inventory.setContents(drops))));
snapshot.edit(plugin, (data -> data.getInventory().ifPresent(inventory -> inventory.setContents(items))));
plugin.getDatabase().addSnapshot(user, snapshot);
}

Expand Down
15 changes: 9 additions & 6 deletions docs/Config-File.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,15 @@ synchronization:
- MPDB_MIGRATION
# Whether to create a snapshot for users on a world when the server saves that world
save_on_world_save: true
# Whether to create a snapshot for users when they die (containing their death drops)
save_on_death: false
# Whether to save empty death drops for users when they die
save_empty_drops_on_death: true
save_on_death:
# Whether to create a snapshot for users when they die (containing their death drops)
enabled: true
# What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server
items_to_save: DROPS
# Should a death snapshot still be created even if the items to save on the player's death are empty?
save_empty_items: false
# Whether dead players who log out and log in to a different server should have their items saved.
sync_dead_players_changing_server: true
# Whether to use the snappy data compression algorithm. Keep on unless you know what you're doing
compress_data: true
# Where to display sync notifications (ACTION_BAR, CHAT, TOAST or NONE)
Expand All @@ -87,8 +92,6 @@ synchronization:
persist_locked_maps: true
# Whether to synchronize player max health (requires health syncing to be enabled)
synchronize_max_health: true
# Whether dead players who log out and log in to a different server should have their items saved. You may need to modify this if you're using the keepInventory gamerule.
synchronize_dead_players_changing_server: true
# If using the DELAY sync method, how long should this server listen for Redis key data updates before pulling data from the database instead (i.e., if the user did not change servers).
network_latency_milliseconds: 500
# Which data types to synchronize (Docs: https://william278.net/docs/husksync/sync-features)
Expand Down
30 changes: 17 additions & 13 deletions docs/Keep-Inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,27 @@ HuskSync has some special handling when players die, to account for scenarios wh
* **Snapshot creation on death**&mdash;HuskSync can create a special snapshot for backup purposes when a player dies, formed by taking their drops and setting this to their inventory. When `keepInventory` is enabled, the player drops are empty, so this creates an inaccurate snapshot. This option is disabled by default.

## How can this be fixed?
You will need to set the `synchronization.save_on_death` (which controls making snapshots on death), `save_empty_drops_on_death` (which controls whether snapshots of players who have no items to drop should be created), and `synchronization.synchronize_dead_players_changing_server` (which controls whether to sync dead players when they change servers) options to `false` in `config.yml`.
You should change the `items_to_save` mode to `ITEMS_TO_KEEP` instead of drops. Also, ensure `save_empty_items` and `sync_dead_players_changing_server` are enabled.

<details>
<summary>Example in config.yml</summary>

```yml
synchronization:
# ...
save_on_death: false # <-- Set this to false
save_empty_drops_on_death: false # <-- Set this to false
# ...
synchronize_dead_players_changing_server: false # <-- Set this to false
```
<summary>Example in config.yml</summary>

```yml
synchronization:
#...
save_on_death:
# Whether to create a snapshot for users when they die (containing their death drops)
enabled: true
# What items to save in death snapshots? (DROPS or ITEMS_TO_KEEP). Note that ITEMS_TO_KEEP (suggested for keepInventory servers) requires a Paper 1.19.4+ server
items_to_save: ITEMS_TO_KEEP
# Should a death snapshot still be created even if the items to save on the player's death are empty?
save_empty_items: true
# Whether dead players who log out and log in to a different server should have their items saved.
sync_dead_players_changing_server: true
#...
```
</details>

## Troubleshooting with custom keepInventory setups
If the above doesn't work for you, you may need to do more things to get this to work properly.

Expand Down
2 changes: 2 additions & 0 deletions docs/Setup.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
This will walk you through installing HuskSync on your network of Spigot servers.

## Requirements
> **Note:** If the plugin fails to load, please check that you are not running an [incompatible version combination](Unsupported-Versions)
* A MySQL Database (v8.0+)
* A Redis Database (v5.0+) &mdash; see [[FAQs]] for more details.
* Any number of Spigot servers, connected by a BungeeCord or Velocity-based proxy (Minecraft v1.16.5+, running Java 16+)
Expand Down
7 changes: 4 additions & 3 deletions docs/Sync-Modes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
HuskSync offers two built-in **synchronization modes**. These sync modes change the way data is synced between servers. This page details the two sync modes available and how they work.
HuskSync offers two built-in **synchronization modes** that utilise Redis and MySQL to optimally sync data as users change servers (illustrated below). These sync modes change the way data is synced between servers, and can be changed in the `config.yml` file.

![Overall architecture of the synchronisation systems](https://raw.githubusercontent.com/WiIIiam278/HuskSync/master/images/system-diagram.png)

## Available Modes
* The `DELAY` sync mode is the default sync mode, that use the `network_latency_miliseconds` value to apply a delay before listening to Redis data
* The `LOCKSTEP` sync mode uses a data checkout system to ensure that all servers are in sync regardless of network latency or tick rate fluctuations. This mode was introduced in HuskSync v3.1

Expand All @@ -18,8 +21,6 @@ synchronization:
</details>
## Delay
![Delay diagram](https://raw.githubusercontent.com/WiIIiam278/HuskSync/master/images/system-diagram.png)
The `DELAY` sync mode works as described below:
* When a user disconnects from a server, a `SERVER_SWITCH` key is immediately set on Redis, followed by a `DATA_UPDATE` key which contains the user's packed and serialized Data Snapshot.
* When the user connects to a server, they are marked as locked (unable to break blocks, use containers, etc.)
Expand Down
10 changes: 10 additions & 0 deletions docs/Unsupported-Versions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
This plugin does not support the following software-Minecraft version combinations. The plugin will fail to load if you attempt to run it with these versions. Apologies for the inconvenience.

## Incompatibility table
| Minecraft Versions | Server Software | Notes |
|--------------------|-------------------------------------------|----------------------------------------|
| 1.19.4 | Only: `Purpur, Pufferfish`&dagger; | Older Paper builds also not supported. |
| 1.19.3 | Only: `Paper, Purpur, Pufferfish`&dagger; | Upgrade to 1.19.4 or use Spigot |
| below 1.16.5 | _All_ | Upgrade to 1.16.5 |

&dagger;Further downstream forks of this server software are also affected.
41 changes: 41 additions & 0 deletions paper/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
dependencies {
implementation project(':bukkit')
compileOnly project(':common')

compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT'
}

shadowJar {
dependencies {
exclude(dependency('com.mojang:brigadier'))
}

relocate 'org.apache.commons.io', 'net.william278.husksync.libraries.commons.io'
relocate 'org.apache.commons.text', 'net.william278.husksync.libraries.commons.text'
relocate 'org.apache.commons.lang3', 'net.william278.husksync.libraries.commons.lang3'
relocate 'com.google.gson', 'net.william278.husksync.libraries.gson'
relocate 'org.json', 'net.william278.husksync.libraries.json'
relocate 'com.fatboyindustrial', 'net.william278.husksync.libraries'
relocate 'de.themoep', 'net.william278.husksync.libraries'
relocate 'net.kyori', 'net.william278.husksync.libraries'
relocate 'org.jetbrains', 'net.william278.husksync.libraries'
relocate 'org.intellij', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
relocate 'dev.dejvokep', 'net.william278.husksync.libraries'
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
relocate 'net.william278.andjam', 'net.william278.husksync.libraries.andjam'
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
relocate 'net.roxeez', 'net.william278.husksync.libraries'

relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
relocate 'net.william278.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter'
relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter'
relocate 'net.william278.annotaml', 'net.william278.husksync.libraries.annotaml'
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
relocate 'de.tr7zw.changeme.nbtapi', 'net.william278.husksync.libraries.nbtapi'
}
Loading

0 comments on commit 7db3ed6

Please sign in to comment.