Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@aikar
Created March 20, 2019 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aikar/26ddaa441afaac4792186b6b2a0ea550 to your computer and use it in GitHub Desktop.
Save aikar/26ddaa441afaac4792186b6b2a0ea550 to your computer and use it in GitHub Desktop.
/*
* 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);
}
}
}
/*
* 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() : ""));
}
}
/*
* 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