Begin improving documentation: update README, add some JavaDoc.

master
James T. Martin 2020-11-24 22:21:34 -08:00
parent f7f37a18f8
commit ea101d5c07
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
14 changed files with 351 additions and 62 deletions

View File

@ -1,14 +1,71 @@
# Wasteland
My friend Obamallama is making an apocalyse-themed Minecraft server,
which is in many ways the spiritual successor to the Minecraft servers Skuli_Steinulf and I used to run.
# Wasteland Plugin Suite
This is the plugin suite I am developing for my work-in-progress Minecraft server, Wasteland.
This plugin will implement the custom functionality for that server that he cannot accomplish through existing plugins.
The system we (Skuli and I) originally used to implement our custom functionality is long dead,
so Obama cannot simply copy over the original code.
I will also be implementing new features that did not exist in the original server.
Wasteland is a post-nuclear zombie apocalypse server with a gameplay experience dramatically different from any other.
It includes custom world generation, a custom (sophisticated!) spawn algorithm, a radiation system,
world and weather events, a player rank system based on the Marines, handles chat,
and includes many, many other supplementary features.
Obama has not been working on the server for a few months now, but still intends to continue.
I will continue work on this plugin when that time comes.
The server is spiritual successor to the servers Skuli_Steinulf and I used to run.
Huge credit goes to Skuli_Steinulf for founding it all, and he and my friends Lyokan and Obamallama for many ideas and much help.
## The Wasteland Plugin
I don't feel like documenting all of this plugin's features right now; I'll do it later.
## The Wasteland Permissions Plugin
This plugin was motivated because existing permissions were either
* buggy,
* incompatible with the `/reload` command,
* not fully compatible with Java 9+ (LuckPerms in particular), or
* encompassed a broader scope than I wanted, interfering with other features.
So I implemented my own, an extremely simple plugin which loads groups and player permissions from a YAML config file.
It only took two or three hours to write, but is exactly what I need and does the job perfectly.
For documentation on how to use this plugin, please see the default `config.yml`.
## The Wasteland ModTools plugin
**This plugin is currently only partially implemented.**
This plugin provides tools which both help moderators track what rule infractions a player has committed,
and help server administrators hold moderators accountable through an audit log.
This plugin provides these basic commands:
* `/ban` and `/unban`
* `/kick`
* `/mute` and `/unmute`
* `/warn`
All bans, kicks, mutes, and warnings are logged,
both to help moderators track what rule infractions a player has committed,
and to help server administrators hold moderators accountable through an audit log.
It is also possible to set the maximum length of bans or mutes that a moderator can issue
in the configuration file.
You may view the currently active bans and mutes using `/bans` and `/mutes` respectively,
or see the history of infractions a player has committed using `/infractions`.
You can remove an infraction from a player's account using commands like `/unwarn`.
Removing an infraction from a player's account will not remove the infraction from the audit log,
and in fact will just add that the infraction was removed to the audit log.
You may view the audit log using `/auditlog`.
For more information on how to use these commands please see the in game `/help` or the `plugin.yml`
For more information on how to configure this plugin, please see the default `config.yml`.
### Infraction histories & audit logs
A set of moderator tools which logs all moderator actions,
### Manuals
There are two built-in manuals: `/rules` and `/faq`.
These manuals provide both brief overviews and more detail which players may view if they wish.
It is also possible for moderators to (forcibly) show players portions of the rules or faq,
as a brief reminder of the rules or to quickly answer a question they were asking.
Please see the in-game `/help` menu or `plugin.yml` for more information on how to use these commands.
## Current Features
* Tracks how many zombies or husks a player kills.

View File

@ -1,5 +1,14 @@
#
# Groups are collections of permissions which make it easier to assign a set of permissions to a lot of players at once.
# It also makes it easier to define permission hierarchies (e.g. trial mod, mod, head mod, admin),
# through the inheritence mechanism.
#
# However, you are not required to use them and can assign permissions directly to players if you choose.
#
# groups:
# # The default group will only be assigned to players with no other group assigned.
# # If you assign a player a group, they will not receive the default permissions,
# # unless you explicitly unclude them as a member of this group.
# default:
# permission.for.newbies: true # track a player's permission
# <groupName>:
@ -16,7 +25,7 @@
# c.d: true
#
# players:
# # You *must* use player UUIDs, not player names.
# # You *must* use player UUIDs, not player names, because player names are not guaranteed to be unique across sessions.
# # You can see a player's UUID in the server log when they join, or look it up online.
# # I would recommend including the player's name as a comment to help you remember whose UUID this is.
# <player-uuid>:

View File

@ -8,32 +8,34 @@ version '0.1.0'
sourceCompatibility = 14
repositories {
mavenCentral()
mavenCentral()
maven {
url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
maven {
url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
content {
includeGroup 'org.bukkit'
includeGroup 'org.spigotmc'
content {
includeGroup 'org.bukkit'
includeGroup 'org.spigotmc'
}
}
}
maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' }
maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' }
maven {
name = "Towny"
url = "https://maven.pkg.github.com/TownyAdvanced/Towny"
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
maven {
name = "Towny"
url = "https://maven.pkg.github.com/TownyAdvanced/Towny"
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
}
}
}
}
dependencies {
compileOnly 'org.spigotmc:spigot-api:1.16.4-R0.1-SNAPSHOT'
compileOnly 'com.palmergames.bukkit.towny:Towny:0.96.2.17'
compileOnly 'com.google.guava:guava:21.0'
// compileOnly 'com.google.guava:guava:27.1
// APIs and libraries provided with the server distribution
compileOnly 'org.spigotmc:spigot-api:1.16.4-R0.1-SNAPSHOT'
compileOnly 'com.google.guava:guava:21.0'
// Plugin APIs
compileOnly 'com.palmergames.bukkit.towny:Towny:0.96.2.17'
}

View File

@ -4,6 +4,9 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
/**
* The command used to manage the state of the {@link Wasteland plugin} as a whole.
*/
class CommandWasteland implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {

View File

@ -2,13 +2,68 @@ package me.jamestmartin.wasteland;
import org.bukkit.plugin.java.JavaPlugin;
/**
* <p>
* This plugin implements a lot of functionality,
* and although it is all needed together to create the desired player experience,
* most parts of the plugin do not depend on most other parts.
* For example, the spawning subsystem does not depend on the chat subsystem.
* However, splitting the plugin into many tiny independent plugins would be excessive.
*
* <p>
* Thus, instead, the plugin is split into many submodules which each manages its own
* state, configuration, et cetera independently.
* This interface is a common interface to those submodules.
*
* @see WastelandState The plugin's main state class which contains all of the other substates.
*/
public interface Substate {
/**
* Register all:
* <ul>
* <li>commands
* <li>listeners
* <li>permission attachments
* </ul>
* and connect to any databases.
*
* @param plugin
* Bukkit requires a {@link JavaPlugin} instances
* to register commands and listeners *for*.
*/
void register(JavaPlugin plugin);
/**
* Disable and unregister all:
* <ul>
* <li>commands
* <li>listeners
* <li>permission attachments
* </ul>
* and disconnect from any databases.
*
* @param plugin
* Bukkit requires a {@link JavaPlugin} instances
* to unregister commands and listeners *from*.
*
* @see #disable(JavaPlugin)
*/
void unregister(JavaPlugin plugin);
/**
* Close everything that needs to be closed without unregistering anything.
* This is called when the plugin is being disabled.
* <p>
* Close everything that needs to be closed without
* {@link #unregister(JavaPlugin) unregistering} anything.
*
* <p>
* When a plugin is disabled, Bukkit automatically unregisters most things, like commands, listeners, and attachments,
* and in fact if we tried to unregister themselves, exceptions would be thrown.
* However, it may still be necessary to do things like clean up database connections,
* which is the purpose of this method.
*
* @param plugin The plugin being disabled.
*
* @see #unregister(JavaPlugin)
*/
default void disable(JavaPlugin plugin) { }
}

View File

@ -18,10 +18,27 @@ public class Wasteland extends JavaPlugin {
private WastelandConfig config;
private WastelandState state;
/**
* The active, enabled instance of this plugin.
*
* @return
* May be null if the plugin:
* <ul>
* <li>has been loaded but not yet enabled,
* <li>has been disabled,
* <li>is in the process of reloading.
* </ul>
*/
public static Wasteland getInstance() {
return instance;
}
/**
* @return
* Either the proxy for accessing Towny if the plugin is loaded,
* or a dummy implementation.
* @see TownyDependency
*/
public static TownyDependency getTowny() {
return towny;
}
@ -43,12 +60,18 @@ public class Wasteland extends JavaPlugin {
@Override
public void onDisable() {
// We do not need to disable
state.disable(this);
state = null;
towny = null;
instance = null;
}
/**
* Completely disable and re-enable the plugin,
* which has the effect of reloading the plugin's configuration
* and database.
*/
public void reload() {
getLogger().info("Reloading wasteland...");
saveDefaultConfig();
@ -64,6 +87,7 @@ public class Wasteland extends JavaPlugin {
config = ConfigParser.parseConfig(getConfig());
}
/** Register all commands, listeners, permission attachments, etc. */
private void register() {
try {
state = new WastelandState(config);
@ -80,6 +104,7 @@ public class Wasteland extends JavaPlugin {
state.register(this);
}
/** Disable all commands, unregister all listeners and permission attachments, etc. */
private void unregister() {
state.unregister(this);
state = null;

View File

@ -15,7 +15,15 @@ import me.jamestmartin.wasteland.spawns.SpawnsState;
import me.jamestmartin.wasteland.store.StoreState;
import me.jamestmartin.wasteland.store.sqlite.SqliteState;
public class WastelandState implements Substate {
/**
* This class contains virtually all of the plugin's internal state.
* Keeping this out of {@link Wasteland the plugin's main class}
* makes it easier to keep track of and reload,
* because we can reset the plugin's state just by setting this to null
* and creating a new instance,
* rather than having to handle each sub-state individually.
*/
class WastelandState implements Substate {
private final CommandWasteland commandWasteland;
private final StoreState storeState;

View File

@ -69,7 +69,7 @@ public class ChatConfig implements ChatFormatter {
}
private String formatPlayerTownPrefix(Player player) {
Optional<String> townTag = Wasteland.getTowny().getTownAbbreviation(player);
Optional<String> townTag = Wasteland.getTowny().getTownTag(player);
if (townTag.isEmpty()) {
return "";
}

View File

@ -5,13 +5,20 @@ import java.util.HashSet;
import org.bukkit.entity.EntityType;
/**
* Collections of {@link EntityType}s.
*
* The primary purpose of this class is to provide {@link #lookupEntityType(String)}.
*/
class EntityTypes {
private static final EntityType[] BOSSES = {
/** The ender dragon and wither. */
public static final EntityType[] BOSSES = {
EntityType.ENDER_DRAGON,
EntityType.WITHER,
};
private static final EntityType[] HOSTILES = {
/** Monsters which will attack the player on sight. */
public static final EntityType[] HOSTILES = {
EntityType.BLAZE,
EntityType.CREEPER,
EntityType.DROWNED,
@ -44,7 +51,8 @@ class EntityTypes {
EntityType.ZOMBIE_VILLAGER,
};
private static final EntityType[] NEUTRALS = {
/** Mobs which will attack the player only if provoked. */
public static final EntityType[] NEUTRALS = {
EntityType.BEE,
EntityType.CAVE_SPIDER,
EntityType.DOLPHIN,
@ -62,21 +70,23 @@ class EntityTypes {
/**
* Monsters which are eligible for the monster hunter achievement but are not hostile.
* All hostile monsters are eligable.
* All hostile monsters are eligible.
*/
private static final EntityType[] NON_HOSTILE_MONSTERS = {
public static final EntityType[] NON_HOSTILE_MONSTERS = {
EntityType.CAVE_SPIDER,
EntityType.ENDERMAN,
EntityType.SPIDER,
EntityType.ZOMBIFIED_PIGLIN,
};
private static final EntityType[] SPIDERS = {
/** Spiders and cave spiders. */
public static final EntityType[] SPIDERS = {
EntityType.CAVE_SPIDER,
EntityType.SPIDER,
};
private static final EntityType[] ZOMBIES = {
/** All zombie variants, including zombie villagers, zoglins, etc. */
public static final EntityType[] ZOMBIES = {
EntityType.GIANT,
EntityType.ZOGLIN,
EntityType.ZOMBIE,
@ -85,6 +95,22 @@ class EntityTypes {
EntityType.ZOMBIFIED_PIGLIN,
};
/**
* @param typeName
* Either a string representing an EntityType value,
* or a lower-case string representing one of the entity collections provided by this class.
* @return
* Either the single entity type represented by the string provided,
* or the collection of entity types specified by the string provided.
*
* @see EntityType#valueOf(String)
* @see BOSSES bosses
* @see HOSTILES hostiles
* @see NEUTRALS neutrals
* @see NON_HOSTILE_MONSTERS monsters
* @see SPIDERS spiders
* @see ZOMBIES zombies
*/
public static EntityType[] lookupEntityType(String typeName) {
switch(typeName) {
case "bosses":

View File

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

View File

@ -1,5 +1,21 @@
package me.jamestmartin.wasteland.towny;
public interface TownyDependency extends TownAbbreviationProvider {
/**
* <p>
* This plugin would like to be able to use features of Towny if it is available,
* without being unable to function if Towny is not available.
* <p>
* Any class which imports Towny packages will not be possible to load if Towny is not available,
* so instead, we restrict all Towny imports to one class, and decide whether or not to load it at runtime.
* If Towny is not available, we will instead use a dummy class which does nothing and returns nothing.
* <p>
* We split the functionality of towny that one might want to access into various provider interfaces.
* This interface collects them all into a single interface so that they might all be implemented
* at once and loaded at once through the enabled/disabled implementation.
*
* @see TownyDisabled The dummy implementation used if Towny is not available.
* @see TownyEnabled The actual implementation used if Towny *is* available.
*/
public interface TownyDependency extends TownyTagProvider {
}

View File

@ -4,9 +4,24 @@ import java.util.Optional;
import org.bukkit.entity.Player;
/**
* <p>
* A set of dummy implementations of Towny-related functionality
* which we will substitute for the actual functionality if Towny is not installed.
* <p>
* These methods do nothing and return nothing.
*
* @see TownyDependency
* @see TownyEnabled
*/
public class TownyDisabled implements TownyDependency {
@Override
public Optional<String> getTownAbbreviation(Player player) {
public Optional<String> getTownTag(Player player) {
return Optional.empty();
}
@Override
public Optional<String> getNationTag(Player player) {
return Optional.empty();
}
}

View File

@ -3,22 +3,57 @@ package me.jamestmartin.wasteland.towny;
import java.util.Optional;
import org.bukkit.entity.Player;
import com.palmergames.bukkit.towny.TownyAPI;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Government;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
/**
* The one class in the entire plugin that directly depends on Towny.
* If Towny is installed, it will be dynamically loaded;
* if not, a dummy implementation will be loaded instead.
*
* @see TownyDependency
* @see TownyDisabled
*/
public class TownyEnabled implements TownyDependency {
private static Resident getResident(Player player) throws NotRegisteredException {
return TownyAPI.getInstance().getDataSource().getResident(player.getName());
}
private static Optional<Town> getTown(Player player) {
try {
return Optional.of(getResident(player).getTown());
} catch (NotRegisteredException e) {
return Optional.empty();
}
}
private static Optional<Nation> getNation(Player player) {
try {
return Optional.of(getResident(player).getTown().getNation());
} catch (NotRegisteredException e) {
return Optional.empty();
}
}
private static Optional<String> getTag(Government gov) {
String tag = gov.getTag();
if (tag == null || tag.isBlank()) {
return Optional.empty();
}
return Optional.of(tag);
}
@Override
public Optional<String> getTownAbbreviation(Player player) {
try {
Resident resident = TownyAPI.getInstance().getDataSource().getResident(player.getName());
String tag = resident.getTown().getTag();
if (tag != null && !tag.equals("")) {
return Optional.of(tag);
}
} catch (NotRegisteredException e) {
}
return Optional.empty();
public Optional<String> getTownTag(Player player) {
return getTown(player).flatMap(TownyEnabled::getTag);
}
@Override
public Optional<String> getNationTag(Player player) {
return getNation(player).flatMap(TownyEnabled::getTag);
}
}

View File

@ -0,0 +1,47 @@
package me.jamestmartin.wasteland.towny;
import java.util.Optional;
import org.bukkit.entity.Player;
/**
* Provides the tags for the town and nation that a player is a member of,
* if Towny is installed and the player is a member of a town or nation.
*/
public interface TownyTagProvider {
/**
* <p>
* A town tag is a short identifier for a town which the town may opt to set.
* It may be used in places where the town's full name would be too long,
* for example to include what town a player is member of in chat.
* <p>
* If Towny is installed, the player is a member of a town,
* and that town has opted to set a town tag,
* then this method will get that town tag.
*
* @param player
* @return
* If the player is in a town which has a town tag, that town tag;
* otherwise, the return value will be empty.
* @see #getNationTag(Player)
*/
public Optional<String> getTownTag(Player player);
/**
* <p>
* A nation tag is a short identifier for a nation which the nation may opt to set.
* It may be used in places where the nation's full name would be too long,
* for example to include what nation a player is member of in chat.
* <p>
* If Towny is installed, the player is a member of a nation,
* and that nation has opted to set a nation tag,
* then this method will get that nation tag.
*
* @param player
* @return
* If the player is in a nation which has a nation tag, that town nation;
* otherwise, the return value will be empty.
* @see #getTownTag(Player)
*/
public Optional<String> getNationTag(Player player);
}