me.cybermaxke.merchants.v17r4.SMerchant.java Source code

Java tutorial

Introduction

Here is the source code for me.cybermaxke.merchants.v17r4.SMerchant.java

Source

/**
 * This file is part of MerchantsAPI.
 * 
 * Copyright (c) 2014, Cybermaxke
 * 
 * MerchantsAPI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MerchantsAPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MerchantsAPI. If not, see <http://www.gnu.org/licenses/>.
 */
package me.cybermaxke.merchants.v17r4;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.minecraft.server.v1_7_R4.Container;
import net.minecraft.server.v1_7_R4.EntityHuman;
import net.minecraft.server.v1_7_R4.EntityPlayer;
import net.minecraft.server.v1_7_R4.IMerchant;
import net.minecraft.server.v1_7_R4.ItemStack;
import net.minecraft.server.v1_7_R4.MerchantRecipe;
import net.minecraft.server.v1_7_R4.MerchantRecipeList;
import net.minecraft.server.v1_7_R4.PacketDataSerializer;
import net.minecraft.server.v1_7_R4.PacketPlayOutCustomPayload;
import net.minecraft.server.v1_7_R4.PacketPlayOutOpenWindow;
import net.minecraft.util.io.netty.buffer.Unpooled;

import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_7_R4.event.CraftEventFactory;
import org.bukkit.entity.Player;
import org.json.simple.parser.ParseException;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import me.cybermaxke.merchants.api.Merchant;
import me.cybermaxke.merchants.api.MerchantOffer;
import me.cybermaxke.merchants.api.MerchantTradeListener;

import static com.google.common.base.Preconditions.checkNotNull;
import static me.cybermaxke.merchants.v17r4.SMerchantAPI.SPIGOT;

@SuppressWarnings("unchecked")
public class SMerchant implements IMerchant, Merchant {
    // The recipes list
    private final MerchantRecipeList offers = new MerchantRecipeList();

    // The customers
    private final Set<Player> customers = Sets.newHashSet();

    // The title of the merchant
    private String title;
    private boolean jsonTitle;

    // The title that will be send
    private String sendTitle;

    // The trade handlers
    protected final Set<MerchantTradeListener> handlers = Sets.newHashSet();

    // Internal use only
    protected SMerchantOffer onTrade;

    public SMerchant(String title, boolean jsonTitle) {
        this.setTitle(title, jsonTitle);
    }

    @Override
    public String getTitle() {
        return this.title;
    }

    @Override
    public boolean isTitleJson() {
        return this.jsonTitle;
    }

    @Override
    public void setTitle(String title, boolean jsonTitle) {
        checkNotNull(title, "title");

        // The old title
        String oldTitle = this.sendTitle;
        String newTitle;

        if (jsonTitle) {
            try {
                newTitle = SUtil.fromJson(title);
            } catch (ParseException e) {
                throw new IllegalArgumentException("invalid json format (" + title + ")", e);
            }
        } else {
            newTitle = title;
        }

        if (title.length() > 32) {
            title = title.substring(0, 32);
        }

        this.sendTitle = newTitle;
        this.jsonTitle = jsonTitle;
        this.title = title;

        // Send a update
        if (!this.sendTitle.equals(oldTitle)) {
            this.sendTitleUpdate();
        }
    }

    @Override
    public void setTitle(String title) {
        this.setTitle(title, false);
    }

    @Override
    public boolean addListener(MerchantTradeListener listener) {
        checkNotNull(listener, "listener");
        return this.handlers.add(listener);
    }

    @Override
    public boolean removeListener(MerchantTradeListener listener) {
        checkNotNull(listener, "listener");
        return this.handlers.remove(listener);
    }

    @Override
    public Collection<MerchantTradeListener> getListeners() {
        return Lists.newArrayList(this.handlers);
    }

    @Override
    public int getOffersCount() {
        return this.offers.size();
    }

    @Override
    public MerchantOffer getOfferAt(int index) {
        if (index < 0 || index >= this.offers.size()) {
            throw new IndexOutOfBoundsException(
                    "index (" + index + ") out of bounds min (0) and max (" + this.offers.size() + ")");
        }

        return (MerchantOffer) this.offers.get(index);
    }

    @Override
    public void setOfferAt(int index, MerchantOffer offer) {
        checkNotNull(offer, "offer");

        if (index < 0 || index >= this.offers.size()) {
            throw new IndexOutOfBoundsException(
                    "index (" + index + ") out of bounds min (0) and max (" + this.offers.size() + ")");
        }

        SMerchantOffer old = (SMerchantOffer) this.offers.set(index, offer);
        old.remove(this);

        // Send the new offer list
        this.sendUpdate();
    }

    @Override
    public void insetOfferAt(int index, MerchantOffer offer) {
        checkNotNull(offer, "offer");

        if (index < 0 || index >= this.offers.size()) {
            throw new IndexOutOfBoundsException(
                    "index (" + index + ") out of bounds min (0) and max (" + this.offers.size() + ")");
        }

        this.offers.add(index, offer);
    }

    @Override
    public void removeOffer(MerchantOffer offer) {
        checkNotNull(offer, "offer");

        if (this.offers.remove(offer)) {
            // Unlink the offer
            ((SMerchantOffer) offer).remove(this);

            // Send the new offer list
            this.sendUpdate();
        }
    }

    @Override
    public void removeOffers(Iterable<MerchantOffer> offers) {
        checkNotNull(offers, "offers");

        // Only update if necessary
        if (!offers.iterator().hasNext()) {
            return;
        }

        if (this.offers.removeAll(Lists.newArrayList(offers))) {
            // Unlink the offers
            for (MerchantOffer offer : offers) {
                ((SMerchantOffer) offer).remove(this);
            }

            // Send the new offer list
            this.sendUpdate();
        }
    }

    @Override
    public void addOffer(MerchantOffer offer) {
        checkNotNull(offer, "offer");

        if (this.offers.contains(offer)) {
            return;
        }

        // Add the offer
        this.offers.add(offer);

        // Link the offer
        ((SMerchantOffer) offer).add(this);

        // Send the new offer list
        this.sendUpdate();
    }

    @Override
    public void addOffers(Iterable<MerchantOffer> offers) {
        checkNotNull(offers, "offers");

        // Only update if necessary
        if (!offers.iterator().hasNext()) {
            return;
        }

        // Add and link the offers
        for (MerchantOffer offer : offers) {
            if (this.offers.contains(offer)) {
                continue;
            }
            this.offers.add(offer);
            ((SMerchantOffer) offer).add(this);
        }

        // Send the new offer list
        this.sendUpdate();
    }

    @Override
    public void sortOffers(Comparator<MerchantOffer> comparator) {
        checkNotNull(comparator, "comparator");

        // Only sort if necessary
        if (this.offers.size() <= 1) {
            return;
        }

        // Sort the offers
        Collections.sort(this.offers, comparator);

        // Send the new offer list
        this.sendUpdate();
    }

    @Override
    public List<MerchantOffer> getOffers() {
        return Lists.newArrayList(this.offers);
    }

    @Override
    public boolean addCustomer(Player player) {
        checkNotNull(player, "player");

        if (this.customers.add(player)) {
            EntityPlayer player0 = ((CraftPlayer) player).getHandle();
            Container container0 = null;

            try {
                container0 = new SContainerMerchant(player0, this);
                container0 = CraftEventFactory.callInventoryOpenEvent(player0, container0);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (container0 == null) {
                this.customers.remove(player);
                return false;
            }

            int window = player0.nextContainerCounter();

            player0.activeContainer = container0;
            player0.activeContainer.windowId = window;
            player0.activeContainer.addSlotListener(player0);

            // Open the window
            player0.playerConnection.sendPacket(new PacketPlayOutOpenWindow(window, 6, this.sendTitle, 3, true));

            // Write the recipe list
            PacketDataSerializer content;
            // Create the proper packet serializer
            if (SPIGOT) {
                content = new PacketDataSerializer(Unpooled.buffer(),
                        player0.playerConnection.networkManager.getVersion());
            } else {
                content = new PacketDataSerializer(Unpooled.buffer());
            }
            content.writeInt(window);
            this.offers.a(content);

            // Send the offers
            player0.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|TrList", content));

            return true;
        }

        return false;
    }

    @Override
    public boolean removeCustomer(Player player) {
        checkNotNull(player, "player");

        if (this.customers.remove(player)) {
            player.closeInventory();
            return true;
        }

        return false;
    }

    @Override
    public boolean hasCustomer(Player player) {
        checkNotNull(player, "player");
        return this.customers.contains(player);
    }

    @Override
    public Collection<Player> getCustomers() {
        return Lists.newArrayList(this.customers);
    }

    @Override
    public MerchantRecipeList getOffers(EntityHuman human) {
        return this.offers;
    }

    @Override
    public void a(MerchantRecipe recipe) {
        // Increment uses
        recipe.f();

        // Used by the custom merchant result slot
        this.onTrade = (SMerchantOffer) recipe;
    }

    @Override
    public void a_(EntityHuman arg0) {
        // Not used
    }

    @Override
    public void a_(ItemStack arg0) {
        // Not used
    }

    @Override
    public EntityHuman b() {
        // Not used
        return null;
    }

    protected void sendTitleUpdate() {
        // Re-send the open window message to update the window name
        Iterator<Player> it = this.customers.iterator();
        while (it.hasNext()) {
            EntityPlayer player0 = ((CraftPlayer) it.next()).getHandle();
            player0.playerConnection.sendPacket(
                    new PacketPlayOutOpenWindow(player0.activeContainer.windowId, 6, this.sendTitle, 3, true));
            player0.updateInventory(player0.activeContainer);
        }
    }

    // Called when the merchant requires a update
    protected void sendUpdate() {
        if (this.customers.isEmpty()) {
            return;
        }

        if (SPIGOT) {
            Collection<EntityPlayer>[] collections = this.split(this.customers);

            // Copy the uses fields if needed
            if (collections[1] != null || collections[2] != null || collections[3] != null) {
                for (Object offer : this.offers) {
                    ((SMerchantOffer) offer).copyUses();
                }
            }

            if (collections[0] != null) {
                this.sendUpdateWithProtocol(27, collections[0]);
            }
            if (collections[1] != null) {
                this.sendUpdateWithProtocol(28, collections[1]);
            }
            if (collections[2] != null) {
                this.sendUpdateWithProtocol(29, collections[2]);
            }
            if (collections[3] != null) {
                this.sendUpdateWithProtocol(47, collections[3]);
            }
        } else {
            this.sendUpdateWithSerializer(new PacketDataSerializer(Unpooled.buffer()),
                    Collections2.transform(this.customers, new Function<Player, EntityPlayer>() {

                        @Override
                        public EntityPlayer apply(Player player) {
                            return ((CraftPlayer) player).getHandle();
                        }

                    }));
        }
    }

    protected void sendUpdateWithProtocol(int protocol, Iterable<EntityPlayer> players) {
        this.sendUpdateWithSerializer(new PacketDataSerializer(Unpooled.buffer(), protocol), players);
    }

    protected void sendUpdateWithSerializer(PacketDataSerializer serializer, Iterable<EntityPlayer> players) {
        // Write the recipe list
        this.offers.a(serializer);

        // Send a packet to all the players
        Iterator<EntityPlayer> it = players.iterator();
        while (it.hasNext()) {
            EntityPlayer player0 = it.next();

            // Every player has a different window id
            PacketDataSerializer content1 = new PacketDataSerializer(Unpooled.buffer());
            content1.writeInt(player0.activeContainer.windowId);
            content1.writeBytes(serializer);

            player0.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|TrList", content1));
        }
    }

    protected Collection<EntityPlayer>[] split(Iterable<Player> players) {
        Collection<EntityPlayer> list0 = null;
        Collection<EntityPlayer> list1 = null;
        Collection<EntityPlayer> list2 = null;
        Collection<EntityPlayer> list3 = null;

        Iterator<Player> it = players.iterator();
        while (it.hasNext()) {
            EntityPlayer player0 = ((CraftPlayer) it.next()).getHandle();

            int version = player0.playerConnection.networkManager.getVersion();
            if (version < 28) {
                if (list0 == null) {
                    list0 = Lists.newArrayList();
                }
                list0.add(player0);
            } else if (version < 29) {
                if (list1 == null) {
                    list1 = Lists.newArrayList();
                }
                list1.add(player0);
            } else if (version < 47) {
                if (list2 == null) {
                    list2 = Lists.newArrayList();
                }
                list2.add(player0);
            } else {
                if (list3 == null) {
                    list3 = Lists.newArrayList();
                }
                list3.add(player0);
            }
        }

        // list0 < 28; list1 < 29; list3 < 47; list4 >= 47
        return new Collection[] { list0, list1, list2, list3 };
    }
}