diff --git a/.gitignore b/.gitignore
index 456756d..a84274b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,224 +1,221 @@
-#################
-## Eclipse
-#################
-
-*.pydevproject
-.metadata/
-bin/
-tmp/
-*.tmp
-*.bak
-*.swp
-*~.nib
-local.properties
-.classpath
-.settings/
-.loadpath
-target/
-.project
-
-# External tool builders
-.externalToolBuilders/
-
-# Locally stored "Eclipse launch configurations"
-*.launch
-
-# CDT-specific
-.cproject
-
-# PDT-specific
-.buildpath
-
-
-#################
-## Visual Studio
-#################
-
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-
-# User-specific files
-*.suo
-*.user
-*.sln.docstates
-
-# Build results
-
-[Dd]ebug/
-[Rr]elease/
-x64/
-build/
-[Bb]in/
-[Oo]bj/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-*_i.c
-*_p.c
-*.ilk
-*.meta
-*.obj
-*.pch
-*.pdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.log
-*.scc
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opensdf
-*.sdf
-*.cachefile
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# NCrunch
-*.ncrunch*
-.*crunch*.local.xml
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.Publish.xml
-*.pubxml
-*.publishproj
-
-# NuGet Packages Directory
-## TO!DO: If you have NuGet Package Restore enabled, uncomment the next line
-#packages/
-
-# Windows Azure Build Output
-csx
-*.build.csdef
-
-# Windows Store app package directory
-AppPackages/
-
-# Others
-sql/
-*.Cache
-ClientBin/
-[Ss]tyle[Cc]op.*
-~$*
-*~
-*.dbmdl
-*.[Pp]ublish.xml
-*.pfx
-*.publishsettings
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file to a newer
-# Visual Studio version. Backup files are not needed, because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-
-# SQL Server files
-App_Data/*.mdf
-App_Data/*.ldf
-
-#############
-## Windows detritus
-#############
-
-# Windows image file caches
-Thumbs.db
-ehthumbs.db
-
-# Folder config file
-Desktop.ini
-
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
-
-# Mac crap
-.DS_Store
-
-
-#############
-## Python
-#############
-
-*.py[cod]
-
-# Packages
-*.egg
-*.egg-info
-dist/
-build/
-eggs/
-parts/
-var/
-sdist/
-develop-eggs/
-.installed.cfg
-
-# Installer logs
-pip-log.txt
-
-# Unit test / coverage reports
-.coverage
-.tox
-
-#Translations
-*.mo
-
-#Mr Developer
-.mr.developer.cfg
-.metadata/*
-TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
-#*.iml
-*.name
-.idea/compiler.xml
-*.xml
-/.apt_generated/
+#################
+## Eclipse
+#################
+
+*.pydevproject
+.metadata/
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+target/
+.project
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+
+#################
+## Visual Studio
+#################
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+build/
+[Bb]in/
+[Oo]bj/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.Publish.xml
+*.pubxml
+*.publishproj
+
+# NuGet Packages Directory
+## TO!DO: If you have NuGet Package Restore enabled, uncomment the next line
+#packages/
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.[Pp]ublish.xml
+*.pfx
+*.publishsettings
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+#############
+## Windows detritus
+#############
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
+
+
+#############
+## Python
+#############
+
+*.py[cod]
+
+# Packages
+*.egg
+*.egg-info
+dist/
+eggs/
+parts/
+var/
+sdist/
+develop-eggs/
+.installed.cfg
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+
+#Translations
+*.mo
+
+#Mr Developer
+.mr.developer.cfg
+.metadata/*
+TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
+*.iml
+*.name
+.idea/compiler.xml
+*.xml
+/.apt_generated/
diff --git a/.idea/ButtonChat.iml b/.idea/ButtonChat.iml
deleted file mode 100644
index dec5138..0000000
--- a/.idea/ButtonChat.iml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 1d962ae..a3dfe4a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,21 +7,17 @@ before_install: | # Wget BuildTools and run if cached folder not found
# grep so that download counts don't appear in log files
java -jar BuildTools.jar --rev 1.12.2 | grep -vE "[^/ ]*/[^/ ]*\s*KB\s*$" | grep -v "^\s*$"
fi
+ cp ci/settings.xml $HOME/.m2/
language: java
jdk:
- oraclejdk8
sudo: true
dist: trusty # Needed for Java 8, although we might not need Java 8
deploy:
- # deploy develop to the staging environment
- - provider: script
- script: chmod +x deploy.sh && sh deploy.sh staging
+ - provider: releases
+ api_key:
+ secure: "F5YiEuD6LyRENUDMCslcSl0O0dg4IDk+nNeb4X2VLYlmb8dW9beMuIgjH8efTMeaQ3D/ntIkN0Dtf2GKvpOduhwkSbAgw4WM028X60SY9f2hmpEO3LmM4T1tKoDlI1T3BmhYP4KeTKBYn+etV1mSPbT07vUybCm/vGzvr96yMZGNFEoKsWLaEu7dZfBFULj4tXOwrLh/KO6BsdAHvZcGKWNVupPq3YoUVT0dpGcUudf5cpn+aaqMwyd709zgMbyCuqf+c5Udps43q4EKvr9z7TWxFUkGTPVVAcUVygJsi2ytuyA8TLMPq/KhYe9htnkNUnizbqv/j49xww0gVaD7OJXENJ4hAUTV4sdn1DXG45JXW+dir3V7YzbRYn3M+eCuKB2O77SXRZBkxcGtTMtCmghP9/tcRAQlXDXnxu7oAnlUVp17g/+aFApvlzZEZVx2N+fkyEe7JrUFlRCixtHyrmTLWhyV0Px9p0FHJpvSSCL0S0UKVAT/sNHYHhD5gouK7owEomEbG58XCsRDH6Et7RuDksB98ekK8brZp6S7dNIS2CVuVx1vIkXC8PzUGcpJQoztvEYUE20Axahh5s8AkE9n/O9jzs9ajcfYaHhWzYeUZzHdHllOYF9l6VoCUitTk4Sl8eJifSq3GzI+T6wGMBepZHLpe230MvBIrqGZ+Vg="
+ file: 'Chroma-Core/target/Chroma-Core.jar'
on:
- branch: dev
- skip_cleanup: true
- # deploy master to production
- - provider: script
- script: chmod +x deploy.sh && sh deploy.sh production
- on:
- branch: master
+ tags: true
skip_cleanup: true
diff --git a/Mvn.txt b/Mvn.txt
deleted file mode 100644
index 8e9f41f..0000000
--- a/Mvn.txt
+++ /dev/null
@@ -1 +0,0 @@
-apache-maven-3.2.5/bin/mvn install:install-file -Dfile=Towny.jar -DgroupId=com.palmergames -DartifactId=Towny -Dversion=0.90.0.0 -Dpackaging=jar
diff --git a/README.md b/README.md
index b00ded7..32b9c62 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,133 @@
-# Chroma-Chat plugin
-A plugin that provides markdown formatting, name mentioning and other chat features.
+# The Button Minecraft server chat plugin
-## Components
+## How to use
+### Players
+#### Obtaining the flair (/u accept)
+At first, you need to connect your Reddit account with your Minecraft account. This is done by writing your Minecraft name to [this thread](https://www.reddit.com/r/Chromagamers/comments/51ys94/flair_thread_for_the_mc_server/), following the instructions in the post.
-### Announcer
-This component simply sends messages globally based on a configurable list in a given interval.
+When you're done, connect to the server, if you aren't already on. You should see a message after a while (max. 10 seconds) asking if you're the correct Reddit user. Type /u accept to confirm it and accept your flair.
-### Append text
-This component allows the players to put tableflips and other texts after their messages using /tableflip and other commands.
+__Note__: You can comment your ingame name from multiple Reddit accounts. In this case, you'll need to specify the exact username you want to pair with your Minecraft account. For example:
-### Chat-only
-This component lets a player become invincible at the cost of not being able to move. It was made for chat clients.
+ /u accept NorbiPeti
-### Flair
-This component downloads /r/TheButton flairs from Reddit for users who comment in a specific thread. This was the original function of the plugin.
+#### Hiding the message (/u ignore)
+You can use this command to hide the notice showing up after you log in if you don't have a flair accepted.
-### Formatter
-This component provides the Markdown and other formatting and name mentioning.
+#### Flair not showing up
+Please note that in some cases your flair cannot be obtained (specifically, if it's not stored by karmadecay.com, in which case possibly Karmancer can't show it as well). In this case, there are two possibilities.
-### Fun
-This component has some random things I added for fun, including "Press F to pay respects" and rainbow chat.
+If you're a non-presser or a can't press and only your time is recorded, it will automatically decide based on your account creation date.
-### Town colors
-This component allows mayors and kings to set a color for their town/nation, which sets each resident's name colors and also shows on Dynmap.
+If nothing is known about your flair, you need to ask an admin to set the flair for you. Please prepare a link to a comment you made on /r/thebutton, if possible.
-### Towny
-This component is needed by the town colors component. Besides, it reads Towny events to be broadcasted to Discord, if that plugin is used.
+#### Getting someone's Reddit username (/u name)
+You can see a player's username if they have a flair shown.
+
+#### Name mentioning
+If you simply say any online player's full playername or nickname, it'll highlight it and play a sound for the target player.
+
+If you say a nickname, it'll show it's original colors, if you say a username, then it will choose based on flair color if known, otherwise it'll use the aqua color.
+
+You can also use @console to ping the console. If someone is there, they'll receive an audible bell signal.
+
+#### RP/OOC mode (/nrp or /ooc)
+You can use /ooc to say something Out-of-Character. Otherwise everything you speak should be treated as said in RP, except when it is obvious it's not in RP (like talking about the server).
+
+#### Greentext support (>message)
+Start your message with '>' to make it green.
+
+#### Hashtags (#hashtag)
+If you say a hashtag in global chat, it'll highlight it and makes it clickable, linking to the hashtag page on Twitter.
+
+#### Paying respects (F)
+If a player dies, sometimes the plugin will tell everyone "Press F to pay respects.". After a few seconds, a message will tell everyone how many people paid their respects.
+
+If you hover over a player's name in chat, you can see how much respect they gained this way, divided by the number of eligible deaths.
+
+#### Copy messages
+To copy a message from chat, click the channel identifier (for example: [g] or [TC]) at the beginning of the message.
+
+#### Tableflipping (/tableflip and /unflip) and shrug (/shrug)
+The idea of this command came from Discord.
+
+Examples:
+
+ /tableflip - (╯°□°)╯︵ ┻━┻
+ /tableflip test - test(╯°□°)╯︵ ┻━┻
+ /unflip - ┬─┬ ノ( ゜-゜ノ)
+ /unflip test - test┬─┬ ノ( ゜-゜ノ)
+
+#### Chat only (/chatonly)
+You can use this mode to protect yourself if you connect from a chat-only client. This will make you invincible, but unable to move, teleport or interact with the world in any way.
+
+#### Rainbow chat (/u c)
+Chat in rainbow/Presser colors.
+
+### Admins
+Type /u admin for a list of the commands.
+#### Seeing player information (/u admin playerinfo)
+You can check someone's flair status and other infos in case something goes wrong.
+
+It outputs the player name (useful if something goes *really* wrong), the player's current ingame flair, the Reddit username, and their flair status:
+
+* Accepted: The user has a flair shown after their name.
+* Ignored: The user chose to hide their flair, but they have one.
+* Recognised: The user has recognised flair(s) but haven't accepted any.
+* Commented: The user commented, but their flair isn't known.
+* NoComment: The user haven't commented in the thread.
+
+#### Reloading the plugin (/u admin reload)
+This is useful if you want to change a file related to the plugin.
+
+Be careful and make sure you do /u admin save before you reload the plugin. You need to confirm your action (/u admin confirm) to make sure no setting is lost.
+
+If you want to edit a file, you need to do /u admin save, then edit the file you want, then do /u admin reload.
+
+#### Setting the flair by hand (/u admin setflair)
+This allows you to set any flair you want to any player. This will override the automatic flairs, though it's not recommended to do so. However, the player can reset the automatic flair at any time (see /u accept).
+
+* To remove a user's flair:
+
+ /u admin setflair none false
+
+* To set a non-presser or can't press flair:
+
+ /u admin setflair non-presser/cant-press false
+
+* To set a cheater flair:
+
+ /u admin setflair true
+
+* To also set the username for the flair:
+
+ /u admin setflair
+
+Usage:
+
+ /u admin setflair > [username]
+
+#### Updating the plugin (/u admin updateplugin)
+I've made a simple command to allow updating the plugin easily. After running this command, the server needs to get restarted for the changes to take effect.
+
+This command will not do any other thing than downloading the JAR file from here to the plugins directory. Do not spam it, because it will then generate unnecessary network traffic on the server.
+
+#### Toggle settings
+
+ /u admin togglerpshow - Toggles [RP] tag shown at each message except /ooc or /nrp.
+ /u admin debug - Toggles debug mode. Currently it outputs a lot of info about the chat formatting.
+
+#### Setting the sound played on name mentioning
+You can set the sound played by editing "notificationsound" and "notificationpitch" in the config file (thebuttonmc.yml).
+
+#### Announcements (/u announce)
+You can make announcements broadcasted every n minutes where you can set n and it defaults to 15.
+
+ /u announce add - Adds a new announcement. It supports formatting codes with &.
+ /u announce remove - Remove announcement by index (see below).
+ /u announce list - List announcements with indexes.
+ /u announce settime - Set the time between announcements in minutes.
+ /u announce edit - Directly edits the announcement at the specified index. If there are less announcements than index, it'll create enough announcements.
+
+#### Color modes (/u c)
+Use /u c \ to set the color of your messages.
diff --git a/ci/settings.xml b/ci/settings.xml
new file mode 100644
index 0000000..a0cab53
--- /dev/null
+++ b/ci/settings.xml
@@ -0,0 +1,36 @@
+
+
+
+ github
+
+
+
+
+ github
+
+
+
+ github
+ GitHub Towny Apache Maven Packages
+ https://maven.pkg.github.com/TownyAdvanced/Towny
+
+
+
+
+
+
+
+ github
+ NorbiPeti
+ ${env.GHTOKEN}
+
+
+
diff --git a/deploy.sh b/deploy.sh
deleted file mode 100644
index 82c8e92..0000000
--- a/deploy.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-FILENAME=$(find target/ -maxdepth 1 ! -name '*original*' -name '*.jar')
-echo Found file: $FILENAME
-
-if [ $1 = 'production' ]; then
-echo Production mode
-echo $UPLOAD_KEY > upload_key
-chmod 400 upload_key
-yes | scp -B -i upload_key -o StrictHostKeyChecking=no $FILENAME travis@server.figytuna.com:/minecraft/main/TBMC/pluginupdates
-fi
diff --git a/src/main/java/buttondevteam/chat/ChatPlayer.java b/src/main/java/buttondevteam/chat/ChatPlayer.java
index 9301bb6..f3ee87b 100644
--- a/src/main/java/buttondevteam/chat/ChatPlayer.java
+++ b/src/main/java/buttondevteam/chat/ChatPlayer.java
@@ -51,7 +51,6 @@ public class ChatPlayer extends TBMCPlayerBase {
public boolean RainbowPresserColorMode = false;
public Color OtherColorMode = null;
public boolean ChatOnly = false;
- public long LastMessageTime = 0L;
public static final int FlairTimeNonPresser = -1;
public static final int FlairTimeCantPress = -2;
@@ -59,8 +58,9 @@ public class ChatPlayer extends TBMCPlayerBase {
/**
* Gets the player's flair, optionally formatting for Minecraft.
- *
- * @param noformats The MC formatting codes will be only applied if false
+ *
+ * @param noformats
+ * The MC formatting codes will be only applied if false
* @return The flair
*/
public String GetFormattedFlair(boolean noformats) {
@@ -76,7 +76,7 @@ public class ChatPlayer extends TBMCPlayerBase {
/**
* Gets the player's flair, formatted for Minecraft.
- *
+ *
* @return The flair
*/
public String GetFormattedFlair() {
@@ -100,7 +100,7 @@ public class ChatPlayer extends TBMCPlayerBase {
// PluginMain.Instance.getServer().getScoreboardManager().getMainScoreboard().getTeams().add()
Player p = Bukkit.getPlayer(uuid);
if (p != null)
- p.setPlayerListName(String.format("%s%s", p.getDisplayName(), GetFormattedFlair()));
+ p.setPlayerListName(String.format("%s%s", p.getDisplayName(), GetFormattedFlair()));
}
public short GetFlairColor() {
diff --git a/src/main/java/buttondevteam/chat/PluginMain.java b/src/main/java/buttondevteam/chat/PluginMain.java
index 6e0f929..acfd694 100644
--- a/src/main/java/buttondevteam/chat/PluginMain.java
+++ b/src/main/java/buttondevteam/chat/PluginMain.java
@@ -69,13 +69,13 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
Component.registerComponent(this, new FunComponent());
Component.registerComponent(this, new AppendTextComponent());
Component.registerComponent(this, new FormatterComponent());
- getCommand2MC().registerCommand(new DebugCommand());
- getCommand2MC().registerCommand(new HelpCommand());
- getCommand2MC().registerCommand(new HistoryCommand());
- getCommand2MC().registerCommand(new InfoCommand());
- getCommand2MC().registerCommand(new MWikiCommand());
- getCommand2MC().registerCommand(new ReloadCommand());
- getCommand2MC().registerCommand(new SnapCommand());
+ registerCommand(new DebugCommand());
+ registerCommand(new HelpCommand());
+ registerCommand(new HistoryCommand());
+ registerCommand(new InfoCommand());
+ registerCommand(new MWikiCommand());
+ registerCommand(new ReloadCommand());
+ registerCommand(new SnapCommand());
}
public static Essentials essentials = null;
diff --git a/src/main/java/buttondevteam/chat/components/chatonly/ChatOnlyComponent.java b/src/main/java/buttondevteam/chat/components/chatonly/ChatOnlyComponent.java
index ecf0ce1..f67c888 100644
--- a/src/main/java/buttondevteam/chat/components/chatonly/ChatOnlyComponent.java
+++ b/src/main/java/buttondevteam/chat/components/chatonly/ChatOnlyComponent.java
@@ -1,6 +1,7 @@
package buttondevteam.chat.components.chatonly;
import buttondevteam.chat.ChatPlayer;
+import buttondevteam.chat.PluginMain;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.ComponentManager;
@@ -17,10 +18,10 @@ 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 moving.
+ * 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 {
+public class ChatOnlyComponent extends Component implements Listener {
@Override
protected void enable() {
registerListener(this);
diff --git a/src/main/java/buttondevteam/chat/components/flair/SetFlairCommand.java b/src/main/java/buttondevteam/chat/components/flair/SetFlairCommand.java
index 36d9275..575d57f 100644
--- a/src/main/java/buttondevteam/chat/components/flair/SetFlairCommand.java
+++ b/src/main/java/buttondevteam/chat/components/flair/SetFlairCommand.java
@@ -23,7 +23,7 @@ public class SetFlairCommand extends AdminCommandBase {
sender.sendMessage("§cPlayer not found.&r");
return true;
}
- short ft = 0x00;
+ short ft;
if (flairtime.equalsIgnoreCase("non-presser"))
ft = ChatPlayer.FlairTimeNonPresser;
else if (flairtime.equalsIgnoreCase("cant-press"))
diff --git a/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java b/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java
index 4e48b8f..7c5148b 100644
--- a/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java
+++ b/src/main/java/buttondevteam/chat/components/formatter/ChatProcessing.java
@@ -6,10 +6,7 @@ import buttondevteam.chat.PluginMain;
import buttondevteam.chat.VanillaUtils;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.components.chatonly.ChatOnlyComponent;
-import buttondevteam.chat.components.formatter.formatting.ChatFormatter;
-import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
-import buttondevteam.chat.components.formatter.formatting.TellrawPart;
-import buttondevteam.chat.components.formatter.formatting.TellrawSerializer;
+import buttondevteam.chat.components.formatter.formatting.*;
import buttondevteam.chat.components.fun.FunComponent;
import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.chat.listener.PlayerListener;
@@ -19,7 +16,6 @@ import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Color;
-import buttondevteam.lib.chat.Priority;
import buttondevteam.lib.chat.TellrawSerializableEnum;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer;
@@ -43,73 +39,56 @@ import java.util.function.Predicate;
import java.util.regex.Pattern;
public class ChatProcessing {
- private static final Pattern NULL_MENTION_PATTERN = Pattern.compile("null");
- private static final Pattern CYAN_PATTERN = Pattern.compile("cyan");
- private static final Pattern ESCAPE_PATTERN = Pattern.compile("\\\\");
- private static final Pattern CONSOLE_PING_PATTERN = Pattern.compile("(?i)" + Pattern.quote("@console"));
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 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");
- 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,
Color.Blue, Color.DarkPurple};
private static final Pattern WORD_PATTERN = Pattern.compile("\\S+");
private static boolean pingedconsole = false;
- public static final ChatFormatter ESCAPE_FORMATTER = ChatFormatter.builder("escape", ESCAPE_PATTERN).build();
-
- private static ArrayList commonFormatters = Lists.newArrayList(
- 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("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(),
- ChatFormatter.builder("spoiler", SPOILER_PATTERN).obfuscated(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
+ private static ArrayList commonFormatters = Lists.newArrayList(
+ new RangeMatchProvider("bold", "**", FormatSettings.builder().bold(true).build()),
+ new RangeMatchProvider("italic", "*", FormatSettings.builder().italic(true).build()),
+ new RangeMatchProvider("underlined", "__", FormatSettings.builder().underlined(true).build()),
+ new RangeMatchProvider("italic2", "_", FormatSettings.builder().italic(true).build()),
+ new RangeMatchProvider("strikethrough", "~~", FormatSettings.builder().strikethrough(true).build()),
+ new RangeMatchProvider("spoiler", "||", FormatSettings.builder().obfuscated(true)
.onmatch((match, cf, fs) -> {
cf.setHoverText(match);
return match;
- }).build(),
- ESCAPE_FORMATTER, ChatFormatter.builder("nullMention", NULL_MENTION_PATTERN).color(Color.DarkRed).build(), // Properly added a bug as a feature
- ChatFormatter.builder("consolePing", CONSOLE_PING_PATTERN).color(Color.Aqua).onmatch((match, builder, section) -> {
- if (!pingedconsole) {
- System.out.print("\007");
- pingedconsole = true; // Will set it to false in ProcessChat
- }
- return match;
- }).priority(Priority.High).build(),
+ }).build()),
+ new StringMatchProvider("nullMention", FormatSettings.builder().color(Color.DarkRed).build(), true, "null"), // Properly added a bug as a feature
+ new StringMatchProvider("consolePing", FormatSettings.builder().color(Color.Aqua)
+ .onmatch((match, builder, section) -> {
+ if (!pingedconsole) {
+ System.out.print("\007");
+ pingedconsole = true; // Will set it to false in ProcessChat
+ }
+ return "@console";
+ }).build(), true, "@console"),
- ChatFormatter.builder("hashtag", HASHTAG_PATTERN).color(Color.Blue).openlink("https://twitter.com/hashtag/$1")
- .priority(Priority.High).build(),
- ChatFormatter.builder("cyan", CYAN_PATTERN).color(Color.Aqua).build(), // #55
- ChatFormatter.builder("code", CODE_PATTERN).color(Color.DarkGray).removeCharCount((short) 1).type(ChatFormatter.Type.Range)
- .build(),
- ChatFormatter.builder("maskedLink", MASKED_LINK_PATTERN).underlined(true).onmatch((match, builder, section) -> {
- String text, link;
- if (section.Matches.size() < 2 || (text = section.Matches.get(0)).length() == 0 || (link = section.Matches.get(1)).length() == 0)
- return "";
- 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("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());
+ new StringMatchProvider("cyan", FormatSettings.builder().color(Color.Aqua).build(), true, "cyan"), // #55
+ new RangeMatchProvider("code", "`", FormatSettings.builder().color(Color.DarkGray).build()),
+ new RegexMatchProvider("maskedLink", MASKED_LINK_PATTERN, FormatSettings.builder().underlined(true)
+ .onmatch((match, builder, section) -> {
+ String text, link;
+ if (section.Matches.size() < 2 || (text = section.Matches.get(0)).length() == 0 || (link = section.Matches.get(1)).length() == 0)
+ return "";
+ builder.setOpenlink(link);
+ return text;
+ }).build()),
+ new RegexMatchProvider("url", URL_PATTERN, FormatSettings.builder().underlined(true).openlink("$1").build()),
+ new RegexMatchProvider("hashtag", HASHTAG_PATTERN, FormatSettings.builder().color(Color.Blue).openlink("https://twitter.com/hashtag/$1").build()),
+ new StringMatchProvider("someone", FormatSettings.builder().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(), true, "@someone"));
private static Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum())
.registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection())
@@ -134,29 +113,20 @@ public class ChatProcessing {
return true;
}
+ doFunStuff(sender, e, message);
+
+ final String channelidentifier = getChannelID(channel, e.getOrigin());
+ PluginMain.Instance.getServer().getConsoleSender()
+ .sendMessage(String.format("%s <%s§r> %s", channelidentifier, getSenderName(sender, player), message));
+
+ if (Bukkit.getOnlinePlayers().size() == 0) return false; //Don't try to send to nobody (errors on 1.14)
+
ChatPlayer mp;
if (player != null)
mp = TBMCPlayerBase.getPlayer(player.getUniqueId(), ChatPlayer.class);
else //Due to the online player map, getPlayer() can be more efficient than getAs()
mp = e.getUser().getAs(ChatPlayer.class); //May be null
- if (mp != null) {
- if (System.nanoTime() - mp.LastMessageTime < 1000 * component.minTimeBetweenMessages().get()) { //0.1s by default
- sender.sendMessage("§cYou are sending messages too fast!");
- return true;
- }
- mp.LastMessageTime = System.nanoTime();
- }
-
- doFunStuff(sender, e, message);
-
- final String channelidentifier = getChannelID(channel, e.getOrigin());
-
- PluginMain.Instance.getServer().getConsoleSender()
- .sendMessage(String.format("%s <%s§r> %s", channelidentifier, getSenderName(sender, player), message));
-
- if (Bukkit.getOnlinePlayers().size() == 0) return false; //Don't try to send to nobody (errors on 1.14)
-
Color colormode = channel.Color().get();
if (mp != null && mp.OtherColorMode != null)
colormode = mp.OtherColorMode;
@@ -164,20 +134,19 @@ public class ChatProcessing {
colormode = Color.Green;
// If greentext, ignore channel or player colors
- ArrayList formatters;
+ ArrayList formatters;
if (component.allowFormatting().get()) {
- formatters = addFormatters(colormode, e::shouldSendTo, component);
+ formatters = addFormatters(e::shouldSendTo, component);
if (colormode == channel.Color().get() && mp != null && mp.RainbowPresserColorMode) { // Only overwrite channel color
createRPC(colormode, formatters);
}
pingedconsole = false; // Will set it to true onmatch (static constructor)
} else
- formatters = Lists.newArrayList(ChatFormatter.builder("entireMessage", ENTIRE_MESSAGE_PATTERN)
- .color(Color.White).priority(Priority.Low).build()); //This formatter is necessary
+ formatters = Lists.newArrayList();
TellrawPart json = createTellraw(sender, message, player, mp, e.getUser(), channelidentifier, e.getOrigin());
long combinetime = System.nanoTime();
- ChatFormatter.Combine(formatters, message, json, component.getConfig());
+ ChatFormatter.Combine(formatters, message, json, component.getConfig(), FormatSettings.builder().color(colormode).build());
combinetime = System.nanoTime() - combinetime;
String jsonstr = toJson(json);
if (jsonstr.length() >= 32767) {
@@ -222,12 +191,12 @@ public class ChatProcessing {
return false;
}
- static void createRPC(Color colormode, ArrayList formatters) {
+ static void createRPC(Color colormode, ArrayList formatters) {
final AtomicInteger rpc = new AtomicInteger(0);
- formatters.add(ChatFormatter.builder("rpc", WORD_PATTERN).color(colormode).onmatch((match, cf, s) -> {
+ formatters.add(new RegexMatchProvider("rpc", WORD_PATTERN, FormatSettings.builder().color(colormode).onmatch((match, cf, s) -> {
cf.setColor(RainbowPresserColors[rpc.getAndUpdate(i -> ++i < RainbowPresserColors.length ? i : 0)]);
return match;
- }).build());
+ }).build()));
}
public static String toJson(TellrawPart json) {
@@ -235,8 +204,8 @@ public class ChatProcessing {
}
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(
@@ -273,43 +242,21 @@ public class ChatProcessing {
+ "]";
}
- static ArrayList addFormatters(Color colormode, Predicate canSee, @Nullable FormatterComponent component) {
+ static ArrayList addFormatters(Predicate canSee, @Nullable FormatterComponent component) {
@SuppressWarnings("unchecked")
- ArrayList formatters = (ArrayList) commonFormatters.clone();
-
- formatters.add(
- ChatFormatter.builder("entireMessage", ENTIRE_MESSAGE_PATTERN).color(colormode).priority(Priority.Low).build());
+ ArrayList formatters = (ArrayList) commonFormatters.clone();
boolean nottest; //Not assigning a default value, so that it can only be used in the if
if ((nottest = Bukkit.getOnlinePlayers().size() > 0) || Bukkit.getVersion().equals("test")) {
- StringBuilder namesb = new StringBuilder("(?i)(");
- boolean addNameFormatter = false; //Needed because some names may be filtered out if they can't see the channel
+ String[] names;
if (nottest)
- for (Player p : Bukkit.getOnlinePlayers()) {
- if (canSee.test(p)) {
- namesb.append(p.getName()).append("|");
- addNameFormatter = true;
- }
- }
+ names = Bukkit.getOnlinePlayers().stream().filter(canSee).map(CommandSender::getName).toArray(String[]::new);
else {
- for (String testPlayer : testPlayers)
- namesb.append(testPlayer).append("|");
- addNameFormatter = true;
+ names = new String[testPlayers.length];
+ System.arraycopy(testPlayers, 0, names, 0, testPlayers.length);
}
- namesb.deleteCharAt(namesb.length() - 1);
- namesb.append(")");
- StringBuilder nicksb = new StringBuilder("(?i)(");
- boolean addNickFormatter = false;
- for (Player p : Bukkit.getOnlinePlayers()) {
- if (!canSee.test(p)) continue;
- final String nick = PlayerListener.nicknames.inverse().get(p.getUniqueId());
- if (nick != null) {
- nicksb.append(nick).append("|");
- addNickFormatter = true;
- }
- }
- nicksb.deleteCharAt(nicksb.length() - 1);
- nicksb.append(")");
+ String[] nicknames = Bukkit.getOnlinePlayers().stream().filter(canSee).map(Player::getUniqueId).map(PlayerListener.nicknames.inverse()::get)
+ .filter(Objects::nonNull).toArray(String[]::new);
Consumer error = message -> {
if (PluginMain.Instance != null)
@@ -318,8 +265,8 @@ public class ChatProcessing {
System.out.println(message);
};
- if (addNameFormatter)
- formatters.add(ChatFormatter.builder("name", Pattern.compile(namesb.toString())).color(Color.Aqua)
+ if (names.length > 0) //Add as first so it handles special characters (_) - though the order of the different types are defined
+ formatters.add(0, new StringMatchProvider("name", FormatSettings.builder().color(Color.Aqua)
.onmatch((match, builder, section) -> {
Player p = Bukkit.getPlayer(match);
Optional pn = nottest ? Optional.empty()
@@ -334,10 +281,10 @@ public class ChatProcessing {
}
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
- }).priority(Priority.High).type(ChatFormatter.Type.Excluder).build());
+ }).build(), true, names));
- if (addNickFormatter)
- formatters.add(ChatFormatter.builder("nickname", Pattern.compile(nicksb.toString())).color(Color.Aqua)
+ if (nicknames.length > 0) //Add as first so it handles special characters
+ formatters.add(0, new StringMatchProvider("nickname", FormatSettings.builder().color(Color.Aqua)
.onmatch((match, builder, section) -> {
if (PlayerListener.nicknames.containsKey(match.toLowerCase())) { //Made a stream and all that but I can actually store it lowercased
Player p = Bukkit.getPlayer(PlayerListener.nicknames.get(match.toLowerCase()));
@@ -352,7 +299,7 @@ public class ChatProcessing {
error.accept("Player nicknamed " + match.toLowerCase()
+ " not found in nickname map but was reported as online.");
return "§c" + match + "§r";
- }).priority(Priority.High).type(ChatFormatter.Type.Excluder).build());
+ }).build(), true, nicknames));
}
return formatters;
}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/FormatterComponent.java b/src/main/java/buttondevteam/chat/components/formatter/FormatterComponent.java
index 49dfded..2f9ec20 100644
--- a/src/main/java/buttondevteam/chat/components/formatter/FormatterComponent.java
+++ b/src/main/java/buttondevteam/chat/components/formatter/FormatterComponent.java
@@ -34,13 +34,6 @@ public class FormatterComponent extends Component {
return getConfig().getData("notificationPitch", 1.0f);
}
- /**
- * The minimum time between messages in milliseconds.
- */
- public ConfigData minTimeBetweenMessages() {
- return getConfig().getData("minTimeBetweenMessages", 100);
- }
-
@Override
protected void enable() {
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java
new file mode 100644
index 0000000..e9f41d2
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatUtils.java
@@ -0,0 +1,31 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public final class ChatFormatUtils {
+ private ChatFormatUtils() {}
+
+ static void sendMessageWithPointer(String str, int... pointer) {
+ DebugCommand.SendDebugMessage(str);
+ StringBuilder sb = new StringBuilder(str.length());
+ Arrays.sort(pointer);
+ for (int i = 0; i < pointer.length; i++) {
+ for (int j = 0; j < pointer[i] - (i > 0 ? pointer[i - 1] + 1 : 0); j++)
+ sb.append(' ');
+ if (pointer[i] == (i > 0 ? pointer[i - 1] : -1))
+ continue;
+ sb.append('^');
+ }
+ DebugCommand.SendDebugMessage(sb.toString());
+ }
+
+ /**
+ * Check if the given start and end position is inside any of the ranges
+ */
+ static boolean isInRange(int start, int end, ArrayList ranges) {
+ return ranges.stream().anyMatch(range -> range[1] >= start && range[0] <= end);
+ }
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java
index 14294f3..bee0798 100644
--- a/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/ChatFormatter.java
@@ -1,79 +1,23 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
-import buttondevteam.chat.components.formatter.ChatProcessing;
-import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.chat.Color;
-import buttondevteam.lib.chat.Priority;
-import lombok.Builder;
-import lombok.Data;
import lombok.val;
import java.util.*;
import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
- * A {@link ChatFormatter} shows what formatting to use based on regular expressions. {@link ChatFormatter#Combine(List, String, TellrawPart, IHaveConfig)} is used to turn it into a {@link TellrawPart}, combining
+ * A {@link MatchProvider} finds where the given {@link FormatSettings} need to be applied. {@link ChatFormatter#Combine(List, String, TellrawPart, IHaveConfig, FormatSettings)}} is used to turn it into a {@link TellrawPart}, combining
* intersecting parts found, for example when {@code _abc*def*ghi_} is said in chat, it'll turn it into an underlined part, then an underlined and italics part, finally an underlined part
* again.
*
* @author NorbiPeti
*/
-@SuppressWarnings("UnusedAssignment")
-@Data
-@Builder
public final class ChatFormatter {
- Pattern regex;
- boolean italic;
- boolean bold;
- boolean underlined;
- boolean strikethrough;
- boolean obfuscated;
- Color color;
- TriFunc onmatch;
- String openlink;
- @Builder.Default
- Priority priority = Priority.Normal;
- @Builder.Default
- short removeCharCount = 0;
- @Builder.Default
- Type type = Type.Normal;
- 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);
- }
-
- private static ChatFormatterBuilder builder() {
- return new ChatFormatterBuilder();
- }
-
- private ConfigData enabled(IHaveConfig config) {
- return config.getData(name + ".enabled", true);
- }
-
- public enum Type {
- Normal,
- /**
- * Matches a start and an end section which gets converted to one section (for example see italics)
- */
- Range,
- /**
- * Exclude matching area from further processing (besides this formatter)
- */
- Excluder
+ private ChatFormatter() {
}
@FunctionalInterface
@@ -81,31 +25,32 @@ public final class ChatFormatter {
R apply(T1 x1, T2 x2, T3 x3);
}
- public static void Combine(List formatters, String str, TellrawPart tp, IHaveConfig config) {
+ //synchronized: Some of the formatters are reused, see createSections(...)
+ public static synchronized void Combine(List formatters, String str, TellrawPart tp, IHaveConfig config, FormatSettings defaults) {
/*
- * This method assumes that there is always a global formatter
+ * A global formatter is no longer needed
*/
header("ChatFormatter.Combine begin");
ArrayList sections = new ArrayList<>();
if (config != null) //null if testing
formatters.removeIf(cf -> !cf.enabled(config).get()); //Remove disabled formatters
- createSections(formatters, str, sections, true);
-
- header("Section creation (excluders done)");
- createSections(formatters, str, sections, false);
- sortSections(sections);
-
+ var excluded = new ArrayList();
/*
* 0: Start - 1: End index
*/
val remchars = new ArrayList();
- header("Range section conversion");
- sections = convertRangeSections(str, sections, remchars);
+ escapeThings(str, excluded, remchars);
- header("Adding remove chars (RC)"); // Important to add after the range section conversion
- addRemChars(sections, remchars, str);
+ sections.add(new FormattedSection(defaults, 0, str.length() - 1, Collections.emptyList())); //Add entire message
+ var providers = formatters.stream().filter(mp -> mp instanceof RegexMatchProvider).collect(Collectors.toList());
+ createSections(providers, str, sections, excluded, remchars);
+ providers = formatters.stream().filter(mp -> mp instanceof StringMatchProvider).collect(Collectors.toList());
+ createSections(providers, str, sections, excluded, remchars);
+ providers = formatters.stream().filter(mp -> mp instanceof RangeMatchProvider).collect(Collectors.toList());
+ createSections(providers, str, sections, excluded, remchars);
+ sortSections(sections);
header("Section combining");
combineSections(str, sections);
@@ -115,145 +60,35 @@ public final class ChatFormatter {
header("ChatFormatter.Combine done");
}
- private static void createSections(List formatters, String str, ArrayList sections,
- boolean excluders) {
- for (ChatFormatter formatter : formatters) {
- if (excluders == (formatter.type != Type.Excluder))
- continue; //If we're looking at excluders and this isn't one, skip - or vica-versa
- Matcher matcher = formatter.regex.matcher(str);
- while (matcher.find()) {
- DebugCommand.SendDebugMessage("Found match from " + matcher.start() + " to " + (matcher.end() - 1));
- DebugCommand.SendDebugMessage("With " + (excluders ? "excluder " : "") + "formatter: " + formatter);
- sendMessageWithPointer(str, matcher.start(), matcher.end() - 1);
- if (formatter.regex != ChatProcessing.ENTIRE_MESSAGE_PATTERN && sections.stream().anyMatch(fs -> fs.type == Type.Excluder && (fs.End >= matcher.start() && fs.Start <= matcher.end() - 1))) {
- DebugCommand.SendDebugMessage("Ignoring formatter because of an excluder");
- continue; //Exclude areas matched by excluders - Range sections are correctly handled afterwards
- }
- ArrayList groups = new ArrayList<>();
- for (int i = 0; i < matcher.groupCount(); i++)
- groups.add(matcher.group(i + 1));
- if (groups.size() > 0)
- DebugCommand.SendDebugMessage("First group: " + groups.get(0));
- FormattedSection section = new FormattedSection(formatter, matcher.start(), matcher.end() - 1, groups,
- formatter.type);
- sections.add(section);
+ private static void escapeThings(String str, ArrayList ignoredAreas, ArrayList remchars) {
+ boolean escaped = false;
+ for (int i = 0; i < str.length(); i++) {
+ if (str.charAt(i) == '\\') {
+ remchars.add(new int[]{i, i});
+ ignoredAreas.add(new int[]{i + 1, i + 1});
+ i++; //Ignore a potential second slash
}
}
}
- private static void newCombine(String str, ArrayList sections, ArrayList remchars) {
- var stack = new Stack();
- for (int i = 0; i < str.length(); i++) {
- for (Iterator iterator = sections.iterator(); iterator.hasNext(); ) {
- FormattedSection section = iterator.next();
- if (section.Start <= i) {
- stack.push(section);
+ private static void createSections(List formatters, String str, ArrayList sections,
+ ArrayList excludedAreas, ArrayList removedCharacters) {
+ formatters.forEach(MatchProviderBase::reset); //Reset state information, as we aren't doing deep cloning
+ while (formatters.size() > 0) {
+ for (var iterator = formatters.iterator(); iterator.hasNext(); ) {
+ MatchProviderBase formatter = iterator.next();
+ DebugCommand.SendDebugMessage("Checking provider: " + formatter);
+ var sect = formatter.getNextSection(str, excludedAreas, removedCharacters);
+ if (sect != null) //Not excluding the area here because the range matcher shouldn't take it all
+ sections.add(sect);
+ if (formatter.isFinished()) {
+ DebugCommand.SendDebugMessage("Provider finished");
iterator.remove();
}
}
}
}
- private static ArrayList convertRangeSections(String str, ArrayList sections, ArrayList remchars) {
- ArrayList combined = new ArrayList<>();
- Map 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) {
- escaped = section.Formatters.contains(ChatProcessing.ESCAPE_FORMATTER) && !escaped; // Enable escaping on first \, disable on second
- if (escaped) {// Don't add the escape character
- remchars.add(new int[]{section.Start, section.Start});
- DebugCommand.SendDebugMessage("Found escaper section: " + section);
- } else {
- combined.add(section); // The above will delete the \
- DebugCommand.SendDebugMessage("Added section: " + section);
- }
- sendMessageWithPointer(str, section.Start, section.End);
- 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 (takenByBigGuy || formatter.removeCharCount < takenEnd - takenStart) {
- DebugCommand.SendDebugMessage("Lose: " + section);
- sendMessageWithPointer(str, section.Start, section.End);
- DebugCommand.SendDebugMessage("And win: " + takenFormatter);
- continue; // The current section loses
- }
- nextSection.remove(takenFormatter); // The current section wins
- DebugCommand.SendDebugMessage("Win: " + section);
- 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 + 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(formatter, section);
- takenByBigGuy = false;
- }
- DebugCommand
- .SendDebugMessage("New area taken: (" + takenStart + "-" + takenEnd + ") " + takenFormatter);
- sendMessageWithPointer(str, takenStart, takenEnd);
- } else {
- DebugCommand.SendDebugMessage("Skipping section: " + section); // This will keep the text (character)
- sendMessageWithPointer(str, section.Start, section.End);
- escaped = false; // Reset escaping if applied, like if we're at the '*' in '\*'
- }
- }
- //Do not finish unfinished sections, ignore them
- sections = combined;
- return sections;
- }
-
- private static void addRemChars(ArrayList sections, ArrayList 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}))
- .forEach(remchars::add);
- sections.stream()
- .flatMap(fs -> fs.Formatters.stream().filter(cf -> cf.removeCharCount > 0)
- .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("; ")));
- sendMessageWithPointer(str,
- remchars.stream().flatMapToInt(Arrays::stream).toArray());
- }
-
private static void combineSections(String str, ArrayList sections) {
for (int i = 1; i < sections.size(); i++) {
DebugCommand.SendDebugMessage("i: " + i);
@@ -262,7 +97,8 @@ public final class ChatFormatter {
{
FormattedSection firstSect = sections.get(i - 1);
FormattedSection lastSect = sections.get(i);
- if (firstSect.Start > lastSect.Start) { //The first can't start later
+ if (firstSect.Start > lastSect.Start //The first can't start later
+ || (firstSect.Start == lastSect.Start && firstSect.End < lastSect.End)) {
var section = firstSect;
firstSect = lastSect;
lastSect = section;
@@ -271,41 +107,40 @@ public final class ChatFormatter {
lastSection = lastSect;
}
DebugCommand.SendDebugMessage("Combining sections " + firstSection);
- sendMessageWithPointer(str, firstSection.Start, firstSection.End);
+ ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
DebugCommand.SendDebugMessage(" and " + lastSection);
- sendMessageWithPointer(str, lastSection.Start, lastSection.End);
+ ChatFormatUtils.sendMessageWithPointer(str, lastSection.Start, lastSection.End);
if (firstSection.Start == lastSection.Start && firstSection.End == lastSection.End) {
- firstSection.Formatters.addAll(lastSection.Formatters);
+ firstSection.Settings.copyFrom(lastSection.Settings);
firstSection.Matches.addAll(lastSection.Matches);
- firstSection.type = lastSection.type;
DebugCommand.SendDebugMessage("To section " + firstSection);
- sendMessageWithPointer(str, firstSection.Start, firstSection.End);
+ ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
sections.remove(i);
i = 0;
sortSections(sections);
continue;
- } else if (firstSection.End > lastSection.Start && firstSection.Start < lastSection.End) {
- int origend2 = firstSection.End;
+ } else if (firstSection.End >= lastSection.Start && firstSection.Start <= lastSection.End) {
+ int firstSectEnd = firstSection.End;
firstSection.End = lastSection.Start - 1;
- int origend = lastSection.End;
- FormattedSection section = new FormattedSection(firstSection.Formatters, lastSection.Start, origend,
- firstSection.Matches, Type.Normal);
- section.Formatters.addAll(lastSection.Formatters);
- section.Matches.addAll(lastSection.Matches); // TODO: Clean
+ int lastSectEnd = lastSection.End;
+ FormattedSection section = new FormattedSection(firstSection.Settings, lastSection.Start, lastSectEnd,
+ firstSection.Matches);
+ section.Settings.copyFrom(lastSection.Settings);
+ section.Matches.addAll(lastSection.Matches);
sections.add(i, section);
- // Use the properties of the first section not the second one
- lastSection.Formatters.clear();
- lastSection.Formatters.addAll(firstSection.Formatters);
- lastSection.Matches.clear();
- lastSection.Matches.addAll(firstSection.Matches);
- lastSection.Start = origend + 1;
- lastSection.End = origend2;
+ if (firstSectEnd > lastSection.End) { //Copy first section info to last as the lastSection initially cuts the firstSection in half
+ lastSection.Settings = FormatSettings.builder().build();
+ lastSection.Settings.copyFrom(firstSection.Settings);
+ }
+
+ lastSection.Start = lastSectEnd + 1;
+ lastSection.End = firstSectEnd;
Predicate removeIfNeeded = s -> {
if (s.Start < 0 || s.End < 0 || s.Start > s.End) {
DebugCommand.SendDebugMessage(" Removed: " + s);
- sendMessageWithPointer(str, s.Start, s.End);
+ ChatFormatUtils.sendMessageWithPointer(str, s.Start, s.End);
sections.remove(s);
return true;
}
@@ -315,15 +150,15 @@ public final class ChatFormatter {
DebugCommand.SendDebugMessage("To sections");
if (!removeIfNeeded.test(firstSection)) {
DebugCommand.SendDebugMessage(" 1:" + firstSection + "");
- sendMessageWithPointer(str, firstSection.Start, firstSection.End);
+ ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
}
if (!removeIfNeeded.test(section)) {
DebugCommand.SendDebugMessage(" 2:" + section + "");
- sendMessageWithPointer(str, section.Start, section.End);
+ ChatFormatUtils.sendMessageWithPointer(str, section.Start, section.End);
}
if (!removeIfNeeded.test(lastSection)) {
DebugCommand.SendDebugMessage(" 3:" + lastSection);
- sendMessageWithPointer(str, lastSection.Start, lastSection.End);
+ ChatFormatUtils.sendMessageWithPointer(str, lastSection.Start, lastSection.End);
}
i = 0;
}
@@ -332,7 +167,7 @@ public final class ChatFormatter {
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);
+ ChatFormatUtils.sendMessageWithPointer(str, sections.get(j).Start, sections.get(j).End);
sections.remove(j);
j--;
i = 0;
@@ -349,7 +184,7 @@ public final class ChatFormatter {
String originaltext;
int start = section.Start, end = section.End;
DebugCommand.SendDebugMessage("Start: " + start + " - End: " + end);
- sendMessageWithPointer(str, start, end);
+ ChatFormatUtils.sendMessageWithPointer(str, start, end);
/*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());*/
@@ -377,29 +212,28 @@ public final class ChatFormatter {
}
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
+ //section.Formatters.sort(Comparator.comparing(cf2 -> cf2.priority.GetValue())); //Apply the highest last, to overwrite previous ones
TellrawPart newtp = new TellrawPart("");
- for (ChatFormatter formatter : section.Formatters) {
- DebugCommand.SendDebugMessage("Applying formatter: " + formatter);
- if (formatter.onmatch != null)
- originaltext = formatter.onmatch.apply(originaltext, formatter, section);
- if (formatter.color != null)
- newtp.setColor(formatter.color);
- if (formatter.bold)
- newtp.setBold(true);
- if (formatter.italic)
- newtp.setItalic(true);
- if (formatter.underlined)
- newtp.setUnderlined(true);
- if (formatter.strikethrough)
- newtp.setStrikethrough(true);
- if (formatter.obfuscated)
- newtp.setObfuscated(true);
- if (formatter.openlink != null)
- openlink = formatter.openlink;
- if (formatter.hoverText != null)
- newtp.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, formatter.hoverText));
- }
+ var settings = section.Settings;
+ DebugCommand.SendDebugMessage("Applying settings: " + settings);
+ if (settings.onmatch != null)
+ originaltext = settings.onmatch.apply(originaltext, settings, section);
+ if (settings.color != null)
+ newtp.setColor(settings.color);
+ if (settings.bold)
+ newtp.setBold(true);
+ if (settings.italic)
+ newtp.setItalic(true);
+ if (settings.underlined)
+ newtp.setUnderlined(true);
+ if (settings.strikethrough)
+ newtp.setStrikethrough(true);
+ if (settings.obfuscated)
+ newtp.setObfuscated(true);
+ if (settings.openlink != null)
+ openlink = settings.openlink;
+ if (settings.hoverText != null)
+ newtp.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, settings.hoverText));
if (lasttp != null && newtp.getColor() == lasttp.getColor()
&& newtp.isBold() == lasttp.isBold()
&& newtp.isItalic() == lasttp.isItalic()
@@ -427,25 +261,10 @@ public final class ChatFormatter {
private static void sortSections(ArrayList sections) {
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(s1.End, s2.End)
+ ? s1.End == s2.End ? 0 : Integer.compare(s1.End, s2.End)
: Integer.compare(s1.Start, s2.Start));
}
- private static void sendMessageWithPointer(String str, int... pointer) {
- DebugCommand.SendDebugMessage(str);
- StringBuilder sb = new StringBuilder(str.length());
- Arrays.sort(pointer);
- for (int i = 0; i < pointer.length; i++) {
- for (int j = 0; j < pointer[i] - (i > 0 ? pointer[i - 1] + 1 : 0); j++)
- sb.append(' ');
- if (pointer[i] == (i > 0 ? pointer[i - 1] : -1))
- continue;
- sb.append('^');
- }
- DebugCommand.SendDebugMessage(sb.toString());
- }
-
private static void header(String message) {
DebugCommand.SendDebugMessage("\n--------\n" + message + "\n--------\n");
}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java
new file mode 100644
index 0000000..e0bfa4c
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormatSettings.java
@@ -0,0 +1,38 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import buttondevteam.lib.chat.Color;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * Describes how a matched section of the message should look. May be combined with other format settings.
+ */
+@Data
+@Builder
+public class FormatSettings {
+ boolean italic;
+ boolean bold;
+ boolean underlined;
+ boolean strikethrough;
+ boolean obfuscated;
+ Color color;
+ ChatFormatter.TriFunc onmatch;
+ String openlink;
+ String hoverText;
+
+ public void copyFrom(FormatSettings settings) {
+ try {
+ for (var field : FormatSettings.class.getDeclaredFields()) {
+ if (field.getType() == boolean.class) {
+ if (field.getBoolean(settings))
+ field.setBoolean(this, true); //Set to true if either of them are true
+ } else if (field.get(settings) != null) {
+ //System.out.println("Setting " + field.getType() + " " + field.getName() + " from " + field.get(this) + " to " + field.get(settings));
+ field.set(this, field.get(settings));
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java
index c609e58..eab4575 100644
--- a/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/FormattedSection.java
@@ -1,36 +1,22 @@
package buttondevteam.chat.components.formatter.formatting;
-import java.util.ArrayList;
-import java.util.Collection;
+import lombok.ToString;
+import java.util.ArrayList;
+import java.util.List;
+
+@ToString
public class FormattedSection {
public int Start;
public int End;
- public ArrayList Formatters = new ArrayList();
- public ArrayList Matches = new ArrayList();
- public ChatFormatter.Type type;
+ public FormatSettings Settings;
+ public List Matches = new ArrayList<>();
- FormattedSection(ChatFormatter formatter, int start, int end, ArrayList matches, ChatFormatter.Type type) {
+ FormattedSection(FormatSettings settings, int start, int end, List matches) {
Start = start;
End = end;
- Formatters.add(formatter);
+ Settings = FormatSettings.builder().build();
+ Settings.copyFrom(settings);
Matches.addAll(matches);
- this.type = type;
- }
-
- FormattedSection(Collection formatters, int start, int end, ArrayList matches,
- ChatFormatter.Type type) {
- Start = start;
- End = end;
- Formatters.addAll(formatters);
- Matches.addAll(matches);
- this.type = type;
- }
-
- @Override
- public String toString() {
- return "Section(" + Start + ", " + End + ", formatters: " +
- Formatters.toString() + ", matches: " + Matches.toString() + ", " +
- type + ")";
}
}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java
new file mode 100644
index 0000000..c1d2d46
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProvider.java
@@ -0,0 +1,21 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+
+/**
+ * Attempts to find a match for the provided message, returning null if none was found.
+ */
+public interface MatchProvider {
+ @Nullable
+ FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters);
+
+ boolean isFinished();
+
+ String getName();
+
+ @Override
+ String toString();
+
+ void reset();
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java
new file mode 100644
index 0000000..fd25901
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/MatchProviderBase.java
@@ -0,0 +1,38 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import buttondevteam.lib.architecture.ConfigData;
+import buttondevteam.lib.architecture.IHaveConfig;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+
+@RequiredArgsConstructor
+public abstract class MatchProviderBase implements MatchProvider {
+ @Getter
+ protected boolean finished;
+ @Getter
+ private final String name;
+
+ @Nullable
+ @Override
+ public abstract FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters);
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ protected abstract void resetSubclass();
+
+ public void reset() {
+ finished = false;
+ resetSubclass();
+ }
+
+ ConfigData enabled(IHaveConfig config) {
+ return config.getData(name + ".enabled", true);
+ }
+
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java
new file mode 100644
index 0000000..aa872cb
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/RangeMatchProvider.java
@@ -0,0 +1,61 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
+import lombok.ToString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class RangeMatchProvider extends MatchProviderBase {
+ private final String pattern;
+ @ToString.Exclude
+ private final FormatSettings settings;
+ private int nextIndex = 0;
+ private FormattedSection startedSection;
+
+ public RangeMatchProvider(String name, String pattern, FormatSettings settings) {
+ super(name);
+ this.pattern = pattern;
+ this.settings = settings;
+ }
+
+ @Override
+ public FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters) {
+ int i, len;
+ i = message.indexOf(pattern, nextIndex);
+ len = pattern.length();
+ nextIndex = i + len; //Set for the next method call
+ if (i == -1) {
+ finished = true; //Won't find any more - unfinished sections will be garbage collected
+ return null;
+ }
+ if (ChatFormatUtils.isInRange(i, i + len - 1, ignoredAreas)) {
+ DebugCommand.SendDebugMessage("Range start is in ignored area, skipping");
+ return null; //Not setting finished to true, so it will go to the next match
+ }
+ ignoredAreas.add(new int[]{i, i + len - 1});
+ if (startedSection == null) {
+ DebugCommand.SendDebugMessage("Started range match from " + i + " to " + (i + len - 1));
+ DebugCommand.SendDebugMessage("With settings: " + settings);
+ ChatFormatUtils.sendMessageWithPointer(message, i, i + len - 1);
+ startedSection = new FormattedSection(settings, i, i + len - 1, Collections.emptyList());
+ return null;
+ } else {
+ var section = startedSection;
+ DebugCommand.SendDebugMessage("Finished range match from " + section.Start + " to " + (i + len - 1));
+ DebugCommand.SendDebugMessage("With settings: " + settings);
+ ChatFormatUtils.sendMessageWithPointer(message, section.Start, i + len - 1);
+ section.End = i + len - 1;
+ removedCharacters.add(new int[]{section.Start, section.Start + len - 1});
+ removedCharacters.add(new int[]{i, i + len - 1});
+ startedSection = null; //Reset so next find creates a new one
+ return section;
+ }
+ }
+
+ @Override
+ public void resetSubclass() {
+ nextIndex = 0;
+ startedSection = null;
+ }
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java
new file mode 100644
index 0000000..c64ddc9
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/RegexMatchProvider.java
@@ -0,0 +1,53 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
+import lombok.ToString;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexMatchProvider extends MatchProviderBase {
+ private final Pattern pattern;
+ @ToString.Exclude
+ private final FormatSettings settings;
+ private Matcher matcher;
+
+ public RegexMatchProvider(String name, Pattern pattern, FormatSettings settings) {
+ super(name);
+ this.pattern = pattern;
+ this.settings = settings;
+ }
+
+ @Nullable
+ @Override
+ public FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters) {
+ if (matcher == null)
+ matcher = pattern.matcher(message);
+ if (!matcher.find()) {
+ finished = true;
+ return null;
+ }
+ int start = matcher.start(), end = matcher.end() - 1;
+ DebugCommand.SendDebugMessage("Found regex match from " + start + " to " + end);
+ DebugCommand.SendDebugMessage("With settings: " + settings);
+ ChatFormatUtils.sendMessageWithPointer(message, start, end);
+ if (ChatFormatUtils.isInRange(start, end, ignoredAreas)) {
+ DebugCommand.SendDebugMessage("Match is in ignored area, skipping");
+ return null; //Not setting finished to true, so it will go to the next match
+ }
+ ArrayList groups = new ArrayList<>();
+ for (int i = 0; i < matcher.groupCount(); i++)
+ groups.add(matcher.group(i + 1));
+ if (groups.size() > 0)
+ DebugCommand.SendDebugMessage("First group: " + groups.get(0));
+ ignoredAreas.add(new int[]{start, end});
+ return new FormattedSection(settings, matcher.start(), matcher.end() - 1, groups);
+ }
+
+ @Override
+ public void resetSubclass() {
+ matcher = null;
+ }
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java
new file mode 100644
index 0000000..a9a1184
--- /dev/null
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/StringMatchProvider.java
@@ -0,0 +1,66 @@
+package buttondevteam.chat.components.formatter.formatting;
+
+import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
+import lombok.ToString;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class StringMatchProvider extends MatchProviderBase {
+ private final String[] strings;
+ @ToString.Exclude
+ private final FormatSettings settings;
+ private int nextIndex = 0;
+ private boolean ignoreCase;
+
+ /**
+ * Matches the given strings in the order given
+ *
+ * @param settings The format settings
+ * @param strings The strings to match in the correct order
+ */
+ public StringMatchProvider(String name, FormatSettings settings, boolean ignoreCase, String... strings) {
+ super(name);
+ this.settings = settings;
+ this.strings = strings;
+ this.ignoreCase = ignoreCase;
+ if (ignoreCase) {
+ for (int i = 0; i < strings.length; i++) {
+ strings[i] = strings[i].toLowerCase();
+ }
+ }
+ }
+
+ @Nullable
+ @Override
+ public FormattedSection getNextSection(String message, ArrayList ignoredAreas, ArrayList removedCharacters) {
+ if (ignoreCase)
+ message = message.toLowerCase();
+ int i = -1, len = 0;
+ for (String string : strings) {
+ i = message.indexOf(string, nextIndex);
+ len = string.length();
+ if (i != -1) break;
+ }
+ if (i == -1) {
+ finished = true; //Won't find any more
+ return null;
+ }
+ nextIndex = i + len;
+ if (ChatFormatUtils.isInRange(i, i + len - 1, ignoredAreas)) {
+ DebugCommand.SendDebugMessage("String is in ignored area, skipping");
+ return null; //Not setting finished to true, so it will go to the next match
+ }
+ DebugCommand.SendDebugMessage("Found string match from " + i + " to " + (i + len - 1));
+ DebugCommand.SendDebugMessage("With settings: " + settings);
+ ChatFormatUtils.sendMessageWithPointer(message, i, i + len - 1);
+ ignoredAreas.add(new int[]{i, i + len - 1});
+ return new FormattedSection(settings, i, i + len - 1, Collections.emptyList());
+ }
+
+ @Override
+ public void resetSubclass() {
+ nextIndex = 0;
+ }
+}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawEvent.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawEvent.java
index 4c532e5..53fe988 100644
--- a/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawEvent.java
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawEvent.java
@@ -71,6 +71,6 @@ public final class TellrawEvent implements Serial
}
}
- public static interface Action extends TellrawSerializableEnum {
+ public interface Action extends TellrawSerializableEnum {
}
}
diff --git a/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawSerializer.java b/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawSerializer.java
index b1a36fd..4f9c450 100644
--- a/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawSerializer.java
+++ b/src/main/java/buttondevteam/chat/components/formatter/formatting/TellrawSerializer.java
@@ -48,6 +48,7 @@ public abstract class TellrawSerializer {
throw new UnsupportedOperationException();
}
+ @SuppressWarnings("ConstantConditions")
@Override
public void write(JsonWriter writer, Boolean val) throws IOException {
if (val)
diff --git a/src/main/java/buttondevteam/chat/components/towncolors/TownColorCommand.java b/src/main/java/buttondevteam/chat/components/towncolors/TownColorCommand.java
index 0bfdcd6..8dd911c 100644
--- a/src/main/java/buttondevteam/chat/components/towncolors/TownColorCommand.java
+++ b/src/main/java/buttondevteam/chat/components/towncolors/TownColorCommand.java
@@ -12,8 +12,6 @@ import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.entity.Player;
-import java.lang.reflect.Method;
-
@CommandClass(helpText = {
"Town Color", //
"This command allows setting a color for a town.", //
@@ -23,18 +21,6 @@ import java.lang.reflect.Method;
@RequiredArgsConstructor
public class TownColorCommand extends UCommandBase {
private final TownColorComponent component;
- @Override
- public String[] getHelpText(Method method, Command2.Subcommand ann) {
- StringBuilder cns = new StringBuilder(" ");
- for (int i = 2; i <= component.colorCount().get(); i++)
- cns.append(" [colorname").append(i).append("]");
- return new String[] { //
- "§6---- Town Color ----", //
- "This command allows setting color(s) for a town.", //
- "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.", //
- };
- }
@Command2.Subcommand
public boolean def(Player player, String... colornames) {
diff --git a/src/main/java/buttondevteam/chat/components/towncolors/TownColorComponent.java b/src/main/java/buttondevteam/chat/components/towncolors/TownColorComponent.java
index d6d0886..7c50ebd 100644
--- a/src/main/java/buttondevteam/chat/components/towncolors/TownColorComponent.java
+++ b/src/main/java/buttondevteam/chat/components/towncolors/TownColorComponent.java
@@ -19,14 +19,12 @@ import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
-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 java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
@@ -81,14 +79,10 @@ public class TownColorComponent extends Component implements Listene
var cs = getConfig().getConfig().getConfigurationSection("towncolors");
if (cs != null)
loadTC.accept(cs);
- else
- load_old(loadTC, null); //Load old data
if (usenc) {
var ncs = getConfig().getConfig().getConfigurationSection("nationcolors");
if (ncs != null)
loadNC.accept(ncs);
- else
- load_old(null, loadNC); //Why not choose by making different args null
}
TownColors.keySet().removeIf(t -> !TownyComponent.TU.getTownsMap().containsKey(t)); // Removes town colors for deleted/renamed towns
@@ -227,25 +221,4 @@ public class TownColorComponent extends Component implements Listene
public void onPlayerJoin(TBMCPlayerJoinEvent event) {
updatePlayerColors(event.getPlayer(), event.GetPlayer().asPluginPlayer(ChatPlayer.class));
}
-
- private static void load_old(Consumer loadTC,
- Consumer loadNC) {
- PluginMain.Instance.getLogger().info("Loading files...");
- try {
- File file = new File("TBMC/chatsettings.yml");
- if (file.exists()) {
- YamlConfiguration yc = new YamlConfiguration();
- yc.load(file);
- ConfigurationSection cs;
- if (loadTC != null && (cs = yc.getConfigurationSection("towncolors")) != null)
- loadTC.accept(cs);
- if (loadNC != null && (cs = yc.getConfigurationSection("nationcolors")) != null)
- loadNC.accept(cs);
- PluginMain.Instance.getLogger().info("Loaded files!");
- } else
- PluginMain.Instance.getLogger().info("No files to load, first run probably.");
- } catch (Exception e) {
- TBMCCoreAPI.SendException("Error while loading chat files!", e);
- }
- }
}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 20f270a..5eefc33 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,6 +1,6 @@
name: Chroma-Chat
main: buttondevteam.chat.PluginMain
-version: 4.0
+version: '4.0'
commands:
u:
description: Auto-flair system. Accept or ignore flair.
diff --git a/src/test/java/buttondevteam/chat/ObjectTestRunner.java b/src/test/java/buttondevteam/chat/ObjectTestRunner.java
index 25785ee..1b5a034 100644
--- a/src/test/java/buttondevteam/chat/ObjectTestRunner.java
+++ b/src/test/java/buttondevteam/chat/ObjectTestRunner.java
@@ -1,15 +1,5 @@
package buttondevteam.chat;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
@@ -20,6 +10,12 @@ import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
+import java.lang.annotation.*;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Based on {@link Parameterized}
*
@@ -32,10 +28,10 @@ public class ObjectTestRunner extends Suite {
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
- public static @interface Objects {
+ public @interface Objects {
}
- private class TestClassRunnerForObjects extends BlockJUnit4ClassRunner {
+ private static class TestClassRunnerForObjects extends BlockJUnit4ClassRunner {
private List objectList;
private int fParameterSetNumber;
@@ -77,13 +73,13 @@ public class ObjectTestRunner extends Suite {
}
}
- private final ArrayList runners = new ArrayList();
+ private final ArrayList runners = new ArrayList<>();
/**
* Only called reflectively. Do not use programmatically.
*/
public ObjectTestRunner(Class> klass) throws Throwable {
- super(klass, Collections.emptyList());
+ super(klass, Collections.emptyList());
List objectsList = getObjectsList(getTestClass());
for (int i = 0; i < objectsList.size(); i++)
runners.add(new TestClassRunnerForObjects(getTestClass().getJavaClass(), objectsList, i));
diff --git a/src/test/java/buttondevteam/chat/components/formatter/ChatFormatIT.java b/src/test/java/buttondevteam/chat/components/formatter/ChatFormatIT.java
index 8b39810..7d2d250 100644
--- a/src/test/java/buttondevteam/chat/components/formatter/ChatFormatIT.java
+++ b/src/test/java/buttondevteam/chat/components/formatter/ChatFormatIT.java
@@ -5,11 +5,9 @@ import buttondevteam.chat.ObjectTestRunner;
import buttondevteam.chat.ObjectTestRunner.Objects;
import buttondevteam.chat.PluginMain;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
-import buttondevteam.chat.components.formatter.formatting.ChatFormatter;
-import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
+import buttondevteam.chat.components.formatter.formatting.*;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent.ClickAction;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent.HoverAction;
-import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.TestPrepare;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.Color;
@@ -32,7 +30,7 @@ public class ChatFormatIT {
DebugCommand.DebugMode = true;
PluginMain.permission = Mockito.mock(Permission.class);
- List list = new ArrayList();
+ List list = new ArrayList<>();
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)));
@@ -64,7 +62,7 @@ public class ChatFormatIT {
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)));
+ .setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/")).setColor(Color.White), new TellrawPart(" heh").setItalic(true).setColor(Color.White)));
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)));
@@ -109,12 +107,12 @@ public class ChatFormatIT {
@Test
public void testMessage() {
- ArrayList cfs = ChatProcessing.addFormatters(Color.White, p -> true, null);
+ ArrayList cfs = ChatProcessing.addFormatters(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);
+ ChatFormatter.Combine(cfs, message, tp, null, FormatSettings.builder().color(Color.White).build());
System.out.println("Testing: " + message);
// System.out.println(ChatProcessing.toJson(tp));
final TellrawPart expectedtp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatUtils.MCORIGIN);