121121)
122122public class BotDetectorPlugin extends Plugin
123123{
124+ /** {@link PlayerSighting} should not be created if the player is logged into one of these {@link WorldType}s. **/
124125 private static final ImmutableSet <WorldType > BLOCKED_WORLD_TYPES =
125126 ImmutableSet .of (
126127 WorldType .LEAGUE ,
@@ -155,6 +156,8 @@ public class BotDetectorPlugin extends Plugin
155156 private static final String GET_AUTH_TOKEN_COMMAND = COMMAND_PREFIX + "GetToken" ;
156157 private static final String SET_AUTH_TOKEN_COMMAND = COMMAND_PREFIX + "SetToken" ;
157158 private static final String CLEAR_AUTH_TOKEN_COMMAND = COMMAND_PREFIX + "ClearToken" ;
159+
160+ /** Command to method map to be used in {@link #onCommandExecuted(CommandExecuted)}. **/
158161 private final ImmutableMap <CaseInsensitiveString , Consumer <String []>> commandConsumerMap =
159162 ImmutableMap .<CaseInsensitiveString , Consumer <String []>>builder ()
160163 .put (wrap (MANUAL_FLUSH_COMMAND ), s -> manualFlushCommand ())
@@ -210,30 +213,65 @@ BotDetectorConfig provideConfig(ConfigManager configManager)
210213 return configManager .getConfig (BotDetectorConfig .class );
211214 }
212215
216+ /** The currently logged in player name, or {@code null} if the user is logged out. **/
213217 @ Getter
214218 private String loggedPlayerName ;
219+ /** The next time an automatic call to {@link #flushPlayersToClient(boolean)} should be allowed to run. **/
215220 private Instant timeToAutoSend ;
221+ /** The total number of names uploaded in the current login session. **/
216222 private int namesUploaded ;
223+ /** The last time a {@link #flushPlayersToClient(boolean)} was successfully attempted. **/
217224 private Instant lastFlush = Instant .MIN ;
225+ /** The last time a {@link #refreshPlayerStats(boolean)}} was successfully attempted. **/
218226 private Instant lastStatsRefresh = Instant .MIN ;
227+ /** See {@link #processCurrentWorld()}. **/
219228 private int currentWorldNumber ;
229+ /** See {@link #processCurrentWorld()}. **/
220230 private boolean isCurrentWorldMembers ;
231+ /** See {@link #processCurrentWorld()}. **/
221232 private boolean isCurrentWorldPVP ;
233+ /** Blocked world types should not log player sightings (see {@link #processCurrentWorld()} and {@link #BLOCKED_WORLD_TYPES}). **/
222234 private boolean isCurrentWorldBlocked ;
235+ /** A queue containing the last two {@link GameState}s from {@link #onGameStateChanged(GameStateChanged)}. **/
223236 private EvictingQueue <GameState > previousTwoGameStates = EvictingQueue .create (2 );
224237
238+ /** The currently loaded token or {@link AuthToken#EMPTY_TOKEN} if no valid token is loaded. **/
225239 @ Getter
226240 private AuthToken authToken = AuthToken .EMPTY_TOKEN ;
227241
228- // Current login maps, clear on logout/shutdown. Feedback/Flag map to selected value in panel.
229- // All map keys should get handled with normalizeAndWrapPlayerName() or using wrap() on an already normalized name
242+ /**
243+ * Contains the last {@link PlayerSighting} for the given {@code player} and {@code regionId}
244+ * since the last successful call to {@link #flushPlayersToClient(boolean, boolean)}.
245+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this table.
246+ */
247+ @ Getter
230248 private final Table <CaseInsensitiveString , Integer , PlayerSighting > sightingTable = Tables .synchronizedTable (HashBasedTable .create ());
249+
250+ /**
251+ * Contains the last {@link PlayerSighting} for the given {@code player} for the current login session.
252+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
253+ */
231254 @ Getter
232255 private final Map <CaseInsensitiveString , PlayerSighting > persistentSightings = new ConcurrentHashMap <>();
256+
257+ /**
258+ * Contains the feedbacks (good/not good) sent per {@code player} for the current login session.
259+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
260+ */
233261 @ Getter
234262 private final Map <CaseInsensitiveString , Boolean > feedbackedPlayers = new ConcurrentHashMap <>();
263+
264+ /**
265+ * Contains the feedback texts sent per {@code player} for the current login session.
266+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
267+ */
235268 @ Getter
236269 private final Map <CaseInsensitiveString , String > feedbackedPlayersText = new ConcurrentHashMap <>();
270+
271+ /**
272+ * Contains the flagging (yes/no) sent per {@code player} for the current login session.
273+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
274+ */
237275 @ Getter
238276 private final Map <CaseInsensitiveString , Boolean > flaggedPlayers = new ConcurrentHashMap <>();
239277
@@ -326,6 +364,9 @@ protected void shutDown()
326364 chatCommandManager .unregisterCommand (VERIFY_DISCORD_COMMAND );
327365 }
328366
367+ /**
368+ * Updates {@link #timeToAutoSend} according to {@link BotDetectorConfig#autoSendMinutes()}.
369+ */
329370 private void updateTimeToAutoSend ()
330371 {
331372 timeToAutoSend = Instant .now ().plusSeconds (60L *
@@ -334,8 +375,10 @@ private void updateTimeToAutoSend()
334375 BotDetectorConfig .AUTO_SEND_MAXIMUM_MINUTES ));
335376 }
336377
337- @ Schedule (period = API_HIT_SCHEDULE_SECONDS ,
338- unit = ChronoUnit .SECONDS , asynchronous = true )
378+ /**
379+ * Do not call this method in code. Continuously calls the automatic variants of API calling methods.
380+ */
381+ @ Schedule (period = API_HIT_SCHEDULE_SECONDS , unit = ChronoUnit .SECONDS , asynchronous = true )
339382 public void hitApi ()
340383 {
341384 if (loggedPlayerName == null )
@@ -351,11 +394,22 @@ public void hitApi()
351394 refreshPlayerStats (false );
352395 }
353396
397+ /**
398+ * Attempts to send the contents of {@link #sightingTable} to {@link BotDetectorClient#sendSightings(Collection, String, boolean)}.
399+ * @param restoreOnFailure The table is cleared before sending. If {@code true}, re-insert the cleared sightings into the table on failure.
400+ * @return {@code true} if there were any names to attempt to send, {@code false} otherwise.
401+ */
354402 public synchronized boolean flushPlayersToClient (boolean restoreOnFailure )
355403 {
356404 return flushPlayersToClient (restoreOnFailure , false );
357405 }
358406
407+ /**
408+ * Attempts to send the contents of {@link #sightingTable} to {@link BotDetectorClient#sendSightings(Collection, String, boolean)}.
409+ * @param restoreOnFailure The table is cleared before sending. If {@code true}, re-insert the cleared sightings into the table on failure.
410+ * @param forceChatNotification Force displays the chat notifications.
411+ * @return {@code true} if there were any names to attempt to send, {@code false} otherwise.
412+ */
359413 public synchronized boolean flushPlayersToClient (boolean restoreOnFailure , boolean forceChatNotification )
360414 {
361415 if (loggedPlayerName == null )
@@ -419,8 +473,10 @@ public synchronized boolean flushPlayersToClient(boolean restoreOnFailure, boole
419473 return true ;
420474 }
421475
422- // Atomic, just to make sure a non-forced call (e.g. auto refresh)
423- // can't get past the checks while another call is setting the last refresh value.
476+ /**
477+ * Attempts to refresh the current player uploading statistics on the plugin panel according to various checks.
478+ * @param forceRefresh If {@code true}, ignore checks in place meant for the automatic calling of this method.
479+ */
424480 public synchronized void refreshPlayerStats (boolean forceRefresh )
425481 {
426482 if (!forceRefresh )
@@ -572,6 +628,10 @@ private void onPlayerSpawned(PlayerSpawned event)
572628 processPlayer (event .getPlayer ());
573629 }
574630
631+ /**
632+ * Processes the given {@code player}, creating and saving a {@link PlayerSighting}.
633+ * @param player The player to process.
634+ */
575635 private void processPlayer (Player player )
576636 {
577637 if (player == null )
@@ -651,6 +711,13 @@ private void onCommandExecuted(CommandExecuted event)
651711 }
652712 }
653713
714+ /**
715+ * Parses the Author and Code from the given message arguments and sends them over to
716+ * {@link BotDetectorClient#verifyDiscord(String, String, String)} for verification.
717+ * Requires that {@link #authToken} has the {@link AuthTokenPermission#VERIFY_DISCORD} permission.
718+ * @param chatMessage The ChatMessage event object.
719+ * @param message The actual chat message.
720+ */
654721 private void verifyDiscord (ChatMessage chatMessage , String message )
655722 {
656723 if (!authToken .getTokenType ().getPermissions ().contains (AuthTokenPermission .VERIFY_DISCORD ))
@@ -810,6 +877,10 @@ private void onWorldChanged(WorldChanged event)
810877 processCurrentWorld ();
811878 }
812879
880+ /**
881+ * Opens the plugin panel and sends over {@code playerName} to {@link BotDetectorPanel#predictPlayer(String)} for prediction.
882+ * @param playerName The player name to predict.
883+ */
813884 public void predictPlayer (String playerName )
814885 {
815886 SwingUtilities .invokeLater (() ->
@@ -823,11 +894,20 @@ public void predictPlayer(String playerName)
823894 });
824895 }
825896
897+ /**
898+ * Sends a message to the in-game chatbox if {@link BotDetectorConfig#enableChatStatusMessages()} is {@code true}.
899+ * @param msg The message to send.
900+ */
826901 public void sendChatStatusMessage (String msg )
827902 {
828903 sendChatStatusMessage (msg , false );
829904 }
830905
906+ /**
907+ * Sends a message to the in-game chatbox.
908+ * @param msg The message to send.
909+ * @param forceShow If {@code true}, bypasses {@link BotDetectorConfig#enableChatStatusMessages()}.
910+ */
831911 public void sendChatStatusMessage (String msg , boolean forceShow )
832912 {
833913 if ((forceShow || config .enableChatStatusMessages ()) && loggedPlayerName != null )
@@ -845,6 +925,9 @@ public void sendChatStatusMessage(String msg, boolean forceShow)
845925 }
846926 }
847927
928+ /**
929+ * Sets various class variables and panel warnings according to what {@link Client#getWorld()} returns.
930+ */
848931 private void processCurrentWorld ()
849932 {
850933 currentWorldNumber = client .getWorld ();
@@ -856,6 +939,11 @@ private void processCurrentWorld()
856939 panel .setWarningVisible (BotDetectorPanel .WarningLabel .BLOCKED_WORLD , isCurrentWorldBlocked ));
857940 }
858941
942+ /**
943+ * Gets the name that should be used when an uploader name is required,
944+ * according to {@link BotDetectorConfig#enableAnonymousUploading()}.
945+ * @return {@link #loggedPlayerName} or {@link #ANONYMOUS_USER_NAME}.
946+ */
859947 public String getUploaderName ()
860948 {
861949 if (loggedPlayerName == null || config .enableAnonymousUploading ())
@@ -866,11 +954,20 @@ public String getUploaderName()
866954 return loggedPlayerName ;
867955 }
868956
957+ /**
958+ * Gets the correct variant of {@link #PREDICT_OPTION} to show ({@code player} agnostic version).
959+ * @return A variant of {@link #PREDICT_OPTION} or {@link #HIGHLIGHTED_PREDICT_OPTION}.
960+ */
869961 private String getPredictOption ()
870962 {
871963 return getPredictOption (null );
872964 }
873965
966+ /**
967+ * Gets the correct variant of {@link #PREDICT_OPTION} to show for the given {@code player}.
968+ * @param playerName The player to get the menu option string for.
969+ * @return A variant of {@link #PREDICT_OPTION} or {@link #HIGHLIGHTED_PREDICT_OPTION}.
970+ */
874971 private String getPredictOption (String playerName )
875972 {
876973 switch (config .highlightPredictOption ())
@@ -885,6 +982,11 @@ private String getPredictOption(String playerName)
885982 }
886983 }
887984
985+ /**
986+ * Helper function to insert a {@link MenuEntry} in {@link Client#setMenuEntries(MenuEntry[])}.
987+ * @param newEntry The entry to add.
988+ * @param entries The current entries (usually from {@link Client#getMenuEntries()}.
989+ */
888990 private void insertMenuEntry (MenuEntry newEntry , MenuEntry [] entries )
889991 {
890992 MenuEntry [] newMenu = ObjectArrays .concat (entries , newEntry );
@@ -893,6 +995,12 @@ private void insertMenuEntry(MenuEntry newEntry, MenuEntry[] entries)
893995 client .setMenuEntries (newMenu );
894996 }
895997
998+ /**
999+ * Normalizes the given {@code playerName} by sanitizing the player name string,
1000+ * removing any Jagex tags and replacing any {@code _} or {@code -} with spaces.
1001+ * @param playerName The player name to normalize.
1002+ * @return The normalized {@code playerName}.
1003+ */
8961004 public static String normalizePlayerName (String playerName )
8971005 {
8981006 if (playerName == null )
@@ -903,13 +1011,23 @@ public static String normalizePlayerName(String playerName)
9031011 return Text .removeTags (Text .toJagexName (playerName ));
9041012 }
9051013
1014+ /**
1015+ * Normalizes the given {@code playerName} using {@link #normalizePlayerName(String)},
1016+ * then wraps the resulting {@link String} with {@link CaseInsensitiveString#wrap(String)}.
1017+ * @param playerName The player name to normalize and wrap.
1018+ * @return A {@link CaseInsensitiveString} containing the normalized {@code playerName}.
1019+ */
9061020 public static CaseInsensitiveString normalizeAndWrapPlayerName (String playerName )
9071021 {
9081022 return wrap (normalizePlayerName (playerName ));
9091023 }
9101024
9111025 //region Commands
9121026
1027+ /**
1028+ * Manually executes {@link #flushPlayersToClient(boolean, boolean)},
1029+ * first checking that {@link #lastFlush} did not occur within {@link #MANUAL_FLUSH_COOLDOWN_SECONDS}.
1030+ */
9131031 private void manualFlushCommand ()
9141032 {
9151033 Instant canFlush = lastFlush .plusSeconds (MANUAL_FLUSH_COOLDOWN_SECONDS );
@@ -928,6 +1046,9 @@ private void manualFlushCommand()
9281046 }
9291047 }
9301048
1049+ /**
1050+ * Manually force a full rescan of all players in {@link Client#getPlayers()} using {@link #processPlayer(Player)}.
1051+ */
9311052 private void manualSightCommand ()
9321053 {
9331054 if (isCurrentWorldBlocked )
@@ -946,12 +1067,19 @@ else if (client.getGameState() != GameState.LOGGED_IN)
9461067 }
9471068 }
9481069
1070+ /**
1071+ * Manually force executes {@link #refreshPlayerStats(boolean)}.
1072+ */
9491073 private void manualRefreshStatsCommand ()
9501074 {
9511075 refreshPlayerStats (true );
9521076 sendChatStatusMessage ("Refreshing player stats..." , true );
9531077 }
9541078
1079+ /**
1080+ * Shows or hides the player ID field in the plugin panel using {@link BotDetectorPanel#setPlayerIdVisible(boolean)}.
1081+ * @param args String arguments from {@link CommandExecuted#getArguments()}, requires 1 argument being either "0" or "1".
1082+ */
9551083 private void showHideIdCommand (String [] args )
9561084 {
9571085 String arg = args .length > 0 ? args [0 ] : "" ;
@@ -971,6 +1099,9 @@ private void showHideIdCommand(String[] args)
9711099 }
9721100 }
9731101
1102+ /**
1103+ * Gets the currently loaded {@link AuthToken} and copies it into the user's system clipboard.
1104+ */
9741105 private void putAuthTokenIntoClipboardCommand ()
9751106 {
9761107 if (authToken .getTokenType () == AuthTokenType .NONE )
@@ -985,6 +1116,10 @@ private void putAuthTokenIntoClipboardCommand()
9851116 }
9861117 }
9871118
1119+ /**
1120+ * Sets the {@link AuthToken} saved in {@link BotDetectorConfig#authFullToken()} to the contents of the clipboard,
1121+ * assuming the contents respect the defined token format in {@link AuthToken#AUTH_TOKEN_PATTERN}.
1122+ */
9881123 private void setAuthTokenFromClipboardCommand ()
9891124 {
9901125 final String clipboardText ;
@@ -1016,6 +1151,9 @@ private void setAuthTokenFromClipboardCommand()
10161151 }
10171152 }
10181153
1154+ /**
1155+ * Clears the current {@link AuthToken} saved in {@link BotDetectorConfig#authFullToken()}.
1156+ */
10191157 private void clearAuthTokenCommand ()
10201158 {
10211159 authToken = AuthToken .EMPTY_TOKEN ;
@@ -1025,7 +1163,10 @@ private void clearAuthTokenCommand()
10251163
10261164 //endregion
10271165
1028- // This isn't perfect but really shouldn't ever happen!
1166+
1167+ /**
1168+ * Displays an error message about being unable to parse a plugin version and links to the Bot Detector Discord.
1169+ */
10291170 private void displayPluginVersionError ()
10301171 {
10311172 JEditorPane ep = new JEditorPane ("text/html" ,
0 commit comments