mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-09-01 01:57:55 +00:00
ClientRegistry
This commit is contained in:
@@ -3,33 +3,34 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import com.sun.org.apache.xpath.internal.operations.Mod;
|
||||
import java.util.HashSet;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.render.TurtleModelLoader;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.media.items.ItemDisk;
|
||||
import dan200.computercraft.shared.media.items.ItemTreasureDisk;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.util.Colour;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.ModelLoader;
|
||||
import net.minecraft.client.render.model.ModelRotation;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture;
|
||||
import net.minecraft.client.util.ModelIdentifier;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
|
||||
import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback;
|
||||
|
||||
/**
|
||||
* Registers textures and models for items.
|
||||
@@ -38,8 +39,7 @@ import java.util.function.Consumer;
|
||||
"MethodCallSideOnly",
|
||||
"LocalVariableDeclarationSideOnly"
|
||||
})
|
||||
public final class ClientRegistry
|
||||
{
|
||||
public final class ClientRegistry {
|
||||
private static final String[] EXTRA_MODELS = new String[] {
|
||||
"turtle_modem_normal_off_left",
|
||||
"turtle_modem_normal_on_left",
|
||||
@@ -58,7 +58,7 @@ public final class ClientRegistry
|
||||
|
||||
"turtle_colour",
|
||||
"turtle_elf_overlay",
|
||||
};
|
||||
};
|
||||
|
||||
private static final String[] EXTRA_TEXTURES = new String[] {
|
||||
// TODO: Gather these automatically from the model. Sadly the model loader isn't available
|
||||
@@ -67,7 +67,7 @@ public final class ClientRegistry
|
||||
"block/turtle_elf_overlay",
|
||||
"block/turtle_crafty_face",
|
||||
"block/turtle_speaker_face",
|
||||
};
|
||||
};
|
||||
|
||||
private ClientRegistry() {}
|
||||
|
||||
@@ -87,76 +87,38 @@ public final class ClientRegistry
|
||||
public static void onItemColours() {
|
||||
ColorProviderRegistry.ITEM.register((stack, layer) -> {
|
||||
return layer == 1 ? ((ItemDisk) stack.getItem()).getColour(stack) : 0xFFFFFF;
|
||||
});
|
||||
}, Registry.ModItems.DISK);
|
||||
|
||||
ColorProviderRegistry.ITEM.register((stack, layer) -> layer == 1 ? ItemTreasureDisk.getColour(stack) : 0xFFFFFF, Registry.ModItems.TREASURE_DISK);
|
||||
|
||||
ColorProviderRegistry.ITEM.register((stack, layer) -> {
|
||||
switch (layer) {
|
||||
case 0:
|
||||
default:
|
||||
return 0xFFFFFF;
|
||||
case 1: // Frame colour
|
||||
return IColouredItem.getColourBasic(stack);
|
||||
case 2: // Light colour
|
||||
{
|
||||
int light = ItemPocketComputer.getLightState(stack);
|
||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||
}
|
||||
case 0:
|
||||
default:
|
||||
return 0xFFFFFF;
|
||||
case 1: // Frame colour
|
||||
return IColouredItem.getColourBasic(stack);
|
||||
case 2: // Light colour
|
||||
{
|
||||
int light = ItemPocketComputer.getLightState(stack);
|
||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||
}
|
||||
}
|
||||
}, Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED);
|
||||
|
||||
// Setup turtle colours
|
||||
ColorProviderRegistry.ITEM.register((stack, tintIndex) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF,
|
||||
Registry.ModBlocks.TURTLE_NORMAL,
|
||||
Registry.ModBlocks.TURTLE_NORMAL,
|
||||
Registry.ModBlocks.TURTLE_ADVANCED);
|
||||
}
|
||||
|
||||
private static BakedModel bake(ModelLoader loader, UnbakedModel model) {
|
||||
private static BakedModel bake(ModelLoader loader, UnbakedModel model, Identifier identifier) {
|
||||
model.getTextureDependencies(loader::getOrLoadModel, new HashSet<>());
|
||||
SpriteAtlasTexture sprite = MinecraftClient.getInstance()
|
||||
.getSpriteAtlas();
|
||||
return model.bake(loader, spriteIdentifier -> MinecraftClient.getInstance()
|
||||
.getSpriteAtlas(spriteIdentifier.getAtlasId()).apply(spriteIdentifier.getTextureId()), ModelRotation.X0_Y0);
|
||||
return model.bake(loader,
|
||||
spriteIdentifier -> MinecraftClient.getInstance()
|
||||
.getSpriteAtlas(spriteIdentifier.getAtlasId())
|
||||
.apply(spriteIdentifier.getTextureId()),
|
||||
ModelRotation.X0_Y0, identifier);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onItemColours( ColorHandlerEvent.Item event )
|
||||
{
|
||||
if( Registry.ModItems.DISK == null || Registry.ModBlocks.TURTLE_NORMAL == null )
|
||||
{
|
||||
ComputerCraft.log.warn( "Block/item registration has failed. Skipping registration of item colours." );
|
||||
return;
|
||||
}
|
||||
|
||||
event.getItemColors().register(
|
||||
( stack, layer ) -> layer == 1 ? ((ItemDisk) stack.getItem()).getColour( stack ) : 0xFFFFFF,
|
||||
Registry.ModItems.DISK.get()
|
||||
);
|
||||
|
||||
event.getItemColors().register(
|
||||
( stack, layer ) -> layer == 1 ? ItemTreasureDisk.getColour( stack ) : 0xFFFFFF,
|
||||
Registry.ModItems.TREASURE_DISK.get()
|
||||
);
|
||||
|
||||
event.getItemColors().register( ( stack, layer ) -> {
|
||||
switch( layer )
|
||||
{
|
||||
case 0:
|
||||
default:
|
||||
return 0xFFFFFF;
|
||||
case 1: // Frame colour
|
||||
return IColouredItem.getColourBasic( stack );
|
||||
case 2: // Light colour
|
||||
{
|
||||
int light = ItemPocketComputer.getLightState( stack );
|
||||
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||
}
|
||||
}
|
||||
}, Registry.ModItems.POCKET_COMPUTER_NORMAL.get(), Registry.ModItems.POCKET_COMPUTER_ADVANCED.get() );
|
||||
|
||||
// Setup turtle colours
|
||||
event.getItemColors().register(
|
||||
( stack, tintIndex ) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour( stack ) : 0xFFFFFF,
|
||||
Registry.ModBlocks.TURTLE_NORMAL.get(), Registry.ModBlocks.TURTLE_ADVANCED.get()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -3,43 +3,42 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public final class FrameInfo
|
||||
{
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
|
||||
public final class FrameInfo {
|
||||
private static int tick;
|
||||
private static long renderFrame;
|
||||
static {
|
||||
|
||||
private FrameInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public static boolean getGlobalCursorBlink()
|
||||
{
|
||||
public static void init() {
|
||||
ClientTickEvents.START_CLIENT_TICK.register(m -> {
|
||||
tick++;
|
||||
});
|
||||
}
|
||||
|
||||
private FrameInfo() {
|
||||
}
|
||||
|
||||
public static boolean getGlobalCursorBlink() {
|
||||
return (tick / 8) % 2 == 0;
|
||||
}
|
||||
|
||||
public static long getRenderFrame()
|
||||
{
|
||||
public static long getRenderFrame() {
|
||||
return renderFrame;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTick( TickEvent.ClientTickEvent event )
|
||||
{
|
||||
if( event.phase == TickEvent.Phase.START ) tick++;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderTick( TickEvent.RenderTickEvent event )
|
||||
{
|
||||
if( event.phase == TickEvent.Phase.START ) renderFrame++;
|
||||
public static void onRenderTick(TickEvent.RenderTickEvent event) {
|
||||
if (event.phase == TickEvent.Phase.START) {
|
||||
renderFrame++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,10 +3,19 @@
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.client.proxy;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.ClientRegistry;
|
||||
import dan200.computercraft.client.FrameInfo;
|
||||
import dan200.computercraft.client.gui.GuiComputer;
|
||||
import dan200.computercraft.client.gui.GuiDiskDrive;
|
||||
import dan200.computercraft.client.gui.GuiPrinter;
|
||||
import dan200.computercraft.client.gui.GuiPrintout;
|
||||
import dan200.computercraft.client.gui.GuiTurtle;
|
||||
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
|
||||
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
|
||||
import dan200.computercraft.client.render.TurtlePlayerRenderer;
|
||||
@@ -17,6 +26,13 @@ import dan200.computercraft.shared.computer.inventory.ContainerViewComputer;
|
||||
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
|
||||
import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer;
|
||||
import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreens;
|
||||
import net.minecraft.client.item.ModelPredicateProvider;
|
||||
import net.minecraft.client.item.ModelPredicateProviderRegistry;
|
||||
@@ -24,84 +40,76 @@ import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.RenderLayers;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD )
|
||||
public final class ComputerCraftProxyClient
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void setupClient( FMLClientSetupEvent event )
|
||||
{
|
||||
registerContainers();
|
||||
|
||||
// While turtles themselves are not transparent, their upgrades may be.
|
||||
RenderLayers.setRenderLayer( Registry.ModBlocks.TURTLE_NORMAL.get(), RenderLayer.getTranslucent() );
|
||||
RenderLayers.setRenderLayer( Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderLayer.getTranslucent() );
|
||||
|
||||
// Monitors' textures have transparent fronts and so count as cutouts.
|
||||
RenderLayers.setRenderLayer( Registry.ModBlocks.MONITOR_NORMAL.get(), RenderLayer.getCutout() );
|
||||
RenderLayers.setRenderLayer( Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderLayer.getCutout() );
|
||||
|
||||
// Setup TESRs
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
|
||||
// TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
|
||||
|
||||
registerItemProperty( "state",
|
||||
( stack, world, player ) -> ItemPocketComputer.getState( stack ).ordinal(),
|
||||
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
registerItemProperty( "state",
|
||||
( stack, world, player ) -> IColouredItem.getColourBasic( stack ) != -1 ? 1 : 0,
|
||||
Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED
|
||||
);
|
||||
}
|
||||
@SuppressWarnings ("MethodCallSideOnly")
|
||||
public final class ComputerCraftProxyClient implements ClientModInitializer {
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty( String name, ModelPredicateProvider getter, Supplier<? extends Item>... items )
|
||||
{
|
||||
Identifier id = new Identifier( ComputerCraft.MOD_ID, name );
|
||||
for( Supplier<? extends Item> item : items )
|
||||
{
|
||||
ModelPredicateProviderRegistry.register( item.get(), id, getter );
|
||||
private static void registerItemProperty(String name, ModelPredicateProvider getter, Supplier<? extends Item>... items) {
|
||||
Identifier id = new Identifier(ComputerCraft.MOD_ID, name);
|
||||
for (Supplier<? extends Item> item : items) {
|
||||
ModelPredicateProviderRegistry.register(item.get(), id, getter);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerContainers()
|
||||
{
|
||||
private static void registerContainers() {
|
||||
// My IDE doesn't think so, but we do actually need these generics.
|
||||
|
||||
HandledScreens.<ContainerComputer, GuiComputer<ContainerComputer>>register( Registry.ModContainers.COMPUTER.get(), GuiComputer::create );
|
||||
HandledScreens.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register( Registry.ModContainers.POCKET_COMPUTER.get(), GuiComputer::createPocket );
|
||||
HandledScreens.register( Registry.ModContainers.TURTLE, GuiTurtle::new );
|
||||
HandledScreens.<ContainerComputer, GuiComputer<ContainerComputer>>register(Registry.ModContainers.COMPUTER.get(), GuiComputer::create);
|
||||
HandledScreens.<ContainerPocketComputer, GuiComputer<ContainerPocketComputer>>register(Registry.ModContainers.POCKET_COMPUTER.get(),
|
||||
GuiComputer::createPocket);
|
||||
HandledScreens.register(Registry.ModContainers.TURTLE, GuiTurtle::new);
|
||||
|
||||
HandledScreens.register( Registry.ModContainers.PRINTER, GuiPrinter::new );
|
||||
HandledScreens.register( Registry.ModContainers.DISK_DRIVE, GuiDiskDrive::new );
|
||||
HandledScreens.register( Registry.ModContainers.PRINTOUT, GuiPrintout::new );
|
||||
HandledScreens.register(Registry.ModContainers.PRINTER, GuiPrinter::new);
|
||||
HandledScreens.register(Registry.ModContainers.DISK_DRIVE, GuiDiskDrive::new);
|
||||
HandledScreens.register(Registry.ModContainers.PRINTOUT, GuiPrintout::new);
|
||||
|
||||
HandledScreens.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register( Registry.ModContainers.VIEW_COMPUTER.get(), GuiComputer::createView );
|
||||
HandledScreens.<ContainerViewComputer, GuiComputer<ContainerViewComputer>>register(Registry.ModContainers.VIEW_COMPUTER.get(),
|
||||
GuiComputer::createView);
|
||||
}
|
||||
|
||||
@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT )
|
||||
public static final class ForgeHandlers
|
||||
{
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
FrameInfo.init();
|
||||
registerContainers();
|
||||
|
||||
// While turtles themselves are not transparent, their upgrades may be.
|
||||
RenderLayers.setRenderLayer(Registry.ModBlocks.TURTLE_NORMAL.get(), RenderLayer.getTranslucent());
|
||||
RenderLayers.setRenderLayer(Registry.ModBlocks.TURTLE_ADVANCED.get(), RenderLayer.getTranslucent());
|
||||
|
||||
// Monitors' textures have transparent fronts and so count as cutouts.
|
||||
RenderLayers.setRenderLayer(Registry.ModBlocks.MONITOR_NORMAL.get(), RenderLayer.getCutout());
|
||||
RenderLayers.setRenderLayer(Registry.ModBlocks.MONITOR_ADVANCED.get(), RenderLayer.getCutout());
|
||||
|
||||
// Setup TESRs
|
||||
ClientRegistry.bindTileEntityRenderer(Registry.ModTiles.MONITOR_NORMAL.get(), TileEntityMonitorRenderer::new);
|
||||
ClientRegistry.bindTileEntityRenderer(Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new);
|
||||
ClientRegistry.bindTileEntityRenderer(Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new);
|
||||
ClientRegistry.bindTileEntityRenderer(Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new);
|
||||
// TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler(Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new);
|
||||
|
||||
registerItemProperty("state",
|
||||
(stack, world, player) -> ItemPocketComputer.getState(stack)
|
||||
.ordinal(),
|
||||
Registry.ModItems.POCKET_COMPUTER_NORMAL,
|
||||
Registry.ModItems.POCKET_COMPUTER_ADVANCED);
|
||||
registerItemProperty("state",
|
||||
(stack, world, player) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
||||
Registry.ModItems.POCKET_COMPUTER_NORMAL,
|
||||
Registry.ModItems.POCKET_COMPUTER_ADVANCED);
|
||||
ClientRegistry.onItemColours();
|
||||
}
|
||||
|
||||
@Mod.EventBusSubscriber (modid = ComputerCraft.MOD_ID, value = Dist.CLIENT)
|
||||
public static final class ForgeHandlers {
|
||||
@SubscribeEvent
|
||||
public static void onWorldUnload( WorldEvent.Unload event )
|
||||
{
|
||||
if( event.getWorld().isClient() )
|
||||
{
|
||||
public static void onWorldUnload(WorldEvent.Unload event) {
|
||||
if (event.getWorld()
|
||||
.isClient()) {
|
||||
ClientMonitor.destroyAll();
|
||||
}
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@ import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.*;
|
||||
|
||||
/**
|
||||
* Various helpers for building chat messages.
|
||||
|
@@ -27,7 +27,8 @@
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [ "dan200.computercraft.ComputerCraft" ],
|
||||
"modmenu": [ "dan200.computercraft.client.ModMenuIntegration" ]
|
||||
"modmenu": [ "dan200.computercraft.client.ModMenuIntegration" ],
|
||||
"client": ["dan200.computercraft.client.proxy.ComputerCraftProxyClient"]
|
||||
},
|
||||
"mixins": [
|
||||
{ "config": "computercraft.client.json", "environment": "client" },
|
||||
|
Reference in New Issue
Block a user