Fixes some errors with HeadGetter (#1569)

* Add getKey and getValue for Pair calls for nicer access.

* Fixes ConcurrentModificationException in HeadGetter.
Fixes an issue when elements with the same name were overwritten by HeadGetter.
This commit is contained in:
BONNe 2020-11-08 21:23:24 +02:00 committed by GitHub
parent 1d4fd435a9
commit 3581537537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 35 deletions

View File

@ -2,17 +2,13 @@ package world.bentobox.bentobox.api.panels;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.OptionalInt;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
@ -190,18 +186,15 @@ public class Panel implements HeadRequester, InventoryHolder {
@Override @Override
public void setHead(PanelItem item) { public void setHead(PanelItem item) {
// Update the panel item // Update the panel item
// Replace the item in the item list if the name is the same // Find panel item index in items and replace it once more in inventory to update it.
items = items.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (item.getName().equals(e.getValue().getName()) ? item : e.getValue()))); OptionalInt index = this.items.entrySet().stream().
// Replace the inventory slot item filter(entry -> entry.getValue() == item).
for (int i = 0; i < inventory.getSize(); i++) { mapToInt(Map.Entry::getKey).findFirst();
ItemStack it = inventory.getItem(i);
if (it != null && it.getType().equals(Material.PLAYER_HEAD)) { if (index.isPresent()) {
ItemMeta meta = it.getItemMeta(); // Update item inside inventory to change icon only if item is inside panel.
if (meta != null && ChatColor.stripColor(item.getName()).equals(ChatColor.stripColor(meta.getLocalizedName()))) { this.inventory.setItem(index.getAsInt(), item.getItem());
inventory.setItem(i, item.getItem());
}
}
} }
} }

View File

@ -17,6 +17,25 @@ public class Pair<X, Z> {
this.z = z; this.z = z;
} }
/**
* Returns X element as key.
* @return X element
*/
public X getKey() {
return x;
}
/**
* Returns Z element as value.
* @return Z element
*/
public Z getValue() {
return z;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Object#toString() * @see java.lang.Object#toString()
*/ */

View File

@ -9,11 +9,11 @@ import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -21,6 +21,7 @@ import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.util.Pair;
/** /**
@ -36,7 +37,7 @@ public class HeadGetter {
/** /**
* Local cache for storing requested names and items which must be updated. * Local cache for storing requested names and items which must be updated.
*/ */
private static final Map<String, PanelItem> names = new HashMap<>(); private static final Queue<Pair<String, PanelItem>> names = new LinkedBlockingQueue<>();
/** /**
* Requesters of player heads. * Requesters of player heads.
@ -89,7 +90,7 @@ public class HeadGetter {
// Get the name // Get the name
headRequesters.computeIfAbsent(panelItem.getPlayerHeadName(), k -> new HashSet<>()). headRequesters.computeIfAbsent(panelItem.getPlayerHeadName(), k -> new HashSet<>()).
add(requester); add(requester);
names.put(panelItem.getPlayerHeadName(), panelItem); names.add(new Pair<>(panelItem.getPlayerHeadName(), panelItem));
} }
} }
@ -117,21 +118,19 @@ public class HeadGetter {
*/ */
private void runPlayerHeadGetter() { private void runPlayerHeadGetter() {
Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> { Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> {
synchronized (names) synchronized (HeadGetter.names)
{ {
Iterator<Entry<String, PanelItem>> it = names.entrySet().iterator(); if (!HeadGetter.names.isEmpty())
if (it.hasNext())
{ {
Entry<String, PanelItem> elementEntry = it.next(); Pair<String, PanelItem> elementEntry = HeadGetter.names.poll();
// TODO: In theory BentoBox could use User instance to find existing user UUID's. // TODO: In theory BentoBox could use User instance to find existing user UUID's.
// It would avoid one API call. // It would avoid one API call.
final String userName = elementEntry.getKey(); final String userName = elementEntry.getKey();
// Use cached userId as userId will not change :) // Use cached userId as userId will not change :)
UUID userId = cachedHeads.containsKey(userName) ? UUID userId = HeadGetter.cachedHeads.containsKey(userName) ?
cachedHeads.get(userName).getUserId() : HeadGetter.cachedHeads.get(userName).getUserId() :
HeadGetter.getUserIdFromName(userName); HeadGetter.getUserIdFromName(userName);
// Create new cache object. // Create new cache object.
@ -140,24 +139,22 @@ public class HeadGetter {
HeadGetter.getTextureFromUUID(userId)); HeadGetter.getTextureFromUUID(userId));
// Save in cache // Save in cache
cachedHeads.put(userName, cache); HeadGetter.cachedHeads.put(userName, cache);
// Tell requesters the head came in // Tell requesters the head came in
if (headRequesters.containsKey(userName)) if (HeadGetter.headRequesters.containsKey(userName))
{ {
for (HeadRequester req : headRequesters.get(userName)) for (HeadRequester req : HeadGetter.headRequesters.get(userName))
{ {
elementEntry.getValue().setHead(cache.getPlayerHead()); elementEntry.getValue().setHead(cache.getPlayerHead());
Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, Bukkit.getServer().getScheduler().runTaskAsynchronously(this.plugin,
() -> req.setHead(elementEntry.getValue())); () -> req.setHead(elementEntry.getValue()));
} }
} }
it.remove();
} }
} }
}, 0L, 20L); }, 0L, 10L);
} }