mirror of
https://github.com/songoda/EpicVouchers.git
synced 2024-11-22 01:56:21 +01:00
Hotfix async vouchers.yml write task as large files cause lag [SD-8155]
I've some a TODO/FIXME explaining what to do. Wanna focus on bringing the tickets down first
This commit is contained in:
parent
063f7b0fee
commit
d7801dd64f
@ -20,6 +20,8 @@ import com.songoda.epicvouchers.libraries.inventory.IconInv;
|
||||
import com.songoda.epicvouchers.listeners.PlayerCommandListener;
|
||||
import com.songoda.epicvouchers.listeners.PlayerInteractListener;
|
||||
import com.songoda.epicvouchers.settings.Settings;
|
||||
import com.songoda.epicvouchers.utils.Callback;
|
||||
import com.songoda.epicvouchers.utils.ThreadSync;
|
||||
import com.songoda.epicvouchers.voucher.CoolDownManager;
|
||||
import com.songoda.epicvouchers.voucher.Voucher;
|
||||
import com.songoda.epicvouchers.voucher.VoucherExecutor;
|
||||
@ -101,121 +103,166 @@ public class EpicVouchers extends SongodaPlugin {
|
||||
|
||||
@Override
|
||||
public void onDataLoad() {
|
||||
if (!new File(this.getDataFolder(), "vouchers.yml").exists())
|
||||
if (!new File(this.getDataFolder(), "vouchers.yml").exists()) {
|
||||
saveResource("vouchers.yml", false);
|
||||
vouchersConfig.load();
|
||||
}
|
||||
|
||||
synchronized (vouchersConfig) {
|
||||
vouchersConfig.load();
|
||||
}
|
||||
|
||||
loadVouchersFromFile();
|
||||
|
||||
connections.openMySQL();
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, this::saveVouchers, 6000, 6000);
|
||||
// FIXME: Config system needs to be greatly redone and only write changes when changes were made - Maybe even split it into multiple smaler files
|
||||
// Issue https://support.songoda.com/browse/SD-8155 has been hotfixed by writing changes to the file async and blocking the main thread when needed. This requires the use of `synchronized`
|
||||
// and expects every modifying code to use it (thread-safety)
|
||||
// Large vouchers.yml files cause huge performance problems otherwise...
|
||||
// Example file for testing: https://support.songoda.com/secure/attachment/17258/17258_vouchers.yml
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this,
|
||||
() -> saveVouchersAsync(ex -> {
|
||||
if (ex != null) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}), 5 * 60 * 20, 5 * 60 * 20); // 5 minutes
|
||||
}
|
||||
|
||||
private void loadVouchersFromFile() {
|
||||
voucherManager.clearVouchers();
|
||||
synchronized (vouchersConfig) {
|
||||
voucherManager.clearVouchers();
|
||||
|
||||
if (vouchersConfig.contains("vouchers")) {
|
||||
for (String key : vouchersConfig.getConfigurationSection("vouchers").getKeys(false)) {
|
||||
Voucher voucher = new Voucher(key, this);
|
||||
ConfigurationSection cs = vouchersConfig.getConfigurationSection("vouchers." + key);
|
||||
if (vouchersConfig.contains("vouchers")) {
|
||||
for (String key : vouchersConfig.getConfigurationSection("vouchers").getKeys(false)) {
|
||||
Voucher voucher = new Voucher(key, this);
|
||||
ConfigurationSection cs = vouchersConfig.getConfigurationSection("vouchers." + key);
|
||||
|
||||
Material material;
|
||||
String stringMaterial = cs.getString("material");
|
||||
Material material;
|
||||
String stringMaterial = cs.getString("material");
|
||||
|
||||
if (stringMaterial == null || stringMaterial.isEmpty()) {
|
||||
material = Material.PAPER;
|
||||
} else {
|
||||
material = Material.matchMaterial(stringMaterial);
|
||||
if (material == null) material = Material.PAPER;
|
||||
if (stringMaterial == null || stringMaterial.isEmpty()) {
|
||||
material = Material.PAPER;
|
||||
} else {
|
||||
material = Material.matchMaterial(stringMaterial);
|
||||
if (material == null) material = Material.PAPER;
|
||||
}
|
||||
|
||||
voucher.setPermission(cs.getString("permission", ""))
|
||||
.setMaterial(material)
|
||||
.setData((short) cs.getInt("data", 0))
|
||||
.setName(cs.getString("name", "default"))
|
||||
.setLore(cs.getStringList("lore"))
|
||||
.setTexture(cs.getString("texture", ""))
|
||||
.setGlow(cs.getBoolean("glow", false))
|
||||
.setConfirm(cs.getBoolean("confirm", true))
|
||||
.setUnbreakable(cs.getBoolean("unbreakable", false))
|
||||
.setHideAttributes(cs.getBoolean("hide-attributes", false))
|
||||
.setRemoveItem(cs.getBoolean("remove-item", true))
|
||||
.setHealPlayer(cs.getBoolean("heal-player", false))
|
||||
.setSmiteEffect(cs.getBoolean("smite-effect", false))
|
||||
.setCoolDown(cs.getInt("coolDown", 0))
|
||||
.setBroadcasts(cs.getStringList("broadcasts"))
|
||||
.setMessages(cs.getStringList("messages"))
|
||||
.setCommands(cs.getStringList("commands"))
|
||||
.setActionBar(cs.getString("actionbar"))
|
||||
.setTitle(cs.getString("titles.title"))
|
||||
.setSubTitle(cs.getString("titles.subtitle"))
|
||||
.setTitleFadeIn(cs.getInt("titles.fade-in", 0))
|
||||
.setTitleStay(cs.getInt("titles.stay", 0))
|
||||
.setTitleFadeOut(cs.getInt("titles.fade-out", 0))
|
||||
.setSound(cs.getString("sounds.sound"))
|
||||
.setSoundPitch(cs.getInt("sounds.pitch", 0))
|
||||
.setParticle(cs.getString("particles.particle"))
|
||||
.setParticleAmount(cs.getInt("particles.amount", 0))
|
||||
.setEffect(cs.getString("effects.effect"))
|
||||
.setEffectAmplifier(cs.getInt("effects.amplifier"))
|
||||
.setItemStack(cs.getItemStack("itemstack", null));
|
||||
|
||||
voucherManager.addVoucher(voucher);
|
||||
}
|
||||
|
||||
voucher.setPermission(cs.getString("permission", ""))
|
||||
.setMaterial(material)
|
||||
.setData((short) cs.getInt("data", 0))
|
||||
.setName(cs.getString("name", "default"))
|
||||
.setLore(cs.getStringList("lore"))
|
||||
.setTexture(cs.getString("texture", ""))
|
||||
.setGlow(cs.getBoolean("glow", false))
|
||||
.setConfirm(cs.getBoolean("confirm", true))
|
||||
.setUnbreakable(cs.getBoolean("unbreakable", false))
|
||||
.setHideAttributes(cs.getBoolean("hide-attributes", false))
|
||||
.setRemoveItem(cs.getBoolean("remove-item", true))
|
||||
.setHealPlayer(cs.getBoolean("heal-player", false))
|
||||
.setSmiteEffect(cs.getBoolean("smite-effect", false))
|
||||
.setCoolDown(cs.getInt("coolDown", 0))
|
||||
.setBroadcasts(cs.getStringList("broadcasts"))
|
||||
.setMessages(cs.getStringList("messages"))
|
||||
.setCommands(cs.getStringList("commands"))
|
||||
.setActionBar(cs.getString("actionbar"))
|
||||
.setTitle(cs.getString("titles.title"))
|
||||
.setSubTitle(cs.getString("titles.subtitle"))
|
||||
.setTitleFadeIn(cs.getInt("titles.fade-in", 0))
|
||||
.setTitleStay(cs.getInt("titles.stay", 0))
|
||||
.setTitleFadeOut(cs.getInt("titles.fade-out", 0))
|
||||
.setSound(cs.getString("sounds.sound"))
|
||||
.setSoundPitch(cs.getInt("sounds.pitch", 0))
|
||||
.setParticle(cs.getString("particles.particle"))
|
||||
.setParticleAmount(cs.getInt("particles.amount", 0))
|
||||
.setEffect(cs.getString("effects.effect"))
|
||||
.setEffectAmplifier(cs.getInt("effects.amplifier"))
|
||||
.setItemStack(cs.getItemStack("itemstack", null));
|
||||
|
||||
voucherManager.addVoucher(voucher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveVouchers() {
|
||||
Collection<Voucher> voucherList = voucherManager.getVouchers();
|
||||
ThreadSync tSync = new ThreadSync();
|
||||
|
||||
for (String voucherName : vouchersConfig.getConfigurationSection("vouchers").getKeys(false)) {
|
||||
if (voucherList.stream().noneMatch(voucher -> voucher.getKey().equals(voucherName))) {
|
||||
vouchersConfig.set("vouchers." + voucherName, null);
|
||||
saveVouchersAsync(ex -> {
|
||||
if (ex != null) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
for (Voucher voucher : voucherList) {
|
||||
String prefix = "vouchers." + voucher.getKey() + ".";
|
||||
tSync.release();
|
||||
});
|
||||
|
||||
vouchersConfig.set(prefix + "permission", voucher.getPermission());
|
||||
vouchersConfig.set(prefix + "material", voucher.getMaterial().name());
|
||||
vouchersConfig.set(prefix + "data", voucher.getData());
|
||||
vouchersConfig.set(prefix + "name", voucher.getName());
|
||||
vouchersConfig.set(prefix + "lore", voucher.getLore());
|
||||
vouchersConfig.set(prefix + "texture", voucher.getTexture());
|
||||
vouchersConfig.set(prefix + "glow", voucher.isGlow());
|
||||
vouchersConfig.set(prefix + "confirm", voucher.isConfirm());
|
||||
vouchersConfig.set(prefix + "unbreakable", voucher.isUnbreakable());
|
||||
vouchersConfig.set(prefix + "hide-attributes", voucher.isHideAttributes());
|
||||
vouchersConfig.set(prefix + "remove-item", voucher.isRemoveItem());
|
||||
vouchersConfig.set(prefix + "heal-player", voucher.isHealPlayer());
|
||||
vouchersConfig.set(prefix + "smite-effect", voucher.isSmiteEffect());
|
||||
vouchersConfig.set(prefix + "coolDown", voucher.getCoolDown());
|
||||
vouchersConfig.set(prefix + "broadcasts", voucher.getBroadcasts());
|
||||
vouchersConfig.set(prefix + "messages", voucher.getMessages());
|
||||
vouchersConfig.set(prefix + "commands", voucher.getCommands());
|
||||
vouchersConfig.set(prefix + "actionbar", voucher.getActionBar());
|
||||
vouchersConfig.set(prefix + "titles.title", voucher.getTitle());
|
||||
vouchersConfig.set(prefix + "titles.subtitle", voucher.getSubTitle());
|
||||
vouchersConfig.set(prefix + "titles.fade-in", voucher.getTitleFadeIn());
|
||||
vouchersConfig.set(prefix + "titles.stay", voucher.getTitleStay());
|
||||
vouchersConfig.set(prefix + "titles.fade-out", voucher.getTitleFadeOut());
|
||||
vouchersConfig.set(prefix + "sounds.sound", voucher.getSound());
|
||||
vouchersConfig.set(prefix + "sounds.pitch", voucher.getSoundPitch());
|
||||
vouchersConfig.set(prefix + "particles.particle", voucher.getParticle());
|
||||
vouchersConfig.set(prefix + "particles.amount", voucher.getParticleAmount());
|
||||
vouchersConfig.set(prefix + "effects.effect", voucher.getEffect());
|
||||
vouchersConfig.set(prefix + "effects.amplifier", voucher.getEffectAmplifier());
|
||||
vouchersConfig.set(prefix + "itemstack", voucher.getItemStack());
|
||||
}
|
||||
tSync.waitForRelease();
|
||||
}
|
||||
|
||||
vouchersConfig.saveChanges();
|
||||
private void saveVouchersAsync(Callback callback) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
synchronized (vouchersConfig) {
|
||||
Collection<Voucher> voucherList = voucherManager.getVouchers();
|
||||
|
||||
ConfigurationSection cfgSec = vouchersConfig.getConfigurationSection("vouchers");
|
||||
if (cfgSec != null) {
|
||||
for (String voucherName : cfgSec.getKeys(false)) {
|
||||
if (voucherList.stream().noneMatch(voucher -> voucher.getKey().equals(voucherName))) {
|
||||
vouchersConfig.set("vouchers." + voucherName, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Voucher voucher : voucherList) {
|
||||
String prefix = "vouchers." + voucher.getKey() + ".";
|
||||
|
||||
vouchersConfig.set(prefix + "permission", voucher.getPermission());
|
||||
vouchersConfig.set(prefix + "material", voucher.getMaterial().name());
|
||||
vouchersConfig.set(prefix + "data", voucher.getData());
|
||||
vouchersConfig.set(prefix + "name", voucher.getName());
|
||||
vouchersConfig.set(prefix + "lore", voucher.getLore());
|
||||
vouchersConfig.set(prefix + "texture", voucher.getTexture());
|
||||
vouchersConfig.set(prefix + "glow", voucher.isGlow());
|
||||
vouchersConfig.set(prefix + "confirm", voucher.isConfirm());
|
||||
vouchersConfig.set(prefix + "unbreakable", voucher.isUnbreakable());
|
||||
vouchersConfig.set(prefix + "hide-attributes", voucher.isHideAttributes());
|
||||
vouchersConfig.set(prefix + "remove-item", voucher.isRemoveItem());
|
||||
vouchersConfig.set(prefix + "heal-player", voucher.isHealPlayer());
|
||||
vouchersConfig.set(prefix + "smite-effect", voucher.isSmiteEffect());
|
||||
vouchersConfig.set(prefix + "coolDown", voucher.getCoolDown());
|
||||
vouchersConfig.set(prefix + "broadcasts", voucher.getBroadcasts());
|
||||
vouchersConfig.set(prefix + "messages", voucher.getMessages());
|
||||
vouchersConfig.set(prefix + "commands", voucher.getCommands());
|
||||
vouchersConfig.set(prefix + "actionbar", voucher.getActionBar());
|
||||
vouchersConfig.set(prefix + "titles.title", voucher.getTitle());
|
||||
vouchersConfig.set(prefix + "titles.subtitle", voucher.getSubTitle());
|
||||
vouchersConfig.set(prefix + "titles.fade-in", voucher.getTitleFadeIn());
|
||||
vouchersConfig.set(prefix + "titles.stay", voucher.getTitleStay());
|
||||
vouchersConfig.set(prefix + "titles.fade-out", voucher.getTitleFadeOut());
|
||||
vouchersConfig.set(prefix + "sounds.sound", voucher.getSound());
|
||||
vouchersConfig.set(prefix + "sounds.pitch", voucher.getSoundPitch());
|
||||
vouchersConfig.set(prefix + "particles.particle", voucher.getParticle());
|
||||
vouchersConfig.set(prefix + "particles.amount", voucher.getParticleAmount());
|
||||
vouchersConfig.set(prefix + "effects.effect", voucher.getEffect());
|
||||
vouchersConfig.set(prefix + "effects.amplifier", voucher.getEffectAmplifier());
|
||||
vouchersConfig.set(prefix + "itemstack", voucher.getItemStack());
|
||||
}
|
||||
|
||||
vouchersConfig.saveChanges();
|
||||
|
||||
callback.accept(null);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
callback.accept(ex);
|
||||
}
|
||||
}, getName() + "-AsyncConfigSave").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigReload() {
|
||||
vouchersConfig.load();
|
||||
synchronized (vouchersConfig) {
|
||||
vouchersConfig.load();
|
||||
}
|
||||
|
||||
loadVouchersFromFile();
|
||||
|
||||
@ -240,10 +287,6 @@ public class EpicVouchers extends SongodaPlugin {
|
||||
return this.voucherExecutor;
|
||||
}
|
||||
|
||||
public Config getVouchersConfig() {
|
||||
return this.vouchersConfig;
|
||||
}
|
||||
|
||||
public CommandManager getCommandManager() {
|
||||
return commandManager;
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
package com.songoda.epicvouchers.utils;
|
||||
|
||||
// TODO: Copied from EpicAnchors - Move to SongodaCore (maybe rename too?)
|
||||
public interface Callback {
|
||||
void accept(Exception ex);
|
||||
}
|
31
src/main/java/com/songoda/epicvouchers/utils/ThreadSync.java
Normal file
31
src/main/java/com/songoda/epicvouchers/utils/ThreadSync.java
Normal file
@ -0,0 +1,31 @@
|
||||
package com.songoda.epicvouchers.utils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
// TODO: Copied from EpicAnchors - Move to SongodaCore
|
||||
public class ThreadSync {
|
||||
private final Object syncObj = new Object();
|
||||
private final AtomicReference<Boolean> waiting = new AtomicReference<>(true);
|
||||
|
||||
public void waitForRelease() {
|
||||
synchronized (syncObj) {
|
||||
while (waiting.get()) {
|
||||
try {
|
||||
syncObj.wait();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
synchronized (syncObj) {
|
||||
waiting.set(false);
|
||||
syncObj.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
waiting.set(true);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user