Misc. fixes, chat history config, clickable announcements #114

Merged
NorbiPeti merged 6 commits from dev into master 2020-02-01 19:13:06 +00:00
33 changed files with 439 additions and 272 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_13_PREVIEW">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
@ -34,14 +34,14 @@
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:LegacyProvider:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:ReflectionProvider:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:FlattenedProvider:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.palmergames.bukkit.towny:Towny:0.95.0.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.milkbowl:VaultAPI:master-4c248aad62-1" level="project" />
<orderEntry type="library" name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.palmergames.bukkit.towny:Towny:0.95.2.0" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.milkbowl:VaultAPI:master-89c00e1cb8-1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.18.10" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.github.webbukkit:Dynmap-Towny:master-0.60-g924051d-7" level="project" />
<orderEntry type="library" name="Maven: com.github.webbukkit:Dynmap:v2.5" level="project" />
<orderEntry type="library" name="Maven: com.nijikokun.bukkit:Permissions:3.1.6" level="project" />
<orderEntry type="library" name="Maven: org.bukkit:bukkit:1.7.10-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: ru.tehkode:PermissionsEx:1.19.1" level="project" />
<orderEntry type="library" name="Maven: de.bananaco:bPermissions:2.9.1" level="project" />
<orderEntry type="library" name="Maven: com.platymuus.bukkit.permissions:PermissionsBukkit:1.6" level="project" />

17
pom.xml
View file

@ -135,6 +135,21 @@
<!-- <plugin> <groupId>org.basepom.maven</groupId> <artifactId>duplicate-finder-maven-plugin</artifactId>
<version>1.2.1</version> <executions> <execution> <goals> <goal>check</goal>
</goals> </execution> </executions> </plugin> -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-testCompile</id>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<groupId>buttondevteam</groupId>
@ -198,7 +213,7 @@
<dependency>
<groupId>com.palmergames.bukkit.towny</groupId>
<artifactId>Towny</artifactId>
<version>0.95.0.0</version>
<version>0.95.2.0</version>
<scope>provided</scope>
</dependency>
<!-- <dependency> <groupId>au.com.mineauz</groupId> <artifactId>Minigames</artifactId>

View file

@ -16,7 +16,6 @@ import buttondevteam.chat.components.towncolors.TownColorComponent;
import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.chat.listener.PlayerJoinLeaveListener;
import buttondevteam.chat.listener.PlayerListener;
import buttondevteam.core.MainPlugin;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin;
@ -37,12 +36,12 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
public static PluginMain Instance;
public static ConsoleCommandSender Console;
public ConfigData<String> notificationSound() {
return getIConfig().getData("notificationSound", "");
}
public ConfigData<Float> notificationPitch() {
return getIConfig().getData("notificationPitch", 1.0f);
/**
* If enabled, stores and displays the last 10 messages the player can see (public, their town chat etc.)
* Can be used with the Discord plugin so players can see some of the conversation they missed that's visible on Discord anyways.
*/
public ConfigData<Boolean> storeChatHistory() {
return getIConfig().getData("storeChatHistory", true);
}
// Fired when plugin is first enabled

View file

@ -16,14 +16,14 @@ import java.net.URLEncoder;
})
public class MWikiCommand extends ICommand2MC {
@Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg String query) {
public boolean def(CommandSender sender, @Command2.OptionalArg @Command2.TextArg String query) {
try {
if (query == null)
sender.sendMessage(new String[] { "§bMinecraft Wiki link: http://minecraft.gamepedia.com/",
"You can also search on it using /mwiki <query>" });
sender.sendMessage(new String[]{"§bMinecraft Wiki link: http://minecraft.gamepedia.com/",
"You can also search on it using /mwiki <query>"});
else
sender.sendMessage("§bMinecraft Wiki link: http://minecraft.gamepedia.com/index.php?search="
+ URLEncoder.encode(query, "UTF-8") + "&title=Special%3ASearch&go=Go");
+ URLEncoder.encode(query, "UTF-8") + "&title=Special%3ASearch&go=Go");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

View file

@ -1,14 +1,8 @@
package buttondevteam.chat.commands.ucmds;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.chat.TBMCCommandBase;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
@CommandClass(modOnly = false, helpText = {
"Help",
@ -36,19 +30,7 @@ public final class HelpCommand extends UCommandBase {
"- Playernames: Hover over them to get some player info",
"-- Respect: This is the number of paid respects divided by eliglble deaths. This is a reference to CoD:AW's \"Press F to pay respects\""});
else if (topicOrCommand.equalsIgnoreCase("commands")) {
ArrayList<String> text = new ArrayList<String>();
text.add("§6---- Command list ----");
for (TBMCCommandBase cmd : TBMCChatAPI.GetCommands().values())
if (!cmd.getClass().getAnnotation(CommandClass.class).modOnly() || PluginMain.permission.has(sender, "tbmc.admin"))
if (!cmd.isPlayerOnly() || sender instanceof Player)
if (!cmd.GetCommandPath().contains(" "))
text.add("/" + cmd.GetCommandPath());
else {
final String topcmd = cmd.GetCommandPath().substring(0, cmd.GetCommandPath().indexOf(' '));
if (!text.contains("/" + topcmd))
text.add("/" + topcmd);
}
sender.sendMessage(text.toArray(new String[0]));
sender.sendMessage(getManager().getCommandsText());
} else if (topicOrCommand.equalsIgnoreCase("colors")) {
sender.sendMessage(new String[]{"§6---- Chat colors/formats ----", //
"Tellraw name - Code | Tellraw name - Code", //

View file

@ -1,5 +1,6 @@
package buttondevteam.chat.commands.ucmds;
import buttondevteam.chat.PluginMain;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.Command2;
@ -21,7 +22,7 @@ public class HistoryCommand extends UCommandBase {
/**
* Key: ChannelID_groupID
*/
private static HashMap<String, LinkedList<HistoryEntry>> messages = new HashMap<>();
private static final HashMap<String, LinkedList<HistoryEntry>> messages = new HashMap<>();
@Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg String channel) {
@ -29,6 +30,10 @@ public class HistoryCommand extends UCommandBase {
}
public static boolean showHistory(CommandSender sender, String channel) {
if (!PluginMain.Instance.storeChatHistory().get()) {
sender.sendMessage("§6Chat history is disabled");
return true;
}
Function<Channel, LinkedList<HistoryEntry>> getThem = ch -> messages.get(ch.ID + "_" + ch.getGroupID(sender)); //If can't see, groupID is null, and that shouldn't be in the map
sender.sendMessage("§6---- Chat History ----");
Stream<Channel> stream;
@ -43,13 +48,15 @@ public class HistoryCommand extends UCommandBase {
stream = Stream.of(och.get());
}
AtomicBoolean sent = new AtomicBoolean();
val arr = stream.map(getThem).filter(Objects::nonNull).flatMap(Collection::stream)
synchronized (messages) {
val arr = stream.map(getThem).filter(Objects::nonNull).flatMap(Collection::stream)
.sorted(Comparator.comparingLong(he -> he.timestamp)).toArray(HistoryEntry[]::new);
for (int i = Math.max(0, arr.length - 10); i < arr.length; i++) {
HistoryEntry e = arr[i];
val cm = e.chatMessage;
sender.sendMessage("[" + e.channel.DisplayName().get() + "] " + cm.getSender().getName() + ": " + cm.getMessage());
sent.set(true);
for (int i = Math.max(0, arr.length - 10); i < arr.length; i++) {
HistoryEntry e = arr[i];
val cm = e.chatMessage;
sender.sendMessage("[" + e.channel.DisplayName().get() + "] " + cm.getSender().getName() + ": " + cm.getMessage());
sent.set(true);
}
}
if (!sent.get())
sender.sendMessage("No messages can be found.");
@ -67,11 +74,14 @@ public class HistoryCommand extends UCommandBase {
}
public static void addChatMessage(ChatMessage chatMessage, Channel channel) {
if (!PluginMain.Instance.storeChatHistory().get()) return;
val groupID = channel.getGroupID(chatMessage.getPermCheck());
if (groupID == null) return; //Just to be sure
var ll = messages.computeIfAbsent(channel.ID + "_" + groupID, k -> new LinkedList<>()); //<-- TIL
ll.add(new HistoryEntry(System.nanoTime(), chatMessage, channel)); //Adds as last element
while (ll.size() > 10)
ll.remove(); //Removes the first element
synchronized (messages) {
var ll = messages.computeIfAbsent(channel.ID + "_" + groupID, k -> new LinkedList<>()); //<-- TIL
ll.add(new HistoryEntry(System.nanoTime(), chatMessage, channel)); //Adds as last element
while (ll.size() > 10)
ll.remove(); //Removes the first element
}
}
}

View file

@ -1,11 +1,13 @@
package buttondevteam.chat.commands.ucmds;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@CommandClass(modOnly = false, helpText = {
@ -20,16 +22,18 @@ public class InfoCommand extends UCommandBase {
sender.sendMessage("The server console.");
return true;
}
try (TBMCPlayer p = TBMCPlayerBase.getFromName(player, TBMCPlayer.class)) {
if (p == null) {
sender.sendMessage("§cThe specified player cannot be found");
return true;
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
try (TBMCPlayer p = TBMCPlayerBase.getFromName(player, TBMCPlayer.class)) {
if (p == null) {
sender.sendMessage("§cThe specified player cannot be found");
return;
}
sender.sendMessage(p.getInfo(InfoTarget.MCCommand));
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while getting player information!", e);
sender.sendMessage("§cError while getting player information!");
}
sender.sendMessage(p.getInfo(InfoTarget.MCCommand));
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while getting player information!", e);
sender.sendMessage("§cError while getting player information!");
}
});
return true;
}
}

View file

@ -2,9 +2,7 @@ package buttondevteam.chat.commands.ucmds;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
@CommandClass(modOnly = false, path = "u")
@OptionallyPlayerCommandClass(playerOnly = false)
public abstract class UCommandBase extends ICommand2MC {
}

View file

@ -1,15 +1,19 @@
package buttondevteam.chat.components.announce;
import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.formatter.ChatProcessing;
import buttondevteam.chat.components.formatter.FormatterComponent;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@CommandClass(modOnly = true)
@OptionallyPlayerCommandClass(playerOnly = false)
@RequiredArgsConstructor
public class AnnounceCommand extends UCommandBase {
private final AnnouncerComponent component;
@ -28,7 +32,7 @@ public class AnnounceCommand extends UCommandBase {
@Command2.Subcommand(helpText = {
"Edit announcement",
"This command lets you edit an announcement by its index.",
"Shouldn't be used directly, use either command blocks or click on an announcement in /u announce list (WIP) instead." //TODO: <--
"Shouldn't be used directly, use either command blocks or click on an announcement in /u announce list instead."
})
public boolean edit(CommandSender sender, byte index, @Command2.TextArg String text) {
String finalmessage1 = text.replace('&', '§');
@ -49,8 +53,19 @@ public class AnnounceCommand extends UCommandBase {
sender.sendMessage("§bList of announce messages:§r");
sender.sendMessage("§bFormat: [index] message§r");
int i = 0;
for (String message : component.announceMessages().get())
sender.sendMessage("[" + i++ + "] " + message);
for (String message : component.announceMessages().get()) {
String msg = "[" + i++ + "] " + message;
//noinspection SuspiciousMethodCalls
if (!ComponentManager.isEnabled(FormatterComponent.class) || !Bukkit.getOnlinePlayers().contains(sender)) {
sender.sendMessage(msg);
continue;
}
String json = ChatProcessing.toJson(new TellrawPart(msg)
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, "Click to edit"))
.setClickEvent(TellrawEvent.create(TellrawEvent.ClickAction.SUGGEST_COMMAND,
"/" + getCommandPath() + " edit " + (i - 1) + " " + message.replace('§', '&'))));
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName() + " " + json);
}
sender.sendMessage("§bCurrent wait time between announcements: "
+ component.announceTime().get() / 60 / 1000 + " minute(s)§r");
return true;

View file

@ -5,23 +5,31 @@ import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.ListConfigData;
import buttondevteam.lib.chat.TBMCChatAPI;
import org.bukkit.Bukkit;
import java.util.ArrayList;
/**
* Displays the configured messages at the set interval when someone is online.
*/
public class AnnouncerComponent extends Component<PluginMain> implements Runnable {
public ConfigData<ArrayList<String>> announceMessages() {
return getConfig().getData("announceMessages", new ArrayList<>(0));
/**
* The messages to display to players.
*/
public ListConfigData<String> announceMessages() {
return getConfig().getListData("announceMessages");
}
/**
* The time in milliseconds between the messages. Use /u announce settime to set minutes.
*/
public ConfigData<Integer> announceTime() {
return getConfig().getData("announceTime", 15 * 60 * 1000);
}
private TBMCSystemChatEvent.BroadcastTarget target;
private static int AnnounceMessageIndex = 0;
private int AnnounceMessageIndex = 0;
@Override
public void run() {
@ -43,7 +51,7 @@ public class AnnouncerComponent extends Component<PluginMain> implements Runnabl
@Override
protected void enable() {
target= TBMCSystemChatEvent.BroadcastTarget.add("announcements");
target = TBMCSystemChatEvent.BroadcastTarget.add("announcements");
registerCommand(new AnnounceCommand(this));
new Thread(this).start();
}

View file

@ -15,8 +15,12 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
/**
* Allows players to append tableflips and other things to their messages. Everything is configurable here.
*/
public class AppendTextComponent extends Component<PluginMain> {
private Map<String, IHaveConfig> appendTexts;
private ConfigData<String[]> helpText(IHaveConfig config) {
return config.getData("helpText", () -> new String[]{
"Tableflip", //

View file

@ -5,6 +5,7 @@ import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
import lombok.val;
@ -15,6 +16,10 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
/**
* Allows players to enter chat-only mode which puts them into spectator mode and disallows everything besides chatting.
*/
@ComponentMetadata(enabledByDefault = false)
public class ChatOnlyComponent extends Component implements Listener {
@Override
protected void enable() {

View file

@ -1,13 +1,11 @@
package buttondevteam.chat.components.chatonly;
import buttondevteam.lib.chat.ICommand2MC;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.PlayerCommandBase;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.player.TBMCPlayer;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
@CommandClass(modOnly = false, helpText = {
"§6---- Chat-only mode ----", //

View file

@ -6,10 +6,8 @@ import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import buttondevteam.lib.player.TBMCPlayer;
import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Timer;
@ -19,14 +17,12 @@ import java.util.Timer;
"Accepts a flair from Reddit", //
"Use /u accept <username> if you commented from multiple accounts"
})
@OptionallyPlayerCommandClass(playerOnly = true)
@RequiredArgsConstructor
public class AcceptCommand extends UCommandBase {
private final FlairComponent component;
@Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg String username) {
final Player player = (Player) sender;
public boolean def(Player player, @Command2.OptionalArg String username) {
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
if (username == null && p.UserNames().size() > 1) {
player.sendMessage("§9Multiple users commented your name. §bPlease pick one using /u accept <username>");

View file

@ -4,6 +4,7 @@ import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.google.gson.JsonArray;
@ -22,9 +23,17 @@ import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* This component checks a specific Reddit thread every 10 seconds for comments such as "IGN: NorbiPeti" to link Reddit accounts and to determine their /r/thebutton flair.
* This was the original goal of this plugin when it was made.
*/
@ComponentMetadata(enabledByDefault = false)
public class FlairComponent extends Component<PluginMain> {
/**
* The Reddit thread to check for account connections. Re-enable the component if this was empty.
*/
ConfigData<String> flairThreadURL() {
return getConfig().getData("flairThreadURL", "https://www.reddit.com/r/Chromagamers/comments/51ys94/flair_thread_for_the_mc_server/");
return getConfig().getData("flairThreadURL", "");
}
/**
@ -52,7 +61,7 @@ public class FlairComponent extends Component<PluginMain> {
private void FlairGetterThreadMethod() {
int errorcount = 0;
while (isEnabled()) {
while (isEnabled() && flairThreadURL().get().length() > 0) {
try {
String body = TBMCCoreAPI.DownloadString(flairThreadURL().get() + ".json?limit=1000");
JsonArray json = new JsonParser().parse(body).getAsJsonArray().get(1).getAsJsonObject().get("data")

View file

@ -4,20 +4,16 @@ import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import buttondevteam.lib.player.TBMCPlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@CommandClass(modOnly = false, helpText = {
"Ignore flair",
"Stop the \"write your name in the thread\" message from showing up"
})
@OptionallyPlayerCommandClass(playerOnly = true)
public final class IgnoreCommand extends UCommandBase {
@Command2.Subcommand
public boolean def(CommandSender sender) {
final Player player = (Player) sender;
public boolean def(Player player) {
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
if (p.FlairState().get().equals(FlairStates.Accepted)) {
player.sendMessage("§cYou can only ignore the \"write your name in the thread\" message.");

View file

@ -25,6 +25,7 @@ import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.earth2me.essentials.User;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@ -49,12 +50,13 @@ public class ChatProcessing {
private static final Pattern HASHTAG_PATTERN = Pattern.compile("#(\\w+)");
private static final Pattern URL_PATTERN = Pattern.compile("(http[\\w:/?=$\\-_.+!*'(),&]+(?:#[\\w]+)?)");
public static final Pattern ENTIRE_MESSAGE_PATTERN = Pattern.compile(".+");
private static final Pattern UNDERLINED_PATTERN = Pattern.compile("_");
private static final Pattern UNDERLINED_PATTERN = Pattern.compile("__");
private static final Pattern ITALIC_PATTERN = Pattern.compile("\\*");
private static final Pattern ITALIC_PATTERN_2 = Pattern.compile("_");
private static final Pattern BOLD_PATTERN = Pattern.compile("\\*\\*");
private static final Pattern CODE_PATTERN = Pattern.compile("`");
private static final Pattern MASKED_LINK_PATTERN = Pattern.compile("\\[([^\\[\\]]+)]\\(([^()]+)\\)");
private static final Pattern SOMEONE_PATTERN = Pattern.compile("@someone"); //TODO
private static final Pattern SOMEONE_PATTERN = Pattern.compile("@someone");
private static final Pattern STRIKETHROUGH_PATTERN = Pattern.compile("~~");
private static final Pattern SPOILER_PATTERN = Pattern.compile("\\|\\|");
private static final Color[] RainbowPresserColors = new Color[]{Color.Red, Color.Gold, Color.Yellow, Color.Green,
@ -68,7 +70,8 @@ public class ChatProcessing {
ChatFormatter.builder("bold", BOLD_PATTERN).bold(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
.priority(Priority.High).build(),
ChatFormatter.builder("italic", ITALIC_PATTERN).italic(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range).build(),
ChatFormatter.builder("underlined", UNDERLINED_PATTERN).underlined(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range)
ChatFormatter.builder("italic2", ITALIC_PATTERN_2).italic(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range).build(),
ChatFormatter.builder("underlined", UNDERLINED_PATTERN).underlined(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
.build(),
ChatFormatter.builder("strikethrough", STRIKETHROUGH_PATTERN).strikethrough(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
.build(),
@ -98,7 +101,15 @@ public class ChatProcessing {
builder.setOpenlink(link);
return text;
}).type(ChatFormatter.Type.Excluder).build(),
ChatFormatter.builder("url", URL_PATTERN).underlined(true).openlink("$1").type(ChatFormatter.Type.Excluder).build());
ChatFormatter.builder("url", URL_PATTERN).underlined(true).openlink("$1").type(ChatFormatter.Type.Excluder).build(),
ChatFormatter.builder("someone", SOMEONE_PATTERN).color(Color.Aqua).onmatch((match, builder, section) -> {
if (Bukkit.getOnlinePlayers().size() == 0) return match;
var players = ImmutableList.copyOf(Bukkit.getOnlinePlayers());
var playerC = new Random().nextInt(players.size());
var player = players.get(playerC);
playPingSound(player, ComponentManager.getIfEnabled(FormatterComponent.class));
return "@someone (" + player.getDisplayName() + "§r)";
}).build());
private static Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum())
.registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection())
@ -146,13 +157,9 @@ public class ChatProcessing {
ArrayList<ChatFormatter> formatters;
if (component.allowFormatting().get()) {
formatters = addFormatters(colormode, e::shouldSendTo);
formatters = addFormatters(colormode, e::shouldSendTo, component);
if (colormode == channel.Color().get() && mp != null && mp.RainbowPresserColorMode) { // Only overwrite channel color
final AtomicInteger rpc = new AtomicInteger(0);
formatters.add(ChatFormatter.builder("word", WORD_PATTERN).color(colormode).onmatch((match, cf, s) -> {
cf.setColor(RainbowPresserColors[rpc.getAndUpdate(i -> ++i < RainbowPresserColors.length ? i : 0)]);
return match;
}).build());
createRPC(colormode, formatters);
}
pingedconsole = false; // Will set it to true onmatch (static constructor)
} else
@ -206,13 +213,21 @@ public class ChatProcessing {
return false;
}
static String toJson(TellrawPart json) {
static void createRPC(Color colormode, ArrayList<ChatFormatter> formatters) {
final AtomicInteger rpc = new AtomicInteger(0);
formatters.add(ChatFormatter.builder("rpc", WORD_PATTERN).color(colormode).onmatch((match, cf, s) -> {
cf.setColor(RainbowPresserColors[rpc.getAndUpdate(i -> ++i < RainbowPresserColors.length ? i : 0)]);
return match;
}).build());
}
public static String toJson(TellrawPart json) {
return gson.toJson(json);
}
static TellrawPart createTellraw(CommandSender sender, String message, @Nullable Player player,
@Nullable ChatPlayer mp, @Nullable ChromaGamerBase cg, final String channelidentifier,
String origin) {
@Nullable ChatPlayer mp, @Nullable ChromaGamerBase cg, final String channelidentifier,
String origin) {
TellrawPart json = new TellrawPart("");
ChatOnlyComponent.tellrawCreate(mp, json); //TODO: Make nice API
json.addExtra(
@ -249,7 +264,7 @@ public class ChatProcessing {
+ "]";
}
static ArrayList<ChatFormatter> addFormatters(Color colormode, Predicate<Player> canSee) {
static ArrayList<ChatFormatter> addFormatters(Color colormode, Predicate<Player> canSee, @Nullable FormatterComponent component) {
@SuppressWarnings("unchecked")
ArrayList<ChatFormatter> formatters = (ArrayList<ChatFormatter>) commonFormatters.clone();
@ -306,7 +321,7 @@ public class ChatProcessing {
}
ChatPlayer mpp = TBMCPlayer.getPlayer(nottest ? p.getUniqueId() : new UUID(0, 0), ChatPlayer.class);
if (nottest) {
playPingSound(p);
playPingSound(p, component);
}
String color = String.format("§%x", (mpp.GetFlairColor() == 0x00 ? 0xb : mpp.GetFlairColor()));
return color + (nottest ? p.getName() : pn.get()) + "§r"; //Fix name casing, except when testing
@ -322,7 +337,7 @@ public class ChatProcessing {
+ match.toLowerCase() + " but was reported as online.");
return "§c" + match + "§r";
}
playPingSound(p);
playPingSound(p, component);
return PluginMain.essentials.getUser(p).getNickname();
}
error.accept("Player nicknamed " + match.toLowerCase()
@ -333,12 +348,12 @@ public class ChatProcessing {
return formatters;
}
private static void playPingSound(Player p) {
if (PluginMain.Instance.notificationSound().get().length() == 0)
private static void playPingSound(Player p, @Nullable FormatterComponent component) {
if (component == null || component.notificationSound().get().length() == 0)
p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 0.5f); // TODO: Airhorn
else
p.playSound(p.getLocation(), PluginMain.Instance.notificationSound().get(), 1.0f,
PluginMain.Instance.notificationPitch().get());
p.playSound(p.getLocation(), component.notificationSound().get(), 1.0f,
component.notificationPitch().get());
}
static void doFunStuff(CommandSender sender, TBMCChatEventBase event, String message) {

View file

@ -8,14 +8,33 @@ import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
/**
* This component handles the custom processing of chat messages. If this component is disabled channels won't be supported either in Minecraft.
* This component handles the custom processing of chat messages. If this component is disabled channels won't be supported in Minecraft.
* If you only want to disable the formatting features, set allowFormatting to false.
* If you're using another chat plugin, you should disable the whole component.
*/
public class FormatterComponent extends Component<PluginMain> {
/**
* Determines whether Markdown formatting, name mentioning and similar features are enabled.
*/
ConfigData<Boolean> allowFormatting() {
return getConfig().getData("allowFormatting", true);
}
/**
* The sound to play when a player is mentioned. Leave empty to use default.
*/
public ConfigData<String> notificationSound() {
return getConfig().getData("notificationSound", "");
}
/**
* The pitch of the notification sound.
*/
public ConfigData<Float> notificationPitch() {
return getConfig().getData("notificationPitch", 1.0f);
}
@Override
protected void enable() {
MainPlugin.Instance.setChatHandlerEnabled(false); //Disable Core chat handler - if this component is disabled then let it do it's job

View file

@ -45,6 +45,13 @@ public final class ChatFormatter {
String hoverText;
String name;
@Override
public String toString() {
return "ChatFormatter{" +
"name='" + name + '\'' +
'}';
}
public static ChatFormatterBuilder builder(String name, Pattern regex) {
return builder().regex(regex).name(name);
}
@ -98,7 +105,7 @@ public final class ChatFormatter {
sections = convertRangeSections(str, sections, remchars);
header("Adding remove chars (RC)"); // Important to add after the range section conversion
addRemChars(sections, remchars);
addRemChars(sections, remchars, str);
header("Section combining");
combineSections(str, sections);
@ -134,12 +141,26 @@ public final class ChatFormatter {
}
}
private static void newCombine(String str, ArrayList<FormattedSection> sections, ArrayList<int[]> remchars) {
var stack = new Stack<FormattedSection>();
for (int i = 0; i < str.length(); i++) {
for (Iterator<FormattedSection> iterator = sections.iterator(); iterator.hasNext(); ) {
FormattedSection section = iterator.next();
if (section.Start <= i) {
stack.push(section);
iterator.remove();
}
}
}
}
private static ArrayList<FormattedSection> convertRangeSections(String str, ArrayList<FormattedSection> sections, ArrayList<int[]> remchars) {
ArrayList<FormattedSection> combined = new ArrayList<>();
Map<ChatFormatter, FormattedSection> nextSection = new HashMap<>();
boolean escaped = false;
int takenStart = -1, takenEnd = -1;
ChatFormatter takenFormatter = null;
boolean takenByBigGuy = false; //Can't win against him (finished sections take precedence)
for (final FormattedSection section : sections) {
// Set ending to -1 until closed with another 1 long "section" - only do this if IsRange is true
if (section.type != Type.Range) {
@ -155,11 +176,12 @@ public final class ChatFormatter {
continue;
}
if (!escaped) {
ChatFormatter formatter = section.Formatters.get(0);
if (section.Start == takenStart || (section.Start > takenStart && section.Start < takenEnd)) {
/*
* if (nextSection.containsKey(section.Formatters.get(0)) ? section.RemCharFromStart <= takenEnd - takenStart : section.RemCharFromStart > takenEnd - takenStart) {
*/
if (section.Formatters.get(0).removeCharCount < takenEnd - takenStart) {
if (takenByBigGuy || formatter.removeCharCount < takenEnd - takenStart) {
DebugCommand.SendDebugMessage("Lose: " + section);
sendMessageWithPointer(str, section.Start, section.End);
DebugCommand.SendDebugMessage("And win: " + takenFormatter);
@ -170,21 +192,38 @@ public final class ChatFormatter {
sendMessageWithPointer(str, section.Start, section.End);
DebugCommand.SendDebugMessage("And lose: " + takenFormatter);
}
boolean hasFormatter = nextSection.containsKey(formatter);
if (!hasFormatter) {
val ff = formatter;
val cfo = nextSection.keySet().stream().filter(f -> f.removeCharCount > ff.removeCharCount).findAny();
if (cfo.isPresent()) {
//formatter = cfo.get();
val s = nextSection.get(cfo.get());
int takenS = section.Start, takenE = section.Start + formatter.removeCharCount;
if (s.Start == takenS || (s.Start > takenS && s.Start < takenE)) { //Peek()
hasFormatter = true;
continue; //Not the formatter we're looking for - TODO: It doesn't fix the problem of italics at the end
}
}
}
takenStart = section.Start;
takenEnd = section.Start + section.Formatters.get(0).removeCharCount;
takenFormatter = section.Formatters.get(0);
if (nextSection.containsKey(section.Formatters.get(0))) {
FormattedSection s = nextSection.remove(section.Formatters.get(0));
takenEnd = section.Start + formatter.removeCharCount;
takenFormatter = formatter;
if (hasFormatter) {
FormattedSection s = nextSection.remove(formatter);
//HACK? If we can find another section that removes more characters, finish that instead
// section: the ending marker section - s: the to-be full section
s.End = takenEnd - 1; //Take the remCharCount into account as well
// s.IsRange = false; // IsRange means it's a 1 long section indicating a start or an end
combined.add(s);
takenByBigGuy = true;
DebugCommand.SendDebugMessage("Finished section: " + s);
sendMessageWithPointer(str, s.Start, s.End);
} else {
DebugCommand.SendDebugMessage("Adding next section: " + section);
sendMessageWithPointer(str, section.Start, section.End);
nextSection.put(section.Formatters.get(0), section);
nextSection.put(formatter, section);
takenByBigGuy = false;
}
DebugCommand
.SendDebugMessage("New area taken: (" + takenStart + "-" + takenEnd + ") " + takenFormatter);
@ -200,7 +239,7 @@ public final class ChatFormatter {
return sections;
}
private static void addRemChars(ArrayList<FormattedSection> sections, ArrayList<int[]> remchars) {
private static void addRemChars(ArrayList<FormattedSection> sections, ArrayList<int[]> remchars, String str) {
sections.stream()
.flatMap(fs -> fs.Formatters.stream().filter(cf -> cf.removeCharCount > 0)
.mapToInt(cf -> cf.removeCharCount).mapToObj(rcc -> new int[]{fs.Start, fs.Start + rcc - 1}))
@ -210,31 +249,41 @@ public final class ChatFormatter {
.mapToInt(cf -> cf.removeCharCount).mapToObj(rcc -> new int[]{fs.End - rcc + 1, fs.End}))
.forEach(remchars::add);
DebugCommand.SendDebugMessage("Added remchars:");
DebugCommand
.SendDebugMessage(remchars.stream().map(Arrays::toString).collect(Collectors.joining("; ")));
DebugCommand.SendDebugMessage(remchars.stream().map(Arrays::toString).collect(Collectors.joining("; ")));
sendMessageWithPointer(str,
remchars.stream().flatMapToInt(Arrays::stream).toArray());
}
private static void combineSections(String str, ArrayList<FormattedSection> sections) {
boolean cont = true;
boolean found = false;
for (int i = 1; cont; ) {
int nextindex = i + 1;
if (sections.size() < 2)
break;
for (int i = 1; i < sections.size(); i++) {
DebugCommand.SendDebugMessage("i: " + i);
FormattedSection firstSection = sections.get(i - 1);
final FormattedSection firstSection;
final FormattedSection lastSection;
{
FormattedSection firstSect = sections.get(i - 1);
FormattedSection lastSect = sections.get(i);
if (firstSect.Start > lastSect.Start) { //The first can't start later
var section = firstSect;
firstSect = lastSect;
lastSect = section;
}
firstSection = firstSect;
lastSection = lastSect;
}
DebugCommand.SendDebugMessage("Combining sections " + firstSection);
sendMessageWithPointer(str, firstSection.Start, firstSection.End);
final FormattedSection lastSection = sections.get(i);
DebugCommand.SendDebugMessage(" and " + lastSection);
sendMessageWithPointer(str, lastSection.Start, lastSection.End);
if (firstSection.Start == lastSection.Start && firstSection.End == lastSection.End) {
firstSection.Formatters.addAll(lastSection.Formatters);
firstSection.Matches.addAll(lastSection.Matches);
firstSection.type = lastSection.type;
DebugCommand.SendDebugMessage("To section " + firstSection);
sendMessageWithPointer(str, firstSection.Start, firstSection.End);
sections.remove(i);
found = true;
i = 0;
sortSections(sections);
continue;
} else if (firstSection.End > lastSection.Start && firstSection.Start < lastSection.End) {
int origend2 = firstSection.End;
firstSection.End = lastSection.Start - 1;
@ -244,7 +293,6 @@ public final class ChatFormatter {
section.Formatters.addAll(lastSection.Formatters);
section.Matches.addAll(lastSection.Matches); // TODO: Clean
sections.add(i, section);
nextindex++;
// Use the properties of the first section not the second one
lastSection.Formatters.clear();
lastSection.Formatters.addAll(firstSection.Formatters);
@ -256,7 +304,7 @@ public final class ChatFormatter {
Predicate<FormattedSection> removeIfNeeded = s -> {
if (s.Start < 0 || s.End < 0 || s.Start > s.End) {
DebugCommand.SendDebugMessage("Removing section: " + s);
DebugCommand.SendDebugMessage(" Removed: " + s);
sendMessageWithPointer(str, s.Start, s.End);
sections.remove(s);
return true;
@ -277,27 +325,19 @@ public final class ChatFormatter {
DebugCommand.SendDebugMessage(" 3:" + lastSection);
sendMessageWithPointer(str, lastSection.Start, lastSection.End);
}
found = true;
i = 0;
}
sortSections(sections);
if (i == 0) continue;
for (int j = i - 1; j <= i + 1; j++) {
if (j < sections.size() && sections.get(j).End < sections.get(j).Start) {
DebugCommand.SendDebugMessage("Removing section: " + sections.get(j));
sendMessageWithPointer(str, sections.get(j).Start, sections.get(j).End);
sections.remove(j);
j--;
found = true;
i = 0;
}
}
i = nextindex - 1;
i++;
if (i >= sections.size()) {
if (found) {
i = 1;
found = false;
sortSections(sections);
} else
cont = false;
}
}
}
@ -310,11 +350,13 @@ public final class ChatFormatter {
int start = section.Start, end = section.End;
DebugCommand.SendDebugMessage("Start: " + start + " - End: " + end);
sendMessageWithPointer(str, start, end);
val rcs = remchars.stream().filter(rc -> rc[0] <= start && start <= rc[1]).findAny();
val rce = remchars.stream().filter(rc -> rc[0] <= end && end <= rc[1]).findAny();
val rci = remchars.stream().filter(rc -> start < rc[0] && rc[1] < end).toArray(int[][]::new);
int s = start, e = end;
if (rcs.isPresent())
/*DebugCommand.SendDebugMessage("RCS: "+remchars.stream().filter(rc -> rc[0] <= start && start <= rc[1]).count());
DebugCommand.SendDebugMessage("RCE: "+remchars.stream().filter(rc -> rc[0] <= end && end <= rc[1]).count());
DebugCommand.SendDebugMessage("RCI: "+remchars.stream().filter(rc -> start < rc[0] || rc[1] < end).count());*/
val rci = remchars.stream().filter(rc -> (rc[0] <= start && rc[1] >= start)
|| (rc[0] >= start && rc[1] <= end)
|| (rc[0] <= end && rc[1] >= end)).sorted(Comparator.comparingInt(rc -> rc[0] * 10000 + rc[1])).toArray(int[][]::new);
/*if (rcs.isPresent())
s = rcs.get()[1] + 1;
if (rce.isPresent())
e = rce.get()[0] - 1;
@ -322,12 +364,17 @@ public final class ChatFormatter {
if (e - s < 0) { //e-s==0 means the end char is the same as start char, so one char message
DebugCommand.SendDebugMessage("Skipping section because of remchars (length would be " + (e - s + 1) + ")");
continue;
}
originaltext = str.substring(s, e + 1);
}*/
DebugCommand.SendDebugMessage("Applying RC: " + Arrays.stream(rci).map(Arrays::toString).collect(Collectors.joining(", ", "[", "]")));
originaltext = str.substring(start, end + 1);
val sb = new StringBuilder(originaltext);
for (int x = rci.length - 1; x >= 0; x--)
sb.delete(rci[x][0] - start - 1, rci[x][1] - start); //Delete going backwards
sb.delete(Math.max(rci[x][0] - start, 0), Math.min(rci[x][1] - start, end) + 1); //Delete going backwards
originaltext = sb.toString();
if (originaltext.length() == 0) {
DebugCommand.SendDebugMessage("Skipping section because of remchars");
continue;
}
DebugCommand.SendDebugMessage("Section text: " + originaltext);
String openlink = null;
section.Formatters.sort(Comparator.comparing(cf2 -> cf2.priority.GetValue())); //Apply the highest last, to overwrite previous ones
@ -381,7 +428,7 @@ public final class ChatFormatter {
sections.sort(
(s1, s2) -> s1.Start == s2.Start
? s1.End == s2.End ? Integer.compare(s2.Formatters.get(0).priority.GetValue(),
s1.Formatters.get(0).priority.GetValue()) : Integer.compare(s2.End, s1.End)
s1.Formatters.get(0).priority.GetValue()) : Integer.compare(s1.End, s2.End)
: Integer.compare(s1.Start, s2.Start));
}

View file

@ -2,7 +2,10 @@ package buttondevteam.chat.components.fun;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.chat.*;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.player.TBMCPlayer;
import org.bukkit.entity.Player;
@ -13,7 +16,6 @@ import java.util.Optional;
"Rainbow mode",
"This command allows you to talk in rainbow colors"
})
@OptionallyPlayerCommandClass(playerOnly = true)
public class CCommand extends ICommand2MC {
@Command2.Subcommand
public boolean def(Player player, @Command2.OptionalArg String color) {

View file

@ -5,7 +5,6 @@ import buttondevteam.chat.PluginMain;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCCommandBase;
import buttondevteam.lib.player.TBMCPlayerBase;
import lombok.val;
import org.bukkit.Bukkit;

View file

@ -26,26 +26,41 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
/**
* Random things I added over the years.
*/
public class FunComponent extends Component<PluginMain> implements Listener {
private boolean ActiveF = false;
private ChatPlayer FPlayer = null;
private BukkitTask Ftask = null;
private ArrayList<CommandSender> Fs = new ArrayList<>();
private HashSet<CommandSender> Fs = new HashSet<>();
private UnlolCommand command;
private TBMCSystemChatEvent.BroadcastTarget unlolTarget;
private TBMCSystemChatEvent.BroadcastTarget fTarget;
/**
* The strings that count as laughs, see unlol.
*/
private ConfigData<String[]> laughStrings() {
return getConfig().getData("laughStrings", () -> new String[]{"xd", "lel", "lawl", "kek", "lmao", "hue", "hah", "rofl"});
}
/**
* The "Press F to pay respects" meme in Minecraft. It will randomly appear on player death and keep track of how many "F"s are said in chat.
* You can hover over a player's name to see their respect.
*/
private ConfigData<Boolean> respect() {
return getConfig().getData("respect", true);
}
/**
* This is an inside joke on our server.
* It keeps track of laughs (lols and what's defined in laughStrings) and if someone does /unlol or /unlaugh it will unlaugh the last person who laughed.
* Which also blinds the laughing person for a few seconds. This action can only be performed once per laugh.
*/
private ConfigData<Boolean> unlol() {
return getConfig().getData("unlol", true);
}
@ -105,7 +120,7 @@ public class FunComponent extends Component<PluginMain> implements Listener {
FPlayer.FCount().set(FPlayer.FCount().get() + Fs.size());
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL,
"§b" + Fs.size() + " " + (Fs.size() == 1 ? "person" : "people")
+ " paid their respects.§r", fTarget);
+ " paid their respects.§r", fTarget);
Fs.clear();
}
};
@ -119,9 +134,10 @@ public class FunComponent extends Component<PluginMain> implements Listener {
FPlayer.FDeaths().set(FPlayer.FDeaths().get() + 1);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL,
"§bPress F to pay respects.§r", fTarget);
Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, tt, 15 * 20);
Ftask = Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, tt, 15 * 20);
}
}
@EventHandler
public void onPlayerLeave(PlayerQuitEvent event) {
if (unlol().get())

View file

@ -6,7 +6,6 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
import org.bukkit.ChatColor;
@ -16,7 +15,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
@OptionallyPlayerCommandClass(playerOnly = true)
@CommandClass(helpText = {
"Name color", //
"This command allows you to set how the town colors look on your name.", //

View file

@ -5,7 +5,6 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident;
@ -17,7 +16,6 @@ import org.bukkit.entity.Player;
"Each town in the nation will have it's first color (border) set to this color.", //
"See the help text for /u towncolor for more details.", //
})
@OptionallyPlayerCommandClass(playerOnly = true)
public class NationColorCommand extends UCommandBase {
@Command2.Subcommand
public boolean def(Player player, String color) {

View file

@ -5,7 +5,6 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
@ -21,7 +20,6 @@ import java.lang.reflect.Method;
"The town will be shown with this color on Dynmap and all players in the town will appear in chat with these colors.", //
"The colors will split the name evenly but residents can override that with /u ncolor.", //
}) // TODO: /u u when annotation not present
@OptionallyPlayerCommandClass(playerOnly = true)
@RequiredArgsConstructor
public class TownColorCommand extends UCommandBase {
private final TownColorComponent component;

View file

@ -23,8 +23,8 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import org.dynmap.towny.DTBridge;
import org.dynmap.towny.DynmapTownyPlugin;
import java.io.File;
import java.util.*;
@ -34,6 +34,10 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Town colors for Towny. It allows mayors and kings to set a color for their town/nation (nation can be disabled).
* This color is applied to the player names in chat and on Dynmap, if used.
*/
@ComponentMetadata(depends = TownyComponent.class)
public class TownColorComponent extends Component<PluginMain> implements Listener {
/**
@ -45,10 +49,17 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
*/
public static Map<String, Color> NationColor = new HashMap<>();
/**
* The amount of town colors allowed. If more than one is used, players can change how many letters to be in a specific color using /u ncolor.
*/
public ConfigData<Byte> colorCount() {
return getConfig().getData("colorCount", (byte) 1, cc -> ((Integer) cc).byteValue(), Byte::intValue);
}
/**
* If enabled, players will have a nation-defined color in addition to town colors, white by default.
* They can change how much of each color they want with this as well.
*/
public ConfigData<Boolean> useNationColors() {
return getConfig().getData("useNationColors", true);
}
@ -60,7 +71,6 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
@Override
protected void enable() {
component = this;
//TODO: Don't register all commands automatically (welp)
Consumer<ConfigurationSection> loadTC = cs -> TownColorComponent.TownColors.putAll(cs.getValues(true).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> ((List<String>) v.getValue()).stream()
.map(Color::valueOf).toArray(Color[]::new))));
@ -85,25 +95,7 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
if (usenc)
NationColor.keySet().removeIf(n -> !TownyComponent.TU.getNationsMap().containsKey(n)); // Removes nation colors for deleted/renamed nations
Bukkit.getScheduler().runTask(getPlugin(), () -> {
val dtp = (DynmapTownyPlugin) Bukkit.getPluginManager().getPlugin("Dynmap-Towny");
if (dtp == null)
return;
for (val entry : TownColors.entrySet()) {
try {
val town = TownyComponent.TU.getTownsMap().get(entry.getKey());
Nation nation;
Color nc;
if (!useNationColors().get())
nc = null;
else if (!town.hasNation() || (nation = town.getNation()) == null || (nc = NationColor.get(nation.getName().toLowerCase())) == null)
nc = Color.White;
setTownColor(dtp, buttondevteam.chat.components.towncolors.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue(), nc);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting town color for town " + entry.getKey() + "!", e);
}
}
});
initDynmap();
registerCommand(new TownColorCommand(this));
if (useNationColors().get())
@ -126,6 +118,28 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
v -> v.getValue().toString())));
}
private void initDynmap() {
Bukkit.getScheduler().runTask(getPlugin(), () -> {
val dtp = Bukkit.getPluginManager().getPlugin("Dynmap-Towny");
if (dtp == null)
return;
for (val entry : TownColors.entrySet()) {
try {
val town = TownyComponent.TU.getTownsMap().get(entry.getKey());
Nation nation;
Color nc;
if (!useNationColors().get())
nc = null;
else if (!town.hasNation() || (nation = town.getNation()) == null || (nc = NationColor.get(nation.getName().toLowerCase())) == null)
nc = Color.White;
setTownColor(dtp, buttondevteam.chat.components.towncolors.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue(), nc);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting town color for town " + entry.getKey() + "!", e);
}
}
});
}
/**
* Sets a town's color on Dynmap.
*
@ -133,8 +147,7 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
* @param town The town's name using the correct casing
* @param colors The town's colors
*/
public static void setTownColor(DynmapTownyPlugin dtp, String town, Color[] colors, Color nationcolor) {
public static void setTownColor(Plugin dtp, String town, Color[] colors, Color nationcolor) {
Function<Color, Integer> c2i = c -> c.getRed() << 16 | c.getGreen() << 8 | c.getBlue();
try {
DTBridge.setTownColor(dtp, town, c2i.apply(nationcolor == null ? colors[0] : nationcolor),

View file

@ -11,7 +11,6 @@ import com.palmergames.bukkit.towny.object.Town;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.dynmap.towny.DynmapTownyPlugin;
import java.util.Arrays;
import java.util.Map;
@ -39,52 +38,52 @@ public class TownColorCommand extends AdminCommandBase { //TODO: Command path al
Color[] clrs = new Color[colors.length];
for (int i = 0; i < colors.length; i++) {
val c = getColorOrSendError(colors[i], sender);
if (!c.isPresent())
return true;
if (!c.isPresent())
return true;
clrs[i] = c.get();
}
Color tnc;
boolean usenc = TownColorComponent.getComponent().useNationColors().get();
if (usenc) {
try {
tnc = TownColorComponent.NationColor.get(town.getNation().getName().toLowerCase());
} catch (Exception e) {
tnc = null;
}
if (tnc == null) tnc = Color.White; //Default nation color - TODO: Make configurable
} else tnc = null;
for (Map.Entry<String, Color[]> other : TownColorComponent.TownColors.entrySet()) {
Color nc;
if (usenc) {
try {
nc = TownColorComponent.NationColor.get(TownyComponent.TU.getTownsMap().get(other.getKey()).getNation().getName().toLowerCase());
} catch (Exception e) { //Too lazy for lots of null-checks and it may throw exceptions anyways
nc = null;
}
if (nc == null) nc = Color.White; //Default nation color
} else nc = null;
if (!usenc || nc.getName().equals(tnc.getName())) {
int C = 0;
if (clrs.length == other.getValue().length)
for (int i = 0; i < clrs.length; i++)
if (clrs[i].getName().equals(other.getValue()[i].getName()))
C++;
else break;
if (C == clrs.length) {
sender.sendMessage("§cThis town color combination is already used!");
return true;
}
}
}
}
Color tnc;
boolean usenc = TownColorComponent.getComponent().useNationColors().get();
if (usenc) {
try {
tnc = TownColorComponent.NationColor.get(town.getNation().getName().toLowerCase());
} catch (Exception e) {
tnc = null;
}
if (tnc == null) tnc = Color.White; //Default nation color - TODO: Make configurable
} else tnc = null;
for (Map.Entry<String, Color[]> other : TownColorComponent.TownColors.entrySet()) {
Color nc;
if (usenc) {
try {
nc = TownColorComponent.NationColor.get(TownyComponent.TU.getTownsMap().get(other.getKey()).getNation().getName().toLowerCase());
} catch (Exception e) { //Too lazy for lots of null-checks and it may throw exceptions anyways
nc = null;
}
if (nc == null) nc = Color.White; //Default nation color
} else nc = null;
if (!usenc || nc.getName().equals(tnc.getName())) {
int C = 0;
if (clrs.length == other.getValue().length)
for (int i = 0; i < clrs.length; i++)
if (clrs[i].getName().equals(other.getValue()[i].getName()))
C++;
else break;
if (C == clrs.length) {
sender.sendMessage("§cThis town color combination is already used!");
return true;
}
}
}
TownColorComponent.TownColors.put(town.getName().toLowerCase(), clrs);
TownyListener.updateTownMembers(town);
val dtp = (DynmapTownyPlugin) Bukkit.getPluginManager().getPlugin("Dynmap-Towny");
val dtp = Bukkit.getPluginManager().getPlugin("Dynmap-Towny");
if (dtp != null) //If it's not found then it's not loaded, it'll be noticed by the admins if needed
TownColorComponent.setTownColor(dtp, town.getName(), clrs, tnc);
sender.sendMessage("§bColor(s) set.");
return true;
}
sender.sendMessage("§bColor(s) set.");
return true;
}
public static Optional<Color> getColorOrSendError(String name, CommandSender sender) {
val c = Arrays.stream(Color.values()).skip(1).filter(cc -> cc.getName().equalsIgnoreCase(name)).findAny();
@ -102,5 +101,5 @@ public class TownColorCommand extends AdminCommandBase { //TODO: Command path al
public static String getTownNameCased(String name) {
return TownyComponent.TU.getTownsMap().get(name.toLowerCase()).getName();
}
}
}

View file

@ -22,6 +22,10 @@ import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* This component manages the town and nation chat. It's also needed for the TownColorComponent.
* It provides the TC and NC channels, and posts Towny messages (global, town, nation) to the correct channels for other platforms like Discord.
*/
public class TownyComponent extends Component<PluginMain> {
public static TownyUniverse TU;
private static ArrayList<String> Towns;

View file

@ -57,7 +57,8 @@ public class PlayerJoinLeaveListener implements Listener {
nwithoutformatting = p.getName();
PlayerListener.nicknames.forcePut(nwithoutformatting.toLowerCase(), p.getUniqueId()); //TODO: FormatterComponent
HistoryCommand.showHistory(e.getPlayer(), null);
if (PluginMain.Instance.storeChatHistory().get())
HistoryCommand.showHistory(e.getPlayer(), null);
}
@EventHandler

View file

@ -161,6 +161,8 @@ public class PlayerListener implements Listener {
}
}
private long lastError = 0;
@EventHandler
public void onPlayerTBMCChat(TBMCChatEvent e) {
try {
@ -169,6 +171,13 @@ public class PlayerListener implements Listener {
HistoryCommand.addChatMessage(e.getCm(), e.getChannel());
e.setCancelled(FormatterComponent.handleChat(e));
} catch (NoClassDefFoundError | Exception ex) { // Weird things can happen
if (lastError < System.nanoTime() - 1000L * 1000L * 1000L * 60 * 60 //60 mins
&& Bukkit.getOnlinePlayers().size() > 0) { //If there are no players on, display to the first person
lastError = System.nanoTime(); //I put the whole thing in the if to protect against race conditions
for (Player p : Bukkit.getOnlinePlayers())
if (e.shouldSendTo(p))
p.sendMessage("[" + e.getChannel().DisplayName().get() + "] §cSome features in the message below might be unavailable due to an error.");
}
ChatUtils.sendChatMessage(e, s -> "§c!§r" + s);
TBMCCoreAPI.SendException("An error occured while processing a chat message!", ex);
}

View file

@ -3,6 +3,7 @@ package org.dynmap.towny;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin;
import org.dynmap.bukkit.DynmapPlugin;
import org.dynmap.markers.MarkerAPI;
@ -14,32 +15,27 @@ import java.util.Map;
public class DTBridge {
/**
* Sets the town color on Dynmap.
*
* @param dtp
* The Dynmap-Towny plugin
* @param townname
* The name of the town, using correct casing
* @param strokecolor
* The stroke color in RGB format
* @param fillcolor
* The fill color in RGB format
* @throws Exception
* When couldn't set the town color
*
* @param dtp The Dynmap-Towny plugin
* @param townname The name of the town, using correct casing
* @param strokecolor The stroke color in RGB format
* @param fillcolor The fill color in RGB format
* @throws Exception When couldn't set the town color
*/
public static void setTownColor(DynmapTownyPlugin dtp, String townname, int strokecolor, int fillcolor)
throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, // Keeping these because why not
IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException {
public static void setTownColor(Plugin dtp, String townname, int strokecolor, int fillcolor)
throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, // Keeping these because why not
IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException {
Class<?> cl = Class.forName(DynmapTownyPlugin.class.getName() + "$AreaStyle");
Field field = DynmapTownyPlugin.class.getDeclaredField("cusstyle");
field.setAccessible(true); // Doesn't allow accessing it from the same package, if it's from a different plugin
field.setAccessible(true); // Doesn't allow accessing it from the same package, if it's from a different plugin
@SuppressWarnings("unchecked")
val map = (Map<String, Object>) field.get(dtp);
Object style = map.get(townname);
if (style == null) {
Constructor<?> c = cl.getDeclaredConstructor(FileConfiguration.class, String.class, MarkerAPI.class);
c.setAccessible(true);
style = c.newInstance(dtp.getConfig(), "custstyle." + townname,
((DynmapPlugin) Bukkit.getPluginManager().getPlugin("dynmap")).getMarkerAPI());
style = c.newInstance(dtp.getConfig(), "custstyle." + townname,
((DynmapPlugin) Bukkit.getPluginManager().getPlugin("dynmap")).getMarkerAPI());
map.put(townname, style);
}
set(cl, style, "fillcolor", fillcolor);
@ -47,7 +43,7 @@ public class DTBridge {
}
private static <T> void set(Class<?> cl, Object style, String fieldname, T value)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Field field = cl.getDeclaredField(fieldname);
field.setAccessible(true);
field.set(style, value);

View file

@ -40,10 +40,6 @@ soft-depend:
- Dynmap-Towny
- Towny
permissions:
tbmc.admin:
description: Gives access to /un- commands and /u admin commands
tbmc.rainbow:
description: Gives access to rainbow colors (/u c).
tbmc.badge.gold:
description: Gives a patron badge.
default: false

View file

@ -37,36 +37,41 @@ public class ChatFormatIT {
list.add(new ChatFormatIT(sender, "*test*", new TellrawPart("test").setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "**test**", new TellrawPart("test").setBold(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***test***",
new TellrawPart("test").setBold(true).setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***_test_***",
new TellrawPart("test").setBold(true).setItalic(true).setUnderlined(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***_~~test~~_***", new TellrawPart("test").setBold(true).setItalic(true)
.setUnderlined(true).setStrikethrough(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "¯\\\\\\_(ツ)\\_/¯", new TellrawPart("¯\\_(ツ)_/¯").setColor(Color.White)));
new TellrawPart("test").setBold(true).setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***__test__***",
new TellrawPart("test").setBold(true).setItalic(true).setUnderlined(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***__~~test~~__***", new TellrawPart("test").setBold(true).setItalic(true)
.setUnderlined(true).setStrikethrough(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "¯\\\\\\_(ツ)\\_/¯", new TellrawPart("¯\\_(ツ)_/¯").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "https://google.hu/",
new TellrawPart("https://google.hu/").setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://google.hu/"))));
list.add(new ChatFormatIT(sender, "*test", new TellrawPart("*test").setColor(Color.White)));
new TellrawPart("https://google.hu/").setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://google.hu/"))));
list.add(new ChatFormatIT(sender, "*test", new TellrawPart("*test").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "**test*", new TellrawPart("**test*").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***test", new TellrawPart("***test").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "Koiiev", new TellrawPart("§bKoiiev§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "Koiiev", new TellrawPart("§bKoiiev§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "norbipeti", new TellrawPart("§bNorbiPeti§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "Arsen_Derby_FTW", new TellrawPart("§bArsen_Derby_FTW§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "Arsen_Derby_FTW", new TellrawPart("§bArsen_Derby_FTW§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "carrot_lynx", new TellrawPart("§bcarrot_lynx§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "*carrot_lynx*", new TellrawPart("§bcarrot_lynx§r").setItalic(true).setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "https://norbipeti.github.io/", new TellrawPart("https://norbipeti.github.io/")
.setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/"))));
.setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/"))));
list.add(new ChatFormatIT(sender, "*https://norbipeti.github.io/ heh*", new TellrawPart("https://norbipeti.github.io/").setItalic(true).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/")), new TellrawPart(" heh").setItalic(true)));
list.add(new ChatFormatIT(sender, "*test _test_ test*", new TellrawPart("test ").setItalic(true).setColor(Color.White),
new TellrawPart("test").setItalic(true).setUnderlined(true).setColor(Color.White), new TellrawPart(" test").setItalic(true).setColor(Color.White)));
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/")), new TellrawPart(" heh").setItalic(true)));
list.add(new ChatFormatIT(sender, "*test _test_ test*", new TellrawPart("test test test").setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "*test __test__ test*", new TellrawPart("test ").setItalic(true).setColor(Color.White),
new TellrawPart("test").setItalic(true).setUnderlined(true).setColor(Color.White), new TellrawPart(" test").setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "**test __test__ test**", new TellrawPart("test ").setBold(true).setColor(Color.White),
new TellrawPart("test").setBold(true).setUnderlined(true).setColor(Color.White), new TellrawPart(" test").setBold(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "**test _test_ test**", new TellrawPart("test ").setBold(true).setColor(Color.White),
new TellrawPart("test").setItalic(true).setBold(true).setColor(Color.White), new TellrawPart(" test").setBold(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "https://norbipeti.github.io/test?test&test#test", new TellrawPart("https://norbipeti.github.io/test?test&test#test")
.setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
@ -77,6 +82,11 @@ public class ChatFormatIT {
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/test"))));
TellrawPart space = new TellrawPart(" ").setColor(Color.White);
list.add(new ChatFormatIT(sender, "A rainbow text for testing. O", new TellrawPart("A").setColor(Color.Red),
space, new TellrawPart("rainbow").setColor(Color.Gold), space, new TellrawPart("text").setColor(Color.Yellow),
space, new TellrawPart("for").setColor(Color.Green), space, new TellrawPart("testing.").setColor(Color.Blue),
space, new TellrawPart("O").setColor(Color.DarkPurple)).setRainbowMode());
return list;
}
@ -84,6 +94,7 @@ public class ChatFormatIT {
private final CommandSender sender;
private final String message;
private final TellrawPart[] extras;
private boolean rainbowMode;
public ChatFormatIT(CommandSender sender, String message, TellrawPart... expectedextras) {
this.sender = sender;
@ -91,10 +102,17 @@ public class ChatFormatIT {
this.extras = expectedextras;
}
private ChatFormatIT setRainbowMode() {
rainbowMode = true;
return this;
}
@Test
public void testMessage() {
ArrayList<ChatFormatter> cfs = ChatProcessing.addFormatters(Color.White, p -> true);
ArrayList<ChatFormatter> cfs = ChatProcessing.addFormatters(Color.White, p -> true, null);
final String chid = ChatProcessing.getChannelID(Channel.GlobalChat, ChatUtils.MCORIGIN);
if (rainbowMode)
ChatProcessing.createRPC(Color.White, cfs);
final TellrawPart tp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatUtils.MCORIGIN);
ChatFormatter.Combine(cfs, message, tp, null);
System.out.println("Testing: " + message);