...
 
Commits (9)
Changelog
=========
v2.1 (2019/07/15)
-----------------
* Added persistent map of player status
* Added message if player is already enabled/disabled
* Added command aliases: `bgt`, `bukkitgreentext`
* Re-write command parser to be more efficient
v2.0 (2019/07/05)
-----------------
* Initial (re-)release
......@@ -40,7 +40,7 @@ for `:` and `_` are specified in the config file.
Custom exceptions are allowed as well, naturally.
Greentext can be either enabled or disabled upon startup.
In the case the parameter `enabled-on-startup` is set true,
In the case the parameter `enabled-by-default` is set true,
each player must manually enable the plugin with `/greentext on`.
Additionally, orangetext, an obscure feature
[used probably once](https://www.reddit.com/r/BannedFrom4chan/comments/221co5/),
......@@ -67,7 +67,6 @@ when I debug my subpar code.
### Some to-do
* Make HashMap of player toggle persistent (via YAML file?)
* Separate exceptions for orangetext and greentext
* Toggle orangetext separately from greentext
* Support for reply reference (`>>31337`) and board reference (`>>>/out/`)
......
......@@ -6,7 +6,7 @@
<name>BukkitGreentext</name>
<description>Add 4chan-style quoting to Minecraft server chat</description>
<version>2.0</version>
<version>2.1</version>
<url>http://www.gitlab.com/ssterling/bukkitgreentext/</url>
<properties>
......
......@@ -32,7 +32,7 @@ import org.bukkit.ChatColor;
/**
* @author Seth Price <ssterling AT firemail DOT cc>
* @version 2.0
* @version 2.1
* @since 1.0
*/
public class BgtCommandExecutor implements CommandExecutor
......@@ -47,27 +47,68 @@ public class BgtCommandExecutor implements CommandExecutor
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
{
/* Convert CommandSender to Player type if sender is indeed a player */
Player player;
/* Why is all this better than a `switch' statement or something, you may ask?
* It probably isn't, but it at least saves my sanity by doing a lot of the
* processing all up here instead of copy-pasted several times throughout
* a more computationally consuming cascade of `if-then' clauses. */
/* NOTE: the reason a lot of the following variables are initialised to `null',
* `false', empty strings, etc. is because Maven screams unless they're
* explicitly set with a value, even if they're never used. */
/* Determine whether command sender is a player; if so, convert to Player type */
Player player = null;
boolean is_player = false;
if (sender instanceof Player) {
player = (Player) sender;
} else {
/* Maven won't compile unless I explicitly initialise this */
player = null;
is_player = true;
}
/* For weird processing later */
boolean arg_enabled;
String enabled_disabled;
/* Determine whether sender sent too many arguments; if so, print usage */
if (args.length > 2) {
return false;
}
/* No arguments = same as `/greentext toggle' */
if (args.length == 0) {
/* Console can't use greentext hence cannot toggle it; exit */
if (!(sender instanceof Player)) {
sender.sendMessage("Console can only toggle greentext for players.");
return true;
/* Determine whether first argument (if present) is `on' or `off'; also,
* set a string to `enabled' or `disabled' appropriately */
boolean arg_enabled = false;
String enabled_disabled = "";
if (args.length > 0) {
if (args[0].equalsIgnoreCase("on")) {
arg_enabled = true;
enabled_disabled = "enabled";
} else if (args[0].equalsIgnoreCase("off")) {
/* `arg_enabled' is already false */
enabled_disabled = "disabled";
} else {
/* Incorrect syntax; print usage */
return false;
}
}
/* Determine whether second argument (if present) is either `global'
* or a valid player name (hopefully no one named `global' is online) */
boolean is_global = false;
Player target_player = null;
if (args.length == 2) {
if (args[1].equalsIgnoreCase("global")) {
is_global = true;
} else {
/* `is_global' is already false */
target_player = plugin.getServer().getPlayer(args[1]);
/* Player is either offline or invalid; exit */
if (target_player == null) {
sender.sendMessage("Player " + args[1] + " is nonexistent or offline.");
return true;
}
}
}
/*-------- actual command processing begins here --------*/
/* If a player sends `/greentext' with no arguments, toggle greentext on/off */
if (args.length == 0 && is_player) {
if (plugin.playerIsEnabled(player)) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " disabled.");
plugin.playerSetEnabled(player, false);
......@@ -76,74 +117,89 @@ public class BgtCommandExecutor implements CommandExecutor
plugin.playerSetEnabled(player, true);
}
return true;
} else if (args.length > 2) {
/* Too many arguments */
return false;
}
/* If a player sends `/greentext <on|off>' with no target, set the status
* for that player to the value specified in the command */
else if (args.length == 1 && is_player) {
/* If player doesn't have permission, exit */
if (!(player.hasPermission("greentext.toggle"))) {
sender.sendMessage(ChatColor.RED + "Insufficient permission to toggle greentext.");
return true;
}
/* This (as opposed to separate clauses for `on' and `off') involves some weird processing,
* as you can see here, but it saves me from re-writing a whole lot of code in the end. */
if (args[0].equalsIgnoreCase("on") || args[0].equalsIgnoreCase("off")) {
/* Player already has status set to value; ignore */
if (plugin.playerIsEnabled(player) == arg_enabled) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " already " + enabled_disabled + ".");
return true;
}
/* The spaces in `enabled_disabled' are necessary; see the concatenation below. */
if (args[0].equalsIgnoreCase("on")) {
arg_enabled = true;
enabled_disabled = " enabled";
} else {
arg_enabled = false;
enabled_disabled = " disabled";
/* Method `playerSetEnabled()' already logs to the console,
* so only pretty-print to players */
if (is_player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " " + enabled_disabled + ".");
}
plugin.playerSetEnabled(player, arg_enabled);
return true;
}
/* If either player or console sends `/greentext <on|off> player', set the status
* for that player to the value specified in the command */
else if (args.length == 2 && !(is_global)) {
/* If sender is player and they doesn't have permission, exit */
if (is_player && !(player.hasPermission("greentext.toggle.others"))) {
sender.sendMessage(ChatColor.RED + "Insufficient permission to toggle greentext for others.");
return true;
}
if (args.length == 2) {
if (!(sender instanceof Player) || player.hasPermission("greentext.toggle.others")) {
/* Sorry for the guy whose IGN is `global' */
if (args[1].equalsIgnoreCase("global")) {
if (sender instanceof Player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + enabled_disabled + " globally.");
}
plugin.globalSetEnabled(arg_enabled);
return true;
} else {
Player targetPlayer = plugin.getServer().getPlayer(args[1]);
if (targetPlayer != null) {
if (sender instanceof Player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + enabled_disabled + " for player " + targetPlayer.getName() + ".");
}
plugin.playerSetEnabled(targetPlayer, arg_enabled);
return true;
} else {
sender.sendMessage("Player " + args[1] + " is nonexistent or offline.");
return true;
}
}
/* Player already has status set to value; ignore */
if (plugin.playerIsEnabled(target_player) == arg_enabled) {
/* Again, to keep consistency with the uncoloured console messages */
if (is_player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " already " + enabled_disabled + " for player " + target_player.getName() + ".");
} else {
sender.sendMessage(ChatColor.RED + "Insufficient permission to toggle greentext for others.");
return true;
}
} else if (args.length == 1) {
/* Console can't use greentext hence cannot toggle it; exit */
if (!(sender instanceof Player)) {
sender.sendMessage("Console can only toggle greentext for players.");
return true;
sender.sendMessage("Greentext already " + enabled_disabled + " for player " + target_player.getName());
}
return true;
}
if (player.hasPermission("greentext.toggle")) {
if (sender instanceof Player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + enabled_disabled + ".");
}
plugin.playerSetEnabled(player, arg_enabled);
return true;
/* Method `globalSetEnabled()' already logs to the console,
* so only pretty-print to players */
if (is_player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " " + enabled_disabled + " for player " + target_player.getName() + ".");
}
plugin.playerSetEnabled(target_player, arg_enabled);
return true;
}
/* If either player or console sends `/greentext <on|off> global', or console sends
* `/greentext <on|off>', set the global status to the value specified in the command */
else if ((args.length == 1 && !(is_player)) || is_global) {
/* If sender is player and they doesn't have permission, exit */
if (is_player && !(player.hasPermission("greentext.toggle.others"))) {
sender.sendMessage(ChatColor.RED + "Insufficient permission to toggle greentext for others.");
return true;
}
/* Player already has status set to value; ignore */
if (plugin.globalIsEnabled() == arg_enabled) {
/* Yet again, to keep consistency with the uncoloured console messages */
if (is_player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " already " + enabled_disabled + " globally.");
} else {
sender.sendMessage(ChatColor.RED + "Insufficient permission to toggle greentext.");
return true;
sender.sendMessage("Greentext already " + enabled_disabled + " globally");
}
} else {
/* Too many arguments */
return false;
return true;
}
/* Method `globalSetEnabled()' already logs to the console,
* so only pretty-print to players */
if (is_player) {
sender.sendMessage(ChatColor.GREEN + ">greentext" + ChatColor.RESET + " " + enabled_disabled + " globally.");
}
plugin.globalSetEnabled(arg_enabled);
return true;
}
/* Argument is invalid */
/* If the command hasn't returned by now, there's been some sort of error;
* just print usage and exit */
return false;
}
}
......@@ -26,11 +26,13 @@ package net.ssterling.BukkitGreentext;
import java.util.HashMap;
import java.util.UUID;
import java.io.File;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.ChatColor;
import org.bukkit.event.player.AsyncPlayerChatEvent;
......@@ -66,6 +68,18 @@ public class BukkitGreentext extends JavaPlugin
*/
private static boolean is_debug;
/**
* The file pointer for the persistent hashmap.
*/
private static File persistent_hashmap_file;
/**
* The persistent hashmap file as a Bukkit configuration file.
*
* @see org.bukkit.configuration.file.FileConfiguration
*/
private static FileConfiguration persistent_hashmap;
private static PluginManager pm;
private static PluginDescriptionFile pdf;
private static Metrics metrics;
......@@ -94,9 +108,47 @@ public class BukkitGreentext extends JavaPlugin
/* TODO: change logger level */
}
/* ...actually, go back and write the `debug' key to the config file
* if it doesn't exist (was added in version 2.0) */
if (!(config.isSet("debug"))) {
getLogger().config("Updating configuration file with new `debug' key...");
/* If it didn't exist, `is_debug' holds the default value
* since `saveDefaultConfig()' was called */
config.set("debug", is_debug);
try {
this.saveConfig();
} catch (Throwable ex) {
getLogger().warning("Failed to update configuration file.");
ex.printStackTrace();
}
}
getLogger().finest("Initialising player hashmap...");
enabled_for_player = new HashMap<UUID, Boolean>();
getLogger().info("Loading persistent hashmap from disk...");
try {
persistent_hashmap_file = new File(getDataFolder(), "playermap.yml");
/* Create the hashmap file if it doesn't exist */
if (!(persistent_hashmap_file.exists())) {
getLogger().fine("`playermap.yml' doesn't exist; creating");
saveResource("playermap.yml", false);
}
persistent_hashmap = new YamlConfiguration();
persistent_hashmap.load(persistent_hashmap_file);
/* Assign all the values to the in-memory HashMap */
for (String uuid : persistent_hashmap.getKeys(true)) {
/* TODO: overload method with UUID instead of Player object? */
enabled_for_player.put(UUID.fromString(uuid), persistent_hashmap.getBoolean(uuid));
}
} catch (Throwable ex) {
getLogger().warning("Failed to load persistent hashmap from disk. All users will default to `" + String.valueOf(enabled_by_default) + "' (value of `enabled_by_default').");
ex.printStackTrace();
}
getLogger().finest("Registering chat listener...");
try {
pm.registerEvents(new BgtChatListener(this), this);
......@@ -122,6 +174,23 @@ public class BukkitGreentext extends JavaPlugin
getLogger().info("Successfully initialised " + pdf.getName() + " v" + pdf.getVersion());
}
@Override
public void onDisable()
{
getLogger().info("Saving persistent hashmap to disk...");
for (UUID uuid : enabled_for_player.keySet()) {
persistent_hashmap.set(uuid.toString(), enabled_for_player.get(uuid));
}
try {
persistent_hashmap.save(persistent_hashmap_file);
} catch (Throwable ex) {
getLogger().warning("Failed to save persistent hashmap to disk.");
ex.printStackTrace();
}
}
/**
* Sets whether Greentext features are enabled for a given player.
*
......
......@@ -5,6 +5,4 @@ exceptions:
- ':'
- '_'
- '.'
# Enable this only if you want oodles of log messages
debug: false
name: BukkitGreentext
author: ssterling
version: 2.0
version: 2.1
description: Add 4chan-style quoting to Minecraft server chat
website: http://www.gitlab.com/ssterling/bukkitgreentext/
......@@ -17,6 +17,7 @@ commands:
/greentext
/greentext <on|off> [player]
/greentext <on|off> global
aliases: [bukkitgreentext, bgt]
permissions:
greentext.chat.*:
......