Merge pull request #31 from TBMCPlugins/dev

Command annotations added instead of methods, improved channels
This commit is contained in:
Norbi Peti 2017-05-15 02:33:21 +02:00 committed by GitHub
commit c0e4a9e065
26 changed files with 514 additions and 224 deletions

2
.factorypath Normal file
View file

@ -0,0 +1,2 @@
<factorypath>
</factorypath>

1
.gitignore vendored
View file

@ -224,3 +224,4 @@ TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
*.xml
TBMC/
/.apt_generated/

View file

@ -1,12 +1,14 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.8
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.processAnnotations=enabled
org.eclipse.jdt.core.compiler.source=1.8

View file

@ -0,0 +1 @@
buttondevteam.lib.AnnotationProcessor

11
pom.xml
View file

@ -9,12 +9,6 @@
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
@ -34,6 +28,7 @@
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-proc:none</compilerArgument>
</configuration>
</plugin>
<plugin>
@ -76,7 +71,7 @@
<outputDirectory>target</outputDirectory>
<resources>
<resource>
<directory>resources</directory>
<directory>src/main/resources</directory>
</resource>
</resources>
</configuration>
@ -114,7 +109,7 @@
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
<dependency>
<groupId>org.apache.commons</groupId>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
<scope>provided</scope>

View file

@ -5,43 +5,35 @@ import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
public class CommandCaller implements CommandExecutor {
private static final String REGISTER_ERROR_MSG = "An error occured while registering commands";
private CommandCaller() {
}
private static CommandCaller instance;
public static void RegisterCommand(TBMCCommandBase cmd) {
public static void RegisterCommand(TBMCCommandBase cmd) throws Exception {
if (instance == null)
instance = new CommandCaller();
if (cmd.GetCommandPath() == null) {
TBMCCoreAPI.SendException(REGISTER_ERROR_MSG,
new Exception("Command " + cmd.getClass().getSimpleName() + " has no command path!"));
return;
}
if (cmd.getPlugin() == null) {
TBMCCoreAPI.SendException(REGISTER_ERROR_MSG,
new Exception("Command " + cmd.GetCommandPath() + " has no plugin!"));
return;
}
String topcmd = cmd.GetCommandPath();
if (topcmd == null)
throw new Exception("Command " + cmd.getClass().getSimpleName() + " has no command path!");
if (cmd.getPlugin() == null)
throw new Exception("Command " + cmd.GetCommandPath() + " has no plugin!");
int i;
String topcmd;
if ((i = (topcmd = cmd.GetCommandPath()).indexOf(' ')) != -1) // Get top-level command
topcmd = cmd.GetCommandPath().substring(0, i);
if ((i = topcmd.indexOf(' ')) != -1) // Get top-level command
topcmd = topcmd.substring(0, i);
{
PluginCommand pc = ((JavaPlugin) cmd.getPlugin()).getCommand(topcmd);
if (pc == null)
TBMCCoreAPI.SendException(REGISTER_ERROR_MSG, new Exception("Top level command " + topcmd
+ " not registered in plugin.yml for plugin: " + cmd.getPlugin().getName()));
throw new Exception("Top level command " + topcmd + " not registered in plugin.yml for plugin: "
+ cmd.getPlugin().getName());
else
pc.setExecutor(instance);
}
@ -69,14 +61,11 @@ public class CommandCaller implements CommandExecutor {
}
return true;
}
if (cmd.GetModOnly() && !MainPlugin.permission.has(sender, "tbmc.admin")) {
if (cmd.getClass().getAnnotation(CommandClass.class).modOnly()
&& !MainPlugin.permission.has(sender, "tbmc.admin")) {
sender.sendMessage("§cYou need to be a mod to use this command.");
return true;
}
if (cmd.GetPlayerOnly() && !(sender instanceof Player)) {
sender.sendMessage("§cOnly ingame players can use this command.");
return true;
}
final String[] cmdargs = args.length > 0 ? Arrays.copyOfRange(args, args.length - argc, args.length) : args;
try {
if (!cmd.OnCommand(sender, alias, cmdargs)) {

View file

@ -1,22 +1,16 @@
package buttondevteam.core;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.chat.Channel.RecipientTestResult;
import buttondevteam.lib.player.TBMCPlayerBase;
import net.milkbowl.vault.permission.Permission;
@ -27,7 +21,6 @@ public class MainPlugin extends JavaPlugin {
private PluginDescriptionFile pdfFile;
private Logger logger;
private int C = 0, keep = 0;
@Override
public void onEnable() {
@ -42,46 +35,15 @@ public class MainPlugin extends JavaPlugin {
TBMCChatAPI.AddCommand(this, ScheduledRestartCommand.class);
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
TBMCCoreAPI.RegisterUserClass(TBMCPlayerBase.class);
logger.info(pdfFile.getName() + " has been Enabled (V." + pdfFile.getVersion() + ").");
Arrays.stream(new File(ChromaGamerBase.TBMC_PLAYERS_DIR).listFiles(f -> !f.isDirectory())).map(f -> {
YamlConfiguration yc = new YamlConfiguration();
try {
yc.load(f);
} catch (IOException | InvalidConfigurationException e) {
TBMCCoreAPI.SendException("Error while converting player data!", e);
}
f.delete();
return yc;
}).forEach(yc -> {
try {
int flairtime = yc.getInt("flairtime"), fcount = yc.getInt("fcount"), fdeaths = yc.getInt("fdeaths");
String flairstate = yc.getString("flairstate");
List<String> usernames = yc.getStringList("usernames");
boolean flaircheater = yc.getBoolean("flaircheater");
final String uuid = yc.getString("uuid");
C++;
if ((fcount == 0 || fdeaths == 0)
&& (flairstate == null || "NoComment".equals(flairstate) || flairtime <= 0))
return; // Those who received no Fs yet will also get their stats reset if no flair
final File file = new File(ChromaGamerBase.TBMC_PLAYERS_DIR + "minecraft", uuid + ".yml");
YamlConfiguration targetyc = YamlConfiguration.loadConfiguration(file);
targetyc.set("PlayerName", yc.getString("playername"));
targetyc.set("minecraft_id", uuid);
ConfigurationSection bc = targetyc.createSection("ButtonChat");
bc.set("FlairTime", "NoComment".equals(flairstate) ? -3 : flairtime); // FlairTimeNone: -3
bc.set("FCount", fcount);
bc.set("FDeaths", fdeaths);
bc.set("FlairState", flairstate);
bc.set("UserNames", usernames);
bc.set("FlairCheater", flaircheater);
targetyc.save(file);
keep++;
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while converting player data!", e);
}
});
Bukkit.getScheduler().runTask(this, () -> logger.info("Converted " + keep + " player data from " + C));
//TODO: Remove once ran it at least once
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null));
TBMCChatAPI.RegisterChatChannel(
Channel.AdminChat = new Channel("§cADMIN§f", Color.Red, "a", s -> s.isOp() ? new RecipientTestResult(0)
: new RecipientTestResult("You need to be an admin to use this channel.")));
TBMCChatAPI.RegisterChatChannel(Channel.ModChat = new Channel("§9MOD§f", Color.Blue, "mod",
s -> s.isOp() || (s instanceof Player && MainPlugin.permission.playerInGroup((Player) s, "mod"))
? new RecipientTestResult(0) //
: new RecipientTestResult("You need to be a mod to use this channel.")));
logger.info(pdfFile.getName() + " has been Enabled (V." + pdfFile.getVersion() + ") Test: " + Test + ".");
}
@Override

View file

@ -9,8 +9,10 @@ import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitTask;
import buttondevteam.lib.ScheduledServerRestartEvent;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCCommandBase;
@CommandClass(modOnly = true, path = "schrestart")
public class ScheduledRestartCommand extends TBMCCommandBase {
private static volatile int restartcounter;
private static volatile BukkitTask restarttask;
@ -32,11 +34,7 @@ public class ScheduledRestartCommand extends TBMCCommandBase {
restartbar = Bukkit.createBossBar("Server restart in " + ticks / 20f, BarColor.RED, BarStyle.SOLID,
BarFlag.DARKEN_SKY);
restartbar.setProgress(1);
// System.out.println("Progress: " + restartbar.getProgress());
Bukkit.getOnlinePlayers().stream().forEach(p -> restartbar.addPlayer(p));
/*
* System.out.println( "Players: " + restartbar.getPlayers().stream().map(p -> p.getName()).collect(Collectors.joining(", ")));
*/
sender.sendMessage("Scheduled restart in " + ticks / 20f);
ScheduledServerRestartEvent e = new ScheduledServerRestartEvent(ticks);
Bukkit.getPluginManager().callEvent(e);
@ -50,9 +48,6 @@ public class ScheduledRestartCommand extends TBMCCommandBase {
Bukkit.broadcastMessage("§c-- The server is restarting in " + restartcounter / 20 + " seconds!");
restartbar.setProgress(restartcounter / (double) restarttime);
restartbar.setTitle(String.format("Server restart in %f.2", restartcounter / 20f));
/*
* if (restartcounter % 20 == 0) System.out.println("Progress: " + restartbar.getProgress());
*/
restartcounter--;
}, 1, 1);
return true;
@ -66,19 +61,4 @@ public class ScheduledRestartCommand extends TBMCCommandBase {
"You can optionally set the amount of ticks to wait before the restart." //
};
}
@Override
public boolean GetPlayerOnly() {
return false;
}
@Override
public boolean GetModOnly() {
return true;
}
@Override
public String GetCommandPath() {
return "schrestart";
}
}

View file

@ -11,14 +11,16 @@ import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI;
public class TestPrepare {
public static void PrepareServer() {
Bukkit.setServer(Mockito.mock(Server.class, new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
// System.out.println("Return type: " + invocation.getMethod().getReturnType());
// System.out.println(String.class.isAssignableFrom(invocation.getMethod().getReturnType()));
if (returns(invocation, String.class))
return "test";
if (returns(invocation, Logger.class))
@ -34,5 +36,6 @@ public class TestPrepare {
return cl.isAssignableFrom(invocation.getMethod().getReturnType());
}
}));
TBMCChatAPI.RegisterChatChannel(Channel.GlobalChat = new Channel("§fg§f", Color.White, "g", null));
}
}

View file

@ -4,8 +4,10 @@ import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCCommandBase;
@CommandClass(modOnly = true)
public class UpdatePluginCommand extends TBMCCommandBase {
@Override
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
@ -37,14 +39,4 @@ public class UpdatePluginCommand extends TBMCCommandBase {
"To list the plugin names: /" + alias //
};
}
@Override
public boolean GetPlayerOnly() {
return false;
}
@Override
public boolean GetModOnly() {
return true;
}
}

View file

@ -0,0 +1,33 @@
package buttondevteam.lib;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import buttondevteam.lib.player.ChromaGamerEnforcer;
/** * A simple session bean type annotation processor. The implementation * is based on the standard annotation processing API in Java 6. */
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AnnotationProcessor extends AbstractProcessor {
/** * Check if both @Stateful and @Stateless are present in an * session bean. If so, emits a warning message. */
@Override
public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment roundEnv) { // TODO: SEparate JAR
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ChromaGamerEnforcer.class);
for (Element element : elements) {
System.out.println("Processing " + element);
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
System.out.println("Annotations: " + annotationMirrors);
for (AnnotationMirror annotation : annotationMirrors) {
String type = annotation.getAnnotationType().toString();
System.out.println("Type: " + type);
}
}
return true; // claim the annotations
}
}

View file

@ -6,7 +6,14 @@ import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.Channel.RecipientTestResult;
/**
* Make sure to only send the message to users who {@link #shouldSendTo(CommandSender)} returns true.
*
* @author NorbiPeti
*
*/
public class TBMCChatEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
@ -14,11 +21,13 @@ public class TBMCChatEvent extends Event implements Cancellable {
private CommandSender sender;
private String message;
private boolean cancelled;
private int score;
public TBMCChatEvent(CommandSender sender, Channel channel, String message) {
public TBMCChatEvent(CommandSender sender, Channel channel, String message, int score) {
this.sender = sender;
this.channel = channel;
this.message = message; // TODO: Message object with data?
this.score = score;
}
/*
@ -56,4 +65,23 @@ public class TBMCChatEvent extends Event implements Cancellable {
this.cancelled = cancelled;
}
/**
* Note: Errors are sent to the sender automatically
*/
public boolean shouldSendTo(CommandSender sender) {
if (channel.filteranderrormsg == null)
return true;
RecipientTestResult result = channel.filteranderrormsg.apply(sender);
return result.errormessage == null && score == result.score;
}
/**
* Note: Errors are sent to the sender automatically
*/
public int getMCScore(CommandSender sender) {
if (channel.filteranderrormsg == null)
return 0;
RecipientTestResult result = channel.filteranderrormsg.apply(sender);
return result.errormessage == null ? result.score : -1;
}
}

View file

@ -0,0 +1,68 @@
package buttondevteam.lib;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import buttondevteam.lib.chat.Channel;
/**
* Can be used to change messages before it's sent.
*
* @author NorbiPeti
*
*/
public class TBMCChatPreprocessEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Channel channel;
private CommandSender sender;
private String message;
private boolean cancelled;
public TBMCChatPreprocessEvent(CommandSender sender, Channel channel, String message) {
this.sender = sender;
this.channel = channel;
this.message = message; // TODO: Message object with data?
}
/*
* public TBMCPlayer getPlayer() { return TBMCPlayer.getPlayer(sender); // TODO: Get Chroma user }
*/
public Channel getChannel() {
return channel;
}
public CommandSender getSender() {
return sender;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View file

@ -201,8 +201,10 @@ public class TBMCCoreAPI {
SendUnsentExceptions();
TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e);
Bukkit.getPluginManager().callEvent(event);
if (!event.isHandled())
exceptionsToSend.put(sourcemsg, e);
synchronized (exceptionsToSend) {
if (!event.isHandled())
exceptionsToSend.put(sourcemsg, e);
}
Bukkit.getLogger().warning(sourcemsg);
e.printStackTrace();
if (debugPotato) {
@ -212,7 +214,6 @@ public class TBMCCoreAPI {
devsOnline.add(player);
}
}
;
if (!devsOnline.isEmpty()) {
DebugPotato potato = new DebugPotato()
.setMessage(new String[] { //
@ -234,8 +235,10 @@ public class TBMCCoreAPI {
SendUnsentDebugMessages();
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(debugMessage);
Bukkit.getPluginManager().callEvent(event);
if (!event.isSent())
debugMessagesToSend.add(debugMessage);
synchronized (debugMessagesToSend) {
if (!event.isSent())
debugMessagesToSend.add(debugMessage);
}
}
/**
@ -258,28 +261,32 @@ public class TBMCCoreAPI {
* Send exceptions that haven't been sent (their events didn't get handled). This method is used by the DiscordPlugin's ready event
*/
public static void SendUnsentExceptions() {
if (exceptionsToSend.size() > 20) {
exceptionsToSend.clear(); // Don't call more and more events if all the handler plugins are unloaded
Bukkit.getLogger().warning("Unhandled exception list is over 20! Clearing!");
}
for (Entry<String, Throwable> entry : exceptionsToSend.entrySet()) {
TBMCExceptionEvent event = new TBMCExceptionEvent(entry.getKey(), entry.getValue());
Bukkit.getPluginManager().callEvent(event);
if (event.isHandled())
exceptionsToSend.remove(entry.getKey());
synchronized (exceptionsToSend) {
if (exceptionsToSend.size() > 20) {
exceptionsToSend.clear(); // Don't call more and more events if all the handler plugins are unloaded
Bukkit.getLogger().warning("Unhandled exception list is over 20! Clearing!");
}
for (Entry<String, Throwable> entry : exceptionsToSend.entrySet()) {
TBMCExceptionEvent event = new TBMCExceptionEvent(entry.getKey(), entry.getValue());
Bukkit.getPluginManager().callEvent(event);
if (event.isHandled())
exceptionsToSend.remove(entry.getKey());
}
}
}
public static void SendUnsentDebugMessages() {
if (debugMessagesToSend.size() > 20) {
debugMessagesToSend.clear(); // Don't call more and more DebugMessages if all the handler plugins are unloaded
Bukkit.getLogger().warning("Unhandled Debug Message list is over 20! Clearing!");
}
for (String message : debugMessagesToSend) {
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(message);
Bukkit.getPluginManager().callEvent(event);
if (event.isSent())
debugMessagesToSend.remove(message);
synchronized (debugMessagesToSend) {
if (debugMessagesToSend.size() > 20) {
debugMessagesToSend.clear(); // Don't call more and more DebugMessages if all the handler plugins are unloaded
Bukkit.getLogger().warning("Unhandled Debug Message list is over 20! Clearing!");
}
for (String message : debugMessagesToSend) {
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(message);
Bukkit.getPluginManager().callEvent(event);
if (event.isSent())
debugMessagesToSend.remove(message);
}
}
}

View file

@ -2,26 +2,41 @@ package buttondevteam.lib.chat;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
public class Channel {
public final String DisplayName;
public final Color color;
public final String Command;
public final String ID;
/**
* Filters both the sender and the targets
*/
public final Function<CommandSender, RecipientTestResult> filteranderrormsg;
private static List<Channel> channels = new ArrayList<>();
public Channel(String displayname, Color color, String command) {
/**
* Creates a channel.
*
* @param displayname
* The name that should appear at the start of the message
* @param color
* The default color of the messages sent in the channel
* @param command
* The command to be used for the channel <i>without /</i>. For example "mod". It's also used for scoreboard objective names.
* @param filteranderrormsg
* Checks all senders against the criteria provided here and sends the message if the index matches the sender's - if no score at all, displays the error.<br>
* May be null to send to everyone.
*/
public Channel(String displayname, Color color, String command,
Function<CommandSender, RecipientTestResult> filteranderrormsg) {
DisplayName = displayname;
this.color = color;
Command = command;
}
static {
channels.add(GlobalChat = new Channel("§fg§f", Color.White, "g"));
channels.add(TownChat = new Channel("§3TC§f", Color.DarkAqua, "tc"));
channels.add(NationChat = new Channel("§6NC§f", Color.Gold, "nc"));
channels.add(AdminChat = new Channel("§cADMIN§f", Color.Red, "a"));
channels.add(ModChat = new Channel("§9MOD§f", Color.Blue, "mod"));
ID = command;
this.filteranderrormsg = filteranderrormsg;
}
public static List<Channel> getChannels() {
@ -29,8 +44,36 @@ public class Channel {
}
public static Channel GlobalChat;
public static Channel TownChat;
public static Channel NationChat;
public static Channel AdminChat;
public static Channel ModChat;
static void RegisterChannel(Channel channel) {
channels.add(channel);
Bukkit.getPluginManager().callEvent(new ChatChannelRegisterEvent(channel));
}
public static class RecipientTestResult {
public String errormessage;
public int score;
/**
* Creates a result that indicates an <b>error</b>
*
* @param errormessage
* The error message to show the sender if they don't meet the criteria.
*/
public RecipientTestResult(String errormessage) {
this.errormessage = errormessage;
}
/**
* Creates a result that indicates a <b>success</b>
*
* @param score
* The score that identifies the target group. For example, the index of the town or nation to send to.
*/
public RecipientTestResult(int score) {
this.score = score;
}
}
}

View file

@ -0,0 +1,27 @@
package buttondevteam.lib.chat;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class ChatChannelRegisterEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private Channel channel;
public ChatChannelRegisterEvent(Channel channel) {
this.channel = channel;
}
public Channel getChannel() {
return channel;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -0,0 +1,41 @@
package buttondevteam.lib.chat;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <b>Abstract classes with no {@link CommandClass} annotations will be ignored.</b> Classes that are not abstract or have the annotation will be included in the command path unless
* {@link #excludeFromPath()} is true.
*
* @author NorbiPeti
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface CommandClass {
/**
* Determines whether the command can only be used by mods and above or regular players can use it as well.
*
* @return If the command is mod only
*/
public boolean modOnly();
/**
* The command's path, or name if top-level command.<br>
* For example:<br>
* "u admin updateplugin" or "u" for the top level one<br>
* <u>The path must be lowercase!</u><br>
*
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it)
*/
public String path() default "";
/**
* Exclude this class from the path. Useful if more commands share some property but aren't subcommands of a common command. See {@link CommandClass} for more details.
*/
public boolean excludeFromPath() default false;
}

View file

@ -1,24 +0,0 @@
package buttondevteam.lib.chat;
public enum Format implements TellrawSerializableEnum {
Bold("bold"), Underlined("underlined"), Italic("italic"), Strikethrough("strikethrough"), Obfuscated(
"obfuscated");
// TODO: Add format codes to /u c <mode>
private String name;
Format(String name) {
this.name = name;
this.flag = 1 << this.ordinal();
}
@Override
public String getName() {
return name;
}
private final int flag;
public int getFlag() {
return flag;
}
}

View file

@ -0,0 +1,30 @@
package buttondevteam.lib.chat;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import buttondevteam.lib.TBMCCoreAPI;
public abstract class OptionallyPlayerCommandBase extends TBMCCommandBase {
public boolean OnCommand(Player player, String alias, String[] args) {
if (getClass().isAnnotationPresent(OptionallyPlayerCommandClass.class)
&& getClass().getAnnotation(OptionallyPlayerCommandClass.class).playerOnly())
TBMCCoreAPI.SendException("Error while executing command " + getClass().getSimpleName() + "!",
new Exception(
"The PlayerCommand annotation is present and set to playerOnly but the Player overload isn't overriden!"));
return true;
}
@Override
public boolean OnCommand(CommandSender sender, String alias, String[] args) {
if (sender instanceof Player)
return OnCommand((Player) sender, alias, args);
if (!getClass().isAnnotationPresent(OptionallyPlayerCommandClass.class)
|| !getClass().getAnnotation(OptionallyPlayerCommandClass.class).playerOnly())
TBMCCoreAPI.SendException("Error while executing command " + getClass().getSimpleName() + "!",
new Exception(
"Command class doesn't override the CommandSender overload and no PlayerCommandClass annotation is present or playerOnly is false!"));
sender.sendMessage("§cYou need to be a player to use this command.");
return true;
}
}

View file

@ -0,0 +1,20 @@
package buttondevteam.lib.chat;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Only needed to use with {@link OptionallyPlayerCommandBase} command classes
*
* @author NorbiPeti
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface OptionallyPlayerCommandClass {
public boolean playerOnly();
}

View file

@ -0,0 +1,16 @@
package buttondevteam.lib.chat;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public abstract class PlayerCommandBase extends TBMCCommandBase {
public abstract boolean OnCommand(Player player, String alias, String[] args);
@Override
public final boolean OnCommand(CommandSender sender, String alias, String[] args) {
if (sender instanceof Player)
return OnCommand((Player) sender, alias, args);
sender.sendMessage("§cYou need to be a player to use this command.");
return true;
}
}

View file

@ -4,6 +4,7 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
@ -19,7 +20,9 @@ import org.reflections.util.ConfigurationBuilder;
import buttondevteam.core.CommandCaller;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatPreprocessEvent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Channel.RecipientTestResult;
public class TBMCChatAPI {
@ -59,19 +62,20 @@ public class TBMCChatAPI {
cmds.add("§6---- Subcommands ----");
cmds.add(cmd);
};
for (TBMCCommandBase cmd : TBMCChatAPI.GetCommands().values()) {
if (cmd.GetCommandPath().startsWith(command + " ")) {
if (cmd.GetPlayerOnly() && !(sender instanceof Player))
for (Entry<String, TBMCCommandBase> cmd : TBMCChatAPI.GetCommands().entrySet()) {
if (cmd.getKey().startsWith(command + " ")) {
if (cmd.getValue().isPlayerOnly() && !(sender instanceof Player))
continue;
if (cmd.GetModOnly() && !MainPlugin.permission.has(sender, "tbmc.admin"))
if (cmd.getClass().getAnnotation(CommandClass.class).modOnly()
&& !MainPlugin.permission.has(sender, "tbmc.admin"))
continue;
int ind = cmd.GetCommandPath().indexOf(' ', command.length() + 2);
int ind = cmd.getKey().indexOf(' ', command.length() + 2);
if (ind >= 0) {
String newcmd = cmd.GetCommandPath().substring(0, ind);
String newcmd = cmd.getKey().substring(0, ind);
if (!cmds.contains("/" + newcmd))
addToCmds.accept("/" + newcmd);
} else
addToCmds.accept("/" + cmd.GetCommandPath());
addToCmds.accept("/" + cmd.getKey());
}
}
return cmds.toArray(new String[cmds.size()]);
@ -96,15 +100,21 @@ public class TBMCChatAPI {
* @param acmdclass
* A command's class to get the package name for commands. The provided class's package and subpackages are scanned for commands.
*/
public static void AddCommands(JavaPlugin plugin, Class<? extends TBMCCommandBase> acmdclass) {
plugin.getLogger().info("Registering commands for " + plugin.getName());
public static synchronized void AddCommands(JavaPlugin plugin, Class<? extends TBMCCommandBase> acmdclass) {
plugin.getLogger().info("Registering commands from " + acmdclass.getPackage().getName());
Reflections rf = new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage(acmdclass.getPackage().getName(),
plugin.getClass().getClassLoader()))
.addUrls(
ClasspathHelper.forClass(OptionallyPlayerCommandBase.class,
OptionallyPlayerCommandBase.class.getClassLoader()),
ClasspathHelper.forClass(PlayerCommandBase.class, PlayerCommandBase.class.getClassLoader())) // http://stackoverflow.com/questions/12917417/using-reflections-for-finding-the-transitive-subtypes-of-a-class-when-not-all
.addClassLoader(plugin.getClass().getClassLoader()).addScanners(new SubTypesScanner()));
Set<Class<? extends TBMCCommandBase>> cmds = rf.getSubTypesOf(TBMCCommandBase.class);
for (Class<? extends TBMCCommandBase> cmd : cmds) {
try {
if (!cmd.getPackage().getName().startsWith(acmdclass.getPackage().getName()))
continue; // It keeps including the commands from here
if (Modifier.isAbstract(cmd.getModifiers()))
continue;
TBMCCommandBase c = cmd.newInstance();
@ -113,9 +123,7 @@ public class TBMCChatAPI {
continue;
commands.put(c.GetCommandPath(), c);
CommandCaller.RegisterCommand(c);
} catch (InstantiationException e) {
TBMCCoreAPI.SendException("An error occured while registering command " + cmd.getName(), e);
} catch (IllegalAccessException e) {
} catch (Exception e) {
TBMCCoreAPI.SendException("An error occured while registering command " + cmd.getName(), e);
}
}
@ -199,7 +207,7 @@ public class TBMCChatAPI {
}
/**
* Sends a chat message to Minecraft
* Sends a chat message to Minecraft. Make sure that the channel is registered with {@link #RegisterChatChannel(Channel)}.
*
* @param channel
* The channel to send to
@ -210,8 +218,35 @@ public class TBMCChatAPI {
* @return The event cancelled state
*/
public static boolean SendChatMessage(Channel channel, CommandSender sender, String message) {
TBMCChatEvent event = new TBMCChatEvent(sender, channel, message);
if (!Channel.getChannels().contains(channel))
throw new RuntimeException("Channel " + channel.DisplayName + " not registered!");
TBMCChatPreprocessEvent eventPre = new TBMCChatPreprocessEvent(sender, channel, message);
Bukkit.getPluginManager().callEvent(eventPre);
if (eventPre.isCancelled())
return true;
int score;
if (channel.filteranderrormsg == null)
score = -1;
else {
RecipientTestResult result = channel.filteranderrormsg.apply(sender);
if (result.errormessage != null) {
sender.sendMessage("§c" + result.errormessage);
return true;
}
score = result.score;
}
TBMCChatEvent event = new TBMCChatEvent(sender, channel, eventPre.getMessage(), score);
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
/**
* Register a chat channel. See {@link Channel#Channel(String, Color, String, java.util.function.Function)} for details.
*
* @param channel
* A new {@link Channel} to register
*/
public static void RegisterChatChannel(Channel channel) {
Channel.RegisterChannel(channel);
}
}

View file

@ -1,11 +1,15 @@
package buttondevteam.lib.chat;
import java.util.function.Function;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import javassist.Modifier;
/**
* Extend this class to create new TBMCCommand and use {@link TBMCChatAPI#AddCommand(org.bukkit.plugin.java.JavaPlugin, TBMCCommandBase)} to add it. <u><b>Note:</b></u> The command path (command name
* and subcommand arguments) will be the class name by default, removing any "command" from it. To change it (especially for subcommands), override {@link #GetCommandPath()}.
* and subcommand arguments) will be the class name by default, removing any "command" from it. To change it (especially for subcommands), use the path field in the {@link CommandClass} annotation.
*
* @author Norbi
*
@ -13,41 +17,64 @@ import org.bukkit.plugin.Plugin;
public abstract class TBMCCommandBase {
public TBMCCommandBase() {
path = getcmdpath();
}
public abstract boolean OnCommand(CommandSender sender, String alias, String[] args);
public abstract String[] GetHelpText(String alias);
private String path = null;
/**
* The command's path, or name if top-level command.<br>
* For example:<br>
* "u admin updateplugin" or "u" for the top level one<br>
* <u>The path must be lowercase!</u><br>
* <b>Abstract classes with no {@link CommandClass} annotations will be ignored.</b>
*
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it)
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it) - Change via the {@link CommandClass} annotation
*/
public String GetCommandPath() {
return getClass().getSimpleName().toLowerCase().replace("command", "");
public final String GetCommandPath() {
return path;
}
/**
* Determines whether the command can only be used as a player, or command blocks or the console can use it as well.
*
* @return If the command is player only
*/
public abstract boolean GetPlayerOnly();
/**
* Determines whether the command can only be used by mods or regular players can use it as well.
*
* @return If the command is mod only
*/
public abstract boolean GetModOnly();
private final String getcmdpath() {
if (!getClass().isAnnotationPresent(CommandClass.class))
throw new RuntimeException(
"No @CommandClass annotation on command class " + getClass().getSimpleName() + "!");
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
.replace("command", "");
String path = getClass().getAnnotation(CommandClass.class).path(),
prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
for (Class<?> cl = getClass().getSuperclass(); cl != null
&& !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl
.getSuperclass()) { //
String newpath;
if (!cl.isAnnotationPresent(CommandClass.class)
|| (newpath = cl.getAnnotation(CommandClass.class).path()).length() == 0
|| newpath.equals(prevpath)) {
if (Modifier.isAbstract(cl.getModifiers()) && (!cl.isAnnotationPresent(CommandClass.class))
|| cl.getAnnotation(CommandClass.class).excludeFromPath()) // <--
continue;
newpath = getFromClass.apply(cl);
}
path = (prevpath = newpath) + " " + path;
}
return path;
}
Plugin plugin; // Used By TBMCChatAPI
public final Plugin getPlugin() { // Used by CommandCaller (ButtonChat)
return plugin;
}
public final boolean isPlayerOnly() {
return this instanceof PlayerCommandBase ? true
: this instanceof OptionallyPlayerCommandBase
? getClass().isAnnotationPresent(OptionallyPlayerCommandClass.class)
? getClass().getAnnotation(OptionallyPlayerCommandClass.class).playerOnly() : true
: false;
}
}

View file

@ -9,6 +9,7 @@ import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import buttondevteam.lib.TBMCCoreAPI;
@ChromaGamerEnforcer
public abstract class ChromaGamerBase implements AutoCloseable {
public static final String TBMC_PLAYERS_DIR = "TBMC/players/";

View file

@ -0,0 +1,14 @@
package buttondevteam.lib.player;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
@Inherited
public @interface ChromaGamerEnforcer {
}

View file

@ -74,17 +74,14 @@ public abstract class TBMCPlayerBase extends ChromaGamerBase {
public static <T extends TBMCPlayerBase> T getPlayer(UUID uuid, Class<T> cl) {
if (playermap.containsKey(uuid + "-" + cl.getSimpleName()))
return (T) playermap.get(uuid + "-" + cl.getSimpleName());
// System.out.println("A");
try {
T player;
if (playermap.containsKey(uuid + "-" + TBMCPlayer.class.getSimpleName())) {
// System.out.println("B"); - Don't program when tired
player = cl.newInstance();
player.plugindata = playermap.get(uuid + "-" + TBMCPlayer.class.getSimpleName()).plugindata;
playermap.put(uuid + "-" + cl.getSimpleName(), player); // It will get removed on player quit
} else
player = ChromaGamerBase.getUser(uuid.toString(), cl);
// System.out.println("C");
player.uuid = uuid;
return player;
} catch (Exception e) {