284 lines
21 KiB
Java
284 lines
21 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Lists
|
|
* com.mojang.brigadier.CommandDispatcher
|
|
* com.mojang.brigadier.arguments.IntegerArgumentType
|
|
* com.mojang.brigadier.builder.ArgumentBuilder
|
|
* com.mojang.brigadier.builder.LiteralArgumentBuilder
|
|
* com.mojang.brigadier.builder.RequiredArgumentBuilder
|
|
* com.mojang.brigadier.context.CommandContext
|
|
* com.mojang.brigadier.exceptions.CommandSyntaxException
|
|
* com.mojang.brigadier.exceptions.DynamicCommandExceptionType
|
|
* it.unimi.dsi.fastutil.objects.ObjectArrayList
|
|
*/
|
|
package net.minecraft.server.commands;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import com.mojang.brigadier.CommandDispatcher;
|
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
|
import com.mojang.brigadier.context.CommandContext;
|
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import net.minecraft.commands.CommandBuildContext;
|
|
import net.minecraft.commands.CommandSourceStack;
|
|
import net.minecraft.commands.Commands;
|
|
import net.minecraft.commands.arguments.EntityArgument;
|
|
import net.minecraft.commands.arguments.ResourceOrIdArgument;
|
|
import net.minecraft.commands.arguments.SlotArgument;
|
|
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
|
|
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
|
|
import net.minecraft.commands.arguments.item.ItemArgument;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.commands.ItemCommands;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.world.Container;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EquipmentSlot;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.SlotAccess;
|
|
import net.minecraft.world.entity.item.ItemEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.storage.loot.LootParams;
|
|
import net.minecraft.world.level.storage.loot.LootTable;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
|
import net.minecraft.world.phys.Vec3;
|
|
|
|
public class LootCommand {
|
|
private static final DynamicCommandExceptionType ERROR_NO_HELD_ITEMS = new DynamicCommandExceptionType(entity -> Component.translatableEscape("commands.drop.no_held_items", entity));
|
|
private static final DynamicCommandExceptionType ERROR_NO_ENTITY_LOOT_TABLE = new DynamicCommandExceptionType(entity -> Component.translatableEscape("commands.drop.no_loot_table.entity", entity));
|
|
private static final DynamicCommandExceptionType ERROR_NO_BLOCK_LOOT_TABLE = new DynamicCommandExceptionType(block -> Component.translatableEscape("commands.drop.no_loot_table.block", block));
|
|
|
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext context) {
|
|
dispatcher.register(LootCommand.addTargets((LiteralArgumentBuilder)Commands.literal("loot").requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS)), (target, output) -> target.then(Commands.literal("fish").then(Commands.argument("loot_table", ResourceOrIdArgument.lootTable(context)).then(((RequiredArgumentBuilder)((RequiredArgumentBuilder)((RequiredArgumentBuilder)Commands.argument("pos", BlockPosArgument.blockPos()).executes(c -> LootCommand.dropFishingLoot((CommandContext<CommandSourceStack>)c, ResourceOrIdArgument.getLootTable((CommandContext<CommandSourceStack>)c, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), ItemStack.EMPTY, output))).then(Commands.argument("tool", ItemArgument.item(context)).executes(c -> LootCommand.dropFishingLoot((CommandContext<CommandSourceStack>)c, ResourceOrIdArgument.getLootTable((CommandContext<CommandSourceStack>)c, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), ItemArgument.getItem(c, "tool").createItemStack(1, false), output)))).then(Commands.literal("mainhand").executes(c -> LootCommand.dropFishingLoot((CommandContext<CommandSourceStack>)c, ResourceOrIdArgument.getLootTable((CommandContext<CommandSourceStack>)c, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), LootCommand.getSourceHandItem((CommandSourceStack)c.getSource(), EquipmentSlot.MAINHAND), output)))).then(Commands.literal("offhand").executes(c -> LootCommand.dropFishingLoot((CommandContext<CommandSourceStack>)c, ResourceOrIdArgument.getLootTable((CommandContext<CommandSourceStack>)c, "loot_table"), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), LootCommand.getSourceHandItem((CommandSourceStack)c.getSource(), EquipmentSlot.OFFHAND), output)))))).then(Commands.literal("loot").then(Commands.argument("loot_table", ResourceOrIdArgument.lootTable(context)).executes(c -> LootCommand.dropChestLoot((CommandContext<CommandSourceStack>)c, ResourceOrIdArgument.getLootTable((CommandContext<CommandSourceStack>)c, "loot_table"), output)))).then(Commands.literal("kill").then(Commands.argument("target", EntityArgument.entity()).executes(c -> LootCommand.dropKillLoot((CommandContext<CommandSourceStack>)c, EntityArgument.getEntity((CommandContext<CommandSourceStack>)c, "target"), output)))).then(Commands.literal("mine").then(((RequiredArgumentBuilder)((RequiredArgumentBuilder)((RequiredArgumentBuilder)Commands.argument("pos", BlockPosArgument.blockPos()).executes(c -> LootCommand.dropBlockLoot((CommandContext<CommandSourceStack>)c, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), ItemStack.EMPTY, output))).then(Commands.argument("tool", ItemArgument.item(context)).executes(c -> LootCommand.dropBlockLoot((CommandContext<CommandSourceStack>)c, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), ItemArgument.getItem(c, "tool").createItemStack(1, false), output)))).then(Commands.literal("mainhand").executes(c -> LootCommand.dropBlockLoot((CommandContext<CommandSourceStack>)c, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), LootCommand.getSourceHandItem((CommandSourceStack)c.getSource(), EquipmentSlot.MAINHAND), output)))).then(Commands.literal("offhand").executes(c -> LootCommand.dropBlockLoot((CommandContext<CommandSourceStack>)c, BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "pos"), LootCommand.getSourceHandItem((CommandSourceStack)c.getSource(), EquipmentSlot.OFFHAND), output)))))));
|
|
}
|
|
|
|
private static <T extends ArgumentBuilder<CommandSourceStack, T>> T addTargets(T root, TailProvider tail) {
|
|
return (T)root.then(((LiteralArgumentBuilder)Commands.literal("replace").then(Commands.literal("entity").then(Commands.argument("entities", EntityArgument.entities()).then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("slot", SlotArgument.slot()), (c, drops, callback) -> LootCommand.entityReplace(EntityArgument.getEntities((CommandContext<CommandSourceStack>)c, "entities"), SlotArgument.getSlot((CommandContext<CommandSourceStack>)c, "slot"), drops.size(), drops, callback)).then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("count", IntegerArgumentType.integer((int)0)), (c, drops, callback) -> LootCommand.entityReplace(EntityArgument.getEntities((CommandContext<CommandSourceStack>)c, "entities"), SlotArgument.getSlot((CommandContext<CommandSourceStack>)c, "slot"), IntegerArgumentType.getInteger((CommandContext)c, (String)"count"), drops, callback))))))).then(Commands.literal("block").then(Commands.argument("targetPos", BlockPosArgument.blockPos()).then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("slot", SlotArgument.slot()), (c, drops, callback) -> LootCommand.blockReplace((CommandSourceStack)c.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "targetPos"), SlotArgument.getSlot((CommandContext<CommandSourceStack>)c, "slot"), drops.size(), drops, callback)).then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("count", IntegerArgumentType.integer((int)0)), (c, drops, callback) -> LootCommand.blockReplace((CommandSourceStack)c.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "targetPos"), IntegerArgumentType.getInteger((CommandContext)c, (String)"slot"), IntegerArgumentType.getInteger((CommandContext)c, (String)"count"), drops, callback))))))).then(Commands.literal("insert").then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("targetPos", BlockPosArgument.blockPos()), (c, drops, callback) -> LootCommand.blockDistribute((CommandSourceStack)c.getSource(), BlockPosArgument.getLoadedBlockPos((CommandContext<CommandSourceStack>)c, "targetPos"), drops, callback)))).then(Commands.literal("give").then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("players", EntityArgument.players()), (c, drops, callback) -> LootCommand.playerGive(EntityArgument.getPlayers((CommandContext<CommandSourceStack>)c, "players"), drops, callback)))).then(Commands.literal("spawn").then(tail.construct((ArgumentBuilder<CommandSourceStack, ?>)Commands.argument("targetPos", Vec3Argument.vec3()), (c, drops, callback) -> LootCommand.dropInWorld((CommandSourceStack)c.getSource(), Vec3Argument.getVec3((CommandContext<CommandSourceStack>)c, "targetPos"), drops, callback))));
|
|
}
|
|
|
|
private static Container getContainer(CommandSourceStack source, BlockPos pos) throws CommandSyntaxException {
|
|
BlockEntity blockEntity = source.getLevel().getBlockEntity(pos);
|
|
if (!(blockEntity instanceof Container)) {
|
|
throw ItemCommands.ERROR_TARGET_NOT_A_CONTAINER.create((Object)pos.getX(), (Object)pos.getY(), (Object)pos.getZ());
|
|
}
|
|
return (Container)((Object)blockEntity);
|
|
}
|
|
|
|
private static int blockDistribute(CommandSourceStack source, BlockPos pos, List<ItemStack> drops, Callback callback) throws CommandSyntaxException {
|
|
Container container = LootCommand.getContainer(source, pos);
|
|
ArrayList usedItems = Lists.newArrayListWithCapacity((int)drops.size());
|
|
for (ItemStack drop : drops) {
|
|
if (!LootCommand.distributeToContainer(container, drop.copy())) continue;
|
|
container.setChanged();
|
|
usedItems.add(drop);
|
|
}
|
|
callback.accept(usedItems);
|
|
return usedItems.size();
|
|
}
|
|
|
|
private static boolean distributeToContainer(Container container, ItemStack itemStack) {
|
|
boolean changed = false;
|
|
for (int slot = 0; slot < container.getContainerSize() && !itemStack.isEmpty(); ++slot) {
|
|
ItemStack current = container.getItem(slot);
|
|
if (!container.canPlaceItem(slot, itemStack)) continue;
|
|
if (current.isEmpty()) {
|
|
container.setItem(slot, itemStack);
|
|
changed = true;
|
|
break;
|
|
}
|
|
if (!LootCommand.canMergeItems(current, itemStack)) continue;
|
|
int space = itemStack.getMaxStackSize() - current.getCount();
|
|
int count = Math.min(itemStack.getCount(), space);
|
|
itemStack.shrink(count);
|
|
current.grow(count);
|
|
changed = true;
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
private static int blockReplace(CommandSourceStack source, BlockPos pos, int startSlot, int slotCount, List<ItemStack> drops, Callback callback) throws CommandSyntaxException {
|
|
Container container = LootCommand.getContainer(source, pos);
|
|
int maxSlot = container.getContainerSize();
|
|
if (startSlot < 0 || startSlot >= maxSlot) {
|
|
throw ItemCommands.ERROR_TARGET_INAPPLICABLE_SLOT.create((Object)startSlot);
|
|
}
|
|
ArrayList usedItems = Lists.newArrayListWithCapacity((int)drops.size());
|
|
for (int i = 0; i < slotCount; ++i) {
|
|
ItemStack toAdd;
|
|
int slot = startSlot + i;
|
|
ItemStack itemStack = toAdd = i < drops.size() ? drops.get(i) : ItemStack.EMPTY;
|
|
if (!container.canPlaceItem(slot, toAdd)) continue;
|
|
container.setItem(slot, toAdd);
|
|
usedItems.add(toAdd);
|
|
}
|
|
callback.accept(usedItems);
|
|
return usedItems.size();
|
|
}
|
|
|
|
private static boolean canMergeItems(ItemStack a, ItemStack b) {
|
|
return a.getCount() <= a.getMaxStackSize() && ItemStack.isSameItemSameComponents(a, b);
|
|
}
|
|
|
|
private static int playerGive(Collection<ServerPlayer> players, List<ItemStack> drops, Callback callback) throws CommandSyntaxException {
|
|
ArrayList usedItems = Lists.newArrayListWithCapacity((int)drops.size());
|
|
for (ItemStack drop : drops) {
|
|
for (ServerPlayer player : players) {
|
|
if (!player.getInventory().add(drop.copy())) continue;
|
|
usedItems.add(drop);
|
|
}
|
|
}
|
|
callback.accept(usedItems);
|
|
return usedItems.size();
|
|
}
|
|
|
|
private static void setSlots(Entity entity, List<ItemStack> itemsToSet, int startSlot, int count, List<ItemStack> usedItems) {
|
|
for (int i = 0; i < count; ++i) {
|
|
ItemStack item = i < itemsToSet.size() ? itemsToSet.get(i) : ItemStack.EMPTY;
|
|
SlotAccess slotAccess = entity.getSlot(startSlot + i);
|
|
if (slotAccess == null || !slotAccess.set(item.copy())) continue;
|
|
usedItems.add(item);
|
|
}
|
|
}
|
|
|
|
private static int entityReplace(Collection<? extends Entity> entities, int startSlot, int count, List<ItemStack> drops, Callback callback) throws CommandSyntaxException {
|
|
ArrayList usedItems = Lists.newArrayListWithCapacity((int)drops.size());
|
|
for (Entity entity : entities) {
|
|
if (entity instanceof ServerPlayer) {
|
|
ServerPlayer player = (ServerPlayer)entity;
|
|
LootCommand.setSlots(entity, drops, startSlot, count, usedItems);
|
|
player.containerMenu.broadcastChanges();
|
|
continue;
|
|
}
|
|
LootCommand.setSlots(entity, drops, startSlot, count, usedItems);
|
|
}
|
|
callback.accept(usedItems);
|
|
return usedItems.size();
|
|
}
|
|
|
|
private static int dropInWorld(CommandSourceStack source, Vec3 pos, List<ItemStack> drops, Callback callback) throws CommandSyntaxException {
|
|
ServerLevel level = source.getLevel();
|
|
drops.forEach(drop -> {
|
|
ItemEntity entity = new ItemEntity(level, pos.x, pos.y, pos.z, drop.copy());
|
|
entity.setDefaultPickUpDelay();
|
|
level.addFreshEntity(entity);
|
|
});
|
|
callback.accept(drops);
|
|
return drops.size();
|
|
}
|
|
|
|
private static void callback(CommandSourceStack source, List<ItemStack> drops) {
|
|
if (drops.size() == 1) {
|
|
ItemStack drop = drops.get(0);
|
|
source.sendSuccess(() -> Component.translatable("commands.drop.success.single", drop.getCount(), drop.getDisplayName()), false);
|
|
} else {
|
|
source.sendSuccess(() -> Component.translatable("commands.drop.success.multiple", drops.size()), false);
|
|
}
|
|
}
|
|
|
|
private static void callback(CommandSourceStack source, List<ItemStack> drops, ResourceKey<LootTable> location) {
|
|
if (drops.size() == 1) {
|
|
ItemStack drop = drops.get(0);
|
|
source.sendSuccess(() -> Component.translatable("commands.drop.success.single_with_table", drop.getCount(), drop.getDisplayName(), Component.translationArg(location.identifier())), false);
|
|
} else {
|
|
source.sendSuccess(() -> Component.translatable("commands.drop.success.multiple_with_table", drops.size(), Component.translationArg(location.identifier())), false);
|
|
}
|
|
}
|
|
|
|
private static ItemStack getSourceHandItem(CommandSourceStack source, EquipmentSlot slot) throws CommandSyntaxException {
|
|
Entity entity = source.getEntityOrException();
|
|
if (entity instanceof LivingEntity) {
|
|
return ((LivingEntity)entity).getItemBySlot(slot);
|
|
}
|
|
throw ERROR_NO_HELD_ITEMS.create((Object)entity.getDisplayName());
|
|
}
|
|
|
|
private static int dropBlockLoot(CommandContext<CommandSourceStack> context, BlockPos pos, ItemStack tool, DropConsumer output) throws CommandSyntaxException {
|
|
CommandSourceStack source = (CommandSourceStack)context.getSource();
|
|
ServerLevel level = source.getLevel();
|
|
BlockState blockState = level.getBlockState(pos);
|
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
|
Optional<ResourceKey<LootTable>> lootTable = blockState.getBlock().getLootTable();
|
|
if (lootTable.isEmpty()) {
|
|
throw ERROR_NO_BLOCK_LOOT_TABLE.create((Object)blockState.getBlock().getName());
|
|
}
|
|
LootParams.Builder lootParams = new LootParams.Builder(level).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)).withParameter(LootContextParams.BLOCK_STATE, blockState).withOptionalParameter(LootContextParams.BLOCK_ENTITY, blockEntity).withOptionalParameter(LootContextParams.THIS_ENTITY, source.getEntity()).withParameter(LootContextParams.TOOL, tool);
|
|
List<ItemStack> drops = blockState.getDrops(lootParams);
|
|
return output.accept(context, drops, usedItems -> LootCommand.callback(source, usedItems, (ResourceKey)lootTable.get()));
|
|
}
|
|
|
|
private static int dropKillLoot(CommandContext<CommandSourceStack> context, Entity target, DropConsumer output) throws CommandSyntaxException {
|
|
Optional<ResourceKey<LootTable>> lootTableId = target.getLootTable();
|
|
if (lootTableId.isEmpty()) {
|
|
throw ERROR_NO_ENTITY_LOOT_TABLE.create((Object)target.getDisplayName());
|
|
}
|
|
CommandSourceStack source = (CommandSourceStack)context.getSource();
|
|
LootParams.Builder builder = new LootParams.Builder(source.getLevel());
|
|
Entity killer = source.getEntity();
|
|
if (killer instanceof Player) {
|
|
Player player = (Player)killer;
|
|
builder.withParameter(LootContextParams.LAST_DAMAGE_PLAYER, player);
|
|
}
|
|
builder.withParameter(LootContextParams.DAMAGE_SOURCE, target.damageSources().magic());
|
|
builder.withOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY, killer);
|
|
builder.withOptionalParameter(LootContextParams.ATTACKING_ENTITY, killer);
|
|
builder.withParameter(LootContextParams.THIS_ENTITY, target);
|
|
builder.withParameter(LootContextParams.ORIGIN, source.getPosition());
|
|
LootParams lootParams = builder.create(LootContextParamSets.ENTITY);
|
|
LootTable lootTable = source.getServer().reloadableRegistries().getLootTable(lootTableId.get());
|
|
ObjectArrayList<ItemStack> drops = lootTable.getRandomItems(lootParams);
|
|
return output.accept(context, (List<ItemStack>)drops, usedItems -> LootCommand.callback(source, usedItems, (ResourceKey)lootTableId.get()));
|
|
}
|
|
|
|
private static int dropChestLoot(CommandContext<CommandSourceStack> context, Holder<LootTable> lootTable, DropConsumer output) throws CommandSyntaxException {
|
|
CommandSourceStack source = (CommandSourceStack)context.getSource();
|
|
LootParams lootParams = new LootParams.Builder(source.getLevel()).withOptionalParameter(LootContextParams.THIS_ENTITY, source.getEntity()).withParameter(LootContextParams.ORIGIN, source.getPosition()).create(LootContextParamSets.CHEST);
|
|
return LootCommand.drop(context, lootTable, lootParams, output);
|
|
}
|
|
|
|
private static int dropFishingLoot(CommandContext<CommandSourceStack> context, Holder<LootTable> lootTable, BlockPos pos, ItemStack tool, DropConsumer output) throws CommandSyntaxException {
|
|
CommandSourceStack source = (CommandSourceStack)context.getSource();
|
|
LootParams lootParams = new LootParams.Builder(source.getLevel()).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)).withParameter(LootContextParams.TOOL, tool).withOptionalParameter(LootContextParams.THIS_ENTITY, source.getEntity()).create(LootContextParamSets.FISHING);
|
|
return LootCommand.drop(context, lootTable, lootParams, output);
|
|
}
|
|
|
|
private static int drop(CommandContext<CommandSourceStack> context, Holder<LootTable> lootTable, LootParams lootParams, DropConsumer output) throws CommandSyntaxException {
|
|
CommandSourceStack source = (CommandSourceStack)context.getSource();
|
|
ObjectArrayList<ItemStack> drops = lootTable.value().getRandomItems(lootParams);
|
|
return output.accept(context, (List<ItemStack>)drops, usedItems -> LootCommand.callback(source, usedItems));
|
|
}
|
|
|
|
@FunctionalInterface
|
|
private static interface TailProvider {
|
|
public ArgumentBuilder<CommandSourceStack, ?> construct(ArgumentBuilder<CommandSourceStack, ?> var1, DropConsumer var2);
|
|
}
|
|
|
|
@FunctionalInterface
|
|
private static interface DropConsumer {
|
|
public int accept(CommandContext<CommandSourceStack> var1, List<ItemStack> var2, Callback var3) throws CommandSyntaxException;
|
|
}
|
|
|
|
@FunctionalInterface
|
|
private static interface Callback {
|
|
public void accept(List<ItemStack> var1) throws CommandSyntaxException;
|
|
}
|
|
}
|
|
|