Merge remote-tracking branch 'origin/main' into 1.20-recode

This commit is contained in:
FlorianMichael 2023-05-17 16:37:06 +02:00
commit dd4b4c1910
20 changed files with 86 additions and 69 deletions

View File

@ -14,7 +14,7 @@
# Why another protocol translator?
ViaFabricPlus is intended to replace [multiconnect](https://github.com/Earthcomputer/multiconnect), and it also promises more compactness and stability. ViaFabricPlus can do everything multiconnect could do, but supports more Minecraft versions (listed below) and has more protocol changes implemented.
### Supported Server versions
- Release (1.0.0 - 1.20 [23w14a])
- Release (1.0.0 - 1.20 [1.20-pre1])
- Beta (b1.0 - b1.8.1)
- Alpha (a1.0.15 - a1.2.6)
- Classic (c0.0.15 - c0.30 including [CPE](https://wiki.vg/Classic_Protocol_Extension))
@ -28,6 +28,7 @@ ViaFabricPlus is intended to replace [multiconnect](https://github.com/Earthcomp
## Known incompatibilities
- ***[ViaFabric](https://github.com/ViaVersion/ViaFabric)***
- ***[multiconnect](https://github.com/Earthcomputer/multiconnect)***
- ***[krypton](https://github.com/astei/krypton)***
## List of all clientside related fixes
<details>

View File

@ -7,8 +7,8 @@ version = project.mod_version
group = project.maven_group
configurations {
implementation.extendsFrom libs
api.extendsFrom libs
implementation.extendsFrom library
api.extendsFrom library
}
allprojects {
@ -51,41 +51,46 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
modImplementation "com.terraformersmc:modmenu:${project.mod_menu_version}"
libs "com.github.FlorianMichael:ViaLoadingBase:${project.vialoadingbase_version}"
libs "com.github.FlorianMichael:Classic4J:${project.classic4j_version}"
library "com.github.FlorianMichael:ViaLoadingBase:${project.vialoadingbase_version}"
library "com.github.FlorianMichael:Classic4J:${project.classic4j_version}"
libs "com.viaversion:viaversion:${project.viaversion_version}"
libs "com.viaversion:viabackwards-common:${project.viabackwards_version}"
libs "org.yaml:snakeyaml:${project.snake_yml_version}"
library "com.viaversion:viaversion:${project.viaversion_version}"
library ("com.viaversion:viabackwards-common:${project.viabackwards_version}") {
exclude group: "com.viaversion", module: "viaversion"
exclude group: "io.netty", module: "netty-all"
exclude group: "com.google.guava", module: "guava"
}
library "org.yaml:snakeyaml:${project.snake_yml_version}"
libs ("net.raphimc:ViaLegacy:${project.vialegacy_version}") {
library ("net.raphimc:ViaLegacy:${project.vialegacy_version}") {
exclude group: "net.lenni0451.mcstructs", module: "text"
}
libs "net.raphimc:ViaAprilFools:${project.viaaprilfools_version}"
libs ("net.raphimc:ViaBedrock:${project.viabedrock_version}") {
library "net.raphimc:ViaAprilFools:${project.viaaprilfools_version}"
library ("net.raphimc:ViaBedrock:${project.viabedrock_version}") {
exclude group: "net.lenni0451.mcstructs", module: "text"
exclude group: "io.jsonwebtoken", module: "jjwt-impl"
exclude group: "io.jsonwebtoken", module: "jjwt-gson"
}
libs ("net.raphimc:MinecraftAuth:${project.minecraftauth_version}") {
library ("net.raphimc:MinecraftAuth:${project.minecraftauth_version}") {
exclude group: "com.google.code.gson", module: "gson"
exclude group: "org.slf4j", module: "slf4j-api"
}
libs("org.cloudburstmc.netty:netty-transport-raknet:${project.raknet_transport_version}") {
library("org.cloudburstmc.netty:netty-transport-raknet:${project.raknet_transport_version}") {
exclude group: "io.netty", module: "netty-common"
exclude group: "io.netty", module: "netty-buffer"
exclude group: "io.netty", module: "netty-codec"
exclude group: "io.netty", module: "netty-transport"
}
libs ("net.lenni0451.mcstructs:text:${project.mcstructs_text_version}") {
library ("net.lenni0451.mcstructs:text:${project.mcstructs_text_version}") {
exclude group: "com.google.code.gson", module: "gson"
}
libs "net.lenni0451:Reflect:${project.reflect_version}"
library "net.lenni0451:Reflect:${project.reflect_version}"
modImplementation "com.terraformersmc:modmenu:${project.mod_menu_version}"
include implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-fabric:${project.mixin_extras_version}"))
}
processResources {
@ -108,10 +113,10 @@ java {
}
jar {
dependsOn configurations.libs
dependsOn configurations.library
from {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
configurations.libs.collect {
configurations.library.collect {
zipTree(it)
}
} {

View File

@ -9,7 +9,7 @@ loader_version=0.14.19
fabric_api_version=0.80.1+1.20
# viafabricplus
mod_version=2.7.1
mod_version=2.7.2
maven_group=de.florianmichael
archives_base_name=viafabricplus
@ -19,8 +19,8 @@ raknet_transport_version=1.0.0.CR1-SNAPSHOT
classic4j_version=1.2.0
# viaversion (and required) libs
viaversion_version=4.7.0-23w18a-SNAPSHOT
viabackwards_version=4.7.0-23w18a-SNAPSHOT
viaversion_version=4.7.0-1.20-pre1-SNAPSHOT
viabackwards_version=4.7.0-1.20-pre1-SNAPSHOT
snake_yml_version=2.0
# raphimc libs
@ -36,3 +36,4 @@ reflect_version=1.1.0
# other libs
mod_menu_version=7.0.0-beta.1
netty_codec_http_version=4.1.90.Final
mixin_extras_version=0.2.0-beta.7

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -8,4 +8,3 @@ pluginManagement {
gradlePluginPortal()
}
}

View File

@ -44,7 +44,9 @@ public class ChatLengthCalculation {
});
LoadClassicProtocolExtensionCallback.EVENT.register(classicProtocolExtension -> {
if (classicProtocolExtension == ClassicProtocolExtension.LONGER_MESSAGES) INSTANCE.maxLength = Short.MAX_VALUE * 2;
if (classicProtocolExtension == ClassicProtocolExtension.LONGER_MESSAGES) {
INSTANCE.maxLength = Short.MAX_VALUE * 2;
}
});
}

View File

@ -17,6 +17,7 @@
*/
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
import com.mojang.authlib.GameProfile;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import de.florianmichael.viafabricplus.ViaFabricPlus;
@ -109,11 +110,9 @@ public abstract class MixinClientPlayNetworkHandler {
}
}
@Redirect(method = "onPlayerSpawnPosition", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/DownloadingTerrainScreen;setReady()V"))
public void moveDownloadingTerrainClosing(DownloadingTerrainScreen instance) {
if (ProtocolHack.getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_19)) {
instance.setReady();
}
@WrapWithCondition(method = "onPlayerSpawnPosition", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/DownloadingTerrainScreen;setReady()V"))
public boolean moveDownloadingTerrainClosing(DownloadingTerrainScreen instance) {
return ProtocolHack.getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_19);
}
@Inject(method = "onPlayerPositionLook", at = @At("RETURN"))
@ -132,11 +131,9 @@ public abstract class MixinClientPlayNetworkHandler {
return constant;
}
@Redirect(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false))
public void removeNewWarning(Logger instance, String s, Object o) {
if (ProtocolHack.getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_19_3)) {
instance.warn(s, o);
}
@WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false))
public boolean removeWarning(Logger instance, String s, Object o) {
return ProtocolHack.getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_19_3);
}
@Redirect(method = "onKeepAlive", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;Ljava/util/function/BooleanSupplier;Ljava/time/Duration;)V"))

View File

@ -17,6 +17,7 @@
*/
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
@ -29,7 +30,6 @@ import de.florianmichael.viafabricplus.injection.access.IScreenHandler;
import de.florianmichael.viafabricplus.protocolhack.ProtocolHack;
import de.florianmichael.viafabricplus.protocolhack.provider.viaversion.ViaFabricPlusHandItemProvider;
import de.florianmichael.viafabricplus.protocolhack.usage.ItemTranslator;
import de.florianmichael.viafabricplus.protocolhack.ProtocolHack;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity;
@ -61,9 +61,12 @@ public abstract class MixinClientPlayerInteractionManager {
@Final
private MinecraftClient client;
@Shadow protected abstract ActionResult interactBlockInternal(ClientPlayerEntity player, Hand hand, BlockHitResult hitResult);
@Shadow
protected abstract ActionResult interactBlockInternal(ClientPlayerEntity player, Hand hand, BlockHitResult hitResult);
@Shadow @Final private ClientPlayNetworkHandler networkHandler;
@Shadow
@Final
private ClientPlayNetworkHandler networkHandler;
@Unique
private ItemStack viafabricplus_oldCursorStack;
@ -99,8 +102,8 @@ public abstract class MixinClientPlayerInteractionManager {
return type == SlotActionType.PICKUP && slot == -999;
}
@Redirect(method = "clickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V"))
private void modifySlotClickPacket(ClientPlayNetworkHandler instance, Packet<?> packet) {
@WrapWithCondition(method = "clickSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V"))
private boolean modifySlotClickPacket(ClientPlayNetworkHandler instance, Packet<?> packet) {
if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(ProtocolVersion.v1_16_4) && packet instanceof ClickSlotC2SPacket clickSlot) {
ItemStack slotItemBeforeModification;
@ -131,25 +134,22 @@ public abstract class MixinClientPlayerInteractionManager {
try {
clickSlotPacket.sendToServer(Protocol1_17To1_16_4.class);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
});
viafabricplus_oldCursorStack = null;
viafabricplus_oldItems = null;
return;
return false;
}
instance.sendPacket(packet);
return true;
}
@Redirect(method = "interactItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V", ordinal = 0),
@WrapWithCondition(method = "interactItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V", ordinal = 0),
slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;syncSelectedSlot()V"),
to = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;sendSequencedPacket(Lnet/minecraft/client/world/ClientWorld;Lnet/minecraft/client/network/SequencedPacketCreator;)V", ordinal = 0)))
private void redirectInteractItem(ClientPlayNetworkHandler clientPlayNetworkHandler, Packet<?> packet) {
if (ProtocolHack.getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_17)) {
clientPlayNetworkHandler.sendPacket(packet);
}
private boolean redirectInteractItem(ClientPlayNetworkHandler instance, Packet<?> packet) {
return ProtocolHack.getTargetVersion().isNewerThanOrEqualTo(ProtocolVersion.v1_17);
}
@Inject(method = "interactItem", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V", ordinal = 0, shift = At.Shift.BEFORE))

View File

@ -17,6 +17,7 @@
*/
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import de.florianmichael.viafabricplus.base.settings.groups.VisualSettings;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
@ -63,10 +64,10 @@ public abstract class MixinInGameHud {
// Moving down all remaining elements
@Redirect(method = "renderStatusBars", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/hud/InGameHud;scaledHeight:I", opcode = Opcodes.GETFIELD))
private int moveHealthDown(InGameHud instance) {
if (VisualSettings.INSTANCE.removeNewerHudElements.getValue()) return scaledHeight + 6;
return scaledHeight;
@ModifyExpressionValue(method = "renderStatusBars", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/hud/InGameHud;scaledHeight:I", opcode = Opcodes.GETFIELD))
private int moveHealthDown(int originalValue) {
if (VisualSettings.INSTANCE.removeNewerHudElements.getValue()) return originalValue + 6;
return originalValue;
}
@ModifyArg(method = "renderStatusBars", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTexture(Lnet/minecraft/util/Identifier;IIIIII)V"), slice = @Slice(

View File

@ -17,6 +17,7 @@
*/
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
@ -56,13 +57,11 @@ public abstract class MixinMinecraftClient implements IMinecraftClient {
@Shadow @Nullable public abstract ClientPlayNetworkHandler getNetworkHandler();
@Redirect(method = "doItemUse",
@WrapWithCondition(method = "doItemUse",
slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;interactItem(Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/Hand;)Lnet/minecraft/util/ActionResult;")),
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;resetEquipProgress(Lnet/minecraft/util/Hand;)V", ordinal = 0))
private void redirectDoItemUse(HeldItemRenderer heldItemRenderer, Hand hand) {
if (ProtocolHack.getTargetVersion().isNewerThan(ProtocolVersion.v1_8) || !(player.getStackInHand(hand).getItem() instanceof SwordItem)) {
heldItemRenderer.resetEquipProgress(hand);
}
public boolean removeEquipProgressReset(HeldItemRenderer instance, Hand hand) {
return ProtocolHack.getTargetVersion().isNewerThan(ProtocolVersion.v1_8) || !(player.getStackInHand(hand).getItem() instanceof SwordItem);
}
@Redirect(method = "doItemUse",

View File

@ -89,7 +89,6 @@ public class MixinServerResourcePackProvider {
if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(ProtocolVersion.v1_8)) {
return instance;
}
return instance.toLowerCase(locale);
}
}

View File

@ -18,6 +18,7 @@
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft;
import de.florianmichael.viafabricplus.definition.ChatLengthCalculation;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.StringHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
@ -28,6 +29,9 @@ public class MixinStringHelper {
@ModifyConstant(method = "truncateChat", constant = @Constant(intValue = 256))
private static int expandChatLength(int constant) {
if (MinecraftClient.getInstance().isInSingleplayer()) {
return 256;
}
return ChatLengthCalculation.INSTANCE.getMaxLength();
}
}

View File

@ -17,6 +17,7 @@
*/
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.entity;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import de.florianmichael.viafabricplus.protocolhack.ProtocolHack;
import de.florianmichael.viafabricplus.base.settings.groups.ExperimentalSettings;
@ -88,13 +89,11 @@ public abstract class MixinLivingEntity extends Entity {
return movingDown;
}
@Redirect(method = "travel",
@WrapWithCondition(method = "travel",
slice = @Slice(from = @At(value = "FIELD", target = "Lnet/minecraft/entity/effect/StatusEffects;LEVITATION:Lnet/minecraft/entity/effect/StatusEffect;", ordinal = 0)),
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;onLanding()V", ordinal = 0))
private void dontResetLevitationFallDistance(LivingEntity instance) {
if (ProtocolHack.getTargetVersion().isNewerThan(ProtocolVersion.v1_12_2)) {
instance.onLanding();
}
private boolean dontResetLevitationFallDistance(LivingEntity instance) {
return ProtocolHack.getTargetVersion().isNewerThan(ProtocolVersion.v1_12_2);
}
@Redirect(method = "travel", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;isSprinting()Z", ordinal = 0))

View File

@ -18,6 +18,7 @@
package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.packet;
import de.florianmichael.viafabricplus.definition.ChatLengthCalculation;
import net.minecraft.client.MinecraftClient;
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -28,6 +29,9 @@ public class MixinChatMessageC2SPacket {
@ModifyArg(method = "write", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketByteBuf;writeString(Ljava/lang/String;I)Lnet/minecraft/network/PacketByteBuf;"))
public int modifyChatLength(int maxLength) {
if (MinecraftClient.getInstance().isInSingleplayer()) {
return 256;
}
return ChatLengthCalculation.INSTANCE.getMaxLength();
}
}

View File

@ -19,6 +19,7 @@ package de.florianmichael.viafabricplus.injection.mixin.fixes.minecraft.screen;
import de.florianmichael.viafabricplus.definition.ChatLengthCalculation;
import de.florianmichael.viafabricplus.base.settings.groups.VisualSettings;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.ChatHud;
import net.minecraft.client.gui.hud.MessageIndicator;
import net.minecraft.client.gui.screen.ChatScreen;
@ -37,7 +38,9 @@ public class MixinChatScreen {
@Inject(method = "init", at = @At("RETURN"))
public void changeChatLength(CallbackInfo ci) {
this.chatField.setMaxLength(ChatLengthCalculation.INSTANCE.getMaxLength());
if (!MinecraftClient.getInstance().isInSingleplayer()) {
this.chatField.setMaxLength(ChatLengthCalculation.INSTANCE.getMaxLength());
}
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;getIndicatorAt(DD)Lnet/minecraft/client/gui/hud/MessageIndicator;"))

View File

@ -36,6 +36,8 @@ public class MixinStructureBlockScreen_1 extends TextFieldWidget {
@Inject(method = "charTyped(CI)Z", at = @At("HEAD"), cancellable = true)
private void onCharTyped(char chr, int keyCode, CallbackInfoReturnable<Boolean> ci) {
if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(ProtocolVersion.v1_12_2)) ci.setReturnValue(super.charTyped(chr, keyCode));
if (ProtocolHack.getTargetVersion().isOlderThanOrEqualTo(ProtocolVersion.v1_12_2)) {
ci.setReturnValue(super.charTyped(chr, keyCode));
}
}
}

View File

@ -45,7 +45,7 @@ public abstract class MixinProtocolVersion {
viafabricplus_remaps.put("1.16.4/5", new Pair<>("1.16.4-1.16.5", null));
viafabricplus_remaps.put("1.18/1.18.1", new Pair<>("1.18-1.18.1", null));
viafabricplus_remaps.put("1.19.1/2", new Pair<>("1.19.1-1.19.2", null));
viafabricplus_remaps.put("1.20", new Pair<>("23w18a", null));
viafabricplus_remaps.put("1.20", new Pair<>("1.20-pre1", null));
}
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;register(ILjava/lang/String;)Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;"))

View File

@ -32,7 +32,7 @@ public class PackFormatsMappings {
private final static Map<Integer, GameVersion> protocolMap = new HashMap<>();
public static void load() {
registerVersion(ProtocolVersion.v1_20, 15, "23w18a");
registerVersion(ProtocolVersion.v1_20, 15, "1.20-pre1");
registerVersion(ProtocolVersion.v1_19_4, 13, "1.19.4");
registerVersion(ProtocolVersion.v1_19_3, 12, "1.19.3");
registerVersion(ProtocolVersion.v1_19_1, 9, "1.19.2");

View File

@ -33,7 +33,7 @@ public class ViaFabricPlusViaCommandSender implements ViaCommandSender {
@Override
public boolean hasPermission(String s) {
return source.hasPermissionLevel(4);
return true;
}
@Override

View File

@ -12,6 +12,7 @@
"words.viafabricplus.online": "Онлайн режим",
"words.viafabricplus.reset": "Зкинути",
"words.viafabricplus.copy": "Зкопіювати код",
"words.viafabricplus.error": "Щось пішло не так! Спробуйте будь ласка пізніше",
"general.viafabricplus.secret": "Показувати Супер Секретні Налаштування",
"general.viafabricplus.extrainformation": "Показувати додаткову інформацію на екрані відладки",