Refactored and fixed chat formatting, deploy to Releases #116

Merged
NorbiPeti merged 10 commits from dev into master 2020-03-04 22:13:07 +00:00
26 changed files with 751 additions and 763 deletions

445
.gitignore vendored
View file

@ -1,224 +1,221 @@
################# #################
## Eclipse ## Eclipse
################# #################
*.pydevproject *.pydevproject
.metadata/ .metadata/
bin/ bin/
tmp/ tmp/
*.tmp *.tmp
*.bak *.bak
*.swp *.swp
*~.nib *~.nib
local.properties local.properties
.classpath .classpath
.settings/ .settings/
.loadpath .loadpath
target/ target/
.project .project
# External tool builders # External tool builders
.externalToolBuilders/ .externalToolBuilders/
# Locally stored "Eclipse launch configurations" # Locally stored "Eclipse launch configurations"
*.launch *.launch
# CDT-specific # CDT-specific
.cproject .cproject
# PDT-specific # PDT-specific
.buildpath .buildpath
################# #################
## Visual Studio ## Visual Studio
################# #################
## Ignore Visual Studio temporary files, build results, and ## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons. ## files generated by popular Visual Studio add-ons.
# User-specific files # User-specific files
*.suo *.suo
*.user *.user
*.sln.docstates *.sln.docstates
# Build results # Build results
[Dd]ebug/ [Dd]ebug/
[Rr]elease/ [Rr]elease/
x64/ x64/
build/ build/
[Bb]in/ [Bb]in/
[Oo]bj/ [Oo]bj/
# MSTest test Results # MSTest test Results
[Tt]est[Rr]esult*/ [Tt]est[Rr]esult*/
[Bb]uild[Ll]og.* [Bb]uild[Ll]og.*
*_i.c *_i.c
*_p.c *_p.c
*.ilk *.ilk
*.meta *.meta
*.obj *.obj
*.pch *.pch
*.pdb *.pdb
*.pgc *.pgc
*.pgd *.pgd
*.rsp *.rsp
*.sbr *.sbr
*.tlb *.tlb
*.tli *.tli
*.tlh *.tlh
*.tmp *.tmp_proj
*.tmp_proj *.log
*.log *.vspscc
*.vspscc *.vssscc
*.vssscc .builds
.builds *.pidb
*.pidb *.scc
*.log
*.scc # Visual C++ cache files
ipch/
# Visual C++ cache files *.aps
ipch/ *.ncb
*.aps *.opensdf
*.ncb *.sdf
*.opensdf *.cachefile
*.sdf
*.cachefile # Visual Studio profiler
*.psess
# Visual Studio profiler *.vsp
*.psess *.vspx
*.vsp
*.vspx # Guidance Automation Toolkit
*.gpState
# Guidance Automation Toolkit
*.gpState # ReSharper is a .NET coding add-in
_ReSharper*/
# ReSharper is a .NET coding add-in *.[Rr]e[Ss]harper
_ReSharper*/
*.[Rr]e[Ss]harper # TeamCity is a build add-in
_TeamCity*
# TeamCity is a build add-in
_TeamCity* # DotCover is a Code Coverage Tool
*.dotCover
# DotCover is a Code Coverage Tool
*.dotCover # NCrunch
*.ncrunch*
# NCrunch .*crunch*.local.xml
*.ncrunch*
.*crunch*.local.xml # Installshield output folder
[Ee]xpress/
# Installshield output folder
[Ee]xpress/ # DocProject is a documentation generator add-in
DocProject/buildhelp/
# DocProject is a documentation generator add-in DocProject/Help/*.HxT
DocProject/buildhelp/ DocProject/Help/*.HxC
DocProject/Help/*.HxT DocProject/Help/*.hhc
DocProject/Help/*.HxC DocProject/Help/*.hhk
DocProject/Help/*.hhc DocProject/Help/*.hhp
DocProject/Help/*.hhk DocProject/Help/Html2
DocProject/Help/*.hhp DocProject/Help/html
DocProject/Help/Html2
DocProject/Help/html # Click-Once directory
publish/
# Click-Once directory
publish/ # Publish Web Output
*.Publish.xml
# Publish Web Output *.pubxml
*.Publish.xml *.publishproj
*.pubxml
*.publishproj # NuGet Packages Directory
## TO!DO: If you have NuGet Package Restore enabled, uncomment the next line
# NuGet Packages Directory #packages/
## TO!DO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/ # Windows Azure Build Output
csx
# Windows Azure Build Output *.build.csdef
csx
*.build.csdef # Windows Store app package directory
AppPackages/
# Windows Store app package directory
AppPackages/ # Others
sql/
# Others *.Cache
sql/ ClientBin/
*.Cache [Ss]tyle[Cc]op.*
ClientBin/ ~$*
[Ss]tyle[Cc]op.* *~
~$* *.dbmdl
*~ *.[Pp]ublish.xml
*.dbmdl *.pfx
*.[Pp]ublish.xml *.publishsettings
*.pfx
*.publishsettings # RIA/Silverlight projects
Generated_Code/
# 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 ;-)
# Backup & report files from converting an old project file to a newer _UpgradeReport_Files/
# Visual Studio version. Backup files are not needed, because we have git ;-) Backup*/
_UpgradeReport_Files/ UpgradeLog*.XML
Backup*/ UpgradeLog*.htm
UpgradeLog*.XML
UpgradeLog*.htm # SQL Server files
App_Data/*.mdf
# SQL Server files App_Data/*.ldf
App_Data/*.mdf
App_Data/*.ldf #############
## Windows detritus
############# #############
## Windows detritus
############# # Windows image file caches
Thumbs.db
# Windows image file caches ehthumbs.db
Thumbs.db
ehthumbs.db # Folder config file
Desktop.ini
# Folder config file
Desktop.ini # Recycle Bin used on file shares
$RECYCLE.BIN/
# Recycle Bin used on file shares
$RECYCLE.BIN/ # Mac crap
.DS_Store
# Mac crap
.DS_Store
#############
## Python
############# #############
## Python
############# *.py[cod]
*.py[cod] # Packages
*.egg
# Packages *.egg-info
*.egg dist/
*.egg-info eggs/
dist/ parts/
build/ var/
eggs/ sdist/
parts/ develop-eggs/
var/ .installed.cfg
sdist/
develop-eggs/ # Installer logs
.installed.cfg pip-log.txt
# Installer logs # Unit test / coverage reports
pip-log.txt .coverage
.tox
# Unit test / coverage reports
.coverage #Translations
.tox *.mo
#Translations #Mr Developer
*.mo .mr.developer.cfg
.metadata/*
#Mr Developer TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
.mr.developer.cfg *.iml
.metadata/* *.name
TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar .idea/compiler.xml
#*.iml *.xml
*.name /.apt_generated/
.idea/compiler.xml
*.xml
/.apt_generated/

View file

@ -1,54 +0,0 @@
<?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_11">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/annotations" isTestSource="false" generated="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:21.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.19" level="project" />
<orderEntry type="library" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.github.TBMCPlugins.ButtonCore:ButtonCore:master-d48a2d17d3-1" level="project" />
<orderEntry type="library" name="Maven: net.sourceforge.htmlcleaner:htmlcleaner:2.16" level="project" />
<orderEntry type="library" name="Maven: org.jdom:jdom2:2.0.5" level="project" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.19.0-GA" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:EssentialsX:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: io.papermc:paperlib:1.0.2" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:NMSProvider:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:UpdatedMetaProvider:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:1_8_R1Provider:2.17.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: net.ess3:1_8_R2Provider:2.17.1" level="project" />
<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.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" />
<orderEntry type="library" name="Maven: org.anjocaido:EssentialsGroupManager:2.10.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-core:2.8.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.logging.log4j:log4j-api:2.8.1" level="project" />
</component>
</module>

View file

@ -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 # 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*$" java -jar BuildTools.jar --rev 1.12.2 | grep -vE "[^/ ]*/[^/ ]*\s*KB\s*$" | grep -v "^\s*$"
fi fi
cp ci/settings.xml $HOME/.m2/
language: java language: java
jdk: jdk:
- oraclejdk8 - oraclejdk8
sudo: true sudo: true
dist: trusty # Needed for Java 8, although we might not need Java 8 dist: trusty # Needed for Java 8, although we might not need Java 8
deploy: deploy:
# deploy develop to the staging environment - provider: releases
- provider: script api_key:
script: chmod +x deploy.sh && sh deploy.sh staging 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: on:
branch: dev tags: true
skip_cleanup: true
# deploy master to production
- provider: script
script: chmod +x deploy.sh && sh deploy.sh production
on:
branch: master
skip_cleanup: true skip_cleanup: true

View file

@ -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

36
ci/settings.xml Normal file
View file

@ -0,0 +1,36 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<activeProfiles>
<activeProfile>github</activeProfile>
</activeProfiles>
<profiles>
<profile>
<id>github</id>
<repositories>
<!-- <repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository> -->
<repository>
<id>github</id>
<name>GitHub Towny Apache Maven Packages</name>
<url>https://maven.pkg.github.com/TownyAdvanced/Towny</url>
</repository>
</repositories>
</profile>
</profiles>
<servers>
<server>
<id>github</id>
<username>NorbiPeti</username>
<password>${env.GHTOKEN}</password>
</server>
</servers>
</settings>

View file

@ -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

View file

@ -69,13 +69,13 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
Component.registerComponent(this, new FunComponent()); Component.registerComponent(this, new FunComponent());
Component.registerComponent(this, new AppendTextComponent()); Component.registerComponent(this, new AppendTextComponent());
Component.registerComponent(this, new FormatterComponent()); Component.registerComponent(this, new FormatterComponent());
getCommand2MC().registerCommand(new DebugCommand()); registerCommand(new DebugCommand());
getCommand2MC().registerCommand(new HelpCommand()); registerCommand(new HelpCommand());
getCommand2MC().registerCommand(new HistoryCommand()); registerCommand(new HistoryCommand());
getCommand2MC().registerCommand(new InfoCommand()); registerCommand(new InfoCommand());
getCommand2MC().registerCommand(new MWikiCommand()); registerCommand(new MWikiCommand());
getCommand2MC().registerCommand(new ReloadCommand()); registerCommand(new ReloadCommand());
getCommand2MC().registerCommand(new SnapCommand()); registerCommand(new SnapCommand());
} }
public static Essentials essentials = null; public static Essentials essentials = null;

View file

@ -1,6 +1,7 @@
package buttondevteam.chat.components.chatonly; package buttondevteam.chat.components.chatonly;
import buttondevteam.chat.ChatPlayer; import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.PluginMain;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent; import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
import buttondevteam.chat.components.formatter.formatting.TellrawPart; import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager;
@ -20,7 +21,7 @@ import org.bukkit.event.player.PlayerTeleportEvent;
* Allows players to enter chat-only mode which puts them into spectator mode and disallows everything besides chatting. * Allows players to enter chat-only mode which puts them into spectator mode and disallows everything besides chatting.
*/ */
@ComponentMetadata(enabledByDefault = false) @ComponentMetadata(enabledByDefault = false)
public class ChatOnlyComponent extends Component implements Listener { public class ChatOnlyComponent extends Component<PluginMain> implements Listener {
@Override @Override
protected void enable() { protected void enable() {
registerListener(this); registerListener(this);

View file

@ -23,7 +23,7 @@ public class SetFlairCommand extends AdminCommandBase {
sender.sendMessage("§cPlayer not found.&r"); sender.sendMessage("§cPlayer not found.&r");
return true; return true;
} }
short ft = 0x00; short ft;
if (flairtime.equalsIgnoreCase("non-presser")) if (flairtime.equalsIgnoreCase("non-presser"))
ft = ChatPlayer.FlairTimeNonPresser; ft = ChatPlayer.FlairTimeNonPresser;
else if (flairtime.equalsIgnoreCase("cant-press")) else if (flairtime.equalsIgnoreCase("cant-press"))

View file

@ -6,10 +6,7 @@ import buttondevteam.chat.PluginMain;
import buttondevteam.chat.VanillaUtils; import buttondevteam.chat.VanillaUtils;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand; import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.components.chatonly.ChatOnlyComponent; import buttondevteam.chat.components.chatonly.ChatOnlyComponent;
import buttondevteam.chat.components.formatter.formatting.ChatFormatter; import buttondevteam.chat.components.formatter.formatting.*;
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.fun.FunComponent; import buttondevteam.chat.components.fun.FunComponent;
import buttondevteam.chat.components.towny.TownyComponent; import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.chat.listener.PlayerListener; import buttondevteam.chat.listener.PlayerListener;
@ -19,7 +16,6 @@ import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatEventBase; import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Priority;
import buttondevteam.lib.chat.TellrawSerializableEnum; import buttondevteam.lib.chat.TellrawSerializableEnum;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
@ -43,73 +39,56 @@ import java.util.function.Predicate;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class ChatProcessing { 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 HASHTAG_PATTERN = Pattern.compile("#(\\w+)");
private static final Pattern URL_PATTERN = Pattern.compile("(http[\\w:/?=$\\-_.+!*'(),&]+(?:#[\\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 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, private static final Color[] RainbowPresserColors = new Color[]{Color.Red, Color.Gold, Color.Yellow, Color.Green,
Color.Blue, Color.DarkPurple}; Color.Blue, Color.DarkPurple};
private static final Pattern WORD_PATTERN = Pattern.compile("\\S+"); private static final Pattern WORD_PATTERN = Pattern.compile("\\S+");
private static boolean pingedconsole = false; private static boolean pingedconsole = false;
public static final ChatFormatter ESCAPE_FORMATTER = ChatFormatter.builder("escape", ESCAPE_PATTERN).build(); private static ArrayList<MatchProviderBase> commonFormatters = Lists.newArrayList(
new RangeMatchProvider("bold", "**", FormatSettings.builder().bold(true).build()),
private static ArrayList<ChatFormatter> commonFormatters = Lists.newArrayList( new RangeMatchProvider("italic", "*", FormatSettings.builder().italic(true).build()),
ChatFormatter.builder("bold", BOLD_PATTERN).bold(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range) new RangeMatchProvider("underlined", "__", FormatSettings.builder().underlined(true).build()),
.priority(Priority.High).build(), new RangeMatchProvider("italic2", "_", FormatSettings.builder().italic(true).build()),
ChatFormatter.builder("italic", ITALIC_PATTERN).italic(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range).build(), new RangeMatchProvider("strikethrough", "~~", FormatSettings.builder().strikethrough(true).build()),
ChatFormatter.builder("italic2", ITALIC_PATTERN_2).italic(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range).build(), new RangeMatchProvider("spoiler", "||", FormatSettings.builder().obfuscated(true)
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)
.onmatch((match, cf, fs) -> { .onmatch((match, cf, fs) -> {
cf.setHoverText(match); cf.setHoverText(match);
return match; return match;
}).build(), }).build()),
ESCAPE_FORMATTER, ChatFormatter.builder("nullMention", NULL_MENTION_PATTERN).color(Color.DarkRed).build(), // Properly added a bug as a feature new StringMatchProvider("nullMention", FormatSettings.builder().color(Color.DarkRed).build(), true, "null"), // Properly added a bug as a feature
ChatFormatter.builder("consolePing", CONSOLE_PING_PATTERN).color(Color.Aqua).onmatch((match, builder, section) -> { new StringMatchProvider("consolePing", FormatSettings.builder().color(Color.Aqua)
if (!pingedconsole) { .onmatch((match, builder, section) -> {
System.out.print("\007"); if (!pingedconsole) {
pingedconsole = true; // Will set it to false in ProcessChat System.out.print("\007");
} pingedconsole = true; // Will set it to false in ProcessChat
return match; }
}).priority(Priority.High).build(), return "@console";
}).build(), true, "@console"),
ChatFormatter.builder("hashtag", HASHTAG_PATTERN).color(Color.Blue).openlink("https://twitter.com/hashtag/$1") new StringMatchProvider("cyan", FormatSettings.builder().color(Color.Aqua).build(), true, "cyan"), // #55
.priority(Priority.High).build(), new RangeMatchProvider("code", "`", FormatSettings.builder().color(Color.DarkGray).build()),
ChatFormatter.builder("cyan", CYAN_PATTERN).color(Color.Aqua).build(), // #55 new RegexMatchProvider("maskedLink", MASKED_LINK_PATTERN, FormatSettings.builder().underlined(true)
ChatFormatter.builder("code", CODE_PATTERN).color(Color.DarkGray).removeCharCount((short) 1).type(ChatFormatter.Type.Range) .onmatch((match, builder, section) -> {
.build(), String text, link;
ChatFormatter.builder("maskedLink", MASKED_LINK_PATTERN).underlined(true).onmatch((match, builder, section) -> { if (section.Matches.size() < 2 || (text = section.Matches.get(0)).length() == 0 || (link = section.Matches.get(1)).length() == 0)
String text, link; return "";
if (section.Matches.size() < 2 || (text = section.Matches.get(0)).length() == 0 || (link = section.Matches.get(1)).length() == 0) builder.setOpenlink(link);
return ""; return text;
builder.setOpenlink(link); }).build()),
return text; new RegexMatchProvider("url", URL_PATTERN, FormatSettings.builder().underlined(true).openlink("$1").build()),
}).type(ChatFormatter.Type.Excluder).build(), new RegexMatchProvider("hashtag", HASHTAG_PATTERN, FormatSettings.builder().color(Color.Blue).openlink("https://twitter.com/hashtag/$1").build()),
ChatFormatter.builder("url", URL_PATTERN).underlined(true).openlink("$1").type(ChatFormatter.Type.Excluder).build(), new StringMatchProvider("someone", FormatSettings.builder().color(Color.Aqua)
ChatFormatter.builder("someone", SOMEONE_PATTERN).color(Color.Aqua).onmatch((match, builder, section) -> { .onmatch((match, builder, section) -> {
if (Bukkit.getOnlinePlayers().size() == 0) return match; if (Bukkit.getOnlinePlayers().size() == 0) return match;
var players = ImmutableList.copyOf(Bukkit.getOnlinePlayers()); var players = ImmutableList.copyOf(Bukkit.getOnlinePlayers());
var playerC = new Random().nextInt(players.size()); var playerC = new Random().nextInt(players.size());
var player = players.get(playerC); var player = players.get(playerC);
playPingSound(player, ComponentManager.getIfEnabled(FormatterComponent.class)); playPingSound(player, ComponentManager.getIfEnabled(FormatterComponent.class));
return "@someone (" + player.getDisplayName() + "§r)"; return "@someone (" + player.getDisplayName() + "§r)";
}).build()); }).build(), true, "@someone"));
private static Gson gson = new GsonBuilder() private static Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum()) .registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum())
.registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection()) .registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection())
@ -155,20 +134,19 @@ public class ChatProcessing {
colormode = Color.Green; colormode = Color.Green;
// If greentext, ignore channel or player colors // If greentext, ignore channel or player colors
ArrayList<ChatFormatter> formatters; ArrayList<MatchProviderBase> formatters;
if (component.allowFormatting().get()) { 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 if (colormode == channel.Color().get() && mp != null && mp.RainbowPresserColorMode) { // Only overwrite channel color
createRPC(colormode, formatters); createRPC(colormode, formatters);
} }
pingedconsole = false; // Will set it to true onmatch (static constructor) pingedconsole = false; // Will set it to true onmatch (static constructor)
} else } else
formatters = Lists.newArrayList(ChatFormatter.builder("entireMessage", ENTIRE_MESSAGE_PATTERN) formatters = Lists.newArrayList();
.color(Color.White).priority(Priority.Low).build()); //This formatter is necessary
TellrawPart json = createTellraw(sender, message, player, mp, e.getUser(), channelidentifier, e.getOrigin()); TellrawPart json = createTellraw(sender, message, player, mp, e.getUser(), channelidentifier, e.getOrigin());
long combinetime = System.nanoTime(); 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; combinetime = System.nanoTime() - combinetime;
String jsonstr = toJson(json); String jsonstr = toJson(json);
if (jsonstr.length() >= 32767) { if (jsonstr.length() >= 32767) {
@ -213,12 +191,12 @@ public class ChatProcessing {
return false; return false;
} }
static void createRPC(Color colormode, ArrayList<ChatFormatter> formatters) { static void createRPC(Color colormode, ArrayList<MatchProviderBase> formatters) {
final AtomicInteger rpc = new AtomicInteger(0); 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)]); cf.setColor(RainbowPresserColors[rpc.getAndUpdate(i -> ++i < RainbowPresserColors.length ? i : 0)]);
return match; return match;
}).build()); }).build()));
} }
public static String toJson(TellrawPart json) { public static String toJson(TellrawPart json) {
@ -264,43 +242,21 @@ public class ChatProcessing {
+ "]"; + "]";
} }
static ArrayList<ChatFormatter> addFormatters(Color colormode, Predicate<Player> canSee, @Nullable FormatterComponent component) { static ArrayList<MatchProviderBase> addFormatters(Predicate<Player> canSee, @Nullable FormatterComponent component) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ArrayList<ChatFormatter> formatters = (ArrayList<ChatFormatter>) commonFormatters.clone(); ArrayList<MatchProviderBase> formatters = (ArrayList<MatchProviderBase>) commonFormatters.clone();
formatters.add(
ChatFormatter.builder("entireMessage", ENTIRE_MESSAGE_PATTERN).color(colormode).priority(Priority.Low).build());
boolean nottest; //Not assigning a default value, so that it can only be used in the if 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")) { if ((nottest = Bukkit.getOnlinePlayers().size() > 0) || Bukkit.getVersion().equals("test")) {
StringBuilder namesb = new StringBuilder("(?i)("); String[] names;
boolean addNameFormatter = false; //Needed because some names may be filtered out if they can't see the channel
if (nottest) if (nottest)
for (Player p : Bukkit.getOnlinePlayers()) { names = Bukkit.getOnlinePlayers().stream().filter(canSee).map(CommandSender::getName).toArray(String[]::new);
if (canSee.test(p)) {
namesb.append(p.getName()).append("|");
addNameFormatter = true;
}
}
else { else {
for (String testPlayer : testPlayers) names = new String[testPlayers.length];
namesb.append(testPlayer).append("|"); System.arraycopy(testPlayers, 0, names, 0, testPlayers.length);
addNameFormatter = true;
} }
namesb.deleteCharAt(namesb.length() - 1); String[] nicknames = Bukkit.getOnlinePlayers().stream().filter(canSee).map(Player::getUniqueId).map(PlayerListener.nicknames.inverse()::get)
namesb.append(")"); .filter(Objects::nonNull).toArray(String[]::new);
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(")");
Consumer<String> error = message -> { Consumer<String> error = message -> {
if (PluginMain.Instance != null) if (PluginMain.Instance != null)
@ -309,8 +265,8 @@ public class ChatProcessing {
System.out.println(message); System.out.println(message);
}; };
if (addNameFormatter) if (names.length > 0) //Add as first so it handles special characters (_) - though the order of the different types are defined
formatters.add(ChatFormatter.builder("name", Pattern.compile(namesb.toString())).color(Color.Aqua) formatters.add(0, new StringMatchProvider("name", FormatSettings.builder().color(Color.Aqua)
.onmatch((match, builder, section) -> { .onmatch((match, builder, section) -> {
Player p = Bukkit.getPlayer(match); Player p = Bukkit.getPlayer(match);
Optional<String> pn = nottest ? Optional.empty() Optional<String> pn = nottest ? Optional.empty()
@ -325,10 +281,10 @@ public class ChatProcessing {
} }
String color = String.format("§%x", (mpp.GetFlairColor() == 0x00 ? 0xb : mpp.GetFlairColor())); 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 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) if (nicknames.length > 0) //Add as first so it handles special characters
formatters.add(ChatFormatter.builder("nickname", Pattern.compile(nicksb.toString())).color(Color.Aqua) formatters.add(0, new StringMatchProvider("nickname", FormatSettings.builder().color(Color.Aqua)
.onmatch((match, builder, section) -> { .onmatch((match, builder, section) -> {
if (PlayerListener.nicknames.containsKey(match.toLowerCase())) { //Made a stream and all that but I can actually store it lowercased 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())); Player p = Bukkit.getPlayer(PlayerListener.nicknames.get(match.toLowerCase()));
@ -343,7 +299,7 @@ public class ChatProcessing {
error.accept("Player nicknamed " + match.toLowerCase() error.accept("Player nicknamed " + match.toLowerCase()
+ " not found in nickname map but was reported as online."); + " not found in nickname map but was reported as online.");
return "§c" + match + "§r"; return "§c" + match + "§r";
}).priority(Priority.High).type(ChatFormatter.Type.Excluder).build()); }).build(), true, nicknames));
} }
return formatters; return formatters;
} }

View file

@ -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<int[]> ranges) {
return ranges.stream().anyMatch(range -> range[1] >= start && range[0] <= end);
}
}

View file

@ -1,79 +1,23 @@
package buttondevteam.chat.components.formatter.formatting; package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand; 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.architecture.IHaveConfig;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Priority;
import lombok.Builder;
import lombok.Data;
import lombok.val; import lombok.val;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors; 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 <i>and italics</i> part, finally an underlined part * 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 <i>and italics</i> part, finally an underlined part
* again. * again.
* *
* @author NorbiPeti * @author NorbiPeti
*/ */
@SuppressWarnings("UnusedAssignment")
@Data
@Builder
public final class ChatFormatter { public final class ChatFormatter {
Pattern regex; private ChatFormatter() {
boolean italic;
boolean bold;
boolean underlined;
boolean strikethrough;
boolean obfuscated;
Color color;
TriFunc<String, ChatFormatter, FormattedSection, String> 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<Boolean> 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
} }
@FunctionalInterface @FunctionalInterface
@ -81,31 +25,32 @@ public final class ChatFormatter {
R apply(T1 x1, T2 x2, T3 x3); R apply(T1 x1, T2 x2, T3 x3);
} }
public static void Combine(List<ChatFormatter> formatters, String str, TellrawPart tp, IHaveConfig config) { //synchronized: Some of the formatters are reused, see createSections(...)
public static synchronized void Combine(List<MatchProviderBase> 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"); header("ChatFormatter.Combine begin");
ArrayList<FormattedSection> sections = new ArrayList<>(); ArrayList<FormattedSection> sections = new ArrayList<>();
if (config != null) //null if testing if (config != null) //null if testing
formatters.removeIf(cf -> !cf.enabled(config).get()); //Remove disabled formatters formatters.removeIf(cf -> !cf.enabled(config).get()); //Remove disabled formatters
createSections(formatters, str, sections, true); var excluded = new ArrayList<int[]>();
header("Section creation (excluders done)");
createSections(formatters, str, sections, false);
sortSections(sections);
/* /*
* 0: Start - 1: End index * 0: Start - 1: End index
*/ */
val remchars = new ArrayList<int[]>(); val remchars = new ArrayList<int[]>();
header("Range section conversion"); escapeThings(str, excluded, remchars);
sections = convertRangeSections(str, sections, remchars);
header("Adding remove chars (RC)"); // Important to add after the range section conversion sections.add(new FormattedSection(defaults, 0, str.length() - 1, Collections.emptyList())); //Add entire message
addRemChars(sections, remchars, str); 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"); header("Section combining");
combineSections(str, sections); combineSections(str, sections);
@ -115,145 +60,35 @@ public final class ChatFormatter {
header("ChatFormatter.Combine done"); header("ChatFormatter.Combine done");
} }
private static void createSections(List<ChatFormatter> formatters, String str, ArrayList<FormattedSection> sections, private static void escapeThings(String str, ArrayList<int[]> ignoredAreas, ArrayList<int[]> remchars) {
boolean excluders) { boolean escaped = false;
for (ChatFormatter formatter : formatters) { for (int i = 0; i < str.length(); i++) {
if (excluders == (formatter.type != Type.Excluder)) if (str.charAt(i) == '\\') {
continue; //If we're looking at excluders and this isn't one, skip - or vica-versa remchars.add(new int[]{i, i});
Matcher matcher = formatter.regex.matcher(str); ignoredAreas.add(new int[]{i + 1, i + 1});
while (matcher.find()) { i++; //Ignore a potential second slash
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<String> 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 newCombine(String str, ArrayList<FormattedSection> sections, ArrayList<int[]> remchars) { private static void createSections(List<MatchProviderBase> formatters, String str, ArrayList<FormattedSection> sections,
var stack = new Stack<FormattedSection>(); ArrayList<int[]> excludedAreas, ArrayList<int[]> removedCharacters) {
for (int i = 0; i < str.length(); i++) { formatters.forEach(MatchProviderBase::reset); //Reset state information, as we aren't doing deep cloning
for (Iterator<FormattedSection> iterator = sections.iterator(); iterator.hasNext(); ) { while (formatters.size() > 0) {
FormattedSection section = iterator.next(); for (var iterator = formatters.iterator(); iterator.hasNext(); ) {
if (section.Start <= i) { MatchProviderBase formatter = iterator.next();
stack.push(section); 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(); 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) {
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<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}))
.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<FormattedSection> sections) { private static void combineSections(String str, ArrayList<FormattedSection> sections) {
for (int i = 1; i < sections.size(); i++) { for (int i = 1; i < sections.size(); i++) {
DebugCommand.SendDebugMessage("i: " + i); DebugCommand.SendDebugMessage("i: " + i);
@ -262,7 +97,8 @@ public final class ChatFormatter {
{ {
FormattedSection firstSect = sections.get(i - 1); FormattedSection firstSect = sections.get(i - 1);
FormattedSection lastSect = sections.get(i); 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; var section = firstSect;
firstSect = lastSect; firstSect = lastSect;
lastSect = section; lastSect = section;
@ -271,41 +107,40 @@ public final class ChatFormatter {
lastSection = lastSect; lastSection = lastSect;
} }
DebugCommand.SendDebugMessage("Combining sections " + firstSection); DebugCommand.SendDebugMessage("Combining sections " + firstSection);
sendMessageWithPointer(str, firstSection.Start, firstSection.End); ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
DebugCommand.SendDebugMessage(" and " + lastSection); 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) { 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.Matches.addAll(lastSection.Matches);
firstSection.type = lastSection.type;
DebugCommand.SendDebugMessage("To section " + firstSection); DebugCommand.SendDebugMessage("To section " + firstSection);
sendMessageWithPointer(str, firstSection.Start, firstSection.End); ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
sections.remove(i); sections.remove(i);
i = 0; i = 0;
sortSections(sections); sortSections(sections);
continue; continue;
} else if (firstSection.End > lastSection.Start && firstSection.Start < lastSection.End) { } else if (firstSection.End >= lastSection.Start && firstSection.Start <= lastSection.End) {
int origend2 = firstSection.End; int firstSectEnd = firstSection.End;
firstSection.End = lastSection.Start - 1; firstSection.End = lastSection.Start - 1;
int origend = lastSection.End; int lastSectEnd = lastSection.End;
FormattedSection section = new FormattedSection(firstSection.Formatters, lastSection.Start, origend, FormattedSection section = new FormattedSection(firstSection.Settings, lastSection.Start, lastSectEnd,
firstSection.Matches, Type.Normal); firstSection.Matches);
section.Formatters.addAll(lastSection.Formatters); section.Settings.copyFrom(lastSection.Settings);
section.Matches.addAll(lastSection.Matches); // TODO: Clean section.Matches.addAll(lastSection.Matches);
sections.add(i, section); 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; if (firstSectEnd > lastSection.End) { //Copy first section info to last as the lastSection initially cuts the firstSection in half
lastSection.End = origend2; lastSection.Settings = FormatSettings.builder().build();
lastSection.Settings.copyFrom(firstSection.Settings);
}
lastSection.Start = lastSectEnd + 1;
lastSection.End = firstSectEnd;
Predicate<FormattedSection> removeIfNeeded = s -> { Predicate<FormattedSection> removeIfNeeded = s -> {
if (s.Start < 0 || s.End < 0 || s.Start > s.End) { if (s.Start < 0 || s.End < 0 || s.Start > s.End) {
DebugCommand.SendDebugMessage(" Removed: " + s); DebugCommand.SendDebugMessage(" Removed: " + s);
sendMessageWithPointer(str, s.Start, s.End); ChatFormatUtils.sendMessageWithPointer(str, s.Start, s.End);
sections.remove(s); sections.remove(s);
return true; return true;
} }
@ -315,15 +150,15 @@ public final class ChatFormatter {
DebugCommand.SendDebugMessage("To sections"); DebugCommand.SendDebugMessage("To sections");
if (!removeIfNeeded.test(firstSection)) { if (!removeIfNeeded.test(firstSection)) {
DebugCommand.SendDebugMessage(" 1:" + firstSection + ""); DebugCommand.SendDebugMessage(" 1:" + firstSection + "");
sendMessageWithPointer(str, firstSection.Start, firstSection.End); ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
} }
if (!removeIfNeeded.test(section)) { if (!removeIfNeeded.test(section)) {
DebugCommand.SendDebugMessage(" 2:" + section + ""); DebugCommand.SendDebugMessage(" 2:" + section + "");
sendMessageWithPointer(str, section.Start, section.End); ChatFormatUtils.sendMessageWithPointer(str, section.Start, section.End);
} }
if (!removeIfNeeded.test(lastSection)) { if (!removeIfNeeded.test(lastSection)) {
DebugCommand.SendDebugMessage(" 3:" + lastSection); DebugCommand.SendDebugMessage(" 3:" + lastSection);
sendMessageWithPointer(str, lastSection.Start, lastSection.End); ChatFormatUtils.sendMessageWithPointer(str, lastSection.Start, lastSection.End);
} }
i = 0; i = 0;
} }
@ -332,7 +167,7 @@ public final class ChatFormatter {
for (int j = i - 1; j <= i + 1; j++) { for (int j = i - 1; j <= i + 1; j++) {
if (j < sections.size() && sections.get(j).End < sections.get(j).Start) { if (j < sections.size() && sections.get(j).End < sections.get(j).Start) {
DebugCommand.SendDebugMessage("Removing section: " + sections.get(j)); 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); sections.remove(j);
j--; j--;
i = 0; i = 0;
@ -349,7 +184,7 @@ public final class ChatFormatter {
String originaltext; String originaltext;
int start = section.Start, end = section.End; int start = section.Start, end = section.End;
DebugCommand.SendDebugMessage("Start: " + start + " - End: " + 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("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("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());*/ 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); DebugCommand.SendDebugMessage("Section text: " + originaltext);
String openlink = null; 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(""); TellrawPart newtp = new TellrawPart("");
for (ChatFormatter formatter : section.Formatters) { var settings = section.Settings;
DebugCommand.SendDebugMessage("Applying formatter: " + formatter); DebugCommand.SendDebugMessage("Applying settings: " + settings);
if (formatter.onmatch != null) if (settings.onmatch != null)
originaltext = formatter.onmatch.apply(originaltext, formatter, section); originaltext = settings.onmatch.apply(originaltext, settings, section);
if (formatter.color != null) if (settings.color != null)
newtp.setColor(formatter.color); newtp.setColor(settings.color);
if (formatter.bold) if (settings.bold)
newtp.setBold(true); newtp.setBold(true);
if (formatter.italic) if (settings.italic)
newtp.setItalic(true); newtp.setItalic(true);
if (formatter.underlined) if (settings.underlined)
newtp.setUnderlined(true); newtp.setUnderlined(true);
if (formatter.strikethrough) if (settings.strikethrough)
newtp.setStrikethrough(true); newtp.setStrikethrough(true);
if (formatter.obfuscated) if (settings.obfuscated)
newtp.setObfuscated(true); newtp.setObfuscated(true);
if (formatter.openlink != null) if (settings.openlink != null)
openlink = formatter.openlink; openlink = settings.openlink;
if (formatter.hoverText != null) if (settings.hoverText != null)
newtp.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, formatter.hoverText)); newtp.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, settings.hoverText));
}
if (lasttp != null && newtp.getColor() == lasttp.getColor() if (lasttp != null && newtp.getColor() == lasttp.getColor()
&& newtp.isBold() == lasttp.isBold() && newtp.isBold() == lasttp.isBold()
&& newtp.isItalic() == lasttp.isItalic() && newtp.isItalic() == lasttp.isItalic()
@ -427,25 +261,10 @@ public final class ChatFormatter {
private static void sortSections(ArrayList<FormattedSection> sections) { private static void sortSections(ArrayList<FormattedSection> sections) {
sections.sort( sections.sort(
(s1, s2) -> s1.Start == s2.Start (s1, s2) -> s1.Start == s2.Start
? s1.End == s2.End ? Integer.compare(s2.Formatters.get(0).priority.GetValue(), ? s1.End == s2.End ? 0 : Integer.compare(s1.End, s2.End)
s1.Formatters.get(0).priority.GetValue()) : Integer.compare(s1.End, s2.End)
: Integer.compare(s1.Start, s2.Start)); : 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) { private static void header(String message) {
DebugCommand.SendDebugMessage("\n--------\n" + message + "\n--------\n"); DebugCommand.SendDebugMessage("\n--------\n" + message + "\n--------\n");
} }

View file

@ -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<String, FormatSettings, FormattedSection, String> 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();
}
}
}

View file

@ -1,36 +1,22 @@
package buttondevteam.chat.components.formatter.formatting; package buttondevteam.chat.components.formatter.formatting;
import java.util.ArrayList; import lombok.ToString;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
@ToString
public class FormattedSection { public class FormattedSection {
public int Start; public int Start;
public int End; public int End;
public ArrayList<ChatFormatter> Formatters = new ArrayList<ChatFormatter>(); public FormatSettings Settings;
public ArrayList<String> Matches = new ArrayList<String>(); public List<String> Matches = new ArrayList<>();
public ChatFormatter.Type type;
FormattedSection(ChatFormatter formatter, int start, int end, ArrayList<String> matches, ChatFormatter.Type type) { FormattedSection(FormatSettings settings, int start, int end, List<String> matches) {
Start = start; Start = start;
End = end; End = end;
Formatters.add(formatter); Settings = FormatSettings.builder().build();
Settings.copyFrom(settings);
Matches.addAll(matches); Matches.addAll(matches);
this.type = type;
}
FormattedSection(Collection<ChatFormatter> formatters, int start, int end, ArrayList<String> 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 + ")";
} }
} }

View file

@ -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<int[]> ignoredAreas, ArrayList<int[]> removedCharacters);
boolean isFinished();
String getName();
@Override
String toString();
void reset();
}

View file

@ -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<int[]> ignoredAreas, ArrayList<int[]> removedCharacters);
@Override
public String toString() {
return name;
}
protected abstract void resetSubclass();
public void reset() {
finished = false;
resetSubclass();
}
ConfigData<Boolean> enabled(IHaveConfig config) {
return config.getData(name + ".enabled", true);
}
}

View file

@ -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<int[]> ignoredAreas, ArrayList<int[]> 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;
}
}

View file

@ -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<int[]> ignoredAreas, ArrayList<int[]> 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<String> 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;
}
}

View file

@ -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<int[]> ignoredAreas, ArrayList<int[]> 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;
}
}

View file

@ -71,6 +71,6 @@ public final class TellrawEvent<T extends TellrawEvent.Action> implements Serial
} }
} }
public static interface Action extends TellrawSerializableEnum { public interface Action extends TellrawSerializableEnum {
} }
} }

View file

@ -48,6 +48,7 @@ public abstract class TellrawSerializer {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public void write(JsonWriter writer, Boolean val) throws IOException { public void write(JsonWriter writer, Boolean val) throws IOException {
if (val) if (val)

View file

@ -12,8 +12,6 @@ import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.lang.reflect.Method;
@CommandClass(helpText = { @CommandClass(helpText = {
"Town Color", // "Town Color", //
"This command allows setting a color for a town.", // "This command allows setting a color for a town.", //
@ -23,18 +21,6 @@ import java.lang.reflect.Method;
@RequiredArgsConstructor @RequiredArgsConstructor
public class TownColorCommand extends UCommandBase { public class TownColorCommand extends UCommandBase {
private final TownColorComponent component; private final TownColorComponent component;
@Override
public String[] getHelpText(Method method, Command2.Subcommand ann) {
StringBuilder cns = new StringBuilder(" <colorname1>");
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 @Command2.Subcommand
public boolean def(Player player, String... colornames) { public boolean def(Player player, String... colornames) {

View file

@ -19,14 +19,12 @@ import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.dynmap.towny.DTBridge; import org.dynmap.towny.DTBridge;
import java.io.File;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -81,14 +79,10 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
var cs = getConfig().getConfig().getConfigurationSection("towncolors"); var cs = getConfig().getConfig().getConfigurationSection("towncolors");
if (cs != null) if (cs != null)
loadTC.accept(cs); loadTC.accept(cs);
else
load_old(loadTC, null); //Load old data
if (usenc) { if (usenc) {
var ncs = getConfig().getConfig().getConfigurationSection("nationcolors"); var ncs = getConfig().getConfig().getConfigurationSection("nationcolors");
if (ncs != null) if (ncs != null)
loadNC.accept(ncs); 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 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<PluginMain> implements Listene
public void onPlayerJoin(TBMCPlayerJoinEvent event) { public void onPlayerJoin(TBMCPlayerJoinEvent event) {
updatePlayerColors(event.getPlayer(), event.GetPlayer().asPluginPlayer(ChatPlayer.class)); updatePlayerColors(event.getPlayer(), event.GetPlayer().asPluginPlayer(ChatPlayer.class));
} }
private static void load_old(Consumer<ConfigurationSection> loadTC,
Consumer<ConfigurationSection> 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);
}
}
} }

View file

@ -1,6 +1,6 @@
name: Chroma-Chat name: Chroma-Chat
main: buttondevteam.chat.PluginMain main: buttondevteam.chat.PluginMain
version: 4.0 version: '4.0'
commands: commands:
u: u:
description: Auto-flair system. Accept or ignore flair. description: Auto-flair system. Accept or ignore flair.

View file

@ -1,15 +1,5 @@
package buttondevteam.chat; 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.Runner;
import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner; 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.Statement;
import org.junit.runners.model.TestClass; 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} * Based on {@link Parameterized}
* *
@ -32,10 +28,10 @@ public class ObjectTestRunner extends Suite {
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public static @interface Objects { public @interface Objects {
} }
private class TestClassRunnerForObjects extends BlockJUnit4ClassRunner { private static class TestClassRunnerForObjects extends BlockJUnit4ClassRunner {
private List<Object> objectList; private List<Object> objectList;
private int fParameterSetNumber; private int fParameterSetNumber;
@ -77,13 +73,13 @@ public class ObjectTestRunner extends Suite {
} }
} }
private final ArrayList<Runner> runners = new ArrayList<Runner>(); private final ArrayList<Runner> runners = new ArrayList<>();
/** /**
* Only called reflectively. Do not use programmatically. * Only called reflectively. Do not use programmatically.
*/ */
public ObjectTestRunner(Class<?> klass) throws Throwable { public ObjectTestRunner(Class<?> klass) throws Throwable {
super(klass, Collections.<Runner>emptyList()); super(klass, Collections.emptyList());
List<Object> objectsList = getObjectsList(getTestClass()); List<Object> objectsList = getObjectsList(getTestClass());
for (int i = 0; i < objectsList.size(); i++) for (int i = 0; i < objectsList.size(); i++)
runners.add(new TestClassRunnerForObjects(getTestClass().getJavaClass(), objectsList, i)); runners.add(new TestClassRunnerForObjects(getTestClass().getJavaClass(), objectsList, i));

View file

@ -5,11 +5,9 @@ import buttondevteam.chat.ObjectTestRunner;
import buttondevteam.chat.ObjectTestRunner.Objects; import buttondevteam.chat.ObjectTestRunner.Objects;
import buttondevteam.chat.PluginMain; import buttondevteam.chat.PluginMain;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand; import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.components.formatter.formatting.ChatFormatter; import buttondevteam.chat.components.formatter.formatting.*;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent.ClickAction; import buttondevteam.chat.components.formatter.formatting.TellrawEvent.ClickAction;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent.HoverAction; import buttondevteam.chat.components.formatter.formatting.TellrawEvent.HoverAction;
import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.TestPrepare; import buttondevteam.core.TestPrepare;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
@ -32,7 +30,7 @@ public class ChatFormatIT {
DebugCommand.DebugMode = true; DebugCommand.DebugMode = true;
PluginMain.permission = Mockito.mock(Permission.class); PluginMain.permission = Mockito.mock(Permission.class);
List<Object> list = new ArrayList<Object>(); List<Object> 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").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).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) 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, .setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue))) 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 test test").setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "*test __test__ test*", new TellrawPart("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))); 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 @Test
public void testMessage() { public void testMessage() {
ArrayList<ChatFormatter> cfs = ChatProcessing.addFormatters(Color.White, p -> true, null); ArrayList<MatchProviderBase> cfs = ChatProcessing.addFormatters(p -> true, null);
final String chid = ChatProcessing.getChannelID(Channel.GlobalChat, ChatUtils.MCORIGIN); final String chid = ChatProcessing.getChannelID(Channel.GlobalChat, ChatUtils.MCORIGIN);
if (rainbowMode) if (rainbowMode)
ChatProcessing.createRPC(Color.White, cfs); ChatProcessing.createRPC(Color.White, cfs);
final TellrawPart tp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatUtils.MCORIGIN); 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("Testing: " + message);
// System.out.println(ChatProcessing.toJson(tp)); // System.out.println(ChatProcessing.toJson(tp));
final TellrawPart expectedtp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatUtils.MCORIGIN); final TellrawPart expectedtp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatUtils.MCORIGIN);