Advertisement
Guest User

Captcha

a guest
May 11th, 2017
1,335
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 35.75 KB | None | 0 0
  1. Index: /trunk/aCis_gameserver/config/server.properties
  2. ===================================================================
  3. --- /trunk/aCis_gameserver/config/server.properties (revision 427)
  4. +++ /trunk/aCis_gameserver/config/server.properties (revision 428)
  5. @@ -286,5 +286,5 @@
  6.  # =================================================================
  7.  #                                Misc
  8.  # =================================================================
  9.  
  10.  # Basic protection against L2Walker.
  11.  L2WalkerProtection = False
  12.  
  13.  # Zone setting.
  14.  #   0 = Peace All the Time
  15.  #   1 = PVP During Siege for siege participants
  16.  #   2 = PVP All the Time
  17.  ZoneTown = 0
  18.  
  19.  # Show "data/html/servnews.htm" when a character logins from lvl 5.
  20.  ServerNews = 5
  21.  
  22.  # Disable tutorial on new player game entrance. Default: False.
  23.  DisableTutorial = False
  24.  
  25. +# =================================================================
  26. +#                                Misc
  27. +# =================================================================
  28. +
  29. +# Bots prevention system.
  30. +EnableBotsPrevention = False
  31. +# How many monsters have to be killed to run validation task?
  32. +KillsCounter = 60
  33. +# Specify range of randomly taken values summed with main counter.
  34. +KillsCounterRandomization = 50
  35. +# How long validation window awaits an answer? (in seconds)
  36. +ValidationTime = 60
  37. +# Punishments:
  38. +0 = move character to the closest village.
  39. +#  1 = kick characters from the server.
  40. +2 = put character to jail.
  41. +3 = ban character from the server.
  42. +Punishment = 0
  43. +# How long character were suppose to stay in jail? (in minutes)
  44. +PunishmentTime = 60
  45. +
  46. +# Basic protection against L2Walker.
  47. +L2WalkerProtection = False
  48. +
  49. Index: trunk/aCis_gameserver/java/net/sf/l2j/Config.java
  50. ===================================================================
  51. --- trunk/aCis_gameserver/java/net/sf/l2j/Config.java   (rιvision 427)
  52. +++ trunk/aCis_gameserver/java/net/sf/l2j/Config.java   (rιvision 428)
  53. @@ -653,5 +653,5 @@
  54.     /** Misc */
  55. +   public static boolean BOTS_PREVENTION;
  56. +   public static int KILLS_COUNTER;
  57. +   public static int KILLS_COUNTER_RANDOMIZATION;
  58. +   public static int VALIDATION_TIME;
  59. +   public static int PUNISHMENT;
  60. +   public static int PUNISHMENT_TIME;
  61. @@ -1264 +1264 @@
  62.         L2WALKER_PROTECTION = server.getProperty("L2WalkerProtection", false);
  63. +       BOTS_PREVENTION = server.getProperty("EnableBotsPrevention", false);
  64. +       KILLS_COUNTER = server.getProperty("KillsCounter", 60);
  65. +       KILLS_COUNTER_RANDOMIZATION = server.getProperty("KillsCounterRandomization", 50);
  66. +       VALIDATION_TIME = server.getProperty("ValidationTime", 60);
  67. +       PUNISHMENT = server.getProperty("Punishment", 0);
  68. +       PUNISHMENT_TIME = server.getProperty("PunishmentTime", 60);
  69.  
  70. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/instancemanager/BotsPreventionManager.java
  71. ===================================================================
  72. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/instancemanager/BotsPreventionManager.java (revision 0)
  73. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/instancemanager/BotsPreventionManager.java (revision 1)
  74. @@ -0 +0 @@
  75. +package net.sf.l2j.gameserver.instancemanager;
  76. +
  77. +import java.awt.image.BufferedImage;
  78. +import java.sql.Connection;
  79. +import java.sql.PreparedStatement;
  80. +import java.sql.SQLException;
  81. +import java.util.HashMap;
  82. +import java.util.Map;
  83. +import java.util.Random;
  84. +import java.util.concurrent.ConcurrentHashMap;
  85. +import java.util.concurrent.Future;
  86. +
  87. +import net.sf.l2j.commons.concurrent.ThreadPool;
  88. +import net.sf.l2j.commons.lang.StringUtil;
  89. +
  90. +import net.sf.l2j.Config;
  91. +import net.sf.l2j.L2DatabaseFactory;
  92. +import net.sf.l2j.gameserver.datatables.MapRegionTable;
  93. +import net.sf.l2j.gameserver.features.Captcha;
  94. +import net.sf.l2j.gameserver.features.DDSConverter;
  95. +import net.sf.l2j.gameserver.features.ImageData;
  96. +import net.sf.l2j.gameserver.model.actor.Character;
  97. +import net.sf.l2j.gameserver.model.actor.instance.Monster;
  98. +import net.sf.l2j.gameserver.model.actor.instance.Player;
  99. +import net.sf.l2j.gameserver.network.serverpackets.NpcHtmlMessage;
  100. +import net.sf.l2j.gameserver.network.serverpackets.PledgeCrest;
  101. +
  102. +public class BotsPreventionManager
  103. +{
  104. +   public class PlayerData
  105. +   {
  106. +       public PlayerData()
  107. +       {
  108. +           firstWindow = true;
  109. +       }
  110. +      
  111. +       public boolean firstWindow;
  112. +       public BufferedImage image;
  113. +       public String captchaText = "";
  114. +       public int captchaID = 0;
  115. +   }
  116. +  
  117. +   protected Random _randomize;
  118. +   protected static Map<Integer, ImageData> _imageMap;
  119. +   protected static Map<Integer, Integer> _monsterscounter;
  120. +   protected static Map<Integer, Future<?>> _beginvalidation;
  121. +   protected static Map<Integer, PlayerData> _validation;
  122. +   protected int WINDOW_DELAY = 3; // delay used to generate new window if previous have been closed.
  123. +   protected int VALIDATION_TIME = Config.VALIDATION_TIME * 1000;
  124. +   private int USEDID = 0;
  125. +  
  126. +   public static final BotsPreventionManager getInstance()
  127. +   {
  128. +       return SingletonHolder._instance;
  129. +   }
  130. +  
  131. +   BotsPreventionManager()
  132. +   {
  133. +       _randomize = new Random();
  134. +       _monsterscounter = new HashMap<>();
  135. +       _beginvalidation = new HashMap<>();
  136. +       _validation = new HashMap<>();
  137. +       _beginvalidation = new HashMap<>();
  138. +       _imageMap = new ConcurrentHashMap<>();
  139. +       _imageMap = Captcha.getInstance().createImageList();
  140. +   }
  141. +  
  142. +   public void updatecounter(Character player, Character monster)
  143. +   {
  144. +       if ((player instanceof Player) && (monster instanceof Monster))
  145. +       {
  146. +           Player killer = (Player) player;
  147. +          
  148. +           if (_validation.get(killer.getObjectId()) != null)
  149. +           {
  150. +               return;
  151. +           }
  152. +          
  153. +           int count = 1;
  154. +           if (_monsterscounter.get(killer.getObjectId()) != null)
  155. +           {
  156. +               count = _monsterscounter.get(killer.getObjectId()) + 1;
  157. +           }
  158. +          
  159. +           int next = _randomize.nextInt(Config.KILLS_COUNTER_RANDOMIZATION);
  160. +           if (Config.KILLS_COUNTER + next < count)
  161. +           {
  162. +               validationtasks(killer);
  163. +               _monsterscounter.remove(killer.getObjectId());
  164. +           }
  165. +           else
  166. +           {
  167. +               _monsterscounter.put(killer.getObjectId(), count);
  168. +           }
  169. +       }
  170. +   }
  171. +  
  172. +   public void prevalidationwindow(Player player)
  173. +   {
  174. +       NpcHtmlMessage html = new NpcHtmlMessage(1);
  175. +       StringBuilder tb = new StringBuilder();
  176. +       StringUtil.append(tb, "<html>");
  177. +       StringUtil.append(tb, "<title>Bots prevention</title>");
  178. +       StringUtil.append(tb, "<body><center><br><br><img src=\"L2UI_CH3.herotower_deco\" width=\"256\" height=\"32\">");
  179. +       StringUtil.append(tb, "<br><br><font color=\"a2a0a2\">If such window appears it means server suspect,<br1>that you may using cheating software.</font>");
  180. +       StringUtil.append(tb, "<br><br><font color=\"b09979\">If given answer results are incorrect or no action is made<br1>server is going to punish character instantly.</font>");
  181. +       StringUtil.append(tb, "<br><br><button value=\"CONTINUE\" action=\"bypass report_continue\" width=\"75\" height=\"21\" back=\"L2UI_CH3.Btn1_normal\" fore=\"L2UI_CH3.Btn1_normal\">");
  182. +       StringUtil.append(tb, "</center></body>");
  183. +       StringUtil.append(tb, "</html>");
  184. +       html.setHtml(tb.toString());
  185. +       player.sendPacket(html);
  186. +   }
  187. +  
  188. +   private static void validationwindow(Player player)
  189. +   {
  190. +       PlayerData container = _validation.get(player.getObjectId());
  191. +       NpcHtmlMessage html = new NpcHtmlMessage(1);
  192. +      
  193. +       StringBuilder tb = new StringBuilder();
  194. +       StringUtil.append(tb, "<html>");
  195. +       StringUtil.append(tb, "<title>Bots prevention</title>");
  196. +       StringUtil.append(tb, "<body><center><br><br><img src=\"L2UI_CH3.herotower_deco\" width=\"256\" height=\"32\">");
  197. +       StringUtil.append(tb, "<br><br><font color=\"a2a0a2\">in order to prove you are a human being<br1>you've to</font> <font color=\"b09979\">enter the code from picture:</font>");
  198. +      
  199. +       // generated main pattern.
  200. +       StringUtil.append(tb, "<br><br><img src=\"Crest.crest_" + Config.SERVER_ID + "_" + (container.captchaID) + "\" width=256 height=64></td></tr>");
  201. +       StringUtil.append(tb, "<br>");
  202. +       StringUtil.append(tb, "<br><br><edit var=\"answer\" width=110>");
  203. +       StringUtil.append(tb, "<br><button value=\"Confirm\" action=\"bypass -h report_ $answer\" width=\"75\" height=\"21\" back=\"L2UI_CH3.Btn1_normal\" fore=\"L2UI_CH3.Btn1_normal\"><br/>");
  204. +       StringUtil.append(tb, "</center></body>");
  205. +       StringUtil.append(tb, "</html>");
  206. +      
  207. +       html.setHtml(tb.toString());
  208. +       player.sendPacket(html);
  209. +   }
  210. +  
  211. +   public void punishmentnwindow(Player player)
  212. +   {
  213. +       NpcHtmlMessage html = new NpcHtmlMessage(1);
  214. +       StringBuilder tb = new StringBuilder();
  215. +       StringUtil.append(tb, "<html>");
  216. +       StringUtil.append(tb, "<title>Bots prevention</title>");
  217. +       StringUtil.append(tb, "<body><center><br><br><img src=\"L2UI_CH3.herotower_deco\" width=\"256\" height=\"32\">");
  218. +       StringUtil.append(tb, "<br><br><font color=\"a2a0a2\">If such window appears, it means character haven't<br1>passed through prevention system.");
  219. +       StringUtil.append(tb, "<br><br><font color=\"b09979\">In such case character get moved to nearest town.</font>");
  220. +       StringUtil.append(tb, "</center></body>");
  221. +       StringUtil.append(tb, "</html>");
  222. +       html.setHtml(tb.toString());
  223. +       player.sendPacket(html);
  224. +   }
  225. +  
  226. +   public void validationtasks(Player player)
  227. +   {
  228. +       PlayerData container = new PlayerData();
  229. +       // Captcha.getInstance().generateCaptcha(container, player);
  230. +       container.image = _imageMap.get(getInstance().USEDID).image;
  231. +       container.captchaID = _imageMap.get(getInstance().USEDID).captchaID;
  232. +       container.captchaText = _imageMap.get(getInstance().USEDID).captchaText;
  233. +       PledgeCrest packet = new PledgeCrest(container.captchaID, DDSConverter.convertToDDS(container.image).array());
  234. +       player.sendPacket(packet);
  235. +       getInstance().USEDID++;
  236. +      
  237. +       if (getInstance().USEDID == 998)
  238. +       {
  239. +           getInstance().USEDID = 0;
  240. +           ThreadPool.schedule(() -> {
  241. +               _imageMap = Captcha.getInstance().createImageList();
  242. +           }, 100);
  243. +       }
  244. +       _validation.put(player.getObjectId(), container);
  245. +      
  246. +       Future<?> newTask = ThreadPool.schedule(new ReportCheckTask(player), VALIDATION_TIME);
  247. +       ThreadPool.schedule(new countdown(player, VALIDATION_TIME / 1000), 0);
  248. +       _beginvalidation.put(player.getObjectId(), newTask);
  249. +   }
  250. +  
  251. +   protected void banpunishment(Player player)
  252. +   {
  253. +       _validation.remove(player.getObjectId());
  254. +       _beginvalidation.get(player.getObjectId()).cancel(true);
  255. +       _beginvalidation.remove(player.getObjectId());
  256. +      
  257. +       switch (Config.PUNISHMENT)
  258. +       {
  259. +           // 0 = move character to the closest village.
  260. +           // 1 = kick characters from the server.
  261. +           // 2 = put character to jail.
  262. +           // 3 = ban character from the server.
  263. +           case 0:
  264. +               player.stopMove(null);
  265. +               player.teleToLocation(MapRegionTable.TeleportType.TOWN);
  266. +               punishmentnwindow(player);
  267. +               break;
  268. +           case 1:
  269. +               if (player.isOnline())
  270. +               {
  271. +                   player.logout(true);
  272. +               }
  273. +               break;
  274. +           case 2:
  275. +               jailpunishment(player, Config.PUNISHMENT_TIME * 60);
  276. +               break;
  277. +           case 3:
  278. +               // player.setAccessLevel(-100);
  279. +               changeaccesslevel(player, -100);
  280. +               break;
  281. +       }
  282. +      
  283. +       player.sendMessage("Unfortunately, code doesn't match.");
  284. +   }
  285. +  
  286. +   private static void changeaccesslevel(Player targetPlayer, int lvl)
  287. +   {
  288. +       if (targetPlayer.isOnline())
  289. +       {
  290. +           targetPlayer.setAccessLevel(lvl);
  291. +           targetPlayer.logout();
  292. +       }
  293. +       else
  294. +       {
  295. +           try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  296. +           {
  297. +               PreparedStatement statement = con.prepareStatement("UPDATE characters SET accesslevel=? WHERE obj_id=?");
  298. +               statement.setInt(1, lvl);
  299. +               statement.setInt(2, targetPlayer.getObjectId());
  300. +               statement.execute();
  301. +               statement.close();
  302. +           }
  303. +           catch (SQLException se)
  304. +           {
  305. +               if (Config.DEBUG)
  306. +                   se.printStackTrace();
  307. +           }
  308. +       }
  309. +   }
  310. +  
  311. +   private static void jailpunishment(Player activeChar, int delay)
  312. +   {
  313. +       if (activeChar.isOnline())
  314. +       {
  315. +           activeChar.setPunishLevel(Player.PunishLevel.JAIL, Config.PUNISHMENT_TIME);
  316. +       }
  317. +       else
  318. +       {
  319. +           try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  320. +           {
  321. +               PreparedStatement statement = con.prepareStatement("UPDATE characters SET x=?, y=?, z=?, punish_level=?, punish_timer=? WHERE obj_id=?");
  322. +               statement.setInt(1, -114356);
  323. +               statement.setInt(2, -249645);
  324. +               statement.setInt(3, -2984);
  325. +               statement.setInt(4, Player.PunishLevel.JAIL.value());
  326. +               statement.setLong(5, (delay > 0 ? delay * Config.PUNISHMENT_TIME * 100 : 0));
  327. +               statement.setInt(6, activeChar.getObjectId());
  328. +              
  329. +               statement.execute();
  330. +               statement.close();
  331. +           }
  332. +           catch (SQLException se)
  333. +           {
  334. +               activeChar.sendMessage("SQLException while jailing player");
  335. +               if (Config.DEBUG)
  336. +                   se.printStackTrace();
  337. +           }
  338. +       }
  339. +   }
  340. +  
  341. +   public void AnalyseBypass(String command, Player player)
  342. +   {
  343. +       if (!_validation.containsKey(player.getObjectId()))
  344. +           return;
  345. +      
  346. +       String params = command.substring(command.indexOf("_") + 1);
  347. +      
  348. +       if (params.startsWith("continue"))
  349. +       {
  350. +           validationwindow(player);
  351. +           _validation.get(player.getObjectId()).firstWindow = false;
  352. +           return;
  353. +       }
  354. +      
  355. +       PlayerData playerData = _validation.get(player.getObjectId());
  356. +       if (!params.trim().equalsIgnoreCase(playerData.captchaText))
  357. +       {
  358. +           banpunishment(player);
  359. +       }
  360. +       else
  361. +       {
  362. +           player.sendMessage("Congratulations, code match!");
  363. +           _validation.remove(player.getObjectId());
  364. +           _beginvalidation.get(player.getObjectId()).cancel(true);
  365. +           _beginvalidation.remove(player.getObjectId());
  366. +       }
  367. +      
  368. +   }
  369. +  
  370. +   protected class countdown implements Runnable
  371. +   {
  372. +       private final Player _player;
  373. +       private int _time;
  374. +      
  375. +       public countdown(Player player, int time)
  376. +       {
  377. +           _time = time;
  378. +           _player = player;
  379. +       }
  380. +      
  381. +       @Override
  382. +       public void run()
  383. +       {
  384. +           if (_player.isOnline())
  385. +           {
  386. +               if (_validation.containsKey(_player.getObjectId()) && _validation.get(_player.getObjectId()).firstWindow)
  387. +               {
  388. +                   if (_time % WINDOW_DELAY == 0)
  389. +                   {
  390. +                       prevalidationwindow(_player);
  391. +                   }
  392. +               }
  393. +              
  394. +               switch (_time)
  395. +               {
  396. +                   case 300:
  397. +                   case 240:
  398. +                   case 180:
  399. +                   case 120:
  400. +                   case 60:
  401. +                       _player.sendMessage(_time / 60 + " minute(s) to enter the code.");
  402. +                       break;
  403. +                   case 30:
  404. +                   case 10:
  405. +                   case 5:
  406. +                   case 4:
  407. +                   case 3:
  408. +                   case 2:
  409. +                   case 1:
  410. +                       _player.sendMessage(_time + " second(s) to enter the code!");
  411. +                       break;
  412. +               }
  413. +               if (_time > 1 && _validation.containsKey(_player.getObjectId()))
  414. +               {
  415. +                   ThreadPool.schedule(new countdown(_player, _time - 1), 1000);
  416. +               }
  417. +           }
  418. +       }
  419. +   }
  420. +  
  421. +   protected boolean tryParseInt(String value)
  422. +   {
  423. +       try
  424. +       {
  425. +           Integer.parseInt(value);
  426. +           return true;
  427. +       }
  428. +      
  429. +       catch (NumberFormatException e)
  430. +       {
  431. +           return false;
  432. +       }
  433. +   }
  434. +  
  435. +   public void CaptchaSuccessfull(Player player)
  436. +   {
  437. +       if (_validation.get(player.getObjectId()) != null)
  438. +       {
  439. +           _validation.remove(player.getObjectId());
  440. +       }
  441. +   }
  442. +  
  443. +   public Boolean IsAlredyInReportMode(Player player)
  444. +   {
  445. +       if (_validation.get(player.getObjectId()) != null)
  446. +       {
  447. +           return true;
  448. +       }
  449. +       return false;
  450. +   }
  451. +  
  452. +   private class ReportCheckTask implements Runnable
  453. +   {
  454. +       private final Player _player;
  455. +      
  456. +       public ReportCheckTask(Player player)
  457. +       {
  458. +           _player = player;
  459. +       }
  460. +      
  461. +       @Override
  462. +       public void run()
  463. +       {
  464. +           if (_validation.get(_player.getObjectId()) != null)
  465. +           {
  466. +               banpunishment(_player);
  467. +           }
  468. +       }
  469. +   }
  470. +  
  471. +   private static class SingletonHolder
  472. +   {
  473. +       protected static final BotsPreventionManager _instance = new BotsPreventionManager();
  474. +   }
  475. +}
  476. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/model/actor/Character.java
  477. ===================================================================
  478. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/model/actor/Character.java (revision 427)
  479. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/model/actor/Character.java (revision 428)
  480. @@ -41 +42 @@
  481. -import net.sf.l2j.gameserver.handler.SkillHandler;
  482. +import net.sf.l2j.gameserver.handler.SkillHandler;
  483. +import net.sf.l2j.gameserver.instancemanager.BotsPreventionManager;
  484. @@ -1613 +1613 @@
  485.         calculateRewards(killer);
  486. +       if (Config.BOTS_PREVENTION)
  487. +       {
  488. +           BotsPreventionManager.getInstance().updatecounter(killer,this);
  489. +       }
  490. +
  491. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestBypassToServer.java
  492. ===================================================================
  493. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestBypassToServer.java   (revision 427)
  494. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/RequestBypassToServer.java   (revision 428)
  495. @@ -24 +24 @@
  496. -import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
  497. +import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
  498. +import net.sf.l2j.gameserver.instancemanager.BotsPreventionManager;
  499. @@ -166 +166 @@
  500. -           else if (_command.startsWith("arenachange")) // change
  501. -           {
  502. -               final boolean isManager = activeChar.getCurrentFolkNPC() instanceof L2OlympiadManagerInstance;
  503. -               if (!isManager)
  504. -               {
  505. -                   // Without npc, command can be used only in observer mode on arena
  506. -                   if (!activeChar.inObserverMode() || activeChar.isInOlympiadMode() || activeChar.getOlympiadGameId() < 0)
  507. -                       return;
  508. -               }
  509. -              
  510. -               if (OlympiadManager.getInstance().isRegisteredInComp(activeChar))
  511. -               {
  512. -                   activeChar.sendPacket(SystemMessageId.WHILE_YOU_ARE_ON_THE_WAITING_LIST_YOU_ARE_NOT_ALLOWED_TO_WATCH_THE_GAME);
  513. -                   return;
  514. -               }
  515. -              
  516. -               final int arenaId = Integer.parseInt(_command.substring(12).trim());
  517. -               activeChar.enterOlympiadObserverMode(arenaId);
  518. -           }
  519. +           else if (_command.startsWith("arenachange")) // change
  520. +               {
  521. +                   final boolean isManager = activeChar.getCurrentFolkNPC() instanceof OlympiadManagerNpc;
  522. +                   if (!isManager)
  523. +                   {
  524. +                       // Without npc, command can be used only in observer mode on arena
  525. +                       if (!activeChar.isInObserverMode() || activeChar.isInOlympiadMode() || activeChar.getOlympiadGameId() < 0)
  526. +                           return;
  527. +                   }
  528. +                  
  529. +                   if (OlympiadManager.getInstance().isRegisteredInComp(activeChar))
  530. +                   {
  531. +                       activeChar.sendPacket(SystemMessageId.WHILE_YOU_ARE_ON_THE_WAITING_LIST_YOU_ARE_NOT_ALLOWED_TO_WATCH_THE_GAME);
  532. +                       return;
  533. +                   }
  534. +                  
  535. +                   final int arenaId = Integer.parseInt(_command.substring(12).trim());
  536. +                   activeChar.enterOlympiadObserverMode(arenaId);
  537. +               }
  538. +               else if (_command.startsWith("report"))
  539. +               {
  540. +                   BotsPreventionManager.getInstance().AnalyseBypass(_command,activeChar);
  541. +               }
  542. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/network/serverpackets/PledgeCrest.java
  543. ===================================================================
  544. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/network/serverpackets/PledgeCrest.java (revision 427)
  545. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/network/serverpackets/PledgeCrest.java (revision 428)
  546. @@ -29 +29 @@
  547. -   }
  548. -  
  549. -   @Override
  550. -   protected final void writeImpl()
  551. +   }
  552. +  
  553. +   public PledgeCrest(int crestId, byte[] data)
  554. +   {
  555. +       _crestId = crestId;
  556. +       _data = data;
  557. +   }
  558. +  
  559. +   @Override
  560. +   protected final void writeImpl()
  561. +
  562. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/DDSConverter.java
  563. ===================================================================
  564. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/DDSConverter.java (revision 427)
  565. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/DDSConverter.java (revision 428)
  566. +package net.sf.l2j.gameserver.features;
  567. +
  568. +import java.awt.image.BufferedImage;
  569. +import java.io.File;
  570. +import java.io.IOException;
  571. +import java.nio.ByteBuffer;
  572. +import java.nio.ByteOrder;
  573. +import java.util.logging.Logger;
  574. +
  575. +import javax.imageio.ImageIO;
  576. +
  577. +public class DDSConverter
  578. +{
  579. +   protected static final Logger LOG = Logger.getLogger(DDSConverter.class.getName());
  580. +  
  581. +   protected static class Color
  582. +   {
  583. +      
  584. +       @Override
  585. +       public boolean equals(Object obj)
  586. +       {
  587. +           if (this == obj)
  588. +           {
  589. +               return true;
  590. +           }
  591. +           if ((obj == null) || (getClass() != obj.getClass()))
  592. +           {
  593. +               return false;
  594. +           }
  595. +           Color color = (Color) obj;
  596. +           if (b != color.b)
  597. +           {
  598. +               return false;
  599. +           }
  600. +           if (g != color.g)
  601. +           {
  602. +               return false;
  603. +           }
  604. +           return r == color.r;
  605. +       }
  606. +      
  607. +       @Override
  608. +       public int hashCode()
  609. +       {
  610. +           int i = r;
  611. +           i = (29 * i) + g;
  612. +           i = (29 * i) + b;
  613. +           return i;
  614. +       }
  615. +      
  616. +       protected int r;
  617. +       protected int g;
  618. +       protected int b;
  619. +      
  620. +       public Color()
  621. +       {
  622. +           r = g = b = 0;
  623. +       }
  624. +      
  625. +       public Color(int i, int j, int k)
  626. +       {
  627. +           r = i;
  628. +           g = j;
  629. +           b = k;
  630. +       }
  631. +   }
  632. +
  633. +   public static ByteBuffer convertToDDS(BufferedImage bufferedimage)
  634. +   {
  635. +       if (bufferedimage == null)
  636. +       {
  637. +           return null;
  638. +       }
  639. +       if (bufferedimage.getColorModel().hasAlpha())
  640. +       {
  641. +           return convertToDxt3(bufferedimage);
  642. +       }
  643. +       return convertToDxt1NoTransparency(bufferedimage);
  644. +   }
  645. +  
  646. +   public static ByteBuffer convertToDDS(File file)
  647. +   {
  648. +       if (file == null)
  649. +       {
  650. +           String s = "nullValue.FileIsNull";
  651. +           LOG.severe(s + " " + new IllegalArgumentException(s));
  652. +           new IllegalArgumentException(s);
  653. +           return null;
  654. +       }
  655. +       if (!file.exists() || !file.canRead())
  656. +       {
  657. +           String s1 = "DDSConverter.NoFileOrNoPermission";
  658. +           LOG.severe(s1 + " " + new IllegalArgumentException(s1));
  659. +           new IllegalArgumentException(s1);
  660. +           return null;
  661. +       }
  662. +       BufferedImage bufferedimage = null;
  663. +       try
  664. +       {
  665. +           bufferedimage = ImageIO.read(file);
  666. +       }
  667. +       catch (IOException e)
  668. +       {
  669. +           e.printStackTrace();
  670. +           //LOG.error("Error while reading Image that needs to be DDSConverted", e);
  671. +       }
  672. +       if (bufferedimage == null)
  673. +       {
  674. +           return null;
  675. +       }
  676. +       if (bufferedimage.getColorModel().hasAlpha())
  677. +       {
  678. +           return convertToDxt3(bufferedimage);
  679. +       }
  680. +       return convertToDxt1NoTransparency(bufferedimage);
  681. +   }
  682. +  
  683. +   public static ByteBuffer convertToDxt1NoTransparency(BufferedImage bufferedimage)
  684. +   {
  685. +       if (bufferedimage == null)
  686. +       {
  687. +           return null;
  688. +       }
  689. +       int ai[] = new int[16];
  690. +       int i = 128 + ((bufferedimage.getWidth() * bufferedimage.getHeight()) / 2);
  691. +       ByteBuffer bytebuffer = ByteBuffer.allocate(i);
  692. +       bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
  693. +       buildHeaderDxt1(bytebuffer, bufferedimage.getWidth(), bufferedimage.getHeight());
  694. +       int j = bufferedimage.getWidth() / 4;
  695. +       int k = bufferedimage.getHeight() / 4;
  696. +       for (int l = 0; l < k; l++)
  697. +       {
  698. +           for (int i1 = 0; i1 < j; i1++)
  699. +           {
  700. +               BufferedImage bufferedimage1 = bufferedimage.getSubimage(i1 * 4, l * 4, 4, 4);
  701. +               bufferedimage1.getRGB(0, 0, 4, 4, ai, 0, 4);
  702. +               Color acolor[] = getColors888(ai);
  703. +               for (int j1 = 0; j1 < ai.length; j1++)
  704. +               {
  705. +                   ai[j1] = getPixel565(acolor[j1]);
  706. +                   acolor[j1] = getColor565(ai[j1]);
  707. +               }
  708. +              
  709. +               int ai1[] = determineExtremeColors(acolor);
  710. +               if (ai[ai1[0]] < ai[ai1[1]])
  711. +               {
  712. +                   int k1 = ai1[0];
  713. +                   ai1[0] = ai1[1];
  714. +                   ai1[1] = k1;
  715. +               }
  716. +               bytebuffer.putShort((short) ai[ai1[0]]);
  717. +               bytebuffer.putShort((short) ai[ai1[1]]);
  718. +               long l1 = computeBitMask(acolor, ai1);
  719. +               bytebuffer.putInt((int) l1);
  720. +           }
  721. +          
  722. +       }
  723. +      
  724. +       return bytebuffer;
  725. +   }
  726. +  
  727. +   public static ByteBuffer convertToDxt3(BufferedImage bufferedimage)
  728. +   {
  729. +       if (bufferedimage == null)
  730. +       {
  731. +           return null;
  732. +       }
  733. +       if (!bufferedimage.getColorModel().hasAlpha())
  734. +       {
  735. +           return convertToDxt1NoTransparency(bufferedimage);
  736. +       }
  737. +       int ai[] = new int[16];
  738. +       int i = 128 + (bufferedimage.getWidth() * bufferedimage.getHeight());
  739. +       ByteBuffer bytebuffer = ByteBuffer.allocate(i);
  740. +       bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
  741. +       buildHeaderDxt3(bytebuffer, bufferedimage.getWidth(), bufferedimage.getHeight());
  742. +       int j = bufferedimage.getWidth() / 4;
  743. +       int k = bufferedimage.getHeight() / 4;
  744. +       for (int l = 0; l < k; l++)
  745. +       {
  746. +           for (int i1 = 0; i1 < j; i1++)
  747. +           {
  748. +               BufferedImage bufferedimage1 = bufferedimage.getSubimage(i1 * 4, l * 4, 4, 4);
  749. +               bufferedimage1.getRGB(0, 0, 4, 4, ai, 0, 4);
  750. +               Color acolor[] = getColors888(ai);
  751. +               for (int j1 = 0; j1 < ai.length; j1 += 2)
  752. +               {
  753. +                   bytebuffer.put((byte) ((ai[j1] >>> 28) | (ai[j1 + 1] >>> 24)));
  754. +               }
  755. +              
  756. +               for (int k1 = 0; k1 < ai.length; k1++)
  757. +               {
  758. +                   ai[k1] = getPixel565(acolor[k1]);
  759. +                   acolor[k1] = getColor565(ai[k1]);
  760. +               }
  761. +              
  762. +               int ai1[] = determineExtremeColors(acolor);
  763. +               if (ai[ai1[0]] < ai[ai1[1]])
  764. +               {
  765. +                   int l1 = ai1[0];
  766. +                   ai1[0] = ai1[1];
  767. +                   ai1[1] = l1;
  768. +               }
  769. +               bytebuffer.putShort((short) ai[ai1[0]]);
  770. +               bytebuffer.putShort((short) ai[ai1[1]]);
  771. +               long l2 = computeBitMask(acolor, ai1);
  772. +               bytebuffer.putInt((int) l2);
  773. +           }
  774. +          
  775. +       }
  776. +      
  777. +       return bytebuffer;
  778. +   }
  779. +  
  780. +   protected static void buildHeaderDxt1(ByteBuffer bytebuffer, int i, int j)
  781. +   {
  782. +       bytebuffer.rewind();
  783. +       bytebuffer.put((byte) 68);
  784. +       bytebuffer.put((byte) 68);
  785. +       bytebuffer.put((byte) 83);
  786. +       bytebuffer.put((byte) 32);
  787. +       bytebuffer.putInt(124);
  788. +       int k = 0xa1007;
  789. +       bytebuffer.putInt(k);
  790. +       bytebuffer.putInt(j);
  791. +       bytebuffer.putInt(i);
  792. +       bytebuffer.putInt((i * j) / 2);
  793. +       bytebuffer.putInt(0);
  794. +       bytebuffer.putInt(0);
  795. +       bytebuffer.position(bytebuffer.position() + 44);
  796. +       bytebuffer.putInt(32);
  797. +       bytebuffer.putInt(4);
  798. +       bytebuffer.put((byte) 68);
  799. +       bytebuffer.put((byte) 88);
  800. +       bytebuffer.put((byte) 84);
  801. +       bytebuffer.put((byte) 49);
  802. +       bytebuffer.putInt(0);
  803. +       bytebuffer.putInt(0);
  804. +       bytebuffer.putInt(0);
  805. +       bytebuffer.putInt(0);
  806. +       bytebuffer.putInt(0);
  807. +       bytebuffer.putInt(4096);
  808. +       bytebuffer.putInt(0);
  809. +       bytebuffer.position(bytebuffer.position() + 12);
  810. +   }
  811. +  
  812. +   protected static void buildHeaderDxt3(ByteBuffer bytebuffer, int i, int j)
  813. +   {
  814. +       bytebuffer.rewind();
  815. +       bytebuffer.put((byte) 68);
  816. +       bytebuffer.put((byte) 68);
  817. +       bytebuffer.put((byte) 83);
  818. +       bytebuffer.put((byte) 32);
  819. +       bytebuffer.putInt(124);
  820. +       int k = 0xa1007;
  821. +       bytebuffer.putInt(k);
  822. +       bytebuffer.putInt(j);
  823. +       bytebuffer.putInt(i);
  824. +       bytebuffer.putInt(i * j);
  825. +       bytebuffer.putInt(0);
  826. +       bytebuffer.putInt(0);
  827. +       bytebuffer.position(bytebuffer.position() + 44);
  828. +       bytebuffer.putInt(32);
  829. +       bytebuffer.putInt(4);
  830. +       bytebuffer.put((byte) 68);
  831. +       bytebuffer.put((byte) 88);
  832. +       bytebuffer.put((byte) 84);
  833. +       bytebuffer.put((byte) 51);
  834. +       bytebuffer.putInt(0);
  835. +       bytebuffer.putInt(0);
  836. +       bytebuffer.putInt(0);
  837. +       bytebuffer.putInt(0);
  838. +       bytebuffer.putInt(0);
  839. +       bytebuffer.putInt(4096);
  840. +       bytebuffer.putInt(0);
  841. +       bytebuffer.position(bytebuffer.position() + 12);
  842. +   }
  843. +  
  844. +   protected static int[] determineExtremeColors(Color acolor[])
  845. +   {
  846. +       int i = 0x80000000;
  847. +       int ai[] = new int[2];
  848. +       for (int j = 0; j < (acolor.length - 1); j++)
  849. +       {
  850. +           for (int k = j + 1; k < acolor.length; k++)
  851. +           {
  852. +               int l = distance(acolor[j], acolor[k]);
  853. +               if (l > i)
  854. +               {
  855. +                   i = l;
  856. +                   ai[0] = j;
  857. +                   ai[1] = k;
  858. +               }
  859. +           }
  860. +          
  861. +       }
  862. +      
  863. +       return ai;
  864. +   }
  865. +  
  866. +   protected static long computeBitMask(Color acolor[], int ai[])
  867. +   {
  868. +       Color acolor1[] =
  869. +       {
  870. +           null,
  871. +           null,
  872. +           new Color(),
  873. +           new Color()
  874. +       };
  875. +       acolor1[0] = acolor[ai[0]];
  876. +       acolor1[1] = acolor[ai[1]];
  877. +       if (acolor1[0].equals(acolor1[1]))
  878. +       {
  879. +           return 0L;
  880. +       }
  881. +       acolor1[2].r = ((2 * acolor1[0].r) + acolor1[1].r + 1) / 3;
  882. +       acolor1[2].g = ((2 * acolor1[0].g) + acolor1[1].g + 1) / 3;
  883. +       acolor1[2].b = ((2 * acolor1[0].b) + acolor1[1].b + 1) / 3;
  884. +       acolor1[3].r = (acolor1[0].r + (2 * acolor1[1].r) + 1) / 3;
  885. +       acolor1[3].g = (acolor1[0].g + (2 * acolor1[1].g) + 1) / 3;
  886. +       acolor1[3].b = (acolor1[0].b + (2 * acolor1[1].b) + 1) / 3;
  887. +       long l = 0L;
  888. +       for (int i = 0; i < acolor.length; i++)
  889. +       {
  890. +           int j = 0x7fffffff;
  891. +           int k = 0;
  892. +           for (int i1 = 0; i1 < acolor1.length; i1++)
  893. +           {
  894. +               int j1 = distance(acolor[i], acolor1[i1]);
  895. +               if (j1 < j)
  896. +               {
  897. +                   j = j1;
  898. +                   k = i1;
  899. +               }
  900. +           }
  901. +          
  902. +           l |= k << (i * 2);
  903. +       }
  904. +      
  905. +       return l;
  906. +   }
  907. +  
  908. +   protected static int getPixel565(Color color)
  909. +   {
  910. +       int i = color.r >> 3;
  911. +       int j = color.g >> 2;
  912. +       int k = color.b >> 3;
  913. +       return (i << 11) | (j << 5) | k;
  914. +   }
  915. +  
  916. +   protected static Color getColor565(int i)
  917. +   {
  918. +       Color color = new Color();
  919. +       color.r = (int) (i & 63488L) >> 11;
  920. +       color.g = (int) (i & 2016L) >> 5;
  921. +       color.b = (int) (i & 31L);
  922. +       return color;
  923. +   }
  924. +  
  925. +   protected static Color[] getColors888(int ai[])
  926. +   {
  927. +       Color acolor[] = new Color[ai.length];
  928. +       for (int i = 0; i < ai.length; i++)
  929. +       {
  930. +           acolor[i] = new Color();
  931. +           acolor[i].r = (int) (ai[i] & 0xff0000L) >> 16;
  932. +           acolor[i].g = (int) (ai[i] & 65280L) >> 8;
  933. +           acolor[i].b = (int) (ai[i] & 255L);
  934. +       }
  935. +      
  936. +       return acolor;
  937. +   }
  938. +  
  939. +   protected static int distance(Color color, Color color1)
  940. +   {
  941. +       return ((color1.r - color.r) * (color1.r - color.r)) + ((color1.g - color.g) * (color1.g - color.g)) + ((color1.b - color.b) * (color1.b - color.b));
  942. +   }
  943. +}
  944. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/ImageData.java
  945. ===================================================================
  946. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/ImageData.java    (revision 427)
  947. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/ImageData.java    (revision 428)
  948. +/*
  949. + * This program is free software: you can redistribute it and/or modify it under
  950. + * the terms of the GNU General Public License as published by the Free Software
  951. + * Foundation, either version 3 of the License, or (at your option) any later
  952. + * version.
  953. + *
  954. + * This program is distributed in the hope that it will be useful, but WITHOUT
  955. + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  956. + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  957. + * details.
  958. + *
  959. + * You should have received a copy of the GNU General Public License along with
  960. + * this program. If not, see <http://www.gnu.org/licenses/>.
  961. + */
  962. +package net.sf.l2j.gameserver.features;
  963. +
  964. +import java.awt.image.BufferedImage;
  965. +
  966. +public class ImageData
  967. +{
  968. +   public ImageData()
  969. +   {
  970. +   }
  971. +          
  972. +   public BufferedImage image;
  973. +   public String captchaText = "";
  974. +   public int captchaID = 0;
  975. +}
  976. Index: trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/Captcha.java
  977. ===================================================================
  978. --- trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/Captcha.java  (revision 427)
  979. +++ trunk/aCis_gameserver/java/net/sf/l2j/gameserver/features/Captcha.java  (revision 428)
  980. +package net.sf.l2j.gameserver.features;
  981. +
  982. +import java.awt.Color;
  983. +import java.awt.Font;
  984. +import java.awt.FontMetrics;
  985. +import java.awt.Graphics2D;
  986. +import java.awt.geom.AffineTransform;
  987. +import java.awt.image.BufferedImage;
  988. +import java.util.Map;
  989. +import java.util.concurrent.ConcurrentHashMap;
  990. +
  991. +import net.sf.l2j.commons.random.Rnd;
  992. +import net.sf.l2j.gameserver.instancemanager.BotsPreventionManager.PlayerData;
  993. +import net.sf.l2j.gameserver.model.actor.instance.Player;
  994. +
  995. +/**
  996. + * Class that handles Generating and Sending Captcha Image to the Player
  997. + */
  998. +public class Captcha
  999. +{
  1000. +   private static final char[] CAPTCHA_TEXT_POSSIBILITIES =
  1001. +   {
  1002. +       'A',
  1003. +       'B',
  1004. +       'C',
  1005. +       'D',
  1006. +       'E',
  1007. +       'F',
  1008. +       'G',
  1009. +       'H',
  1010. +       'K',
  1011. +       'L',
  1012. +       'M',
  1013. +       'P',
  1014. +       'R',
  1015. +       'S',
  1016. +       'T',
  1017. +       'U',
  1018. +       'W',
  1019. +       'X',
  1020. +       'Y',
  1021. +       'Z'
  1022. +   };
  1023. +   private static final int CAPTCHA_WORD_LENGTH = 5;
  1024. +  
  1025. +   private static final int CAPTCHA_MIN_ID = 19000;
  1026. +   private static final int CAPTCHA_MAX_ID = 25000;
  1027. +  
  1028. +   Captcha()
  1029. +   {
  1030. +   }
  1031. +  
  1032. +   public static Captcha getInstance()
  1033. +   {
  1034. +       return SingletonHolder._instance;
  1035. +   }
  1036. +  
  1037. +   private static class SingletonHolder
  1038. +   {
  1039. +       protected static final Captcha _instance = new Captcha();
  1040. +   }
  1041. +  
  1042. +    public void generateCaptcha(PlayerData container, Player target)
  1043. +    {
  1044. +    int captchaId = generateRandomCaptchaId();
  1045. +    char[] captchaText = generateCaptchaText();
  1046. +  
  1047. +    container.image = generateCaptcha(captchaText);
  1048. +    // PledgeCrest packet = new PledgeCrest(captchaId, DDSConverter.convertToDDS(image).array());
  1049. +    // target.sendPacket(packet);
  1050. +  
  1051. +    container.captchaID = captchaId;
  1052. +    container.captchaText = String.valueOf(captchaText);
  1053. +    }
  1054. +   char[] captchaText;
  1055. +   int captchaId;
  1056. +  
  1057. +   public synchronized Map<Integer, ImageData> createImageList()
  1058. +   {
  1059. +       Map<Integer, ImageData> _imageMap = new ConcurrentHashMap<>(); 
  1060. +       for (int i = 0; i < 1000; i++)
  1061. +       {
  1062. +           do
  1063. +           {
  1064. +               captchaId = generateRandomCaptchaId();
  1065. +               captchaText = generateCaptchaText();
  1066. +           }
  1067. +           while (!_imageMap.isEmpty() && _imageMap.values().stream().anyMatch(txt -> txt.captchaText.equals(String.valueOf(captchaText))) || _imageMap.values().stream().anyMatch(s -> s.captchaID == captchaId));
  1068. +           ImageData dt = new ImageData();
  1069. +           dt.captchaID = captchaId;
  1070. +           dt.captchaText = String.valueOf(captchaText);
  1071. +           dt.image = generateCaptcha(captchaText);
  1072. +           _imageMap.put(i, dt);
  1073. +       }
  1074. +       return _imageMap;
  1075. +   }
  1076. +  
  1077. +   private static char[] generateCaptchaText()
  1078. +   {
  1079. +       char[] text = new char[CAPTCHA_WORD_LENGTH];
  1080. +       for (int i = 0; i < CAPTCHA_WORD_LENGTH; i++)
  1081. +           text[i] = CAPTCHA_TEXT_POSSIBILITIES[Rnd.get(CAPTCHA_TEXT_POSSIBILITIES.length)];
  1082. +       return text;
  1083. +   }
  1084. +  
  1085. +   private static int generateRandomCaptchaId()
  1086. +   {
  1087. +       return Rnd.get(CAPTCHA_MIN_ID, CAPTCHA_MAX_ID);
  1088. +   }
  1089. +  
  1090. +   public static BufferedImage generateCaptcha(char[] text)
  1091. +   {
  1092. +       Color textColor = new Color(38, 213, 30);
  1093. +       Color circleColor = new Color(73, 100, 151);
  1094. +       Font textFont = new Font("comic sans ms", Font.BOLD, 24);
  1095. +       int charsToPrint = 5;
  1096. +       int width = 256;
  1097. +       int height = 64;
  1098. +       int circlesToDraw = 8;
  1099. +       float horizMargin = 20.0f;
  1100. +       double rotationRange = 0.7; // this is radians
  1101. +       BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  1102. +      
  1103. +       Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
  1104. +      
  1105. +       // Draw an oval
  1106. +       g.setColor(new Color(30, 31, 31));
  1107. +       g.fillRect(0, 0, width, height);
  1108. +      
  1109. +       g.setColor(circleColor);
  1110. +       for (int i = 0; i < circlesToDraw; i++)
  1111. +       {
  1112. +           int circleRadius = (int) (Math.random() * height / 2.0);
  1113. +           int circleX = (int) (Math.random() * width - circleRadius);
  1114. +           int circleY = (int) (Math.random() * height - circleRadius);
  1115. +           g.drawOval(circleX, circleY, circleRadius * 2, circleRadius * 2);
  1116. +       }
  1117. +      
  1118. +       g.setColor(textColor);
  1119. +       g.setFont(textFont);
  1120. +      
  1121. +       FontMetrics fontMetrics = g.getFontMetrics();
  1122. +       int maxAdvance = fontMetrics.getMaxAdvance();
  1123. +       int fontHeight = fontMetrics.getHeight();
  1124. +      
  1125. +       float spaceForLetters = -horizMargin * 2.0F + width;
  1126. +       float spacePerChar = spaceForLetters / (charsToPrint - 1.0f);
  1127. +      
  1128. +       for (int i = 0; i < charsToPrint; i++)
  1129. +       {
  1130. +           char characterToShow = text[i];
  1131. +          
  1132. +           // this is a separate canvas used for the character so that
  1133. +           // we can rotate it independently
  1134. +           int charWidth = fontMetrics.charWidth(characterToShow);
  1135. +           int charDim = Math.max(maxAdvance, fontHeight);
  1136. +           int halfCharDim = charDim / 2;
  1137. +          
  1138. +           BufferedImage charImage = new BufferedImage(charDim, charDim, BufferedImage.TYPE_INT_ARGB);
  1139. +           Graphics2D charGraphics = charImage.createGraphics();
  1140. +           charGraphics.translate(halfCharDim, halfCharDim);
  1141. +           double angle = (Math.random() - 0.5) * rotationRange;
  1142. +           charGraphics.transform(AffineTransform.getRotateInstance(angle));
  1143. +           charGraphics.translate(-halfCharDim, -halfCharDim);
  1144. +           charGraphics.setColor(textColor);
  1145. +           charGraphics.setFont(textFont);
  1146. +          
  1147. +           int charX = (int) (0.5 * charDim - 0.5 * charWidth);
  1148. +           charGraphics.drawString(String.valueOf(characterToShow), charX, (charDim - fontMetrics.getAscent()) / 2 + fontMetrics.getAscent());
  1149. +          
  1150. +           float x = horizMargin + spacePerChar * i - charDim / 2.0f;
  1151. +           int y = (height - charDim) / 2;
  1152. +           g.drawImage(charImage, (int) x, y, charDim, charDim, null, null);
  1153. +          
  1154. +           charGraphics.dispose();
  1155. +       }
  1156. +      
  1157. +       g.dispose();
  1158. +      
  1159. +       return bufferedImage;
  1160. +   }
  1161. +}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement