* Batch network extraction on shift crafting potential fix for #2104 * resolve requested changes * resolve more requested changes
This commit is contained in:
@@ -1,9 +1,12 @@
|
|||||||
package com.refinedmods.refinedstorage.api.network.grid;
|
package com.refinedmods.refinedstorage.api.network.grid;
|
||||||
|
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.crafting.ICraftingRecipe;
|
import net.minecraft.item.crafting.ICraftingRecipe;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines default behavior of crafting grids.
|
* Defines default behavior of crafting grids.
|
||||||
*/
|
*/
|
||||||
@@ -11,11 +14,13 @@ public interface ICraftingGridBehavior {
|
|||||||
/**
|
/**
|
||||||
* Logic for regular crafting.
|
* Logic for regular crafting.
|
||||||
*
|
*
|
||||||
* @param grid the grid
|
* @param grid the grid
|
||||||
* @param recipe the recipe
|
* @param recipe the recipe
|
||||||
* @param player the player
|
* @param player the player
|
||||||
|
* @param availableItems the items available for shift crafting
|
||||||
|
* @param usedItems the items used by shift crafting
|
||||||
*/
|
*/
|
||||||
void onCrafted(INetworkAwareGrid grid, ICraftingRecipe recipe, PlayerEntity player);
|
void onCrafted(INetworkAwareGrid grid, ICraftingRecipe recipe, PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logic for crafting with shift click (mass crafting).
|
* Logic for crafting with shift click (mass crafting).
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.refinedmods.refinedstorage.api.network.grid.handler.IItemGridHandler;
|
|||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
||||||
import com.refinedmods.refinedstorage.api.util.IFilter;
|
import com.refinedmods.refinedstorage.api.util.IFilter;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||||
import net.minecraft.inventory.CraftResultInventory;
|
import net.minecraft.inventory.CraftResultInventory;
|
||||||
@@ -201,9 +202,11 @@ public interface IGrid {
|
|||||||
/**
|
/**
|
||||||
* Called when an item is crafted in a crafting grid.
|
* Called when an item is crafted in a crafting grid.
|
||||||
*
|
*
|
||||||
* @param player the player that crafted the item
|
* @param player the player that crafted the item
|
||||||
|
* @param availableItems the items available for shift crafting
|
||||||
|
* @param usedItems the items used by shift crafting
|
||||||
*/
|
*/
|
||||||
void onCrafted(PlayerEntity player);
|
void onCrafted(PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the clear button is pressed in the pattern grid or crafting grid.
|
* Called when the clear button is pressed in the pattern grid or crafting grid.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.refinedmods.refinedstorage.api.network.grid.INetworkAwareGrid;
|
|||||||
import com.refinedmods.refinedstorage.api.network.security.Permission;
|
import com.refinedmods.refinedstorage.api.network.security.Permission;
|
||||||
import com.refinedmods.refinedstorage.api.util.Action;
|
import com.refinedmods.refinedstorage.api.util.Action;
|
||||||
import com.refinedmods.refinedstorage.api.util.IComparer;
|
import com.refinedmods.refinedstorage.api.util.IComparer;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.API;
|
import com.refinedmods.refinedstorage.apiimpl.API;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.network.node.GridNetworkNode;
|
import com.refinedmods.refinedstorage.apiimpl.network.node.GridNetworkNode;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
@@ -19,12 +20,13 @@ import net.minecraftforge.common.ForgeHooks;
|
|||||||
import net.minecraftforge.fml.hooks.BasicEventHooks;
|
import net.minecraftforge.fml.hooks.BasicEventHooks;
|
||||||
import net.minecraftforge.items.ItemHandlerHelper;
|
import net.minecraftforge.items.ItemHandlerHelper;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CraftingGridBehavior implements ICraftingGridBehavior {
|
public class CraftingGridBehavior implements ICraftingGridBehavior {
|
||||||
@Override
|
@Override
|
||||||
public void onCrafted(INetworkAwareGrid grid, ICraftingRecipe recipe, PlayerEntity player) {
|
public void onCrafted(INetworkAwareGrid grid, ICraftingRecipe recipe, PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems) {
|
||||||
NonNullList<ItemStack> remainder = recipe.getRemainingItems(grid.getCraftingMatrix());
|
NonNullList<ItemStack> remainder = recipe.getRemainingItems(grid.getCraftingMatrix());
|
||||||
|
|
||||||
INetwork network = grid.getNetwork();
|
INetwork network = grid.getNetwork();
|
||||||
@@ -53,7 +55,17 @@ public class CraftingGridBehavior implements ICraftingGridBehavior {
|
|||||||
}
|
}
|
||||||
} else if (!slot.isEmpty()) { // We don't have a remainder, but the slot is not empty.
|
} else if (!slot.isEmpty()) { // We don't have a remainder, but the slot is not empty.
|
||||||
if (slot.getCount() == 1 && network != null) { // Attempt to refill the slot with the same item from the network, only if we have a network and only if it's the last item.
|
if (slot.getCount() == 1 && network != null) { // Attempt to refill the slot with the same item from the network, only if we have a network and only if it's the last item.
|
||||||
ItemStack refill = network.extractItem(slot, 1, Action.PERFORM);
|
ItemStack refill;
|
||||||
|
if (availableItems == null) { // for regular crafting
|
||||||
|
refill = network.extractItem(slot, 1, Action.PERFORM);
|
||||||
|
} else { // for shift crafting
|
||||||
|
if (availableItems.get(slot) != null) {
|
||||||
|
refill = availableItems.remove(slot, 1).getStack().copy();
|
||||||
|
usedItems.add(refill);
|
||||||
|
} else {
|
||||||
|
refill = ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
matrix.setInventorySlotContents(i, refill);
|
matrix.setInventorySlotContents(i, refill);
|
||||||
|
|
||||||
@@ -71,28 +83,48 @@ public class CraftingGridBehavior implements ICraftingGridBehavior {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCraftedShift(INetworkAwareGrid grid, PlayerEntity player) {
|
public void onCraftedShift(INetworkAwareGrid grid, PlayerEntity player) {
|
||||||
|
CraftingInventory matrix = grid.getCraftingMatrix();
|
||||||
|
INetwork network = grid.getNetwork();
|
||||||
List<ItemStack> craftedItemsList = new ArrayList<>();
|
List<ItemStack> craftedItemsList = new ArrayList<>();
|
||||||
int amountCrafted = 0;
|
|
||||||
ItemStack crafted = grid.getCraftingResult().getStackInSlot(0);
|
ItemStack crafted = grid.getCraftingResult().getStackInSlot(0);
|
||||||
|
|
||||||
int maxCrafted = crafted.getMaxStackSize();
|
int maxCrafted = crafted.getMaxStackSize();
|
||||||
|
|
||||||
ForgeHooks.setCraftingPlayer(player);
|
int amountCrafted = 0;
|
||||||
|
boolean useNetwork = network != null;
|
||||||
|
|
||||||
|
IStackList<ItemStack> availableItems = null;
|
||||||
|
if (useNetwork) {
|
||||||
|
// We need a modifiable list of the items in storage that are relevant for this craft.
|
||||||
|
// For performance reason we extract these into an extra list
|
||||||
|
availableItems = createFilteredItemList(network, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
//A second list to remember which items have been extracted
|
||||||
|
IStackList<ItemStack> usedItems = API.instance().createItemStackList();
|
||||||
|
|
||||||
|
ForgeHooks.setCraftingPlayer(player);
|
||||||
// Do while the item is still craftable (aka is the result slot still the same as the original item?) and we don't exceed the max stack size.
|
// Do while the item is still craftable (aka is the result slot still the same as the original item?) and we don't exceed the max stack size.
|
||||||
do {
|
do {
|
||||||
grid.onCrafted(player);
|
grid.onCrafted(player, availableItems, usedItems);
|
||||||
|
|
||||||
craftedItemsList.add(crafted.copy());
|
craftedItemsList.add(crafted.copy());
|
||||||
|
|
||||||
amountCrafted += crafted.getCount();
|
amountCrafted += crafted.getCount();
|
||||||
} while (API.instance().getComparer().isEqual(crafted, grid.getCraftingResult().getStackInSlot(0)) && amountCrafted < maxCrafted && amountCrafted + crafted.getCount() <= maxCrafted );
|
} while (API.instance().getComparer().isEqual(crafted, grid.getCraftingResult().getStackInSlot(0)) && amountCrafted < maxCrafted && amountCrafted + crafted.getCount() <= maxCrafted);
|
||||||
|
|
||||||
INetwork network = grid.getNetwork();
|
if (useNetwork) {
|
||||||
|
usedItems.getStacks().forEach(stack -> network.extractItem(stack.getStack(), stack.getStack().getCount(), Action.PERFORM));
|
||||||
|
}
|
||||||
|
|
||||||
for (ItemStack craftedItem : craftedItemsList) {
|
for (ItemStack craftedItem : craftedItemsList) {
|
||||||
if (!player.inventory.addItemStackToInventory(craftedItem.copy())) {
|
if (!player.inventory.addItemStackToInventory(craftedItem.copy())) {
|
||||||
ItemStack remainder = network == null ? craftedItem : network.insertItem(craftedItem, craftedItem.getCount(), Action.PERFORM);
|
|
||||||
|
ItemStack remainder = craftedItem;
|
||||||
|
|
||||||
|
if (useNetwork) {
|
||||||
|
remainder = network.insertItem(craftedItem, craftedItem.getCount(), Action.PERFORM);
|
||||||
|
}
|
||||||
|
|
||||||
if (!remainder.isEmpty()) {
|
if (!remainder.isEmpty()) {
|
||||||
InventoryHelper.spawnItemStack(player.getEntityWorld(), player.getPosition().getX(), player.getPosition().getY(), player.getPosition().getZ(), remainder);
|
InventoryHelper.spawnItemStack(player.getEntityWorld(), player.getPosition().getX(), player.getPosition().getY(), player.getPosition().getZ(), remainder);
|
||||||
@@ -108,6 +140,19 @@ public class CraftingGridBehavior implements ICraftingGridBehavior {
|
|||||||
ForgeHooks.setCraftingPlayer(null);
|
ForgeHooks.setCraftingPlayer(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IStackList<ItemStack> createFilteredItemList(INetwork network, CraftingInventory matrix) {
|
||||||
|
IStackList<ItemStack> availableItems = API.instance().createItemStackList();
|
||||||
|
for (int i = 0; i < matrix.getSizeInventory(); ++i) {
|
||||||
|
ItemStack stack = network.getItemStorageCache().getList().get(matrix.getStackInSlot(i));
|
||||||
|
|
||||||
|
//Don't add the same item twice into the list. Items may appear twice in a recipe but not in storage.
|
||||||
|
if (stack != null && availableItems.get(stack) == null) {
|
||||||
|
availableItems.add(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return availableItems;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRecipeTransfer(INetworkAwareGrid grid, PlayerEntity player, ItemStack[][] recipe) {
|
public void onRecipeTransfer(INetworkAwareGrid grid, PlayerEntity player, ItemStack[][] recipe) {
|
||||||
INetwork network = grid.getNetwork();
|
INetwork network = grid.getNetwork();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
|||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
||||||
import com.refinedmods.refinedstorage.api.util.Action;
|
import com.refinedmods.refinedstorage.api.util.Action;
|
||||||
import com.refinedmods.refinedstorage.api.util.IFilter;
|
import com.refinedmods.refinedstorage.api.util.IFilter;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.API;
|
import com.refinedmods.refinedstorage.apiimpl.API;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.autocrafting.AllowedTagList;
|
import com.refinedmods.refinedstorage.apiimpl.autocrafting.AllowedTagList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.storage.cache.listener.FluidGridStorageCacheListener;
|
import com.refinedmods.refinedstorage.apiimpl.storage.cache.listener.FluidGridStorageCacheListener;
|
||||||
@@ -55,10 +56,7 @@ import net.minecraftforge.items.wrapper.InvWrapper;
|
|||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, IType {
|
public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, IType {
|
||||||
public static final ResourceLocation ID = new ResourceLocation(RS.ID, "grid");
|
public static final ResourceLocation ID = new ResourceLocation(RS.ID, "grid");
|
||||||
@@ -422,8 +420,8 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCrafted(PlayerEntity player) {
|
public void onCrafted(PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems) {
|
||||||
API.instance().getCraftingGridBehavior().onCrafted(this, currentRecipe, player);
|
API.instance().getCraftingGridBehavior().onCrafted(this, currentRecipe, player, availableItems, usedItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class ResultCraftingGridSlot extends CraftingResultSlot {
|
|||||||
onCrafting(stack);
|
onCrafting(stack);
|
||||||
|
|
||||||
if (!player.getEntityWorld().isRemote) {
|
if (!player.getEntityWorld().isRemote) {
|
||||||
grid.onCrafted(player);
|
grid.onCrafted(player, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ItemStack.EMPTY;
|
return ItemStack.EMPTY;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.refinedmods.refinedstorage.api.network.grid.handler.IItemGridHandler;
|
|||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
||||||
import com.refinedmods.refinedstorage.api.util.IFilter;
|
import com.refinedmods.refinedstorage.api.util.IFilter;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.storage.cache.listener.FluidGridStorageCacheListener;
|
import com.refinedmods.refinedstorage.apiimpl.storage.cache.listener.FluidGridStorageCacheListener;
|
||||||
import com.refinedmods.refinedstorage.inventory.item.FilterItemHandler;
|
import com.refinedmods.refinedstorage.inventory.item.FilterItemHandler;
|
||||||
import com.refinedmods.refinedstorage.item.WirelessFluidGridItem;
|
import com.refinedmods.refinedstorage.item.WirelessFluidGridItem;
|
||||||
@@ -271,7 +272,7 @@ public class WirelessFluidGrid implements INetworkAwareGrid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCrafted(PlayerEntity player) {
|
public void onCrafted(PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems) {
|
||||||
// NO OP
|
// NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.refinedmods.refinedstorage.api.network.grid.handler.IItemGridHandler;
|
|||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCache;
|
||||||
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
import com.refinedmods.refinedstorage.api.storage.cache.IStorageCacheListener;
|
||||||
import com.refinedmods.refinedstorage.api.util.IFilter;
|
import com.refinedmods.refinedstorage.api.util.IFilter;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.storage.cache.listener.ItemGridStorageCacheListener;
|
import com.refinedmods.refinedstorage.apiimpl.storage.cache.listener.ItemGridStorageCacheListener;
|
||||||
import com.refinedmods.refinedstorage.inventory.item.FilterItemHandler;
|
import com.refinedmods.refinedstorage.inventory.item.FilterItemHandler;
|
||||||
import com.refinedmods.refinedstorage.item.WirelessGridItem;
|
import com.refinedmods.refinedstorage.item.WirelessGridItem;
|
||||||
@@ -277,7 +278,7 @@ public class WirelessGrid implements INetworkAwareGrid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCrafted(PlayerEntity player) {
|
public void onCrafted(PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems) {
|
||||||
// NO OP
|
// NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import com.refinedmods.refinedstorage.api.storage.disk.IStorageDiskContainerCont
|
|||||||
import com.refinedmods.refinedstorage.api.storage.disk.IStorageDiskProvider;
|
import com.refinedmods.refinedstorage.api.storage.disk.IStorageDiskProvider;
|
||||||
import com.refinedmods.refinedstorage.api.storage.disk.StorageDiskSyncData;
|
import com.refinedmods.refinedstorage.api.storage.disk.StorageDiskSyncData;
|
||||||
import com.refinedmods.refinedstorage.api.util.IFilter;
|
import com.refinedmods.refinedstorage.api.util.IFilter;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.API;
|
import com.refinedmods.refinedstorage.apiimpl.API;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableFluidGridHandler;
|
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableFluidGridHandler;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableItemGridHandler;
|
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableItemGridHandler;
|
||||||
@@ -397,7 +398,7 @@ public class PortableGrid implements IGrid, IPortableGrid, IStorageDiskContainer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCrafted(PlayerEntity player) {
|
public void onCrafted(PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems) {
|
||||||
// NO OP
|
// NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.refinedmods.refinedstorage.api.storage.disk.IStorageDiskContainerCont
|
|||||||
import com.refinedmods.refinedstorage.api.storage.disk.IStorageDiskProvider;
|
import com.refinedmods.refinedstorage.api.storage.disk.IStorageDiskProvider;
|
||||||
import com.refinedmods.refinedstorage.api.storage.tracker.IStorageTracker;
|
import com.refinedmods.refinedstorage.api.storage.tracker.IStorageTracker;
|
||||||
import com.refinedmods.refinedstorage.api.util.IFilter;
|
import com.refinedmods.refinedstorage.api.util.IFilter;
|
||||||
|
import com.refinedmods.refinedstorage.api.util.IStackList;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.API;
|
import com.refinedmods.refinedstorage.apiimpl.API;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableFluidGridHandler;
|
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableFluidGridHandler;
|
||||||
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableItemGridHandler;
|
import com.refinedmods.refinedstorage.apiimpl.network.grid.handler.PortableItemGridHandler;
|
||||||
@@ -490,7 +491,7 @@ public class PortableGridTile extends BaseTile implements IGrid, IPortableGrid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCrafted(PlayerEntity player) {
|
public void onCrafted(PlayerEntity player, @Nullable IStackList<ItemStack> availableItems, @Nullable IStackList<ItemStack> usedItems) {
|
||||||
// NO OP
|
// NO OP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user