Fix click
This commit is contained in:
@@ -30,11 +30,12 @@ public final class RenderEvent {
|
||||
*/
|
||||
private final float partialTicks;
|
||||
|
||||
private final MatrixStack matrixStack;
|
||||
private final MatrixStack modelViewStack, projectionStack;
|
||||
|
||||
public RenderEvent(float partialTicks, MatrixStack matrixStack) {
|
||||
public RenderEvent(float partialTicks, MatrixStack modelViewStack, MatrixStack projectionStack) {
|
||||
this.partialTicks = partialTicks;
|
||||
this.matrixStack = matrixStack;
|
||||
this.modelViewStack = modelViewStack;
|
||||
this.projectionStack = projectionStack;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +45,11 @@ public final class RenderEvent {
|
||||
return this.partialTicks;
|
||||
}
|
||||
|
||||
public final MatrixStack getMatrixStack() {
|
||||
return this.matrixStack;
|
||||
public final MatrixStack getModelViewStack() {
|
||||
return this.modelViewStack;
|
||||
}
|
||||
|
||||
public final MatrixStack getProjectionStack() {
|
||||
return this.projectionStack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,14 @@ import baritone.api.BaritoneAPI;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.event.events.RenderEvent;
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class MixinGameRenderer {
|
||||
@@ -36,11 +39,12 @@ public class MixinGameRenderer {
|
||||
value = "INVOKE_STRING",
|
||||
target = "Lnet/minecraft/profiler/IProfiler;endStartSection(Ljava/lang/String;)V",
|
||||
args = {"ldc=hand"}
|
||||
)
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void renderWorldPass(float partialTicks, long finishTimeNano, MatrixStack matrixStack, CallbackInfo ci) {
|
||||
private void renderWorldPass(float partialTicks, long finishTimeNano, MatrixStack modelViewMatrix, CallbackInfo ci, boolean flag, ActiveRenderInfo activerenderinfo, MatrixStack projectionMatrix, float f, Matrix4f matrix4f) {
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
ibaritone.getGameEventHandler().onRenderPass(new RenderEvent(partialTicks, matrixStack));
|
||||
ibaritone.getGameEventHandler().onRenderPass(new RenderEvent(partialTicks, modelViewMatrix, projectionMatrix));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public class SelCommand extends Command {
|
||||
float lineWidth = Baritone.settings().selectionLineWidth.value;
|
||||
boolean ignoreDepth = Baritone.settings().renderSelectionIgnoreDepth.value;
|
||||
IRenderer.startLines(color, opacity, lineWidth, ignoreDepth);
|
||||
IRenderer.drawAABB(event.getMatrixStack(), new AxisAlignedBB(pos1, pos1.add(1, 1, 1)));
|
||||
IRenderer.drawAABB(event.getModelViewStack(), new AxisAlignedBB(pos1, pos1.add(1, 1, 1)));
|
||||
IRenderer.endLines(ignoreDepth);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,6 +53,6 @@ public class SelectionRenderer implements IRenderer, AbstractGameEventListener {
|
||||
|
||||
@Override
|
||||
public void onRenderPass(RenderEvent event) {
|
||||
renderSelections(event.getMatrixStack(), manager.getSelections());
|
||||
renderSelections(event.getModelViewStack(), manager.getSelections());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,17 +27,16 @@ import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.renderer.Matrix4f;
|
||||
import net.minecraft.client.renderer.Vector4f;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.event.ClickEvent;
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
import java.awt.*;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.Collections;
|
||||
|
||||
import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX;
|
||||
@@ -45,11 +44,7 @@ import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
public class GuiClick extends Screen implements Helper {
|
||||
|
||||
// My name is Brady and I grant leijurv permission to use this pasted code
|
||||
private final FloatBuffer MODELVIEW = BufferUtils.createFloatBuffer(16);
|
||||
private final FloatBuffer PROJECTION = BufferUtils.createFloatBuffer(16);
|
||||
private final IntBuffer VIEWPORT = BufferUtils.createIntBuffer(16);
|
||||
private final FloatBuffer TO_WORLD_BUFFER = BufferUtils.createFloatBuffer(3);
|
||||
private Matrix4f projectionViewMatrix;
|
||||
|
||||
private BlockPos clickStart;
|
||||
private BlockPos currentMouseOver;
|
||||
@@ -115,15 +110,15 @@ public class GuiClick extends Screen implements Helper {
|
||||
return super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
|
||||
public void onRender(MatrixStack stack) {
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, (FloatBuffer) MODELVIEW.clear());
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, (FloatBuffer) PROJECTION.clear());
|
||||
glGetIntegerv(GL_VIEWPORT, (IntBuffer) VIEWPORT.clear());
|
||||
public void onRender(MatrixStack modelViewStack, MatrixStack projectionStack) {
|
||||
this.projectionViewMatrix = projectionStack.getLast().getPositionMatrix().copy();
|
||||
this.projectionViewMatrix.multiply(modelViewStack.getLast().getPositionMatrix());
|
||||
this.projectionViewMatrix.invert();
|
||||
|
||||
if (currentMouseOver != null) {
|
||||
Entity e = mc.getRenderViewEntity();
|
||||
// drawSingleSelectionBox WHEN?
|
||||
PathRenderer.drawManySelectionBoxes(stack, e, Collections.singletonList(currentMouseOver), Color.CYAN);
|
||||
PathRenderer.drawManySelectionBoxes(modelViewStack, e, Collections.singletonList(currentMouseOver), Color.CYAN);
|
||||
if (clickStart != null && !clickStart.equals(currentMouseOver)) {
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
@@ -134,7 +129,7 @@ public class GuiClick extends Screen implements Helper {
|
||||
RenderSystem.disableDepthTest();
|
||||
BetterBlockPos a = new BetterBlockPos(currentMouseOver);
|
||||
BetterBlockPos b = new BetterBlockPos(clickStart);
|
||||
IRenderer.drawAABB(stack, new AxisAlignedBB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1));
|
||||
IRenderer.drawAABB(modelViewStack, new AxisAlignedBB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1));
|
||||
RenderSystem.enableDepthTest();
|
||||
|
||||
RenderSystem.depthMask(true);
|
||||
@@ -145,194 +140,22 @@ public class GuiClick extends Screen implements Helper {
|
||||
}
|
||||
|
||||
private Vec3d toWorld(double x, double y, double z) {
|
||||
boolean result = gluUnProject((float) x, (float) y, (float) z, MODELVIEW, PROJECTION, VIEWPORT, (FloatBuffer) TO_WORLD_BUFFER.clear());
|
||||
if (result) {
|
||||
return new Vec3d(TO_WORLD_BUFFER.get(0), TO_WORLD_BUFFER.get(1), TO_WORLD_BUFFER.get(2));
|
||||
if (this.projectionViewMatrix == null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// skidded from lwjgl2 :ok_hand:
|
||||
// its uhhhhh mit license so its ok
|
||||
// here is the uhh license
|
||||
/*
|
||||
* Copyright (c) 2002-2007 Lightweight Java Game Library Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'Light Weight Java Game Library' nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
public static boolean gluUnProject(
|
||||
float winx,
|
||||
float winy,
|
||||
float winz,
|
||||
FloatBuffer modelMatrix,
|
||||
FloatBuffer projMatrix,
|
||||
IntBuffer viewport,
|
||||
FloatBuffer obj_pos) {
|
||||
FloatBuffer finalMatrix = BufferUtils.createFloatBuffer(16);
|
||||
float[] in = new float[4];
|
||||
float[] out = new float[4];
|
||||
|
||||
__gluMultMatricesf(modelMatrix, projMatrix, finalMatrix);
|
||||
|
||||
if (!__gluInvertMatrixf(finalMatrix, finalMatrix))
|
||||
return false;
|
||||
|
||||
in[0] = winx;
|
||||
in[1] = winy;
|
||||
in[2] = winz;
|
||||
in[3] = 1.0f;
|
||||
|
||||
// Map x and y from window coordinates
|
||||
in[0] = (in[0] - viewport.get(viewport.position() + 0)) / viewport.get(viewport.position() + 2);
|
||||
in[1] = (in[1] - viewport.get(viewport.position() + 1)) / viewport.get(viewport.position() + 3);
|
||||
|
||||
// Map to range -1 to 1
|
||||
in[0] = in[0] * 2 - 1;
|
||||
in[1] = in[1] * 2 - 1;
|
||||
in[2] = in[2] * 2 - 1;
|
||||
|
||||
__gluMultMatrixVecf(finalMatrix, in, out);
|
||||
|
||||
if (out[3] == 0.0)
|
||||
return false;
|
||||
|
||||
out[3] = 1.0f / out[3];
|
||||
|
||||
obj_pos.put(obj_pos.position() + 0, out[0] * out[3]);
|
||||
obj_pos.put(obj_pos.position() + 1, out[1] * out[3]);
|
||||
obj_pos.put(obj_pos.position() + 2, out[2] * out[3]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void __gluMultMatrixVecf(FloatBuffer m, float[] in, float[] out) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
out[i] =
|
||||
in[0] * m.get(m.position() + 0 * 4 + i)
|
||||
+ in[1] * m.get(m.position() + 1 * 4 + i)
|
||||
+ in[2] * m.get(m.position() + 2 * 4 + i)
|
||||
+ in[3] * m.get(m.position() + 3 * 4 + i);
|
||||
x /= mc.getMainWindow().getWidth();
|
||||
y /= mc.getMainWindow().getHeight();
|
||||
x = x * 2 - 1;
|
||||
y = y * 2 - 1;
|
||||
|
||||
Vector4f pos = new Vector4f((float) x, (float) y, (float) z, 1.0F);
|
||||
pos.transform(this.projectionViewMatrix);
|
||||
if (pos.getW() == 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void __gluMultMatricesf(FloatBuffer a, FloatBuffer b, FloatBuffer r) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
r.put(r.position() + i * 4 + j,
|
||||
a.get(a.position() + i * 4 + 0) * b.get(b.position() + 0 * 4 + j) + a.get(a.position() + i * 4 + 1) * b.get(b.position() + 1 * 4 + j) + a.get(a.position() + i * 4 + 2) * b.get(b.position() + 2 * 4 + j) + a.get(a.position() + i * 4 + 3) * b.get(b.position() + 3 * 4 + j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean __gluInvertMatrixf(FloatBuffer src, FloatBuffer inverse) {
|
||||
int i, j, k, swap;
|
||||
float t;
|
||||
FloatBuffer temp = BufferUtils.createFloatBuffer(16);
|
||||
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
temp.put(i, src.get(i + src.position()));
|
||||
}
|
||||
__gluMakeIdentityf(inverse);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/*
|
||||
* * Look for largest element in column
|
||||
*/
|
||||
swap = i;
|
||||
for (j = i + 1; j < 4; j++) {
|
||||
/*
|
||||
* if (fabs(temp[j][i]) > fabs(temp[i][i])) { swap = j;
|
||||
*/
|
||||
if (Math.abs(temp.get(j * 4 + i)) > Math.abs(temp.get(i * 4 + i))) {
|
||||
swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (swap != i) {
|
||||
/*
|
||||
* * Swap rows.
|
||||
*/
|
||||
for (k = 0; k < 4; k++) {
|
||||
t = temp.get(i * 4 + k);
|
||||
temp.put(i * 4 + k, temp.get(swap * 4 + k));
|
||||
temp.put(swap * 4 + k, t);
|
||||
|
||||
t = inverse.get(i * 4 + k);
|
||||
inverse.put(i * 4 + k, inverse.get(swap * 4 + k));
|
||||
//inverse.put((i << 2) + k, inverse.get((swap << 2) + k));
|
||||
inverse.put(swap * 4 + k, t);
|
||||
//inverse.put((swap << 2) + k, t);
|
||||
}
|
||||
}
|
||||
|
||||
if (temp.get(i * 4 + i) == 0) {
|
||||
/*
|
||||
* * No non-zero pivot. The matrix is singular, which shouldn't *
|
||||
* happen. This means the user gave us a bad matrix.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
t = temp.get(i * 4 + i);
|
||||
for (k = 0; k < 4; k++) {
|
||||
temp.put(i * 4 + k, temp.get(i * 4 + k) / t);
|
||||
inverse.put(i * 4 + k, inverse.get(i * 4 + k) / t);
|
||||
}
|
||||
for (j = 0; j < 4; j++) {
|
||||
if (j != i) {
|
||||
t = temp.get(j * 4 + i);
|
||||
for (k = 0; k < 4; k++) {
|
||||
temp.put(j * 4 + k, temp.get(j * 4 + k) - temp.get(i * 4 + k) * t);
|
||||
inverse.put(j * 4 + k, inverse.get(j * 4 + k) - inverse.get(i * 4 + k) * t);
|
||||
/*inverse.put(
|
||||
(j << 2) + k,
|
||||
inverse.get((j << 2) + k) - inverse.get((i << 2) + k) * t);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final float[] IDENTITY_MATRIX =
|
||||
new float[]{
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
private static void __gluMakeIdentityf(FloatBuffer m) {
|
||||
int oldPos = m.position();
|
||||
m.put(IDENTITY_MATRIX);
|
||||
m.position(oldPos);
|
||||
pos.perspectiveDivide();
|
||||
return new Vec3d(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public final class PathRenderer implements IRenderer, Helper {
|
||||
float partialTicks = event.getPartialTicks();
|
||||
Goal goal = behavior.getGoal();
|
||||
if (Helper.mc.currentScreen instanceof GuiClick) {
|
||||
((GuiClick) Helper.mc.currentScreen).onRender(event.getMatrixStack());
|
||||
((GuiClick) Helper.mc.currentScreen).onRender(event.getModelViewStack(), event.getProjectionStack());
|
||||
}
|
||||
|
||||
int thisPlayerDimension = behavior.baritone.getPlayerContext().world().getDimension().getType().getId();
|
||||
@@ -95,7 +95,7 @@ public final class PathRenderer implements IRenderer, Helper {
|
||||
}
|
||||
|
||||
if (goal != null && settings.renderGoal.value) {
|
||||
drawDankLitGoalBox(event.getMatrixStack(), renderView, goal, partialTicks, settings.colorGoalBox.value);
|
||||
drawDankLitGoalBox(event.getModelViewStack(), renderView, goal, partialTicks, settings.colorGoalBox.value);
|
||||
}
|
||||
|
||||
if (!settings.renderPath.value) {
|
||||
@@ -105,9 +105,9 @@ public final class PathRenderer implements IRenderer, Helper {
|
||||
PathExecutor current = behavior.getCurrent(); // this should prevent most race conditions?
|
||||
PathExecutor next = behavior.getNext(); // like, now it's not possible for current!=null to be true, then suddenly false because of another thread
|
||||
if (current != null && settings.renderSelectionBoxes.value) {
|
||||
drawManySelectionBoxes(event.getMatrixStack(), renderView, current.toBreak(), settings.colorBlocksToBreak.value);
|
||||
drawManySelectionBoxes(event.getMatrixStack(), renderView, current.toPlace(), settings.colorBlocksToPlace.value);
|
||||
drawManySelectionBoxes(event.getMatrixStack(), renderView, current.toWalkInto(), settings.colorBlocksToWalkInto.value);
|
||||
drawManySelectionBoxes(event.getModelViewStack(), renderView, current.toBreak(), settings.colorBlocksToBreak.value);
|
||||
drawManySelectionBoxes(event.getModelViewStack(), renderView, current.toPlace(), settings.colorBlocksToPlace.value);
|
||||
drawManySelectionBoxes(event.getModelViewStack(), renderView, current.toWalkInto(), settings.colorBlocksToWalkInto.value);
|
||||
}
|
||||
|
||||
//drawManySelectionBoxes(player, Collections.singletonList(behavior.pathStart()), partialTicks, Color.WHITE);
|
||||
@@ -115,22 +115,22 @@ public final class PathRenderer implements IRenderer, Helper {
|
||||
// Render the current path, if there is one
|
||||
if (current != null && current.getPath() != null) {
|
||||
int renderBegin = Math.max(current.getPosition() - 3, 0);
|
||||
drawPath(event.getMatrixStack(), current.getPath(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20);
|
||||
drawPath(event.getModelViewStack(), current.getPath(), renderBegin, settings.colorCurrentPath.value, settings.fadePath.value, 10, 20);
|
||||
}
|
||||
|
||||
if (next != null && next.getPath() != null) {
|
||||
drawPath(event.getMatrixStack(), next.getPath(), 0, settings.colorNextPath.value, settings.fadePath.value, 10, 20);
|
||||
drawPath(event.getModelViewStack(), next.getPath(), 0, settings.colorNextPath.value, settings.fadePath.value, 10, 20);
|
||||
}
|
||||
|
||||
// If there is a path calculation currently running, render the path calculation process
|
||||
behavior.getInProgress().ifPresent(currentlyRunning -> {
|
||||
currentlyRunning.bestPathSoFar().ifPresent(p -> {
|
||||
drawPath(event.getMatrixStack(), p, 0, settings.colorBestPathSoFar.value, settings.fadePath.value, 10, 20);
|
||||
drawPath(event.getModelViewStack(), p, 0, settings.colorBestPathSoFar.value, settings.fadePath.value, 10, 20);
|
||||
});
|
||||
|
||||
currentlyRunning.pathToMostRecentNodeConsidered().ifPresent(mr -> {
|
||||
drawPath(event.getMatrixStack(), mr, 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20);
|
||||
drawManySelectionBoxes(event.getMatrixStack(), renderView, Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value);
|
||||
drawPath(event.getModelViewStack(), mr, 0, settings.colorMostRecentConsidered.value, settings.fadePath.value, 10, 20);
|
||||
drawManySelectionBoxes(event.getModelViewStack(), renderView, Collections.singletonList(mr.getDest()), settings.colorMostRecentConsidered.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user