From 9935466a908e805350ed1b641e0af2e240641ead Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Sun, 12 Jun 2022 22:23:13 +0300 Subject: [PATCH 01/20] Implement async captcha loading --- .../java/ru/leymooo/botfilter/BotFilter.java | 6 +- .../java/ru/leymooo/botfilter/Connector.java | 5 + .../botfilter/caching/CachedCaptcha.java | 46 +++-- .../botfilter/captcha/CaptchaGeneration.java | 167 +++++++++++++----- 4 files changed, 158 insertions(+), 66 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java index 1263b994af..3a4204bf7f 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java @@ -24,7 +24,6 @@ import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.protocol.Protocol; -import ru.leymooo.botfilter.caching.CachedCaptcha; import ru.leymooo.botfilter.caching.PacketUtils; import ru.leymooo.botfilter.caching.PacketUtils.KickType; import ru.leymooo.botfilter.captcha.CaptchaGeneration; @@ -74,10 +73,7 @@ public BotFilter(boolean startup) Settings.IMP.reload( new File( "BotFilter", "config.yml" ) ); Scoreboard.DISABLE_DUBLICATE = Settings.IMP.FIX_SCOREBOARD_TEAMS; checkForUpdates( startup ); - if ( !CachedCaptcha.generated ) - { - CaptchaGeneration.generateImages(); - } + CaptchaGeneration.generateImages(); normalState = getCheckState( Settings.IMP.PROTECTION.NORMAL ); attackState = getCheckState( Settings.IMP.PROTECTION.ON_ATTACK ); PacketUtils.init(); diff --git a/proxy/src/main/java/ru/leymooo/botfilter/Connector.java b/proxy/src/main/java/ru/leymooo/botfilter/Connector.java index f084c6b441..2018988e23 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/Connector.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/Connector.java @@ -359,6 +359,11 @@ public void sendPing() private void sendCaptcha() { CaptchaHolder captchaHolder = PacketUtils.captchas.randomCaptcha(); + if ( captchaHolder == null ) + { + //TODO Кикать игрока если нет капчи + return; + } captchaAnswer = captchaHolder.getAnswer(); channel.write( PacketUtils.getCachedPacket( PacketsPosition.SETSLOT_MAP ).get( version ), channel.voidPromise() ); captchaHolder.write( channel, version, true ); diff --git a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java index 5d35ca92a9..9ac6b3e2a4 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java @@ -3,36 +3,28 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.Setter; import net.md_5.bungee.protocol.ProtocolConstants; import ru.leymooo.botfilter.packets.MapDataPacket; /** * @author Leymooo */ +@Setter public class CachedCaptcha { - - //уже пора с этим чтото придумать - //В принципе я вроде чтото придумал для версии под Velocity, но будет ли она?.... private static final int PACKETID_18 = 0x34; private static final int PACKETID_19and119 = 0x24; private static final int PACKETID_113and114and116 = 0x26; private static final int PACKETID_115and117 = 0x27; private static final int PACKETID_1162 = 0x25; + private final Random random = new Random(); + private CaptchaHolder[] captchas = null; - - private static final Random random = new Random(); - - private static final CaptchaHolder[] captchas = new CaptchaHolder[900]; - private static final AtomicInteger counter = new AtomicInteger(); - - public static boolean generated = false; - - public void createCaptchaPacket(MapDataPacket map, String answer) + public static CaptchaHolder createCaptchaPacket(MapDataPacket map, String answer) { ByteBuf byteBuf18 = PacketUtils.createPacket( map, PACKETID_18, ProtocolConstants.MINECRAFT_1_8 ); @@ -44,13 +36,36 @@ public void createCaptchaPacket(MapDataPacket map, String answer) ByteBuf byteBuf117 = PacketUtils.createPacket( map, PACKETID_115and117, ProtocolConstants.MINECRAFT_1_17 ); ByteBuf byteBuf119 = PacketUtils.createPacket( map, PACKETID_19and119, ProtocolConstants.MINECRAFT_1_19 ); - captchas[counter.getAndIncrement()] = new CaptchaHolder( answer, byteBuf18, byteBuf19, byteBuf113, byteBuf114And116, byteBuf115, byteBuf1162, byteBuf117, byteBuf119 ); + return new CaptchaHolder( answer, byteBuf18, byteBuf19, byteBuf113, byteBuf114And116, byteBuf115, byteBuf1162, byteBuf117, byteBuf119 ); + } - //TODO: Do something with this shit. + public void clearCaptcha() + { + if ( captchas == null ) + { + return; + } + for ( CaptchaHolder holder : captchas ) + { + //TODO Придумать способ очищать байты в заполнителе автоматически + holder.getBuf18().release(); + holder.getBuf19().release(); + holder.getBuf113().release(); + holder.getBuf114And116().release(); + holder.getBuf115().release(); + holder.getBuf1162().release(); + holder.getBuf117().release(); + holder.getBuf119().release(); + } + captchas = null; } public CaptchaHolder randomCaptcha() { + if ( this.captchas == null ) + { + return null; + } return captchas[random.nextInt( captchas.length )]; } @@ -63,7 +78,6 @@ public static class CaptchaHolder public void write(Channel channel, int version, boolean flush) { - if ( version == ProtocolConstants.MINECRAFT_1_8 ) { channel.write( buf18.retainedDuplicate(), channel.voidPromise() ); diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index bc8e246936..123a69313c 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -3,10 +3,17 @@ import java.awt.Color; import java.awt.Font; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import lombok.experimental.UtilityClass; import net.md_5.bungee.BungeeCord; @@ -23,56 +30,114 @@ @UtilityClass public class CaptchaGeneration { - Random rnd = new Random(); + private static volatile boolean generation = false; - public void generateImages() + public static synchronized void generateImages() { - Font[] fonts = new Font[] + if ( generation ) { - new Font( Font.SANS_SERIF, Font.PLAIN, 50 ), - new Font( Font.SERIF, Font.PLAIN, 50 ), - new Font( Font.MONOSPACED, Font.BOLD, 50 ) - }; - - ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() ); - CaptchaPainter painter = new CaptchaPainter(); - MapPalette.prepareColors(); - for ( int i = 100; i <= 999; i++ ) - { - executor.execute( () -> - { - String answer = randomAnswer(); - BufferedImage image = painter.draw( fonts[rnd.nextInt( fonts.length )], randomNotWhiteColor(), answer ); - final CraftMapCanvas map = new CraftMapCanvas(); - map.drawImage( 0, 0, image ); - MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); - PacketUtils.captchas.createCaptchaPacket( packet, answer ); - } ); + return; } - long start = System.currentTimeMillis(); - ThreadPoolExecutor ex = (ThreadPoolExecutor) executor; - while ( ex.getActiveCount() != 0 ) + generation = true; + + List fonts = Arrays.asList( + new Font( Font.SANS_SERIF, Font.PLAIN, 50 ), + new Font( Font.SERIF, Font.PLAIN, 50 ), + new Font( Font.MONOSPACED, Font.BOLD, 50 ) ); + Thread thread = new Thread( () -> { - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [{0}/900]", 900 - ex.getQueue().size() - ex.getActiveCount() ); try { - Thread.sleep( 1000L ); - } catch ( InterruptedException ex1 ) + ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), + new CaptchaThreadFactory() ); + CaptchaPainter painter = new CaptchaPainter(); + MapPalette.prepareColors(); + + int captchaCount = 900; + try + { + String captchaCountString = System.getProperty( "captchaCount" ); + if ( captchaCountString != null && !captchaCountString.isEmpty() ) + { + captchaCount = Integer.parseInt( captchaCountString ); + } + } catch ( Exception ignored ) + { + } + + if ( captchaCount <= 0 ) + { + captchaCount = 1; + } + + List holders = Collections.synchronizedList( new ArrayList<>() ); + + for ( int i = 1; i <= captchaCount; i++ ) + { + executor.execute( () -> + { + try + { + Random rnd = ThreadLocalRandom.current(); + String answer = randomAnswer( rnd ); + BufferedImage image = painter.draw( fonts.get( rnd.nextInt( fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); + final CraftMapCanvas map = new CraftMapCanvas(); + map.drawImage( 0, 0, image ); + MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); + CachedCaptcha.CaptchaHolder holder = CachedCaptcha.createCaptchaPacket( packet, answer ); + holders.add( holder ); + } catch ( Throwable e ) + { + //Прекращаем генерацию если случилась любая ошибка + e.printStackTrace(); + executor.shutdownNow(); + } + } ); + } + + long start = System.currentTimeMillis(); + ThreadPoolExecutor ex = (ThreadPoolExecutor) executor; + while ( ex.getActiveCount() != 0 ) + { + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); + + //Текущий список всех капч каждую секунду переобразуем в массив и вставляем, чтобы хоть какая та часть была доступна во время генерации + PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); + try + { + Thread.sleep( 1000L ); + } catch ( InterruptedException ex1 ) + { + BungeeCord.getInstance().getLogger().log( Level.WARNING, "[BotFilter] Не могу сгенерировать капчу. Выключаю банджу", ex1 ); + System.exit( 0 ); + return; + } + } + + executor.shutdownNow(); + + //Окончательно устанавливаем оставшиеся капчи + PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); + System.gc(); + + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерирована за {0} мс", System.currentTimeMillis() - start ); + } catch ( Exception e ) + { + e.printStackTrace(); + } finally { - BungeeCord.getInstance().getLogger().log( Level.WARNING, "[BotFilter] Не могу сгенерировать капчу. Выключаю банджу", ex1 ); - System.exit( 0 ); - return; + generation = false; } - } - CachedCaptcha.generated = true; - executor.shutdownNow(); - System.gc(); - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерированна за {0} мс", System.currentTimeMillis() - start ); + } ); + + thread.setName( "CaptchaGeneration-provider-thread" ); + thread.setPriority( Thread.MIN_PRIORITY ); + thread.start(); } - private Color randomNotWhiteColor() + private static Color randomNotWhiteColor(Random rnd) { Color color = MapPalette.colors[rnd.nextInt( MapPalette.colors.length )]; @@ -82,32 +147,31 @@ private Color randomNotWhiteColor() if ( r == 255 && g == 255 && b == 255 ) { - return randomNotWhiteColor(); + return randomNotWhiteColor( rnd ); } if ( r == 220 && g == 220 && b == 220 ) { - return randomNotWhiteColor(); + return randomNotWhiteColor( rnd ); } if ( r == 199 && g == 199 && b == 199 ) { - return randomNotWhiteColor(); + return randomNotWhiteColor( rnd ); } if ( r == 255 && g == 252 && b == 245 ) { - return randomNotWhiteColor(); + return randomNotWhiteColor( rnd ); } if ( r == 220 && g == 217 && b == 211 ) { - return randomNotWhiteColor(); + return randomNotWhiteColor( rnd ); } if ( r == 247 && g == 233 && b == 163 ) { - return randomNotWhiteColor(); + return randomNotWhiteColor( rnd ); } return color; } - - private String randomAnswer() + private static String randomAnswer(Random rnd) { if ( rnd.nextBoolean() ) { @@ -117,4 +181,17 @@ private String randomAnswer() return Integer.toString( rnd.nextInt( ( 9999 - 1000 ) + 1 ) + 1000 ); } } + //Для правильного именования потоков которые генерируют капчу и для создания низкого приоритета + private static class CaptchaThreadFactory implements ThreadFactory + { + private static final AtomicInteger counter = new AtomicInteger(); + @Override + public Thread newThread(Runnable task) + { + Thread thr = new Thread( task ); + thr.setPriority( Thread.MIN_PRIORITY ); + thr.setName( "CaptchaGenerator-thread-" + counter.incrementAndGet() ); + return thr; + } + } } From da663ce68c063af1ed1150450623232a3862d071 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Sun, 12 Jun 2022 22:50:13 +0300 Subject: [PATCH 02/20] Hide captcha generation progress messages if server not fully loaded --- proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 4 ++++ .../ru/leymooo/botfilter/captcha/CaptchaGeneration.java | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index edee54dcea..fd9b9f5a92 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -113,6 +113,9 @@ public class BungeeCord extends ProxyServer * Current operation state. */ public volatile boolean isRunning; + //BotFilter + @Getter + private volatile boolean enabled; /** * Configuration. */ @@ -334,6 +337,7 @@ public void run() independentThreadStop( getTranslation( "restart" ), false ); } } ); + enabled = true; } public void startListeners() diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 123a69313c..e00e04c245 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -49,6 +49,7 @@ public static synchronized void generateImages() { try { + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] " + ( BungeeCord.getInstance().isEnabled() ? "Начата генерация капчи в фоне." : "Генерация капчи продолжится параллельно с загрузкой BungeeCord." ) ); ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), new CaptchaThreadFactory() ); CaptchaPainter painter = new CaptchaPainter(); @@ -100,7 +101,11 @@ public static synchronized void generateImages() ThreadPoolExecutor ex = (ThreadPoolExecutor) executor; while ( ex.getActiveCount() != 0 ) { - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); + //Отображаем прогресс генерации только после полной загрузки банжи, чтобы логи не путались с другими важными логами при включении + if ( BungeeCord.getInstance().isEnabled() ) + { + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); + } //Текущий список всех капч каждую секунду переобразуем в массив и вставляем, чтобы хоть какая та часть была доступна во время генерации PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); @@ -120,7 +125,6 @@ public static synchronized void generateImages() //Окончательно устанавливаем оставшиеся капчи PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); System.gc(); - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерирована за {0} мс", System.currentTimeMillis() - start ); } catch ( Exception e ) { From 3fa23ba145b36672454ba983149ba1f7c93ca058 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 13 Jun 2022 12:04:33 +0300 Subject: [PATCH 03/20] Add generate command to regenerate captcha --- .../main/java/ru/leymooo/botfilter/BotFilter.java | 10 +++++++++- .../java/ru/leymooo/botfilter/BotFilterCommand.java | 13 +++++++++++++ .../botfilter/captcha/CaptchaGeneration.java | 4 ++-- .../captcha/CaptchaGenerationException.java | 9 +++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationException.java diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java index 3a4204bf7f..bf900319d4 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilter.java @@ -27,6 +27,7 @@ import ru.leymooo.botfilter.caching.PacketUtils; import ru.leymooo.botfilter.caching.PacketUtils.KickType; import ru.leymooo.botfilter.captcha.CaptchaGeneration; +import ru.leymooo.botfilter.captcha.CaptchaGenerationException; import ru.leymooo.botfilter.config.Settings; import ru.leymooo.botfilter.utils.GeoIp; import ru.leymooo.botfilter.utils.ManyChecksUtils; @@ -73,7 +74,14 @@ public BotFilter(boolean startup) Settings.IMP.reload( new File( "BotFilter", "config.yml" ) ); Scoreboard.DISABLE_DUBLICATE = Settings.IMP.FIX_SCOREBOARD_TEAMS; checkForUpdates( startup ); - CaptchaGeneration.generateImages(); + //Глушим исключение при попытке сгенерировать капчу. Если капча уже генерируется и бот фильтр был почему-то + //перезапущен, капча сгенерируется все равно и доступ к ней будет прежний. + try + { + CaptchaGeneration.generateImages(); + } catch ( CaptchaGenerationException ignored ) + { + } normalState = getCheckState( Settings.IMP.PROTECTION.NORMAL ); attackState = getCheckState( Settings.IMP.PROTECTION.ON_ATTACK ); PacketUtils.init(); diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java index bfeb035c6f..6d065b3d0d 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java @@ -14,6 +14,8 @@ import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.plugin.Command; +import ru.leymooo.botfilter.captcha.CaptchaGeneration; +import ru.leymooo.botfilter.captcha.CaptchaGenerationException; import ru.leymooo.botfilter.config.Settings; public class BotFilterCommand extends Command @@ -39,6 +41,7 @@ public void execute(CommandSender sender, String[] args) sender.sendMessage( "§r> §lbotfilter stat §6- §aПоказать статистику" ); sender.sendMessage( "§r> §lbotfilter export §6- §aВыгрузить список игроков, которые прошли проверку" ); sender.sendMessage( "§r> §lbotfilter protection on/off §6- §aВключить или выключить ручной режим 'под атакой'" ); + sender.sendMessage( "§r> §lbotfilter generate §6- §aСгенерировать новую капчу" ); sender.sendMessage( "§r--------------- §bBotFilter §r-----------------" ); } else if ( args[0].equalsIgnoreCase( "reload" ) ) { @@ -52,6 +55,16 @@ public void execute(CommandSender sender, String[] args) { export( sender, args ); sender.sendMessage( "§aКоманда выполнена" ); + } else if ( args[0].equalsIgnoreCase( "generate" ) ) + { + try + { + CaptchaGeneration.generateImages(); + sender.sendMessage( "§aКоманда выполнена" ); + } catch ( CaptchaGenerationException e ) + { + sender.sendMessage( "§cОшибка при попытке сгенерировать капчу: " + e.getMessage() ); + } } else if ( args[0].equalsIgnoreCase( "protection" ) ) { if ( args.length >= 2 ) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index e00e04c245..f896de15bf 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -32,11 +32,11 @@ public class CaptchaGeneration { private static volatile boolean generation = false; - public static synchronized void generateImages() + public static synchronized void generateImages() throws CaptchaGenerationException { if ( generation ) { - return; + throw new CaptchaGenerationException( "Капча уже генерируется!" ); } generation = true; diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationException.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationException.java new file mode 100644 index 0000000000..bb0f266b51 --- /dev/null +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationException.java @@ -0,0 +1,9 @@ +package ru.leymooo.botfilter.captcha; + +public class CaptchaGenerationException extends Exception +{ + public CaptchaGenerationException(String msg) + { + super( msg ); + } +} From 346d01db83cca977360908d356c6b84769f4a9e4 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 13 Jun 2022 12:25:05 +0300 Subject: [PATCH 04/20] Clear captcha first --- .../java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index f896de15bf..133e7e066a 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -49,6 +49,8 @@ public static synchronized void generateImages() throws CaptchaGenerationExcepti { try { + //Перед началом генерации, нужно удалить старую капчу, освободив байтовый буфер. + PacketUtils.captchas.clearCaptcha(); BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] " + ( BungeeCord.getInstance().isEnabled() ? "Начата генерация капчи в фоне." : "Генерация капчи продолжится параллельно с загрузкой BungeeCord." ) ); ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), new CaptchaThreadFactory() ); From cb2aabd8fb100b6db30eb04225068b7b253882cb Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 13 Jun 2022 12:37:56 +0300 Subject: [PATCH 05/20] Generate captcha every 6 hours --- .../ru/leymooo/botfilter/BotFilterThread.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java index 798d185677..2c6fdb473d 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java @@ -7,6 +7,8 @@ import ru.leymooo.botfilter.BotFilter.CheckState; import ru.leymooo.botfilter.caching.PacketUtils.KickType; import ru.leymooo.botfilter.caching.PacketsPosition; +import ru.leymooo.botfilter.captcha.CaptchaGeneration; +import ru.leymooo.botfilter.captcha.CaptchaGenerationException; import ru.leymooo.botfilter.config.Settings; import ru.leymooo.botfilter.utils.FailedUtils; import ru.leymooo.botfilter.utils.ManyChecksUtils; @@ -107,12 +109,13 @@ public static void startCleanUpThread() { Thread t = new Thread( () -> { - byte counter = 0; + byte counterClean = 0; + int counterCaptcha = 0; while ( !Thread.interrupted() && sleep( 5 * 1000 ) ) { - if ( ++counter == 12 ) + if ( ++counterClean == 12 ) { - counter = 0; + counterClean = 0; ManyChecksUtils.cleanUP(); if ( bungee.getBotFilter() != null ) { @@ -132,6 +135,19 @@ public static void startCleanUpThread() } } FailedUtils.flushQueue(); + //TODO Не понятно, долго или часто это пока что + //Каждые 6 часов капча сама регенерируется + if ( ++counterCaptcha == ( 12 * 60 ) * 6 ) + { + counterCaptcha = 0; + + try + { + CaptchaGeneration.generateImages(); + } catch ( CaptchaGenerationException ignored ) + { + } + } } }, "CleanUp thread" ); t.setDaemon( true ); From 9b404a1aa906814010ffb42d75bc8ae91e9d7fb6 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:02:59 +0300 Subject: [PATCH 06/20] Kick player if captcha is not generated --- proxy/src/main/java/ru/leymooo/botfilter/Connector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/Connector.java b/proxy/src/main/java/ru/leymooo/botfilter/Connector.java index 2018988e23..c8c265a7cb 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/Connector.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/Connector.java @@ -361,7 +361,7 @@ private void sendCaptcha() CaptchaHolder captchaHolder = PacketUtils.captchas.randomCaptcha(); if ( captchaHolder == null ) { - //TODO Кикать игрока если нет капчи + failed( KickType.FAILED_CAPTCHA, "Captcha was not generated" ); return; } captchaAnswer = captchaHolder.getAnswer(); From 71ba5f715aafd32316e45d09e3e4e5e5a53e310b Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:10:31 +0300 Subject: [PATCH 07/20] Fix 0 length random captcha --- .../main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java index 9ac6b3e2a4..d2cbf21e30 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java @@ -66,6 +66,10 @@ public CaptchaHolder randomCaptcha() { return null; } + if ( this.captchas.length == 0 ) + { + return null; + } return captchas[random.nextInt( captchas.length )]; } From 89f30c55a953ff6b684dbf417c5d4b6a280f582c Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 13 Jun 2022 20:18:06 +0300 Subject: [PATCH 08/20] Little improve CaptchaGeneration --- .../botfilter/captcha/CaptchaGeneration.java | 172 +++++++++--------- 1 file changed, 87 insertions(+), 85 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 133e7e066a..13b33f9155 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -40,108 +40,110 @@ public static synchronized void generateImages() throws CaptchaGenerationExcepti } generation = true; - - List fonts = Arrays.asList( - new Font( Font.SANS_SERIF, Font.PLAIN, 50 ), - new Font( Font.SERIF, Font.PLAIN, 50 ), - new Font( Font.MONOSPACED, Font.BOLD, 50 ) ); - Thread thread = new Thread( () -> + Thread thread = new Thread( CaptchaGeneration::generateCaptchas ); + thread.setName( "CaptchaGeneration-provider-thread" ); + thread.setPriority( Thread.MIN_PRIORITY ); + thread.start(); + } + private static void generateCaptchas() + { + try { + List fonts = Arrays.asList( + new Font( Font.SANS_SERIF, Font.PLAIN, 50 ), + new Font( Font.SERIF, Font.PLAIN, 50 ), + new Font( Font.MONOSPACED, Font.BOLD, 50 ) ); + //Перед началом генерации, нужно удалить старую капчу, освободив байтовый буфер. + PacketUtils.captchas.clearCaptcha(); + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] " + ( BungeeCord.getInstance().isEnabled() ? "Начата генерация капчи в фоне." : "Генерация капчи продолжится параллельно с загрузкой BungeeCord." ) ); + ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), + new CaptchaThreadFactory() ); + CaptchaPainter painter = new CaptchaPainter(); + MapPalette.prepareColors(); + + int captchaCount = 900; try { - //Перед началом генерации, нужно удалить старую капчу, освободив байтовый буфер. - PacketUtils.captchas.clearCaptcha(); - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] " + ( BungeeCord.getInstance().isEnabled() ? "Начата генерация капчи в фоне." : "Генерация капчи продолжится параллельно с загрузкой BungeeCord." ) ); - ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), - new CaptchaThreadFactory() ); - CaptchaPainter painter = new CaptchaPainter(); - MapPalette.prepareColors(); - - int captchaCount = 900; - try - { - String captchaCountString = System.getProperty( "captchaCount" ); - if ( captchaCountString != null && !captchaCountString.isEmpty() ) - { - captchaCount = Integer.parseInt( captchaCountString ); - } - } catch ( Exception ignored ) + //TODO Как лучше быть? Использовать конфиг или через аргумент запуска? + String captchaCountString = System.getProperty( "captchaCount" ); + if ( captchaCountString != null && !captchaCountString.isEmpty() ) { + captchaCount = Integer.parseInt( captchaCountString ); } + } catch ( Exception ignored ) + { + } - if ( captchaCount <= 0 ) - { - captchaCount = 1; - } + if ( captchaCount <= 0 ) + { + captchaCount = 1; + } - List holders = Collections.synchronizedList( new ArrayList<>() ); + List holders = Collections.synchronizedList( new ArrayList<>() ); - for ( int i = 1; i <= captchaCount; i++ ) - { - executor.execute( () -> - { - try - { - Random rnd = ThreadLocalRandom.current(); - String answer = randomAnswer( rnd ); - BufferedImage image = painter.draw( fonts.get( rnd.nextInt( fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); - final CraftMapCanvas map = new CraftMapCanvas(); - map.drawImage( 0, 0, image ); - MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); - CachedCaptcha.CaptchaHolder holder = CachedCaptcha.createCaptchaPacket( packet, answer ); - holders.add( holder ); - } catch ( Throwable e ) - { - //Прекращаем генерацию если случилась любая ошибка - e.printStackTrace(); - executor.shutdownNow(); - } - } ); - } + for ( int i = 1; i <= captchaCount; i++ ) + { + executor.execute( () -> generate( executor, painter, fonts, holders ) ); + } - long start = System.currentTimeMillis(); - ThreadPoolExecutor ex = (ThreadPoolExecutor) executor; - while ( ex.getActiveCount() != 0 ) + long start = System.currentTimeMillis(); + ThreadPoolExecutor ex = (ThreadPoolExecutor) executor; + while ( ex.getActiveCount() != 0 ) + { + //Отображаем прогресс генерации только после полной загрузки банжи, чтобы логи не путались с другими важными логами при включении + if ( BungeeCord.getInstance().isEnabled() ) { - //Отображаем прогресс генерации только после полной загрузки банжи, чтобы логи не путались с другими важными логами при включении - if ( BungeeCord.getInstance().isEnabled() ) - { - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); - } - - //Текущий список всех капч каждую секунду переобразуем в массив и вставляем, чтобы хоть какая та часть была доступна во время генерации - PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); - try - { - Thread.sleep( 1000L ); - } catch ( InterruptedException ex1 ) - { - BungeeCord.getInstance().getLogger().log( Level.WARNING, "[BotFilter] Не могу сгенерировать капчу. Выключаю банджу", ex1 ); - System.exit( 0 ); - return; - } + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); } - executor.shutdownNow(); - - //Окончательно устанавливаем оставшиеся капчи + //Текущий список всех капч каждую секунду переобразуем в массив и вставляем, чтобы хоть какая та часть была доступна во время генерации PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); - System.gc(); - BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерирована за {0} мс", System.currentTimeMillis() - start ); - } catch ( Exception e ) - { - e.printStackTrace(); - } finally - { - generation = false; + try + { + Thread.sleep( 1000L ); + } catch ( InterruptedException ex1 ) + { + BungeeCord.getInstance().getLogger().log( Level.WARNING, "[BotFilter] Не могу сгенерировать капчу. Выключаю банджу", ex1 ); + System.exit( 0 ); + return; + } } - } ); - thread.setName( "CaptchaGeneration-provider-thread" ); - thread.setPriority( Thread.MIN_PRIORITY ); - thread.start(); + executor.shutdownNow(); + + //Окончательно устанавливаем оставшиеся капчи + PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); + System.gc(); + BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерирована за {0} мс", System.currentTimeMillis() - start ); + } catch ( Exception e ) + { + e.printStackTrace(); + } finally + { + generation = false; + } } + private static void generate(ExecutorService executor, CaptchaPainter painter, List fonts, + List holders) + { + try + { + Random rnd = ThreadLocalRandom.current(); + String answer = randomAnswer( rnd ); + BufferedImage image = painter.draw( fonts.get( rnd.nextInt( fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); + final CraftMapCanvas map = new CraftMapCanvas(); + map.drawImage( 0, 0, image ); + MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); + CachedCaptcha.CaptchaHolder holder = CachedCaptcha.createCaptchaPacket( packet, answer ); + holders.add( holder ); + } catch ( Throwable e ) + { + //Прекращаем генерацию если случилась любая ошибка + e.printStackTrace(); + executor.shutdownNow(); + } + } private static Color randomNotWhiteColor(Random rnd) { From 474e915ecaaf7bc379003d6b51ad687791ad5977 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 19:44:00 +0300 Subject: [PATCH 09/20] Create method for release buff in captcha holder --- .../botfilter/caching/CachedCaptcha.java | 23 +++++++++++-------- .../botfilter/captcha/CaptchaGeneration.java | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java index d2cbf21e30..df43af04e6 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java @@ -39,7 +39,7 @@ public static CaptchaHolder createCaptchaPacket(MapDataPacket map, String answer return new CaptchaHolder( answer, byteBuf18, byteBuf19, byteBuf113, byteBuf114And116, byteBuf115, byteBuf1162, byteBuf117, byteBuf119 ); } - public void clearCaptcha() + public void clear() { if ( captchas == null ) { @@ -47,15 +47,7 @@ public void clearCaptcha() } for ( CaptchaHolder holder : captchas ) { - //TODO Придумать способ очищать байты в заполнителе автоматически - holder.getBuf18().release(); - holder.getBuf19().release(); - holder.getBuf113().release(); - holder.getBuf114And116().release(); - holder.getBuf115().release(); - holder.getBuf1162().release(); - holder.getBuf117().release(); - holder.getBuf119().release(); + holder.release(); } captchas = null; } @@ -118,5 +110,16 @@ public void write(Channel channel, int version, boolean flush) channel.flush(); } } + public void release() + { + buf18.release(); + buf19.release(); + buf113.release(); + buf114And116.release(); + buf115.release(); + buf1162.release(); + buf117.release(); + buf119.release(); + } } } diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 13b33f9155..e8474ea201 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -54,7 +54,7 @@ private static void generateCaptchas() new Font( Font.SERIF, Font.PLAIN, 50 ), new Font( Font.MONOSPACED, Font.BOLD, 50 ) ); //Перед началом генерации, нужно удалить старую капчу, освободив байтовый буфер. - PacketUtils.captchas.clearCaptcha(); + PacketUtils.captchas.clear(); BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] " + ( BungeeCord.getInstance().isEnabled() ? "Начата генерация капчи в фоне." : "Генерация капчи продолжится параллельно с загрузкой BungeeCord." ) ); ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), new CaptchaThreadFactory() ); From d8465e820da5bf0c793821a9833026b35675231f Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 19:56:14 +0300 Subject: [PATCH 10/20] Use ArrayList in CachedCaptcha --- .../java/ru/leymooo/botfilter/caching/CachedCaptcha.java | 7 ++++--- .../ru/leymooo/botfilter/captcha/CaptchaGeneration.java | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java index df43af04e6..42b39ff55a 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java @@ -2,6 +2,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; +import java.util.List; import java.util.Random; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -22,7 +23,7 @@ public class CachedCaptcha private static final int PACKETID_115and117 = 0x27; private static final int PACKETID_1162 = 0x25; private final Random random = new Random(); - private CaptchaHolder[] captchas = null; + private List captchas = null; public static CaptchaHolder createCaptchaPacket(MapDataPacket map, String answer) { @@ -58,11 +59,11 @@ public CaptchaHolder randomCaptcha() { return null; } - if ( this.captchas.length == 0 ) + if ( this.captchas.size() == 0 ) { return null; } - return captchas[random.nextInt( captchas.length )]; + return captchas.get( random.nextInt( captchas.size() ) ); } @RequiredArgsConstructor diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index e8474ea201..9ea0ebcc89 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -96,8 +96,8 @@ private static void generateCaptchas() BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); } - //Текущий список всех капч каждую секунду переобразуем в массив и вставляем, чтобы хоть какая та часть была доступна во время генерации - PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); + //Текущий список всех капч каждую секунду вставляем в новый список для гарантии потокобезопасности. + PacketUtils.captchas.setCaptchas( new ArrayList<>( holders ) ); try { Thread.sleep( 1000L ); @@ -112,7 +112,7 @@ private static void generateCaptchas() executor.shutdownNow(); //Окончательно устанавливаем оставшиеся капчи - PacketUtils.captchas.setCaptchas( holders.toArray( new CachedCaptcha.CaptchaHolder[0] ) ); + PacketUtils.captchas.setCaptchas( new ArrayList<>( holders ) ); System.gc(); BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерирована за {0} мс", System.currentTimeMillis() - start ); } catch ( Exception e ) From 5be43a8e7af563db52492f4408e82a41579564ee Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:08:00 +0300 Subject: [PATCH 11/20] Configure captcha count in settings --- .../botfilter/captcha/CaptchaGeneration.java | 14 ++------------ .../java/ru/leymooo/botfilter/config/Settings.java | 9 +++++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 9ea0ebcc89..007e98b670 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -22,6 +22,7 @@ import ru.leymooo.botfilter.captcha.generator.CaptchaPainter; import ru.leymooo.botfilter.captcha.generator.map.CraftMapCanvas; import ru.leymooo.botfilter.captcha.generator.map.MapPalette; +import ru.leymooo.botfilter.config.Settings; import ru.leymooo.botfilter.packets.MapDataPacket; /** @@ -61,18 +62,7 @@ private static void generateCaptchas() CaptchaPainter painter = new CaptchaPainter(); MapPalette.prepareColors(); - int captchaCount = 900; - try - { - //TODO Как лучше быть? Использовать конфиг или через аргумент запуска? - String captchaCountString = System.getProperty( "captchaCount" ); - if ( captchaCountString != null && !captchaCountString.isEmpty() ) - { - captchaCount = Integer.parseInt( captchaCountString ); - } - } catch ( Exception ignored ) - { - } + int captchaCount = Settings.IMP.CAPTCHA.COUNT; if ( captchaCount <= 0 ) { diff --git a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java index cade126ff0..12e2831e01 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java @@ -35,6 +35,8 @@ public class Settings extends Config @Create public PROTECTION PROTECTION; @Create + public CAPTCHA CAPTCHA; + @Create public SQL SQL; @Comment( { @@ -250,4 +252,11 @@ public static class DIMENSIONS }) public int TYPE = 0; } + + @Comment("Настройка капчи") + public static class CAPTCHA + { + @Comment("Сколько экземпляров капчи нужно сгенерировать. Большие значения могут занять много оперативной памяти.") + public int COUNT = 900; + } } From 1ab060d9f5505034b2243603968e80a1d1013ef8 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:20:56 +0300 Subject: [PATCH 12/20] Improve comment --- proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java index 12e2831e01..615c221714 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java @@ -256,7 +256,7 @@ public static class DIMENSIONS @Comment("Настройка капчи") public static class CAPTCHA { - @Comment("Сколько экземпляров капчи нужно сгенерировать. Большие значения могут занять много оперативной памяти.") + @Comment("Сколько экземпляров капчи нужно сгенерировать. Большие количество может занять много оперативной памяти.") public int COUNT = 900; } } From 3398bbe021149ae10799c26cd482ce48421db9da Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:43:30 +0300 Subject: [PATCH 13/20] Create CaptchaGenerationTask --- .../botfilter/captcha/CaptchaGeneration.java | 97 ++----------------- .../captcha/CaptchaGenerationTask.java | 92 ++++++++++++++++++ 2 files changed, 99 insertions(+), 90 deletions(-) create mode 100644 proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 007e98b670..98d5836535 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -1,29 +1,22 @@ package ru.leymooo.botfilter.captcha; -import java.awt.Color; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.awt.Font; -import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import lombok.experimental.UtilityClass; import net.md_5.bungee.BungeeCord; import ru.leymooo.botfilter.caching.CachedCaptcha; import ru.leymooo.botfilter.caching.PacketUtils; import ru.leymooo.botfilter.captcha.generator.CaptchaPainter; -import ru.leymooo.botfilter.captcha.generator.map.CraftMapCanvas; import ru.leymooo.botfilter.captcha.generator.map.MapPalette; import ru.leymooo.botfilter.config.Settings; -import ru.leymooo.botfilter.packets.MapDataPacket; /** * @author Leymooo @@ -42,7 +35,7 @@ public static synchronized void generateImages() throws CaptchaGenerationExcepti generation = true; Thread thread = new Thread( CaptchaGeneration::generateCaptchas ); - thread.setName( "CaptchaGeneration-provider-thread" ); + thread.setName( "CaptchaGenerationProvider-thread" ); thread.setPriority( Thread.MIN_PRIORITY ); thread.start(); } @@ -58,7 +51,10 @@ private static void generateCaptchas() PacketUtils.captchas.clear(); BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] " + ( BungeeCord.getInstance().isEnabled() ? "Начата генерация капчи в фоне." : "Генерация капчи продолжится параллельно с загрузкой BungeeCord." ) ); ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), - new CaptchaThreadFactory() ); + new ThreadFactoryBuilder() + .setPriority( Thread.MIN_PRIORITY ) + .setNameFormat( "CaptchaGenerationTask-thread-%d" ) + .build() ); CaptchaPainter painter = new CaptchaPainter(); MapPalette.prepareColors(); @@ -73,7 +69,7 @@ private static void generateCaptchas() for ( int i = 1; i <= captchaCount; i++ ) { - executor.execute( () -> generate( executor, painter, fonts, holders ) ); + executor.execute( new CaptchaGenerationTask( executor, painter, fonts, holders ) ); } long start = System.currentTimeMillis(); @@ -113,83 +109,4 @@ private static void generateCaptchas() generation = false; } } - - private static void generate(ExecutorService executor, CaptchaPainter painter, List fonts, - List holders) - { - try - { - Random rnd = ThreadLocalRandom.current(); - String answer = randomAnswer( rnd ); - BufferedImage image = painter.draw( fonts.get( rnd.nextInt( fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); - final CraftMapCanvas map = new CraftMapCanvas(); - map.drawImage( 0, 0, image ); - MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); - CachedCaptcha.CaptchaHolder holder = CachedCaptcha.createCaptchaPacket( packet, answer ); - holders.add( holder ); - } catch ( Throwable e ) - { - //Прекращаем генерацию если случилась любая ошибка - e.printStackTrace(); - executor.shutdownNow(); - } - } - - private static Color randomNotWhiteColor(Random rnd) - { - Color color = MapPalette.colors[rnd.nextInt( MapPalette.colors.length )]; - - int r = color.getRed(); - int g = color.getGreen(); - int b = color.getBlue(); - - if ( r == 255 && g == 255 && b == 255 ) - { - return randomNotWhiteColor( rnd ); - } - if ( r == 220 && g == 220 && b == 220 ) - { - return randomNotWhiteColor( rnd ); - } - if ( r == 199 && g == 199 && b == 199 ) - { - return randomNotWhiteColor( rnd ); - } - if ( r == 255 && g == 252 && b == 245 ) - { - return randomNotWhiteColor( rnd ); - } - if ( r == 220 && g == 217 && b == 211 ) - { - return randomNotWhiteColor( rnd ); - } - if ( r == 247 && g == 233 && b == 163 ) - { - return randomNotWhiteColor( rnd ); - } - return color; - } - private static String randomAnswer(Random rnd) - { - if ( rnd.nextBoolean() ) - { - return Integer.toString( rnd.nextInt( ( 99999 - 10000 ) + 1 ) + 10000 ); - } else - { - return Integer.toString( rnd.nextInt( ( 9999 - 1000 ) + 1 ) + 1000 ); - } - } - //Для правильного именования потоков которые генерируют капчу и для создания низкого приоритета - private static class CaptchaThreadFactory implements ThreadFactory - { - private static final AtomicInteger counter = new AtomicInteger(); - @Override - public Thread newThread(Runnable task) - { - Thread thr = new Thread( task ); - thr.setPriority( Thread.MIN_PRIORITY ); - thr.setName( "CaptchaGenerator-thread-" + counter.incrementAndGet() ); - return thr; - } - } } diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java new file mode 100644 index 0000000000..41d028876b --- /dev/null +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java @@ -0,0 +1,92 @@ +package ru.leymooo.botfilter.captcha; + +import java.awt.Color; +import java.awt.Font; +import java.awt.image.BufferedImage; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadLocalRandom; +import lombok.AllArgsConstructor; +import lombok.Data; +import ru.leymooo.botfilter.caching.CachedCaptcha; +import ru.leymooo.botfilter.captcha.generator.CaptchaPainter; +import ru.leymooo.botfilter.captcha.generator.map.CraftMapCanvas; +import ru.leymooo.botfilter.captcha.generator.map.MapPalette; +import ru.leymooo.botfilter.packets.MapDataPacket; + +@Data +@AllArgsConstructor +public class CaptchaGenerationTask implements Runnable +{ + private final ExecutorService executor; + private final CaptchaPainter painter; + private final List fonts; + private final List holders; + @Override + public void run() + { + try + { + Random rnd = ThreadLocalRandom.current(); + String answer = randomAnswer( rnd ); + BufferedImage image = this.painter.draw( this.fonts.get( rnd.nextInt( this.fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); + final CraftMapCanvas map = new CraftMapCanvas(); + map.drawImage( 0, 0, image ); + MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); + CachedCaptcha.CaptchaHolder holder = CachedCaptcha.createCaptchaPacket( packet, answer ); + this.holders.add( holder ); + } catch ( Throwable e ) + { + //Прекращаем генерацию если случилась любая ошибка + e.printStackTrace(); + this.executor.shutdownNow(); + } + } + + private static Color randomNotWhiteColor(Random rnd) + { + Color color = MapPalette.colors[rnd.nextInt( MapPalette.colors.length )]; + + int r = color.getRed(); + int g = color.getGreen(); + int b = color.getBlue(); + + if ( r == 255 && g == 255 && b == 255 ) + { + return randomNotWhiteColor( rnd ); + } + if ( r == 220 && g == 220 && b == 220 ) + { + return randomNotWhiteColor( rnd ); + } + if ( r == 199 && g == 199 && b == 199 ) + { + return randomNotWhiteColor( rnd ); + } + if ( r == 255 && g == 252 && b == 245 ) + { + return randomNotWhiteColor( rnd ); + } + if ( r == 220 && g == 217 && b == 211 ) + { + return randomNotWhiteColor( rnd ); + } + if ( r == 247 && g == 233 && b == 163 ) + { + return randomNotWhiteColor( rnd ); + } + return color; + } + + private static String randomAnswer(Random rnd) + { + if ( rnd.nextBoolean() ) + { + return Integer.toString( rnd.nextInt( ( 99999 - 10000 ) + 1 ) + 10000 ); + } else + { + return Integer.toString( rnd.nextInt( ( 9999 - 1000 ) + 1 ) + 1000 ); + } + } +} From d6bfac40e5a821e958f802fb74fb86c517486341 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:51:11 +0300 Subject: [PATCH 14/20] Add ability to change captcha regeneration time in config --- .../main/java/ru/leymooo/botfilter/BotFilterThread.java | 9 ++++++--- .../main/java/ru/leymooo/botfilter/config/Settings.java | 4 +++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java index 2c6fdb473d..d8cc26b2ed 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java @@ -135,9 +135,12 @@ public static void startCleanUpThread() } } FailedUtils.flushQueue(); - //TODO Не понятно, долго или часто это пока что - //Каждые 6 часов капча сама регенерируется - if ( ++counterCaptcha == ( 12 * 60 ) * 6 ) + int captchaMin = Settings.IMP.CAPTCHA.CAPTCHA_REGENERATION_TIME; + if ( captchaMin <= 0 ) + { + captchaMin = 1; + } + if ( ++counterCaptcha == ( 12 * captchaMin ) ) { counterCaptcha = 0; diff --git a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java index 615c221714..2a1ca9bc35 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java @@ -256,7 +256,9 @@ public static class DIMENSIONS @Comment("Настройка капчи") public static class CAPTCHA { - @Comment("Сколько экземпляров капчи нужно сгенерировать. Большие количество может занять много оперативной памяти.") + @Comment("Сколько экземпляров капчи нужно сгенерировать. Большое количество может занять много оперативной памяти.") public int COUNT = 900; + @Comment("Как часто капча должна сама регенерироваться в минутах.") + public int CAPTCHA_REGENERATION_TIME = 360; } } From b3e57a474b744ba9ec82c2b960873234b4a8aeb3 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Wed, 15 Jun 2022 22:11:51 +0300 Subject: [PATCH 15/20] Provide ThreadLocalRandom to CaptchaPainter --- .../java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java | 4 +--- .../ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java | 4 ++-- .../leymooo/botfilter/captcha/generator/CaptchaPainter.java | 4 +++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 98d5836535..51931c17d6 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -14,7 +14,6 @@ import net.md_5.bungee.BungeeCord; import ru.leymooo.botfilter.caching.CachedCaptcha; import ru.leymooo.botfilter.caching.PacketUtils; -import ru.leymooo.botfilter.captcha.generator.CaptchaPainter; import ru.leymooo.botfilter.captcha.generator.map.MapPalette; import ru.leymooo.botfilter.config.Settings; @@ -55,7 +54,6 @@ private static void generateCaptchas() .setPriority( Thread.MIN_PRIORITY ) .setNameFormat( "CaptchaGenerationTask-thread-%d" ) .build() ); - CaptchaPainter painter = new CaptchaPainter(); MapPalette.prepareColors(); int captchaCount = Settings.IMP.CAPTCHA.COUNT; @@ -69,7 +67,7 @@ private static void generateCaptchas() for ( int i = 1; i <= captchaCount; i++ ) { - executor.execute( new CaptchaGenerationTask( executor, painter, fonts, holders ) ); + executor.execute( new CaptchaGenerationTask( executor, fonts, holders ) ); } long start = System.currentTimeMillis(); diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java index 41d028876b..961eb5e663 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java @@ -20,7 +20,6 @@ public class CaptchaGenerationTask implements Runnable { private final ExecutorService executor; - private final CaptchaPainter painter; private final List fonts; private final List holders; @Override @@ -30,7 +29,8 @@ public void run() { Random rnd = ThreadLocalRandom.current(); String answer = randomAnswer( rnd ); - BufferedImage image = this.painter.draw( this.fonts.get( rnd.nextInt( this.fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); + CaptchaPainter painter = new CaptchaPainter( rnd ); + BufferedImage image = painter.draw( this.fonts.get( rnd.nextInt( this.fonts.size() ) ), randomNotWhiteColor( rnd ), answer ); final CraftMapCanvas map = new CraftMapCanvas(); map.drawImage( 0, 0, image ); MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/generator/CaptchaPainter.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/generator/CaptchaPainter.java index 66af52aa87..70b03aa0e5 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/generator/CaptchaPainter.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/generator/CaptchaPainter.java @@ -14,14 +14,16 @@ import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.util.Random; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor public class CaptchaPainter { private final int width = 128; private final int height = 128; private final Color background = Color.WHITE; - private final Random rnd = new Random(); + private final Random rnd; public BufferedImage draw(Font font, Color fGround, String text) { From 704dbe3658a720e10339efb903e238d1c3a82fe2 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 11 Jul 2022 18:39:51 +0300 Subject: [PATCH 16/20] Use ThreadLocalRandom in randomCaptcha method --- .../ru/leymooo/botfilter/caching/CachedCaptcha.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java index 42b39ff55a..59d61f4bed 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/caching/CachedCaptcha.java @@ -3,7 +3,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import java.util.List; -import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -22,7 +22,6 @@ public class CachedCaptcha private static final int PACKETID_113and114and116 = 0x26; private static final int PACKETID_115and117 = 0x27; private static final int PACKETID_1162 = 0x25; - private final Random random = new Random(); private List captchas = null; public static CaptchaHolder createCaptchaPacket(MapDataPacket map, String answer) @@ -55,15 +54,11 @@ public void clear() public CaptchaHolder randomCaptcha() { - if ( this.captchas == null ) + if ( this.captchas == null || this.captchas.isEmpty() ) { return null; } - if ( this.captchas.size() == 0 ) - { - return null; - } - return captchas.get( random.nextInt( captchas.size() ) ); + return captchas.get( ThreadLocalRandom.current().nextInt( captchas.size() ) ); } @RequiredArgsConstructor From 30fa0baa0ccf3e7ab39b2605d4039a2a4aa9ec12 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 11 Jul 2022 18:40:48 +0300 Subject: [PATCH 17/20] Remove volatile from "enabled" field in BungeeCord class --- proxy/src/main/java/net/md_5/bungee/BungeeCord.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index fd9b9f5a92..6c2f3f9a6b 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -115,7 +115,7 @@ public class BungeeCord extends ProxyServer public volatile boolean isRunning; //BotFilter @Getter - private volatile boolean enabled; + private boolean enabled; /** * Configuration. */ From aed7ef53e9cf00ad867a6e4f6541c53f8bd28313 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 11 Jul 2022 18:52:03 +0300 Subject: [PATCH 18/20] Little change error message in generate command --- proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java index 6d065b3d0d..4b14c7744b 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterCommand.java @@ -63,7 +63,7 @@ public void execute(CommandSender sender, String[] args) sender.sendMessage( "§aКоманда выполнена" ); } catch ( CaptchaGenerationException e ) { - sender.sendMessage( "§cОшибка при попытке сгенерировать капчу: " + e.getMessage() ); + sender.sendMessage( "§cОшибка: " + e.getMessage() ); } } else if ( args[0].equalsIgnoreCase( "protection" ) ) { From 753dc9ba002cbaaaad0082a6a1a5814a0a791c84 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Mon, 11 Jul 2022 19:20:17 +0300 Subject: [PATCH 19/20] Add ability to disable captcha auto regeneration --- proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java | 2 +- proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java index d8cc26b2ed..25c84b6de6 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/BotFilterThread.java @@ -138,7 +138,7 @@ public static void startCleanUpThread() int captchaMin = Settings.IMP.CAPTCHA.CAPTCHA_REGENERATION_TIME; if ( captchaMin <= 0 ) { - captchaMin = 1; + continue; } if ( ++counterCaptcha == ( 12 * captchaMin ) ) { diff --git a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java index 2a1ca9bc35..e270b731c1 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/config/Settings.java @@ -258,7 +258,7 @@ public static class CAPTCHA { @Comment("Сколько экземпляров капчи нужно сгенерировать. Большое количество может занять много оперативной памяти.") public int COUNT = 900; - @Comment("Как часто капча должна сама регенерироваться в минутах.") + @Comment("Как часто капча должна сама регенерироваться в минутах. Укажите -1 чтобы отключить.") public int CAPTCHA_REGENERATION_TIME = 360; } } From 891717ff20586f6ee8447c7a01e1bb4c1520f034 Mon Sep 17 00:00:00 2001 From: BoomEaro <21033866+BoomEaro@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:48:52 +0300 Subject: [PATCH 20/20] Use future tasks --- .../botfilter/captcha/CaptchaGeneration.java | 38 ++++++++++++++----- .../captcha/CaptchaGenerationTask.java | 10 ++--- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java index 51931c17d6..a1588526d8 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGeneration.java @@ -4,10 +4,11 @@ import java.awt.Font; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.logging.Level; import lombok.experimental.UtilityClass; @@ -62,14 +63,17 @@ private static void generateCaptchas() { captchaCount = 1; } - - List holders = Collections.synchronizedList( new ArrayList<>() ); - + List tasks = new ArrayList<>(); for ( int i = 1; i <= captchaCount; i++ ) { - executor.execute( new CaptchaGenerationTask( executor, fonts, holders ) ); + tasks.add( new CaptchaGenerationTask( executor, fonts ) ); } + List> futureHolders = new ArrayList<>(); + for ( CaptchaGenerationTask task : tasks ) + { + futureHolders.add( executor.submit( task ) ); + } long start = System.currentTimeMillis(); ThreadPoolExecutor ex = (ThreadPoolExecutor) executor; while ( ex.getActiveCount() != 0 ) @@ -79,9 +83,8 @@ private static void generateCaptchas() { BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Генерирую капчу [" + ( captchaCount - ex.getQueue().size() ) + "/" + captchaCount + "]" ); } - - //Текущий список всех капч каждую секунду вставляем в новый список для гарантии потокобезопасности. - PacketUtils.captchas.setCaptchas( new ArrayList<>( holders ) ); + //Вставляем сгенерированные капчи + PacketUtils.captchas.setCaptchas( findDoneTasks( futureHolders ) ); try { Thread.sleep( 1000L ); @@ -92,11 +95,10 @@ private static void generateCaptchas() return; } } - executor.shutdownNow(); //Окончательно устанавливаем оставшиеся капчи - PacketUtils.captchas.setCaptchas( new ArrayList<>( holders ) ); + PacketUtils.captchas.setCaptchas( findDoneTasks( futureHolders ) ); System.gc(); BungeeCord.getInstance().getLogger().log( Level.INFO, "[BotFilter] Капча сгенерирована за {0} мс", System.currentTimeMillis() - start ); } catch ( Exception e ) @@ -107,4 +109,20 @@ private static void generateCaptchas() generation = false; } } + private static List findDoneTasks(List> futureHolders) throws ExecutionException, InterruptedException + { + List doneTasks = new ArrayList<>(); + for ( Future future : futureHolders ) + { + if ( future.isDone() ) + { + CachedCaptcha.CaptchaHolder holder = future.get(); + if ( holder != null ) + { + doneTasks.add( holder ); + } + } + } + return doneTasks; + } } diff --git a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java index 961eb5e663..620691140d 100644 --- a/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java +++ b/proxy/src/main/java/ru/leymooo/botfilter/captcha/CaptchaGenerationTask.java @@ -5,6 +5,7 @@ import java.awt.image.BufferedImage; import java.util.List; import java.util.Random; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadLocalRandom; import lombok.AllArgsConstructor; @@ -17,13 +18,12 @@ @Data @AllArgsConstructor -public class CaptchaGenerationTask implements Runnable +public class CaptchaGenerationTask implements Callable { private final ExecutorService executor; private final List fonts; - private final List holders; @Override - public void run() + public CachedCaptcha.CaptchaHolder call() { try { @@ -34,14 +34,14 @@ public void run() final CraftMapCanvas map = new CraftMapCanvas(); map.drawImage( 0, 0, image ); MapDataPacket packet = new MapDataPacket( 0, (byte) 0, map.getMapData() ); - CachedCaptcha.CaptchaHolder holder = CachedCaptcha.createCaptchaPacket( packet, answer ); - this.holders.add( holder ); + return CachedCaptcha.createCaptchaPacket( packet, answer ); } catch ( Throwable e ) { //Прекращаем генерацию если случилась любая ошибка e.printStackTrace(); this.executor.shutdownNow(); } + return null; } private static Color randomNotWhiteColor(Random rnd)