API Version 13
The CoreProtect API enables plugins to log changes, look up recorded activity, and perform rollbacks and restores.
| API Details | |
|---|---|
| API Version | 13 |
| Plugin Version | v25.0+ |
| Maven | maven.playpro.com |
Upgrading from API v12
All API v12 methods remain available. API v13 adds the following:
| Addition | Description |
|---|---|
blockLookup(LookupOptions) |
Searches block breaks, placements, and interactions at an exact location, within a radius, in a world, or across all worlds. |
entityLookup(LookupOptions) and EntityResult |
Searches entity spawns and kills, with action and entity-type filters. |
LookupOptions.world(World) |
Selects a whole world for spatial typed lookups. |
| User and material lists | Adds users, excludeUsers, includeMaterials, and excludeMaterials to typed lookup options. |
| Typed action filters | Adds blockActions, entityActions, containerActions, itemActions, inventoryActions, and sessionActions. |
| Text prefix filters | Adds textStartsWithAny and textStartsWithNone for chat, command, and sign lookups. |
| Entity-spawn action | Adds CoreProtectAction.ENTITY_SPAWN with ID 13. Legacy lookup, rollback, and restore methods accept this action explicitly. |
| Entity result parsing | BlockResult and ParseResult expose getEntityType() for entity kills and spawns. Their material and block-data getters return null for entity rows. |
| Tracked containers | Container and inventory lookups include transactions in tracked boats and minecarts. |
| Pre-log events | Adds CoreProtectPreLogEvent.Action.ENTITY_SPAWN and ENTITY_INTERACTION. |
The existing blockLookup(Block, LookupOptions) overload retains entity events for compatibility. The new blockLookup(LookupOptions) overload returns only block events; use entityLookup for entities.
Entity and tracked-container searches can match persisted current/final positions as well as original event positions. See entity lookups and container lookups for the coordinates returned by each method.
Getting Started
CoreProtect 25.0 or higher is required to use API v13.
Add the dependency
Compile against a CoreProtect build that provides API v13. You can add the CoreProtect jar to your IDE or use the Maven repository:
<repositories>
<repository>
<id>playpro</id>
<url>https://maven.playpro.com</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.coreprotect</groupId>
<artifactId>CoreProtect</artifactId>
<version>25.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Use the published version matching the CoreProtect build you target. For prerelease builds, use that build's published version instead of 25.0. CoreProtect runs as a separate server plugin; do not package its classes inside your plugin.
Add CoreProtect to your plugin's plugin.yml so it loads first:
depend: [CoreProtect]
Use softdepend: [CoreProtect] instead if your plugin also works without CoreProtect, and only initialize the integration when CoreProtect is present.
Obtain the API
The following helper belongs in your JavaPlugin class:
import org.bukkit.plugin.Plugin;
import net.coreprotect.CoreProtect;
import net.coreprotect.CoreProtectAPI;
private CoreProtectAPI getCoreProtect() {
Plugin plugin = getServer().getPluginManager().getPlugin("CoreProtect");
if (!(plugin instanceof CoreProtect)) {
return null;
}
CoreProtectAPI api = ((CoreProtect) plugin).getAPI();
if (!api.isEnabled() || api.APIVersion() < 13) {
return null;
}
return api;
}
Call it during your plugin's initialization:
CoreProtectAPI api = getCoreProtect();
if (api != null) {
api.testAPI();
}
Options and action enums are in net.coreprotect.api. Typed result classes are in net.coreprotect.api.result. Events are in net.coreprotect.event.
Threading and return values
Database lookups run synchronously on the calling thread. Run database searches asynchronously to avoid blocking the server thread. Both performRollback and performRestore must be called asynchronously; CoreProtect schedules the required world updates internally.
Capture locations and any required Bukkit state on the appropriate server thread before starting database work. Logging methods that inspect live blocks, players, or inventories should run on that server thread. A BlockState overload does not make every logging operation safe to call asynchronously.
Typed lookups and the legacy blockLookup, performLookup, performPartialLookup, performRollback, and performRestore methods return null when the API is disabled. queueLookup and the legacy sessionLookup(String, int) instead return an empty list. Legacy searches can also return null for invalid search parameters. An empty list can mean no matching records or an operation that could not proceed; it is not a separate success status. Logging methods return a boolean indicating whether the request was accepted, not whether it has already been written to the database.
API rollback and restore methods perform no changes when database maintenance or another conflicting operation prevents them from starting. This includes an active database purge.
API Overview
Available methods
The table below lists the supported methods on CoreProtectAPI. Click a method to jump to its parameters and behavior.
| Method | Return type | Purpose |
|---|---|---|
APIVersion() |
int |
Returns 13 for this API version. |
isEnabled() |
boolean |
Checks whether the API is enabled in CoreProtect's configuration. |
testAPI() |
void |
Prints an API test message to the console. |
blockLookup(Block, int) |
List<String[]> |
Legacy lookup at a block. |
blockLookup(Block, LookupOptions) |
List<BlockResult> |
Typed lookup at a block, retaining legacy entity events. |
blockLookup(LookupOptions) |
List<BlockResult> |
Typed block-only lookup. |
entityLookup(LookupOptions) |
List<EntityResult> |
Entity spawn and kill history. |
containerLookup(Location, int) |
List<ContainerResult> |
Container history at a location. |
containerLookup(LookupOptions) |
List<ContainerResult> |
Container history with shared filters. |
itemLookup(LookupOptions) |
List<ItemResult> |
World item transactions. |
inventoryLookup(LookupOptions) |
List<InventoryResult> |
Transactions affecting player inventories. |
chatLookup(String, int) / chatLookup(LookupOptions) |
List<MessageResult> |
Chat history. |
commandLookup(String, int) / commandLookup(LookupOptions) |
List<MessageResult> |
Command history. |
signLookup(Location, int) / signLookup(LookupOptions) |
List<SignResult> |
Written sign text. |
sessionLookup(String, int) |
List<String[]> |
Legacy login/logout history. |
sessionLookup(LookupOptions) |
List<SessionResult> |
Typed login/logout history. |
usernameLookup(String, int) / usernameLookup(LookupOptions) |
List<UsernameResult> |
Username history. |
queueLookup(Block) |
List<String[]> |
Pending block changes in the consumer queue. |
parseResult(String[]) |
CoreProtectAPI.ParseResult |
Parses a legacy result row. |
performLookup(...) / performPartialLookup(...) |
List<String[]> |
Legacy block/entity searches, with optional pagination. |
performRollback(...) / performRestore(...) |
List<String[]> |
Rolls back or restores matching changes. |
hasPlaced(String, Block, int, int) / hasRemoved(String, Block, int, int) |
boolean |
Checks a player's persisted block history. |
logChat(Player, String) / logCommand(Player, String) |
boolean |
Queues player messages or commands. |
logPlacement(...) / logRemoval(...) |
boolean |
Queues block changes. |
logContainerTransaction(String, Location) |
boolean |
Captures a block inventory before your changes. |
logInteraction(String, Location) |
boolean |
Queues a block interaction. |
performPurge(int) |
void |
Dispatches a database purge command. |
Shared typed lookup options
Create options with LookupOptions.builder() and finish with build():
LookupOptions options = LookupOptions.builder()
.user("Notch")
.time(3600)
.radius(location, 5)
.limit(0, 100)
.build();
| Builder method | Behavior |
|---|---|
user(String user) |
Restricts the search to one user. null, an empty string, or #global imposes no single-user restriction. |
users(List<String> users) |
Includes any matching user in the list. Combines with user as an additional restriction. |
excludeUsers(List<String> users) |
Excludes matching users, including users selected by either inclusion option. |
time(int seconds) |
Searches back this many seconds. Omitted, zero, or negative values impose no time restriction for typed lookups. |
location(Location location) |
Searches the exact block coordinates and world. |
radius(Location location, int radius) |
A positive radius searches an inclusive X/Z square around the location, at all heights. Zero or a negative radius searches the exact block coordinates. |
world(World world) |
Searches the entire selected world. |
limit(int offset, int count) |
Skips offset matching rows and returns at most count. The offset is zero-based. Both values must be nonnegative for the limit to apply; a count of zero returns no rows. |
includeMaterials(List<Material> materials) |
Includes any matching logged block/item material in supported lookup types. |
excludeMaterials(List<Material> materials) |
Excludes matching logged block/item materials. |
includeEntities(List<EntityType> entities) |
Includes any matching entity type in entityLookup. |
excludeEntities(List<EntityType> entities) |
Excludes matching entity types in entityLookup. |
blockActions(List<BlockAction> actions) |
Selects block actions. |
entityActions(List<EntityAction> actions) |
Selects entity actions. |
containerActions(List<ContainerAction> actions) |
Selects container transaction actions. |
itemActions(List<ItemAction> actions) |
Selects world item transaction actions. |
inventoryActions(List<InventoryAction> actions) |
Selects source-specific inventory transactions. |
sessionActions(List<SessionAction> actions) |
Selects login/logout actions. |
textStartsWithAny(List<String> prefixes) |
Requires text to begin with at least one supplied prefix. |
textStartsWithNone(List<String> prefixes) |
Rejects text beginning with any supplied prefix. |
The last call to world, location, or radius determines the search area. With no area specified, spatial typed lookups search all worlds. The blockLookup(Block, LookupOptions) overload always uses the supplied block's location instead.
All filters that apply to a lookup are combined. Empty filter lists retain the default behavior. List setters copy their inputs and reject null lists or null entries. A built options object's lists cannot be modified. Passing null instead of a LookupOptions object uses the defaults.
Within users, an empty string or #global removes that list's user restriction. Within excludeUsers, either value excludes everyone. Use an empty list when you want no exclusion.
Results are returned in descending event order. Limits apply after filtering; omitting a limit permits all matching rows. Use a bounded time range and pagination for large histories. Offset pagination does not hold a snapshot across separate API calls, so new records can shift later pages.
| Lookup | User/time/limit | Spatial filters | Material filters | Additional filters |
|---|---|---|---|---|
| Block | Yes | Yes; supplied Block overrides options |
Yes | blockActions |
| Entity | Yes | Yes | No | entityActions, includeEntities, excludeEntities |
| Container | Yes | Yes | Yes | containerActions |
| Item | Yes | Yes | Yes | itemActions |
| Inventory | Yes | Yes | Yes | inventoryActions |
| Chat / command | Yes | Yes | No | Text prefixes |
| Sign | Yes | Yes | No | Text prefixes |
| Session | Yes | Yes | No | sessionActions |
| Username | Yes | No | No | None |
Filters outside a lookup's scope are ignored. Username history has no location, radius, or world.
Text prefix filters
textStartsWithAny matches any included prefix. textStartsWithNone rejects any excluded prefix, so exclusions take precedence. Either option can be used alone.
The API uses the same SQL matching and escaping as the in-game f: / filter: option, including the database's case-handling rules. Each API list entry is already one literal prefix: commas, leading -, and leading/trailing spaces are preserved. %, _, ~, and * are not wildcards. The API does not parse command-style comma separators or exclusion markers.
Prefixes must contain at least three Unicode code points. Shorter prefixes throw IllegalArgumentException when the builder method is called. Empty lists impose no restriction.
Sign filters check each line on the logged face. An inclusion match on any line includes the entry; an exclusion match on any line excludes the whole entry. Text on the opposite face does not affect the match.
Action filters
Use the enum associated with the lookup. Multiple values match any listed action; an omitted or empty list retains that lookup's defaults.
| Lookup | Enum values and IDs | Default selection |
|---|---|---|
| Block | BlockAction.BREAK = 0, PLACE = 1, INTERACTION = 2 |
All three for blockLookup(options); the supplied-block overload also retains entity events unless narrowed. |
| Entity | EntityAction.KILL = 3, SPAWN = 13 |
Both. |
| Container | ContainerAction.REMOVE = 0, ADD = 1 |
Both. |
| Session | SessionAction.LOGOUT = 0, LOGIN = 1 |
Both. |
| Item | See below. | DROP, PICKUP, REMOVE_ENDER, ADD_ENDER, THROW, SHOOT. |
| Inventory | See below. | Block placements, container transactions, and all supported item transactions. |
ItemAction |
ID | Result action string |
|---|---|---|
DROP |
2 | drop |
PICKUP |
3 | pickup |
REMOVE_ENDER |
4 | withdraw |
ADD_ENDER |
5 | deposit |
THROW |
6 | throw |
SHOOT |
7 | shoot |
BREAK |
8 | break |
DESTROY |
9 | destroy |
CREATE |
10 | create |
SELL |
11 | sell |
BUY |
12 | buy |
BREAK, DESTROY, CREATE, SELL, and BUY require explicit selection in itemLookup. They are included by default in inventoryLookup.
InventoryAction selects both a source and its underlying action:
| Source | InventoryAction values |
|---|---|
| Block | BLOCK_PLACE |
| Container | CONTAINER_REMOVE, CONTAINER_ADD |
| Item | ITEM_DROP, ITEM_PICKUP, ITEM_REMOVE_ENDER, ITEM_ADD_ENDER, ITEM_THROW, ITEM_SHOOT, ITEM_BREAK, ITEM_DESTROY, ITEM_CREATE, ITEM_SELL, ITEM_BUY |
Each action enum exposes id(). InventoryAction also exposes sourceId(). Container remove/add describes the container's contents: taking an item out of a container adds it to the player's inventory, and depositing an item removes it from the player's inventory.
CoreProtectAction
CoreProtectAction names the general lookup categories. It exposes id(), actionString(), and fromId(int); an unrecognized ID maps to UNKNOWN.
| Constant | ID | Action string |
|---|---|---|
BLOCK_BREAK |
0 | break |
BLOCK_PLACE |
1 | place |
INTERACTION |
2 | click |
ENTITY_KILL |
3 | kill |
CONTAINER |
4 | container |
CHAT |
6 | chat |
COMMAND |
7 | command |
SESSION |
8 | session |
USERNAME |
9 | username |
SIGN |
10 | sign |
ITEM |
11 | item |
ENTITY_SPAWN |
13 | spawn |
UNKNOWN |
-1 | unknown |
The legacy performLookup, performPartialLookup, performRollback, and performRestore methods accept only block/entity action IDs 0, 1, 2, 3, and 13. Use the dedicated typed methods for the other categories.
A result's getActionId() uses that result type's action values. For example, SessionResult uses 0/1 for logout/login, and InventoryResult uses 0/1 for removal/addition to the player's inventory. Do not interpret every result ID through CoreProtectAction.fromId().
API Status Methods
APIVersion()
int APIVersion() returns the API version supported by the installed CoreProtect plugin. This version returns 13. Check for 13 or higher before using API v13 methods; the API version is separate from the plugin's release version, such as 25.0.
isEnabled()
boolean isEnabled() returns true when the server has the CoreProtect API enabled in its configuration, and false when it is disabled. Check this before using the API.
testAPI()
void testAPI() prints an API test message to the server console. With the default English language configuration, the message is:
[CoreProtect] API test successful.
The message follows CoreProtect's language configuration. This method returns no value and does not test database reads or writes; use it to confirm that your plugin can call the API.
Typed Lookup Methods
For convenience overloads taking int time, the argument is how many seconds to search back. As with LookupOptions.time, zero or negative values impose no time restriction.
Block lookups
| Signature | Returns |
|---|---|
blockLookup(LookupOptions options) |
List<BlockResult> |
blockLookup(Block block, LookupOptions options) |
List<BlockResult> |
The options-only method searches logged block breaks, placements, and interactions. It supports exact locations, X/Z radius searches, whole-world searches, and searches across all worlds.
The supplied-block method always searches that block's coordinates and world. Location, radius, and world options are ignored. It retains entity kills and spawns stored at those coordinates unless an explicit blockActions filter narrows the search.
Material filters compare the logged material, regardless of the block currently at the location. Including materials selects matching block events. Excluding materials preserves entity events in the supplied-block overload. Use getEntityType() to read entity rows; getType() and getBlockData() return null for them.
Entity lookups
| Signature | Returns |
|---|---|
entityLookup(LookupOptions options) |
List<EntityResult> |
Returns entity spawns and kills. Use entityActions to select SPAWN or KILL, and includeEntities / excludeEntities to filter Bukkit EntityType values. Empty lists select both actions and all entity types.
For spawn events, location/radius filters match either the original spawn location or the persisted current/final location. Whole-world filters similarly match either world. Kill events match the location where the kill was logged. Results always expose the original event coordinates and world, even when a tracked position matched the filter.
These synchronous lookups use persisted tracking data and do not wait for a live-entity scan. Material filters and other lookup-specific action filters do not affect entity lookups.
The in-game a:block filters present placed and destroyed boats/minecarts as block changes. That command/display alias does not change API action IDs: their spawn and kill rows remain ENTITY_SPAWN (13) and ENTITY_KILL (3).
Container lookups
| Signature | Returns |
|---|---|
containerLookup(Location location, int time) |
List<ContainerResult> |
containerLookup(LookupOptions options) |
List<ContainerResult> |
Searches item additions to and removals from containers. The location/time overload searches an exact location. The options overload also supports radius, world, user, material, action, and pagination filters.
Both methods include tracked boat and minecart container transactions. Their location filters match either the transaction's original location or the entity's persisted current/final location. Typed results for these tracked containers expose the persisted current/final coordinates and world. Ordinary block containers expose the transaction location.
Tracked positions are checkpointed when transactions are logged and during relevant entity lifecycle changes. Lookups do not wait for a live-entity scan. containerActions applies to both block containers and tracked entity containers.
Item lookups
| Signature | Returns |
|---|---|
itemLookup(LookupOptions options) |
List<ItemResult> |
Searches world item transactions, including drops, pickups, ender-chest transfers, throws, and shots by default. Use itemActions to select specific actions, including item breaks, destruction, creation, sales, and purchases. Those last five categories are not included in the default item lookup.
Inventory lookups
| Signature | Returns |
|---|---|
inventoryLookup(LookupOptions options) |
List<InventoryResult> |
Searches transactions affecting player inventories. The default combines block placements, container transactions, and all supported world item transactions. Use inventoryActions to select source-specific events.
Results normalize the action to removal from (0) or addition to (1) the player's inventory. Use getTransactionActionId() and getSourceId() together to identify the underlying event. For example, a container removal is an inventory addition.
Tracked boat and minecart transactions follow the location rules described under container lookups and remain part of the public container source.
Chat and command lookups
| Signature | Returns |
|---|---|
chatLookup(String user, int time) |
List<MessageResult> |
chatLookup(LookupOptions options) |
List<MessageResult> |
commandLookup(String user, int time) |
List<MessageResult> |
commandLookup(LookupOptions options) |
List<MessageResult> |
Searches logged chat or commands. The user/time overloads accept null or #global for all users. The options overloads also support spatial filters, user lists, pagination, and text prefix filters.
Commands include their leading /. MessageResult.getActionId() is 6 for chat and 7 for commands.
Sign lookups
| Signature | Returns |
|---|---|
signLookup(Location location, int time) |
List<SignResult> |
signLookup(LookupOptions options) |
List<SignResult> |
Searches logged written sign text. Returned rows use the placed/written action (1) and contain nonempty text; this method is not a listing of sign breaks or all sign state changes.
The location/time overload searches an exact location. The options overload supports user, spatial, text-prefix, and pagination filters. Results contain both sides' saved lines and identify which face was logged.
Session lookups
| Signature | Returns |
|---|---|
sessionLookup(LookupOptions options) |
List<SessionResult> |
Searches player logins and logouts. Both are included by default. Use sessionActions to select LOGIN or LOGOUT, and shared options for user, time, spatial, and pagination filters.
The older sessionLookup(String user, int time) overload returns raw String[] rows; see legacy block, session, and queue lookups.
Username lookups
| Signature | Returns |
|---|---|
usernameLookup(String user, int time) |
List<UsernameResult> |
usernameLookup(LookupOptions options) |
List<UsernameResult> |
Searches the usernames recorded for player UUIDs. User filters accept current usernames, historical usernames, or UUID strings and resolve the associated username history. null or #global selects all users.
Use getUsername() for the historical logged name and getUuid() for its UUID. getPlayer() returns the current known username when available, with the logged name as a fallback.
Only user, time, and limit filters apply. Location, radius, world, material, entity, action, and text filters are ignored.
Reading Results
Shared fields
All typed result classes and ParseResult implement CoreProtectResult:
| Method | Return type | Meaning |
|---|---|---|
getPlayer() |
String |
Username associated with the event. See username history for its current-name behavior. |
getTimestamp() |
long |
Unix timestamp in milliseconds. Lookup time parameters are in seconds. |
getX(), getY(), getZ() |
int |
Block coordinates, with the tracked-location behavior described above. |
worldName() |
String |
World name. Username results return an empty string and zero coordinates. |
getActionId() |
int |
Action ID in the result type's action family. |
getActionString() |
String |
Action name, such as break, spawn, add, or login. |
BlockResult
| Additional method | Return type | Meaning |
|---|---|---|
getType() |
Material |
Logged block material; null for entity rows or an unresolved material. |
getBlockData() |
BlockData |
Logged block state; null for entity rows. |
getEntityType() |
EntityType |
Entity type for kill/spawn rows; null for block rows. |
getData() |
int |
Deprecated legacy block data value. Prefer getBlockData(). |
isRolledBack() |
boolean |
Whether the block/entity change is currently rolled back. |
Action strings are break, place, click, kill, or spawn as applicable.
EntityResult
| Additional method | Return type | Meaning |
|---|---|---|
getEntityType() |
EntityType |
Logged entity type, including PLAYER for player-kill records. |
isRolledBack() |
boolean |
Whether the entity change is currently rolled back. |
Actions are KILL (3, kill) and SPAWN (13, spawn).
ContainerResult and ItemResult
| Additional method | Return type | Meaning |
|---|---|---|
getType() |
Material |
Logged item material, or null if unresolved. |
getAmount() |
int |
Number of items. |
getMetadata() |
byte[] |
Copy of the raw item metadata, or null when absent. |
isRolledBack() |
boolean |
Whether the transaction is currently rolled back. |
ContainerResult.getData() |
int |
Stored item data value. |
Container actions are 0 / remove and 1 / add. Item actions use the IDs and strings in the item action table.
InventoryResult
| Additional method | Return type | Meaning |
|---|---|---|
getType() |
Material |
Item material, with block sources normalized to an inventory item. |
getAmount() |
int |
Number of items. |
getMetadata() |
byte[] |
Copy of raw item metadata, or null. |
getData() |
int |
Deprecated legacy item data value. |
getTransactionActionId() |
int |
Underlying transaction action. Interpret together with its source. |
getTransactionActionString() |
String |
Transaction action string, such as remove, drop, or buy. |
getSourceId() |
int |
Public source: 0 = block, 1 = container, 2 = item. |
getSource() |
String |
block, container, or item. |
isRolledBack() |
boolean |
Whether the inventory change is currently rolled back. |
The shared getActionId() / getActionString() report 0 / remove or 1 / add from the player's inventory perspective. Tracked entity-container transactions use source 1 / container.
MessageResult
getMessage() returns the chat message or command as a String. Actions are 6 / chat and 7 / command.
SignResult
| Additional method | Return type | Meaning |
|---|---|---|
getLine(int line) |
String |
Zero-based saved line: 0–3 front, 4–7 back. An out-of-range index returns an empty string. |
getLines() |
String[] |
Copy of all eight saved lines. |
getMessage() |
String |
Nonempty lines on the logged face joined with spaces. Nonempty messages end with a space; a face with no text returns an empty string. |
isFront() |
boolean |
Whether the logged face is the front. |
getColor() |
int |
Stored front text color. |
getColorSecondary() |
int |
Stored back text color. |
getData() |
int |
Raw sign-state flags. |
isFrontGlowing() / isBackGlowing() |
boolean |
Whether the corresponding side glows. |
isWaxed() |
boolean |
Whether the sign is waxed. |
The returned action is 1 / place.
SessionResult
Uses the shared fields. Actions are 0 / logout and 1 / login.
UsernameResult
getUsername() returns the logged name and getUuid() returns the UUID as a String. The shared action is 9 / username. These results have no world or coordinates.
Legacy Lookups, Rollbacks, and Restores
Full and partial searches
performLookup searches persisted block and entity history using the supplied filters. performPartialLookup performs the same search with an offset and maximum result count for pagination. Use parseResult to read each returned row.
The following methods return List<String[]>:
performLookup(time, restrictUsers, excludeUsers, restrictBlocks, excludeBlocks, actionList, radius, radiusLocation)
performPartialLookup(time, restrictUsers, excludeUsers, restrictBlocks, excludeBlocks, actionList, radius, radiusLocation, limitOffset, limitCount)
performRollback(time, restrictUsers, excludeUsers, restrictBlocks, excludeBlocks, actionList, radius, radiusLocation)
performRestore(time, restrictUsers, excludeUsers, restrictBlocks, excludeBlocks, actionList, radius, radiusLocation)
| Parameter | Type | Behavior |
|---|---|---|
time |
int |
How far back to search in seconds. Use a positive value; these legacy methods do not share the typed options' zero-means-all-time convention. |
restrictUsers |
List<String> |
Users to include. null or an empty list selects all users and requires a positive radius and location. |
excludeUsers |
List<String> |
Users to exclude; null imposes no exclusion. |
restrictBlocks |
List<Object> |
Bukkit Material or EntityType values to include; null imposes no type restriction. |
excludeBlocks |
List<Object> |
Bukkit Material or EntityType values to exclude; null imposes no type exclusion. |
actionList |
List<Integer> |
Block/entity action IDs. Accepted IDs are 0, 1, 2, 3, and 13. |
radius |
int |
Positive X/Z radius at all heights. Zero or a negative value disables the radius. |
radiusLocation |
Location |
Center of a positive radius. May be null for user-restricted searches without a radius. |
limitOffset |
int |
Zero-based matching-row offset for performPartialLookup. |
limitCount |
int |
Maximum rows for performPartialLookup. Use nonnegative offset/count values. |
With no explicit actions or type restrictions, the action list defaults to block breaks and placements. Material restrictions infer block breaks/placements; entity-type restrictions infer entity kills. To include entity spawns, explicitly select CoreProtectAction.ENTITY_SPAWN.id().
Unsupported explicit action IDs are ignored. If none remain, the method returns an empty list. An unrestricted global search without a positive radius, or a positive radius without a location, returns null.
For legacy lookups, a supplied location also restricts the world even when the radius is disabled. Use typed options for a clear whole-world or exact-location search.
Rollback and restore behavior
performRollback reverses the selected changes. performRestore reapplies previously rolled-back changes. Both require an asynchronous caller and return raw result rows. They do not accept LookupOptions.
Entity spawns are opt-in through action ID 13. This preserves legacy default block behavior; in-game rollbacks/restores with no action filter can include entity spawns when rollback-entities is enabled. Spawn lookup selection can match original or tracked positions, while rollback/restore world selection uses the tracked current/final world.
An empty result can also indicate that CoreProtect could not start the operation, including during a purge, a conflicting database operation, or when called from the primary server thread. Do not treat it as a guaranteed successful rollback of zero rows.
Legacy block, session, and queue lookups
| Signature | Returns | Behavior |
|---|---|---|
blockLookup(Block block, int time) |
List<String[]> |
Searches persisted block-table history at the supplied block coordinates, including entity rows. time is seconds to look back. |
sessionLookup(String user, int time) |
List<String[]> |
Searches persisted login/logout history for the user. time is seconds to look back. |
queueLookup(Block block) |
List<String[]> |
Searches pending block breaks and placements at the block's location that have not yet been persisted. |
The legacy block and session overloads impose no time restriction when time is zero or negative. The legacy session overload requires a concrete username; null, an empty string, or #global returns no results. Use typed session options to search all users.
Queue lookups cover pending block changes, not every logging category. A newly queued event might not yet appear in a database lookup. Queue result timestamps reflect the time of the queue lookup, not the original event time, so they cannot reliably enforce an event-age filter.
Parsing legacy rows
Call api.parseResult(row) for rows returned by these legacy methods. It returns CoreProtectAPI.ParseResult, which extends net.coreprotect.api.result.ParseResult and preserves the existing nested-class import.
ParseResult implements the shared result fields and adds:
| Method | Return type | Meaning |
|---|---|---|
getType() |
Material |
Logged block material; null for entity or session rows. |
getBlockData() |
BlockData |
Logged block data; null for entity or session rows. |
getEntityType() |
EntityType |
Entity type for entity rows; null for block or session rows. |
isRolledBack() |
boolean |
Whether the block/entity change is rolled back; false for session rows. |
getData() |
int |
Deprecated legacy data value. |
getTime() |
int |
Deprecated Unix timestamp in seconds. Prefer getTimestamp() for milliseconds. |
Block/entity action strings are lowercase break, place, click, kill, and spawn. Session rows use logout and login. Use the typed result classes for their corresponding lookup methods instead of passing typed results to parseResult.
Logging and Utility Methods
Block placement and removal
All of these overloads return boolean:
| Signature | Data recorded |
|---|---|
logPlacement(String user, BlockState blockState) |
Placement using the supplied block-state snapshot. |
logPlacement(String user, Location location, Material type, BlockData blockData) |
Placement of the supplied material and optional block data. |
logPlacement(String user, Location location, BlockData blockData) |
Placement using the current block's material and supplied non-null block data. |
logRemoval(String user, BlockState blockState) |
Removal using the supplied block-state snapshot. |
logRemoval(String user, Location location, Material type, BlockData blockData) |
Removal of the supplied material and optional block data, with container-break handling. |
logRemoval(String user, Location location, BlockData blockData) |
Removal using the current block's material and supplied non-null block data, with container-break handling. |
user identifies the player or plugin responsible for the change. Capture removal data before changing the block. For placements, supply the state/material being placed. The explicit-material overloads allow null block data; the Location, BlockData overloads require non-null data.
Location-based removal checks the existing block for container contents. The BlockState overload does not perform that separate container-break check. Call methods that inspect live blocks on the appropriate server thread.
Deprecated placement/removal overloads taking (String user, Location location, Material type, byte data) remain for older integrations. Use BlockData for new code.
Chat and commands
| Signature | Behavior |
|---|---|
logChat(Player player, String message) |
Queues a nonempty chat message. Messages beginning with / are rejected. |
logCommand(Player player, String command) |
Queues a nonempty command, which must begin with /. |
Both return boolean, require the API to be enabled, and honor the world's corresponding player-message or player-command logging setting. Supply a valid player with a world.
Container transactions
boolean logContainerTransaction(String user, Location location) captures a block inventory so CoreProtect can detect your subsequent changes. Call it immediately before modifying the inventory, on the appropriate server thread.
user is the username or plugin actor credited with adding or removing the items. location is the block container's location.
This method is for a block container. Tracked entity-container lookup support does not add an entity overload to this logging method.
Block interactions
boolean logInteraction(String user, Location location) records an interaction with the block currently at the location. It reads the block's material and block data.
user is the username or plugin actor credited with the interaction. location identifies the affected block.
Placement and removal checks
| Signature | Behavior |
|---|---|
hasPlaced(String user, Block block, int time, int offset) |
Checks whether the user placed a block at these coordinates during the requested window. |
hasRemoved(String user, Block block, int time, int offset) |
Checks whether the user removed a block at these coordinates during the requested window. |
Both return boolean and compare usernames without case sensitivity. time is how many seconds to search back; offset excludes the most recent seconds from that window. Use offset = 0 to include the latest persisted records. These methods search persisted history; use queueLookup separately for pending block changes.
Purging history
void performPurge(int time) dispatches the console command co purge t:<time>s. The argument is the age in seconds beyond which history should be purged: 120 selects records older than two minutes.
Call this command-dispatching method on the appropriate server thread. It returns no completion result and uses the normal command's purge behavior and restrictions.
Available Events
CoreProtectPreLogEvent
net.coreprotect.event.CoreProtectPreLogEvent is a cancellable asynchronous event emitted by participating loggers before they write a record. Keep listeners safe for asynchronous execution and avoid accessing live Bukkit world state from the listener.
| Getter | Meaning | Setter |
|---|---|---|
getUser() |
User under which the action will be logged. | setUser(String) |
getLocation() |
Location associated with the record. | setLocation(Location) |
getAction() |
CoreProtectPreLogEvent.Action category. |
None |
getActionId() |
Logger-specific action value, or -1 when not applicable. |
None |
getMaterial() |
Material when applicable; otherwise null. |
None |
getEntityType() |
Entity type when applicable; otherwise null. |
None |
getMessage() |
Message metadata when applicable; otherwise null. |
None |
isCancelled() |
Whether this record has been cancelled. | setCancelled(boolean) |
setUser rejects null or empty names. setLocation rejects null. Cancelling prevents the record from being logged; it does not undo the gameplay action.
The action enum contains:
| Category | Meaning |
|---|---|
UNKNOWN |
Unspecified action, including compatibility constructors. |
BLOCK_BREAK, BLOCK_PLACE |
Block removal or placement. |
PLAYER_INTERACTION |
Player interaction with a block. |
ENTITY_KILL, PLAYER_KILL |
Entity or player death. |
ENTITY_SPAWN |
Entity spawn. |
ENTITY_INTERACTION |
Interaction with a tracked entity. |
SIGN_TEXT |
Sign text logging. |
CONTAINER_TRANSACTION |
Container item transaction. |
ITEM_TRANSACTION |
World item transaction. |
PLAYER_COMMAND |
Player command. |
Use getAction() to identify the category before interpreting the action ID. Container, item, and sign events use their own stored action values; player-command events use -1. Command events expose their command as the message. Entity-interaction events expose the interaction action name. Sign-text events do not provide their lines through getMessage().
The event is not emitted for every logging category; for example, chat and session logging do not emit it.
The metadata constructor is:
CoreProtectPreLogEvent(String user, Location location, Action action, int actionId, Material material, EntityType entityType, String message)
CoreProtectPreLogEvent(String user, Location location) remains available. The older CoreProtectPreLogEvent(String user) constructor is deprecated and supplies a null location. Listeners should tolerate absent optional metadata.
Examples
These examples use an enabled API v13 instance named api and your plugin instance named plugin. Import java.util.List, the required Bukkit types, and the relevant classes from net.coreprotect.api and net.coreprotect.api.result.
Capture location, world, and other live Bukkit values before starting asynchronous work. In the examples that show only a lookup body, run that body asynchronously as demonstrated below. Database queries return lists directly; they do not create a background task themselves.
Search blocks asynchronously
Find the first 100 block changes by Notch in the last minute, excluding dirt and grass blocks:
LookupOptions options = LookupOptions.builder()
.user("Notch")
.time(60)
.excludeMaterials(List.of(Material.DIRT, Material.GRASS_BLOCK))
.limit(0, 100)
.build();
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
List<BlockResult> results = api.blockLookup(options);
if (results != null) {
for (BlockResult result : results) {
plugin.getLogger().info(result.getPlayer() + " " + result.getActionString()
+ " " + result.getType() + " at " + result.worldName()
+ " " + result.getX() + "," + result.getY() + "," + result.getZ());
}
}
});
Use .radius(location, 5) to restrict this search to an X/Z area, or .world(world) for a whole world. To use the exact-block compatibility overload:
List<BlockResult> results = api.blockLookup(block, LookupOptions.builder()
.time(60)
.blockActions(List.of(BlockAction.BREAK, BlockAction.PLACE))
.limit(0, 100)
.build());
Find entity spawns
Find zombie and skeleton spawns within 50 blocks of a captured location:
LookupOptions options = LookupOptions.builder()
.time(3600)
.radius(location, 50)
.entityActions(List.of(EntityAction.SPAWN))
.includeEntities(List.of(EntityType.ZOMBIE, EntityType.SKELETON))
.limit(0, 100)
.build();
List<EntityResult> results = api.entityLookup(options);
The search can match a tracked position, but each result still reports its original spawn coordinates.
Find container deposits
Find diamonds added to containers within five blocks of a location:
LookupOptions options = LookupOptions.builder()
.time(3600)
.radius(location, 5)
.includeMaterials(List.of(Material.DIAMOND))
.containerActions(List.of(ContainerAction.ADD))
.limit(0, 100)
.build();
List<ContainerResult> results = api.containerLookup(options);
For an exact-location lookup without additional filters, use api.containerLookup(location, 60).
Find item and inventory transactions
Look up dropped and picked-up items:
LookupOptions options = LookupOptions.builder()
.user("Notch")
.time(3600)
.itemActions(List.of(ItemAction.DROP, ItemAction.PICKUP))
.limit(0, 100)
.build();
List<ItemResult> results = api.itemLookup(options);
Find inventory removals caused by container deposits or block placements:
LookupOptions options = LookupOptions.builder()
.user("Notch")
.time(3600)
.inventoryActions(List.of(InventoryAction.CONTAINER_ADD, InventoryAction.BLOCK_PLACE))
.limit(0, 100)
.build();
List<InventoryResult> results = api.inventoryLookup(options);
if (results != null) {
for (InventoryResult result : results) {
plugin.getLogger().info(result.getActionString() + " " + result.getAmount()
+ " " + result.getType() + " via " + result.getSource()
+ ":" + result.getTransactionActionString());
}
}
Filter commands by prefix
Find /shop ... and /market ... commands from the last hour, excluding commands beginning with /shop admin:
LookupOptions options = LookupOptions.builder()
.time(3600)
.textStartsWithAny(List.of("/shop ", "/market "))
.textStartsWithNone(List.of("/shop admin"))
.limit(0, 100)
.build();
List<MessageResult> results = api.commandLookup(options);
The trailing spaces in the inclusion prefixes are significant: bare /shop and /market commands do not match. Use chatLookup(options) to apply the same kind of filters to chat, or chatLookup("Notch", 60) for an unfiltered user/time search.
Find sign text
Find signs whose logged face has a line beginning with Diamond Shop:
LookupOptions options = LookupOptions.builder()
.time(3600)
.radius(location, 10)
.textStartsWithAny(List.of("Diamond Shop"))
.limit(0, 100)
.build();
List<SignResult> results = api.signLookup(options);
if (results != null) {
for (SignResult result : results) {
plugin.getLogger().info((result.isFront() ? "Front: " : "Back: ") + result.getMessage());
}
}
Use api.signLookup(location, 60) for an exact-location search without additional filters.
Read sessions and username history
Find logins for Notch in the last day:
LookupOptions options = LookupOptions.builder()
.user("Notch")
.time(24 * 60 * 60)
.sessionActions(List.of(SessionAction.LOGIN))
.limit(0, 100)
.build();
List<SessionResult> results = api.sessionLookup(options);
Read username history for the same period:
List<UsernameResult> results = api.usernameLookup("Notch", 24 * 60 * 60);
if (results != null) {
for (UsernameResult result : results) {
plugin.getLogger().info(result.getUuid() + " used " + result.getUsername());
}
}
Parse a legacy lookup
Read the last minute of Notch's block history and parse its coordinates:
List<String[]> rows = api.performLookup(60, List.of("Notch"), null,
null, null, null, 0, null);
if (rows != null) {
for (String[] row : rows) {
CoreProtectAPI.ParseResult result = api.parseResult(row);
plugin.getLogger().info(result.getActionString() + " at "
+ result.getX() + "," + result.getY() + "," + result.getZ());
}
}
For a page of results within five blocks of a location:
List<String[]> rows = api.performPartialLookup(60, null, null,
null, null, List.of(CoreProtectAction.BLOCK_BREAK.id(), CoreProtectAction.BLOCK_PLACE.id()),
5, location, 0, 100);
The deprecated single-user overloads of performLookup, performPartialLookup, performRollback, and performRestore remain available. Prefer the list-based signatures above for new integrations.
Roll back changes asynchronously
Reverse Notch's block changes and entity spawns from the last minute within five blocks of a captured location:
List<Integer> actions = List.of(CoreProtectAction.BLOCK_BREAK.id(),
CoreProtectAction.BLOCK_PLACE.id(), CoreProtectAction.ENTITY_SPAWN.id());
Location center = location.clone();
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
List<String[]> rows = api.performRollback(60, List.of("Notch"), null,
null, null, actions, 5, center);
if (rows != null) {
plugin.getLogger().info("Rollback returned " + rows.size() + " rows.");
}
});
To restore changes, use performRestore with the same parameter structure in an asynchronous task. An empty result alone does not distinguish no matches from an operation that could not start.
Include pending block changes in a placement check
boolean placed = api.hasPlaced("Notch", block, 60, 0);
if (!placed) {
List<String[]> pending = api.queueLookup(block);
if (pending != null) {
for (String[] row : pending) {
CoreProtectAPI.ParseResult result = api.parseResult(row);
if (result.getActionId() == CoreProtectAction.BLOCK_PLACE.id()
&& result.getPlayer().equalsIgnoreCase("Notch")) {
placed = true;
break;
}
}
}
}
plugin.getLogger().info("Recent placement found: " + placed);
The persisted check covers the last 60 seconds; the pending check cannot enforce that age because queue rows use the lookup time as their timestamp. Use hasRemoved and CoreProtectAction.BLOCK_BREAK.id() for removal checks. A nonzero offset on the persisted check can exclude the most recent seconds when needed by your integration.
Log a block replacement
Run this on the appropriate server thread, while the original block still exists:
api.logRemoval("Notch", block.getLocation(), block.getType(), block.getBlockData());
block.setType(Material.STONE);
api.logPlacement("Notch", block.getState());
Use the actual responsible username or a plugin-specific actor name. If CoreProtect already records the gameplay event, additional API logging can create duplicate history.
Log inventory changes
For a block-backed inventory, capture its contents immediately before changing them:
api.logContainerTransaction("Notch", inventory.getLocation());
inventory.addItem(itemStack);
Listen for pre-log events
This listener cancels block-placement records attributed to a plugin's preview actor:
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import net.coreprotect.event.CoreProtectPreLogEvent;
public final class PreviewLogListener implements Listener {
@EventHandler
public void onPreLog(CoreProtectPreLogEvent event) {
if (event.getAction() == CoreProtectPreLogEvent.Action.BLOCK_PLACE
&& "MyPluginPreview".equals(event.getUser())) {
event.setCancelled(true);
}
}
}
Register it during plugin initialization:
plugin.getServer().getPluginManager().registerEvents(new PreviewLogListener(), plugin);
The listener executes asynchronously. Cancelling it suppresses the log record, not the original block placement.