-
Notifications
You must be signed in to change notification settings - Fork 3
Reflex API
This documentation is written for Reflex public API version 8
There are often situations when one may want to somehow interact with an application they haven't got access to the source code of through their own one. The best way to do that is to use the API (Application Programming Interface) of that desired application. Reflex has also got a comprehensive API to enable people to create comprehensive addons of any kind.
NOTE: this chapter requires you to have at least some basic Java and Bukkit programming knowledge. If you are just a server owner and looking for a way to interact with Reflex through custom plugins, refer this page to your developer(s) instead.
<repositories>
<repository>
<id>reflex.public</id>
<name>Public Reflex Repository</name>
<url>https://archiva.reflex.rip/repository/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>rip.reflex</groupId>
<artifactId>ReflexAPI</artifactId>
<version>8</version>
<scope>provided</scope>
</dependency>
</dependencies>
repositories {
maven {
name 'Public Reflex Repository'
url 'https://archiva.reflex.rip/repository/public/'
}
}
dependencies {
compileOnly group: 'rip.reflex', name: 'ReflexAPI', version: '8'
}
Everywhere in this chapter, we assume that:
Player is org.bukkit.entity.Player
String/Integer/... are from java.lang.[...]
All Reflex-related classes/enums/interfaces are from rip.reflex.api.[...]
The API of Reflex is not static. This means that code like this:
int ping = ReflexAPI.getPing(player);
will not compile. Instead, the API is bound to a one single instance that is created upon complete Reflex startup. This is necessary to prevent any plugins from attempting to interact with Reflex before it gets ready.
In order to get the main and the only working ReflexAPI
instance, you should listen for a ReflexLoadEvent
(a regular Bukkit event, handled just like any other events, for example, like PlayerJoinEvent
), and in it, assign your class field which you created somewhere before using ReflexAPIProvider#getAPI()
.
We hold our API version separate from plugin version. You can retrieve it in many ways, for example, from a public static field API_VERSION
in class ReflexAPI
. The API version is only changed when there are either a) backward-incompatible changes made, or b) massive changes (even backward compatible) or new features added. If you don't want to face unexpected and sometimes difficult to debug ("phantom") bugs/issues, we heavily recommend you to bind your plugin to a single Reflex API version, and shut down forcefully throwing a warning in case of API versions mismatch. That is, if you're creating your plugin for Reflex API version, say, 5
, you should make your plugin fail to start with an error (or disable Reflex hooks with a warning) if it finds out the server is running Reflex with API version, say, 6
. And when you find out your plugin is bound to an outdated API version (in case of an update), you should check the changelogs, find out what's changed in the new API version, and update your plugin.
An example of a good Reflex hook may look as follows:
public class ReflexListener implements Listener {
/**
* Our hook is designed to work with Reflex API version 5 only.
*
* TODO: change this field and make the appropriate changes to the code
* when a new API version comes out.
*/
private static final int RAPI_VERSION = 5;
private ReflexAPI reflexApi;
@EventHandler
public void reflexLoaded(ReflexLoadEvent e) {
if (e.getApiVersion() == RAPI_VERSION) {
// Now we can use the `reflexApi` field to interact with Reflex from our plugin.
reflexApi = ReflexAPIProvider.getApi();
MyReflexHook.getInstance().getLogger().info("Successfully " +
"hooked into Reflex API version " + RAPI_VERSION);
} else {
MyReflexHook.getInstance().getLogger().warn(String.format("Incompatible Reflex API! " +
"The plugin is designed to work with version %s, but the server " +
"is running Reflex with version %s", RAPI_VERSION, e.getApiVersion()));
Bukkit.getPluginManager().disablePlugin(MyReflexHook.getInstance());
}
}
}
Reflex fires several Bukkit events in different situations, for example, when a check is ran, so that all other plugins can keep track of its actions. Let's look closer at all of them.
extends Event
Called upon Reflex's complete startup (including NN data load and preparation finished and Cloud connection established, if enabled). Should be used by Reflex hooks to retrieve an instance of ReflexAPI
.
String getVersion()
- Returns Reflex plugin version.
int getApiVersion()
- Returns Reflex API version of the running Reflex JAR.
extends Event implements Cancellable
Called when any check runs, no matter the result. This means that the event is not only called when a player fails a check but when they pass one as well.
Player getPlayer()
- Returns the player checked.
Cheat getCheat()
- Returns the check ran.
String getComponent()
- Returns the exact name of the component (subcheck) of the check ran.
String getViolationId()
- Returns the ID of this check, even if it is passed (named "violation" for historical reasons). Format (RegEx): ^#[0-9A-Z]{8}$
, i.e. 8 randomly generated chars (either digits from 0 to 9 or uppercase latin letters) with a preceding #
symbol.
CheckResult getResult()
- Returns the result of this check.
void setCancelled(boolean cancel)
- Set whether this event should be cancelled or not. If true
, Reflex will revert player's number of violations on this check, abort any violation-related "things", will not print any verbose/logs related to this particular check and will not execute any commands or call command events if this is a violation. @see isCancelled().
boolean isCancelled()
- Returns whether this event should be cancelled. @see setCancelled(...).
extends Event implements Cancellable
Called when a player fails a check and reaches one of the numbers of violations listed in this check's actions
configuration, or, simply, when Reflex is about to execute a command against a possible cheater. A ReflexCheckEvent
with same check ID is always guaranteed to be called before this method. If the check event corresponding to this command event was cancelled, then this event will not be called.
Player getPlayer()
- Returns the player command is executed against.
Cheat getCheat()
- Returns the check failed.
String getComponent()
- Returns the exact name of the component (subcheck) of the check failed.
String getViolationId()
- Returns the ID of this check, same as in the corresponding preceding ReflexCheckEvent. Format (RegEx): ^#[0-9A-Z]{8}$
, i.e. 8 randomly generated chars (either digits from 0 to 9 or uppercase latin letters) with a preceding #
symbol.
String getCommand()
- Returns the command from this check's actions
configuration that Reflex wanted to execute against this player. The command is always guaranteed to correspond to a violation level less than or equal to the violation level of this player on this check after this violation.
void setCancelled(boolean cancel)
- Set whether this event should be cancelled or not. If true
, Reflex will not send this command. The violation itself will not be affected.
boolean isCancelled()
- Returns whether this event should be cancelled. @see setCancelled(...).
extends Event
This event is called when (1) Reflex finishes gathering a player's combat behavior model and wants to save/share it after passing it to cloud or neural network for analysis and (2) if the killaura.debug_heuristics
option is set to either 1
(API only), 2
(all), 3
(legit only) or 4
(cheats only).
The event could be used as a "smarter" replacement for Reflex's built-in heuristics debugging mechanism to enable, for example, specific player whitelists and more.
Player getPlayer()
- Returns the player whose combat behavior model this event cares.
boolean isLegit()
- Returns whether this combat behavior model was classified as legitimate or not.
String getSnapshotID()
- Returns the ID of this heuristic check. The ID is same as the one shown in verbose messages related to this particular analysis. There is no strictly defined format for snapshot IDs - we may change it any time without prior notice.
byte[] getBody()
- The recorded and analyzed combat behavior model presented as a byte array of undefined length, i.e. different events may produce bodies of different size, there is no constant array length. Can be saved to a file or transferred through a network, for example.
Objects of this type are returned by the getResult()
method of ReflexCheckEvent
. Check results can be used to construct verbose messages, retrieve the number of violations added or subtracted and more.
(other methods present in the class but not documented here take no effect for external plugins and should not be used)
int getViolationsMod()
- Returns the number of violations that will be added to the player after check. If the value is negative (e.g. -3
), then this means that player's violations number will be reduced (the check is guaranteed to be passed in that case).
List<String> getTags()
- Returns the list of all tags assigned to this particular check by all components. This does not include server TPS, player ping, player lag status, and player lag points number. Example: ["hitbox (period)", "recent suspicions: 9"]
.
boolean hasTag(String tag)
- Returns true one of the check tags equals to the specified string, false otherwise. @see getTags().
boolean isCheckFailed()
- Returns whether this check is failed or not. A check is considered failed if the violationsMod number is greater than zero, i.e. there are some violations added. @see isCheckPassed().
boolean isCheckPassed()
- The opposite to isCheckFailed()
, returns whether this check is passed or not. A check is considered passed if the violationsMod number is less than or equal to zero, i.e. the number of player's violations is either reduced or kept as is. @see isCheckFailed().
boolean shouldCancel()
- Returns whether this check should cancel the player's action it corresponds to, for example, if a player is hitting too far and their hits should be cancelled (or they should be given an attack penalty for a period of time). Guaranteed to return false
if the check's silent
configuration option is set to true
, i.e. the check should not cancel any suspicious actions.
The enum Cheat
contains all checks Reflex has now or may have in the future. It is important to note that some of the fields in this enum correspond to are only planned, currently unreleased checks and should not be touched (for example, Cheat.Speed
). Such checks may be removed at any time.
The enum does not list checks' components (subchecks), but the core checks, for example, Cheat.KillAura
. It can be used, for example, to toggle certain checks wholly without any configuration changes, right at runtime.
The Cheat
enum also provides a (maybe) useful method:
String correctName(String cheat)
,
which transforms the given check name String to make it match the name in of its Cheat
enum. For example, correctName("killaura")
→ "KillAura"
. A NoSuchElementException
is thrown in case there is no such check with the name entered, for example, when calling correctName("sjkgasjsadfa")
.
P.S.: we are aware that enums should be named LIKE_THIS
and not LikeThis
, but there were some specific reasons for us to choose our current naming in the past, and since then we just kept it as is.
Please follow the instructions listed under the "Getting started" section in order to get an instance of ReflexAPI
and feel free to proceed after done.
NOTE: all methods that return ReflexAPI
return the Reflex API instance itself back (convenience self-returning methods).
String memoryDump()
- Returns a Reflex memory dump string similar to ones created with the /reflex dump
command.
long[] getMemory()
- Returns current server memory usage. It is a long
array with a guaranteed length of 3
: the maximum amount of memory that the Java virtual machine will attempt to use at index 0
, the total amount of memory in the Java virtual machine at index 1
, and the amount of free memory in the Java Virtual Machine at index 2
. All values are measured in bytes.
long getMemoryUsedEstimate()
- Returns the estimated number of bytes Reflex is using in some of its most heavy/unsafe places. Calculated by serializing all the objects in memory and getting their post-serialization byte array lengths. This method itself does not calculate anything. Instead, it returns the last value calculated with the /reflex dump
command. If the command was never used, this method will return 0
.
ReflexAPI allowHacking(Player p, Cheat cheat, boolean allow)
- Allow (if allow=true
) or disallow (if allow=false
) the specified player to bypass the given check. A player is considered formally "bypassing" a check if they are fully ignored by the check. @see canBypass(...).
boolean canBypass(Player p, Cheat cheat)
- Returns whether the specified player is allowed to bypass the specified check formally. @see allowHacking(...).
ReflexAPI transferViolations(Player from, Player to)
- Transfer Reflex violation level numbers from player from
to player to
.
ReflexAPI transferHeuristicsData(Player from, Player to)
- Transfer the currently recorded combat behavior data (NN model) from player from
to player to
.
ReflexAPI transfer(Player from, Player to)
- Transfer violations and heuristics data from player from
to player to
. This is a convenience method that simply combines several other transfer methods. @see transferViolations(...) and transferHeuristicsData(...).
ReflexPlayerData getPlayerData(Player p)
- Retrieve a data object containing basically all the data that could be safely transferred as bytes, including violations and heuristics data. This could be very handy for very short minigames where Reflex is unable to record enough data for checking. For example, a player joins SkyWars Game #1. Spends a few seconds in combat in total, then dies. Not enough data collected. But here, you could save this data somewhere for further use (by serializing the ReflexPlayerData
object and saving it to, say, a database). Then that player joins SkyWars Game #2. You get the previously saved data, deserialize it back to a ReflexPlayerData
object and assign it to the player. Then they go PvP again for a few seconds, which's basically Old Data + New Data = Enough Data
. A player is then greatly and fully checked, just like it should be in longer game modes. @see setPlayerData(...).
ReflexAPI setPlayerData(Player p, ReflexPlayerData data)
- Assign the specified Reflex player data object to the specified player. @see getPlayerData(...).
double getTps()
- Returns server's TPS according to Reflex. WARNING: Reflex's TPS meter is currently disabled for an unknown period of time. This method always returns 20.0
.
int getPing(Player player)
- Returns ping in milliseconds of the specified player (computed by client, and therefore can be spoofed, for example, with PingSpoof cheats). Refer to lag points and lag status information provided by other Reflex API methods if you want to create some custom lag accounting instead. @see getLagPoints(...) and getLagStatus(...).
String getMCBrand(Player player)
- Returns the MC|Brand
parameter value of the specified player. NOTE: the client brand parameter is set by client, and can easily be spoofed. Usually, however, it is something like "FML"
or "Forge"
for Forge clients and "Vanilla"
for everything else (including hacked clients).
String getClientLocale(Player player)
- Returns Minecraft client locale of the specified player (for example, en_gb
, ru_ru
, ja_jp
, etc.). | See https://minecraft.gamepedia.com/Language for details.
int getProtocolVersion(Player player)
- Returns first base release (e.g. 1.8x, 1.9x even for 1.8.8 or 1.9/1.9.1/1.9.2...) Minecraft client protocol version of the specified player. Reflex will ask ViaVersion or ProtocolSupport about that if either is installed or will return the protocol version of your server otherwise. Again, this method does not care about subversions. No matter if a player joins with 1.12, 1.12.1 or 1.12.2, this method will return the protocol version for version 1.12. Do not use this method if you want exact protocol version numbers. | See https://wiki.vg/Protocol_version_numbers for details.
int getLagPoints(Player player)
- Returns the number of lag points computed by Reflex for the specified player. @see getLagStatus(...). | See https://github.com/MeGysssTaa/ReflexIssueTracker/wiki/Lag-Accounting for details.
String getLagStatus(Player player)
- Returns the lag status name (e.g. "MINOR_LAG"
) computed by Reflex for the specified player. @see getLagPoints(...). | See https://github.com/MeGysssTaa/ReflexIssueTracker/wiki/Lag-Accounting for details.
int getViolations(Player player, Cheat cheat)
- Returns the number of violations of the specified player on the specified check at the moment. Guaranteed to be greater than or equal to zero. @see setViolations(...).
ReflexAPI setViolations(Player player, Cheat cheat, int violations)
- Set the number of violations of the specified player on the specified check. Negative violations
values are treated as zero. @see getViolations(...).
BanInfo getBanInfo(Player player)
- Returns ban ("/reflex ban") information of the specified player as a BanInfo
object.
ReflexAPI unbanPlayer(Player player)
- Unbans the specified player previously banned using Reflex ("/reflex ban").
ReflexAPI banPlayer(Player player, String message, long banTime, TimeUnit timeUnit)
- Ban the specified player using Reflex (like "/reflex ban") with the specified ban message (aka ban reason) for the specified period of time. banTime
is measured in the specified timeUnit
unit. message
may contain color codes and newline characters. Color codes like &3
are not guaranteed to be translated, use §3
or ChatColor#translateAlternateColorCodes(...)
for that.
boolean isPlayerBanned(Player player)
- Returns whether the specified player is banned using Reflex (e.g. with "/reflex ban").
int getBanCount()
- Returns the number of players banned using Reflex (e.g. with "/reflex ban") in total.
int getBanCountIn(TimeUnit unit, long time)
- Returns the number of players banned using Reflex (e.g. with "/reflex ban") recently. "Recently" is defined by the specified max time offset. time
is measured in unit
unit. For example, getBanCountIn(TimeUnit.DAYS, 7)
returns the number of players banned last week.
BanImpl getBanImpl()
- Returns the instance of BanImpl
currently used by Reflex.
DatabaseClient getDBClient()
- Returns the instance of DatabaseClient
currently used by Reflex.
DailyLogFilePublisher getLogImpl()
- Returns the instance of DailyLogFilePublisher
currently used by Reflex.
DataImpl getDataImpl()
- Returns the instance of DataImpl
currently used by Reflex.
boolean isCheckEnabled(Cheat c)
- Returns whether the specified check is enabled or not. May throw a RuntimeException
if the specified check is not loaded (for example, the if it was initially disabled in config or if an invalid Cheat
enum was passed).
ReflexAPI enableCheck(Cheat c)
- Enables the specified check if it is loaded but disabled. May throw a RuntimeException
if the specified check is not loaded (for example, the if it was initially disabled in config or if an invalid Cheat
enum was passed).
ReflexAPI disableCheck(Cheat c)
- Disables the specified check if it is loaded and enabled. May throw a RuntimeException
if the specified check is not loaded (for example, the if it was initially disabled in config or if an invalid Cheat
enum was passed).
ReflexAPI toggleCheck(Cheat c)
- Enables the specified check if it is loaded but disabled or disables it if it is loaded and enabled. May throw a RuntimeException
if the specified check is not loaded (for example, the if it was initially disabled in config or if an invalid Cheat
enum was passed).
ReflexAPI enableComponent(String name)
- Enables the specified component. Takes no effect if the core check of the component is disabled. | See https://github.com/MeGysssTaa/ReflexIssueTracker/wiki/Checks'-Components for details.
ReflexAPI disableComponent(String name)
- Disables the specified component. Takes no effect if the core check of the component is disabled. | See https://github.com/MeGysssTaa/ReflexIssueTracker/wiki/Checks'-Components for details.
boolean isComponentEnabled(String name)
- Returns whether the specified component is enabled or not. May return true
even if the core check of the component is disabled, as it works by basically doing return !isComponentDisabled(name)
. @see isComponentDisabled(...). | See https://github.com/MeGysssTaa/ReflexIssueTracker/wiki/Checks'-Components for details.
boolean isComponentDisabled(String name)
- Returns whether the specified component is disabled or not. true
is returned if and only if the specified component name is either in the check's disabled_checks
list or has been disabled using disableComponent(name)
. Even if the core check of the component (e.g. AntiKnockback) is wholly disabled, this method may return false
if the specified component itself is not disabled explicitly. @see isComponentEnabled(...). | See https://github.com/MeGysssTaa/ReflexIssueTracker/wiki/Checks'-Components for details.
ReflexAPI reset(Player p)
- Perform hard and complete Reflex data reset of the specified player. Reflex will treat the player as "just joined" after that. Same as running "/reflex reset".
ReflexAPI reloadConfig()
- Reload Reflex config.yml and advcfg.yml, update themes (if necessary) and do some other reload magic. Same as running "/reflex reload".
ReflexAPI reloadPermissionCaches()
- Clear all Reflex player permissions caches.
ReflexAPI reloadTheme()
- Update themes (if neccessary) and reload the current one.
ReflexAPI reloadAll()
- Reload everything (or nearly everything) possible, including configurations, themes, and caches.
String translateThemeString(String key)
- Returns the appropriate translation from the current theme of the specified key, if present, or the key itself otherwise. For example, having the ModernBlue_JA
(Japanese default) theme active and calling translateThemeString("noPermissions")
will return "あなたは弱すぎます!もっと力がいります"
. Calling translateThemeString("Some Invalid Key")
will return "Some Invalid Key"
.
String translateAndFormat(String key)
- Translates the specified key using the current theme, performs some sort of formatting (like colors and newlines) and adds Reflex's prefix (also from the current theme) to it, then returns the result. @see translateThemeString(...).
boolean isConnectedToCloud()
- Returns whether Reflex is connected to Reflex Cloud at the moment.
ReflexAPI connectToCloud(String key)
- Tells Reflex to establish a new Cloud connection and use the specified key for authorization. WARNING: you must check for and close any active connection(s) first. Otherwise, the behavior of your call will be undefined. @see isConnectedToCloud().
int getPingToCloud()
- Returns ping to Reflex Cloud at the moment (one direction). The number basically shows how long does it take in milliseconds for Cloud to deliver your server a 4-byte number. Measured as (pingResponseTime - pingRequestTime) / 2
. If Cloud connections have never been established yet, then this method always returns zero. Otherwise, it returns the last ping packet latency even if connection was lost. @see isConnectedToCloud().
String reflexVersion()
- Returns Reflex plugin version (from its JAR's plugin.yml
).
int nnVersion()
- Returns the neural network model file version Reflex is currently running with. NOTE: Reflex uses no NN models locally if Cloud is active. That is, this method's behavior is undefined if a Reflex Cloud connection is established.
int apiVersion()
- Returns the version of Reflex API the currently installed Reflex plugin JAR cares. Same as accessing public static field ReflexAPI.API_VERSION
.
int cloudProtocolVersion()
- Returns Reflex Cloud protocol version the currently installed Reflex plugin JAR cares.
String getReflexPrefix()
- Returns Reflex's prefix from the currently active theme.
Objects of type BanInfo
care information of/for Reflex bans (e.g. "/reflex ban" or ReflexAPI bans).
String getMessage()
- Returns the string that is displayed to a player as kick (login denial) reason/message if they are banned using Reflex.
long getBanTime()
- For how long is the player banned (in milliseconds). Special cases: -1
indicates that the player is banned permanently; 0
indicates that the player is not banned.
Objects of type ReflexPlayerData
care basically all Reflex player data that can be safely serialized and transferred from player to player, saved to a file or a database or sent through a network. While you cannot do anything with these objects, Reflex can. You should only use them for copying/saving/transferring players' data, for example, from server to server (@see getReflexData(...) above in section "ReflexAPI").
Although there is already plenty of questions on StackOverflow and other places and tutorials about data serialization, we still decided to leave a simple utility for that here to save you some time if you are new to data manipulation in Java.
NOTE: this util does not perform any sort of compression. Google a bit if you want one.
public static byte[] ser(Object obj) {
if (obj == null)
return new byte[0];
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
byte[] bytes = baos.toByteArray();
baos.close();
return bytes;
} catch (Exception ex) {
throw new RuntimeException("failed to serialize " + obj.getClass().getName(), ex);
}
}
public static Object deser(byte[] bytes) {
if ((bytes == null) || (bytes.length == 0))
return null;
try {
ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bain);
Object obj = ois.readObject();
bain.close();
ois.close();
return obj;
} catch (Exception ex) {
throw new RuntimeException("failed to deserialize " + bytes.length + " bytes", ex);
}
}
Then you can do something like this (example data transfer between two (for example, SkyWars) servers):
/**
* WARNING: we do not pretend this to be a perfect or even a good example.
* It is just a WAY OF using ReflexPlayerData.
* We recommend storing the data in a database instead, and moreover,
* we also recommend you compressing it.
*/
// ... SERVER ONE ...
// (for example, before player quit)
ReflexPlayerData data = reflexApi.getPlayerData(player);
byte[] bytes = ser(data);
File dataFile = new File("reflex-data/" + player.getName() + ".dat");
if (dataFile.exists())
dataFile.delete();
Files.write(dataFile.toPath(), bytes);
System.out.println("Saved ReflexPlayerData of " + player.getName());
// ... SERVER TWO ...
// (for example, on player join)
File dataFile = new File("reflex-data/" + player.getName() + ".dat");
if (dataFile.exists()) {
byte[] bytes = Files.readAllBytes(dataFile.toPath());
ReflexPlayerData data = (ReflexPlayerData) deser(bytes);
reflexApi.setPlayerData(player, data);
System.out.println("Loaded ReflexPlayerData of " + player.getName());
dataFile.delete();
}