120120)
121121public class BotDetectorPlugin extends Plugin
122122{
123+ /** {@link PlayerSighting} should not be created if the player is logged into one of these {@link WorldType}s. **/
123124 private static final ImmutableSet <WorldType > BLOCKED_WORLD_TYPES =
124125 ImmutableSet .of (
125126 WorldType .LEAGUE ,
@@ -146,6 +147,8 @@ public class BotDetectorPlugin extends Plugin
146147 private static final String GET_AUTH_TOKEN_COMMAND = COMMAND_PREFIX + "GetToken" ;
147148 private static final String SET_AUTH_TOKEN_COMMAND = COMMAND_PREFIX + "SetToken" ;
148149 private static final String CLEAR_AUTH_TOKEN_COMMAND = COMMAND_PREFIX + "ClearToken" ;
150+
151+ /** Command to method map to be used in {@link #onCommandExecuted(CommandExecuted)}. **/
149152 private final ImmutableMap <CaseInsensitiveString , Consumer <String []>> commandConsumerMap =
150153 ImmutableMap .<CaseInsensitiveString , Consumer <String []>>builder ()
151154 .put (wrap (MANUAL_FLUSH_COMMAND ), s -> manualFlushCommand ())
@@ -201,30 +204,65 @@ BotDetectorConfig provideConfig(ConfigManager configManager)
201204 return configManager .getConfig (BotDetectorConfig .class );
202205 }
203206
207+ /** The currently logged in player name, or {@code null} if the user is logged out. **/
204208 @ Getter
205209 private String loggedPlayerName ;
210+ /** The next time an automatic call to {@link #flushPlayersToClient(boolean)} should be allowed to run. **/
206211 private Instant timeToAutoSend ;
212+ /** The total number of names uploaded in the current login session. **/
207213 private int namesUploaded ;
214+ /** The last time a {@link #flushPlayersToClient(boolean)} was successfully attempted. **/
208215 private Instant lastFlush = Instant .MIN ;
216+ /** The last time a {@link #refreshPlayerStats(boolean)}} was successfully attempted. **/
209217 private Instant lastStatsRefresh = Instant .MIN ;
218+ /** See {@link #processCurrentWorld()}. **/
210219 private int currentWorldNumber ;
220+ /** See {@link #processCurrentWorld()}. **/
211221 private boolean isCurrentWorldMembers ;
222+ /** See {@link #processCurrentWorld()}. **/
212223 private boolean isCurrentWorldPVP ;
224+ /** Blocked world types should not log player sightings (see {@link #processCurrentWorld()} and {@link #BLOCKED_WORLD_TYPES}). **/
213225 private boolean isCurrentWorldBlocked ;
226+ /** A queue containing the last two {@link GameState}s from {@link #onGameStateChanged(GameStateChanged)}. **/
214227 private EvictingQueue <GameState > previousTwoGameStates = EvictingQueue .create (2 );
215228
229+ /** The currently loaded token or {@link AuthToken#EMPTY_TOKEN} if no valid token is loaded. **/
216230 @ Getter
217231 private AuthToken authToken = AuthToken .EMPTY_TOKEN ;
218232
219- // Current login maps, clear on logout/shutdown. Feedback/Flag map to selected value in panel.
220- // All map keys should get handled with normalizeAndWrapPlayerName() or using wrap() on an already normalized name
233+ /**
234+ * Contains the last {@link PlayerSighting} for the given {@code player} and {@code regionId}
235+ * since the last successful call to {@link #flushPlayersToClient(boolean, boolean)}.
236+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this table.
237+ */
238+ @ Getter
221239 private final Table <CaseInsensitiveString , Integer , PlayerSighting > sightingTable = Tables .synchronizedTable (HashBasedTable .create ());
240+
241+ /**
242+ * Contains the last {@link PlayerSighting} for the given {@code player} for the current login session.
243+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
244+ */
222245 @ Getter
223246 private final Map <CaseInsensitiveString , PlayerSighting > persistentSightings = new ConcurrentHashMap <>();
247+
248+ /**
249+ * Contains the feedbacks (good/not good) sent per {@code player} for the current login session.
250+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
251+ */
224252 @ Getter
225253 private final Map <CaseInsensitiveString , Boolean > feedbackedPlayers = new ConcurrentHashMap <>();
254+
255+ /**
256+ * Contains the feedback texts sent per {@code player} for the current login session.
257+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
258+ */
226259 @ Getter
227260 private final Map <CaseInsensitiveString , String > feedbackedPlayersText = new ConcurrentHashMap <>();
261+
262+ /**
263+ * Contains the flagging (yes/no) sent per {@code player} for the current login session.
264+ * Always use {@link #normalizeAndWrapPlayerName(String)} when keying into this map.
265+ */
228266 @ Getter
229267 private final Map <CaseInsensitiveString , Boolean > flaggedPlayers = new ConcurrentHashMap <>();
230268
@@ -317,6 +355,9 @@ protected void shutDown()
317355 chatCommandManager .unregisterCommand (VERIFY_DISCORD_COMMAND );
318356 }
319357
358+ /**
359+ * Updates {@link #timeToAutoSend} according to {@link BotDetectorConfig#autoSendMinutes()}.
360+ */
320361 private void updateTimeToAutoSend ()
321362 {
322363 timeToAutoSend = Instant .now ().plusSeconds (60L *
@@ -325,8 +366,10 @@ private void updateTimeToAutoSend()
325366 BotDetectorConfig .AUTO_SEND_MAXIMUM_MINUTES ));
326367 }
327368
328- @ Schedule (period = API_HIT_SCHEDULE_SECONDS ,
329- unit = ChronoUnit .SECONDS , asynchronous = true )
369+ /**
370+ * Do not call this method in code. Continuously calls the automatic variants of API calling methods.
371+ */
372+ @ Schedule (period = API_HIT_SCHEDULE_SECONDS , unit = ChronoUnit .SECONDS , asynchronous = true )
330373 public void hitApi ()
331374 {
332375 if (loggedPlayerName == null )
@@ -342,11 +385,22 @@ public void hitApi()
342385 refreshPlayerStats (false );
343386 }
344387
388+ /**
389+ * Attempts to send the contents of {@link #sightingTable} to {@link BotDetectorClient#sendSightings(Collection, String, boolean)}.
390+ * @param restoreOnFailure The table is cleared before sending. If {@code true}, re-insert the cleared sightings into the table on failure.
391+ * @return {@code true} if there were any names to attempt to send, {@code false} otherwise.
392+ */
345393 public synchronized boolean flushPlayersToClient (boolean restoreOnFailure )
346394 {
347395 return flushPlayersToClient (restoreOnFailure , false );
348396 }
349397
398+ /**
399+ * Attempts to send the contents of {@link #sightingTable} to {@link BotDetectorClient#sendSightings(Collection, String, boolean)}.
400+ * @param restoreOnFailure The table is cleared before sending. If {@code true}, re-insert the cleared sightings into the table on failure.
401+ * @param forceChatNotification Force displays the chat notifications.
402+ * @return {@code true} if there were any names to attempt to send, {@code false} otherwise.
403+ */
350404 public synchronized boolean flushPlayersToClient (boolean restoreOnFailure , boolean forceChatNotification )
351405 {
352406 if (loggedPlayerName == null )
@@ -410,8 +464,10 @@ public synchronized boolean flushPlayersToClient(boolean restoreOnFailure, boole
410464 return true ;
411465 }
412466
413- // Atomic, just to make sure a non-forced call (e.g. auto refresh)
414- // can't get past the checks while another call is setting the last refresh value.
467+ /**
468+ * Attempts to refresh the current player uploading statistics on the plugin panel according to various checks.
469+ * @param forceRefresh If {@code true}, ignore checks in place meant for the automatic calling of this method.
470+ */
415471 public synchronized void refreshPlayerStats (boolean forceRefresh )
416472 {
417473 if (!forceRefresh )
@@ -563,6 +619,10 @@ private void onPlayerSpawned(PlayerSpawned event)
563619 processPlayer (event .getPlayer ());
564620 }
565621
622+ /**
623+ * Processes the given {@code player}, creating and saving a {@link PlayerSighting}.
624+ * @param player The player to process.
625+ */
566626 private void processPlayer (Player player )
567627 {
568628 if (player == null )
@@ -642,6 +702,13 @@ private void onCommandExecuted(CommandExecuted event)
642702 }
643703 }
644704
705+ /**
706+ * Parses the Author and Code from the given message arguments and sends them over to
707+ * {@link BotDetectorClient#verifyDiscord(String, String, String)} for verification.
708+ * Requires that {@link #authToken} has the {@link AuthTokenPermission#VERIFY_DISCORD} permission.
709+ * @param chatMessage The ChatMessage event object.
710+ * @param message The actual chat message.
711+ */
645712 private void verifyDiscord (ChatMessage chatMessage , String message )
646713 {
647714 if (!authToken .getTokenType ().getPermissions ().contains (AuthTokenPermission .VERIFY_DISCORD ))
@@ -795,6 +862,10 @@ private void onWorldChanged(WorldChanged event)
795862 processCurrentWorld ();
796863 }
797864
865+ /**
866+ * Opens the plugin panel and sends over {@code playerName} to {@link BotDetectorPanel#predictPlayer(String)} for prediction.
867+ * @param playerName The player name to predict.
868+ */
798869 public void predictPlayer (String playerName )
799870 {
800871 SwingUtilities .invokeLater (() ->
@@ -808,11 +879,20 @@ public void predictPlayer(String playerName)
808879 });
809880 }
810881
882+ /**
883+ * Sends a message to the in-game chatbox if {@link BotDetectorConfig#enableChatStatusMessages()} is {@code true}.
884+ * @param msg The message to send.
885+ */
811886 public void sendChatStatusMessage (String msg )
812887 {
813888 sendChatStatusMessage (msg , false );
814889 }
815890
891+ /**
892+ * Sends a message to the in-game chatbox.
893+ * @param msg The message to send.
894+ * @param forceShow If {@code true}, bypasses {@link BotDetectorConfig#enableChatStatusMessages()}.
895+ */
816896 public void sendChatStatusMessage (String msg , boolean forceShow )
817897 {
818898 if ((forceShow || config .enableChatStatusMessages ()) && loggedPlayerName != null )
@@ -830,6 +910,9 @@ public void sendChatStatusMessage(String msg, boolean forceShow)
830910 }
831911 }
832912
913+ /**
914+ * Sets various class variables and panel warnings according to what {@link Client#getWorld()} returns.
915+ */
833916 private void processCurrentWorld ()
834917 {
835918 currentWorldNumber = client .getWorld ();
@@ -841,6 +924,11 @@ private void processCurrentWorld()
841924 panel .setWarningVisible (BotDetectorPanel .WarningLabel .BLOCKED_WORLD , isCurrentWorldBlocked ));
842925 }
843926
927+ /**
928+ * Gets the name that should be used when an uploader name is required,
929+ * according to {@link BotDetectorConfig#enableAnonymousUploading()}.
930+ * @return {@link #loggedPlayerName} or {@link #ANONYMOUS_USER_NAME}.
931+ */
844932 public String getUploaderName ()
845933 {
846934 if (loggedPlayerName == null || config .enableAnonymousUploading ())
@@ -851,11 +939,20 @@ public String getUploaderName()
851939 return loggedPlayerName ;
852940 }
853941
942+ /**
943+ * Gets the correct variant of {@link #PREDICT_OPTION} to show ({@code player} agnostic version).
944+ * @return A variant of {@link #PREDICT_OPTION} or {@link #HIGHLIGHTED_PREDICT_OPTION}.
945+ */
854946 private String getPredictOption ()
855947 {
856948 return getPredictOption (null );
857949 }
858950
951+ /**
952+ * Gets the correct variant of {@link #PREDICT_OPTION} to show for the given {@code player}.
953+ * @param playerName The player to get the menu option string for.
954+ * @return A variant of {@link #PREDICT_OPTION} or {@link #HIGHLIGHTED_PREDICT_OPTION}.
955+ */
859956 private String getPredictOption (String playerName )
860957 {
861958 switch (config .highlightPredictOption ())
@@ -870,6 +967,11 @@ private String getPredictOption(String playerName)
870967 }
871968 }
872969
970+ /**
971+ * Helper function to insert a {@link MenuEntry} in {@link Client#setMenuEntries(MenuEntry[])}.
972+ * @param newEntry The entry to add.
973+ * @param entries The current entries (usually from {@link Client#getMenuEntries()}.
974+ */
873975 private void insertMenuEntry (MenuEntry newEntry , MenuEntry [] entries )
874976 {
875977 MenuEntry [] newMenu = ObjectArrays .concat (entries , newEntry );
@@ -878,6 +980,12 @@ private void insertMenuEntry(MenuEntry newEntry, MenuEntry[] entries)
878980 client .setMenuEntries (newMenu );
879981 }
880982
983+ /**
984+ * Normalizes the given {@code playerName} by sanitizing the player name string,
985+ * removing any Jagex tags and replacing any {@code _} or {@code -} with spaces.
986+ * @param playerName The player name to normalize.
987+ * @return The normalized {@code playerName}.
988+ */
881989 public static String normalizePlayerName (String playerName )
882990 {
883991 if (playerName == null )
@@ -888,13 +996,23 @@ public static String normalizePlayerName(String playerName)
888996 return Text .removeTags (Text .toJagexName (playerName ));
889997 }
890998
999+ /**
1000+ * Normalizes the given {@code playerName} using {@link #normalizePlayerName(String)},
1001+ * then wraps the resulting {@link String} with {@link CaseInsensitiveString#wrap(String)}.
1002+ * @param playerName The player name to normalize and wrap.
1003+ * @return A {@link CaseInsensitiveString} containing the normalized {@code playerName}.
1004+ */
8911005 public static CaseInsensitiveString normalizeAndWrapPlayerName (String playerName )
8921006 {
8931007 return wrap (normalizePlayerName (playerName ));
8941008 }
8951009
8961010 //region Commands
8971011
1012+ /**
1013+ * Manually executes {@link #flushPlayersToClient(boolean, boolean)},
1014+ * first checking that {@link #lastFlush} did not occur within {@link #MANUAL_FLUSH_COOLDOWN_SECONDS}.
1015+ */
8981016 private void manualFlushCommand ()
8991017 {
9001018 Instant canFlush = lastFlush .plusSeconds (MANUAL_FLUSH_COOLDOWN_SECONDS );
@@ -913,6 +1031,9 @@ private void manualFlushCommand()
9131031 }
9141032 }
9151033
1034+ /**
1035+ * Manually force a full rescan of all players in {@link Client#getPlayers()} using {@link #processPlayer(Player)}.
1036+ */
9161037 private void manualSightCommand ()
9171038 {
9181039 if (isCurrentWorldBlocked )
@@ -931,12 +1052,19 @@ else if (client.getGameState() != GameState.LOGGED_IN)
9311052 }
9321053 }
9331054
1055+ /**
1056+ * Manually force executes {@link #refreshPlayerStats(boolean)}.
1057+ */
9341058 private void manualRefreshStatsCommand ()
9351059 {
9361060 refreshPlayerStats (true );
9371061 sendChatStatusMessage ("Refreshing player stats..." , true );
9381062 }
9391063
1064+ /**
1065+ * Shows or hides the player ID field in the plugin panel using {@link BotDetectorPanel#setPlayerIdVisible(boolean)}.
1066+ * @param args String arguments from {@link CommandExecuted#getArguments()}, requires 1 argument being either "0" or "1".
1067+ */
9401068 private void showHideIdCommand (String [] args )
9411069 {
9421070 String arg = args .length > 0 ? args [0 ] : "" ;
@@ -956,6 +1084,9 @@ private void showHideIdCommand(String[] args)
9561084 }
9571085 }
9581086
1087+ /**
1088+ * Gets the currently loaded {@link AuthToken} and copies it into the user's system clipboard.
1089+ */
9591090 private void putAuthTokenIntoClipboardCommand ()
9601091 {
9611092 if (authToken .getTokenType () == AuthTokenType .NONE )
@@ -970,6 +1101,10 @@ private void putAuthTokenIntoClipboardCommand()
9701101 }
9711102 }
9721103
1104+ /**
1105+ * Sets the {@link AuthToken} saved in {@link BotDetectorConfig#authFullToken()} to the contents of the clipboard,
1106+ * assuming the contents respect the defined token format in {@link AuthToken#AUTH_TOKEN_PATTERN}.
1107+ */
9731108 private void setAuthTokenFromClipboardCommand ()
9741109 {
9751110 final String clipboardText ;
@@ -1001,6 +1136,9 @@ private void setAuthTokenFromClipboardCommand()
10011136 }
10021137 }
10031138
1139+ /**
1140+ * Clears the current {@link AuthToken} saved in {@link BotDetectorConfig#authFullToken()}.
1141+ */
10041142 private void clearAuthTokenCommand ()
10051143 {
10061144 authToken = AuthToken .EMPTY_TOKEN ;
@@ -1010,7 +1148,10 @@ private void clearAuthTokenCommand()
10101148
10111149 //endregion
10121150
1013- // This isn't perfect but really shouldn't ever happen!
1151+
1152+ /**
1153+ * Displays an error message about being unable to parse a plugin version and links to the Bot Detector Discord.
1154+ */
10141155 private void displayPluginVersionError ()
10151156 {
10161157 JEditorPane ep = new JEditorPane ("text/html" ,
0 commit comments