mirror of
https://github.com/chylex/Better-Controls.git
synced 2025-08-22 19:01:06 +02:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
7c2279b1d8
|
|||
0814ddaedf
|
|||
0aed6282b4
|
|||
9217852c0b
|
|||
21dcd60b91
|
|||
9922828f09
|
@@ -4,7 +4,7 @@ val fabricVersion: String by project
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
idea
|
idea
|
||||||
id("fabric-loom") version "0.9-SNAPSHOT"
|
id("fabric-loom") version "0.10-SNAPSHOT"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -42,7 +42,7 @@ tasks.processResources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.jar {
|
tasks.jar {
|
||||||
exclude("io/github/prospector/")
|
exclude("com/terraformersmc/modmenu/")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.remapJar {
|
tasks.remapJar {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package chylex.bettercontrols.compatibility;
|
package chylex.bettercontrols.compatibility;
|
||||||
import chylex.bettercontrols.gui.BetterControlsScreen;
|
import chylex.bettercontrols.gui.BetterControlsScreen;
|
||||||
import io.github.prospector.modmenu.api.ConfigScreenFactory;
|
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||||
import io.github.prospector.modmenu.api.ModMenuApi;
|
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||||
|
|
||||||
public class ModMenuSupport implements ModMenuApi {
|
public class ModMenuSupport implements ModMenuApi {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
package io.github.prospector.modmenu.api;
|
package com.terraformersmc.modmenu.api;
|
||||||
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ConfigScreenFactory<S extends Screen> {
|
public interface ConfigScreenFactory<S extends Screen> {
|
||||||
S create(Screen var1);
|
S create(Screen parent);
|
||||||
}
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
package com.terraformersmc.modmenu.api;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public interface ModMenuApi {
|
||||||
|
default ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||||
|
return screen -> null;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,14 +0,0 @@
|
|||||||
package io.github.prospector.modmenu.api;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public interface ModMenuApi {
|
|
||||||
default ConfigScreenFactory<?> getModConfigScreenFactory() {
|
|
||||||
return ignore -> null;
|
|
||||||
}
|
|
||||||
|
|
||||||
default Map<String, ConfigScreenFactory<?>> getProvidedConfigScreenFactories() {
|
|
||||||
return ImmutableMap.of();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,18 +1,20 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "bettercontrols",
|
"id": "${id}",
|
||||||
"version": "${version}",
|
|
||||||
|
|
||||||
"name": "${name}",
|
"name": "${name}",
|
||||||
"description": "Adds many powerful key bindings and options to control your movement.\nThe features complement vanilla mechanics without giving unfair advantages, so server use should be fine.",
|
"description": "${description}",
|
||||||
"icon": "assets/bettercontrols/icon.png",
|
"version": "${version}",
|
||||||
"license": "MPL-2.0",
|
"license": "${license}",
|
||||||
|
|
||||||
|
"icon": "assets/bettercontrols/icon.png",
|
||||||
|
|
||||||
|
"authors": [
|
||||||
|
"${author}"
|
||||||
|
],
|
||||||
|
|
||||||
"authors": [ "chylex" ],
|
|
||||||
"contact": {
|
"contact": {
|
||||||
"homepage": "",
|
"issues": "${issuesURL}",
|
||||||
"sources": "https://github.com/chylex/Better-Controls",
|
"sources": "${sourcesURL}"
|
||||||
"issues": "https://github.com/chylex/Better-Controls/issues"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"environment": "client",
|
"environment": "client",
|
||||||
@@ -22,7 +24,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"mixins": [{
|
"mixins": [{
|
||||||
"config": "bettercontrols.mixins.json",
|
"config": "${id}.mixins.json",
|
||||||
"environment": "client"
|
"environment": "client"
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
@@ -4,20 +4,20 @@ import chylex.bettercontrols.gui.BetterControlsScreen;
|
|||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.client.ConfigGuiHandler.ConfigGuiFactory;
|
||||||
import net.minecraftforge.fml.DistExecutor;
|
import net.minecraftforge.fml.DistExecutor;
|
||||||
import net.minecraftforge.fml.IExtensionPoint.DisplayTest;
|
import net.minecraftforge.fml.IExtensionPoint.DisplayTest;
|
||||||
import net.minecraftforge.fml.ModLoadingContext;
|
import net.minecraftforge.fml.ModLoadingContext;
|
||||||
import net.minecraftforge.fml.common.Mod;
|
import net.minecraftforge.fml.common.Mod;
|
||||||
import net.minecraftforge.fml.loading.FMLPaths;
|
import net.minecraftforge.fml.loading.FMLPaths;
|
||||||
import net.minecraftforge.fmlclient.ConfigGuiHandler.ConfigGuiFactory;
|
import net.minecraftforge.network.NetworkConstants;
|
||||||
import net.minecraftforge.fmllegacy.network.FMLNetworkConstants;
|
|
||||||
|
|
||||||
@Mod("bettercontrols")
|
@Mod("bettercontrols")
|
||||||
public final class BetterControlsMod {
|
public final class BetterControlsMod {
|
||||||
public BetterControlsMod() {
|
public BetterControlsMod() {
|
||||||
BetterControlsCommon.setConfig(DistExecutor.safeCallWhenOn(Dist.CLIENT, () -> ClientLoader::loadConfig));
|
BetterControlsCommon.setConfig(DistExecutor.safeCallWhenOn(Dist.CLIENT, () -> ClientLoader::loadConfig));
|
||||||
ModLoadingContext.get().registerExtensionPoint(ConfigGuiFactory.class, () -> new ConfigGuiFactory(ClientLoader::createScreen));
|
ModLoadingContext.get().registerExtensionPoint(ConfigGuiFactory.class, () -> new ConfigGuiFactory(ClientLoader::createScreen));
|
||||||
ModLoadingContext.get().registerExtensionPoint(DisplayTest.class, () -> new DisplayTest(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
ModLoadingContext.get().registerExtensionPoint(DisplayTest.class, () -> new DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ClientLoader {
|
private static final class ClientLoader {
|
||||||
|
@@ -1,28 +1,25 @@
|
|||||||
modLoader = "javafml"
|
modLoader = "javafml"
|
||||||
loaderVersion = "[37,)"
|
loaderVersion = "[37,)"
|
||||||
|
|
||||||
authors = "chylex"
|
authors = "${author}"
|
||||||
license = "MPL-2.0"
|
license = "${license}"
|
||||||
issueTrackerURL = "https://github.com/chylex/Better-Controls/issues"
|
issueTrackerURL = "${issuesURL}"
|
||||||
|
|
||||||
[[mods]]
|
[[mods]]
|
||||||
modId = "bettercontrols"
|
modId = "${id}"
|
||||||
version = "${version}"
|
version = "${version}"
|
||||||
displayName = "${name}"
|
displayName = "${name}"
|
||||||
|
description = "${description}"
|
||||||
logoFile = "icon.png"
|
logoFile = "icon.png"
|
||||||
description = '''
|
|
||||||
Adds many powerful key bindings and options to control your movement.
|
|
||||||
The features complement vanilla mechanics without giving unfair advantages, so server use should be fine.
|
|
||||||
'''
|
|
||||||
|
|
||||||
[[dependencies.bettercontrols]]
|
[[dependencies.${id}]]
|
||||||
modId = "minecraft"
|
modId = "minecraft"
|
||||||
mandatory = true
|
mandatory = true
|
||||||
versionRange = "[1.17.1,)"
|
versionRange = "[1.17.1,)"
|
||||||
ordering = "NONE"
|
ordering = "NONE"
|
||||||
side = "BOTH"
|
side = "BOTH"
|
||||||
|
|
||||||
[[dependencies.bettercontrols]]
|
[[dependencies.${id}]]
|
||||||
modId = "forge"
|
modId = "forge"
|
||||||
mandatory = true
|
mandatory = true
|
||||||
versionRange = "[37,)"
|
versionRange = "[37,)"
|
||||||
|
@@ -4,8 +4,13 @@ import java.util.Date
|
|||||||
|
|
||||||
val modId: String by project
|
val modId: String by project
|
||||||
val modName: String by project
|
val modName: String by project
|
||||||
|
val modDescription: String by project
|
||||||
val modAuthor: String by project
|
val modAuthor: String by project
|
||||||
val modVersion: String by project
|
val modVersion: String by project
|
||||||
|
val modLicense: String by project
|
||||||
|
val modSourcesURL: String by project
|
||||||
|
val modIssuesURL: String by project
|
||||||
|
|
||||||
val minecraftVersion: String by project
|
val minecraftVersion: String by project
|
||||||
val mixinVersion: String by project
|
val mixinVersion: String by project
|
||||||
|
|
||||||
@@ -26,7 +31,17 @@ plugins {
|
|||||||
|
|
||||||
idea {
|
idea {
|
||||||
module {
|
module {
|
||||||
excludeDirs.add(project.file("run"))
|
excludeDirs.add(file("gradle"))
|
||||||
|
excludeDirs.add(file("run"))
|
||||||
|
excludeDirs.add(file("Fabric/run"))
|
||||||
|
|
||||||
|
if (findProject(":Forge") == null) {
|
||||||
|
excludeDirs.add(file("Forge"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findProject(":Fabric") == null) {
|
||||||
|
excludeDirs.add(file("Fabric"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,12 +75,12 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extensions.getByType<JavaPluginExtension>().apply {
|
extensions.getByType<JavaPluginExtension>().apply {
|
||||||
toolchain.languageVersion.set(JavaLanguageVersion.of(16))
|
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
tasks.withType<JavaCompile> {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
options.release.set(16)
|
options.release.set(17)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,10 +102,18 @@ subprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
from(rootProject.sourceSets.main.get().resources)
|
inputs.property("id", modId)
|
||||||
|
|
||||||
inputs.property("name", modName)
|
inputs.property("name", modName)
|
||||||
|
inputs.property("description", modDescription)
|
||||||
inputs.property("version", modVersion)
|
inputs.property("version", modVersion)
|
||||||
|
inputs.property("author", modAuthor)
|
||||||
|
inputs.property("license", modLicense)
|
||||||
|
inputs.property("sourcesURL", modSourcesURL)
|
||||||
|
inputs.property("issuesURL", modIssuesURL)
|
||||||
|
|
||||||
|
from(rootProject.sourceSets.main.get().resources) {
|
||||||
|
expand(inputs.properties)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.jar {
|
tasks.jar {
|
||||||
@@ -111,19 +134,40 @@ subprojects {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
onlyIf { false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("setupIdea") {
|
||||||
|
group = "mod"
|
||||||
|
|
||||||
|
dependsOn(tasks.findByName("decompile"))
|
||||||
|
|
||||||
|
val forge = findProject(":Forge")
|
||||||
|
if (forge != null) {
|
||||||
|
dependsOn(forge.tasks.getByName("genIntellijRuns"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val fabric = findProject(":Fabric")
|
||||||
|
if (fabric != null) {
|
||||||
|
dependsOn(fabric.tasks.getByName("genSources"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val copyJars = tasks.register<Copy>("copyJars") {
|
val copyJars = tasks.register<Copy>("copyJars") {
|
||||||
|
group = "build"
|
||||||
duplicatesStrategy = EXCLUDE
|
duplicatesStrategy = EXCLUDE
|
||||||
|
|
||||||
for (subproject in subprojects) {
|
for (subproject in subprojects) {
|
||||||
dependsOn(subproject.tasks.build)
|
dependsOn(subproject.tasks.assemble)
|
||||||
from(subproject.base.libsDirectory.file("${subproject.base.archivesName.get()}-$jarVersion.jar"))
|
from(subproject.base.libsDirectory.file("${subproject.base.archivesName.get()}-$jarVersion.jar"))
|
||||||
}
|
}
|
||||||
|
|
||||||
into(file("${project.buildDir}/dist"))
|
into(file("${project.buildDir}/dist"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.build {
|
tasks.assemble {
|
||||||
finalizedBy(copyJars)
|
finalizedBy(copyJars)
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,17 @@
|
|||||||
# Mod
|
# Mod
|
||||||
modId=bettercontrols
|
modId=bettercontrols
|
||||||
modName=Better Controls
|
modName=Better Controls
|
||||||
|
modDescription=Adds many powerful key bindings and options to control your movement.\\n\\nThe features complement vanilla mechanics without giving unfair advantages, so server use should be fine.
|
||||||
modAuthor=chylex
|
modAuthor=chylex
|
||||||
modVersion=1.2.1
|
modVersion=1.2.3
|
||||||
|
modLicense=MPL-2.0
|
||||||
|
modSourcesURL=https://github.com/chylex/Better-Controls
|
||||||
|
modIssuesURL=https://github.com/chylex/Better-Controls/issues
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
minecraftVersion=1.17.1
|
minecraftVersion=1.18
|
||||||
forgeVersion=37.0.75
|
forgeVersion=38.0.8
|
||||||
fabricVersion=0.11.7
|
fabricVersion=0.12.8
|
||||||
mixinVersion=0.8.4
|
mixinVersion=0.8.4
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
|
@@ -4,14 +4,14 @@ import net.minecraft.client.KeyMapping;
|
|||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.Options;
|
import net.minecraft.client.Options;
|
||||||
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
||||||
import net.minecraft.client.gui.screens.controls.ControlList;
|
import net.minecraft.client.gui.screens.controls.KeyBindsList;
|
||||||
import net.minecraft.client.gui.screens.controls.ControlList.Entry;
|
import net.minecraft.client.gui.screens.controls.KeyBindsList.Entry;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
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.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(ControlList.class)
|
@Mixin(KeyBindsList.class)
|
||||||
public abstract class HookControlsListWidget extends ContainerObjectSelectionList<Entry> {
|
public abstract class HookControlsListWidget extends ContainerObjectSelectionList<Entry> {
|
||||||
public HookControlsListWidget(final Minecraft client, final int width, final int height, final int top, final int bottom, final int itemHeight) {
|
public HookControlsListWidget(final Minecraft client, final int width, final int height, final int top, final int bottom, final int itemHeight) {
|
||||||
super(client, width, height, top, bottom, itemHeight);
|
super(client, width, height, top, bottom, itemHeight);
|
||||||
|
@@ -1,28 +1,61 @@
|
|||||||
package chylex.bettercontrols.mixin;
|
package chylex.bettercontrols.mixin;
|
||||||
|
|
||||||
import chylex.bettercontrols.gui.BetterControlsScreen;
|
import chylex.bettercontrols.gui.BetterControlsScreen;
|
||||||
import net.minecraft.client.CycleOption;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.Option;
|
|
||||||
import net.minecraft.client.Options;
|
import net.minecraft.client.Options;
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import net.minecraft.client.gui.screens.OptionsSubScreen;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.client.gui.screens.controls.ControlsScreen;
|
import net.minecraft.client.gui.screens.controls.ControlsScreen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
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.Redirect;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(ControlsScreen.class)
|
@Mixin(ControlsScreen.class)
|
||||||
public abstract class HookControlsScreen {
|
public abstract class HookControlsScreen extends OptionsSubScreen {
|
||||||
@Redirect(method = "init", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/CycleOption;createButton(Lnet/minecraft/client/Options;III)Lnet/minecraft/client/gui/components/AbstractWidget;"))
|
public HookControlsScreen(final Screen parentScreen, final Options options, final Component title) {
|
||||||
private AbstractWidget replaceAutoJumpButton(final CycleOption<?> option, final Options options, final int x, final int y, final int width) {
|
super(parentScreen, options, title);
|
||||||
if (option == Option.AUTO_JUMP && !Screen.hasAltDown()) {
|
}
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
final ControlsScreen screen = (ControlsScreen)(Object)this;
|
@Inject(method = "init", at = @At("RETURN"))
|
||||||
return new Button(x, y, width, 20, BetterControlsScreen.TITLE.plainCopy().append("..."), btn -> Minecraft.getInstance().setScreen(new BetterControlsScreen(screen)));
|
public void afterInit(final CallbackInfo ci) {
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
final ControlsScreen screen = (ControlsScreen)(Object)this;
|
||||||
|
final int center = width / 2;
|
||||||
|
|
||||||
|
int leftBottomY = 0;
|
||||||
|
int rightBottomY = 0;
|
||||||
|
|
||||||
|
for (final GuiEventListener child : children()) {
|
||||||
|
if (!(child instanceof AbstractWidget widget)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int bottomY = widget.y + widget.getHeight();
|
||||||
|
|
||||||
|
if (widget.x + widget.getWidth() < center) {
|
||||||
|
leftBottomY = Math.max(leftBottomY, bottomY);
|
||||||
|
}
|
||||||
|
if (widget.x >= center) {
|
||||||
|
rightBottomY = Math.max(rightBottomY, bottomY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return option.createButton(options, x, y, width);
|
final int x, y;
|
||||||
|
|
||||||
|
if (leftBottomY < rightBottomY) {
|
||||||
|
x = center - 155;
|
||||||
|
y = leftBottomY + 4;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x = center + 5;
|
||||||
|
y = rightBottomY + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
addRenderableWidget(new Button(x, y, 150, 20, BetterControlsScreen.TITLE.plainCopy().append("..."), btn -> Minecraft.getInstance().setScreen(new BetterControlsScreen(screen))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
"minVersion": "0.8",
|
"minVersion": "0.8",
|
||||||
"package": "chylex.bettercontrols.mixin",
|
"package": "chylex.bettercontrols.mixin",
|
||||||
"refmap": "bettercontrols.refmap.json",
|
"refmap": "bettercontrols.refmap.json",
|
||||||
"compatibilityLevel": "JAVA_16",
|
"compatibilityLevel": "JAVA_17",
|
||||||
"client": [
|
"client": [
|
||||||
"AccessCameraFields",
|
"AccessCameraFields",
|
||||||
"AccessClientPlayerFields",
|
"AccessClientPlayerFields",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"pack": {
|
"pack": {
|
||||||
"description": "Better Controls",
|
"description": "${name}",
|
||||||
"pack_format": 7,
|
"pack_format": 7,
|
||||||
"_comment": ""
|
"_comment": ""
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user