diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml
index 2047703..7bca7ba 100644
--- a/.github/workflows/build-and-test.yaml
+++ b/.github/workflows/build-and-test.yaml
@@ -9,6 +9,7 @@ on:
branches:
- master
- main
+ - dev
jobs:
build:
@@ -27,15 +28,15 @@ jobs:
- name: Set up Maven
uses: stCarolas/setup-maven@v4.5
with:
- maven-version: 3.9.1
+ maven-version: 3.9.9
- name: build application
shell: bash
run: |
- mvn clean install
+ mvn clean package
- name: Upload artifact
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
- name: ${{ github.event.repository.name }}-artifact
- path: target/*.jar
\ No newline at end of file
+ name: artifacts
+ path: target/*.jar
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 70c055d..5269456 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -36,9 +36,10 @@ jobs:
- name: Create GitHub Release
if: ${{ !endsWith(env.PROJECT_VERSION, '-SNAPSHOT') }}
- uses: softprops/action-gh-release@v1
+ uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.PROJECT_VERSION }}
+ body_path: changelogs/${{ env.PROJECT_VERSION }}.md
files: |
target/*.jar
env:
diff --git a/LICENSE b/LICENSE
index ab5a771..6dc18d1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 RetroMC Development Group
+Copyright (c) 2025 RetroHaven Development Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/LICENSE-retromc b/LICENSE-retromc
new file mode 100644
index 0000000..ab5a771
--- /dev/null
+++ b/LICENSE-retromc
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 RetroMC Development Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index d2b175d..983918b 100644
--- a/README.md
+++ b/README.md
@@ -1,45 +1,27 @@
-# Poseidon-Plugin-Template
+# SimpleNotes
-This repository serves as a template to assist with creating plugins for Project Poseidon.
+This plugin aims to provide a note/warning system implemented easily for beta 1.7.3. No other Minecraft versions are targeted and no support is provided for those other versions.
-It includes examples of:
-- A configuration file.
-- A listener.
-- A command.
+This plugin currently assumes all players are paid accounts. Adding offline players support isn't planned for now.
-## Steps to Use This Template
+Tested with Project Poseidon. Plain Bukkit should work since this project doesn't use any of Poseidon's APIs.
-1. **Clone the Repository**
- - Clone this repository to your local machine.
+## Commands
-2. **Modify `pom.xml`**
- - Update the following fields to reflect your plugin:
- - `name`
- - `version`
- - `description`
- - **Note:** Removing `-SNAPSHOT` from the version will trigger the `release.yml` GitHub Action to create a GitHub release.
+The plugin uses one command, /note (with aliases /notes, /warn, /warns, /warning and /warnings).
-3. **Refactor Package Structure**
- - Refactor the package `org.retromc.templateplugin` to a unique package name for your plugin to avoid conflicts.
+This command is disabled by default to make you look through the config (it's located in plugins/SimpleNotes/config.yml!).
-4. **Update `plugin.yml`**
- - Update the `plugin.yml` file to match the refactored package name and plugin metadata.
+### Subcommands
-5. **Modify the Code**
- - Customize the code as required for your plugin.
- - **Important:**
- - Remove the player greeting example in the listener.
- - Remove the test command.
+- /note help: list the subcommands you have access to
+- /note list: list your own notes/warnings (permission: simplenotes.see.self.notes/warns)
+- /note list [player]: list another player's notes/warnings (permission: simplenotes.see.others.notes/warns)
+- /note add [player] [content]: add a note to a player (permission: simplenotes.addnotes)
+- /warn add [player] [content]: add a warn to a player (permission: simplenotes.addnotes)
+ - This is the only case where the alias matter. You can use any of the warn* aliases for this.
+- /note remove [player] [id]: remove a note from a player (permission: simplenotes.removenotes)
-## GitHub Actions
+## Compiling
-This repository includes a pre-configured GitHub Action:
-
-1. **`build-and-test.yml`**:
- - Runs tests on every push to ensure code quality.
- - Uploads an artifact for each commit, allowing others to download the plugin for testing.
-
-2. **`release.yml`**:
- - Automatically creates a GitHub release if the `-SNAPSHOT` suffix is removed from the version in `pom.xml`.
-
-With this template, you can kickstart your plugin development for Project Poseidon quickly and efficiently.
+Clone the repository and run `mvn clean package`. The resulting jar should be in the `target` folder.
\ No newline at end of file
diff --git a/changelogs/1.0.0.md b/changelogs/1.0.0.md
new file mode 100644
index 0000000..1936fc8
--- /dev/null
+++ b/changelogs/1.0.0.md
@@ -0,0 +1,3 @@
+First release.
+
+Adds /note add, /warn add, /note remove, /note list and /note help.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 643ddfa..d29e639 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,10 +4,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- org.example
- Poseidon-Plugin-Template
- 1.0.0-SNAPSHOT
- A template for creating plugins for the Poseidon server software.
+ org.retrohaven.mc.notes
+ SimpleNotes
+ 1.0.0
+ A note system for moderators and admins, with the objective to be light on the system and on the codebase.
8
@@ -57,18 +57,66 @@
1.8
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+
+ com.opencsv:opencsv
+ org.json:json
+ com.google.guava:*
+
+
+
+
+ com.google.common
+ org.retrohaven.shaded.com.google.common
+
+
+
+
+ com.google.guava:guava
+
+ com/google/common/**
+
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
-
-
com.legacyminecraft.poseidon
poseidon-craftbukkit
- 1.1.8
+ 1.1.10-250328-1731-f67a8e3
+ provided
+
+
+ org.json
+ json
+ 20250107
+
+
+ com.opencsv
+ opencsv
+ 5.11
+
+
+ com.google.guava
+ guava
+ 33.4.8-jre
-
-
-
\ No newline at end of file
+
diff --git a/src/main/java/org/retromc/templateplugin/TemplateConfig.java b/src/main/java/org/retrohaven/mc/notes/NoteConfig.java
similarity index 71%
rename from src/main/java/org/retromc/templateplugin/TemplateConfig.java
rename to src/main/java/org/retrohaven/mc/notes/NoteConfig.java
index 072c1d8..1322802 100644
--- a/src/main/java/org/retromc/templateplugin/TemplateConfig.java
+++ b/src/main/java/org/retrohaven/mc/notes/NoteConfig.java
@@ -1,7 +1,6 @@
-package org.retromc.templateplugin;
+package org.retrohaven.mc.notes;
import org.bukkit.util.config.Configuration;
-import org.jetbrains.annotations.Nullable;
import java.io.File;
@@ -10,19 +9,18 @@
* Extends the {@link Configuration} class to provide additional utility methods for
* reading and writing configuration options with defaults.
*/
-public class TemplateConfig extends Configuration {
- private final int configVersion = 1;
+public class NoteConfig extends Configuration {
+ private final int configVersion = 0;
-
- private TemplatePlugin plugin;
+ private NotePlugin plugin;
/**
- * Constructs a new TemplateConfig instance.
+ * Constructs a new NoteConfig instance.
*
* @param plugin The plugin instance associated with this configuration.
* @param configFile The configuration file to be managed.
*/
- public TemplateConfig(TemplatePlugin plugin, File configFile) {
+ public NoteConfig(NotePlugin plugin, File configFile) {
super(configFile);
this.plugin = plugin;
this.reload();
@@ -44,24 +42,20 @@ private void write() {
generateConfigOption("config-version", configVersion);
// Plugin options
- generateConfigOption("settings.test-command.enabled.value", true);
- generateConfigOption("settings.test-command.enabled.info", "Whether the test command is enabled."); // Informational comment
+ generateConfigOption("settings.plugin.enabled.value", false);
+ generateConfigOption("settings.plugin.enabled.info", "Whether the command is enabled.");
- generateConfigOption("settings.test-command.response.value", "This is the response sent to players when they execute the test command.");
- generateConfigOption("settings.test-command.response.info", "The response sent to players when they execute the test command."); // Informational comment
+ generateConfigOption("settings.warns.showonlogin.value", true);
+ generateConfigOption("settings.warns.showonlogin.info", "Whether one's warns should be shown on login. Requires the player to have simplenotes.see.self.warns.");
- generateConfigOption("settings.welcome-message.value", "Welcome to the server, %player%!");
- generateConfigOption("settings.welcome-message.info", "The message sent to players when join the server."); // Informational comment
+ generateConfigOption("settings.notes.showonlogin.value", false);
+ generateConfigOption("settings.notes.showonlogin.info", "Whether one's notes should be shown on login. Requires the player to have simplenotes.see.self.notes.");
}
private void convertToNewConfig() {
// Convert old configuration keys to new keys
-
- // Convert from old config version 0 to new config version 1
- if(this.getString("config-version") == null || Integer.valueOf(this.getString("config-version")) < 1) {
- convertToNewAddress("settings.test-command-response.value", "settings.test-command.response.value", true);
- convertToNewAddress("settings.test-command.enabled", "settings.test-command.enabled.value", true);
- }
+ // Currently unused
+ ;
}
/**
diff --git a/src/main/java/org/retrohaven/mc/notes/NoteListener.java b/src/main/java/org/retrohaven/mc/notes/NoteListener.java
new file mode 100644
index 0000000..bc6a462
--- /dev/null
+++ b/src/main/java/org/retrohaven/mc/notes/NoteListener.java
@@ -0,0 +1,28 @@
+package org.retrohaven.mc.notes;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerListener;
+import org.retrohaven.mc.notes.commands.NoteCommand;
+
+public class NoteListener extends PlayerListener {
+ private NotePlugin plugin;
+ private NoteConfig config;
+
+ // Constructor to link the plugin instance
+ public NoteListener(NotePlugin plugin) {
+ this.plugin = plugin;
+ this.config = plugin.getConfig();
+ }
+
+ @Override
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ if ((!player.hasPermission("simplenotes.see.self.warns") && !player.isOp() && !config.getConfigBoolean("settings.warns.showonlogin.value"))
+ && (!player.hasPermission("simplenotes.see.self.notes") && !player.isOp() && !config.getConfigBoolean("settings.notes.showonlogin.value"))) {
+ return;
+ }
+ NoteCommand commands = new NoteCommand(plugin);
+ boolean b = commands.NoteList(player, new String[] {""}, true);
+ }
+}
diff --git a/src/main/java/org/retrohaven/mc/notes/NotePlugin.java b/src/main/java/org/retrohaven/mc/notes/NotePlugin.java
new file mode 100644
index 0000000..e11cbf4
--- /dev/null
+++ b/src/main/java/org/retrohaven/mc/notes/NotePlugin.java
@@ -0,0 +1,59 @@
+package org.retrohaven.mc.notes;
+
+import org.bukkit.Bukkit;
+import org.bukkit.event.Event;
+import org.bukkit.plugin.PluginDescriptionFile;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.retrohaven.mc.notes.commands.NoteCommand;
+
+import java.io.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class NotePlugin extends JavaPlugin {
+ private JavaPlugin plugin;
+ private Logger log;
+ private String pluginName;
+ private PluginDescriptionFile pdf;
+
+ private NoteConfig configuration;
+
+ @Override
+ public void onEnable() {
+ plugin = this;
+ log = this.getServer().getLogger();
+ pdf = this.getDescription();
+ pluginName = pdf.getName();
+ log.info("[" + pluginName + "] is loading, version: " + pdf.getVersion());
+
+ // Load configuration
+ configuration = new NoteConfig(this, new File(getDataFolder(), "config.yml")); // Load the configuration file from the plugin's data folder
+
+ // Register the command and the aliases
+ getCommand("note").setExecutor(new NoteCommand(this));
+
+ // Register the listeners
+ if (configuration.getConfigBoolean("settings.warns.showonlogin.value") || configuration.getConfigBoolean("settings.notes.showonlogin.value")) {
+ NoteListener listener = new NoteListener(this);
+ getServer().getPluginManager().registerEvent(Event.Type.PLAYER_JOIN, listener, Event.Priority.Monitor, this);
+ }
+
+ log.info("[" + pluginName + "] Plugin loaded!");
+ }
+
+ @Override
+ public void onDisable() {
+ // Save configuration
+ //config.save(); // Save the configuration file to disk. This should only be necessary if the configuration cam be modified during runtime.
+
+ log.info("[" + pluginName + "] Plugin unloaded!");
+ }
+
+ public void logger(Level level, String message) {
+ Bukkit.getLogger().log(level, "[" + plugin.getDescription().getName() + "] " + message);
+ }
+
+ public NoteConfig getConfig() {
+ return configuration;
+ }
+}
diff --git a/src/main/java/org/retrohaven/mc/notes/commands/NoteCommand.java b/src/main/java/org/retrohaven/mc/notes/commands/NoteCommand.java
new file mode 100644
index 0000000..029952b
--- /dev/null
+++ b/src/main/java/org/retrohaven/mc/notes/commands/NoteCommand.java
@@ -0,0 +1,492 @@
+package org.retrohaven.mc.notes.commands;
+
+import com.opencsv.CSVParserBuilder;
+import com.opencsv.CSVReader;
+import com.opencsv.CSVReaderBuilder;
+import com.opencsv.CSVWriter;
+import com.opencsv.exceptions.CsvException;
+import com.opencsv.exceptions.CsvValidationException;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.retrohaven.mc.notes.NoteConfig;
+import org.retrohaven.mc.notes.NotePlugin;
+import org.json.JSONObject;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+public class NoteCommand implements CommandExecutor {
+
+ private final NotePlugin plugin;
+ private final NoteConfig config;
+ private final String errorColorCode = "§e";
+ private final String permissionColorCode = "§3";
+
+ public NoteCommand(NotePlugin plugin) {
+ this.plugin = plugin;
+ this.config = plugin.getConfig();
+ }
+
+ public boolean CheckFileExists(File file) {
+ if (file.exists()) {
+ return true;
+ } else {
+ // we create the folders above
+ List parentListInit = Arrays.asList(file.getParentFile());
+ List parentList = new ArrayList(parentListInit);
+ while (parentList.get(parentList.size() - 1).getParentFile() != null && !parentList.get(parentList.size() - 1).getParentFile().exists()) {
+ parentList.add(parentList.get(parentList.size() - 1).getParentFile());
+ }
+ for (int i = parentList.size()-1; i > -1; i--) {
+ // create all parents, going through the list in reverse
+ try {
+ boolean b = parentList.get(i).mkdirs();
+ if (!b) {
+ throw new RuntimeException();
+ }
+ } catch (Exception e) {
+ // probably a permission issue, failed to create the dirs
+ return false;
+ }
+ }
+ // then we return false, now that all parents exist.
+ return false;
+ }
+ }
+
+ // caching for UUIDs, to prevent unnecessary requests to the API
+ private final Cache uuidCache = CacheBuilder.newBuilder()
+ .expireAfterWrite(30, TimeUnit.DAYS)
+ .build();
+ public Object getUUIDFromName(String playerName) {
+ try {
+ return uuidCache.get(playerName, () -> {
+ try {
+ String urlString = "https://api.mojang.com/users/profiles/minecraft/" + playerName;
+ URL url = URI.create(urlString).toURL();
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+
+ int responseCode = conn.getResponseCode();
+ BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ String inputLine;
+ StringBuilder content = new StringBuilder();
+
+ while ((inputLine = in.readLine()) != null) {
+ content.append(inputLine);
+ }
+
+ in.close();
+ conn.disconnect();
+
+ JSONObject jsonobj = new JSONObject(content.toString());
+ if (jsonobj.has("errorMessage")) {
+ // couldn't find a profile with this name
+ return null;
+ }
+ return jsonobj.getString("id");
+ } catch (Exception e) {
+ // probably network error. nothing we can do.
+ // unlikely to happen realistically though
+ return null;
+ }
+ });
+ } catch (ExecutionException e) {
+ return null;
+ }
+ }
+
+ public boolean NoteAdd(CommandSender sender, String[] args, String type) {
+ if (!sender.hasPermission("simplenotes.addnotes") && !sender.isOp()) {
+ sender.sendMessage(permissionColorCode+"You do not have permission to use this subcommand.");
+ return false;
+ }
+ if (args.length == 1) {
+ sender.sendMessage(errorColorCode+"Please provide a player name.");
+ return false;
+ }
+ String requestSubject = args[1];
+ Object subjectUUID = this.getUUIDFromName(requestSubject);
+ if (subjectUUID == null) {
+ sender.sendMessage(errorColorCode+"Player doesn't exist.");
+ return false;
+ }
+ subjectUUID = subjectUUID.toString();
+
+ String filename = plugin.getDataFolder()+File.separator+"data"+File.separator+subjectUUID+".csv";
+ File dataFile = new File(filename);
+ if (!this.CheckFileExists(dataFile)) {
+ try {
+ if (!dataFile.createNewFile()) {
+ throw new IOException("this didn't work");
+ }
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to create "+dataFile.getAbsolutePath());
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ }
+
+ // the following is copied from opencsv's documentation
+ CSVReader reader = null;
+ try {
+ reader = new CSVReaderBuilder(new FileReader(filename)).build();
+ } catch (FileNotFoundException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ String [] nextLine;
+ int i = 1;
+ try {
+ while (true) {
+ if ((nextLine = reader.readNext()) != null) {
+ i = Integer.parseInt(nextLine[0]) + 1;
+ } else {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ } catch (CsvValidationException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ reader.close();
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+
+ Integer id = i; // we use the last ID + 1
+ String id_str = id.toString();
+ type = type.substring(0,4).toUpperCase(); // we keep the first 4 char, so that we have note or warn
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ Date date = new Date();
+ String dateStr = dateFormat.format(date);
+
+ String author;
+ if (sender instanceof Player) {
+ author = sender.getName();
+ } else {
+ author = "CONSOLE";
+ }
+ try {
+ CSVWriter writer = new CSVWriter(new FileWriter(filename, true),
+ CSVWriter.DEFAULT_SEPARATOR,
+ '\"',
+ CSVWriter.DEFAULT_ESCAPE_CHARACTER,
+ CSVWriter.DEFAULT_LINE_END);
+ String[] line = {
+ id_str,
+ type,
+ dateStr,
+ author,
+ String.join(" ",
+ Arrays.copyOfRange(args, 2, args.length)
+ )
+ };
+ writer.writeNext(line, true);
+ writer.close();
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ // success!
+ sender.sendMessage(type.charAt(0) + type.substring(1).toLowerCase()+" added.");
+ return true;
+ }
+
+ public boolean NoteRemove(CommandSender sender, String[] args) {
+ if (!sender.hasPermission("simplenotes.removenotes") && !sender.isOp()) {
+ sender.sendMessage(permissionColorCode+"You do not have permission to use this subcommand.");
+ return false;
+ }
+ if (args.length <= 2) {
+ sender.sendMessage(errorColorCode+"Please provide a player name and an id.");
+ return true;
+ }
+
+ String requestSubject = args[1];
+ Object subjectUUID = this.getUUIDFromName(requestSubject);
+ if (subjectUUID == null) {
+ sender.sendMessage(errorColorCode+"Player doesn't exist.");
+ return true;
+ }
+ subjectUUID = subjectUUID.toString();
+
+ String filename = plugin.getDataFolder()+File.separator+"data"+File.separator+subjectUUID+".csv";
+ File dataFile = new File(filename);
+ if (!this.CheckFileExists(dataFile)) {
+ // we do not try to create the file, since if it doesn't exist, the warn/note doesn't exist either
+ sender.sendMessage(errorColorCode+"Note/warn not found.");
+ return false;
+ }
+
+ // We dump the entire CSV file into allElements
+ CSVReader reader = null;
+ try {
+ reader = new CSVReaderBuilder(new FileReader(filename))
+ .withCSVParser(new CSVParserBuilder().build())
+ .build();
+ } catch (FileNotFoundException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ List allElements = null;
+ try {
+ allElements = reader.readAll();
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ } catch (CsvException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ reader.close();
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+
+ /*
+ we delete the file, since we recreate the contents entirely later.
+ this is the recommended way to remove a specific line in a CSV file
+ */
+ try {
+ if (!dataFile.delete() || !dataFile.createNewFile()) {
+ // something went wrong. we do not do data loss here
+ // so everything is printed to console
+ this.plugin.logger(Level.INFO,"Content of "+filename+":");
+ this.plugin.logger(Level.INFO,allElements.stream().map(Arrays::toString).reduce((a, b) -> a + "\n" + b).orElse(""));
+ throw new IOException("this didn't work");
+ }
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ int rowNumber = -1;
+ for (int i = 0; i < allElements.size(); i++) {
+ // we iterate through the list, and check the id of each row
+ if (Integer.parseInt(allElements.get(i)[0]) == Integer.parseInt(args[2])) {
+ rowNumber = i;
+ break;
+ }
+ }
+ if (rowNumber == -1) {
+ sender.sendMessage(errorColorCode+"Note/warn not found.");
+ return false;
+ }
+ allElements.remove(rowNumber);
+
+ CSVWriter writer = null;
+ try {
+ writer = new CSVWriter(new FileWriter(filename),
+ CSVWriter.DEFAULT_SEPARATOR,
+ '\"',
+ CSVWriter.DEFAULT_ESCAPE_CHARACTER,
+ CSVWriter.DEFAULT_LINE_END);
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ writer.writeAll(allElements);
+ try {
+ writer.close();
+ } catch (IOException e) {
+ sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ // success!
+ sender.sendMessage("Note/warn removed.");
+ return true;
+ }
+
+ public boolean NoteList(CommandSender sender, String[] args, boolean isListener) {
+ String RequestSubject;
+ if (args.length == 1) {
+ if (!(sender instanceof Player)) {
+ sender.sendMessage("You need to provide a player name when running this in the console.");
+ return true;
+ }
+ RequestSubject = sender.getName();
+ }
+ else RequestSubject = args[1];
+ if (RequestSubject.equals(sender.getName()) && !sender.hasPermission("simplenotes.see.self.notes") && !sender.hasPermission("simplenotes.see.self.warns") && !sender.isOp()) {
+ if (!isListener) sender.sendMessage(permissionColorCode+"You do not have permission to check your own notes/warns.");
+ return true;
+ }
+ if (!RequestSubject.equals(sender.getName()) && !sender.hasPermission("simplenotes.see.others.notes") && !sender.hasPermission("simplenotes.see.others.warns") && !sender.isOp()) {
+ if (!isListener) sender.sendMessage(permissionColorCode+"You do not have permission to check other people's notes/warns.");
+ return true;
+ }
+
+ Object subjectUUID = this.getUUIDFromName(RequestSubject);
+ if (subjectUUID == null) {
+ if (!isListener) sender.sendMessage(errorColorCode+"Player doesn't exist.");
+ return true;
+ }
+ subjectUUID = subjectUUID.toString();
+
+ String filename = plugin.getDataFolder()+File.separator+"data"+File.separator+subjectUUID+".csv";
+ File dataFile = new File(filename);
+ if (!this.CheckFileExists(dataFile)) {
+ // we do not try to create the file, since if it doesn't exist, there are no warns/notes
+ if (!isListener) sender.sendMessage(errorColorCode+"No notes or warns to show.");
+ return true;
+ }
+
+ CSVReader reader = null;
+ try {
+ reader = new CSVReaderBuilder(new FileReader(filename))
+ .withCSVParser(new CSVParserBuilder().build())
+ .build();
+ } catch (FileNotFoundException e) {
+ if (!isListener) sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+ List allElements = null;
+ try {
+ allElements = reader.readAll();
+ } catch (IOException e) {
+ if (!isListener) sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ } catch (CsvException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ reader.close();
+ } catch (IOException e) {
+ if (!isListener) sender.sendMessage(errorColorCode+"Internal error. Ask your local sys-admin to check the console.");
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] ERROR: Failed to access "+filename);
+ this.plugin.logger(Level.SEVERE,"["+this.plugin.getDescription().getName()+"] Check the permissions of the folders.");
+ return false;
+ }
+
+ int shownCounter = 0;
+ for (String[] line : allElements) {
+ // some transformation first
+ if (Objects.equals(line[1], "NOTE")) line[1] = "§eNOTE";
+ if (Objects.equals(line[1], "WARN")) line[1] = "§cWARN";
+ line[2] = "§9"+line[2];
+
+ if (!RequestSubject.equals(sender.getName()) && (sender.hasPermission("simplenotes.see.others.notes") || sender.isOp()) && Objects.equals(line[1].substring(line[1].length() -4), "NOTE")) {
+ shownCounter = shownCounter + 1;
+ sender.sendMessage("§8| " + String.join(" §8|§r ", line));
+ } else if (RequestSubject.equals(sender.getName()) && (sender.hasPermission("simplenotes.see.self.notes") || sender.isOp()) && Objects.equals(line[1].substring(line[1].length() -4), "NOTE") && (!isListener || config.getConfigBoolean("settings.notes.showonlogin.value"))) {
+ shownCounter = shownCounter + 1;
+ sender.sendMessage("§8| " + String.join(" §8|§r ", line));
+ } else if (!RequestSubject.equals(sender.getName()) && (sender.hasPermission("simplenotes.see.others.warns") || sender.isOp()) && Objects.equals(line[1].substring(line[1].length() -4), "WARN")) {
+ shownCounter = shownCounter + 1;
+ sender.sendMessage("§8| " + String.join(" §8|§r ", line));
+ } else if (RequestSubject.equals(sender.getName()) && (sender.hasPermission("simplenotes.see.self.warns") || sender.isOp()) && Objects.equals(line[1].substring(line[1].length() -4), "WARN") && (!isListener || config.getConfigBoolean("settings.warns.showonlogin.value"))) {
+ shownCounter = shownCounter + 1;
+ sender.sendMessage("§8| " + String.join(" §8|§r ", line));
+ }
+ }
+ if (shownCounter == 0) {
+ sender.sendMessage("No notes or warns to show.");
+ }
+ return true;
+ }
+
+ // the following should be used in all cases.
+ // the only reason we have isListener is because of the NoteListener
+ public boolean NoteList(CommandSender sender, String[] args) {
+ return NoteList(sender, args, false);
+ }
+
+ public boolean NoteHelp(CommandSender sender, String alias) {
+ int removedCommands = 4;
+ sender.sendMessage("=== Command list ===");
+ sender.sendMessage("- /"+alias+" help: show this");
+ if ((sender.hasPermission("simplenotes.see.self.notes") && sender.hasPermission("simplenotes.see.self.warns")) || sender.isOp()) {
+ sender.sendMessage("- /"+alias+" list: show your notes/warns");
+ removedCommands = removedCommands - 1;
+ } else if (sender.hasPermission("simplenotes.see.self.notes")) {
+ sender.sendMessage("- /"+alias+" list: show your notes");
+ removedCommands = removedCommands - 1;
+ } else if (sender.hasPermission("simplenotes.see.self.warns")) {
+ sender.sendMessage("- /"+alias+" list: show your warns");
+ removedCommands = removedCommands - 1;
+ }
+ if (sender.hasPermission("simplenotes.see.others.notes") || sender.hasPermission("simplenotes.see.others.warns") || sender.isOp()) {
+ sender.sendMessage("- /"+alias+" list : show another player's notes");
+ removedCommands = removedCommands - 1;
+ }
+ if (sender.hasPermission("simplenotes.addnotes") || sender.isOp()) {
+ sender.sendMessage("- /"+alias+" add : add a "+alias.substring(0, 4));
+ removedCommands = removedCommands - 1;
+ }
+ if (sender.hasPermission("simplenotes.removenotes") || sender.isOp()) {
+ sender.sendMessage("- /"+alias+" remove : remove a note/warn (you can get the id via /"+alias+" list!)");
+ removedCommands = removedCommands - 1;
+ }
+ if (removedCommands > 0) {
+ sender.sendMessage(permissionColorCode+"You do not have access to any other subcommand.");
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ // Check if the command is enabled
+ Boolean isEnabled = config.getConfigBoolean("settings.plugin.enabled.value");
+ if (!isEnabled) {
+ sender.sendMessage("This command is currently disabled. Please check the config.");
+ return true;
+ }
+ // objects.equals is used to shut the IDE. it's not gonna be null let's be realistic.
+ if (args.length == 0 || Objects.equals(args[0], "help")) {
+ return this.NoteHelp(sender, label);
+ } else if (Objects.equals(args[0], "add")) {
+ // label is needed because of the differentiation between notes and warnings
+ return this.NoteAdd(sender, args, label);
+ } else if (Objects.equals(args[0], "remove")) {
+ return this.NoteRemove(sender, args);
+ } else if (Objects.equals(args[0], "list")) {
+ return this.NoteList(sender, args);
+ } else {
+ sender.sendMessage(errorColorCode+"Unrecognized argument.");
+ return this.NoteHelp(sender, label);
+ }
+ }
+}
diff --git a/src/main/java/org/retromc/templateplugin/TemplateListener.java b/src/main/java/org/retromc/templateplugin/TemplateListener.java
deleted file mode 100644
index 846827e..0000000
--- a/src/main/java/org/retromc/templateplugin/TemplateListener.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.retromc.templateplugin;
-
-import org.bukkit.event.Event;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerJoinEvent;
-
-public class TemplateListener implements Listener {
- private TemplatePlugin plugin;
- private TemplateConfig config;
-
- // Constructor to link the plugin instance
- public TemplateListener(TemplatePlugin plugin) {
- this.plugin = plugin;
- this.config = plugin.getConfig();
- }
-
- // Handle player join event
- @EventHandler(priority = Event.Priority.Normal)
- public void onPlayerJoin(PlayerJoinEvent event) {
- // Example action: Send a welcome message configured in the plugin's configuration file to the player when they join the server
-
- String welcomeMessage = config.getConfigString("settings.welcome-message.value");
- welcomeMessage = welcomeMessage.replace("%player%", event.getPlayer().getName());
-
- event.getPlayer().sendMessage(welcomeMessage);
- }
-}
diff --git a/src/main/java/org/retromc/templateplugin/TemplatePlugin.java b/src/main/java/org/retromc/templateplugin/TemplatePlugin.java
deleted file mode 100644
index 5f06a40..0000000
--- a/src/main/java/org/retromc/templateplugin/TemplatePlugin.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.retromc.templateplugin;
-
-import org.bukkit.Bukkit;
-import org.bukkit.plugin.PluginDescriptionFile;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.retromc.templateplugin.commands.TemplateTestCommand;
-
-import java.io.File;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class TemplatePlugin extends JavaPlugin {
- private JavaPlugin plugin;
- private Logger log;
- private String pluginName;
- private PluginDescriptionFile pdf;
-
- private TemplateConfig configuration;
-
-
- @Override
- public void onEnable() {
- plugin = this;
- log = this.getServer().getLogger();
- pdf = this.getDescription();
- pluginName = pdf.getName();
- log.info("[" + pluginName + "] Is Loading, Version: " + pdf.getVersion());
-
- // Load configuration
- configuration = new TemplateConfig(this, new File(getDataFolder(), "config.yml")); // Load the configuration file from the plugin's data folder
-
- // Register the commands
- getCommand("testcommand").setExecutor(new TemplateTestCommand(this));
-
- // Register the listeners
- TemplateListener listener = new TemplateListener(this);
- getServer().getPluginManager().registerEvents(listener, this);
-
- log.info("[" + pluginName + "] Is Loaded, Version: " + pdf.getVersion());
- }
-
- @Override
- public void onDisable() {
- log.info("[" + pluginName + "] Is Unloading, Version: " + pdf.getVersion());
-
- // Save configuration
- //config.save(); // Save the configuration file to disk. This should only be necessary if the configuration cam be modified during runtime.
-
- log.info("[" + pluginName + "] Is Unloaded, Version: " + pdf.getVersion());
- }
-
- public void logger(Level level, String message) {
- Bukkit.getLogger().log(level, "[" + plugin.getDescription().getName() + "] " + message);
- }
-
- public TemplateConfig getConfig() {
- return configuration;
- }
-}
diff --git a/src/main/java/org/retromc/templateplugin/commands/TemplateTestCommand.java b/src/main/java/org/retromc/templateplugin/commands/TemplateTestCommand.java
deleted file mode 100644
index b23c08e..0000000
--- a/src/main/java/org/retromc/templateplugin/commands/TemplateTestCommand.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.retromc.templateplugin.commands;
-
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-import org.retromc.templateplugin.TemplateConfig;
-import org.retromc.templateplugin.TemplatePlugin;
-
-public class TemplateTestCommand implements CommandExecutor {
-
- private final TemplatePlugin plugin;
-
- private final TemplateConfig config;
-
- public TemplateTestCommand(TemplatePlugin plugin) {
- this.plugin = plugin;
- this.config = plugin.getConfig();
- }
-
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- // Check if the sender is a player
- if (!(sender instanceof Player)) {
- sender.sendMessage("This command can only be executed by players.");
- return true;
- }
-
- if (!sender.hasPermission("myplugin.testcommand") && !sender.isOp()) {
- sender.sendMessage("You do not have permission to execute this command.");
- return true;
- }
-
- // Check if the command is enabled
- Boolean isEnabled = config.getConfigBoolean("settings.test-command.enabled.value");
- if (!isEnabled) {
- sender.sendMessage("This command is currently disabled.");
- return true;
- }
-
- // Get the response message from the config
- String response = config.getConfigString("settings.test-command.response.value");
-
- // Send the response message to the player
- sender.sendMessage(response);
- return true;
- }
-}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index ab373a5..09d1590 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,22 +1,52 @@
name: ${project.name}
description: ${project.description}
-main: org.retromc.templateplugin.TemplatePlugin
+main: org.retrohaven.mc.notes.NotePlugin
version: ${project.version}
authors:
- - JohnyMuffin
-softdepend:
- - Essentials
+ - eleanorsilly
commands:
- testcommand:
- description: A test command for the plugin.
- usage: / [args]
- aliases: [tc]
- permission: myplugin.testcommand
- permission-message: You do not have permission to use this command.
+ note:
+ description: Add, remove or see notes or warnings. /note help for more details.
+ usage: /note [subcommand] [player] [additional arguments]
+ aliases: [notes, warn, warns, warning, warnings]
permissions:
- myplugin.*:
- description: Gives access to all myplugin commands.
+ simplenotes.*:
+ description: Gives access to all commands of the plugin. Should be used sparingly, e.g. for admins.
children:
- myplugin.testcommand: true
\ No newline at end of file
+ simplenotes.addnotes: true
+ simplenotes.removenotes: true
+ simplenotes.see.self.notes: true
+ simplenotes.see.self.warns: true
+ simplenotes.see.others.notes: true
+ simplenotes.see.others.warns: true
+ simplenotes.addnotes:
+ description: Gives the permission to add notes/warnings.
+ simplenotes.removenotes:
+ description: Gives the permission to remove notes/warnings.
+ simplenotes.see.*:
+ description: Gives the permission to see the notes/warnings of all players.
+ children:
+ simplenotes.see.self.notes: true
+ simplenotes.see.self.warns: true
+ simplenotes.see.others.notes: true
+ simplenotes.see.others.warns: true
+ simplenotes.see.self.*:
+ description: Gives the permission to see your own notes/warnings
+ children:
+ simplenotes.see.self.notes: true
+ simplenotes.see.self.warns: true
+ simplenotes.see.self.notes:
+ description: Gives the permission to see your own notes
+ simplenotes.see.self.warns:
+ description: Gives the permission to see your own warnings
+ simplenotes.see.others.*:
+ description: "Gives the permission to see other people's notes/warnings"
+ children:
+ simplenotes.see.others.notes: true
+ simplenotes.see.others.warns: true
+ simplenotes.see.others.notes:
+ description: "Gives the permission to see other people's notes"
+ simplenotes.see.others.warns:
+ description: "Gives the permission to see other people's warnings"