mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-07 01:52:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de5750e4eb | ||
|
|
71b4bad41c | ||
|
|
05d2e55d69 | ||
|
|
41fe39cf0f | ||
|
|
fa2e68be20 | ||
|
|
2139147952 |
No files matched your search
+2
-1
@@ -57,10 +57,11 @@ If you or one of your players experiences a false positive, make sure to report
|
||||
## Sponsors
|
||||
Massive thanks to the sponsors of Sonar who help keep this project running:
|
||||
|
||||
<a href="https://github.com/Hydoxl"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/107579333?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
<a href="https://github.com/Chumb3x"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/79834154?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
|
||||
### Past Sponsors
|
||||
|
||||
<a href="https://github.com/Hydoxl"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/107579333?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
<a href="https://github.com/ItzErpandX"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/84748484?v=4?s=400?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
|
||||
## Building
|
||||
|
||||
@@ -74,7 +74,8 @@ public enum ProtocolVersion {
|
||||
MINECRAFT_1_21_5(770, "1.21.5"),
|
||||
MINECRAFT_1_21_6(771, "1.21.6"),
|
||||
MINECRAFT_1_21_7(772, "1.21.7"),
|
||||
MINECRAFT_1_21_9(773, "1.21.9");
|
||||
MINECRAFT_1_21_9(773, "1.21.9"),
|
||||
MINECRAFT_1_21_11(774, "1.21.11");
|
||||
|
||||
private final int protocol;
|
||||
private final String name;
|
||||
|
||||
@@ -78,7 +78,10 @@ public enum BukkitServerVersion {
|
||||
MINECRAFT_1_21_5,
|
||||
MINECRAFT_1_21_6,
|
||||
MINECRAFT_1_21_7,
|
||||
MINECRAFT_1_21_8;
|
||||
MINECRAFT_1_21_8,
|
||||
MINECRAFT_1_21_9,
|
||||
MINECRAFT_1_21_10,
|
||||
MINECRAFT_1_21_11;
|
||||
|
||||
static final BukkitServerVersion[] REVERSED_VALUES;
|
||||
|
||||
|
||||
@@ -49,8 +49,11 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final Color[] COLORS = new Color[4];
|
||||
private static final float[] COLOR_FRACTIONS = new float[COLORS.length];
|
||||
static final Font FONT;
|
||||
|
||||
static {
|
||||
FONT = TTFFontProvider.loadFont("/assets/fonts/Kingthings_Trypewriter_2.ttf");
|
||||
|
||||
FBM.setAmount(0.6f);
|
||||
FBM.setScale(15);
|
||||
|
||||
@@ -124,8 +127,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
|
||||
for (int i = 0; i < answer.length; i++) {
|
||||
// Create a glyph vector for the character with a random font
|
||||
final Font font = StandardTTFFontProvider.FONTS[RANDOM.nextInt(StandardTTFFontProvider.FONTS.length)];
|
||||
glyphs[i] = font.createGlyphVector(ctx, String.valueOf(answer[i]));
|
||||
glyphs[i] = FONT.createGlyphVector(ctx, String.valueOf(answer[i]));
|
||||
}
|
||||
|
||||
final double scalingXY = 5 - Math.min(answer.length, 5) * 0.65;
|
||||
@@ -135,7 +137,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
.mapToDouble(glyph -> glyph.getLogicalBounds().getWidth() * scalingXY - 1)
|
||||
.sum();
|
||||
double beginX = Math.max(Math.min(width / 2D - totalWidth / 2D, totalWidth), 0);
|
||||
double beginY = (height + StandardTTFFontProvider.STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
double beginY = (height + TTFFontProvider.STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
|
||||
// Draw each glyph one by one
|
||||
for (final GlyphVector glyph : glyphs) {
|
||||
|
||||
+2
-12
@@ -17,26 +17,16 @@
|
||||
|
||||
package xyz.jonesdev.sonar.captcha;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
@UtilityClass
|
||||
class StandardTTFFontProvider {
|
||||
private static final String[] FONT_NAMES = {"Kingthings_Trypewriter_2"};
|
||||
static final Font[] FONTS = new Font[FONT_NAMES.length];
|
||||
public final class TTFFontProvider {
|
||||
static final int STANDARD_FONT_SIZE = 25;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < FONT_NAMES.length; i++) {
|
||||
FONTS[i] = loadFont(String.format("/assets/fonts/%s.ttf", FONT_NAMES[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private static Font loadFont(final @NotNull String path) {
|
||||
public static Font loadFont(final @NotNull String path) {
|
||||
try (final InputStream inputStream = StandardCaptchaGenerator.class.getResourceAsStream(path)) {
|
||||
// Load the font from the TTF file
|
||||
final Font customFont = Font.createFont(Font.TRUETYPE_FONT, Objects.requireNonNull(inputStream));
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.captcha.complex;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.TTFFontProvider;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.QuadCurve2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class ComplexCaptchaGenerator implements CaptchaGenerator {
|
||||
private final File background;
|
||||
private final int width = 128, height = 128;
|
||||
private BufferedImage backgroundImage;
|
||||
|
||||
private static final Font[] FONTS = {
|
||||
TTFFontProvider.loadFont("/assets/fonts/dotness.ttf"), // by http://bythebutterfly.com/
|
||||
TTFFontProvider.loadFont("/assets/fonts/DotMatrix.ttf"),
|
||||
};
|
||||
private static final Random RANDOM = new SecureRandom();
|
||||
private static final double MAX_ROTATION_RADIANS = Math.toRadians(15);
|
||||
private static final int LINE_COUNT = 8;
|
||||
private static final int MIN_LINE_COLOR = 100;
|
||||
private static final int MAX_TEXT_COLOR = 150;
|
||||
private static final int DOT_COUNT = 100;
|
||||
|
||||
private @NotNull BufferedImage createBackgroundImage() {
|
||||
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
final Graphics2D graphics = image.createGraphics();
|
||||
if (background == null) {
|
||||
graphics.setBackground(Color.WHITE);
|
||||
graphics.clearRect(0, 0, image.getWidth(), image.getHeight());
|
||||
return image;
|
||||
}
|
||||
if (backgroundImage == null) {
|
||||
try {
|
||||
backgroundImage = ImageIO.read(background);
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Could not read background image", exception);
|
||||
}
|
||||
}
|
||||
graphics.drawImage(backgroundImage, 0, 0, Color.WHITE, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BufferedImage createImage(char @NotNull [] answer) {
|
||||
final BufferedImage image = createBackgroundImage();
|
||||
final Graphics2D graphics = image.createGraphics();
|
||||
|
||||
final String text = new String(answer);
|
||||
final int padding = 12;
|
||||
final double maxWidth = width - 2D * padding;
|
||||
final double maxHeight = height - 2D * padding;
|
||||
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
final FontRenderContext frc = graphics.getFontRenderContext();
|
||||
float optimalSize = 24;
|
||||
final float trialSize = 100;
|
||||
|
||||
final Rectangle2D trialBounds = FONTS[0].deriveFont(trialSize).getStringBounds(text, frc);
|
||||
|
||||
if (trialBounds.getWidth() > 0 && trialBounds.getHeight() > 0) {
|
||||
final double scaleFactorWidth = maxWidth / trialBounds.getWidth();
|
||||
final double scaleFactorHeight = maxHeight / trialBounds.getHeight();
|
||||
optimalSize = (float) (trialSize * Math.min(scaleFactorWidth, scaleFactorHeight));
|
||||
}
|
||||
|
||||
if (optimalSize < 1f) {
|
||||
optimalSize = 1f;
|
||||
}
|
||||
|
||||
final Font finalFont = FONTS[0].deriveFont(optimalSize);
|
||||
graphics.setFont(finalFont);
|
||||
|
||||
final Rectangle2D fullBounds = finalFont.getStringBounds(text, frc);
|
||||
|
||||
double currentX = (width - fullBounds.getWidth()) / 2D;
|
||||
|
||||
final double baselineY = (height / 2D) - (fullBounds.getHeight() / 2D) - fullBounds.getY();
|
||||
double currentY = baselineY;
|
||||
|
||||
for (final char c : answer) {
|
||||
final Font font = FONTS[RANDOM.nextInt(FONTS.length)].deriveFont(optimalSize);
|
||||
graphics.setFont(font);
|
||||
|
||||
final String character = String.valueOf(c);
|
||||
final Rectangle2D charBounds = font.getStringBounds(character, frc);
|
||||
|
||||
final double charWidth = charBounds.getWidth();
|
||||
final double charHeight = charBounds.getHeight();
|
||||
final double charCenterX = currentX + charWidth / 2D;
|
||||
final double charCenterY = baselineY + charBounds.getY() + charHeight / 2D;
|
||||
|
||||
final Color[] colors = new Color[2 + RANDOM.nextInt(2)];
|
||||
final float[] colorFractions = new float[colors.length];
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
colors[i] = getRandomTextColor();
|
||||
colorFractions[i] = i / (float) colors.length;
|
||||
}
|
||||
|
||||
graphics.setPaint(new RadialGradientPaint((float) currentX, (float) currentY, (float) charWidth,
|
||||
colorFractions, colors, MultipleGradientPaint.CycleMethod.REFLECT));
|
||||
|
||||
final double rotation = (RANDOM.nextDouble() * 2 * MAX_ROTATION_RADIANS) - MAX_ROTATION_RADIANS;
|
||||
|
||||
final AffineTransform originalTransform = graphics.getTransform();
|
||||
|
||||
graphics.rotate(rotation, charCenterX, charCenterY);
|
||||
|
||||
final TextLayout layout = new TextLayout(character, font, frc);
|
||||
|
||||
final AffineTransform translate = AffineTransform.getTranslateInstance(currentX, currentY);
|
||||
final Shape outline = layout.getOutline(translate);
|
||||
graphics.fill(outline);
|
||||
graphics.setTransform(originalTransform);
|
||||
|
||||
currentX += charWidth;
|
||||
currentY += RANDOM.nextDouble(charHeight / 4) - charHeight / 8;
|
||||
}
|
||||
|
||||
drawNoiseLines(graphics);
|
||||
drawNoiseDots(graphics);
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
private @NotNull Color getRandomTextColor() {
|
||||
final int r = Math.min(RANDOM.nextInt(256), MAX_TEXT_COLOR);
|
||||
final int g = Math.min(RANDOM.nextInt(256), MAX_TEXT_COLOR);
|
||||
final int b = Math.min(RANDOM.nextInt(256), MAX_TEXT_COLOR);
|
||||
return new Color(r, g, b, 220);
|
||||
}
|
||||
|
||||
private @NotNull Color getRandomColor() {
|
||||
final int r = Math.max(RANDOM.nextInt(256), MIN_LINE_COLOR);
|
||||
final int g = Math.max(RANDOM.nextInt(256), MIN_LINE_COLOR);
|
||||
final int b = Math.max(RANDOM.nextInt(256), MIN_LINE_COLOR);
|
||||
final int a = Math.max(Math.min(RANDOM.nextInt(256), 200), MIN_LINE_COLOR);
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
|
||||
private void drawNoiseLines(final @NotNull Graphics2D graphics) {
|
||||
for (int i = 0; i < LINE_COUNT; i++) {
|
||||
graphics.setColor(getRandomColor());
|
||||
graphics.setStroke(new BasicStroke(1f + RANDOM.nextFloat() * 2f,
|
||||
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
|
||||
if (RANDOM.nextBoolean()) {
|
||||
float x1 = RANDOM.nextInt(width);
|
||||
float y1 = RANDOM.nextInt(height);
|
||||
float ctrlX = RANDOM.nextInt(width);
|
||||
float ctrlY = RANDOM.nextInt(height);
|
||||
float x2 = RANDOM.nextInt(width);
|
||||
float y2 = RANDOM.nextInt(height);
|
||||
|
||||
graphics.draw(new QuadCurve2D.Float(x1, y1, ctrlX, ctrlY, x2, y2));
|
||||
} else {
|
||||
int x1 = RANDOM.nextInt(width);
|
||||
int y1 = RANDOM.nextInt(height);
|
||||
int x2 = RANDOM.nextInt(width);
|
||||
int y2 = RANDOM.nextInt(height);
|
||||
|
||||
graphics.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawNoiseDots(final @NotNull Graphics2D graphics) {
|
||||
for (int i = 0; i < DOT_COUNT; i++) {
|
||||
graphics.setColor(getRandomColor());
|
||||
|
||||
final int x = RANDOM.nextInt(width);
|
||||
final int y = RANDOM.nextInt(height);
|
||||
final int radius = 1 + RANDOM.nextInt(5);
|
||||
|
||||
graphics.fillOval(x, y, radius, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.captcha.filters;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class CircleInverseFilter {
|
||||
private final int amount, minRadius, maxRadiusExpansion;
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
public void transform(final @NotNull BufferedImage image) {
|
||||
final int minX = image.getWidth() / 4;
|
||||
final int minY = image.getHeight() / 4;
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
// Generate random position and radius for the circle
|
||||
final float centerX = minX + RANDOM.nextInt(image.getWidth() - minX * 2);
|
||||
final float centerY = minY + RANDOM.nextInt(image.getHeight() - minY * 2);
|
||||
final int radius = minRadius + RANDOM.nextInt(maxRadiusExpansion);
|
||||
|
||||
final Circle circle = new Circle(centerX, centerY, radius);
|
||||
// Draw inverted circle
|
||||
_addInvertedCircle(image, circle);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _addInvertedCircle(final @NotNull BufferedImage bufferedImage,
|
||||
final @NotNull Circle circle) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x++) {
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y++) {
|
||||
if (isWithinCircle(x, y, circle, circle.radius)) {
|
||||
// Invert colors within the circle
|
||||
final int rgb = bufferedImage.getRGB(x, y);
|
||||
final int invertedRGB = invertColorAndFilter(rgb);
|
||||
bufferedImage.setRGB(x, y, invertedRGB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWithinCircle(final float x, final float y, final @NotNull Circle circle, int radius) {
|
||||
final float distanceX = x - circle.centerX;
|
||||
final float distanceY = y - circle.centerY;
|
||||
return distanceX * distanceX + distanceY * distanceY <= radius * radius;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
static final class Circle {
|
||||
private final float centerX;
|
||||
private final float centerY;
|
||||
private final int radius;
|
||||
}
|
||||
|
||||
private static int invertColorAndFilter(final int rgb) {
|
||||
final Color color = new Color(rgb);
|
||||
final int red = Math.max(Color.LIGHT_GRAY.getRed() - color.getRed(), 0);
|
||||
final int green = Math.max(Color.LIGHT_GRAY.getGreen() - color.getGreen(), 0);
|
||||
final int blue = Math.max(Color.LIGHT_GRAY.getBlue() - color.getBlue(), 0);
|
||||
return new Color(red, green, blue).getRGB();
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.captcha.filters;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class GridOverlayFilter {
|
||||
private final int amount, lineWidth, randomOffset;
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
public void transform(final @NotNull BufferedImage image,
|
||||
final @NotNull Graphics2D graphics) {
|
||||
final int spacingX = image.getWidth() / amount + lineWidth;
|
||||
final int spacingY = image.getHeight() / amount + lineWidth;
|
||||
int currentX = spacingX / 2;
|
||||
int currentY = spacingY / 2;
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
graphics.setStroke(new BasicStroke(lineWidth * 0.5f + lineWidth * RANDOM.nextFloat()));
|
||||
graphics.drawLine(currentX, 0, currentX + RANDOM.nextInt(randomOffset), image.getHeight());
|
||||
graphics.drawLine(0, currentY, image.getWidth(), currentY + RANDOM.nextInt(randomOffset));
|
||||
|
||||
currentX += spacingX + lineWidth;
|
||||
currentY += spacingY + lineWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
My fonts are free for personal use. You also MAY redistribute the fonts on your website as long as you guve credit to http://bythebutterfly.com as the creator of the fonts. If you are interested in commercial use, please email me at BYTHEBUTTERFLY@GMAIL.COM
|
||||
|
||||
Thanks, and enjoy :)
|
||||
|
||||
Vanessa
|
||||
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import xyz.jonesdev.sonar.captcha.StandardCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.api.fallback.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.complex.ComplexCaptchaGenerator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -28,7 +29,7 @@ import java.util.Random;
|
||||
public class Main {
|
||||
public void main(final String... args) throws IOException {
|
||||
final Random random = new Random();
|
||||
final StandardCaptchaGenerator standardCaptchaGenerator = new StandardCaptchaGenerator(null);
|
||||
final CaptchaGenerator standardCaptchaGenerator = new ComplexCaptchaGenerator(null);
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
final char[] dictionary = {'a', 'b', 'c', 'd', 'e', 'f'/*, 'g'*/, 'h'/*, 'i'*/, 'j',
|
||||
|
||||
+19
-11
@@ -28,6 +28,7 @@ import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.event.impl.CaptchaGenerationStartEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.captcha.StandardCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.complex.ComplexCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.legacy.LegacyCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockUpdate;
|
||||
@@ -96,6 +97,7 @@ public class FallbackPreparer {
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_21_2 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_2);
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_21_4 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_4);
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_21_5 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_5);
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_21_11 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_11);
|
||||
public final FallbackPacket START_WRITING_CHUNKS = new GameEventPacket(13, 0);
|
||||
public final static FallbackPacket INVALID_HELD_ITEM_SLOT = new SetHeldItemPacket(-1);
|
||||
public final FallbackPacket RANDOM_KEEP_ALIVE = new FallbackPacketSnapshot(new KeepAlivePacket(RANDOM.nextInt()));
|
||||
@@ -234,7 +236,6 @@ public class FallbackPreparer {
|
||||
|
||||
if (Sonar.get0().getConfig().getVerification().getMap().getTiming() != SonarConfiguration.Verification.Timing.NEVER
|
||||
|| Sonar.get0().getConfig().getVerification().getGravity().isCaptchaOnFail()) {
|
||||
// Prepare CAPTCHA messages
|
||||
enterCodeMessage = new FallbackPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get0().getConfig().getMessagesConfig().getString("verification.captcha.enter"),
|
||||
@@ -247,7 +248,6 @@ public class FallbackPreparer {
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("attempts-left", String.valueOf(i + 1))))));
|
||||
}
|
||||
// Prepare countdown
|
||||
xpCountdown = new FallbackPacket[Sonar.get0().getConfig().getVerification().getMap().getMaxDuration() / 1000];
|
||||
|
||||
for (int i = 0; i < xpCountdown.length; i++) {
|
||||
@@ -255,27 +255,33 @@ public class FallbackPreparer {
|
||||
xpCountdown[i] = new FallbackPacketSnapshot(new SetExperiencePacket(bar, i, 0));
|
||||
}
|
||||
|
||||
// Update the CAPTCHA generator if necessary
|
||||
final CaptchaGenerationStartEvent generationStartEvent = new CaptchaGenerationStartEvent(
|
||||
Sonar.get0().getFallback().getCaptchaGenerator());
|
||||
Sonar.get0().getEventManager().publish(generationStartEvent);
|
||||
|
||||
if (generationStartEvent.getCaptchaGenerator() == null
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof StandardCaptchaGenerator
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof LegacyCaptchaGenerator) {
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof LegacyCaptchaGenerator
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof ComplexCaptchaGenerator) {
|
||||
final File backgroundImage = Sonar.get0().getConfig().getVerification().getMap().getBackgroundImage();
|
||||
final boolean useLegacyCaptchaGenerator = Sonar.get0().getConfig().getGeneralConfig()
|
||||
.getString("verification.checks.map-captcha.style").equalsIgnoreCase("legacy");
|
||||
Sonar.get0().getFallback().setCaptchaGenerator(useLegacyCaptchaGenerator
|
||||
? new LegacyCaptchaGenerator(backgroundImage) : new StandardCaptchaGenerator(backgroundImage));
|
||||
switch (Sonar.get0().getConfig().getGeneralConfig()
|
||||
.getString("verification.checks.map-captcha.style").toLowerCase()) {
|
||||
case "legacy":
|
||||
Sonar.get0().getFallback().setCaptchaGenerator(new LegacyCaptchaGenerator(backgroundImage));
|
||||
break;
|
||||
case "complex":
|
||||
Sonar.get0().getFallback().setCaptchaGenerator(new ComplexCaptchaGenerator(backgroundImage));
|
||||
break;
|
||||
default:
|
||||
Sonar.get0().getFallback().setCaptchaGenerator(new StandardCaptchaGenerator(backgroundImage));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Sonar.get0().getLogger().info("Custom CAPTCHA generator detected, skipping reinitialization.");
|
||||
}
|
||||
|
||||
// Prepare CAPTCHA answers
|
||||
CaptchaPreparer.prepare();
|
||||
} else {
|
||||
// Throw away if not needed
|
||||
enterCodeMessage = null;
|
||||
incorrectCaptcha = null;
|
||||
xpCountdown = null;
|
||||
@@ -290,7 +296,9 @@ public class FallbackPreparer {
|
||||
}
|
||||
|
||||
public static FallbackPacket[] getRegistryPackets(final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_5)) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_11)) {
|
||||
return REGISTRY_SYNC_1_21_11;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_5)) {
|
||||
return REGISTRY_SYNC_1_21_5;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||
return REGISTRY_SYNC_1_21_4;
|
||||
|
||||
+2
@@ -40,6 +40,7 @@ public final class DimensionRegistry {
|
||||
public final CompoundBinaryTag CODEC_1_21_2;
|
||||
public final CompoundBinaryTag CODEC_1_21_4;
|
||||
public final CompoundBinaryTag CODEC_1_21_5;
|
||||
public final CompoundBinaryTag CODEC_1_21_11;
|
||||
|
||||
static {
|
||||
CODEC_1_16 = getCodec("codec_1_16.nbt");
|
||||
@@ -54,6 +55,7 @@ public final class DimensionRegistry {
|
||||
CODEC_1_21_2 = getCodec("codec_1_21_2.nbt");
|
||||
CODEC_1_21_4 = getCodec("codec_1_21_4.nbt");
|
||||
CODEC_1_21_5 = getCodec("codec_1_21_5.nbt");
|
||||
CODEC_1_21_11 = getCodec("codec_1_21_11.nbt");
|
||||
}
|
||||
|
||||
private @NotNull CompoundBinaryTag getCodec(final @NotNull String fileName) {
|
||||
|
||||
+8
-2
@@ -64,7 +64,10 @@ public enum EntityType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 85;
|
||||
}
|
||||
return 87;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
|
||||
return 87;
|
||||
}
|
||||
return 89;
|
||||
}),
|
||||
MINECART(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
@@ -106,7 +109,10 @@ public enum EntityType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 82;
|
||||
}
|
||||
return 84;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
|
||||
return 84;
|
||||
}
|
||||
return 85;
|
||||
});
|
||||
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
|
||||
@@ -90,7 +90,10 @@ public enum ItemType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 36;
|
||||
}
|
||||
return 37;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
|
||||
return 37;
|
||||
}
|
||||
return 44;
|
||||
});
|
||||
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
|
||||
+5
-5
@@ -60,10 +60,10 @@ public final class SetContainerSlotPacket implements FallbackPacket {
|
||||
ProtocolUtil.writeVarInt(byteBuf, count);
|
||||
}
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13_2)) {
|
||||
byteBuf.writeShort(itemType.getId().apply(protocolVersion));
|
||||
} else {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_13_2)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, itemType.getId().apply(protocolVersion));
|
||||
} else {
|
||||
byteBuf.writeShort(itemType.getId().apply(protocolVersion));
|
||||
}
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
@@ -83,8 +83,8 @@ public final class SetContainerSlotPacket implements FallbackPacket {
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, compoundBinaryTag);
|
||||
} else {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // component count to add
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // component count to remove
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // addedComponentCount
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // removedComponentCount
|
||||
ProtocolUtil.writeVarInt(byteBuf, itemType.getComponents().apply(protocolVersion));
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // data
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -112,6 +112,7 @@ verification:
|
||||
background: ''
|
||||
# Styl mapy CAPTCHA (Výchozí: 'modern').
|
||||
# Chcete-li použít starý design, nastavte tuto hodnotu na 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Kolik odpovědí by měl Sonar předgenerovat?
|
||||
# Tento úkol běží souběžně v pozadí
|
||||
|
||||
@@ -109,6 +109,7 @@ verification:
|
||||
background: ''
|
||||
# Stil der Karte CAPTCHA (Standard: 'modern').
|
||||
# Um das alte Design zu verwenden, setzen Sie dies auf 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Wie viele Antworten soll Sonar vorbereiten?
|
||||
# Dieser Vorgang erfolgt asynchron im Hintergrund
|
||||
|
||||
@@ -111,6 +111,7 @@ verification:
|
||||
background: ''
|
||||
# Style of the map CAPTCHA (Default: 'modern').
|
||||
# To use the old design, set this to 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# How many CAPTCHA answers should Sonar prepare?
|
||||
# This task happens asynchronously in the background
|
||||
|
||||
@@ -111,6 +111,7 @@ verification:
|
||||
background: ''
|
||||
# Style de la carte CAPTCHA (par défaut : 'moderne').
|
||||
# Pour utiliser l'ancien design, mettez ce paramètre à 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Combien de réponses Sonar devrait-il préparer ?
|
||||
# Cette tâche se déroule de manière asynchrone en arrière-plan
|
||||
|
||||
@@ -111,6 +111,7 @@ verification:
|
||||
background: ''
|
||||
# რუკის სტილი CAPTCHA (ნაგულისხმევი: 'modern').
|
||||
# ძველი დიზაინის გამოსაყენებლად დააყენეთ ეს 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# რამდენი პასუხი უნდა მოამზადოს Sonar-მა?
|
||||
# ეს ხდება ასინქრონულად (ანუ ამის გამოთვლას სერვერი არ დაელოდება) უკანა ფონზე
|
||||
|
||||
@@ -111,6 +111,7 @@ verification:
|
||||
background: ''
|
||||
# Stijl van de CAPTCHA kaart (Standaard: 'modern').
|
||||
# Om het oude ontwerp te gebruiken, stel je dit in op 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Hoeveel antwoorden moet Sonar voorbereiden?
|
||||
# Deze taak gebeurt asynchroon op de achtergrond
|
||||
|
||||
@@ -111,6 +111,7 @@ verification:
|
||||
background: ''
|
||||
# Styl mapy CAPTCHA (domyślnie: 'nowoczesny').
|
||||
# Aby użyć starego projektu, ustaw to na 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Ile możliwych odpowiedzi powinien przygotować Sonar?
|
||||
# Zadanie to wykonywane jest asynchronicznie w tle
|
||||
|
||||
@@ -110,6 +110,7 @@ verification:
|
||||
background: ''
|
||||
# Estilo do CAPTCHA do mapa (Padrão: 'modern').
|
||||
# Para usar o design antigo, defina-o como 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Quantas respostas de CAPTCHA o Sonar deve preparar?
|
||||
# Esta tarefa acontece de forma assíncrona em segundo plano
|
||||
|
||||
@@ -110,6 +110,7 @@ verification:
|
||||
background: ''
|
||||
# Стиль капч (по умолчанию: 'modern').
|
||||
# Чтобы использовать старый дизайн, установите значение 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Сколько капч должен подготовить Sonar?
|
||||
# Эта задача выполняется асинхронно в фоновом режиме
|
||||
|
||||
@@ -110,6 +110,7 @@ verification:
|
||||
background: ''
|
||||
# 地图验证码的风格(默认:'modern').
|
||||
# 要使用旧设计, 请将其设置为 'legacy'.
|
||||
# To use an experimental design, set this to 'complex'.
|
||||
style: 'modern'
|
||||
# Sonar 应该一次准备多少个验证码?
|
||||
precompute: 500
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
group=xyz.jonesdev
|
||||
version=2.1.41
|
||||
version=2.1.42
|
||||
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
|
||||
Reference in new issue
Block a user