Compare commits
10
Commits
fbd0e23fb3
...
0.1.2-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd91f3844a | ||
|
|
b7cf3092da | ||
|
|
60afc12893 | ||
|
|
8677837a8f | ||
|
|
02f6cf7d71 | ||
|
|
85a46c0bba | ||
|
|
30f65bd914 | ||
|
|
bedb45c8aa | ||
|
|
1602520624 | ||
|
|
f69c379ae1 |
+1
-1
Submodule common updated: 15fa973553...ef0ead6bf8
+1
-1
@@ -6,7 +6,7 @@ org.gradle.caching=true
|
|||||||
# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349
|
# IntelliJ IDEA is not yet fully compatible with configuration cache, see: https://github.com/FabricMC/fabric-loom/issues/1349
|
||||||
org.gradle.configuration-cache=false
|
org.gradle.configuration-cache=false
|
||||||
|
|
||||||
mod_version=0.1.1-beta
|
mod_version=0.1.2-beta
|
||||||
|
|
||||||
# Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes)
|
# Saturn's emote library (git.selimaj.dev/saturnclientmc/emotes)
|
||||||
emotes_version=0.1.0
|
emotes_version=0.1.0
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
package org.saturnclient.impl.features.mixins.render;
|
package org.saturnclient.impl.features.mixins.render;
|
||||||
|
|
||||||
|
//? if >=1.21.6
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
//? if >=1.21.6
|
||||||
|
import net.minecraft.client.option.GameOptions;
|
||||||
import net.minecraft.client.render.GameRenderer;
|
import net.minecraft.client.render.GameRenderer;
|
||||||
|
|
||||||
|
//? if >=1.21.6
|
||||||
|
import org.saturnclient.impl.ui.SaturnScreenFabric;
|
||||||
import org.saturnclient.mod.mods.FreelookMod;
|
import org.saturnclient.mod.mods.FreelookMod;
|
||||||
import org.saturnclient.mod.mods.ZoomMod;
|
import org.saturnclient.mod.mods.ZoomMod;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
//? if >=1.21.6
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,4 +46,18 @@ public class GameRendererMixin {
|
|||||||
cir.setReturnValue(zoomedFov);
|
cir.setReturnValue(zoomedFov);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//? if >=1.21.6 {
|
||||||
|
// From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a
|
||||||
|
// Saturn screen is open, it's Saturn's background blur instead, faded in with the screen.
|
||||||
|
@Redirect(method = "render", at = @At(value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I"))
|
||||||
|
private int useSaturnBlur(GameOptions options) {
|
||||||
|
if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) {
|
||||||
|
return saturnScreen.screen.getBackgroundBlur();
|
||||||
|
}
|
||||||
|
|
||||||
|
return options.getMenuBackgroundBlurrinessValue();
|
||||||
|
}
|
||||||
|
//?}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,17 @@ package org.saturnclient.impl.provider;
|
|||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.toast.SystemToast;
|
import net.minecraft.client.toast.SystemToast;
|
||||||
|
//? if >=1.21.9 {
|
||||||
|
import net.minecraft.entity.player.SkinTextures;
|
||||||
|
//?} else
|
||||||
|
//import net.minecraft.client.util.SkinTextures;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
import org.saturnclient.common.provider.SaturnProvider;
|
import org.saturnclient.common.provider.SaturnProvider;
|
||||||
import org.saturnclient.common.ref.asset.IdentifierRef;
|
import org.saturnclient.common.ref.asset.IdentifierRef;
|
||||||
@@ -16,6 +22,9 @@ import org.saturnclient.impl.cosmetics.utils.IdentifierUtils;
|
|||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
|
|
||||||
public class SaturnProviderImpl implements SaturnProvider {
|
public class SaturnProviderImpl implements SaturnProvider {
|
||||||
|
/** Looks up the signed-in player's skin; made once, since the skin provider caches it */
|
||||||
|
private Supplier<Identifier> playerSkin;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MinecraftClientRef getClient() {
|
public MinecraftClientRef getClient() {
|
||||||
return (MinecraftClientRef) SaturnClient.client;
|
return (MinecraftClientRef) SaturnClient.client;
|
||||||
@@ -50,6 +59,23 @@ public class SaturnProviderImpl implements SaturnProvider {
|
|||||||
/** Shown longer than the vanilla default, since these ask the player to act. */
|
/** Shown longer than the vanilla default, since these ask the player to act. */
|
||||||
private static final SystemToast.Type WARNING_TOAST = new SystemToast.Type(15_000L);
|
private static final SystemToast.Type WARNING_TOAST = new SystemToast.Type(15_000L);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierRef getPlayerSkin() {
|
||||||
|
if (playerSkin == null) {
|
||||||
|
MinecraftClient client = MinecraftClient.getInstance();
|
||||||
|
// 1.21.9 moved SkinTextures and renamed the skin provider's lookup.
|
||||||
|
//? if >=1.21.9 {
|
||||||
|
Supplier<SkinTextures> textures = client.getSkinProvider().supplySkinTextures(client.getGameProfile(), false);
|
||||||
|
playerSkin = () -> textures.get().body().texturePath();
|
||||||
|
//?} else {
|
||||||
|
/*Supplier<SkinTextures> textures = client.getSkinProvider().getSkinTexturesSupplier(client.getGameProfile());
|
||||||
|
playerSkin = () -> textures.get().texture();
|
||||||
|
*///?}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (IdentifierRef) (Object) playerSkin.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getModVersion() {
|
public String getModVersion() {
|
||||||
return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID)
|
return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
import org.saturnclient.ui.ElementContext;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
import org.saturnclient.ui.RenderScope;
|
||||||
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
||||||
|
|
||||||
@@ -21,33 +20,32 @@ import net.minecraft.entity.LivingEntity;
|
|||||||
|
|
||||||
public class EntityDrawerImpl implements EntityDrawer {
|
public class EntityDrawerImpl implements EntityDrawer {
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderScope renderScope, boolean negativeAngle, float angle, float scale, ElementContext ctx) {
|
public boolean render(RenderScope renderScope, int width, int height, float yaw) {
|
||||||
LivingEntity entity = SaturnClient.client.player;
|
LivingEntity entity = SaturnClient.client.player;
|
||||||
|
|
||||||
if (entity != null) {
|
if (entity == null) {
|
||||||
drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale,
|
return false;
|
||||||
SaturnClient.client.player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The player is about 1.8 blocks tall; leave some room above and below.
|
||||||
|
drawEntity(renderScope, 0, 0, width, height, (int) (height / 2.2F), 0.0625F, yaw, entity);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draws the entity in the box, turned by yaw degrees (0 faces the viewer) with its head straight.
|
||||||
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float scale,
|
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float scale,
|
||||||
float mouseX, float mouseY, LivingEntity entity) {
|
float yaw, LivingEntity entity) {
|
||||||
float f = (float) (x1 + x2) / 2.0F;
|
|
||||||
float g = (float) (y1 + y2) / 2.0F;
|
|
||||||
renderScope.enableScissor(x1, y1, x2, y2);
|
renderScope.enableScissor(x1, y1, x2, y2);
|
||||||
float h = (float) Math.atan((double) ((f - mouseX) / 40.0F));
|
|
||||||
float i = (float) Math.atan((double) ((g - mouseY) / 40.0F));
|
|
||||||
Quaternionf quaternionf = (new Quaternionf()).rotateZ((float) Math.PI);
|
Quaternionf quaternionf = (new Quaternionf()).rotateZ((float) Math.PI);
|
||||||
Quaternionf quaternionf2 = (new Quaternionf()).rotateX(i * 20.0F * ((float) Math.PI / 180F));
|
Quaternionf quaternionf2 = new Quaternionf();
|
||||||
quaternionf.mul(quaternionf2);
|
|
||||||
float j = entity.bodyYaw;
|
float j = entity.bodyYaw;
|
||||||
float k = entity.getYaw();
|
float k = entity.getYaw();
|
||||||
float l = entity.getPitch();
|
float l = entity.getPitch();
|
||||||
float m = entity.lastHeadYaw;
|
float m = entity.lastHeadYaw;
|
||||||
float n = entity.headYaw;
|
float n = entity.headYaw;
|
||||||
entity.bodyYaw = 180.0F + h * 20.0F;
|
entity.bodyYaw = 180.0F + yaw;
|
||||||
entity.setYaw(180.0F + h * 40.0F);
|
entity.setYaw(180.0F + yaw);
|
||||||
entity.setPitch(-i * 20.0F);
|
entity.setPitch(0.0F);
|
||||||
entity.headYaw = entity.getYaw();
|
entity.headYaw = entity.getYaw();
|
||||||
entity.lastHeadYaw = entity.getYaw();
|
entity.lastHeadYaw = entity.getYaw();
|
||||||
float o = entity.getScale();
|
float o = entity.getScale();
|
||||||
@@ -83,7 +81,6 @@ public class EntityDrawerImpl implements EntityDrawer {
|
|||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.saturnclient.saturnclient.SaturnClient;
|
import org.saturnclient.saturnclient.SaturnClient;
|
||||||
import org.saturnclient.ui.ElementContext;
|
|
||||||
import org.saturnclient.ui.RenderScope;
|
import org.saturnclient.ui.RenderScope;
|
||||||
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
import org.saturnclient.ui.components.SkinPreview.EntityDrawer;
|
||||||
|
|
||||||
@@ -92,14 +89,12 @@ import net.minecraft.client.render.entity.EntityRenderDispatcher;
|
|||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
|
||||||
public class EntityDrawerImpl implements EntityDrawer {
|
public class EntityDrawerImpl implements EntityDrawer {
|
||||||
|
// Draws the entity in the box, turned by yaw degrees (0 faces the viewer) with its head straight.
|
||||||
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float f,
|
public static void drawEntity(RenderScope renderScope, int x1, int y1, int x2, int y2, int size, float f,
|
||||||
float mouseX,
|
float yaw, LivingEntity entity) {
|
||||||
float mouseY, LivingEntity entity, float angle, boolean negativeAngle) {
|
|
||||||
float g = (float) (x1 + x2) / 2.0F;
|
float g = (float) (x1 + x2) / 2.0F;
|
||||||
float h = (float) (y1 + y2) / 2.0F;
|
float h = (float) (y1 + y2) / 2.0F;
|
||||||
renderScope.enableScissor(0, 6, x2, y2);
|
renderScope.enableScissor(x1, y1, x2, y2);
|
||||||
float i = (float) Math.atan((double) ((g - mouseX) / 40.0F));
|
|
||||||
float j = (float) Math.atan((double) ((h - mouseY) / 40.0F));
|
|
||||||
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
|
Quaternionf quaternionf = (new Quaternionf()).rotateZ(3.1415927F);
|
||||||
float k = entity.bodyYaw;
|
float k = entity.bodyYaw;
|
||||||
float l = entity.getYaw();
|
float l = entity.getYaw();
|
||||||
@@ -109,10 +104,10 @@ public class EntityDrawerImpl implements EntityDrawer {
|
|||||||
//? if <1.21.5
|
//? if <1.21.5
|
||||||
//float n = entity.prevHeadYaw;
|
//float n = entity.prevHeadYaw;
|
||||||
float o = entity.headYaw;
|
float o = entity.headYaw;
|
||||||
entity.setYaw(angle);
|
entity.setYaw(180.0F + yaw);
|
||||||
entity.bodyYaw = angle;
|
entity.bodyYaw = 180.0F + yaw;
|
||||||
entity.setPitch(-(j * 20.0F));
|
entity.setPitch(0.0F);
|
||||||
entity.headYaw = negativeAngle ? (i * 40.0F) + 180f : -(i * 40.0F);
|
entity.headYaw = entity.getYaw();
|
||||||
//? if >=1.21.5
|
//? if >=1.21.5
|
||||||
entity.lastHeadYaw = entity.getHeadYaw();
|
entity.lastHeadYaw = entity.getHeadYaw();
|
||||||
//? if <1.21.5
|
//? if <1.21.5
|
||||||
@@ -166,12 +161,15 @@ public class EntityDrawerImpl implements EntityDrawer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(RenderScope renderScope, boolean negativeAngle, float angle, float scale, ElementContext ctx) {
|
public boolean render(RenderScope renderScope, int width, int height, float yaw) {
|
||||||
LivingEntity entity = SaturnClient.client.player;
|
LivingEntity entity = SaturnClient.client.player;
|
||||||
if (entity != null) {
|
if (entity == null) {
|
||||||
drawEntity(renderScope, 0, 0, 75, 78, 30, 0.0625F, ctx.mouseX / scale, ctx.mouseY / scale, entity, angle,
|
return false;
|
||||||
negativeAngle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The player is about 1.8 blocks tall; leave some room above and below.
|
||||||
|
drawEntity(renderScope, 0, 0, width, height, (int) (height / 2.2F), 0.0625F, yaw, entity);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*///?}
|
*///?}
|
||||||
|
|||||||
@@ -89,25 +89,18 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
|
|||||||
mouseX *= 2;
|
mouseX *= 2;
|
||||||
mouseY *= 2;
|
mouseY *= 2;
|
||||||
|
|
||||||
// From 1.21.6 the panorama renderer has no opacity (and takes no delta), so it's drawn fully
|
// From 1.21.6 the background, blur included, is drawn in renderBackground.
|
||||||
// opaque there.
|
//? if <1.21.6 {
|
||||||
if (panoramaReady && client.world == null && client.getCurrentServerEntry() == null) {
|
/*if (isShowingPanorama()) {
|
||||||
//? if >=1.21.6 {
|
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, screen.backgroundOpacity, delta);
|
||||||
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, true);
|
|
||||||
//?} else
|
|
||||||
//ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, screen.backgroundOpacity, delta);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: the background blur (screen.backgroundBlur) is only drawn before 1.21.6 so far. See
|
PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
|
||||||
// also issue #23.
|
|
||||||
|
|
||||||
//? if <1.21.6 {
|
|
||||||
/*PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect(
|
|
||||||
Identifier.ofVanilla("blur"),
|
Identifier.ofVanilla("blur"),
|
||||||
DefaultFramebufferSet.MAIN_ONLY);
|
DefaultFramebufferSet.MAIN_ONLY);
|
||||||
|
|
||||||
if (postEffectProcessor != null) {
|
if (postEffectProcessor != null) {
|
||||||
float radius = screen.backgroundBlur * Math.min((float) elapsed / 700, 1.0f);
|
float radius = screen.getBackgroundBlur();
|
||||||
//? if >=1.21.5
|
//? if >=1.21.5
|
||||||
postEffectProcessor.render(this.client.getFramebuffer(), this.pool, pass -> pass.setUniform("Radius", radius));
|
postEffectProcessor.render(this.client.getFramebuffer(), this.pool, pass -> pass.setUniform("Radius", radius));
|
||||||
//? if <1.21.5
|
//? if <1.21.5
|
||||||
@@ -122,6 +115,32 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider {
|
|||||||
screen.render(renderScope, mouseX, mouseY, delta, elapsed);
|
screen.render(renderScope, mouseX, mouseY, delta, elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//? if >=1.21.6 {
|
||||||
|
// Vanilla's background would cover Saturn's panorama, and blurs by the vanilla setting. The
|
||||||
|
// strength of this blur comes from GameRendererMixin, which gives Saturn's setting instead.
|
||||||
|
@Override
|
||||||
|
public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||||
|
// The panorama renderer has no opacity (and takes no delta) from 1.21.6, so it's drawn fully
|
||||||
|
// opaque.
|
||||||
|
if (isShowingPanorama()) {
|
||||||
|
ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screen.getBackgroundBlur() >= 1) {
|
||||||
|
context.applyBlur();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client.world != null) {
|
||||||
|
renderDarkening(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//?}
|
||||||
|
|
||||||
|
/** Whether Saturn's panorama is behind the screen: outside a world, once it's loaded. */
|
||||||
|
private boolean isShowingPanorama() {
|
||||||
|
return panoramaReady && client.world == null && client.getCurrentServerEntry() == null;
|
||||||
|
}
|
||||||
|
|
||||||
// 1.21.9 moved mouse and key events to Click and KeyInput.
|
// 1.21.9 moved mouse and key events to Click and KeyInput.
|
||||||
//? if >=1.21.9 {
|
//? if >=1.21.9 {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.saturnclient.saturnclient.SaturnClient;
|
|||||||
import org.saturnclient.common.provider.Providers;
|
import org.saturnclient.common.provider.Providers;
|
||||||
import org.saturnclient.client.ServiceClient;
|
import org.saturnclient.client.ServiceClient;
|
||||||
import org.saturnclient.emotes.fabric.Emotes;
|
import org.saturnclient.emotes.fabric.Emotes;
|
||||||
|
import org.saturnclient.impl.ui.SaturnScreenFabric;
|
||||||
import org.saturnclient.ui.screens.EmoteWheel;
|
import org.saturnclient.ui.screens.EmoteWheel;
|
||||||
import org.saturnclient.ui.screens.ShiftMenu;
|
import org.saturnclient.ui.screens.ShiftMenu;
|
||||||
|
|
||||||
@@ -51,12 +52,20 @@ public class KeyInputHandler {
|
|||||||
Providers.saturn.getClient().setScreen(new ShiftMenu());
|
Providers.saturn.getClient().setScreen(new ShiftMenu());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.openEmoteWheel.wasKeyPressed() && client.currentScreen == null
|
// The emote wheel key opens the wheel, and closes it again while it's open. Closing it
|
||||||
|
// from the screen's own key handling would reopen it on the next tick, since the key is
|
||||||
|
// still down and no screen is open by then.
|
||||||
|
if (Config.openEmoteWheel.wasKeyPressed()
|
||||||
//? if >=1.21.9 {
|
//? if >=1.21.9 {
|
||||||
&& !InputUtil.isKeyPressed(client.getWindow(), GLFW.GLFW_KEY_F3)) {
|
&& !InputUtil.isKeyPressed(client.getWindow(), GLFW.GLFW_KEY_F3)) {
|
||||||
//?} else
|
//?} else
|
||||||
//&& !InputUtil.isKeyPressed(client.getWindow().getHandle(), GLFW.GLFW_KEY_F3)) {
|
//&& !InputUtil.isKeyPressed(client.getWindow().getHandle(), GLFW.GLFW_KEY_F3)) {
|
||||||
|
if (client.currentScreen == null) {
|
||||||
Providers.saturn.getClient().setScreen(new EmoteWheel());
|
Providers.saturn.getClient().setScreen(new EmoteWheel());
|
||||||
|
} else if (client.currentScreen instanceof SaturnScreenFabric saturnScreen
|
||||||
|
&& saturnScreen.screen instanceof EmoteWheel) {
|
||||||
|
saturnScreen.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sneaking cancels your own emote, for everyone.
|
// Sneaking cancels your own emote, for everyone.
|
||||||
|
|||||||
Reference in New Issue
Block a user