commit 69f7fb2534d461776676dda53e6809a503baaf3b Author: stephen Date: Mon Jul 8 19:08:51 2024 -0400 Bukkitstr. Thanks ChatGPT... and nostr:npub1826v365he5ty69lk3xgvzqrwy8587vdfrxnsz0k09khzustf8r7s6j7t95 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..f5d9807 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# BukkitstrPlugin + +BukkitstrPlugin is a Minecraft plugin that integrates with the Nostr relay to authenticate players. Players must authenticate by signing a valid Nostr event before they can move in the game. + +## Features + +- **WebSocket Integration**: Connects to a Nostr relay using WebSockets. +- **Player Authentication**: Players must authenticate with their Minecraft username via a Nostr event. +- **Movement Lock**: Prevents players from moving until they are authenticated. +- **Real-time Messaging**: Sends and receives messages from the Nostr relay in real-time. + +## Prerequisites + +- **Java 8 or higher** +- **Maven** (for building the plugin) +- **Spigot/Paper** server (Minecraft 1.20 or higher) + +## Installation + +1. **Clone the repository**: + ```sh + git clone https://git.vanderwarker.family/nostr/bukkitstr.git + cd bukkitstr + ``` + +2. **Build the plugin**: + ```sh + mvn clean package + ``` + +3. **Copy the plugin JAR**: + Copy the generated `bukkitstr-plugin-1.0-SNAPSHOT.jar` file from the `target` directory to your Minecraft server's `plugins` directory. + +4. **Configure the plugin**: + Make sure to replace `wss://your-nostr-relay-url` in the `BukkitstrPlugin.java` file with your actual Nostr relay WebSocket URL. + +5. **Start your server**: + Start your Minecraft server and check the console for any errors. The plugin should initialize and connect to the Nostr relay. + +## Usage + +### Authentication Command + +Players need to use the `/auth` command to start the authentication process. The plugin will wait for a Nostr event containing the player's Minecraft username in the `d` tag content. + +```sh +/auth +``` + +### Sign Event + +Next, you need to sign a event kind "13378008" and the "d" tag to be your Minecraft username. + +Using [nostril]() is quite easy. +```sh +nostril --kind 13378008 --tag "d" "MinecraftUsername" +``` + +Then send to the relay you've specified in src/../BukkitstrPlugin.java + + +### Player Authentication Process + +1. **Connect to the Nostr relay**: The plugin connects to the specified Nostr relay when the server starts. +2. **Send Authentication Request**: The plugin sends an initial request to the Nostr relay to listen for authentication events. +3. **Wait for Nostr Event**: When a player uses the `/auth` command, the plugin waits for a Nostr event containing the player's username in the `d` tag. +4. **Authenticate Player**: If the received Nostr event's `d` tag content matches the player's username, the player is authenticated and can move freely. Otherwise, they will receive an authentication failure message. + +## Developer Information + +### Source Code + +The main source code is located in the `src/main/java/family/vanderwarker/bukkitstr/BukkitstrPlugin.java` file. + +### Dependencies + +Ensure the following dependencies are added to your `pom.xml`: + +```xml + + + org.java-websocket + Java-WebSocket + 1.5.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.13.3 + + +``` + +## Acknowledgments + +- Thanks to the SpigotMC community for their documentation and support. +- Thanks to the Nostr community for their innovative protocol. +- Thanks to [npub1826v365he5ty69lk3xgvzqrwy8587vdfrxnsz0k09khzustf8r7s6j7t95](nostr:npub1826v365he5ty69lk3xgvzqrwy8587vdfrxnsz0k09khzustf8r7s6j7t95) +- Thanks to ChatGPT 4.o diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..2826387 --- /dev/null +++ b/pom.xml @@ -0,0 +1,78 @@ + + 4.0.0 + + family.vanderwarker.bukkitstr + bukkistr + 1.0-SNAPSHOT + + + 1.8 + 1.8 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + + org.spigotmc + spigot-api + 1.16.5-R0.1-SNAPSHOT + provided + + + + org.java-websocket + Java-WebSocket + 1.5.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.12.3 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + org.java_websocket + shaded.org.java_websocket + + + + + + + + + diff --git a/src/main/java/family/vanderwarker/bukkitstr/BukkitstrPlugin.java b/src/main/java/family/vanderwarker/bukkitstr/BukkitstrPlugin.java new file mode 100644 index 0000000..a83b841 --- /dev/null +++ b/src/main/java/family/vanderwarker/bukkitstr/BukkitstrPlugin.java @@ -0,0 +1,150 @@ +package family.vanderwarker.bukkitstr; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; +import java.util.logging.Logger; + +public class BukkitstrPlugin extends JavaPlugin implements Listener { + + private NostrWebSocketClient webSocketClient; + private final Set authenticatedPlayers = new HashSet<>(); + + @Override + public void onEnable() { + getLogger().info("BukkitstrPlugin enabled!"); + try { + URI uri = new URI("wss://"); // Replace with your Nostr relay URL + webSocketClient = new NostrWebSocketClient(uri, getLogger()); + webSocketClient.connect(); + } catch (URISyntaxException e) { + getLogger().severe("Invalid URI for Nostr relay: " + e.getMessage()); + } + getServer().getPluginManager().registerEvents(this, this); + } + + @Override + public void onDisable() { + getLogger().info("BukkitstrPlugin disabled!"); + if (webSocketClient != null) { + webSocketClient.close(); + } + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (sender instanceof Player) { + Player player = (Player) sender; + player.sendMessage("Waiting for Nostr event..."); + + webSocketClient.waitForEvent(content -> handleSpecificEvent(content, player)); + + return true; + } + return false; + } + + private void handleSpecificEvent(String content, Player player) { + if (content.equals(player.getName())) { + authenticatedPlayers.add(player); + player.sendMessage("You are now authenticated."); + } else { + player.sendMessage("Authentication failed. Username does not match."); + } + } + + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + if (!authenticatedPlayers.contains(player)) { + event.setCancelled(true); + player.sendMessage("You need to authenticate first!"); + } + } + + private class NostrWebSocketClient extends WebSocketClient { + + private final Logger logger; + private final ObjectMapper objectMapper; + private Consumer eventConsumer; + + public NostrWebSocketClient(URI serverUri, Logger logger) { + super(serverUri); + this.logger = logger; + this.objectMapper = new ObjectMapper(); + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + logger.info("Connected to Nostr relay: " + getURI()); + sendInitialRequest(); + } + + @Override + public void onMessage(String message) { + logger.info("Received message from Nostr relay: " + message); + handleNostrEvent(message); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + logger.warning("Connection to Nostr relay closed: " + reason); + } + + @Override + public void onError(Exception ex) { + logger.severe("Error with WebSocket connection: " + ex.getMessage()); + } + + private void handleNostrEvent(String message) { + try { + JsonNode jsonNode = objectMapper.readTree(message); + if (jsonNode.isArray() && jsonNode.size() > 2) { + JsonNode eventNode = jsonNode.get(2); + JsonNode tagsNode = eventNode.path("tags"); + if (tagsNode.isArray()) { + for (JsonNode tag : tagsNode) { + if (tag.isArray() && tag.size() > 1 && "d".equals(tag.get(0).asText())) { + String content = tag.get(1).asText(); + if (eventConsumer != null) { + eventConsumer.accept(content); + } + } + } + } else { + logger.warning("No 'tags' array found in the event message."); + } + } else { + logger.warning("Event message is not in the expected format."); + } + } catch (JsonProcessingException e) { + logger.severe("Failed to parse Nostr event: " + e.getMessage()); + } + } + + public void waitForEvent(Consumer eventConsumer) { + this.eventConsumer = eventConsumer; + } + + private void sendInitialRequest() { + String initialRequest = "[\"REQ\",\"mc_auth\",{\"kinds\":[13378008],\"limit\":0}]"; + send(initialRequest); + logger.info("Sent initial request to Nostr relay: " + initialRequest); + } + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..cef2d19 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,8 @@ +name: BukkitstrPlugin +version: 0.1 +main: family.vanderwarker.bukkitstr.BukkitstrPlugin +api-version: 1.20 +commands: + auth: + description: Authenticate with the server using Nostr + usage: /auth \ No newline at end of file