mirror of
https://github.com/ViaVersion/ViaFabricPlus.git
synced 2024-12-21 16:38:39 +01:00
Initial 1.21 update (#445)
This commit is contained in:
parent
72d575797d
commit
3d48023e20
@ -61,7 +61,7 @@ ViaFabricPlus uses Gradle, to make sure that it is installed properly you can ch
|
||||
From experience, most changes are related to either movement or networking,
|
||||
packages like `gametest` or `server` can be skipped usually when updating. It's important to always diff code inside
|
||||
the `net.minecraft.client` package as well as `net.minecraft.world` package, as these are the most likely to contain changes. (Mojang mappings)
|
||||
7. Update protocol constants in the `VFPProtocol` class
|
||||
7. Update protocol constants in the `ViaFabricPlusProtocol` class
|
||||
8. Check the ViaVersion/upstream protocol implementation for issues and report them if necessary or if these issues can't be fixed,
|
||||
without tons of work, implement a workaround in ViaFabricPlus.
|
||||
9. Run the game and check all GUIs and other visuals for issues.
|
||||
|
@ -3,13 +3,13 @@ org.gradle.jvmargs=-Xmx8G
|
||||
org.gradle.parallel=true
|
||||
|
||||
# Minecraft/Fabric
|
||||
minecraft_version=1.20.6
|
||||
yarn_mappings=1.20.6+build.1
|
||||
loader_version=0.15.10
|
||||
fabric_api_version=0.100.0+1.20.6
|
||||
minecraft_version=1.21
|
||||
yarn_mappings=1.21+build.1
|
||||
loader_version=0.15.11
|
||||
fabric_api_version=0.100.1+1.21
|
||||
|
||||
# Project Details
|
||||
mod_version=3.3.2
|
||||
mod_version=3.4.0-SNAPSHOT
|
||||
maven_group=de.florianmichael
|
||||
archives_base_name=ViaFabricPlus
|
||||
|
||||
@ -31,5 +31,5 @@ reflect_version=1.3.2
|
||||
mcping_version=1.4.0
|
||||
|
||||
# Misc Libraries
|
||||
mod_menu_version=9.0.0
|
||||
mod_menu_version=11.0.0-beta.1
|
||||
classic4j_version=2.0.2
|
||||
|
@ -40,6 +40,9 @@ import java.util.concurrent.CompletableFuture;
|
||||
* - Entity attachment calculation got changed completely
|
||||
* - Particle handling has slightly changed
|
||||
*
|
||||
* TODO | Port 1.21
|
||||
* - Fix MixinMouseOptionsScreen
|
||||
*
|
||||
* TODO | General
|
||||
* - Make recipe fixes dynamic instead of a data dump in java classes
|
||||
* - Window interactions in <= 1.16.5 has changed and can be detected by the server
|
||||
|
@ -48,6 +48,10 @@ public class ItemRegistryDiff {
|
||||
public static final List<Item> EXTENDED_CLASSIC_ITEMS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ITEM_DIFF.put(MUSIC_DISC_CREATOR_MUSIC_BOX, andNewer(v1_21));
|
||||
ITEM_DIFF.put(MUSIC_DISC_CREATOR, andNewer(v1_21));
|
||||
ITEM_DIFF.put(MUSIC_DISC_PRECIPICE, andNewer(v1_21));
|
||||
|
||||
ITEM_DIFF.put(TUFF_SLAB, andNewer(v1_20_5));
|
||||
ITEM_DIFF.put(TUFF_STAIRS, andNewer(v1_20_5));
|
||||
ITEM_DIFF.put(TUFF_WALL, andNewer(v1_20_5));
|
||||
|
@ -23,7 +23,6 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.BannerPatternsComponent;
|
||||
import net.minecraft.inventory.RecipeInputInventory;
|
||||
import net.minecraft.item.BannerItem;
|
||||
import net.minecraft.item.DyeItem;
|
||||
import net.minecraft.item.Item;
|
||||
@ -32,6 +31,7 @@ import net.minecraft.recipe.RecipeSerializer;
|
||||
import net.minecraft.recipe.SpecialCraftingRecipe;
|
||||
import net.minecraft.recipe.SpecialRecipeSerializer;
|
||||
import net.minecraft.recipe.book.CraftingRecipeCategory;
|
||||
import net.minecraft.recipe.input.CraftingRecipeInput;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.util.DyeColor;
|
||||
@ -46,10 +46,10 @@ public class AddBannerPatternRecipe extends SpecialCraftingRecipe {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(RecipeInputInventory inv, World world) {
|
||||
public boolean matches(CraftingRecipeInput inv, World world) {
|
||||
boolean foundBanner = false;
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
ItemStack stack = inv.getStack(i);
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
ItemStack stack = inv.getStackInSlot(i);
|
||||
if (stack.getItem() instanceof BannerItem) {
|
||||
if (foundBanner)
|
||||
return false;
|
||||
@ -62,11 +62,11 @@ public class AddBannerPatternRecipe extends SpecialCraftingRecipe {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack craft(RecipeInputInventory inv, RegistryWrapper.WrapperLookup lookup) {
|
||||
public ItemStack craft(CraftingRecipeInput inv, RegistryWrapper.WrapperLookup lookup) {
|
||||
ItemStack result = ItemStack.EMPTY;
|
||||
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
ItemStack stack = inv.getStack(i);
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
ItemStack stack = inv.getStackInSlot(i);
|
||||
if (!stack.isEmpty() && stack.getItem() instanceof BannerItem) {
|
||||
result = stack.copy();
|
||||
result.setCount(1);
|
||||
@ -78,8 +78,8 @@ public class AddBannerPatternRecipe extends SpecialCraftingRecipe {
|
||||
if (pattern != null) {
|
||||
final var patternKey = lookup.getWrapperOrThrow(RegistryKeys.BANNER_PATTERN).getOrThrow(pattern.getKey());
|
||||
DyeColor color = ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_12_2) ? DyeColor.BLACK : DyeColor.WHITE;
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
Item item = inv.getStack(i).getItem();
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
Item item = inv.getStackInSlot(i).getItem();
|
||||
if (item instanceof DyeItem dyeItem) {
|
||||
color = dyeItem.getColor();
|
||||
}
|
||||
@ -106,7 +106,7 @@ public class AddBannerPatternRecipe extends SpecialCraftingRecipe {
|
||||
return SERIALIZER;
|
||||
}
|
||||
|
||||
private static BannerPattern_1_13_2 getBannerPattern(RecipeInputInventory inv) {
|
||||
private static BannerPattern_1_13_2 getBannerPattern(CraftingRecipeInput inv) {
|
||||
for (BannerPattern_1_13_2 pattern : BannerPattern_1_13_2.values()) {
|
||||
if (!pattern.isCraftable())
|
||||
continue;
|
||||
@ -115,8 +115,8 @@ public class AddBannerPatternRecipe extends SpecialCraftingRecipe {
|
||||
if (pattern.hasBaseStack()) {
|
||||
boolean foundBaseItem = false;
|
||||
boolean foundDye = false;
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
ItemStack stack = inv.getStack(i);
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
ItemStack stack = inv.getStackInSlot(i);
|
||||
if (!stack.isEmpty() && !(stack.getItem() instanceof BannerItem)) {
|
||||
if (stack.getItem() instanceof DyeItem) {
|
||||
if (foundDye) {
|
||||
@ -134,12 +134,12 @@ public class AddBannerPatternRecipe extends SpecialCraftingRecipe {
|
||||
}
|
||||
}
|
||||
if (!foundBaseItem || (!foundDye && ProtocolTranslator.getTargetVersion().newerThan(ProtocolVersion.v1_10))) matches = false;
|
||||
} else if (inv.size() == pattern.getRecipePattern().length * pattern.getRecipePattern()[0].length()) {
|
||||
} else if (inv.getSize() == pattern.getRecipePattern().length * pattern.getRecipePattern()[0].length()) {
|
||||
DyeColor patternColor = null;
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
int row = i / 3;
|
||||
int col = i % 3;
|
||||
ItemStack stack = inv.getStack(i);
|
||||
ItemStack stack = inv.getStackInSlot(i);
|
||||
Item item = stack.getItem();
|
||||
if (!stack.isEmpty() && !(item instanceof BannerItem)) {
|
||||
if (!(item instanceof DyeItem)) {
|
||||
|
@ -708,10 +708,11 @@ public class Recipes1_11_2 {
|
||||
public static void setCraftingResultSlot(final int syncId, final ScreenHandler screenHandler, final RecipeInputInventory inventory) {
|
||||
final var network = MinecraftClient.getInstance().getNetworkHandler();
|
||||
final var world = MinecraftClient.getInstance().world;
|
||||
final var craftingRecipeInput = inventory.createRecipeInput();
|
||||
|
||||
final var result = network.getRecipeManager()
|
||||
.getFirstMatch(RecipeType.CRAFTING, inventory, world) // Get the first matching recipe
|
||||
.map(recipe -> recipe.value().craft(inventory, network.getRegistryManager())) // Craft the recipe to get the result
|
||||
.getFirstMatch(RecipeType.CRAFTING, craftingRecipeInput, world) // Get the first matching recipe
|
||||
.map(recipe -> recipe.value().craft(craftingRecipeInput, network.getRegistryManager())) // Craft the recipe to get the result
|
||||
.orElse(ItemStack.EMPTY); // If there is no recipe, set the result to air
|
||||
|
||||
// Update the result slot
|
||||
|
@ -22,6 +22,7 @@ package de.florianmichael.viafabricplus.fixes.versioned;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.Enchantments;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -29,7 +30,7 @@ import java.util.Optional;
|
||||
|
||||
public class Enchantments1_14_4 {
|
||||
|
||||
private static final Map<String, Enchantment> ENCHANTMENT_REGISTRY = new HashMap<>();
|
||||
private static final Map<String, RegistryKey<Enchantment>> ENCHANTMENT_REGISTRY = new HashMap<>();
|
||||
|
||||
static {
|
||||
ENCHANTMENT_REGISTRY.put("protection", Enchantments.PROTECTION);
|
||||
@ -71,7 +72,7 @@ public class Enchantments1_14_4 {
|
||||
ENCHANTMENT_REGISTRY.put("vanishing_curse", Enchantments.VANISHING_CURSE);
|
||||
}
|
||||
|
||||
public static Optional<Enchantment> getOrEmpty(final String identifier) {
|
||||
public static Optional<RegistryKey<Enchantment>> getOrEmpty(final String identifier) {
|
||||
if (identifier == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import net.minecraft.util.Identifier;
|
||||
*/
|
||||
public class BoatModel1_8 extends CompositeEntityModel<BoatEntity> {
|
||||
|
||||
public static final EntityModelLayer MODEL_LAYER = new EntityModelLayer(new Identifier("viafabricplus", "boat1_8"), "main");
|
||||
public static final EntityModelLayer MODEL_LAYER = new EntityModelLayer(Identifier.of("viafabricplus", "boat1_8"), "main");
|
||||
private final ImmutableList<ModelPart> parts;
|
||||
|
||||
public BoatModel1_8(ModelPart root) {
|
||||
|
@ -35,7 +35,7 @@ import net.minecraft.util.math.RotationAxis;
|
||||
*/
|
||||
public class BoatRenderer1_8 extends EntityRenderer<BoatEntity> {
|
||||
|
||||
private static final Identifier TEXTURE = new Identifier("viafabricplus", "textures/boat1_8.png");
|
||||
private static final Identifier TEXTURE = Identifier.of("viafabricplus", "textures/boat1_8.png");
|
||||
private final BoatModel1_8 model;
|
||||
|
||||
public BoatRenderer1_8(EntityRendererFactory.Context ctx) {
|
||||
@ -68,7 +68,7 @@ public class BoatRenderer1_8 extends EntityRenderer<BoatEntity> {
|
||||
matrices.scale(-1, -1, 1);
|
||||
model.setAngles(entity, tickDelta, 0, -0.1f, 0, 0);
|
||||
VertexConsumer vertexConsumer = vertexConsumers.getBuffer(model.getLayer(TEXTURE));
|
||||
model.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
|
||||
model.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV);
|
||||
|
||||
matrices.pop();
|
||||
super.render(entity, yaw, tickDelta, matrices, vertexConsumers, light);
|
||||
|
@ -36,7 +36,7 @@ import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class FootStepParticle1_12_2 extends SpriteBillboardParticle {
|
||||
|
||||
public static final Identifier ID = new Identifier("viafabricplus", "footstep");
|
||||
public static final Identifier ID = Identifier.of("viafabricplus", "footstep");
|
||||
public static int RAW_ID;
|
||||
|
||||
protected FootStepParticle1_12_2(ClientWorld clientWorld, double x, double y, double z) {
|
||||
@ -72,10 +72,10 @@ public class FootStepParticle1_12_2 extends SpriteBillboardParticle {
|
||||
final float maxV = this.getMaxV();
|
||||
|
||||
final int light = this.getBrightness(tickDelta); // This is missing in the original code, that's why the particles are broken
|
||||
vertexConsumer.vertex(x - scale, y, z + scale).texture(maxU, maxV).color(this.red, this.green, this.blue, this.alpha).light(light).next();
|
||||
vertexConsumer.vertex(x + scale, y, z + scale).texture(maxU, minV).color(this.red, this.green, this.blue, this.alpha).light(light).next();
|
||||
vertexConsumer.vertex(x + scale, y, z - scale).texture(minU, minV).color(this.red, this.green, this.blue, this.alpha).light(light).next();
|
||||
vertexConsumer.vertex(x - scale, y, z - scale).texture(minU, maxV).color(this.red, this.green, this.blue, this.alpha).light(light).next();
|
||||
vertexConsumer.vertex(x - scale, y, z + scale).texture(maxU, maxV).color(this.red, this.green, this.blue, this.alpha).light(light);
|
||||
vertexConsumer.vertex(x + scale, y, z + scale).texture(maxU, minV).color(this.red, this.green, this.blue, this.alpha).light(light);
|
||||
vertexConsumer.vertex(x + scale, y, z - scale).texture(minU, minV).color(this.red, this.green, this.blue, this.alpha).light(light);
|
||||
vertexConsumer.vertex(x - scale, y, z - scale).texture(minU, maxV).color(this.red, this.green, this.blue, this.alpha).light(light);
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
@ -27,8 +27,8 @@ import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
|
||||
import com.viaversion.viaversion.api.protocol.packet.State;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPackets1_20_5;
|
||||
import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ServerboundPackets1_20_5;
|
||||
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import net.minecraft.network.packet.BrandCustomPayload;
|
||||
@ -58,7 +58,8 @@ public class ViaFabricPlusProtocol extends AbstractSimpleProtocol {
|
||||
wrapper.passthrough(Types.STRING);
|
||||
wrapper.passthrough(Types.INT);
|
||||
});
|
||||
registerMapping(DebugGameTestClearCustomPayload.ID, ProtocolVersion.v1_14, wrapper -> {});
|
||||
registerMapping(DebugGameTestClearCustomPayload.ID, ProtocolVersion.v1_14, wrapper -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -122,7 +123,7 @@ public class ViaFabricPlusProtocol extends AbstractSimpleProtocol {
|
||||
}
|
||||
|
||||
public static ClientboundPacketType getCustomPayload() {
|
||||
return ClientboundPackets1_20_5.CUSTOM_PAYLOAD;
|
||||
return ClientboundPackets1_21.CUSTOM_PAYLOAD;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
@ -201,7 +201,7 @@ public abstract class MixinLivingEntity extends Entity {
|
||||
}
|
||||
|
||||
@Inject(method = "getPreferredEquipmentSlot", at = @At("HEAD"), cancellable = true)
|
||||
private static void removeShieldSlotPreference(ItemStack stack, CallbackInfoReturnable<EquipmentSlot> cir) {
|
||||
private void removeShieldSlotPreference(ItemStack stack, CallbackInfoReturnable<EquipmentSlot> cir) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_9_3) && stack.isOf(Items.SHIELD)) {
|
||||
cir.setReturnValue(EquipmentSlot.MAINHAND);
|
||||
}
|
||||
|
@ -26,8 +26,11 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import de.florianmichael.viafabricplus.settings.impl.VisualSettings;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.enchantment.Enchantments;
|
||||
import net.minecraft.entity.*;
|
||||
import net.minecraft.entity.attribute.EntityAttribute;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.effect.StatusEffect;
|
||||
import net.minecraft.entity.effect.StatusEffects;
|
||||
@ -37,6 +40,7 @@ import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.item.ElytraItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.Hand;
|
||||
@ -51,6 +55,8 @@ import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class MixinPlayerEntity extends LivingEntity {
|
||||
|
||||
@ -69,7 +75,7 @@ public abstract class MixinPlayerEntity extends LivingEntity {
|
||||
private static final EntityDimensions viaFabricPlus$sneaking_dimensions_v1_13_2 = EntityDimensions.changing(0.6F, 1.65F).withEyeHeight(1.54F);
|
||||
|
||||
@Unique
|
||||
private static final SoundEvent viaFabricPlus$oof_hurt = SoundEvent.of(new Identifier("viafabricplus", "oof.hurt"));
|
||||
private static final SoundEvent viaFabricPlus$oof_hurt = SoundEvent.of(Identifier.of("viafabricplus", "oof.hurt"));
|
||||
|
||||
@Unique
|
||||
public boolean viaFabricPlus$isSprinting;
|
||||
@ -78,7 +84,7 @@ public abstract class MixinPlayerEntity extends LivingEntity {
|
||||
super(entityType, world);
|
||||
}
|
||||
|
||||
@ModifyConstant(method = "method_59818", constant = @Constant(doubleValue = 9.999999747378752E-6 /* 1.0E-5F */))
|
||||
@ModifyConstant(method = "isSpaceAroundPlayerEmpty", constant = @Constant(doubleValue = 9.999999747378752E-6 /* 1.0E-5F */))
|
||||
private double removeOffsetWhenCheckingSneakingCollision(double constant) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_20_3)) {
|
||||
return 0;
|
||||
@ -186,9 +192,23 @@ public abstract class MixinPlayerEntity extends LivingEntity {
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "getBlockBreakingSpeed", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;getAttributeValue(Lnet/minecraft/registry/entry/RegistryEntry;)D", ordinal = 0))
|
||||
private double getSpeedBasedOnEfficiency(PlayerEntity instance, RegistryEntry<EntityAttribute> attribute) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_20_5)) {
|
||||
final int effLevel = this.viaFabricPlus$getEfficiencyLevel();
|
||||
if (effLevel > 0 && !this.getMainHandStack().isEmpty()) {
|
||||
return effLevel * effLevel + 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return instance.getAttributeValue(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getBlockBreakingSpeed", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/effect/StatusEffectUtil;hasHaste(Lnet/minecraft/entity/LivingEntity;)Z", shift = At.Shift.BEFORE))
|
||||
private void changeSpeedCalculation(BlockState block, CallbackInfoReturnable<Float> cir, @Local LocalFloatRef f) {
|
||||
final int efficiency = EnchantmentHelper.getEfficiency(this);
|
||||
final int efficiency = this.viaFabricPlus$getEfficiencyLevel();
|
||||
if (efficiency <= 0) return;
|
||||
|
||||
final float speed = this.inventory.getBlockBreakingSpeed(block);
|
||||
@ -219,4 +239,10 @@ public abstract class MixinPlayerEntity extends LivingEntity {
|
||||
return hasMiningFatigue;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int viaFabricPlus$getEfficiencyLevel() {
|
||||
final Optional<RegistryEntry.Reference<Enchantment>> enchantment = this.getWorld().getRegistryManager().getWrapperOrThrow(RegistryKeys.ENCHANTMENT).getOptional(Enchantments.EFFICIENCY);
|
||||
return enchantment.map(e -> EnchantmentHelper.getEquipmentLevel(e, this)).orElse(-1);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.entity;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import net.minecraft.entity.passive.SquidEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@ -31,8 +30,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
@Mixin(SquidEntity.class)
|
||||
public abstract class MixinSquidEntity {
|
||||
|
||||
@Inject(method = "canBeLeashedBy", at = @At("HEAD"), cancellable = true)
|
||||
private void cancelLeashing(PlayerEntity player, CallbackInfoReturnable<Boolean> cir) {
|
||||
@Inject(method = "canBeLeashed", at = @At("HEAD"), cancellable = true)
|
||||
private void cancelLeashing(CallbackInfoReturnable<Boolean> cir) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_16_4)) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.item;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Equipment;
|
||||
import net.minecraft.item.Item;
|
||||
@ -48,7 +47,7 @@ public interface MixinEquipment {
|
||||
private void cancelArmorSwap(Item item, World world, PlayerEntity user, Hand hand, CallbackInfoReturnable<TypedActionResult<ItemStack>> cir) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_19_3)) {
|
||||
final ItemStack heldItem = user.getStackInHand(hand);
|
||||
final EquipmentSlot targetSlot = MobEntity.getPreferredEquipmentSlot(heldItem);
|
||||
final EquipmentSlot targetSlot = user.getPreferredEquipmentSlot(heldItem);
|
||||
final ItemStack targetItem = user.getEquippedStack(targetSlot);
|
||||
|
||||
if (!targetItem.isEmpty()) {
|
||||
|
@ -23,21 +23,23 @@ import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.fixes.versioned.Enchantments1_14_4;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import de.florianmichael.viafabricplus.util.ItemUtil;
|
||||
import net.minecraft.client.item.TooltipType;
|
||||
import net.minecraft.component.DataComponentType;
|
||||
import net.minecraft.component.ComponentType;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.entity.attribute.EntityAttribute;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.TooltipAppender;
|
||||
import net.minecraft.item.tooltip.TooltipAppender;
|
||||
import net.minecraft.item.tooltip.TooltipType;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
@ -57,7 +59,7 @@ public abstract class MixinItemStack {
|
||||
public abstract Item getItem();
|
||||
|
||||
@Inject(method = "appendTooltip", at = @At("HEAD"), cancellable = true)
|
||||
private <T extends TooltipAppender> void replaceEnchantmentTooltip(DataComponentType<T> componentType, Item.TooltipContext context, Consumer<Text> textConsumer, TooltipType type, CallbackInfo ci) {
|
||||
private <T extends TooltipAppender> void replaceEnchantmentTooltip(ComponentType<T> componentType, Item.TooltipContext context, Consumer<Text> textConsumer, TooltipType type, CallbackInfo ci) {
|
||||
if (ProtocolTranslator.getTargetVersion().newerThan(ProtocolVersion.v1_14_4)) {
|
||||
return;
|
||||
}
|
||||
@ -67,10 +69,10 @@ public abstract class MixinItemStack {
|
||||
return;
|
||||
}
|
||||
if (componentType == DataComponentTypes.ENCHANTMENTS) {
|
||||
this.viaFabricPlus$appendEnchantments1_14_4("Enchantments", tag, textConsumer);
|
||||
this.viaFabricPlus$appendEnchantments1_14_4("Enchantments", tag, context, textConsumer);
|
||||
ci.cancel();
|
||||
} else if (componentType == DataComponentTypes.STORED_ENCHANTMENTS) {
|
||||
this.viaFabricPlus$appendEnchantments1_14_4("StoredEnchantments", tag, textConsumer);
|
||||
this.viaFabricPlus$appendEnchantments1_14_4("StoredEnchantments", tag, context, textConsumer);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@ -85,16 +87,20 @@ public abstract class MixinItemStack {
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void viaFabricPlus$appendEnchantments1_14_4(final String name, final NbtCompound nbt, final Consumer<Text> tooltip) {
|
||||
private void viaFabricPlus$appendEnchantments1_14_4(final String name, final NbtCompound nbt, Item.TooltipContext context, final Consumer<Text> tooltip) {
|
||||
final RegistryWrapper.WrapperLookup registryLookup = context.getRegistryLookup();
|
||||
final NbtList enchantments = nbt.getList(name, NbtElement.COMPOUND_TYPE);
|
||||
for (NbtElement element : enchantments) {
|
||||
final NbtCompound enchantment = (NbtCompound) element;
|
||||
|
||||
final String id = enchantment.getString("id");
|
||||
final Optional<Enchantment> value = Enchantments1_14_4.getOrEmpty(id);
|
||||
final Optional<RegistryKey<Enchantment>> value = Enchantments1_14_4.getOrEmpty(id);
|
||||
value.ifPresent(e -> {
|
||||
final int lvl = enchantment.getInt("lvl");
|
||||
tooltip.accept(e.getName(MathHelper.clamp(lvl, Short.MIN_VALUE, Short.MAX_VALUE)));
|
||||
if (registryLookup != null) {
|
||||
final Optional<RegistryEntry.Reference<Enchantment>> v = registryLookup.getWrapperOrThrow(RegistryKeys.ENCHANTMENT).getOptional(e);
|
||||
v.ifPresent(enchantmentReference -> tooltip.accept(Enchantment.getName(enchantmentReference, MathHelper.clamp(lvl, Short.MIN_VALUE, Short.MAX_VALUE))));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public abstract class MixinClientConfigurationNetworkHandler extends ClientCommo
|
||||
|
||||
@Inject(method = "onFeatures", at = @At(value = "HEAD"))
|
||||
private void notifyAboutFeatures(FeaturesS2CPacket packet, CallbackInfo ci) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThan(ProtocolVersion.v1_20) && packet.features().contains(new Identifier("update_1_20"))) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThan(ProtocolVersion.v1_20) && packet.features().contains(Identifier.of("update_1_20"))) {
|
||||
ChatUtil.sendPrefixedMessage(Text.literal("This server has the update_1_20 features enabled. This is not fully supported and may cause issues.").formatted(Formatting.RED));
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,9 @@ public abstract class MixinClientPlayNetworkHandler extends ClientCommonNetworkH
|
||||
@Shadow
|
||||
protected abstract boolean isSecureChatEnforced();
|
||||
|
||||
@Shadow
|
||||
private ClientWorld world;
|
||||
|
||||
protected MixinClientPlayNetworkHandler(MinecraftClient client, ClientConnection connection, ClientConnectionState connectionState) {
|
||||
super(client, connection, connectionState);
|
||||
}
|
||||
@ -215,7 +218,7 @@ public abstract class MixinClientPlayNetworkHandler extends ClientCommonNetworkH
|
||||
final List<RecipeEntry<?>> recipes = new ArrayList<>();
|
||||
final List<RecipeInfo> recipeInfos = Recipes1_11_2.getRecipes(ProtocolTranslator.getTargetVersion());
|
||||
for (int i = 0; i < recipeInfos.size(); i++) {
|
||||
recipes.add(recipeInfos.get(i).create(new Identifier("viafabricplus", "recipe/" + i)));
|
||||
recipes.add(recipeInfos.get(i).create(Identifier.of("viafabricplus", "recipe/" + i)));
|
||||
}
|
||||
this.onSynchronizeRecipes(new SynchronizeRecipesS2CPacket(recipes));
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.c2s.play.ClickSlotC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerInteractBlockC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
|
||||
import net.minecraft.screen.slot.SlotActionType;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
@ -108,6 +109,13 @@ public abstract class MixinClientPlayerInteractionManager implements IClientPlay
|
||||
@Unique
|
||||
private final ClientPlayerInteractionManager1_18_2 viaFabricPlus$1_18_2InteractionManager = new ClientPlayerInteractionManager1_18_2();
|
||||
|
||||
@Inject(method = "interactItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;syncSelectedSlot()V", shift = At.Shift.AFTER))
|
||||
private void sendPlayerPosPacket(PlayerEntity player, Hand hand, CallbackInfoReturnable<ActionResult> cir) {
|
||||
if (ProtocolTranslator.getTargetVersion().betweenInclusive(ProtocolVersion.v1_17, ProtocolVersion.v1_20_5)) {
|
||||
this.networkHandler.sendPacket(new PlayerMoveC2SPacket.Full(player.getX(), player.getY(), player.getZ(), player.getYaw(), player.getPitch(), player.isOnGround()));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getBlockBreakingProgress", at = @At("HEAD"), cancellable = true)
|
||||
private void changeCalculation(CallbackInfoReturnable<Integer> cir) {
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_19_4)) {
|
||||
@ -130,11 +138,6 @@ public abstract class MixinClientPlayerInteractionManager implements IClientPlay
|
||||
return new PlayerActionC2SPacket(action, pos, direction);
|
||||
}
|
||||
|
||||
@WrapWithCondition(method = "interactItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V", ordinal = 0))
|
||||
private boolean redirectPlayerPosPacket(ClientPlayNetworkHandler instance, Packet<?> packet) {
|
||||
return ProtocolTranslator.getTargetVersion().newerThan(ProtocolVersion.v1_16_4);
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "clickSlot", at = @At(value = "STORE"), ordinal = 0)
|
||||
private List<ItemStack> captureOldItems(List<ItemStack> oldItems) {
|
||||
viaFabricPlus$oldCursorStack = client.player.currentScreenHandler.getCursorStack().copy();
|
||||
|
@ -19,35 +19,29 @@
|
||||
|
||||
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.screen;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.util.MathUtil;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.option.GameOptionsScreen;
|
||||
import net.minecraft.client.gui.screen.option.MouseOptionsScreen;
|
||||
import net.minecraft.client.gui.widget.OptionListWidget;
|
||||
import net.minecraft.client.option.GameOptions;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
@Mixin(MouseOptionsScreen.class)
|
||||
public abstract class MixinMouseOptionsScreen extends GameOptionsScreen {
|
||||
|
||||
@Shadow
|
||||
private OptionListWidget buttonList;
|
||||
/*@Shadow
|
||||
private OptionListWidget buttonList;*/
|
||||
|
||||
public MixinMouseOptionsScreen(Screen parent, GameOptions gameOptions, Text title) {
|
||||
super(parent, gameOptions, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
/*@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
if (ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_13_2) && this.buttonList.getWidgetFor(this.gameOptions.getMouseSensitivity()).isHovered()) {
|
||||
context.drawTooltip(textRenderer, Text.of("<=1.13.2 Sensitivity: " + MathUtil.get1_13SliderValue(this.gameOptions.getMouseSensitivity().getValue().floatValue()).valueInt() + "%"), mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.inventory.RecipeInputInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.recipe.CraftingRecipe;
|
||||
import net.minecraft.recipe.input.CraftingRecipeInput;
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler;
|
||||
import net.minecraft.screen.CraftingScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
@ -37,7 +39,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(CraftingScreenHandler.class)
|
||||
public abstract class MixinCraftingScreenHandler extends AbstractRecipeScreenHandler<RecipeInputInventory> {
|
||||
public abstract class MixinCraftingScreenHandler extends AbstractRecipeScreenHandler<CraftingRecipeInput, CraftingRecipe> {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
|
@ -22,9 +22,10 @@ package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.screen.s
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import de.florianmichael.viafabricplus.fixes.data.recipe.Recipes1_11_2;
|
||||
import de.florianmichael.viafabricplus.protocoltranslator.ProtocolTranslator;
|
||||
import net.minecraft.inventory.CraftingInventory;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.inventory.RecipeInputInventory;
|
||||
import net.minecraft.recipe.CraftingRecipe;
|
||||
import net.minecraft.recipe.input.CraftingRecipeInput;
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
@ -39,7 +40,7 @@ import org.spongepowered.asm.mixin.injection.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PlayerScreenHandler.class)
|
||||
public abstract class MixinPlayerScreenHandler extends AbstractRecipeScreenHandler<CraftingInventory> {
|
||||
public abstract class MixinPlayerScreenHandler extends AbstractRecipeScreenHandler<CraftingRecipeInput, CraftingRecipe> {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
@ -57,7 +58,7 @@ public abstract class MixinPlayerScreenHandler extends AbstractRecipeScreenHandl
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>",
|
||||
slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/screen/PlayerScreenHandler$2;<init>(Lnet/minecraft/screen/PlayerScreenHandler;Lnet/minecraft/inventory/Inventory;IIILnet/minecraft/entity/player/PlayerEntity;)V")),
|
||||
slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/screen/PlayerScreenHandler$1;<init>(Lnet/minecraft/screen/PlayerScreenHandler;Lnet/minecraft/inventory/Inventory;IIILnet/minecraft/entity/player/PlayerEntity;)V")),
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/PlayerScreenHandler;addSlot(Lnet/minecraft/screen/slot/Slot;)Lnet/minecraft/screen/slot/Slot;", ordinal = 0))
|
||||
private Slot removeOffhandSlot(PlayerScreenHandler screenHandler, Slot slot) {
|
||||
return ProtocolTranslator.getTargetVersion().olderThanOrEqualTo(ProtocolVersion.v1_8) ? null : addSlot(slot);
|
||||
|
@ -40,14 +40,14 @@ public abstract class MixinJoinPackets {
|
||||
return seed;
|
||||
}
|
||||
|
||||
@Redirect(method = "lambda$register$8", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/packet/PacketWrapper;read(Lcom/viaversion/viaversion/api/type/Type;)Ljava/lang/Object;", ordinal = 56))
|
||||
@Redirect(method = "lambda$register$8", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/packet/PacketWrapper;read(Lcom/viaversion/viaversion/api/type/Type;)Ljava/lang/Object;", ordinal = 59))
|
||||
private static Object trackLevelId(PacketWrapper instance, Type<StringType> tType) {
|
||||
final Object levelId = instance.read(tType);
|
||||
instance.user().get(BedrockJoinGameTracker.class).setLevelId((String) levelId);
|
||||
return levelId;
|
||||
}
|
||||
|
||||
@Redirect(method = "lambda$register$8", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/packet/PacketWrapper;read(Lcom/viaversion/viaversion/api/type/Type;)Ljava/lang/Object;", ordinal = 64))
|
||||
@Redirect(method = "lambda$register$8", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/packet/PacketWrapper;read(Lcom/viaversion/viaversion/api/type/Type;)Ljava/lang/Object;", ordinal = 67))
|
||||
private static Object trackEnchantmentSeed(PacketWrapper instance, Type<VarIntType> tType) {
|
||||
final Object enchantmentSeed = instance.read(tType);
|
||||
instance.user().get(BedrockJoinGameTracker.class).setEnchantmentSeed((Integer) enchantmentSeed);
|
||||
|
@ -87,7 +87,7 @@ public class ProtocolTranslator {
|
||||
/**
|
||||
* The native version of the client
|
||||
*/
|
||||
public static final ProtocolVersion NATIVE_VERSION = ProtocolVersion.v1_20_5;
|
||||
public static final ProtocolVersion NATIVE_VERSION = ProtocolVersion.v1_21;
|
||||
|
||||
/**
|
||||
* Protocol version that is used to enable protocol auto-detect
|
||||
|
@ -28,7 +28,7 @@ import net.minecraft.util.Identifier;
|
||||
|
||||
public record DataCustomPayload(PacketByteBuf buf) implements CustomPayload {
|
||||
|
||||
public static final CustomPayload.Id<DataCustomPayload> ID = new CustomPayload.Id<>(new Identifier(ClientsideFixes.PACKET_SYNC_IDENTIFIER));
|
||||
public static final CustomPayload.Id<DataCustomPayload> ID = new CustomPayload.Id<>(Identifier.of(ClientsideFixes.PACKET_SYNC_IDENTIFIER));
|
||||
|
||||
static {
|
||||
PayloadTypeRegistry.playS2C().register(DataCustomPayload.ID, CustomPayload.codecOf((value, buf) -> {
|
||||
|
@ -54,7 +54,7 @@
|
||||
"fabric-lifecycle-events-v1": ">=2.2.28",
|
||||
"fabric-particles-v1": ">=1.1.6",
|
||||
"fabric-registry-sync-v0": ">=4.0.13",
|
||||
"minecraft": ">=1.20.5",
|
||||
"minecraft": ">=1.21",
|
||||
"java": ">=21"
|
||||
},
|
||||
"breaks": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
accessWidener v1 named
|
||||
|
||||
accessible field net/minecraft/item/Item ATTACK_DAMAGE_MODIFIER_ID Ljava/util/UUID;
|
||||
#accessible field net/minecraft/item/Item ATTACK_DAMAGE_MODIFIER_ID Ljava/util/UUID;
|
||||
accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel;
|
||||
accessible field net/minecraft/client/MinecraftClient fontManager Lnet/minecraft/client/font/FontManager;
|
||||
accessible field net/minecraft/client/network/AllowedAddressResolver redirectResolver Lnet/minecraft/client/network/RedirectResolver;
|
||||
@ -28,4 +28,4 @@ accessible class net/minecraft/client/gui/screen/GameModeSelectionScreen$GameMod
|
||||
accessible class net/minecraft/client/font/FontStorage$GlyphPair
|
||||
|
||||
mutable field net/minecraft/entity/EntityType dimensions Lnet/minecraft/entity/EntityDimensions;
|
||||
mutable field net/minecraft/block/AbstractBlock velocityMultiplier F
|
||||
mutable field net/minecraft/block/AbstractBlock velocityMultiplier F
|
||||
|
Loading…
Reference in New Issue
Block a user