v1.3.1: NBT preservation, GUI previews, and global placement toggle

This commit is contained in:
2026-02-08 10:59:02 -05:00
parent 84b4471fcb
commit 58b7d44d8f
31 changed files with 1204 additions and 443 deletions

View File

@@ -0,0 +1,57 @@
# oyeOwner Integration Guide for AI Assistants
This document provides concise instructions for integrating the **oyeOwner** API into other Bukkit/Spigot plugins.
## 1. Dependency Configuration (Maven)
Add the `oyeOwner` project as a dependency in your `pom.xml`.
```xml
<dependency>
<groupId>party.cybsec</groupId>
<artifactId>oyeOwner</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
```
## 2. Plugin Configuration (plugin.yml)
Add `oyeOwner` as a dependency to ensure it loads before your plugin.
```yaml
depend: [oyeOwner]
```
## 3. Java API Usage
### Accessing the API
The API is accessible via a static getter in the main class: `party.cybsec.OyeOwner.getAPI()`.
### Sync Lookup (Blocking)
Use this if you are already in an asynchronous task or if a tiny delay is acceptable.
```java
import org.bukkit.block.Block;
import party.cybsec.OyeOwner;
// Returns String username or null
String owner = OyeOwner.getAPI().getBlockOwner(block);
```
### Async Lookup (Non-blocking)
Recommended for use on the main thread to avoid lag.
```java
import org.bukkit.block.Block;
import party.cybsec.OyeOwner;
OyeOwner.getAPI().getBlockOwnerAsync(block).thenAccept(owner -> {
if (owner != null) {
// Player name found: owner
} else {
// No ownership data found
}
});
```
## 4. Summary of Capabilities
- **Lookback Period**: 60 days.
- **Action Tracked**: Block Placement (Action ID 1).
- **Core Engine**: Powered by CoreProtect with a reflection-based safe hook.

73
oyeOwner/pom.xml Normal file
View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>party.cybsec</groupId>
<artifactId>oyeOwner</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>oyeOwner</name>
<properties>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>playpro</id>
<url>https://maven.playpro.com</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.coreprotect</groupId>
<artifactId>coreprotect</artifactId>
<version>23.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@@ -0,0 +1,56 @@
package party.cybsec;
import net.coreprotect.CoreProtectAPI;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Method;
public class CoreProtectHook {
private final OyeOwner plugin;
public CoreProtectHook(OyeOwner plugin) {
this.plugin = plugin;
}
public CoreProtectAPI getCoreProtect() {
Plugin cpPlugin = Bukkit.getPluginManager().getPlugin("CoreProtect");
// Check that CoreProtect is loaded and enabled
if (cpPlugin == null || !cpPlugin.isEnabled()) {
return null;
}
// Use reflection to get the API to avoid NoClassDefFoundError at load time
try {
// Verify it's the right class name
if (!cpPlugin.getClass().getName().equals("net.coreprotect.CoreProtect")) {
return null;
}
Method getAPIMethod = cpPlugin.getClass().getMethod("getAPI");
Object apiObject = getAPIMethod.invoke(cpPlugin);
if (apiObject instanceof CoreProtectAPI) {
CoreProtectAPI api = (CoreProtectAPI) apiObject;
// Check that the API is enabled
if (!api.isEnabled()) {
return null;
}
// Check that a compatible version of the API is loaded
if (api.APIVersion() < 11) {
return null;
}
return api;
}
} catch (Exception e) {
plugin.getLogger().warning("Failed to hook into CoreProtect API via reflection: " + e.getMessage());
}
return null;
}
}

View File

@@ -0,0 +1,50 @@
package party.cybsec;
import party.cybsec.command.WhoCommand;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.logging.Logger;
public class OyeOwner extends JavaPlugin {
private static OyeOwner instance;
private CoreProtectHook coreProtectHook;
private OyeOwnerAPI api;
@Override
public void onEnable() {
instance = this;
Logger logger = getLogger();
logger.info("oyeOwner is enabling...");
this.coreProtectHook = new CoreProtectHook(this);
this.api = new OyeOwnerAPI(this);
if (coreProtectHook.getCoreProtect() == null) {
logger.severe("CoreProtect not found or incompatible! Disabling oyeOwner.");
getServer().getPluginManager().disablePlugin(this);
return;
}
getCommand("who").setExecutor(new WhoCommand(this));
logger.info("oyeOwner enabled successfully.");
}
@Override
public void onDisable() {
getLogger().info("oyeOwner disabled.");
}
public CoreProtectHook getCoreProtectHook() {
return coreProtectHook;
}
public OyeOwnerAPI getOyeAPI() {
return api;
}
public static OyeOwnerAPI getAPI() {
return instance.api;
}
}

View File

@@ -0,0 +1,59 @@
package party.cybsec;
import net.coreprotect.CoreProtectAPI;
import org.bukkit.block.Block;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class OyeOwnerAPI {
private final OyeOwner plugin;
public OyeOwnerAPI(OyeOwner plugin) {
this.plugin = plugin;
}
/**
* Get the owner (player who placed) of a block.
* Searches back 60 days.
*
* @param block The block to check.
* @return The username of the player who placed the block, or null if not
* found/error.
*/
public String getBlockOwner(Block block) {
CoreProtectAPI api = plugin.getCoreProtectHook().getCoreProtect();
if (api == null || block == null) {
return null;
}
// 60 days in seconds
int time = 60 * 24 * 60 * 60;
List<String[]> lookup = api.blockLookup(block, time);
if (lookup == null || lookup.isEmpty()) {
return null;
}
for (String[] result : lookup) {
CoreProtectAPI.ParseResult parsed = api.parseResult(result);
// Action ID 1 is "Placement"
if (parsed.getActionId() == 1) {
return parsed.getPlayer();
}
}
return null;
}
/**
* Get the owner of a block asynchronously.
*
* @param block The block to check.
* @return A CompletableFuture containing the username or null.
*/
public CompletableFuture<String> getBlockOwnerAsync(Block block) {
return CompletableFuture.supplyAsync(() -> getBlockOwner(block));
}
}

View File

@@ -0,0 +1,47 @@
package party.cybsec.command;
import party.cybsec.OyeOwner;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class WhoCommand implements CommandExecutor {
private final OyeOwner plugin;
public WhoCommand(OyeOwner plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "Only players can use this command.");
return true;
}
Player player = (Player) sender;
Block targetBlock = player.getTargetBlockExact(5);
if (targetBlock == null || targetBlock.getType() == Material.AIR) {
player.sendMessage(ChatColor.RED + "You must be looking at a block.");
return true;
}
String owner = plugin.getOyeAPI().getBlockOwner(targetBlock);
if (owner != null) {
player.sendMessage(ChatColor.DARK_AQUA + "--- Block Owner Info ---");
player.sendMessage(ChatColor.GOLD + "user: " + ChatColor.WHITE + owner);
player.sendMessage(ChatColor.DARK_AQUA + "------------------------");
return true;
}
player.sendMessage(ChatColor.GRAY + "No placement records found for this block.");
return true;
}
}

View File

@@ -0,0 +1,14 @@
name: oyeOwner
version: '1.0'
main: party.cybsec.OyeOwner
api-version: '1.21'
depend: [CoreProtect]
commands:
who:
description: check who placed the block you are looking at
permission: oyeowner.use
usage: /<command>
permissions:
oyeowner.use:
description: Allows player to use the /who command
default: op

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,14 @@
name: oyeOwner
version: '1.0'
main: party.cybsec.OyeOwner
api-version: '1.21'
depend: [CoreProtect]
commands:
who:
description: check who placed the block you are looking at
permission: oyeowner.use
usage: /<command>
permissions:
oyeowner.use:
description: Allows player to use the /who command
default: op

View File

@@ -0,0 +1,3 @@
artifactId=oyeOwner
groupId=party.cybsec
version=1.0-SNAPSHOT

View File

@@ -0,0 +1,4 @@
party/cybsec/command/WhoCommand.class
party/cybsec/OyeOwner.class
party/cybsec/CoreProtectHook.class
party/cybsec/OyeOwnerAPI.class

View File

@@ -0,0 +1,4 @@
/Users/jacktotonchi/oyeOwner/src/main/java/party/cybsec/CoreProtectHook.java
/Users/jacktotonchi/oyeOwner/src/main/java/party/cybsec/OyeOwner.java
/Users/jacktotonchi/oyeOwner/src/main/java/party/cybsec/OyeOwnerAPI.java
/Users/jacktotonchi/oyeOwner/src/main/java/party/cybsec/command/WhoCommand.java