Frage / Hilfe Arrayliste auf Objekt prüfen - Performance - Leistung verbessern

GasGas

Mitglied
Nov 2, 2017
10

Heyho liebe Nexuscube Community,


ich bräuchte mal Hilfe für Frage in Sachen Performance:

Ich schrieb grade ein Challange-Plugin, wo die Spieler alle Items des Games sammeln sollten. Nach gewissen Items bekommt man Belohnungen etc...
Und da ich mich in Sachen Performance nicht gut auskenne wollte ich euch mal fragen, ob ihr eine Lösung wisst.

Zum Problem:


Ich Speicher Jedes Item (über 800) in einem Einzelnen Objekt.

Java:
package at.collect.itemplug.Object;

import at.collect.itemplug.Enum.ItemType;
import at.collect.itemplug.Enum.Level;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.inventory.ItemStack;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class FItem implements ConfigurationSerializable {

    private final Material mat;
    private final ItemStack itemStack;

    private final ItemType type;

    /**
     * DO NOT USE
     * @param mat Material
     * @param itemStack ItemStack
     */
    public FItem(Material mat, ItemStack itemStack) {
        this.mat = mat;
        this.itemStack = itemStack;
        if (mat != null) this.type = ItemType.Material;
                else this.type = ItemType.ItemStack;
    }

    /**
     * Init Mat
     * @param mat Material
     */
    public FItem(Material mat) {
        this(mat, null);
    }

    /**
     * init itemStack
     * @param itemStack ItemStack
     */
    public FItem(ItemStack itemStack) {
        this(null, itemStack);
    }

    /**
     * Retrun the Material only if the type is ItemType.Material
     * @return Material
     */
    public Material getMaterial() {
        return mat;
    }

    /**
     * Retrun the Material only if the type is ItemType.ItemStack
     * @return ItemStack
     */
    public ItemStack getItemStack() {
        return itemStack;
    }

    /**
     * @return returns the itemtype
     */
    public ItemType getType() {
        return type;
    }

    /**
     * Get defenetly an itemStack
     * @return ItemStack
     */
    public ItemStack getcompleadItemStack(){
        ItemStack item = null;

        if (mat != null){
            item = new ItemStack(mat);
        }else{
            item = itemStack;
        }
        return item;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof FItem)) return false;
        if ((((FItem) obj).getType().equals(ItemType.Material))){
            if (((FItem) obj).getMaterial().equals(this.getMaterial())) return true;
        }else{
            ItemStack itm = ((FItem) obj).getItemStack();
            if (itm.isSimilar(this.getItemStack())) return true;
        }
        return false;
    }

    /**
     * Checks if the ItemStack is specialy ifnot will create a Material based FItem
     * @param item Item to Check
     * @return new FItem
     */
    public static FItem getFitem(ItemStack item){
        ArrayList<ItemStack> itemStacks = Level.getAllItemStacks();

        for (ItemStack itemtocompare : itemStacks){
            if (item.equals(itemtocompare)){
                return new FItem(item);
            }
        }
        return new FItem(item.getType());
    }


    // serialize

    public FItem(Map<String, Object> map) {
        if (map.containsKey("mat")){
            this.mat = (Material) map.get("mat");
            this.itemStack = null;
        }else {
            this.itemStack = (ItemStack) map.get("item");
            this.mat = null;
        }

        this.type = (ItemType) map.get("type");
    }

    @Override
    public Map<String, Object> serialize() {
        Map<String, Object> map = new HashMap<>();

        if (this.mat == null){
            map.put("item", this.itemStack);
        }else map.put("mat", this.mat);
        map.put("type", this.type);

        return map;
    }

}

Wenn ein Item gesammelt wird, wird diese Methode aufgerufen:

Java:
    /**
     * Checks and Rewards a Player if he completet Row (5,40)
     * @param p player
     * @param itm found item
     */
    public static void checkreward(Player p, ItemStack itm){
        User user = Itemplug.userManager.getUser(p);

        FItem founditem = FItem.getFitem(itm);

        for (Reward reward : Itemplug.getRewardManager().getReihen()){
            if (reward.getNeeded().contains(founditem)){
                boolean check = true;
                for (FItem fItem : reward.getNeeded()){
                    if (!user.getFounditems().contains(fItem)){
                        check = false;
                        break;
                    }
                }
                if (check){
                    reward.rewardPlayer(p);
                    break;
                }
            }
        }

        for (Reward reward : Itemplug.getRewardManager().getRow()){
            if (reward.getNeeded().contains(founditem)){
                boolean check = true;
                for (FItem fItem : reward.getNeeded()){
                    if (!user.getFounditems().contains(fItem)){
                        check = false;
                        break;
                    }
                }
                if (check){
                    reward.rewardPlayer(p);
                    break;
                }
            }
        }

    }

Was ich aus dem Reward hole:

Java:
    public ArrayList<FItem> getNeeded() {
        return needed;
    }

Weiters wird jedes Item was ein Spieler (User) gefunden hat in einer Arraylist<FItem> gspeichert

So weit funktioniert alles, wie geplant nur man merkt, dass das ganze einen Performance hit gibt wenn man viele Items auf einmal aufsammelt.
Kann ich die Performance überhaupt verbessern? Und wie?

Ich bedanke mich schon mal im Voraus!

~ lois

p.s: die Kommentare sind nur für mich gedacht gewesen 🙈
 

SirYwell

Moderator
Donator
Nov 2, 2017
8
2
Um ehrlich zu sein, werde ich aus deinem Code noch nicht ganz schlau, vielleicht weil der konkrete Zusammenhang mit den Bukkit-Events fehlt.

Ich würde allerdings mal behaupten, dass es vielleicht hilfreicher wäre, wenn du erläuterst, wie das Plugin funktionieren soll, dann ist es leichter zu sagen, was anders gemacht werden kann (und insbesondere wie).
 

GasGas

Mitglied
Nov 2, 2017
10
Das Ziel ist es jedes Item zu sammeln. Nach gewissen Items bekommt man Rewards Geld etc. (zusammen sind das in etwa 300 Rewards).

Um zu überprüfen ob jemand einen Reward verdient hat habe ich die checkreward (wird aus dem EntityPickupItemEvent aufgerufen) Methode geschrieben. Diese looped alle Rewards die es gibt und schaut nach ob es die Anforderungen erfüllt sind.

Das FItem Objekt speichert nur ob das Item ein Material wie Cobblestone ist oder ein spezielles ItemStack mit verzauberungen ist. Dafür habe ich die equals Methode angepasst.

Soweit funktioniert alles einwandfrei, ich wollte fragen ob ich das ganze Performanter schreiben könnte?
Da man auf meinen Test Server (welcher zwar nicht die beste Hardware hat) schon einen Performance hit merkt wenn viele Items aufgesammelt werden.
 

SirYwell

Moderator
Donator
Nov 2, 2017
8
2
Also sowas wie contains ist mit (Hash/Tree)Sets oder Maps immer asymptotisch schneller als bei einer Liste. Du musst dur natürlich anschauen, ob damit immer noch alle Anforderungen eingehalten werden. Was auch oft hilft ist das Sortieren der Daten, die öfter wiederverwendet werden (z.B. der Rewards). Das kommt aber natürlich drauf an, wie die Rewards genau aussehen und nach was man sie sinnvoll sortieren kann, aber wenn z.B. die Anzahl an benötigten FItems stark variiert, könntest du die Rewards anhanddessen sortieren und nur so lange durch die Rewards schauen, bis die Items im Inventar weniger sind als man für einen Reward braucht und dann bereits abbrechen.

Solche Optimierungen hängen halt stark von deinem Vorhaben ab und könnten sich eventuell auch negativ auswirken.

Alternativ könntest du mit einem Timer z.B. alle n Ticks die Inventare durchgehen und auf Rewards überprüfen, oder sogar einen Snapshot des Inventars machen und dann asynchron verarbeiten. Auch das hängt aber natürlich von deinen Anforderungen an das System ab.
 
  • Like
Reaktionen: GasGas

Users who are viewing this thema