Added /kit command.

master
James T. Martin 2020-11-21 17:59:02 -08:00
parent 2f1fb0ca92
commit 7024ba2e3a
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
25 changed files with 591 additions and 192 deletions

View File

@ -5,4 +5,10 @@ import org.bukkit.plugin.java.JavaPlugin;
public interface Substate {
void register(JavaPlugin plugin);
void unregister(JavaPlugin plugin);
/**
* Close everything that needs to be closed without unregistering anything.
* This is called when the plugin is being disabled.
*/
default void disable(JavaPlugin plugin) { }
}

View File

@ -1,16 +1,14 @@
package me.jamestmartin.wasteland;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Level;
import me.jamestmartin.wasteland.config.ConfigParser;
import me.jamestmartin.wasteland.store.SqliteDatabase;
import me.jamestmartin.wasteland.towny.TownyEnabled;
import me.jamestmartin.wasteland.towny.TownyDisabled;
import me.jamestmartin.wasteland.towny.TownyDependency;
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Level;
import org.bukkit.plugin.java.JavaPlugin;
public class Wasteland extends JavaPlugin {
@ -18,7 +16,6 @@ public class Wasteland extends JavaPlugin {
private static TownyDependency towny;
private WastelandConfig config;
private SqliteDatabase database;
private WastelandState state;
public static Wasteland getInstance() {
@ -33,54 +30,42 @@ public class Wasteland extends JavaPlugin {
public void onEnable() {
instance = this;
if (Wasteland.getInstance().getServer().getPluginManager().isPluginEnabled("Towny")) {
if (this.getServer().getPluginManager().isPluginEnabled("Towny")) {
towny = new TownyEnabled();
} else {
towny = new TownyDisabled();
}
initialize();
initializeConfig();
register();
}
@Override
public void onDisable() {
// No need to unregister stuff; Bukkit will do that for us.
state.disable(this);
state = null;
deinitialize();
towny = null;
instance = null;
}
public void reload() {
getLogger().info("Reloading wasteland...");
reloadConfig();
reinitialize();
initializeConfig();
unregister();
register();
getLogger().info("Done.");
}
private void reinitialize() {
deinitialize();
initialize();
}
private void initialize() {
initializeConfig();
initializeDatabase();
}
private void initializeConfig() {
saveDefaultConfig();
config = ConfigParser.parseConfig(getConfig());
}
private void initializeDatabase() {
String databaseFilename = config.getDatabaseFilename();
try {
database = new SqliteDatabase(new File(this.getDataFolder(), databaseFilename));
private void register() {
try {
state = new WastelandState(config);
} catch (IOException e) {
this.getLogger().log(Level.SEVERE, "Failed to write database file: " + e.getMessage());
this.getPluginLoader().disablePlugin(this);
@ -91,22 +76,6 @@ public class Wasteland extends JavaPlugin {
this.getLogger().log(Level.SEVERE, "SQLite exception on initialize.", e);
this.getPluginLoader().disablePlugin(this);
}
}
private void deinitialize() {
config = null;
try {
if (database != null)
database.close();
} catch (SQLException e) {
this.getLogger().log(Level.SEVERE, "Failed to close database.", e);
}
database = null;
}
private void register() {
state = new WastelandState(config, database);
state.register(this);
}

View File

@ -2,6 +2,7 @@ package me.jamestmartin.wasteland;
import me.jamestmartin.wasteland.chat.ChatConfig;
import me.jamestmartin.wasteland.kills.KillsConfig;
import me.jamestmartin.wasteland.kit.KitConfig;
import me.jamestmartin.wasteland.ranks.AllRanks;
import me.jamestmartin.wasteland.spawns.SpawnsConfig;
@ -11,18 +12,21 @@ public class WastelandConfig {
private final KillsConfig killsConfig;
private final AllRanks ranks;
private final SpawnsConfig spawnsConfig;
private final KitConfig kitConfig;
public WastelandConfig(
String databaseFilename,
ChatConfig chatConfig,
KillsConfig killsConfig,
AllRanks ranks,
SpawnsConfig spawnsConfig) {
SpawnsConfig spawnsConfig,
KitConfig kitConfig) {
this.databaseFilename = databaseFilename;
this.chatConfig = chatConfig;
this.killsConfig = killsConfig;
this.ranks = ranks;
this.spawnsConfig = spawnsConfig;
this.kitConfig = kitConfig;
}
public String getDatabaseFilename() {
@ -44,4 +48,8 @@ public class WastelandConfig {
public SpawnsConfig getSpawnsConfig() {
return spawnsConfig;
}
public KitConfig getKitConfig() {
return kitConfig;
}
}

View File

@ -1,35 +1,44 @@
package me.jamestmartin.wasteland;
import java.io.IOException;
import java.sql.SQLException;
import org.bukkit.plugin.java.JavaPlugin;
import me.jamestmartin.wasteland.chat.ChatState;
import me.jamestmartin.wasteland.kills.KillsState;
import me.jamestmartin.wasteland.kit.KitState;
import me.jamestmartin.wasteland.ranks.PermissionsPlayerRankProvider;
import me.jamestmartin.wasteland.ranks.PlayerRankProvider;
import me.jamestmartin.wasteland.ranks.RanksState;
import me.jamestmartin.wasteland.spawns.SpawnsState;
import me.jamestmartin.wasteland.store.SqliteDatabase;
import me.jamestmartin.wasteland.store.StoreState;
import me.jamestmartin.wasteland.store.sqlite.SqliteState;
public class WastelandState implements Substate {
private final CommandWasteland commandWasteland;
private final StoreState storeState;
private final ChatState chatState;
private final KillsState killsState;
private final RanksState ranksState;
private final SpawnsState spawnsState;
private final KitState kitState;
public WastelandState(WastelandConfig config, SqliteDatabase database) {
public WastelandState(WastelandConfig config) throws IOException, ClassNotFoundException, SQLException {
this.commandWasteland = new CommandWasteland();
PlayerRankProvider rankProvider = new PermissionsPlayerRankProvider(config.getRanks());
this.ranksState = new RanksState(config.getKillsConfig(), database, rankProvider);
this.storeState = new SqliteState(config.getDatabaseFilename());
this.ranksState = new RanksState(config.getKillsConfig(), storeState.getKillsStore(), rankProvider);
this.chatState = new ChatState(config.getChatConfig(), rankProvider);
this.killsState = new KillsState(config.getKillsConfig(), ranksState.getPlayerKillsStore(), rankProvider);
this.spawnsState = new SpawnsState(config.getSpawnsConfig());
this.kitState = new KitState(config.getKitConfig(), storeState.getKitStore());
}
private Substate[] getSubstates() {
Substate[] substates = { chatState, killsState, ranksState, spawnsState };
Substate[] substates = { storeState, chatState, killsState, ranksState, spawnsState, kitState };
return substates;
}
@ -50,4 +59,11 @@ public class WastelandState implements Substate {
substate.unregister(plugin);
}
}
@Override
public void disable(JavaPlugin plugin) {
for (Substate substate : getSubstates()) {
substate.disable(plugin);
}
}
}

View File

@ -11,6 +11,7 @@ import java.util.Optional;
import java.util.Set;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.EntityType;
import org.bukkit.permissions.Permission;
@ -23,6 +24,7 @@ import com.google.common.graph.ValueGraphBuilder;
import me.jamestmartin.wasteland.WastelandConfig;
import me.jamestmartin.wasteland.chat.ChatConfig;
import me.jamestmartin.wasteland.kills.KillsConfig;
import me.jamestmartin.wasteland.kit.KitConfig;
import me.jamestmartin.wasteland.ranks.AllRanks;
import me.jamestmartin.wasteland.ranks.EnlistedRank;
import me.jamestmartin.wasteland.ranks.EnlistedRanks;
@ -40,12 +42,13 @@ public class ConfigParser {
ChatConfig chatConfig = parseChatConfig(c.getConfigurationSection("chat"));
KillsConfig killsConfig = parseKillsConfig(c.getConfigurationSection("kills"));
SpawnsConfig spawnsConfig = parseSpawnsConfig(c.getConfigurationSection("spawns"));
KitConfig kitConfig = parseKitConfig(c.getConfigurationSection("kit"));
ConfigurationSection enlistedSection = c.getConfigurationSection("enlisted");
ConfigurationSection officerSection = c.getConfigurationSection("officer");
AllRanks ranks = parseRanks(enlistedSection, officerSection);
return new WastelandConfig(databaseFilename, chatConfig, killsConfig, ranks, spawnsConfig);
return new WastelandConfig(databaseFilename, chatConfig, killsConfig, ranks, spawnsConfig, kitConfig);
}
private static ChatConfig parseChatConfig(ConfigurationSection c) {
@ -283,6 +286,23 @@ public class ConfigParser {
phaseMultipliers);
}
private static KitConfig parseKitConfig(ConfigurationSection c) {
long kitPeriod = c.getLong("period");
List<Material> kitTools = new ArrayList<>();
for (String tool : c.getStringList("tools")) {
kitTools.add(Material.valueOf(tool));
}
ConfigurationSection itemsc = c.getConfigurationSection("items");
Map<Material, Integer> kitItems = new HashMap<>();
for (String item : itemsc.getKeys(false)) {
kitItems.put(Material.valueOf(item), itemsc.getInt(item));
}
return new KitConfig(kitPeriod, kitTools, kitItems);
}
/** Orphaned method. */
private static Optional<ChatColor> readColor(ConfigurationSection c, String path) {
return (Optional<ChatColor>) Optional.ofNullable(c.getString(path)).map(ChatColor::valueOf);

View File

@ -10,9 +10,9 @@ import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.Wasteland;
class CommandSetKills implements CommandExecutor {
private final PlayerKillsStore store;
private final KillsStore store;
public CommandSetKills(PlayerKillsStore store) {
public CommandSetKills(KillsStore store) {
this.store = store;
}

View File

@ -15,10 +15,10 @@ import me.jamestmartin.wasteland.ranks.PlayerRankProvider;
class KillsListener implements Listener {
private final KillsConfig config;
private final PlayerKillsStore store;
private final KillsStore store;
private final PlayerRankProvider provider;
public KillsListener(KillsConfig config, PlayerKillsStore store, PlayerRankProvider provider) {
public KillsListener(KillsConfig config, KillsStore store, PlayerRankProvider provider) {
this.config = config;
this.store = store;
this.provider = provider;

View File

@ -2,7 +2,7 @@ package me.jamestmartin.wasteland.kills;
import org.bukkit.entity.Player;
public interface PlayerKillsProvider {
public interface KillsProvider {
/** Get how many monsters a player has killed. */
public int getPlayerKills(Player player) throws Exception;
}

View File

@ -11,7 +11,7 @@ public class KillsState implements Substate {
private final CommandRankEligibleMobs commandRankEligibleMobs;
private final CommandSetKills commandSetKills;
public KillsState(KillsConfig config, PlayerKillsStore store, PlayerRankProvider rankProvider) {
public KillsState(KillsConfig config, KillsStore store, PlayerRankProvider rankProvider) {
this.killsListener = new KillsListener(config, store, rankProvider);
this.commandRankEligibleMobs = new CommandRankEligibleMobs(config);
this.commandSetKills = new CommandSetKills(store);

View File

@ -3,7 +3,7 @@ package me.jamestmartin.wasteland.kills;
import org.bukkit.entity.Player;
/** A data store which stores how many monsters a player has killed. */
public interface PlayerKillsStore extends PlayerKillsProvider {
public interface KillsStore extends KillsProvider {
/** Set how many monsters a player has killed. */
public void setPlayerKills(Player player, int kills) throws Exception;
/** Add to the number of monsters a player has killed. */
@ -12,7 +12,4 @@ public interface PlayerKillsStore extends PlayerKillsProvider {
public default void incrementPlayerKills(Player player) throws Exception {
addPlayerKills(player, 1);
}
/** Add a player to the store if they are not already present, if necessary. */
public default void initPlayer(Player player) throws Exception { }
}

View File

@ -0,0 +1,125 @@
package me.jamestmartin.wasteland.kit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.Optional;
import java.util.Random;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import me.jamestmartin.wasteland.Wasteland;
public class CommandKit implements CommandExecutor {
private final KitConfig config;
private final KitStore store;
private final Random rand = new Random();
public CommandKit(KitConfig config, KitStore store) {
this.config = config;
this.store = store;
}
private Optional<ItemStack> makeTool(Material material, double quality) {
int durability = material.getMaxDurability() - (short) Math.floor(material.getMaxDurability() * rand.nextDouble() * quality);
if (durability <= material.getMaxDurability() / 8) {
return Optional.empty();
}
ItemStack tool = new ItemStack(material);
ItemMeta meta = tool.getItemMeta();
((Damageable) meta).setDamage(durability);
tool.setItemMeta(meta);
return Optional.of(tool);
}
private Collection<ItemStack> selectKitItems(Player player) throws Exception {
int totalKits = store.getTotalKitsRecieved(player);
player.sendMessage("Total: " + totalKits);
double quality = Math.pow(2, ((double) -totalKits) / 2 + 1);
List<ItemStack> items = new ArrayList<>();
for (Material tool : config.getKitTools()) {
makeTool(tool, quality).ifPresent(items::add);
}
for (Entry<Material, Integer> item : config.getKitItems().entrySet()) {
int quantity = (int) Math.ceil(item.getValue() * rand.nextDouble() * quality);
System.out.println(item.getKey().toString());
System.out.println(quantity);
if (quantity > item.getValue() / 8) {
items.add(new ItemStack(item.getKey(), quantity));
}
}
return items;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length > 0) {
sender.sendMessage("Too many arguments.");
return false;
}
if (!(sender instanceof Player)) {
sender.sendMessage("You must be a player to receive a kit.");
return false;
}
Player player = (Player) sender;
try {
if (store.getLastKitTime(player) + config.getKitPeriod() > player.getWorld().getFullTime()) {
sender.sendMessage("You've already received a kit recently.");
return true;
}
sender.sendMessage("Last time: " + store.getLastKitTime(player));
} catch (Exception e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to get player's last kit time.", e);
sender.sendMessage("ERROR: Failed to get last kit time. Please notify a server administrator.");
return true;
}
Collection<ItemStack> kitItems;
try {
kitItems = selectKitItems(player);
} catch (Exception e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to get player's total kits received.", e);
sender.sendMessage("ERROR: Failed to get total kits received. Please notify a server administrator.");
return true;
}
if (kitItems.isEmpty()) {
sender.sendMessage("Looks like supplies have dried up.");
return true;
}
try {
store.useKit(player);
} catch (Exception e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to update player's kit usage.", e);
sender.sendMessage("ERROR: Failed to update kit usage. Please notify a server administrator.");
return true;
}
for (ItemStack stack : kitItems) {
player.getWorld().dropItemNaturally(player.getLocation(), stack);
}
return true;
}
}

View File

@ -0,0 +1,30 @@
package me.jamestmartin.wasteland.kit;
import java.util.Collection;
import java.util.Map;
import org.bukkit.Material;
public class KitConfig {
private final long kitPeriod;
private final Collection<Material> kitTools;
private final Map<Material, Integer> kitItems;
public KitConfig(long kitPeriod, Collection<Material> kitTools, Map<Material, Integer> kitItems) {
this.kitPeriod = kitPeriod;
this.kitTools = kitTools;
this.kitItems = kitItems;
}
public long getKitPeriod() {
return kitPeriod;
}
public Collection<Material> getKitTools() {
return kitTools;
}
public Map<Material, Integer> getKitItems() {
return kitItems;
}
}

View File

@ -0,0 +1,23 @@
package me.jamestmartin.wasteland.kit;
import org.bukkit.plugin.java.JavaPlugin;
import me.jamestmartin.wasteland.Substate;
public class KitState implements Substate {
private final CommandKit commandKit;
public KitState(KitConfig config, KitStore store) {
this.commandKit = new CommandKit(config, store);
}
@Override
public void register(JavaPlugin plugin) {
plugin.getCommand("kit").setExecutor(commandKit);
}
@Override
public void unregister(JavaPlugin plugin) {
plugin.getCommand("kit").setExecutor(null);
}
}

View File

@ -0,0 +1,12 @@
package me.jamestmartin.wasteland.kit;
import org.bukkit.entity.Player;
public interface KitStore {
/** Get the last time the player received a kit. */
long getLastKitTime(Player player) throws Exception;
/** Get the total number of kits a player has received. */
int getTotalKitsRecieved(Player player) throws Exception;
/** Increments the total kits received and updates the last kit time. */
void useKit(Player player) throws Exception;
}

View File

@ -11,14 +11,14 @@ import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.Wasteland;
import me.jamestmartin.wasteland.kills.KillsConfig;
import me.jamestmartin.wasteland.kills.PlayerKillsProvider;
import me.jamestmartin.wasteland.kills.KillsProvider;
public class CommandRank implements CommandExecutor {
private final PlayerKillsProvider killsProvider;
private final KillsProvider killsProvider;
private final PlayerRankProvider rankProvider;
private final String eligibleMobsName;
public CommandRank(KillsConfig killsConfig, PlayerKillsProvider killsProvider, PlayerRankProvider rankProvider) {
public CommandRank(KillsConfig killsConfig, KillsProvider killsProvider, PlayerRankProvider rankProvider) {
this.killsProvider = killsProvider;
this.rankProvider = rankProvider;
this.eligibleMobsName = killsConfig.getEligibleMobsName();
@ -88,13 +88,13 @@ public class CommandRank implements CommandExecutor {
andHasToGo = " and" + need + "to kill " + moreZombies
+ " more to reach " + nextRank.get().formatFull() + ChatColor.RESET + ".";
} else {
andHasToGo = " and" + has + "reached maximum rank.";
andHasToGo = " and" + has + "no further promotions available.";
}
sender.sendMessage(hasKilled + andHasToGo);
} else if (nextRank.isPresent()) {
sender.sendMessage(playerNameS + " next rank will be " + nextRank.get().formatExtended() + ChatColor.RESET + ".");
} else {
sender.sendMessage(playerName + has + "reached maximum rank.");
sender.sendMessage(playerName + has + "no further promotions available.");
}
} catch (Exception e) {
sender.sendMessage("Command failed due to database exception. Contact the server administrator.");

View File

@ -9,15 +9,15 @@ import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachment;
import me.jamestmartin.wasteland.Wasteland;
import me.jamestmartin.wasteland.kills.PlayerKillsStore;
import me.jamestmartin.wasteland.kills.KillsStore;
public class RankAttachments {
private final EnlistedRanks ranks;
private final PlayerKillsStore killsStore;
private final KillsStore killsStore;
private final Map<UUID, PermissionAttachment> attachments = new HashMap<>();
public RankAttachments(EnlistedRanks ranks, PlayerKillsStore killsStore) {
public RankAttachments(EnlistedRanks ranks, KillsStore killsStore) {
this.ranks = ranks;
this.killsStore = killsStore;
}
@ -36,16 +36,11 @@ public class RankAttachments {
}
}
private void createAttachment(Player player) throws Exception {
void createAttachment(Player player) throws Exception {
int kills = killsStore.getPlayerKills(player);
createAttachment(player, kills);
}
public void initializePlayer(Player player) throws Exception {
killsStore.initPlayer(player);
createAttachment(player);
}
public void updatePlayerRank(Player player) throws Exception {
removeAttachment(player);
createAttachment(player);
@ -59,7 +54,7 @@ public class RankAttachments {
public void register() {
for (Player player : Wasteland.getInstance().getServer().getOnlinePlayers()) {
try {
initializePlayer(player);
createAttachment(player);
} catch (Exception e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to get player's kills.", e);
}
@ -76,7 +71,7 @@ public class RankAttachments {
attachments.clear();
}
public class AttachmentUpdatingPlayerKillsStore implements PlayerKillsStore {
public class AttachmentUpdatingPlayerKillsStore implements KillsStore {
@Override
public int getPlayerKills(Player player) throws Exception {
return killsStore.getPlayerKills(player);

View File

@ -22,7 +22,7 @@ public class RankAttachmentsListener implements Listener {
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
try {
attachments.initializePlayer(player);
attachments.createAttachment(player);
} catch (Exception e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to get player's kills.", e);
return;

View File

@ -6,7 +6,7 @@ import org.bukkit.plugin.java.JavaPlugin;
import me.jamestmartin.wasteland.Substate;
import me.jamestmartin.wasteland.kills.KillsConfig;
import me.jamestmartin.wasteland.kills.PlayerKillsStore;
import me.jamestmartin.wasteland.kills.KillsStore;
public class RanksState implements Substate {
private final RankAttachmentsListener rankAttachmentsListener;
@ -15,9 +15,9 @@ public class RanksState implements Substate {
private final CommandRanks commandRanks;
private final RankAttachments rankAttachments;
private final PlayerKillsStore killsStore;
private final KillsStore killsStore;
public RanksState(KillsConfig killsConfig, PlayerKillsStore killsStore, PlayerRankProvider rankProvider) {
public RanksState(KillsConfig killsConfig, KillsStore killsStore, PlayerRankProvider rankProvider) {
this.rankAttachments = new RankAttachments(rankProvider.getRanks().getEnlistedRanks(), killsStore);
this.rankAttachmentsListener = new RankAttachmentsListener(rankAttachments);
this.killsStore = rankAttachments.new AttachmentUpdatingPlayerKillsStore();
@ -26,7 +26,7 @@ public class RanksState implements Substate {
this.commandRanks = new CommandRanks(rankProvider.getRanks());
}
public PlayerKillsStore getPlayerKillsStore() {
public KillsStore getPlayerKillsStore() {
return killsStore;
}

View File

@ -1,107 +0,0 @@
package me.jamestmartin.wasteland.store;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.kills.PlayerKillsStore;
public class SqliteDatabase implements AutoCloseable, PlayerKillsStore {
private static final String CREATE_KILLS_TABLE =
"CREATE TABLE IF NOT EXISTS player_kills"
+ "( `player` VARCHAR(36) PRIMARY KEY"
+ ", `kills` UNSIGNED INT NOT NULL"
+ ")";
private static final String INIT_PLAYER_KILLS =
"INSERT OR IGNORE INTO `player_kills`(`player`, `kills`) VALUES (?, 0)";
private static final String GET_PLAYER_KILLS =
"SELECT `kills` FROM `player_kills` WHERE `player` = ?";
private static final String ADD_PLAYER_KILLS =
"UPDATE `player_kills` SET `kills`=`kills` + ? WHERE `player` = ?";
private static final String SET_PLAYER_KILLS =
"UPDATE `player_kills` SET `kills` = ? WHERE `player` = ?";
private final Connection connection;
private final PreparedStatement psInitPlayerKills;
private final PreparedStatement psGetPlayerKills;
private final PreparedStatement psAddPlayerKills;
private final PreparedStatement psSetPlayerKills;
public SqliteDatabase(File file)
throws IOException, ClassNotFoundException, SQLException {
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
// Ensures that JDBC is installed.
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + file);
// I want to let this be true, but it doesn't work if it's true.
// I can't figure out why.
connection.setAutoCommit(false);
// Initialize database tables
Statement s = connection.createStatement();
s.executeUpdate(CREATE_KILLS_TABLE);
s.close();
psInitPlayerKills = connection.prepareStatement(INIT_PLAYER_KILLS);
psGetPlayerKills = connection.prepareStatement(GET_PLAYER_KILLS);
psAddPlayerKills = connection.prepareStatement(ADD_PLAYER_KILLS);
psSetPlayerKills = connection.prepareStatement(SET_PLAYER_KILLS);
}
@Override
public void initPlayer(Player player) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psInitPlayerKills.setString(1, playerUUID);
psInitPlayerKills.executeUpdate();
connection.commit();
}
@Override
public int getPlayerKills(Player player) throws SQLException
{
String playerUUID = player.getUniqueId().toString();
psGetPlayerKills.setString(1, playerUUID);
try (ResultSet result = psGetPlayerKills.executeQuery()) {
if (!result.next()) return 0;
return result.getInt("kills");
}
}
@Override
public void addPlayerKills(Player player, int kills) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psAddPlayerKills.setInt(1, kills);
psAddPlayerKills.setString(2, playerUUID);
psAddPlayerKills.executeUpdate();;
connection.commit();
}
public void setPlayerKills(Player player, int kills) throws SQLException {
if (kills < 0)
throw new IllegalArgumentException("Number of kills must not be negative: " + kills);
String playerUUID = player.getUniqueId().toString();
psSetPlayerKills.setInt(1, kills);
psSetPlayerKills.setString(2, playerUUID);
psSetPlayerKills.executeUpdate();
connection.commit();
}
@Override
public void close() throws SQLException {
psInitPlayerKills.close();
psGetPlayerKills.close();
psAddPlayerKills.close();
psSetPlayerKills.close();
connection.commit();
connection.close();
}
}

View File

@ -0,0 +1,10 @@
package me.jamestmartin.wasteland.store;
import me.jamestmartin.wasteland.Substate;
import me.jamestmartin.wasteland.kills.KillsStore;
import me.jamestmartin.wasteland.kit.KitStore;
public interface StoreState extends Substate {
KillsStore getKillsStore();
KitStore getKitStore();
}

View File

@ -0,0 +1,173 @@
package me.jamestmartin.wasteland.store.sqlite;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.bukkit.entity.Player;
import me.jamestmartin.wasteland.kills.KillsStore;
import me.jamestmartin.wasteland.kit.KitStore;
public class SqliteDatabase implements AutoCloseable, KillsStore, KitStore {
private static final String CREATE_KILLS_TABLE =
"CREATE TABLE IF NOT EXISTS `player_kills`"
+ "( `player` VARCHAR(36) PRIMARY KEY"
+ ", `kills` UNSIGNED INT NOT NULL"
+ ")";
private static final String INIT_PLAYER_KILLS =
"INSERT OR IGNORE INTO `player_kills`(`player`, `kills`) VALUES (?, 0)";
private static final String GET_PLAYER_KILLS =
"SELECT `kills` FROM `player_kills` WHERE `player` = ?";
private static final String ADD_PLAYER_KILLS =
"UPDATE `player_kills` SET `kills`=`kills` + ? WHERE `player` = ?";
private static final String SET_PLAYER_KILLS =
"UPDATE `player_kills` SET `kills` = ? WHERE `player` = ?";
private final PreparedStatement psInitPlayerKills;
private final PreparedStatement psGetPlayerKills;
private final PreparedStatement psAddPlayerKills;
private final PreparedStatement psSetPlayerKills;
private static final String CREATE_PLAYER_KITS_TABLE =
"CREATE TABLE IF NOT EXISTS `player_kits`"
+ "( `player` VARCHAR(36) PRIMARY KEY"
+ ", `total_kits` UNSIGNED INT NOT NULL"
+ ", `last_time` UNSIGNED INT"
+ ")";
private static final String INIT_PLAYER_KITS =
"INSERT OR IGNORE INTO `player_kits`(`player`, `total_kits`, `last_time`) VALUES (?, 0, 0)";
private static final String GET_PLAYER_LAST_KIT_TIME =
"SELECT `last_time` FROM `player_kits` WHERE `player` = ?";
private static final String GET_PLAYER_TOTAL_KITS =
"SELECT `total_kits` FROM `player_kits` WHERE `player` = ?";
private static final String USE_PLAYER_KIT =
"UPDATE `player_kits` SET `total_kits` = `total_kits` + 1, `last_time` = ? WHERE `player` = ?";
private final PreparedStatement psInitPlayerKits;
private final PreparedStatement psGetPlayerLastKitTime;
private final PreparedStatement psGetPlayerTotalKits;
private final PreparedStatement psUsePlayerKit;
private final Connection connection;
public SqliteDatabase(File file)
throws IOException, ClassNotFoundException, SQLException {
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
// Ensures that JDBC is installed.
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + file);
// I want to let this be true, but it doesn't work if it's true.
// I can't figure out why.
connection.setAutoCommit(false);
// Initialize database tables
Statement s = connection.createStatement();
s.executeUpdate(CREATE_KILLS_TABLE);
s.executeUpdate(CREATE_PLAYER_KITS_TABLE);
s.close();
psInitPlayerKills = connection.prepareStatement(INIT_PLAYER_KILLS);
psGetPlayerKills = connection.prepareStatement(GET_PLAYER_KILLS);
psAddPlayerKills = connection.prepareStatement(ADD_PLAYER_KILLS);
psSetPlayerKills = connection.prepareStatement(SET_PLAYER_KILLS);
psInitPlayerKits = connection.prepareStatement(INIT_PLAYER_KITS);
psGetPlayerLastKitTime = connection.prepareStatement(GET_PLAYER_LAST_KIT_TIME);
psGetPlayerTotalKits = connection.prepareStatement(GET_PLAYER_TOTAL_KITS);
psUsePlayerKit = connection.prepareStatement(USE_PLAYER_KIT);
}
public void initPlayer(Player player) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psInitPlayerKills.setString(1, playerUUID);
psInitPlayerKills.executeUpdate();
psInitPlayerKits.setString(1, playerUUID);
psInitPlayerKits.executeUpdate();
connection.commit();
}
@Override
public int getPlayerKills(Player player) throws SQLException
{
String playerUUID = player.getUniqueId().toString();
psGetPlayerKills.setString(1, playerUUID);
try (ResultSet result = psGetPlayerKills.executeQuery()) {
if (!result.next()) throw new SQLException("No result for player kills");
return result.getInt("kills");
}
}
@Override
public void addPlayerKills(Player player, int kills) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psAddPlayerKills.setInt(1, kills);
psAddPlayerKills.setString(2, playerUUID);
psAddPlayerKills.executeUpdate();;
connection.commit();
}
@Override
public void setPlayerKills(Player player, int kills) throws SQLException {
if (kills < 0)
throw new IllegalArgumentException("Number of kills must not be negative: " + kills);
String playerUUID = player.getUniqueId().toString();
psSetPlayerKills.setInt(1, kills);
psSetPlayerKills.setString(2, playerUUID);
psSetPlayerKills.executeUpdate();
connection.commit();
}
@Override
public long getLastKitTime(Player player) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psGetPlayerLastKitTime.setString(1, playerUUID);
try (ResultSet result = psGetPlayerLastKitTime.executeQuery()) {
if (!result.next()) throw new SQLException("No result for player last kit time");
// FIXME: This value can be null.
return result.getLong("last_time");
}
}
@Override
public int getTotalKitsRecieved(Player player) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psGetPlayerTotalKits.setString(1, playerUUID);
try (ResultSet result = psGetPlayerTotalKits.executeQuery()) {
if (!result.next()) throw new SQLException("No result for player total kits received");
return result.getInt("total_kits");
}
}
@Override
public void useKit(Player player) throws SQLException {
String playerUUID = player.getUniqueId().toString();
psUsePlayerKit.setLong(1, player.getWorld().getFullTime());
psUsePlayerKit.setString(2, playerUUID);
psUsePlayerKit.executeUpdate();
connection.commit();
}
@Override
public void close() throws SQLException {
psInitPlayerKills.close();
psGetPlayerKills.close();
psAddPlayerKills.close();
psSetPlayerKills.close();
psInitPlayerKits.close();
psGetPlayerLastKitTime.close();
psGetPlayerTotalKits.close();
psUsePlayerKit.close();
connection.commit();
connection.close();
}
}

View File

@ -0,0 +1,32 @@
package me.jamestmartin.wasteland.store.sqlite;
import java.sql.SQLException;
import java.util.logging.Level;
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.PlayerJoinEvent;
import me.jamestmartin.wasteland.Wasteland;
public class SqliteInitializerListener implements Listener {
private final SqliteDatabase db;
public SqliteInitializerListener(SqliteDatabase db) {
this.db = db;
}
// I'd prefer for this to be MONITOR, but it needs to
// make sure the database is initialized before the e.g. RankAttachments.
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
try {
db.initPlayer(player);
} catch (SQLException e) {
Wasteland.getInstance().getLogger().log(Level.SEVERE, "Failed to initialize player in database.", e);
player.sendMessage("ERROR: Failed to initialize you in the Wasteland database. Please contact a server administrator.");
}
}
}

View File

@ -0,0 +1,63 @@
package me.jamestmartin.wasteland.store.sqlite;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Level;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import me.jamestmartin.wasteland.Wasteland;
import me.jamestmartin.wasteland.kills.KillsStore;
import me.jamestmartin.wasteland.kit.KitStore;
import me.jamestmartin.wasteland.store.StoreState;
public class SqliteState implements StoreState {
private final SqliteDatabase database;
private final SqliteInitializerListener initializerListener;
public SqliteState(String databaseFilename) throws IOException, ClassNotFoundException, SQLException {
database = new SqliteDatabase(new File(Wasteland.getInstance().getDataFolder(), databaseFilename));
initializerListener = new SqliteInitializerListener(database);
}
@Override
public void disable(JavaPlugin plugin) {
try {
database.close();
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "SQLite exception while closing database.", e);
plugin.getPluginLoader().disablePlugin(plugin);
}
}
@Override
public void register(JavaPlugin plugin) {
for (Player player : plugin.getServer().getOnlinePlayers()) {
try {
database.initPlayer(player);
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to initialize player", e);
}
}
}
@Override
public void unregister(JavaPlugin plugin) {
PlayerJoinEvent.getHandlerList().unregister(initializerListener);
disable(plugin);
}
@Override
public KillsStore getKillsStore() {
return database;
}
@Override
public KitStore getKitStore() {
return database;
}
}

View File

@ -122,7 +122,7 @@ enlisted:
fstsgt:
name: First Sergeant
abbreviation: 1stSgt
description: {kills} kills. Alternative rank to GySgt.
description: "{kills} kills. Alternative rank to GySgt."
succeeds: gysgt
color: DARK_RED
kills: 25000
@ -136,7 +136,7 @@ enlisted:
sgtmaj:
name: Sergeant Major
abbreviation: SgtMaj
description: A survivor of the wasteland, with over {kills} kills. Alternative rank to MGySgt, promoted from 1stSgt.
description: A survivor of the wasteland, with over {kills} kills. Alternative rank to MSgt, promoted from 1stSgt.
succeeds: fstsgt
color: DARK_PURPLE
kills: 50000
@ -349,4 +349,21 @@ spawns:
WAXING_GIBBOUS: 1.5
WANING_GIBBOUS: 1.5
NEW: 0.5
kit:
# How long a player has to wait between kits, in ticks.
# The default is five minutes.
period: 6000
# A random durability will be chosen per tool depending on how many kits the player has received.
# The tool will not be dropped if the durability chosen is less than one eight of the item's max.
tools:
- GOLDEN_SWORD
- GOLDEN_PICKAXE
- GOLDEN_SHOVEL
- GOLDEN_AXE
- GOLDEN_HOE
# A random quantity will be chosen depending on how many kits the player has received.
# The item type will not be dropped if the quantity chosen is less than one eighth of the maximum.
items:
ROTTEN_FLESH: 32

View File

@ -50,6 +50,12 @@ commands:
usage: "Usage: /<command>"
permission: wasteland.debug.spawns.weights
permission-message: You do not have permission to view spawn debug information.
kit:
description: Get some free starter gear.
usage: "Usage: /<command>"
permission: wasteland.kit
permission-message: You do not have permission to receive a starter kit.
permissions:
wasteland.reload:
@ -107,3 +113,7 @@ permissions:
wasteland.debug.spawns.weights:
description: Allows you to see the spawn weights by mob type at a block.
default: false
wasteland.kit:
description: Allows you to receive a starter kit.
default: true