* WIP screen upgrades
* Updated README
This commit is contained in:
parent
143e16e69b
commit
2d8a3792b7
|
|
@ -2,13 +2,12 @@
|
||||||
This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The text below is my "TODO" list.
|
This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The text below is my "TODO" list.
|
||||||
|
|
||||||
### Missing features
|
### Missing features
|
||||||
* Redstone interface
|
|
||||||
* Server blocks (to store some of the player's web pages)
|
|
||||||
* Screen upgrade: "laser" mouse
|
* Screen upgrade: "laser" mouse
|
||||||
* Screen upgrade: redstone input
|
* Screen upgrade: redstone input
|
||||||
* Screen upgrade: redstone output
|
* Screen upgrade: redstone output
|
||||||
* Peripheral: ComputerCraft interface
|
* ~~Peripheral: ComputerCraft interface~~ (CC not up to date)
|
||||||
* Peripheral: OpenComputers interface
|
* Peripheral: OpenComputers interface
|
||||||
|
* Server blocks (to store some of the player's web pages)
|
||||||
* Read config (see "Config elements" below)
|
* Read config (see "Config elements" below)
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
@ -30,5 +29,5 @@ This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The tex
|
||||||
* Homepage
|
* Homepage
|
||||||
* RPMP (Real pixels per Minecraft pixels)
|
* RPMP (Real pixels per Minecraft pixels)
|
||||||
* Browser language
|
* Browser language
|
||||||
* Screen load/unload distance
|
* Screen load/unload distance (max distance = 60.0)
|
||||||
* Disable ownership thief item
|
* Disable ownership thief item
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import net.minecraftforge.common.property.IUnlistedProperty;
|
||||||
import net.minecraftforge.common.property.Properties;
|
import net.minecraftforge.common.property.Properties;
|
||||||
import net.montoyo.wd.WebDisplays;
|
import net.montoyo.wd.WebDisplays;
|
||||||
import net.montoyo.wd.core.ScreenRights;
|
import net.montoyo.wd.core.ScreenRights;
|
||||||
|
import net.montoyo.wd.core.IUpgrade;
|
||||||
import net.montoyo.wd.data.SetURLData;
|
import net.montoyo.wd.data.SetURLData;
|
||||||
import net.montoyo.wd.entity.TileEntityScreen;
|
import net.montoyo.wd.entity.TileEntityScreen;
|
||||||
import net.montoyo.wd.utilities.*;
|
import net.montoyo.wd.utilities.*;
|
||||||
|
|
@ -102,7 +103,10 @@ public class BlockScreen extends WDBlockContainer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onBlockActivated(World world, BlockPos bpos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
|
public boolean onBlockActivated(World world, BlockPos bpos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
|
||||||
if(!player.getHeldItem(hand).isEmpty())
|
ItemStack heldItem = player.getHeldItem(hand);
|
||||||
|
if(heldItem.isEmpty())
|
||||||
|
heldItem = null; //Easier to work with
|
||||||
|
else if(!(heldItem.getItem() instanceof IUpgrade))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(world.isRemote)
|
if(world.isRemote)
|
||||||
|
|
@ -125,7 +129,18 @@ public class BlockScreen extends WDBlockContainer {
|
||||||
(new SetURLData(pos, scr.side, scr.url)).sendTo((EntityPlayerMP) player);
|
(new SetURLData(pos, scr.side, scr.url)).sendTo((EntityPlayerMP) player);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else if(heldItem != null && !te.hasUpgrade(side, heldItem)) { //Add upgrade
|
||||||
|
if((scr.rightsFor(player) & ScreenRights.MANAGE_UPGRADES) == 0) {
|
||||||
|
Util.toast(player, "restrictions");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
te.addUpgrade(side, heldItem);
|
||||||
|
if(!player.isCreative())
|
||||||
|
heldItem.shrink(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else { //Click
|
||||||
if((scr.rightsFor(player) & ScreenRights.CLICK) == 0) {
|
if((scr.rightsFor(player) & ScreenRights.CLICK) == 0) {
|
||||||
Util.toast(player, "restrictions");
|
Util.toast(player, "restrictions");
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
21
src/main/java/net/montoyo/wd/core/IUpgrade.java
Normal file
21
src/main/java/net/montoyo/wd/core/IUpgrade.java
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 BARBOTIN Nicolas
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.montoyo.wd.core;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.montoyo.wd.entity.TileEntityScreen;
|
||||||
|
import net.montoyo.wd.utilities.BlockSide;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public interface IUpgrade {
|
||||||
|
|
||||||
|
void onInstall(@Nonnull TileEntityScreen tes, @Nonnull BlockSide screenSide, @Nullable EntityPlayer player, @Nonnull ItemStack is);
|
||||||
|
boolean onRemove(@Nonnull TileEntityScreen tes, @Nonnull BlockSide screenSide, @Nullable EntityPlayer player, @Nonnull ItemStack is); //Return true to prevent dropping
|
||||||
|
void updateUpgrade(@Nonnull TileEntityScreen tes, @Nonnull BlockSide screenSide, @Nonnull ItemStack is);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ package net.montoyo.wd.entity;
|
||||||
|
|
||||||
import net.minecraft.entity.player.EntityPlayer;
|
import net.minecraft.entity.player.EntityPlayer;
|
||||||
import net.minecraft.entity.player.EntityPlayerMP;
|
import net.minecraft.entity.player.EntityPlayerMP;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.NBTTagCompound;
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import net.minecraft.nbt.NBTTagList;
|
import net.minecraft.nbt.NBTTagList;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
|
|
@ -16,6 +17,7 @@ import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||||
import net.montoyo.mcef.api.IBrowser;
|
import net.montoyo.mcef.api.IBrowser;
|
||||||
import net.montoyo.wd.WebDisplays;
|
import net.montoyo.wd.WebDisplays;
|
||||||
import net.montoyo.wd.block.BlockScreen;
|
import net.montoyo.wd.block.BlockScreen;
|
||||||
|
import net.montoyo.wd.core.IUpgrade;
|
||||||
import net.montoyo.wd.core.ScreenRights;
|
import net.montoyo.wd.core.ScreenRights;
|
||||||
import net.montoyo.wd.data.ScreenConfigData;
|
import net.montoyo.wd.data.ScreenConfigData;
|
||||||
import net.montoyo.wd.net.CMessageAddScreen;
|
import net.montoyo.wd.net.CMessageAddScreen;
|
||||||
|
|
@ -23,8 +25,9 @@ import net.montoyo.wd.net.CMessageScreenUpdate;
|
||||||
import net.montoyo.wd.net.SMessageRequestTEData;
|
import net.montoyo.wd.net.SMessageRequestTEData;
|
||||||
import net.montoyo.wd.utilities.*;
|
import net.montoyo.wd.utilities.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import javax.annotation.Nullable;
|
||||||
import java.util.UUID;
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class TileEntityScreen extends TileEntity {
|
public class TileEntityScreen extends TileEntity {
|
||||||
|
|
||||||
|
|
@ -43,6 +46,7 @@ public class TileEntityScreen extends TileEntity {
|
||||||
public int friendRights;
|
public int friendRights;
|
||||||
public int otherRights;
|
public int otherRights;
|
||||||
public IBrowser browser;
|
public IBrowser browser;
|
||||||
|
public ArrayList<ItemStack> upgrades;
|
||||||
|
|
||||||
public static boolean isYouTubeURL(String url) {
|
public static boolean isYouTubeURL(String url) {
|
||||||
return url.matches(YT_REGEX1) || url.matches(YT_REGEX2);
|
return url.matches(YT_REGEX1) || url.matches(YT_REGEX2);
|
||||||
|
|
@ -83,6 +87,13 @@ public class TileEntityScreen extends TileEntity {
|
||||||
|
|
||||||
ret.friendRights = tag.getByte("FriendRights");
|
ret.friendRights = tag.getByte("FriendRights");
|
||||||
ret.otherRights = tag.getByte("OtherRights");
|
ret.otherRights = tag.getByte("OtherRights");
|
||||||
|
|
||||||
|
NBTTagList upgrades = tag.getTagList("Upgrades", 10);
|
||||||
|
ret.upgrades = new ArrayList<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < upgrades.tagCount(); i++)
|
||||||
|
ret.upgrades.add(new ItemStack(upgrades.getCompoundTagAt(i)));
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,6 +125,12 @@ public class TileEntityScreen extends TileEntity {
|
||||||
tag.setTag("Friends", list);
|
tag.setTag("Friends", list);
|
||||||
tag.setByte("FriendRights", (byte) friendRights);
|
tag.setByte("FriendRights", (byte) friendRights);
|
||||||
tag.setByte("OtherRights", (byte) otherRights);
|
tag.setByte("OtherRights", (byte) otherRights);
|
||||||
|
|
||||||
|
list = new NBTTagList();
|
||||||
|
for(ItemStack is: upgrades)
|
||||||
|
list.appendTag(is.writeToNBT(new NBTTagCompound()));
|
||||||
|
|
||||||
|
tag.setTag("Upgrades", list);
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,6 +219,7 @@ public class TileEntityScreen extends TileEntity {
|
||||||
ret.friends = new ArrayList<>();
|
ret.friends = new ArrayList<>();
|
||||||
ret.friendRights = ScreenRights.DEFAULTS;
|
ret.friendRights = ScreenRights.DEFAULTS;
|
||||||
ret.otherRights = ScreenRights.DEFAULTS;
|
ret.otherRights = ScreenRights.DEFAULTS;
|
||||||
|
ret.upgrades = new ArrayList<>();
|
||||||
|
|
||||||
if(resolution == null || resolution.x < 1 || resolution.y < 1) {
|
if(resolution == null || resolution.x < 1 || resolution.y < 1) {
|
||||||
float psx = ((float) size.x) * 16.f - 4.f;
|
float psx = ((float) size.x) * 16.f - 4.f;
|
||||||
|
|
@ -536,4 +554,69 @@ public class TileEntityScreen extends TileEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateUpgrades(BlockSide side, ItemStack[] upgrades) {
|
||||||
|
if(!world.isRemote) {
|
||||||
|
Log.error("Tried to call TileEntityScreen.updateUpgrades() from server side...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Screen scr = getScreen(side);
|
||||||
|
if(scr == null) {
|
||||||
|
Log.error("Tried to update upgrades on invalid screen on side %s", side.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
scr.upgrades.clear();
|
||||||
|
Collections.addAll(scr.upgrades, upgrades);
|
||||||
|
}
|
||||||
|
|
||||||
|
//If equal is null, no duplicate check is preformed
|
||||||
|
public boolean addUpgrade(BlockSide side, ItemStack is, @Nullable Predicate<ItemStack> equal) {
|
||||||
|
//TODO: Remove Predicate. It should be obtained from IUpgrade.
|
||||||
|
if(world.isRemote)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Screen scr = getScreen(side);
|
||||||
|
if(scr == null) {
|
||||||
|
Log.error("Tried to add an upgrade on invalid screen on side %s", side.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(is.getItem() instanceof IUpgrade)) {
|
||||||
|
Log.error("Tried to add a non-upgrade item %s to screen (%s does not implement IUpgrade)", is.getItem().getRegistryName().toString(), is.getItem().getClass().getCanonicalName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(equal != null && scr.upgrades.stream().anyMatch(equal))
|
||||||
|
return false; //Upgrade already exists
|
||||||
|
|
||||||
|
scr.upgrades.add(is);
|
||||||
|
WebDisplays.NET_HANDLER.sendToAllAround(CMessageScreenUpdate.upgrade(this, side), point());
|
||||||
|
((IUpgrade) is.getItem()).onInstall(this, side, null, is);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Uses the default item stack comparing (same Item & metadata)
|
||||||
|
public boolean addUpgrade(BlockSide side, ItemStack is) {
|
||||||
|
return addUpgrade(side, is, (other) -> other.getItem() == is.getItem() && other.getMetadata() == is.getMetadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Uses the default item stack comparing (same Item & metadata)
|
||||||
|
public boolean hasUpgrade(BlockSide side, ItemStack is) {
|
||||||
|
Screen scr = getScreen(side);
|
||||||
|
if(scr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return scr.upgrades.stream().anyMatch((other) -> other.getItem() == is.getItem() && other.getMetadata() == is.getMetadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUpgrade(BlockSide side, Predicate<ItemStack> equal) {
|
||||||
|
//TODO: Remove Predicate. It should be obtained from IUpgrade.
|
||||||
|
Screen scr = getScreen(side);
|
||||||
|
if(scr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return scr.upgrades.stream().anyMatch(equal);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package net.montoyo.wd.net;
|
package net.montoyo.wd.net;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraftforge.fml.common.network.ByteBufUtils;
|
import net.minecraftforge.fml.common.network.ByteBufUtils;
|
||||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||||
|
|
@ -17,6 +18,8 @@ import net.montoyo.wd.utilities.Log;
|
||||||
import net.montoyo.wd.utilities.Vector2i;
|
import net.montoyo.wd.utilities.Vector2i;
|
||||||
import net.montoyo.wd.utilities.Vector3i;
|
import net.montoyo.wd.utilities.Vector3i;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Message(messageId = 0, side = Side.CLIENT)
|
@Message(messageId = 0, side = Side.CLIENT)
|
||||||
public class CMessageAddScreen implements IMessage, Runnable {
|
public class CMessageAddScreen implements IMessage, Runnable {
|
||||||
|
|
||||||
|
|
@ -55,6 +58,11 @@ public class CMessageAddScreen implements IMessage, Runnable {
|
||||||
screens[i].size = new Vector2i(buf);
|
screens[i].size = new Vector2i(buf);
|
||||||
screens[i].url = ByteBufUtils.readUTF8String(buf);
|
screens[i].url = ByteBufUtils.readUTF8String(buf);
|
||||||
screens[i].resolution = new Vector2i(buf);
|
screens[i].resolution = new Vector2i(buf);
|
||||||
|
screens[i].upgrades = new ArrayList<>();
|
||||||
|
|
||||||
|
int numUpgrades = buf.readByte();
|
||||||
|
for(int j = 0; j < numUpgrades; j++)
|
||||||
|
screens[i].upgrades.add(ByteBufUtils.readItemStack(buf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,6 +77,10 @@ public class CMessageAddScreen implements IMessage, Runnable {
|
||||||
scr.size.writeTo(buf);
|
scr.size.writeTo(buf);
|
||||||
ByteBufUtils.writeUTF8String(buf, scr.url);
|
ByteBufUtils.writeUTF8String(buf, scr.url);
|
||||||
scr.resolution.writeTo(buf);
|
scr.resolution.writeTo(buf);
|
||||||
|
buf.writeByte(scr.upgrades.size());
|
||||||
|
|
||||||
|
for(ItemStack is: scr.upgrades)
|
||||||
|
ByteBufUtils.writeItemStack(buf, is);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package net.montoyo.wd.net;
|
package net.montoyo.wd.net;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraftforge.fml.common.network.ByteBufUtils;
|
import net.minecraftforge.fml.common.network.ByteBufUtils;
|
||||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||||
|
|
@ -17,6 +18,8 @@ import net.montoyo.wd.utilities.Log;
|
||||||
import net.montoyo.wd.utilities.Vector2i;
|
import net.montoyo.wd.utilities.Vector2i;
|
||||||
import net.montoyo.wd.utilities.Vector3i;
|
import net.montoyo.wd.utilities.Vector3i;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Message(messageId = 4, side = Side.CLIENT)
|
@Message(messageId = 4, side = Side.CLIENT)
|
||||||
public class CMessageScreenUpdate implements IMessage, Runnable {
|
public class CMessageScreenUpdate implements IMessage, Runnable {
|
||||||
|
|
||||||
|
|
@ -25,6 +28,7 @@ public class CMessageScreenUpdate implements IMessage, Runnable {
|
||||||
public static final int UPDATE_DELETE = 2;
|
public static final int UPDATE_DELETE = 2;
|
||||||
public static final int UPDATE_CLICK = 3;
|
public static final int UPDATE_CLICK = 3;
|
||||||
public static final int UPDATE_TYPE = 4;
|
public static final int UPDATE_TYPE = 4;
|
||||||
|
public static final int UPDATE_UPGRADES = 5;
|
||||||
|
|
||||||
private Vector3i pos;
|
private Vector3i pos;
|
||||||
private BlockSide side;
|
private BlockSide side;
|
||||||
|
|
@ -33,6 +37,7 @@ public class CMessageScreenUpdate implements IMessage, Runnable {
|
||||||
private Vector2i resolution;
|
private Vector2i resolution;
|
||||||
private Vector2i click;
|
private Vector2i click;
|
||||||
private String text;
|
private String text;
|
||||||
|
private ItemStack[] upgrades;
|
||||||
|
|
||||||
public CMessageScreenUpdate() {
|
public CMessageScreenUpdate() {
|
||||||
}
|
}
|
||||||
|
|
@ -83,6 +88,21 @@ public class CMessageScreenUpdate implements IMessage, Runnable {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CMessageScreenUpdate upgrade(TileEntityScreen tes, BlockSide side) {
|
||||||
|
CMessageScreenUpdate ret = new CMessageScreenUpdate();
|
||||||
|
ret.pos = new Vector3i(tes.getPos());
|
||||||
|
ret.side = side;
|
||||||
|
ret.action = UPDATE_UPGRADES;
|
||||||
|
|
||||||
|
ArrayList<ItemStack> upgrades = tes.getScreen(side).upgrades;
|
||||||
|
ret.upgrades = new ItemStack[upgrades.size()];
|
||||||
|
|
||||||
|
for(int i = 0; i < upgrades.size(); i++)
|
||||||
|
ret.upgrades[i] = upgrades.get(i).copy();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void fromBytes(ByteBuf buf) {
|
public void fromBytes(ByteBuf buf) {
|
||||||
pos = new Vector3i(buf);
|
pos = new Vector3i(buf);
|
||||||
|
|
@ -97,6 +117,12 @@ public class CMessageScreenUpdate implements IMessage, Runnable {
|
||||||
resolution = new Vector2i(buf);
|
resolution = new Vector2i(buf);
|
||||||
else if(action == UPDATE_TYPE)
|
else if(action == UPDATE_TYPE)
|
||||||
text = ByteBufUtils.readUTF8String(buf);
|
text = ByteBufUtils.readUTF8String(buf);
|
||||||
|
else if(action == UPDATE_UPGRADES) {
|
||||||
|
upgrades = new ItemStack[buf.readByte()];
|
||||||
|
|
||||||
|
for(int i = 0; i < upgrades.length; i++)
|
||||||
|
upgrades[i] = ByteBufUtils.readItemStack(buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -113,6 +139,12 @@ public class CMessageScreenUpdate implements IMessage, Runnable {
|
||||||
resolution.writeTo(buf);
|
resolution.writeTo(buf);
|
||||||
else if(action == UPDATE_TYPE)
|
else if(action == UPDATE_TYPE)
|
||||||
ByteBufUtils.writeUTF8String(buf, text);
|
ByteBufUtils.writeUTF8String(buf, text);
|
||||||
|
else if(action == UPDATE_UPGRADES) {
|
||||||
|
buf.writeByte(upgrades.length);
|
||||||
|
|
||||||
|
for(ItemStack is: upgrades)
|
||||||
|
ByteBufUtils.writeItemStack(buf, is);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user