Created
March 20, 2019 05:07
-
-
Save aikar/26ddaa441afaac4792186b6b2a0ea550 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2016. Starlis LLC / dba Empire Minecraft | |
* | |
* This source code is proprietary software and must not be redistributed without Starlis LLC's approval | |
* | |
*/ | |
package com.empireminecraft.systems.packages; | |
import co.aikar.idb.DbRow; | |
import co.aikar.idb.DbStatement; | |
import com.empireminecraft.config.EmpireServer; | |
import com.empireminecraft.systems.inventoryui.InventoryUI; | |
import com.empireminecraft.systems.inventoryui.interfaces.PackageInterface; | |
import com.empireminecraft.util.Log; | |
import com.empireminecraft.util.Util; | |
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; | |
import org.bukkit.entity.Player; | |
import org.bukkit.inventory.Inventory; | |
import org.jetbrains.annotations.NotNull; | |
import org.jetbrains.annotations.Nullable; | |
import java.sql.SQLException; | |
import java.util.Map; | |
import java.util.stream.Collectors; | |
import static com.empireminecraft.util.Util.sendMsg; | |
public class OpenedPackage { | |
// Package Id | |
public static final Map<Long, OpenedPackage> packages = new Long2ObjectOpenHashMap(); | |
private final Package pkg; | |
private PackageInterface openedInterface; | |
private volatile boolean opened = true; | |
private OpenedPackage(@NotNull Package pkg) { | |
this.pkg = pkg; | |
} | |
//<editor-fold desc="// Getters/Forwarders"> | |
public Long getPackageId() { | |
return pkg.getPackageId(); | |
} | |
public Long getOwnerId() { | |
return pkg.getOwnerId(); | |
} | |
public Inventory getInventory() { | |
return pkg.getInventory(); | |
} | |
public synchronized void save() { | |
pkg.save(); | |
if (!opened) { | |
Log.exception("Saved a closed package " + getPackageId()); | |
} | |
} | |
public void delete(Player player) { | |
pkg.delete(player); | |
} | |
public void delete() { | |
pkg.delete(); | |
} | |
public Package getPackage() { | |
return pkg; | |
} | |
// </editor-fold> | |
public synchronized void close() { | |
final Long packageId = pkg.getPackageId(); | |
synchronized (packages) { | |
if (!opened) { | |
Package.logException(packageId, "Unexpected Close"); | |
} else { | |
Package.logInfo(packageId, "Closing"); | |
} | |
packages.remove(packageId); | |
opened = false; | |
pkg.unlockPackage(); | |
} | |
} | |
private void forceClose() { | |
if (openedInterface != null) { | |
openedInterface.forceClose(); | |
} | |
close(); | |
} | |
private synchronized void tick() { | |
if (openedInterface != null && opened && openedInterface.isInitialized()) { | |
final String players = openedInterface.getPlayers().stream().map(Player::getName).collect(Collectors.joining(", ")); | |
Package.logInfo(pkg.getPackageId(), null, "Tick (Viewing: " + players + ")"); | |
if (!openedInterface.tryClose()) { | |
openedInterface.save(); | |
} | |
} | |
} | |
/** | |
* Determines if the player has access to this package. | |
* | |
* @param player | |
* @return {@code true} if the player has access to this package | |
*/ | |
private boolean hasAccess(Player player) { | |
if (!player.hasPermission("empire.packageadmin")) { | |
if (player.getUserId() != pkg.getOwnerId()) { | |
sendMsg(player, "&cYou do not have permission to open this package."); | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Opens this package for the player if they have access. | |
* | |
* @param player | |
* @return {@code true} if the package is opened | |
*/ | |
public synchronized void open(Player player) { | |
final Long packageId = pkg.getPackageId(); | |
if (!opened) { | |
Util.sendMsg(player, "&cThis package is already closed."); | |
Package.logError(packageId, player, "tried to open closed package"); | |
return; | |
} | |
if (!hasAccess(player)) { | |
Package.logError(packageId, player, "did not have access to package"); | |
return; | |
} | |
if (openedInterface == null) { | |
openedInterface = new PackageInterface(); | |
} | |
openedInterface.setPkg(this); | |
Package.logInfo(packageId, player, "Opened Interface"); | |
InventoryUI.open(player, openedInterface); | |
} | |
public synchronized void open(Player player, @NotNull PackageInterface pkgInterface) { | |
if (this.openedInterface == null) { | |
// Not safe to throw away an existing interface thats open. | |
this.openedInterface = pkgInterface; | |
} | |
open(player); | |
} | |
static OpenedPackage openPackage(@Nullable Player player, @NotNull Package pkg) { | |
final Long packageId = pkg.getPackageId(); | |
try (DbStatement stm = new DbStatement()) { | |
synchronized (packages) { | |
OpenedPackage isOpen = packages.get(packageId); | |
if (isOpen != null) { | |
Package.logInfo(packageId, player, "Opened Cached"); | |
return isOpen; | |
} | |
stm.startTransaction(); | |
stm.query("SELECT open_on FROM packages WHERE id = ? FOR UPDATE"); | |
stm.execute(packageId); | |
DbRow rs = stm.getNextRow(); | |
if (rs == null) { | |
Log.exception("Why the hell did this happen? rs was null for tryOpen"); | |
stm.rollback(); | |
return null; | |
} | |
Integer openOn = rs.get("open_on"); | |
Integer serverId = EmpireServer.getServerId(); | |
if (openOn != null && openOn != 0 && !openOn.equals(serverId)) { | |
final String serverName = EmpireServer.getServerName(openOn); | |
if (player != null) { | |
if (!serverId.equals(openOn)) { | |
Util.sendMsg(player, "&cThis package is open on another server: &f" + serverName); | |
} else { | |
Util.sendMsg(player, "&cThis package is pending close, please try again."); | |
} | |
} | |
Package.logError(packageId, player, "Already open on " + serverName); | |
stm.rollback(); | |
return null; | |
} else { | |
if (openOn != null && openOn.equals(serverId)) { | |
Package.logException(packageId, "Unexpected lock issue. Locked on current server"); | |
if (player != null) { | |
Util.sendMsg(player, "&cSomething totally unexpected just happened. Hopefully things are ok, but please report anything odd to the Dev team ASAP!"); | |
} | |
} | |
stm.query("UPDATE packages SET open_on = ? WHERE id = ?"); | |
stm.executeUpdate(serverId, packageId); | |
stm.commit(); | |
final OpenedPackage openedPackage = new OpenedPackage(pkg); | |
Package.logInfo(packageId, player, "Opened"); | |
packages.put(packageId, openedPackage); | |
return openedPackage; | |
} | |
} | |
} catch (SQLException e) { | |
Log.exception("Exception in getPackage: " + packageId, e); | |
} | |
return null; | |
} | |
static Package getPackageIfOpened(Long packageId) { | |
synchronized (packages) { | |
OpenedPackage openPkg = packages.get(packageId); | |
if (openPkg != null) { | |
return openPkg.getPackage(); | |
} | |
} | |
return null; | |
} | |
public static void tickOpenPackages() { | |
synchronized (packages) { | |
packages.values().forEach(OpenedPackage::tick); | |
} | |
} | |
public static void closeAllPackages() { | |
synchronized (packages) { | |
packages.values().forEach(OpenedPackage::forceClose); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2016. Starlis LLC / dba Empire Minecraft | |
* | |
* This source code is proprietary software and must not be redistributed without Starlis LLC's approval | |
* | |
*/ | |
package com.empireminecraft.systems.packages; | |
import co.aikar.idb.DB; | |
import co.aikar.idb.DbRow; | |
import co.aikar.taskchain.TaskChain; | |
import com.empireminecraft.EmpirePlugin; | |
import com.empireminecraft.user.EmpireUser; | |
import com.empireminecraft.util.Compression; | |
import com.empireminecraft.util.Log; | |
import com.empireminecraft.util.serialization.Serialization; | |
import org.bukkit.entity.Player; | |
import org.bukkit.inventory.Inventory; | |
import org.bukkit.inventory.ItemStack; | |
import org.jetbrains.annotations.NotNull; | |
import org.jetbrains.annotations.Nullable; | |
import java.sql.SQLException; | |
import java.util.Arrays; | |
import java.util.function.Consumer; | |
public class Package { | |
private final Long packageId; | |
// Not Final! Saving now rewrites this value. | |
private String serialized; | |
private final String type; | |
private final Long ownerId; | |
private final Inventory inventory; | |
private final String chainName; | |
Package(@NotNull Long packageId) throws Exception { | |
DbRow rs = DB.getFirstRow("SELECT id, name, user_id, type, data, last_accessed, open_on FROM packages WHERE id = ?", packageId); | |
if (rs == null) { | |
throw new NullPointerException("Could not find package " + packageId); | |
} | |
this.packageId = packageId; | |
this.chainName = getChainName(packageId); | |
this.type = rs.get("type"); | |
this.ownerId = rs.get("user_id"); | |
String json = Compression.decompress(rs.get("data")); | |
if (json != null) { | |
Inventory inventory = Serialization.deserializeInventory(json, rs.get("name")); | |
if (inventory != null) { | |
this.serialized = json; | |
this.inventory = inventory; | |
return; | |
} else { | |
logError(packageId, null, "Inventory was null"); | |
} | |
} else { | |
logError(packageId, null, "Decompressed data was null"); | |
} | |
this.serialized = null; | |
this.inventory = null; | |
} | |
/** | |
* Saves the package back to the database. | |
*/ | |
public void save() { | |
EmpirePlugin.newSharedChain(chainName) | |
.syncFirst( () -> { | |
TaskChain<?> chain = TaskChain.getCurrentChain(); | |
chain.setTaskData("title", inventory.getTitle()); | |
chain.setTaskData("size", inventory.getSize()); | |
final ItemStack[] contents = inventory.getStorageContents(); | |
for (int i = 0, storageContentLength = contents.length; i < storageContentLength; i++) { | |
if (contents[i] != null) { | |
contents[i] = contents[i].clone(); | |
} | |
} | |
return contents; | |
}) | |
.asyncLast( (contents) -> { | |
TaskChain<?> chain = TaskChain.getCurrentChain(); | |
String title = chain.getTaskData("title"); | |
Integer size = chain.getTaskData("size"); | |
String json = Serialization.serializeInventory(title, size, contents); | |
if (json == null) { | |
logError(packageId, "Package failed to serialize..." + Arrays.toString(contents)); | |
return; | |
} | |
try { | |
if (json.equals(serialized)) { | |
logInfo(packageId, null, "Tried to save but unchanged"); | |
return; | |
} | |
logInfo(packageId, null, "Saving"); | |
json = Compression.compress(json); | |
if (json != null) { | |
DB.executeUpdate("UPDATE packages SET data = ?, last_accessed = UNIX_TIMESTAMP() WHERE id = ?", json, packageId); | |
serialized = json; | |
} else { | |
logError(packageId, "Package failed to compress..."); | |
} | |
} catch (Exception ex) { | |
Log.exception("Exception in package save" + packageId, ex); | |
} | |
}).execute(); | |
} | |
public void delete(Player player) { | |
logInfo(packageId, player, "Deleted"); | |
EmpireUser.getUser(player).executeDbUpdateAsync("DELETE FROM packages WHERE id = ?", packageId); | |
inventory.setStorageContents(new ItemStack[]{}); | |
} | |
public void delete() { | |
logInfo(packageId, null, "Deleted"); | |
DB.executeUpdateAsync("DELETE FROM packages WHERE id = ?", packageId); | |
inventory.setStorageContents(new ItemStack[]{}); | |
} | |
public void open(Player player) { | |
open(player, (pkg) -> { | |
if (pkg != null) { | |
pkg.open(player); | |
} | |
}); | |
} | |
public synchronized void open(Player player, @NotNull Consumer<OpenedPackage> cb) { | |
EmpirePlugin.newSharedChain(chainName).async( () -> cb.accept(OpenedPackage.openPackage(player, this))).execute(); | |
} | |
void unlockPackage() { | |
EmpirePlugin.newSharedChain(chainName).async( () -> { | |
try { | |
logInfo(packageId, "Unlocking"); | |
DB.executeUpdate("UPDATE packages SET open_on = 0 WHERE id = ?", packageId); | |
} catch (SQLException e) { | |
Log.exception(e); | |
} | |
}).execute(); | |
} | |
public Long getPackageId() { | |
return this.packageId; | |
} | |
public Long getOwnerId() { | |
return this.ownerId; | |
} | |
public Inventory getInventory() { | |
return this.inventory; | |
} | |
public static String getChainName(long packageId) { | |
return "PACKAGE_" + packageId; | |
} | |
public static void logInfo(@NotNull Long packageId, @NotNull String msg) { | |
logInfo(packageId, null, msg); | |
} | |
public static void logInfo(@NotNull Long packageId, @Nullable Player player, @NotNull String msg) { | |
Log.info("[PACKAGE:" + packageId + "] " + msg + (player != null ? " triggered by " + player.getName() : "")); | |
} | |
public static void logError(@NotNull Long packageId, @NotNull String msg) { | |
logError(packageId, null, msg); | |
} | |
public static void logError(@NotNull Long packageId, @Nullable Player player, @NotNull String msg) { | |
Log.severe("[PACKAGE:" + packageId + "] " + msg + (player != null ? " triggered by " + player.getName() : "")); | |
} | |
public static void logException(@NotNull Long packageId, @NotNull String msg) { | |
logException(packageId, null, msg); | |
} | |
public static void logException(@NotNull Long packageId, @Nullable Player player, @NotNull String msg) { | |
Log.exception("[PACKAGE:" + packageId + "] " + msg + (player != null ? " triggered by " + player.getName() : "")); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2016. Starlis LLC / dba Empire Minecraft | |
* | |
* This source code is proprietary software and must not be redistributed without Starlis LLC's approval | |
* | |
*/ | |
package com.empireminecraft.systems.packages; | |
import co.aikar.idb.DB; | |
import co.aikar.idb.DbStatement; | |
import co.aikar.taskchain.TaskChainTasks.FirstTask; | |
import co.aikar.taskchain.TaskChainTasks.Task; | |
import com.empireminecraft.EmpirePlugin; | |
import com.empireminecraft.systems.inventoryui.InventoryUI; | |
import com.empireminecraft.systems.inventoryui.InventoryUserInterface; | |
import com.empireminecraft.systems.inventoryui.interfaces.PackageInterface; | |
import com.empireminecraft.systems.inventoryui.interfaces.PreviewChest; | |
import com.empireminecraft.util.Compression; | |
import com.empireminecraft.util.Log; | |
import com.empireminecraft.util.serialization.Serialization; | |
import org.bukkit.Bukkit; | |
import org.bukkit.entity.Player; | |
import org.bukkit.inventory.Inventory; | |
import org.jetbrains.annotations.NotNull; | |
import static co.aikar.taskchain.BukkitTaskChainFactory.COLOR_MESSAGE; | |
public final class PackageManager { | |
private PackageManager() {} | |
public static void openPackage(@NotNull Long packageId, @NotNull Player player) { | |
EmpirePlugin.newSharedChain(Package.getChainName(packageId)).asyncFirst(() -> getPackage(packageId)) | |
.abortIfNull(COLOR_MESSAGE, player, "&cCould not open package") | |
.async((pkg) -> OpenedPackage.openPackage(player, pkg)) | |
.abortIfNull() | |
.syncLast( (openedPackage) -> openedPackage.open(player)).execute(); | |
} | |
public static void openPackage(@NotNull Long packageId, @NotNull Player player, @NotNull PackageInterface packageInterface) { | |
EmpirePlugin.newSharedChain(Package.getChainName(packageId)).asyncFirst(() -> getPackage(packageId)) | |
.abortIfNull(COLOR_MESSAGE, player, "&cCould not open package") | |
.async((pkg) -> OpenedPackage.openPackage(player, pkg)) | |
.abortIfNull() | |
.syncLast( (openPackage) -> openPackage.open(player, packageInterface)).execute(); | |
} | |
public static boolean previewPackage(Long packageId, Player player, String description) { | |
Package pkg = getPackage(packageId); | |
if (pkg == null) { | |
return false; | |
} | |
InventoryUI.open(player, new PreviewChest(pkg.getInventory(), description)); | |
return true; | |
} | |
public static FirstTask<Package> getPackageTask(Long packageId) { | |
return () -> getPackage(packageId); | |
} | |
public static Task<Package, Long> getPackageTask() { | |
return PackageManager::getPackage; | |
} | |
public static Package getPackage(@NotNull Long packageId) { | |
Package openedPackage = OpenedPackage.getPackageIfOpened(packageId); | |
if (openedPackage != null) { | |
return openedPackage; | |
} | |
try { | |
return new Package(packageId); | |
} catch (Exception e) { | |
Log.exception("Exception in getPackage: " + packageId, e); | |
} | |
return null; | |
} | |
public static void setPackageName(Long packageId, String name) { | |
DB.executeUpdateAsync("UPDATE packages SET name = ? WHERE id = ?", name, packageId); | |
} | |
public static boolean hasPackageOpen(Player player) { | |
InventoryUserInterface iui = InventoryUI.getInterface(player); | |
return iui != null && iui instanceof PackageInterface; | |
} | |
public static FirstTask<Long> newPackageIdTask(Long userId, String title, String type, int size) { | |
return () -> newPackageId(userId, title, type, size); | |
} | |
public static Long newPackageId(Long userId, String title, String type, int size) { | |
Long packageId = null; | |
try (DbStatement statement = new DbStatement()) { | |
Inventory inv = Bukkit.createInventory(null, size, title); | |
String json = Compression.compress(Serialization.serializeInventory(inv)); | |
statement.query("INSERT INTO packages (name, user_id, type, data) VALUES (?, ?, ?, ?)"); | |
statement.executeUpdate(title, userId, type, json); | |
packageId = statement.getLastInsertId(); | |
} catch (Exception e) { | |
Log.exception("Exception in newPackage:" + userId + ":" + type, e); | |
} | |
return packageId; | |
} | |
public static Package newPackage(Long userId, String title, String type, int size) { | |
final Long pkgId = newPackageId(userId, title, type, size); | |
return getPackage(pkgId); | |
} | |
public static FirstTask<Package> newPackageTask(Long userId, String title, String type, int size) { | |
return () -> newPackage(userId, title, type, size); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment