Made all of the ranks and chat totally configurable.

master
James T. Martin 2019-09-11 03:32:14 -07:00
parent fdab8a4eae
commit 05ca2a142a
16 changed files with 712 additions and 377 deletions

View File

@ -1 +1,197 @@
databaseFile: wasteland.sqlite3
databaseFile: wasteland.sqlite3
# Colors and decoration names must be written in ALL_CAPS_USING_UNDERLINES.
# For a list of valid formatting codes, see https://minecraft.gamepedia.com/Formatting_codes#Color_codes
# If `true`, chat will be prefixed with the player's Towny town tag if the town's tag is set.
# These settings have no effect if Towny is not enabled.
prefixTownTag: true
prefixTownTagColor: BLUE
# If `true`, the officer rank will also be used in regular chat, not just `/official` messages.
# Even if false, this can still be set per-player using `wasteland.chat.officer`.
preferOfficerRank: false
# If `true`, chat shows as `[Pvt] HuskFodder` instead of `Pvt HuskFodder`.
bracketChatRank: true
#
# enlistedRanksDefaultDecoration: <ColorCode> # optional
# enlistedRankDefaultDescription: <description> # optional, may reference the rank's number of {kills}.
# enlistedRanks:
# <rankID>: # the rank permission will be `wasteland.rank.<rankID>`.
# name: <full rank name> # optional, defaults to the rank ID
# abbreviation: <rank abbreviation> # optional, defaults to the name
# description: <rank description> # optional, defaults to `enlistedRankDefaultDescription`.
# succeeds: <rankID> # optional, the prior rank
# preferred: <bool> # optional, used to disambiguate multiple ranks
# # which both succeed the same rank.
# # Defaults to `true`.
# color: <ColorCode> # optional
# decoration: <ColorCode> # optional, defaults to `enlistedRanksDefaultDecoration`
# kills: <0+> # optional, but the rank won't be used without it
# ...
#
# The last listed rank that a player qualifies for will always be the one chosen.
# Players will automatically be promoted to any ranks they have enough kills for.
#
# The default ranks are based on those of the U.S. marines.
# The default colors are based on Terraria's rarity scheme (https://terraria.gamepedia.com/Rarity).
# You may wish to remove the unused alternative ranks.
enlistedRankDefaultDescription: "{kills} kills."
enlistedRanks:
# A default rank which is not part of the marines.
fodder:
# `name` is optional and defaults to the `title`.
name: Zombie Fodder
# `abbreviation` is optional and defaults to the rank ID.
abbreviation: Fodder
description: This person probably won't survive long.
color: GRAY
kills: 0
pvt:
name: Private
abbreviation: Pvt
succeeds: fodder
color: WHITE
kills: 100
pfc:
name: Private First Class
abbreviation: PFC
succeeds: pvt
color: BLUE
kills: 250
lcpl:
name: Lieutenant Corporal
abbreviation: LCpl
succeeds: pfc
color: GREEN
kills: 500
cpl:
name: Corporal
abbreviation: Cpl
succeeds: lcpl
color: RED
kills: 1000
sgt:
name: Sergeant
abbreviation: Sgt
succeeds: cpl
color: LIGHT_PURPLE
kills: 2500
ssgt:
name: Staff Sergeant
abbreviation: SSgt
succeeds: sgt
color: YELLOW
kills: 5000
gysgt:
name: Gunnery Sergeant
abbreviation: GySgt
succeeds: ssgt
color: AQUA
kills: 10000
# Alternative DARK_RED rank, currently unused.
fstsgt:
name: First Sergeant
abbreviation: 1stSgt
description: Unused alternative rank for {kills} kills.
succeeds: gysgt
preferred: false
color: DARK_RED
kills: 25000
msgt:
name: Master Sergeant
abbreviation: MSgt
succeeds: gysgt
color: DARK_RED
kills: 25000
# Alternative DARK_PURPLE rank, currently unused.
sgtmaj:
name: Sergeant Major
abbreviation: SgtMaj
description: Unused alternative rank for {kills} kills.
succeeds: fstsgt
color: DARK_PURPLE
kills: 50000
mgysgt:
name: Master Gunnery Sergeant
abbreviation: MGySgt
description: A survivor of the wasteland, with over {kills} kills.
succeeds: msgt
color: DARK_PURPLE
kills: 50000
# Officer ranks cannot be obtained by killing zombies.
# They must manually be set using permissions.
# Setting permissions for any rank also gives permissions for all previous ranks.
# You may also optionally specify a rank `description`,
# which will be listed to describe what a staff member of that rank does,
# or what a donor of that rank donated.
# It may be best to remove ranks that you are not using to avoid cluttering the ranks list.
officerRankDefaultDecoration: BOLD
officerRanks:
sndlt:
name: Second Lieutenant
abbreviation: 2ndLt
color: WHITE
fstlt:
name: First Lieutenant
abbreviation: 1stLt
succeeds: sndlt
color: BLUE
capt:
name: Captain
abbreviation: Capt
succeeds: fstlt
color: GREEN
maj:
name: Major
abbreviation: Maj
succeeds: capt
color: RED
ltcol:
name: Lieutenant Colonel
abbreviation: LtCol
succeeds: maj
color: LIGHT_PURPLE
col:
name: Colonel
abbreviation: Col
succeeds: ltcol
color: YELLOW
bgen:
name: Brigadier General
abbreviation: BGen
succeeds: col
color: AQUA
majgen:
name: Major General
abbreviation: MajGen
succeeds: bgen
color: DARK_RED
ltgen:
name: Lieutenant General
abbreviation: LtGen
succeeds: majgen
color: DARK_PURPLE
gen:
name: General
abbreviation: Gen
succeeds: ltgen
color: GOLD
# The rank used by the console when it makes an official message.
consoleRank:
name: General of the Armies # mandatory
abbreviation: GAS # Currently unused
description: The server console.
color: GOLD
decoration: BOLD

View File

@ -1,22 +0,0 @@
package me.jtmar.wasteland;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.TownyUniverse;
public class TownyDependency {
public static String getTownTagPrefix(Player player) {
try {
Resident resident = TownyUniverse.getDataSource().getResident(player.getName());
String tag = resident.getTown().getTag();
if (tag != null && !tag.equals("")) {
return "[" + ChatColor.BLUE + tag + ChatColor.RESET + "] ";
}
} catch (NotRegisteredException e) {
}
return "";
}
}

View File

@ -13,19 +13,33 @@ import me.jtmar.wasteland.commands.CommandRank;
import me.jtmar.wasteland.commands.CommandRanks;
import me.jtmar.wasteland.commands.CommandOfficial;
import me.jtmar.wasteland.commands.CommandSetKills;
import me.jtmar.wasteland.config.WastelandConfig;
import me.jtmar.wasteland.listeners.ChatListener;
import me.jtmar.wasteland.listeners.RankListener;
import me.jtmar.wasteland.towny.TownyDependency;
import me.jtmar.wasteland.towny.TownyDisabled;
import me.jtmar.wasteland.towny.TownyPrefix;
public class Wasteland extends JavaPlugin {
private static Wasteland instance;
private Database database;
private WastelandConfig config;
private RankListener rankListener;
private TownyPrefix townyPrefix;
public static Wasteland getInstance() {
return instance;
}
public WastelandConfig getSettings() {
return config;
}
public TownyPrefix getTownyPrefix() {
return townyPrefix;
}
public Database getDatabase() {
return database;
}
@ -34,6 +48,19 @@ public class Wasteland extends JavaPlugin {
rankListener.updatePlayerRank(player);
}
private void initializeConfig() {
this.saveDefaultConfig();
this.config = new WastelandConfig(this.getConfig());
}
private void initializeTowny() {
if (Wasteland.getInstance().getServer().getPluginManager().isPluginEnabled("Towny")) {
this.townyPrefix = new TownyDependency(config.prefixTownTagColor());
} else {
this.townyPrefix = new TownyDisabled();
}
}
private void initializeDatabase() {
String databaseFilename = this.getConfig().getString("databaseFile");
try {
@ -67,7 +94,8 @@ public class Wasteland extends JavaPlugin {
@Override
public void onEnable() {
instance = this;
this.saveDefaultConfig();
initializeConfig();
initializeTowny();
initializeDatabase();
registerCommands();
registerListeners();
@ -75,10 +103,12 @@ public class Wasteland extends JavaPlugin {
@Override
public void onDisable() {
rankListener.close();
if (rankListener != null)
rankListener.close();
rankListener = null;
try {
database.close();
if (database != null)
database.close();
} catch (SQLException e) {
this.getLogger().log(Level.SEVERE, "Failed to close database.", e);
}

View File

@ -1,12 +1,15 @@
package me.jtmar.wasteland.commands;
import java.util.Optional;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import me.jtmar.wasteland.ranks.OfficerRank;
import net.md_5.bungee.api.ChatColor;
import me.jtmar.wasteland.Wasteland;
import me.jtmar.wasteland.ranks.Rank;
public class CommandOfficial implements CommandExecutor {
@Override
@ -19,17 +22,26 @@ public class CommandOfficial implements CommandExecutor {
String senderFormat;
if (sender instanceof Player) {
Player player = (Player) sender;
OfficerRank rank = OfficerRank.getRank(player);
if (rank == null) {
Optional<Rank> rank = Rank.getOfficerRank(player);
if (!rank.isPresent()) {
sender.sendMessage("You are not a staff member.");
return true;
}
senderFormat = "[" + rank.format() + ChatColor.RESET + "] " + player.getDisplayName();
String rankFormat = rank.get().formatAbbreviated() + ChatColor.RESET;
if (Wasteland.getInstance().getSettings().bracketChatRank()) {
rankFormat = ChatColor.RESET + "[" + rankFormat + "]";
}
senderFormat = rankFormat + " " + player.getDisplayName();
} else {
senderFormat = ChatColor.GOLD + ChatColor.BOLD.toString() + "The General of the Armies";
Optional<Rank> consoleRank = Wasteland.getInstance().getSettings().consoleRank();
if (!consoleRank.isPresent()) {
sender.sendMessage("No console rank is configured to send messages with!");
return true;
}
senderFormat = consoleRank.get().formatFull();
}
sender.getServer().broadcastMessage(senderFormat + ChatColor.RESET + ": " + ChatColor.ITALIC + String.join(" ", args));
sender.getServer().broadcastMessage(senderFormat + ChatColor.RESET + ": " + String.join(" ", args));
return true;
}
}

View File

@ -1,6 +1,7 @@
package me.jtmar.wasteland.commands;
import java.sql.SQLException;
import java.util.Optional;
import java.util.logging.Level;
import org.bukkit.command.Command;
@ -24,48 +25,65 @@ public class CommandRank implements CommandExecutor {
String has;
String is;
String need;
String playerNameS;
Player subject;
boolean seeKills;
if (args.length == 1) {
subject = sender.getServer().getPlayer(args[0]);
if (!sender.equals(subject) && !sender.hasPermission("wasteland.kills.other")) {
sender.sendMessage("You do not have permission to view another player's kills.");
if (!sender.equals(subject) && !sender.hasPermission("wasteland.view-rank.other")) {
sender.sendMessage("You do not have permission to view another player's rank.");
return false;
}
if (subject == null) {
sender.sendMessage("Unknown player: " + args[0]);
return false;
}
seeKills = !sender.equals(subject) && sender.hasPermission("wasteland.view-rank.other.kills");
playerName = subject.getDisplayName();
has = " has ";
is = " is ";
need = " needs ";
playerNameS = playerName + "'s";
} else if (sender instanceof Player) {
if (!sender.hasPermission("wasteland.kills")) {
sender.sendMessage("You do not have permission to view your own kills.");
if (!sender.hasPermission("wasteland.view-rank")) {
sender.sendMessage("You do not have permission to view your own rank.");
}
seeKills = sender.hasPermission("wasteland.view-rank.kills");
subject = (Player) sender;
playerName = "You";
has = " have ";
is = " are ";
need = " need ";
playerNameS = "Your";
} else {
sender.sendMessage("You are not a player! Please specify a player whose kills you want to view.");
sender.sendMessage("You are not a player! Please specify a player whose rank you want to view.");
sender.sendMessage(command.getUsage());
return false;
}
try {
int kills = Wasteland.getInstance().getDatabase().getPlayerKills(subject);
EnlistedRank rank = EnlistedRank.getRankFromKills(kills);
EnlistedRank nextRank = EnlistedRank.getNextRank(subject);
sender.sendMessage(playerName + is + "rank " + rank.formatFull() + ".");
String beginString = playerName + has + "killed " + kills + " zombies";
if (nextRank == null) {
sender.sendMessage(beginString + ".");
Optional<EnlistedRank> rank = EnlistedRank.getRankFromKills(kills);
Optional<EnlistedRank> nextRank = EnlistedRank.getNextRank(subject);
if (rank.isPresent()) {
sender.sendMessage(playerName + is + "rank " + rank.get().formatFull() + ".");
}
if (seeKills) {
String hasKilled = playerName + has + "killed " + kills + " zombies";
String andHasToGo;
if (nextRank.isPresent()) {
int moreZombies = nextRank.get().getKills().get() - kills;
andHasToGo = " and" + need + "to kill " + moreZombies
+ " more to reach " + nextRank.get().formatFull() + ".";
} else {
andHasToGo = " and" + has + "reached maximum rank.";
}
sender.sendMessage(hasKilled + andHasToGo);
} else if (nextRank.isPresent()) {
sender.sendMessage(playerNameS + " next rank will be " + nextRank.get().formatExtended() + ".");
} else {
int moreZombies = nextRank.getMinimumKills() - kills;
sender.sendMessage(beginString + " and " + need + "to kill " + moreZombies
+ " more to reach " + nextRank.formatFull() + ".");
sender.sendMessage(playerName + has + "reached maximum rank.");
}
} catch (SQLException e) {
sender.sendMessage("Command failed due to database exception. Contact the server administrator.");

View File

@ -4,11 +4,18 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import me.jtmar.wasteland.Wasteland;
import me.jtmar.wasteland.config.WastelandConfig;
import me.jtmar.wasteland.ranks.EnlistedRank;
import me.jtmar.wasteland.ranks.OfficerRank;
import net.md_5.bungee.api.ChatColor;
import me.jtmar.wasteland.ranks.Rank;
public class CommandRanks implements CommandExecutor {
private String makeElement(Rank rank) {
if (rank.getDescription().isPresent())
return rank.formatExtended() + ": " + rank.getDescription().get();
return rank.formatExtended();
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
@ -16,16 +23,21 @@ public class CommandRanks implements CommandExecutor {
return false;
}
WastelandConfig config = Wasteland.getInstance().getSettings();
sender.sendMessage("Enlisted ranks:");
for (EnlistedRank rank : EnlistedRank.values()) {
sender.sendMessage("* " + rank.formatFull() + ": " + rank.getMinimumKills() + " kills");
for (EnlistedRank rank : config.enlistedRanks()) {
sender.sendMessage("* " + makeElement(rank));
}
sender.sendMessage("Officer (server staff) ranks:");
for (OfficerRank rank : OfficerRank.values()) {
sender.sendMessage("* " + rank.formatFull());
for (Rank rank : config.officerRanks()) {
sender.sendMessage("* " + makeElement(rank));
}
if (config.consoleRank().isPresent()) {
sender.sendMessage("* " + makeElement(config.consoleRank().get()));
}
sender.sendMessage("* " + ChatColor.GOLD + ChatColor.BOLD.toString() + "General of the Armies");
return true;
}
}

View File

@ -0,0 +1,92 @@
package me.jtmar.wasteland.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import me.jtmar.wasteland.ranks.EnlistedRank;
import me.jtmar.wasteland.ranks.Rank;
public class WastelandConfig {
private final String databaseFile;
private final boolean prefixTownTag;
private final Optional<ChatColor> prefixTownTagColor;
private final boolean preferOfficerRank, bracketChatRank, nameUsesRankColor;
private final Collection<EnlistedRank> enlistedRanks;
private final Collection<Rank> officerRanks;
private final Optional<Rank> consoleRank;
/** Orphaned method. */
public static Optional<ChatColor> readColor(ConfigurationSection c, String path) {
return (Optional<ChatColor>) Optional.ofNullable(c.getString(path)).map(ChatColor::valueOf);
}
public WastelandConfig(ConfigurationSection c) {
this.databaseFile = c.getString("databaseFile", "wasteland.sqlite3");
this.prefixTownTag = c.getBoolean("prefixTownTag", true);
this.prefixTownTagColor = readColor(c, "prefixTownTagColor");
this.preferOfficerRank = c.getBoolean("preferOfficerRank", false);
this.bracketChatRank = c.getBoolean("bracketChatRank", true);
this.nameUsesRankColor = c.getBoolean("nameUsesRankColor", false);
ConfigurationSection ers = c.getConfigurationSection("enlistedRanks");
ArrayList<EnlistedRank> enlistedRanks = new ArrayList<>();
this.enlistedRanks = enlistedRanks;
if (ers != null) {
Optional<ChatColor> defaultDecoration = readColor(c, "enlistedRankDefaultDecoration");
Optional<String> defaultDescription =
Optional.ofNullable(c.getString("enlistedRankDefaultDescription"));
Set<String> rankIDs = ers.getKeys(false);
enlistedRanks.ensureCapacity(rankIDs.size());
for (String id : rankIDs) {
EnlistedRank result = new EnlistedRank(defaultDescription, defaultDecoration,
ers.getConfigurationSection(id));
enlistedRanks.add(result);
}
enlistedRanks.sort(new EnlistedRank.EnlistedRankComparator(enlistedRanks));
}
ConfigurationSection ors = c.getConfigurationSection("officerRanks");
ArrayList<Rank> officerRanks = new ArrayList<>();
this.officerRanks = officerRanks;
if (ors != null) {
Optional<ChatColor> defaultDecoration = readColor(c, "officerRankDefaultDecoration");
Set<String> rankIDs = ors.getKeys(false);
officerRanks.ensureCapacity(rankIDs.size());
for (String id : rankIDs) {
ConfigurationSection rank = ors.getConfigurationSection(id);
Rank result = new Rank(Optional.empty(), defaultDecoration, rank);
officerRanks.add(result);
}
officerRanks.sort(new Rank.RankComparator(officerRanks));
}
ConfigurationSection crs = c.getConfigurationSection("consoleRank");
if (crs == null) {
this.consoleRank = Optional.empty();
} else {
this.consoleRank = Optional.of(new Rank(Optional.empty(), Optional.empty(), crs));
}
}
public String databaseFile() { return this.databaseFile; }
public boolean prefixTownTag() { return this.prefixTownTag; }
public Optional<ChatColor> prefixTownTagColor() { return this.prefixTownTagColor; }
public boolean preferOfficerRank() { return this.preferOfficerRank; }
public boolean bracketChatRank() { return this.bracketChatRank; }
public boolean nameUsesRankColor() { return this.nameUsesRankColor; }
public Collection<EnlistedRank> enlistedRanks() { return this.enlistedRanks; }
public Collection<Rank> officerRanks() { return this.officerRanks; }
public Optional<Rank> consoleRank() { return this.consoleRank; }
}

View File

@ -1,24 +1,42 @@
package me.jtmar.wasteland.listeners;
import java.util.Optional;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import me.jtmar.wasteland.TownyDependency;
import me.jtmar.wasteland.Wasteland;
import me.jtmar.wasteland.ranks.EnlistedRank;
import me.jtmar.wasteland.ranks.Rank;
public class ChatListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerChat(AsyncPlayerChatEvent event) {
String townyPrefix = "";
if (Wasteland.getInstance().getServer().getPluginManager().isPluginEnabled("Towny")) {
townyPrefix = TownyDependency.getTownTagPrefix(event.getPlayer());
Player player = event.getPlayer();
Optional<String> townyPrefixQ = Wasteland.getInstance().getTownyPrefix().getPrefix(player);
String townyPrefix = townyPrefixQ.map(x -> x + " ").orElse("");
Optional<Rank> rank;
if (Wasteland.getInstance().getSettings().preferOfficerRank()
|| player.hasPermission("wasteland.chat.officer")) {
rank = Rank.getHighestRank(player);
} else {
rank = EnlistedRank.getEnlistedRank(player).map(x -> (Rank) x);
}
event.setFormat(
townyPrefix
+ "[" + EnlistedRank.getRank(event.getPlayer()).format() + ChatColor.RESET + "] " + " "
+ "%s" + ChatColor.WHITE + ": %s");
String rankPrefix;
if (rank.isPresent()) {
rankPrefix = rank.get().formatAbbreviated();
if (Wasteland.getInstance().getSettings().bracketChatRank())
rankPrefix = ChatColor.RESET + "[" + rankPrefix + ChatColor.RESET + "]";
rankPrefix = rankPrefix + " ";
} else {
rankPrefix = "";
}
event.setFormat(townyPrefix + rankPrefix + "%s" + ChatColor.WHITE + ": %s");
}
}

View File

@ -3,9 +3,11 @@ package me.jtmar.wasteland.listeners;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -17,8 +19,6 @@ import org.bukkit.permissions.PermissionAttachment;
import me.jtmar.wasteland.Wasteland;
import me.jtmar.wasteland.ranks.EnlistedRank;
import me.jtmar.wasteland.ranks.Rank;
import net.md_5.bungee.api.ChatColor;
public class RankListener implements Listener, AutoCloseable {
private Map<UUID, PermissionAttachment> attachments = new HashMap<>();
@ -37,8 +37,10 @@ public class RankListener implements Listener, AutoCloseable {
PermissionAttachment attachment = player.addAttachment(Wasteland.getInstance());
attachments.put(player.getUniqueId(), attachment);
int kills = Wasteland.getInstance().getDatabase().getPlayerKills(player);
EnlistedRank rank = EnlistedRank.getRankFromKills(kills);
attachments.get(player.getUniqueId()).setPermission(rank.getPermission(), true);
Optional<EnlistedRank> rank = EnlistedRank.getRankFromKills(kills);
if (rank.isPresent()) {
attachment.setPermission(rank.get().getPermission(), true);
}
}
private void removeAttachment(Player player) {
@ -79,12 +81,25 @@ public class RankListener implements Listener, AutoCloseable {
case ZOMBIE_VILLAGER:
try {
Wasteland.getInstance().getDatabase().incrementPlayerKills(player);
Rank oldRank = EnlistedRank.getRank(player);
Optional<EnlistedRank> oldRank = EnlistedRank.getEnlistedRank(player);
updatePlayerRank(player);
Rank newRank = EnlistedRank.getRank(player);
if (!newRank.equals(oldRank)) {
final String formatString = "%s " + ChatColor.RESET + "has been promoted from %s " + ChatColor.RESET + "to %s" + ChatColor.RESET + "!";
player.getServer().broadcastMessage(String.format(formatString, player.getDisplayName(), oldRank.formatFull(), newRank.formatFull()));
Optional<EnlistedRank> newRank = EnlistedRank.getEnlistedRank(player);
if (newRank.isPresent()) {
final String formatString;
if (oldRank.isPresent()) {
if (!newRank.get().equals(oldRank.get())) {
formatString = "%s" + ChatColor.RESET + " has been promoted from %s " + ChatColor.RESET + "to %s" + ChatColor.RESET + "!";
player.getServer().broadcastMessage(
String.format(formatString, player.getDisplayName(),
oldRank.get().formatFull(), newRank.get().formatFull()));
}
} else {
formatString = "%s" + ChatColor.RESET + " has been promoted to %s" + ChatColor.RESET + "!";
player.getServer().broadcastMessage(
String.format(formatString, player.getDisplayName(), newRank.get()));
}
}
} catch (SQLException e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to increment player kills.", e);

View File

@ -1,112 +1,90 @@
package me.jtmar.wasteland.ranks;
import java.util.Collection;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionDefault;
public enum EnlistedRank implements Rank {
FODDER( 0, "Fodder", "Zombie Fodder"),
PVT ( 100, "Pvt", "Private"),
PFC ( 250, "Pfc", "Private First Class"),
LCPL ( 500, "LCpl", "Lance Corporal"),
CPL ( 1_000, "Cpl", "Corporal"),
SGT ( 2_500, "Sgt", "Sergeant"),
SSGT ( 5_000, "SSgt", "Staff Sergeant"),
GYSGT (10_000, "GySgt", "Gunnery Sergeant"),
MSGT (25_000, "MSgt", "Master Sergeant"),
MGYSGT(50_000, "MGySgt", "Master Gunnery Sergeant");
import me.jtmar.wasteland.Wasteland;
public class EnlistedRank extends Rank {
private final Optional<Integer> kills;
private final int minKills;
private final String abbreviation;
private final String fullName;
private EnlistedRank(int minKills, String abbreviation, String fullName) {
if(minKills < 0)
throw new IllegalArgumentException("Number of kills must not be negative: " + minKills);
this.minKills = minKills;
this.abbreviation = abbreviation;
this.fullName = fullName;
public EnlistedRank(Optional<String> defaultDescription, Optional<ChatColor> defaultDecoration,
ConfigurationSection c) {
super(defaultDescription, defaultDecoration, c);
if (c.getKeys(false).contains("kills")) this.kills = Optional.of(c.getInt("kills"));
else this.kills = Optional.empty();
PermissionDefault def;
if (kills.isPresent() && kills.get() == 0) def = PermissionDefault.TRUE;
else def = PermissionDefault.FALSE;
super.getPermission().setDefault(def);
}
public static EnlistedRank getRankFromKills(int kills) {
if (kills < 0)
throw new IllegalArgumentException("Number of kills must not be negative: " + kills);
@Override
public Optional<String> getDescription() {
Optional<String> format = super.getDescription();
String descriptionFormat;
if (format.isPresent()) descriptionFormat = format.get();
else return Optional.empty();
String result;
if (kills.isPresent()) result = descriptionFormat.replace("{kills}", kills.get().toString());
else result = descriptionFormat;
return Optional.of(result);
}
public final Optional<Integer> getKills() {
return kills;
}
public static Optional<EnlistedRank> getEnlistedRank(Player player) {
EnlistedRank result = null;
for(EnlistedRank rank : EnlistedRank.values()) {
if (kills < rank.getMinimumKills()) break;
result = rank;
for (EnlistedRank rank : Wasteland.getInstance().getSettings().enlistedRanks()) {
if (player.hasPermission(rank.getPermission())) result = rank;
}
if (result == null) throw new IllegalStateException("Null KillsRank.");
return result;
return Optional.ofNullable(result);
}
public static EnlistedRank getRank(Player player) {
EnlistedRank result = FODDER;
for (EnlistedRank rank : EnlistedRank.values()) {
if (player.hasPermission(rank.getPermission())) {
result = rank;
}
public static Optional<EnlistedRank> getRankFromKills(int kills) {
if (kills < 0) throw new IllegalArgumentException("Number of kills must not be negative.");
return Wasteland.getInstance().getSettings().enlistedRanks()
.stream().filter(rank -> rank.getKills().map(k -> k <= kills).orElse(false))
.reduce((acc, rank) -> rank);
}
public static Optional<EnlistedRank> getNextRank(Player player) {
Optional<EnlistedRank> currentRank = getEnlistedRank(player);
Stream<EnlistedRank> ranks = Wasteland.getInstance().getSettings().enlistedRanks().stream();
if (!currentRank.isPresent()) {
return ranks.filter(rank -> rank.kills.isPresent()).findFirst();
}
return result;
return ranks.filter(rank -> rank.kills.isPresent() && rank.isSuccessorOf(
Wasteland.getInstance().getSettings().enlistedRanks().stream().map(x -> (Rank) x).collect(Collectors.toList())
, currentRank.get())).findFirst();
}
/**
* @param player
* @return Returns null if the player is at the maximum rank.
*/
public static EnlistedRank getNextRank(Player player) {
EnlistedRank nextRank = null;
for (EnlistedRank rank : EnlistedRank.values()) {
if (!player.hasPermission(rank.getPermission())) {
nextRank = rank;
break;
}
public static class EnlistedRankComparator implements Comparator<EnlistedRank> {
private final Comparator<Rank> delegate;
public EnlistedRankComparator(Collection<EnlistedRank> ranks) {
this.delegate = new RankComparator(
ranks.stream().map((EnlistedRank x) -> (Rank) x).collect(Collectors.toList()));
}
return nextRank;
}
public int getMinimumKills() {
return this.minKills;
}
@Override
public String getAbbreviation() {
return this.abbreviation;
}
@Override
public String getFullName() {
return this.fullName;
}
@Override
public String toString() {
return this.getAbbreviation();
}
@Override
public ChatColor getColor() {
switch (this) {
case FODDER:
return ChatColor.GRAY;
case PVT:
return ChatColor.WHITE;
case PFC:
return ChatColor.BLUE;
case LCPL:
return ChatColor.GREEN;
case CPL:
return ChatColor.RED;
case SGT:
return ChatColor.LIGHT_PURPLE;
case SSGT:
return ChatColor.YELLOW;
case GYSGT:
return ChatColor.AQUA;
case MSGT:
return ChatColor.DARK_RED;
case MGYSGT:
return ChatColor.DARK_PURPLE;
@Override
public int compare(EnlistedRank a, EnlistedRank b) {
if (a.getKills().isPresent() && b.getKills().isPresent()) {
if (a.getKills().get() > b.getKills().get()) return 1;
if (b.getKills().get() > a.getKills().get()) return -1;
}
return delegate.compare(a, b);
}
throw new IllegalStateException();
}
}

View File

@ -1,86 +0,0 @@
package me.jtmar.wasteland.ranks;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
public enum OfficerRank implements Rank {
SNDLT ("2ndLt", "Second Lieutenant"),
FSTLT ("1stLt", "First Lieutenant"),
CAPT ("Capt", "Captain"),
MAJ ("Maj", "Major"),
LTCOL ("LtCol", "Lieutenant Colonel"),
COL ("Col", "Colonel"),
BGEN ("BGen", "Brigadier General"),
MAJGEN("MajGen", "Major General"),
LTGEN ("LtGen", "Lieutenant General"),
GEN ("Gen", "General");
private final String abbreviation;
private final String fullName;
private OfficerRank(String abbreviation, String fullName) {
this.abbreviation = abbreviation;
this.fullName = fullName;
}
/**
* @param player
* @return Returns `null` if the player is not staff.
*/
public static OfficerRank getRank(Player player) {
OfficerRank result = null;
for (OfficerRank rank : OfficerRank.values()) {
if (player.hasPermission(rank.getPermission())) {
result = rank;
}
}
return result;
}
@Override
public String getAbbreviation() {
return abbreviation;
}
@Override
public String getFullName() {
return fullName;
}
@Override
public String toString() {
return this.getAbbreviation();
}
@Override
public ChatColor getColor() {
switch (this) {
case SNDLT:
return ChatColor.WHITE;
case FSTLT:
return ChatColor.BLUE;
case CAPT:
return ChatColor.GREEN;
case MAJ:
return ChatColor.RED;
case LTCOL:
return ChatColor.LIGHT_PURPLE;
case COL:
return ChatColor.YELLOW;
case BGEN:
return ChatColor.AQUA;
case MAJGEN:
return ChatColor.DARK_RED;
case LTGEN:
return ChatColor.DARK_PURPLE;
case GEN:
return ChatColor.GOLD;
}
throw new IllegalStateException();
}
@Override
public ChatColor getDecoration() {
return ChatColor.BOLD;
}
}

View File

@ -1,51 +1,134 @@
package me.jtmar.wasteland.ranks;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public interface Rank {
String getAbbreviation();
String getFullName();
ChatColor getColor();
public default ChatColor getDecoration() {
return null;
}
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import me.jtmar.wasteland.Wasteland;
import me.jtmar.wasteland.config.WastelandConfig;
public class Rank {
private final String id, abbreviation, name;
private final Optional<String> description;
private final Optional<ChatColor> color, decoration;
private final Permission permission;
private final Optional<String> predecessor;
private final boolean preferred;
public default String format() {
ChatColor decoration = getDecoration();
if (decoration != null) {
return getColor() + decoration.toString() + getAbbreviation();
public Rank(Optional<String> defaultDescription, Optional<ChatColor> defaultDecoration,
ConfigurationSection c) {
this.id = c.getName();
this.name = c.getString("name", id);
this.abbreviation = c.getString("abbreviation", name);
Optional<String> description = Optional.ofNullable(c.getString("description"));
if (!description.isPresent()) this.description = defaultDescription;
else this.description = description;
this.color = WastelandConfig.readColor(c, "color");
Optional<ChatColor> decoration = WastelandConfig.readColor(c, "decoration");
if (!decoration.isPresent()) this.decoration = defaultDecoration;
else this.decoration = decoration;
this.predecessor = Optional.ofNullable(c.getString("succeeds"));
this.preferred = c.getBoolean("preferred", true);
Map<String, Boolean> children = new HashMap<>();
if (predecessor.isPresent()) {
children.put(predecessor.get(), true);
}
return getColor() + getAbbreviation();
this.permission =
new Permission("wasteland.rank." + id, description.orElse(null), PermissionDefault.FALSE, children);
}
public default String formatFull() {
ChatColor decoration = getDecoration();
if (decoration != null) {
return getColor() + decoration.toString() + getFullName();
public String getId() { return this.id; }
public Permission getPermission() { return this.permission; }
public String getAbbreviation() { return this.abbreviation; }
public String getFullName() { return this.name; }
public Optional<String> getDescription() { return description; }
public Optional<ChatColor> getColor() { return this.color; }
public Optional<ChatColor> getDecoration() { return this.decoration; }
public Optional<String> getPredecessor() { return this.predecessor; }
public boolean isPreferred() { return this.preferred; }
public boolean isSuccessorOf(Collection<Rank> ranks, Rank other) {
if (this == other) return false;
if (this.getId().equals(other.getId())) return false;
if (!this.getPredecessor().isPresent()) return false;
if (this.getPredecessor().get().equals(other.getId())) return true;
String predecessorId = this.getPredecessor().get();
for (Rank predecessor : ranks) {
if (predecessor.getId().equals(predecessorId))
return predecessor.isSuccessorOf(ranks, other);
}
return getColor() + getFullName();
throw new IllegalArgumentException("Predecessor rank " + predecessorId + " not found.");
}
public default String formatExtended() {
return formatFull() + ChatColor.RESET + " (" + format() + ChatColor.RESET + ")";
public final String getFormat() {
String color = this.color.map(ChatColor::toString).orElse("");
String decoration = this.decoration.map(ChatColor::toString).orElse("");
return color + decoration;
}
/** The rank abbreviation with chat formatting codes. */
public final String formatAbbreviated() {
return getFormat() + getAbbreviation();
}
/** The rank name with chat formatting codes. */
public final String formatFull() {
return getFormat() + getFullName();
}
/** `Rank Name (RankAbbr)` with chat formatting codes. */
public final String formatExtended() {
return formatFull() + ChatColor.RESET + " (" + formatAbbreviated() + ChatColor.RESET + ")";
}
/**
* @param player
* @return Does the player have at least this permission?
* @return Does the player have this rank?
*/
public default boolean hasRank(Player player) {
public final boolean hasRank(Player player) {
return player.hasPermission(getPermission());
}
public default String getPermission() {
return "wasteland.rank." + getAbbreviation().toLowerCase();
@Override
public String toString() {
return this.abbreviation;
}
public static Rank getRank(Player player) {
OfficerRank rank = OfficerRank.getRank(player);
if (rank != null) return rank;
return EnlistedRank.getRank(player);
public static Optional<Rank> getOfficerRank(Player player) {
Rank result = null;
for (Rank rank : Wasteland.getInstance().getSettings().officerRanks()) {
if (player.hasPermission(rank.getPermission())) result = rank;
}
return Optional.ofNullable(result);
}
public static Optional<Rank> getHighestRank(Player player) {
Optional<Rank> officerRank = getOfficerRank(player);
if (officerRank.isPresent()) return officerRank;
return EnlistedRank.getEnlistedRank(player).map(x -> (Rank) x);
}
public static class RankComparator implements Comparator<Rank> {
private final Collection<Rank> ranks;
public RankComparator(Collection<Rank> ranks) { this.ranks = ranks; }
@Override
public int compare(Rank a, Rank b) {
if (a == b) return 0;
if (a.getId().equals(b.getId())) return 0;
if (a.isSuccessorOf(ranks, b)) return 1;
if (b.isSuccessorOf(ranks, a)) return -1;
// incomparable
return 0;
}
}
}

View File

@ -0,0 +1,39 @@
package me.jtmar.wasteland.towny;
import java.util.Optional;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.TownyUniverse;
public class TownyDependency implements TownyPrefix {
private final String townTagColor;
public TownyDependency(Optional<ChatColor> townTagColor) {
this.townTagColor = townTagColor.map(ChatColor::toString).orElse("");
}
public TownyDependency() {
this(Optional.empty());
}
public TownyDependency(ChatColor townTagColor) {
this(Optional.of(townTagColor));
}
@Override
public Optional<String> getPrefix(Player player) {
try {
Resident resident = TownyUniverse.getDataSource().getResident(player.getName());
String tag = resident.getTown().getTag();
if (tag != null && !tag.equals("")) {
return Optional.of(ChatColor.RESET + "[" + townTagColor + tag + ChatColor.RESET + "]");
}
} catch (NotRegisteredException e) {
}
return Optional.empty();
}
}

View File

@ -0,0 +1,12 @@
package me.jtmar.wasteland.towny;
import java.util.Optional;
import org.bukkit.entity.Player;
public class TownyDisabled implements TownyPrefix {
@Override
public Optional<String> getPrefix(Player player) {
return Optional.empty();
}
}

View File

@ -0,0 +1,9 @@
package me.jtmar.wasteland.towny;
import java.util.Optional;
import org.bukkit.entity.Player;
public interface TownyPrefix {
public Optional<String> getPrefix(Player player);
}

View File

@ -1,18 +1,17 @@
name: Wasteland
author: jtmar
author: HuskFodder
main: me.jtmar.wasteland.Wasteland
version: 0.0.0
api-version: 1.12
api-version: 1.14
softdepend: [Towny]
commands:
rank:
description: View your rank and how many monsters you have killed.
usage: "Usage: /<command> [player]"
aliases: [zkills, kills]
permission: wasteland.kills
permission-message: You do not have permission to view your own kills.
usage: "Usage: /rank [<player>]"
permission: wasteland.view-rank
permission-message: You do not have permission to view your rank.
ranks:
description: List the server ranks.
usage: "Usage: /<command>"
@ -28,102 +27,32 @@ commands:
permission-message: You are not a staff member.
permissions:
wasteland.kills:
description: Allows you to view your /kills.
wasteland.chat.officer:
description: Show a player's officer rank even in normal chat.
default: false
wasteland.view-rank:
description: Allows you to view your /rank.
default: true
wasteland.view-rank.kills:
description: Allows you to see how many kills you have using /rank.
default: true
wasteland.kills.other:
description: Allows you to view another player's /kills.
default: op
children:
wasteland.kills: true
wasteland.view-rank: true
wasteland.view-rank.other:
description: Allows you to see someone else's /rank.
default: true
children:
wasteland.view-rank: true
wasteland.view-rank.other.kills:
description: Allows you to see how many kills someone else has using /rank.
default: false
children:
wasteland.view-rank.kills: true
wasteland.view-rank.other: true
wasteland.kills.set:
description: Allows you to set player kills using /setkills.
default: op
children:
wasteland.kills.other: true
# Kill ranks
wasteland.rank.fodder:
default: true
wasteland.rank.pvt:
default: false
children:
wasteland.rank.fodder: true
wasteland.rank.pfc:
default: false
children:
wasteland.rank.pvt: true
wasteland.rank.lcpl:
default: false
children:
wasteland.rank.pfc: true
wasteland.rank.cpl:
default: false
children:
wasteland.rank.lcpl: true
wasteland.rank.sgt:
default: false
children:
wasteland.rank.cpl: true
wasteland.rank.ssgt:
default: false
children:
wasteland.rank.sgt: true
wasteland.rank.gysgt:
default: false
children:
wasteland.rank.ssgt: true
wasteland.rank.msgt:
default: false
children:
wasteland.rank.gysgt: true
wasteland.rank.mgysgt:
default: false
children:
wasteland.rank.msgt: true
# Staff ranks
wasteland.official:
default: op
wasteland.rank.2ndlt:
default: false
children:
wasteland.official: true
wasteland.rank.1stlt:
default: false
children:
wasteland.rank.2ndlt: true
wasteland.rank.capt:
default: false
children:
wasteland.rank.1stlt: true
wasteland.rank.maj:
default: false
children:
wasteland.rank.capt: true
wasteland.rank.ltcol:
default: false
children:
wasteland.rank.maj: true
wasteland.rank.col:
default: false
children:
wasteland.rank.ltcol: true
wasteland.rank.bgen:
default: false
children:
wasteland.rank.col: true
wasteland.rank.majgen:
default: false
children:
wasteland.rank.bgen: true
wasteland.rank.ltgen:
default: false
children:
wasteland.rank.majgen: true
wasteland.rank.gen:
default: false
children:
wasteland.rank.ltgen: true
wasteland.view-rank.other.kills: true