Store only creative inventories per MyWorlds group and leave survival to MyWorlds
This commit is contained in:
1 parent
2dc348ff18
commit
2baf35e35b
16 files changed
+775
-305
No files matched your search
@@ -38,3 +38,6 @@ bin/
|
||||
|
||||
# IntelliJ files
|
||||
.idea/
|
||||
|
||||
# Vendored compile-only APIs under libs/ are tracked on purpose.
|
||||
!libs/*/*.jar
|
||||
@@ -13,6 +13,7 @@ This is a [TrueOG Network](https://trueog.net) maintained soft fork of [eccentri
|
||||
- Gamemode rules live in one place, `api/GameModePolicy`, which every enforcement point in the plugin asks. It is published for other plugins so they enforce the same rule instead of reimplementing it: fetch it from the Bukkit services manager, or reflectively via `GameModeInventories#getGameModePolicy()`. `mayUse(player, gameMode, location)` answers for any gamemode; survival is always allowed, adventure never is.
|
||||
- Creative is enforced on movement and teleports, judged at the destination, and unsanctioned creative is forced back to survival with one-time fall protection. Gamemode switches pre-validated through the policy (`internalGameModeChange`) are trusted rather than policed a second time.
|
||||
- Login is deferred to [Spawn-OG](https://github.com/true-og/Spawn-OG) when it is installed. It normalizes gamemode inside a safety transaction that also relocates the player, so forcing survival underneath it would race that transaction. Spawn-OG also uses the policy and `internalGameModeChange` to hand creative or spectator back after a rescue or `/spawnback` return. Without Spawn-OG, GameModeInventories-OG still forces survival on join for creative players outside a creative region.
|
||||
- Only the **creative** inventory is stored here, one per bundle (a bundle is a MyWorlds inventory group, so world/world_nether/world_the_end share one). The **survival** inventory is owned by MyWorlds in Purpur's RocksDB, so no inventory is ever stored in two places and the two stores cannot diverge. On the creative boundary GMI saves the outgoing creative inventory at `LOW` and loads the incoming one at `HIGH`, so MyWorlds (which saves/restores survival at the same priorities) always captures the outgoing inventory before the incoming one is loaded. Creative follows the bundle on a world change too (saved for the world left, loaded for the world entered via `PlayerProfileAppliedEvent`), and is persisted on quit, shutdown and login. `(uuid, gamemode, bundle)` is unique, and a failed creative swap or legacy bundle assignment refuses (kick or no enable) rather than hand out a wrong inventory. Spectator and adventure are transient (not persisted). Death inventories are parked in a `deaths` table, keyed by bundle and game mode, and are added back (never overwriting what the player holds) when they are next in that bundle and mode; if parking fails the normal drops are kept. Without MyWorlds the bundle is the world name and players are switched to survival before a cross-world teleport.
|
||||
- Dropped LogBlock integration. Block-removal logging now runs exclusively through CoreProtect / CoreProtect-OG.
|
||||
- Targets Purpur API 1.19.4 (Java 17, GraalVM toolchain).
|
||||
|
||||
|
||||
@@ -47,6 +47,10 @@ dependencies {
|
||||
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT") // Declare Purpur API version to be packaged.
|
||||
compileOnly("net.coreprotect:coreprotect:22.4") // Import the CoreProtect API.
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9") // Import the WorldGuard API.
|
||||
compileOnly(files("libs/MyWorlds/MyWorlds-1.19.4-v1.jar")) // Import MyWorlds API (local build).
|
||||
compileOnly(
|
||||
files("libs/BKCommonLib/BKCommonLib-1.19.4-v2.jar")
|
||||
) // Import BKCommonLib API (local build, required by MyWorlds at compile time).
|
||||
implementation("com.zaxxer:HikariCP:5.0.1")
|
||||
implementation("org.slf4j:slf4j-api:2.0.17") // Bundle a relocated SLF4J API for shaded dependencies.
|
||||
implementation("org.slf4j:slf4j-nop:2.0.17") // Provide a relocated no-op SLF4J backend to avoid provider warnings.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -42,6 +42,7 @@ public class GameModeInventories extends JavaPlugin {
|
||||
private final Set<UUID> internalSwitches = new HashSet<>();
|
||||
public BukkitTask recordingTask;
|
||||
private GameModeInventoriesInventory inventoryHandler;
|
||||
private GameModeInventoriesBundles bundles;
|
||||
private GameModePolicy gameModePolicy;
|
||||
private GameModeInventoriesBlock block;
|
||||
private GameModeInventoriesMessage m;
|
||||
@@ -58,13 +59,13 @@ public class GameModeInventories extends JavaPlugin {
|
||||
|
||||
getServer().getOnlinePlayers().forEach((p) -> {
|
||||
|
||||
if (p.hasPermission("gamemodeinventories.use")) {
|
||||
// Persist creative inventories on shutdown; survival is saved by MyWorlds and
|
||||
// the server
|
||||
if (p.hasPermission("gamemodeinventories.use") && p.isOnline()
|
||||
&& p.getGameMode().equals(GameMode.CREATIVE))
|
||||
{
|
||||
|
||||
if (p.isOnline()) {
|
||||
|
||||
inventoryHandler.switchInventories(p, p.getGameMode());
|
||||
|
||||
}
|
||||
inventoryHandler.saveCreative(p, getBundles().of(p.getWorld()));
|
||||
|
||||
}
|
||||
|
||||
@@ -148,7 +149,19 @@ public class GameModeInventories extends JavaPlugin {
|
||||
|
||||
}
|
||||
|
||||
bundles = new GameModeInventoriesBundles(this);
|
||||
inventoryHandler = new GameModeInventoriesInventory(this);
|
||||
// If legacy slots cannot be bundled, serving inventories would read empty, so
|
||||
// refuse to enable
|
||||
if (!inventoryHandler.assignLegacySlotsTo(bundles.of(getServer().getWorlds().get(0)))) {
|
||||
|
||||
getLogger().severe(
|
||||
"[GameModeInventories] Could not assign legacy inventory slots, disabling to avoid serving empty inventories");
|
||||
pm.disablePlugin(this);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Published before the listeners so anything reacting to enable can read it.
|
||||
gameModePolicy = new GameModePolicy(this);
|
||||
getServer().getServicesManager().register(GameModePolicy.class, gameModePolicy, this,
|
||||
@@ -156,6 +169,12 @@ public class GameModeInventories extends JavaPlugin {
|
||||
pm.registerEvents(new GameModeInventoriesListener(this), this);
|
||||
pm.registerEvents(new GameModeInventoriesChunkLoadListener(this), this);
|
||||
pm.registerEvents(new GameModeInventoriesDeath(this), this);
|
||||
if (bundles.usesMyWorlds()) {
|
||||
|
||||
pm.registerEvents(new GameModeInventoriesMyWorldsListener(this), this);
|
||||
|
||||
}
|
||||
|
||||
pm.registerEvents(new GameModeInventoriesBlockListener(this), this);
|
||||
if (getConfig().getBoolean("track_creative_place.dont_track_is_whitelist")) {
|
||||
|
||||
@@ -380,6 +399,12 @@ public class GameModeInventories extends JavaPlugin {
|
||||
|
||||
}
|
||||
|
||||
public GameModeInventoriesBundles getBundles() {
|
||||
|
||||
return bundles;
|
||||
|
||||
}
|
||||
|
||||
public GameModeInventoriesBlock getBlock() {
|
||||
|
||||
return block;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014 eccentric_nz.
|
||||
*/
|
||||
package me.eccentric_nz.gamemodeinventories;
|
||||
|
||||
import org.bukkit.World;
|
||||
|
||||
// Names a world's inventory bundle: its MyWorlds group, else the world itself
|
||||
public final class GameModeInventoriesBundles {
|
||||
|
||||
private final boolean myWorlds;
|
||||
|
||||
public GameModeInventoriesBundles(GameModeInventories plugin) {
|
||||
|
||||
myWorlds = plugin.getServer().getPluginManager().isPluginEnabled("MyWorlds") && MyWorldsBundles.available();
|
||||
plugin.getLogger().info("[GameModeInventories] Inventory bundles: "
|
||||
+ (myWorlds ? "MyWorlds inventory groups" : "one per world"));
|
||||
|
||||
}
|
||||
|
||||
public boolean usesMyWorlds() {
|
||||
|
||||
return myWorlds;
|
||||
|
||||
}
|
||||
|
||||
public String of(World world) {
|
||||
|
||||
if (myWorlds) {
|
||||
|
||||
String group = MyWorldsBundles.of(world);
|
||||
if (group != null) {
|
||||
|
||||
return group;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return world.getName();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,8 +8,10 @@ import java.util.Properties;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
|
||||
@@ -35,9 +37,13 @@ public class GameModeInventoriesDeath implements Listener {
|
||||
Player p = event.getEntity();
|
||||
if (p.hasPermission("gamemodeinventories.death") && plugin.getConfig().getBoolean("save_on_death")) {
|
||||
|
||||
// save their inventory
|
||||
plugin.getInventoryHandler().saveOnDeath(p);
|
||||
event.getDrops().clear();
|
||||
// Only suppress vanilla drops when the inventory was actually parked, else the
|
||||
// items are lost
|
||||
if (plugin.getInventoryHandler().saveOnDeath(p)) {
|
||||
|
||||
event.getDrops().clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -77,6 +83,27 @@ public class GameModeInventoriesDeath implements Listener {
|
||||
|
||||
}
|
||||
|
||||
// A parked death inventory returns once the player is back in its world
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onWorldChange(PlayerChangedWorldEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (p.hasPermission("gamemodeinventories.death") && plugin.getConfig().getBoolean("save_on_death")) {
|
||||
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
|
||||
|
||||
if (p.isOnline()) {
|
||||
|
||||
plugin.getInventoryHandler().restoreOnSpawn(p);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// reads force-gamemode and gamemode from server.properties
|
||||
private ServerValues getServerForceGamemode() {
|
||||
|
||||
@@ -88,8 +115,10 @@ public class GameModeInventoriesDeath implements Listener {
|
||||
String path = "server.properties";
|
||||
in = new FileInputStream(path);
|
||||
properties.load(in);
|
||||
sv.setForce(properties.getProperty("force-gamemode").equalsIgnoreCase("true"));
|
||||
GameMode gmode = GameMode.valueOf(properties.getProperty("gamemode").toUpperCase(Locale.ENGLISH));
|
||||
// Missing keys mean the vanilla defaults, not a crash on enable
|
||||
sv.setForce(properties.getProperty("force-gamemode", "false").equalsIgnoreCase("true"));
|
||||
GameMode gmode = GameMode
|
||||
.valueOf(properties.getProperty("gamemode", "survival").toUpperCase(Locale.ENGLISH));
|
||||
sv.setMode(gmode);
|
||||
|
||||
} catch (FileNotFoundException ex) {
|
||||
|
||||
+379
-287
@@ -13,15 +13,18 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.entity.minecart.HopperMinecart;
|
||||
import org.bukkit.entity.minecart.PoweredMinecart;
|
||||
import org.bukkit.entity.minecart.StorageMinecart;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
// Owns only the CREATIVE inventory per bundle. Survival lives in Purpur's RocksDB through MyWorlds, so no
|
||||
// inventory is ever stored in two places and the two stores cannot diverge.
|
||||
public class GameModeInventoriesInventory {
|
||||
|
||||
private static final String CREATIVE = GameMode.CREATIVE.name();
|
||||
|
||||
private final GameModeInventories plugin;
|
||||
private final boolean saveXP;
|
||||
private final boolean saveArmour;
|
||||
@@ -38,36 +41,189 @@ public class GameModeInventoriesInventory {
|
||||
|
||||
}
|
||||
|
||||
public void switchInventories(Player player, GameMode newGM) {
|
||||
// Persists the player's current (creative) inventory for this bundle. Kicks on
|
||||
// failure so an unsaved
|
||||
// creative inventory is never silently dropped or overwritten later.
|
||||
public void saveCreative(Player player, String bundle) {
|
||||
|
||||
String uuid = player.getUniqueId().toString();
|
||||
String name = player.getName();
|
||||
String currentGM = player.getGameMode().name();
|
||||
GameModeInventoriesXPCalculator xpc = null;
|
||||
if (saveXP) {
|
||||
GameModeInventoriesXPCalculator xpc = saveXP ? new GameModeInventoriesXPCalculator(player) : null;
|
||||
String inv = GameModeInventoriesBukkitSerialization.toDatabase(player.getInventory().getContents());
|
||||
int xp = saveXP ? xpc.getCurrentExp() : 0;
|
||||
String arm = saveArmour
|
||||
? GameModeInventoriesBukkitSerialization.toDatabase(player.getInventory().getArmorContents())
|
||||
: null;
|
||||
String ender = (saveEnderChest && player.getEnderChest() != null)
|
||||
? GameModeInventoriesBukkitSerialization.toDatabase(player.getEnderChest().getContents())
|
||||
: null;
|
||||
|
||||
xpc = new GameModeInventoriesXPCalculator(player);
|
||||
try (Connection connection = plugin.getDatabaseConnection()) {
|
||||
|
||||
connection.setAutoCommit(false);
|
||||
try {
|
||||
|
||||
saveSlot(connection, uuid, name, CREATIVE, bundle, inv, xp, arm, ender);
|
||||
connection.commit();
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
connection.rollback();
|
||||
throw e;
|
||||
|
||||
} finally {
|
||||
|
||||
connection.setAutoCommit(true);
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
GameModeInventories.plugin.debug("Could not save creative inventory, kicking to avoid losing it, " + e);
|
||||
player.kickPlayer("Inventory sync failed, please rejoin");
|
||||
|
||||
}
|
||||
|
||||
String inv = GameModeInventoriesBukkitSerialization.toDatabase(player.getInventory().getContents());
|
||||
try (Connection connection = plugin.getDatabaseConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + plugin.getPrefix() + "inventories WHERE uuid = ? AND gamemode = ?");)
|
||||
}
|
||||
|
||||
// Loads the player's creative inventory for this bundle, or clears it when none
|
||||
// is stored. Kicks on failure.
|
||||
public void loadCreative(Player player, String bundle) {
|
||||
|
||||
String uuid = player.getUniqueId().toString();
|
||||
GameModeInventoriesXPCalculator xpc = saveXP ? new GameModeInventoriesXPCalculator(player) : null;
|
||||
try (Connection connection = plugin.getDatabaseConnection()) {
|
||||
|
||||
LoadedSlot dest = readSlot(connection, uuid, CREATIVE, bundle);
|
||||
applyCreative(player, dest, xpc);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
GameModeInventories.plugin
|
||||
.debug("Could not load creative inventory, kicking to avoid a wrong profile, " + e);
|
||||
player.kickPlayer("Inventory sync failed, please rejoin");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Removes potion effects the player picked up in creative, matching the old
|
||||
// leave-creative behavior.
|
||||
public void clearCreativePotions(Player player) {
|
||||
|
||||
if (potions) {
|
||||
|
||||
player.getActivePotionEffects().forEach((effect) -> player.removePotionEffect(effect.getType()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The decoded contents of one (uuid, gamemode, bundle) slot, or null when none
|
||||
// is stored.
|
||||
private static final class LoadedSlot {
|
||||
|
||||
final ItemStack[] inv;
|
||||
final ItemStack[] arm;
|
||||
final ItemStack[] ender;
|
||||
final int xp;
|
||||
|
||||
LoadedSlot(ItemStack[] inv, ItemStack[] arm, ItemStack[] ender, int xp) {
|
||||
|
||||
this.inv = inv;
|
||||
this.arm = arm;
|
||||
this.ender = ender;
|
||||
this.xp = xp;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static ItemStack[] decode(String s) throws IOException {
|
||||
|
||||
return s.startsWith("[") ? GameModeInventoriesJSONSerialization.toItemStacks(s)
|
||||
: GameModeInventoriesBukkitSerialization.fromDatabase(s);
|
||||
|
||||
}
|
||||
|
||||
// Reads and fully decodes a slot before anything is applied. Null when the
|
||||
// player has none yet.
|
||||
private LoadedSlot readSlot(Connection connection, String uuid, String gamemode, String bundle)
|
||||
throws SQLException, IOException
|
||||
{
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + plugin.getPrefix() + "inventories WHERE uuid = ? AND gamemode = ? AND bundle = ?");)
|
||||
{
|
||||
|
||||
// get their current gamemode inventory from database
|
||||
statement.setString(1, uuid);
|
||||
statement.setString(2, currentGM);
|
||||
try (ResultSet rsInv = statement.executeQuery();) {
|
||||
statement.setString(2, gamemode);
|
||||
statement.setString(3, bundle);
|
||||
try (ResultSet rs = statement.executeQuery();) {
|
||||
|
||||
int id = 0;
|
||||
if (rsInv.next()) {
|
||||
if (!rs.next()) {
|
||||
|
||||
// update it with their current inventory
|
||||
id = rsInv.getInt("id");
|
||||
String updateQuery = "UPDATE " + plugin.getPrefix() + "inventories SET inventory = ? WHERE id = ?";
|
||||
try (PreparedStatement ps = connection.prepareStatement(updateQuery);) {
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
ItemStack[] inv = decode(rs.getString("inventory"));
|
||||
int xp = rs.getInt("xp");
|
||||
ItemStack[] arm = null;
|
||||
if (saveArmour) {
|
||||
|
||||
String savedarmour = rs.getString("armour");
|
||||
if (savedarmour != null) {
|
||||
|
||||
arm = decode(savedarmour);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ItemStack[] ender = null;
|
||||
if (saveEnderChest) {
|
||||
|
||||
String savedender = rs.getString("enderchest");
|
||||
if (savedender == null || savedender.equals("[Null]") || savedender.isEmpty()) {
|
||||
|
||||
savedender = "[\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\"]";
|
||||
|
||||
}
|
||||
|
||||
ender = decode(savedender);
|
||||
|
||||
}
|
||||
|
||||
return new LoadedSlot(inv, arm, ender, xp);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Upserts one (uuid, gamemode, bundle) slot with the given contents.
|
||||
private void saveSlot(Connection connection, String uuid, String name, String gamemode, String bundle, String inv,
|
||||
int xp, String arm, String ender) throws SQLException
|
||||
{
|
||||
|
||||
int id;
|
||||
try (PreparedStatement select = connection.prepareStatement(
|
||||
"SELECT id FROM " + plugin.getPrefix() + "inventories WHERE uuid = ? AND gamemode = ? AND bundle = ?");)
|
||||
{
|
||||
|
||||
select.setString(1, uuid);
|
||||
select.setString(2, gamemode);
|
||||
select.setString(3, bundle);
|
||||
try (ResultSet rs = select.executeQuery();) {
|
||||
|
||||
if (rs.next()) {
|
||||
|
||||
id = rs.getInt("id");
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE " + plugin.getPrefix() + "inventories SET inventory = ? WHERE id = ?");)
|
||||
{
|
||||
|
||||
ps.setString(1, inv);
|
||||
ps.setInt(2, id);
|
||||
@@ -77,26 +233,21 @@ public class GameModeInventoriesInventory {
|
||||
|
||||
} else {
|
||||
|
||||
// they haven't got an inventory saved yet so make one with their current
|
||||
// inventory
|
||||
String insertQuery = "INSERT INTO " + plugin.getPrefix()
|
||||
+ "inventories (uuid, player, gamemode, inventory) VALUES (?, ?, ?, ?)";
|
||||
try (PreparedStatement ps = connection.prepareStatement(insertQuery,
|
||||
try (PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO " + plugin.getPrefix()
|
||||
+ "inventories (uuid, player, gamemode, inventory, bundle) VALUES (?, ?, ?, ?, ?)",
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);)
|
||||
{
|
||||
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, name);
|
||||
ps.setString(3, currentGM);
|
||||
ps.setString(3, gamemode);
|
||||
ps.setString(4, inv);
|
||||
ps.setString(5, bundle);
|
||||
ps.executeUpdate();
|
||||
try (ResultSet idRS = ps.getGeneratedKeys();) {
|
||||
try (ResultSet keys = ps.getGeneratedKeys();) {
|
||||
|
||||
if (idRS.next()) {
|
||||
|
||||
id = idRS.getInt(1);
|
||||
|
||||
}
|
||||
id = keys.next() ? keys.getInt(1) : 0;
|
||||
|
||||
}
|
||||
|
||||
@@ -104,306 +255,222 @@ public class GameModeInventoriesInventory {
|
||||
|
||||
}
|
||||
|
||||
if (saveXP) {
|
||||
|
||||
// get players XP
|
||||
int a = xpc.getCurrentExp();
|
||||
String xpQuery = "UPDATE " + plugin.getPrefix() + "inventories SET xp = ? WHERE id = ?";
|
||||
try (PreparedStatement psx = connection.prepareStatement(xpQuery);) {
|
||||
|
||||
psx.setInt(1, a);
|
||||
psx.setInt(2, id);
|
||||
psx.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (saveArmour) {
|
||||
|
||||
// get players armour
|
||||
String arm = GameModeInventoriesBukkitSerialization
|
||||
.toDatabase(player.getInventory().getArmorContents());
|
||||
String armourQuery = "UPDATE " + plugin.getPrefix() + "inventories SET armour = ? WHERE id = ?";
|
||||
try (PreparedStatement psa = connection.prepareStatement(armourQuery);) {
|
||||
|
||||
psa.setString(1, arm);
|
||||
psa.setInt(2, id);
|
||||
psa.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (saveEnderChest) {
|
||||
|
||||
// get players enderchest
|
||||
Inventory ec = player.getEnderChest();
|
||||
if (ec != null) {
|
||||
|
||||
String ender = GameModeInventoriesBukkitSerialization.toDatabase(ec.getContents());
|
||||
String enderQuery = "UPDATE " + plugin.getPrefix()
|
||||
+ "inventories SET enderchest = ? WHERE id = ?";
|
||||
try (PreparedStatement pse = connection.prepareStatement(enderQuery);) {
|
||||
|
||||
pse.setString(1, ender);
|
||||
pse.setInt(2, id);
|
||||
pse.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (potions && currentGM.equals("CREATIVE") && !newGM.equals(GameMode.CREATIVE)) {
|
||||
|
||||
// remove all potion effects
|
||||
player.getActivePotionEffects().forEach((effect) -> {
|
||||
|
||||
player.removePotionEffect(effect.getType());
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// check if they have an inventory for the new gamemode
|
||||
try {
|
||||
|
||||
statement.setString(1, uuid);
|
||||
statement.setString(2, newGM.name());
|
||||
try (ResultSet rsNewInv = statement.executeQuery();) {
|
||||
|
||||
int amount;
|
||||
if (rsNewInv.next()) {
|
||||
|
||||
// set their inventory to the saved one
|
||||
String savedinventory = rsNewInv.getString("inventory");
|
||||
ItemStack[] stacks;
|
||||
if (savedinventory.startsWith("[")) {
|
||||
|
||||
stacks = GameModeInventoriesJSONSerialization.toItemStacks(savedinventory);
|
||||
|
||||
} else {
|
||||
|
||||
stacks = GameModeInventoriesBukkitSerialization.fromDatabase(savedinventory);
|
||||
|
||||
}
|
||||
|
||||
player.getInventory().setContents(stacks);
|
||||
amount = rsNewInv.getInt("xp");
|
||||
if (saveArmour) {
|
||||
|
||||
String savedarmour = rsNewInv.getString("armour");
|
||||
if (savedarmour != null) {
|
||||
|
||||
ItemStack[] a;
|
||||
if (savedarmour.startsWith("[")) {
|
||||
|
||||
a = GameModeInventoriesJSONSerialization.toItemStacks(savedarmour);
|
||||
|
||||
} else {
|
||||
|
||||
a = GameModeInventoriesBukkitSerialization.fromDatabase(savedarmour);
|
||||
|
||||
}
|
||||
|
||||
player.getInventory().setArmorContents(a);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (saveEnderChest) {
|
||||
|
||||
String savedender = rsNewInv.getString("enderchest");
|
||||
if (savedender == null || savedender.equals("[Null]") || savedender.equals("")
|
||||
|| savedender.isEmpty())
|
||||
{
|
||||
|
||||
// empty inventory
|
||||
savedender = "[\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\",\"null\"]";
|
||||
|
||||
}
|
||||
|
||||
ItemStack[] e;
|
||||
if (savedender.startsWith("[")) {
|
||||
|
||||
e = GameModeInventoriesJSONSerialization.toItemStacks(savedender);
|
||||
|
||||
} else {
|
||||
|
||||
e = GameModeInventoriesBukkitSerialization.fromDatabase(savedender);
|
||||
|
||||
}
|
||||
|
||||
Inventory echest = player.getEnderChest();
|
||||
echest.setContents(e);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// start with an empty inventory
|
||||
player.getInventory().clear();
|
||||
if (saveArmour) {
|
||||
|
||||
player.getInventory().setBoots(null);
|
||||
player.getInventory().setChestplate(null);
|
||||
player.getInventory().setLeggings(null);
|
||||
player.getInventory().setHelmet(null);
|
||||
|
||||
}
|
||||
|
||||
if (saveEnderChest) {
|
||||
|
||||
Inventory echest = player.getEnderChest();
|
||||
echest.clear();
|
||||
|
||||
}
|
||||
|
||||
amount = 0;
|
||||
|
||||
}
|
||||
|
||||
if (saveXP) {
|
||||
|
||||
xpc.setExp(amount);
|
||||
|
||||
}
|
||||
|
||||
player.updateInventory();
|
||||
|
||||
}
|
||||
|
||||
} catch (IOException ex) {
|
||||
|
||||
GameModeInventories.plugin.debug("Could not restore inventory on gamemode change, " + ex);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
}
|
||||
|
||||
GameModeInventories.plugin.debug("Could not save inventory on gamemode change, " + e);
|
||||
if (saveXP) {
|
||||
|
||||
try (PreparedStatement ps = connection
|
||||
.prepareStatement("UPDATE " + plugin.getPrefix() + "inventories SET xp = ? WHERE id = ?");)
|
||||
{
|
||||
|
||||
ps.setInt(1, xp);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (saveArmour && arm != null) {
|
||||
|
||||
try (PreparedStatement ps = connection
|
||||
.prepareStatement("UPDATE " + plugin.getPrefix() + "inventories SET armour = ? WHERE id = ?");)
|
||||
{
|
||||
|
||||
ps.setString(1, arm);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (saveEnderChest && ender != null) {
|
||||
|
||||
try (PreparedStatement ps = connection
|
||||
.prepareStatement("UPDATE " + plugin.getPrefix() + "inventories SET enderchest = ? WHERE id = ?");)
|
||||
{
|
||||
|
||||
ps.setString(1, ender);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void saveOnDeath(Player p) {
|
||||
// Puts the decoded creative slot onto the player, or clears everything when
|
||||
// there was none.
|
||||
private void applyCreative(Player player, LoadedSlot dest, GameModeInventoriesXPCalculator xpc) {
|
||||
|
||||
if (dest == null) {
|
||||
|
||||
player.getInventory().clear();
|
||||
if (saveArmour) {
|
||||
|
||||
player.getInventory().setBoots(null);
|
||||
player.getInventory().setChestplate(null);
|
||||
player.getInventory().setLeggings(null);
|
||||
player.getInventory().setHelmet(null);
|
||||
|
||||
}
|
||||
|
||||
if (saveEnderChest) {
|
||||
|
||||
player.getEnderChest().clear();
|
||||
|
||||
}
|
||||
|
||||
if (saveXP) {
|
||||
|
||||
xpc.setExp(0);
|
||||
|
||||
}
|
||||
|
||||
player.updateInventory();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
player.getInventory().setContents(dest.inv);
|
||||
if (saveArmour && dest.arm != null) {
|
||||
|
||||
player.getInventory().setArmorContents(dest.arm);
|
||||
|
||||
}
|
||||
|
||||
if (saveEnderChest && dest.ender != null) {
|
||||
|
||||
player.getEnderChest().setContents(dest.ender);
|
||||
|
||||
}
|
||||
|
||||
if (saveXP) {
|
||||
|
||||
xpc.setExp(dest.xp);
|
||||
|
||||
}
|
||||
|
||||
player.updateInventory();
|
||||
|
||||
}
|
||||
|
||||
// Pre-bundle slots were used everywhere, so they join the main bundle. False
|
||||
// when the migration could not run.
|
||||
public boolean assignLegacySlotsTo(String bundle) {
|
||||
|
||||
try (Connection connection = plugin.getDatabaseConnection();
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE " + plugin.getPrefix() + "inventories SET bundle = ? WHERE bundle IS NULL");)
|
||||
{
|
||||
|
||||
ps.setString(1, bundle);
|
||||
int moved = ps.executeUpdate();
|
||||
if (moved > 0) {
|
||||
|
||||
plugin.getLogger().info(
|
||||
"[GameModeInventories] Assigned " + moved + " legacy inventory slots to bundle " + bundle);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (SQLException e) {
|
||||
|
||||
GameModeInventories.plugin.debug("Could not assign legacy inventory slots to a bundle, " + e);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Parks a death's inventory under its bundle and mode. Always inserts, so an
|
||||
// earlier unrestored death is never
|
||||
// overwritten. Returns true only when the row is durably written, so the caller
|
||||
// can keep vanilla drops otherwise.
|
||||
public boolean saveOnDeath(Player p) {
|
||||
|
||||
String uuid = p.getUniqueId().toString();
|
||||
String name = p.getName();
|
||||
String gm = p.getGameMode().name();
|
||||
String bundle = plugin.getBundles().of(p.getWorld());
|
||||
String inv = GameModeInventoriesBukkitSerialization.toDatabase(p.getInventory().getContents());
|
||||
String arm = GameModeInventoriesBukkitSerialization.toDatabase(p.getInventory().getArmorContents());
|
||||
try (Connection connection = plugin.getDatabaseConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT id FROM " + plugin.getPrefix() + "inventories WHERE uuid = ? AND gamemode = ?");)
|
||||
PreparedStatement ps = connection.prepareStatement("INSERT INTO " + plugin.getPrefix()
|
||||
+ "deaths (uuid, player, gamemode, world, inventory, armour) VALUES (?, ?, ?, ?, ?, ?)");)
|
||||
{
|
||||
|
||||
// get their current gamemode inventory from database
|
||||
statement.setString(1, uuid);
|
||||
statement.setString(2, gm);
|
||||
try (ResultSet rsInv = statement.executeQuery();) {
|
||||
|
||||
if (rsInv.isBeforeFirst() && rsInv.next()) {
|
||||
|
||||
// update it with their current inventory
|
||||
int id = rsInv.getInt("id");
|
||||
String updateQuery = "UPDATE " + plugin.getPrefix()
|
||||
+ "inventories SET inventory = ?, armour = ? WHERE id = ?";
|
||||
try (PreparedStatement ps = connection.prepareStatement(updateQuery);) {
|
||||
|
||||
ps.setString(1, inv);
|
||||
ps.setString(2, arm);
|
||||
ps.setInt(3, id);
|
||||
ps.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// they haven't got an inventory saved yet so make one with their current
|
||||
// inventory
|
||||
String invQuery = "INSERT INTO " + plugin.getPrefix()
|
||||
+ "inventories (uuid, player, gamemode, inventory, armour) VALUES (?, ?, ?, ?, ?,)";
|
||||
try (PreparedStatement ps = connection.prepareStatement(invQuery);) {
|
||||
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, name);
|
||||
ps.setString(3, gm);
|
||||
ps.setString(4, inv);
|
||||
ps.setString(5, arm);
|
||||
ps.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
ps.setString(1, uuid);
|
||||
ps.setString(2, name);
|
||||
ps.setString(3, gm);
|
||||
// The 'world' column holds the inventory bundle, so a shared group restores
|
||||
// anywhere in it
|
||||
ps.setString(4, bundle);
|
||||
ps.setString(5, inv);
|
||||
ps.setString(6, arm);
|
||||
return ps.executeUpdate() > 0;
|
||||
|
||||
} catch (SQLException e) {
|
||||
|
||||
GameModeInventories.plugin.debug("Could not save inventories on player death, " + e);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Returns the oldest death parked in this bundle and mode, added to the player
|
||||
// without discarding what they hold.
|
||||
public void restoreOnSpawn(Player p) {
|
||||
|
||||
String uuid = p.getUniqueId().toString();
|
||||
String gm = p.getGameMode().name();
|
||||
String bundle = plugin.getBundles().of(p.getWorld());
|
||||
try (Connection connection = plugin.getDatabaseConnection();
|
||||
PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + plugin.getPrefix() + "inventories WHERE uuid = ? AND gamemode = ?");)
|
||||
PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + plugin.getPrefix()
|
||||
+ "deaths WHERE uuid = ? AND world = ? AND gamemode = ? ORDER BY id LIMIT 1");)
|
||||
{
|
||||
|
||||
// get their current gamemode inventory from database
|
||||
statement.setString(1, uuid);
|
||||
statement.setString(2, gm);
|
||||
try (ResultSet rsInv = statement.executeQuery();) {
|
||||
statement.setString(2, bundle);
|
||||
statement.setString(3, gm);
|
||||
try (ResultSet rs = statement.executeQuery();) {
|
||||
|
||||
if (rsInv.next()) {
|
||||
if (!rs.next()) {
|
||||
|
||||
try {
|
||||
return;
|
||||
|
||||
// set their inventory to the saved one
|
||||
String savedinventory = rsInv.getString("inventory");
|
||||
ItemStack[] i;
|
||||
if (savedinventory.startsWith("[")) {
|
||||
}
|
||||
|
||||
i = GameModeInventoriesJSONSerialization.toItemStacks(savedinventory);
|
||||
int id = rs.getInt("id");
|
||||
ItemStack[] i;
|
||||
ItemStack[] a;
|
||||
try {
|
||||
|
||||
} else {
|
||||
i = decode(rs.getString("inventory"));
|
||||
a = decode(rs.getString("armour"));
|
||||
|
||||
i = GameModeInventoriesBukkitSerialization.fromDatabase(savedinventory);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
GameModeInventories.plugin.debug("Could not restore inventories on respawn, " + e);
|
||||
return;
|
||||
|
||||
p.getInventory().setContents(i);
|
||||
String savedarmour = rsInv.getString("armour");
|
||||
ItemStack[] a;
|
||||
if (savedarmour.startsWith("[")) {
|
||||
}
|
||||
|
||||
a = GameModeInventoriesJSONSerialization.toItemStacks(savedarmour);
|
||||
giveOrDrop(p, i);
|
||||
giveOrDrop(p, a);
|
||||
|
||||
} else {
|
||||
try (PreparedStatement clear = connection
|
||||
.prepareStatement("DELETE FROM " + plugin.getPrefix() + "deaths WHERE id = ?");)
|
||||
{
|
||||
|
||||
a = GameModeInventoriesBukkitSerialization.fromDatabase(savedarmour);
|
||||
|
||||
}
|
||||
|
||||
p.getInventory().setArmorContents(a);
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
GameModeInventories.plugin.debug("Could not restore inventories on respawn, " + e);
|
||||
|
||||
}
|
||||
clear.setInt(1, id);
|
||||
clear.executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
@@ -417,6 +484,31 @@ public class GameModeInventoriesInventory {
|
||||
|
||||
}
|
||||
|
||||
// Adds items to the player, dropping any that do not fit, so a restore never
|
||||
// clobbers what they already hold.
|
||||
private void giveOrDrop(Player p, ItemStack[] items) {
|
||||
|
||||
if (items == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
for (ItemStack item : items) {
|
||||
|
||||
if (item == null || item.getType().isAir()) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
p.getInventory().addItem(item).values()
|
||||
.forEach(leftover -> p.getWorld().dropItem(p.getLocation(), leftover));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isInstanceOf(Entity e) {
|
||||
|
||||
return e instanceof PoweredMinecart || e instanceof StorageMinecart || e instanceof HopperMinecart
|
||||
|
||||
@@ -113,7 +113,8 @@ public class GameModeInventoriesListener implements Listener {
|
||||
|
||||
if (p.isOnline()) {
|
||||
|
||||
plugin.getInventoryHandler().switchInventories(p, newGM);
|
||||
// The creative inventory is swapped by the LOW/HIGH handlers below; survival is
|
||||
// MyWorlds' job
|
||||
if (newGM.equals(GameMode.CREATIVE) && plugin.getConfig().getBoolean("creative_world.switch_to")) {
|
||||
|
||||
Location loc = plugin.getServer().getWorld(plugin.getConfig().getString("creative_world.world"))
|
||||
@@ -170,6 +171,47 @@ public class GameModeInventoriesListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
// Save the outgoing creative inventory at LOW, before any HIGH handler loads
|
||||
// the incoming one over live.
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onLeaveCreative(PlayerGameModeChangeEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (!p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (p.getGameMode().equals(GameMode.CREATIVE) && !event.getNewGameMode().equals(GameMode.CREATIVE)) {
|
||||
|
||||
plugin.getInventoryHandler().saveCreative(p, plugin.getBundles().of(p.getWorld()));
|
||||
plugin.getInventoryHandler().clearCreativePotions(p);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load the incoming creative inventory at HIGH, after every LOW handler has
|
||||
// saved the outgoing one.
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onEnterCreative(PlayerGameModeChangeEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (!p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!p.getGameMode().equals(GameMode.CREATIVE) && event.getNewGameMode().equals(GameMode.CREATIVE)) {
|
||||
|
||||
plugin.getInventoryHandler().loadCreative(p, plugin.getBundles().of(p.getWorld()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void clearMoveDestination(PlayerMoveEvent event) {
|
||||
|
||||
@@ -226,6 +268,21 @@ public class GameModeInventoriesListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
// A creative player rejoins holding the survival profile MyWorlds loaded, so
|
||||
// swap in their creative inventory
|
||||
// before anything (like a forced-survival check) runs next tick.
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onJoinLoadCreative(PlayerJoinEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (p.getGameMode().equals(GameMode.CREATIVE) && p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
plugin.getInventoryHandler().loadCreative(p, plugin.getBundles().of(p.getWorld()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
|
||||
@@ -263,8 +320,16 @@ public class GameModeInventoriesListener implements Listener {
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
|
||||
protectedFromForcedFall.remove(event.getPlayer().getUniqueId());
|
||||
pendingModeCheckLocations.remove(event.getPlayer().getUniqueId());
|
||||
Player p = event.getPlayer();
|
||||
protectedFromForcedFall.remove(p.getUniqueId());
|
||||
pendingModeCheckLocations.remove(p.getUniqueId());
|
||||
// Persist a creative inventory on quit; survival is saved by MyWorlds and the
|
||||
// server
|
||||
if (p.getGameMode().equals(GameMode.CREATIVE) && p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
plugin.getInventoryHandler().saveCreative(p, plugin.getBundles().of(p.getWorld()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2014 eccentric_nz.
|
||||
*/
|
||||
package me.eccentric_nz.gamemodeinventories;
|
||||
|
||||
import com.bergerkiller.bukkit.mw.events.PlayerProfileAppliedEvent;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
|
||||
// The one class compiled against MyWorlds, registered only when it is present. Creative follows the bundle too,
|
||||
// so a world change swaps the creative inventory just like a game mode change does.
|
||||
public class GameModeInventoriesMyWorldsListener implements Listener {
|
||||
|
||||
private final GameModeInventories plugin;
|
||||
|
||||
public GameModeInventoriesMyWorldsListener(GameModeInventories plugin) {
|
||||
|
||||
this.plugin = plugin;
|
||||
|
||||
}
|
||||
|
||||
// Before MyWorlds loads the destination's survival profile, save the creative
|
||||
// inventory of the world being left.
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onWorldChange(PlayerChangedWorldEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (p.getGameMode().equals(GameMode.CREATIVE) && p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
plugin.getInventoryHandler().saveCreative(p, plugin.getBundles().of(event.getFrom()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MyWorlds loads the destination survival profile at MONITOR and then fires
|
||||
// this; a creative player gets the
|
||||
// destination bundle's creative inventory loaded over it.
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onProfileApplied(PlayerProfileAppliedEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (p.getGameMode().equals(GameMode.CREATIVE) && p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
plugin.getInventoryHandler().loadCreative(p, plugin.getBundles().of(p.getWorld()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+47
-1
@@ -13,9 +13,12 @@ import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public class GameModeInventoriesWorldListener implements Listener {
|
||||
|
||||
private final GameModeInventories plugin;
|
||||
@@ -46,7 +49,26 @@ public class GameModeInventoriesWorldListener implements Listener {
|
||||
|
||||
World from = event.getFrom();
|
||||
World to = p.getWorld();
|
||||
if (from != to) {
|
||||
if (from == to) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (plugin.getBundles().usesMyWorlds()) {
|
||||
|
||||
// MyWorlds loads the profile at MONITOR, so the swap waits a tick
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
|
||||
if (p.isOnline()) {
|
||||
|
||||
plugin.internalGameModeChange(p, GameMode.SURVIVAL);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
plugin.internalGameModeChange(p, GameMode.SURVIVAL);
|
||||
|
||||
@@ -54,6 +76,30 @@ public class GameModeInventoriesWorldListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
// Without MyWorlds, survival must be in hand before the next world's save
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onCrossWorldTeleport(PlayerTeleportEvent event) {
|
||||
|
||||
Player p = event.getPlayer();
|
||||
if (plugin.getBundles().usesMyWorlds() || event.getTo() == null
|
||||
|| event.getFrom().getWorld().equals(event.getTo().getWorld()))
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (p.getGameMode().equals(GameMode.SURVIVAL) || !p.hasPermission("gamemodeinventories.use")) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
plugin.internalGameModeChange(p, GameMode.SURVIVAL);
|
||||
plugin.message(p, Component.text("Switched to survival before changing worlds"));
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2014 eccentric_nz.
|
||||
*/
|
||||
package me.eccentric_nz.gamemodeinventories;
|
||||
|
||||
import com.bergerkiller.bukkit.mw.WorldConfig;
|
||||
import com.bergerkiller.bukkit.mw.WorldInventory;
|
||||
import org.bukkit.World;
|
||||
|
||||
// The only class referencing MyWorlds, loaded once MyWorlds is known present
|
||||
final class MyWorldsBundles {
|
||||
|
||||
private MyWorldsBundles() {
|
||||
|
||||
}
|
||||
|
||||
static boolean available() {
|
||||
|
||||
try {
|
||||
|
||||
Class.forName("com.bergerkiller.bukkit.mw.WorldInventory");
|
||||
return true;
|
||||
|
||||
} catch (Throwable t) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Storage world of the inventory group, null when MyWorlds has none
|
||||
static String of(World world) {
|
||||
|
||||
try {
|
||||
|
||||
WorldInventory group = WorldConfig.get(world).inventory;
|
||||
return group == null ? null : group.getSharedWorldName();
|
||||
|
||||
} catch (Throwable t) {
|
||||
|
||||
GameModeInventories.plugin
|
||||
.debug("Could not resolve the MyWorlds inventory group of " + world.getName() + ", " + t);
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+33
-1
@@ -28,8 +28,40 @@ public class GameModeInventoriesMySQL {
|
||||
|
||||
// add inventories table
|
||||
String queryInventories = "CREATE TABLE IF NOT EXISTS " + plugin.getPrefix()
|
||||
+ "inventories (id int(11) NOT NULL AUTO_INCREMENT, uuid varchar(48) DEFAULT '', player varchar(24) DEFAULT '', gamemode varchar(24) DEFAULT '', inventory text, xp double, armour text, enderchest text, attributes text, armour_attributes text, PRIMARY KEY (id)) DEFAULT CHARSET=utf8 COLLATE utf8_general_ci";
|
||||
+ "inventories (id int(11) NOT NULL AUTO_INCREMENT, uuid varchar(48) DEFAULT '', player varchar(24) DEFAULT '', gamemode varchar(24) DEFAULT '', inventory text, xp double, armour text, enderchest text, attributes text, armour_attributes text, bundle varchar(64), PRIMARY KEY (id)) DEFAULT CHARSET=utf8 COLLATE utf8_general_ci";
|
||||
statement.executeUpdate(queryInventories);
|
||||
// deaths table, parked until the player is back in the death world
|
||||
String queryDeaths = "CREATE TABLE IF NOT EXISTS " + plugin.getPrefix()
|
||||
+ "deaths (id int(11) NOT NULL AUTO_INCREMENT, uuid varchar(48) DEFAULT '', player varchar(24) DEFAULT '', gamemode varchar(24) DEFAULT '', world varchar(64), inventory text, armour text, PRIMARY KEY (id)) DEFAULT CHARSET=utf8 COLLATE utf8_general_ci";
|
||||
statement.executeUpdate(queryDeaths);
|
||||
|
||||
// add the bundle column, the inventory group a slot belongs to
|
||||
String queryBundle = "SHOW COLUMNS FROM " + plugin.getPrefix() + "inventories LIKE 'bundle'";
|
||||
try (ResultSet rsBundle = statement.executeQuery(queryBundle);) {
|
||||
|
||||
if (!rsBundle.next()) {
|
||||
|
||||
statement.executeUpdate("ALTER TABLE " + plugin.getPrefix() + "inventories ADD bundle varchar(64)");
|
||||
plugin.getLogger().log(Level.INFO, "[GameModeInventories] Adding bundle to database!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// One slot per (uuid, gamemode, bundle); stops a failed legacy assignment
|
||||
// leaving conflicting rows
|
||||
String queryUnique = "SHOW INDEX FROM " + plugin.getPrefix() + "inventories WHERE Key_name = 'uq_gmi_slot'";
|
||||
try (ResultSet rsUnique = statement.executeQuery(queryUnique);) {
|
||||
|
||||
if (!rsUnique.next()) {
|
||||
|
||||
statement.executeUpdate("ALTER TABLE " + plugin.getPrefix()
|
||||
+ "inventories ADD UNIQUE KEY uq_gmi_slot (uuid, gamemode, bundle)");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update inventories if there is no attributes column
|
||||
String queryAttr = "SHOW COLUMNS FROM " + plugin.getPrefix() + "inventories LIKE 'attributes'";
|
||||
try (ResultSet rsAttr = statement.executeQuery(queryAttr);) {
|
||||
|
||||
+25
-1
@@ -27,8 +27,32 @@ public class GameModeInventoriesSQLite {
|
||||
{
|
||||
|
||||
String queryInventories = "CREATE TABLE IF NOT EXISTS " + plugin.getPrefix()
|
||||
+ "inventories (id INTEGER PRIMARY KEY NOT NULL, uuid TEXT, player TEXT, gamemode TEXT, inventory TEXT, xp REAL, armour TEXT, enderchest TEXT, attributes TEXT, armour_attributes TEXT)";
|
||||
+ "inventories (id INTEGER PRIMARY KEY NOT NULL, uuid TEXT, player TEXT, gamemode TEXT, inventory TEXT, xp REAL, armour TEXT, enderchest TEXT, attributes TEXT, armour_attributes TEXT, bundle TEXT)";
|
||||
statement.executeUpdate(queryInventories);
|
||||
// deaths table, parked until the player is back in the death world
|
||||
String queryDeaths = "CREATE TABLE IF NOT EXISTS " + plugin.getPrefix()
|
||||
+ "deaths (id INTEGER PRIMARY KEY NOT NULL, uuid TEXT, player TEXT, gamemode TEXT, world TEXT, inventory TEXT, armour TEXT)";
|
||||
statement.executeUpdate(queryDeaths);
|
||||
|
||||
// add the bundle column, the inventory group a slot belongs to
|
||||
String queryBundle = "SELECT sql FROM sqlite_master WHERE tbl_name = '" + plugin.getPrefix()
|
||||
+ "inventories' AND sql LIKE '%bundle TEXT%'";
|
||||
try (ResultSet rsBundle = statement.executeQuery(queryBundle);) {
|
||||
|
||||
if (!rsBundle.next()) {
|
||||
|
||||
statement.executeUpdate("ALTER TABLE " + plugin.getPrefix() + "inventories ADD bundle TEXT");
|
||||
plugin.getLogger().log(Level.INFO, "[GameModeInventories] Adding bundle to database!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// One slot per (uuid, gamemode, bundle); stops a failed legacy assignment
|
||||
// leaving conflicting rows
|
||||
statement.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS uq_gmi_slot ON " + plugin.getPrefix()
|
||||
+ "inventories (uuid, gamemode, bundle)");
|
||||
|
||||
// update inventories if there is no uuid column
|
||||
String queryUUID = "SELECT sql FROM sqlite_master WHERE tbl_name = '" + plugin.getPrefix()
|
||||
+ "inventories' AND sql LIKE '%uuid TEXT%'";
|
||||
|
||||
@@ -4,7 +4,7 @@ main: me.eccentric_nz.gamemodeinventories.GameModeInventories
|
||||
name: GameModeInventories-OG
|
||||
load: POSTWORLD
|
||||
depend: [ WorldGuard ]
|
||||
softdepend: [ Multiverse-Core,MultiWorld,CoreProtect ]
|
||||
softdepend: [ Multiverse-Core,MultiWorld,CoreProtect,MyWorlds ]
|
||||
version: 3.5.0
|
||||
api-version: 1.19
|
||||
libraries:
|
||||
|
||||
Reference in new issue
Block a user