Merge branch 'dev'

# Conflicts:
#	.idea/ButtonChat.iml
This commit is contained in:
Norbi Peti 2020-03-04 23:11:31 +01:00
commit f3ec9e7870
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
26 changed files with 751 additions and 763 deletions

5
.gitignore vendored
View file

@ -69,14 +69,12 @@ build/
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
@ -195,7 +193,6 @@ $RECYCLE.BIN/
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
@ -217,7 +214,7 @@ pip-log.txt
.mr.developer.cfg
.metadata/*
TheButtonAutoFlair/out/artifacts/Autoflair/Autoflair.jar
#*.iml
*.iml
*.name
.idea/compiler.xml
*.xml

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.ChromaCore:Chroma-Core:master-59aa13cd74-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
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

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

View file

@ -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;
@ -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 moving.
*/
@ComponentMetadata(enabledByDefault = false)
public class ChatOnlyComponent extends Component implements Listener {
public class ChatOnlyComponent extends Component<PluginMain> implements Listener {
@Override
protected void enable() {
registerListener(this);

View file

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

View file

@ -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<ChatFormatter> 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<MatchProviderBase> 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) -> {
}).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 match;
}).priority(Priority.High).build(),
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) -> {
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;
}).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) -> {
}).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());
}).build(), true, "@someone"));
private static Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum())
.registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection())
@ -164,20 +143,19 @@ public class ChatProcessing {
colormode = Color.Green;
// If greentext, ignore channel or player colors
ArrayList<ChatFormatter> formatters;
ArrayList<MatchProviderBase> 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 +200,12 @@ public class ChatProcessing {
return false;
}
static void createRPC(Color colormode, ArrayList<ChatFormatter> formatters) {
static void createRPC(Color colormode, ArrayList<MatchProviderBase> 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) {
@ -273,43 +251,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")
ArrayList<ChatFormatter> formatters = (ArrayList<ChatFormatter>) commonFormatters.clone();
formatters.add(
ChatFormatter.builder("entireMessage", ENTIRE_MESSAGE_PATTERN).color(colormode).priority(Priority.Low).build());
ArrayList<MatchProviderBase> formatters = (ArrayList<MatchProviderBase>) 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<String> error = message -> {
if (PluginMain.Instance != null)
@ -318,8 +274,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<String> pn = nottest ? Optional.empty()
@ -334,10 +290,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 +308,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;
}

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;
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 <i>and italics</i> 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<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
private ChatFormatter() {
}
@FunctionalInterface
@ -81,31 +25,32 @@ public final class ChatFormatter {
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");
ArrayList<FormattedSection> 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<int[]>();
/*
* 0: Start - 1: End index
*/
val remchars = new ArrayList<int[]>();
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<ChatFormatter> formatters, String str, ArrayList<FormattedSection> 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<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 escapeThings(String str, ArrayList<int[]> ignoredAreas, ArrayList<int[]> 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<FormattedSection> sections, ArrayList<int[]> remchars) {
var stack = new Stack<FormattedSection>();
for (int i = 0; i < str.length(); i++) {
for (Iterator<FormattedSection> iterator = sections.iterator(); iterator.hasNext(); ) {
FormattedSection section = iterator.next();
if (section.Start <= i) {
stack.push(section);
private static void createSections(List<MatchProviderBase> formatters, String str, ArrayList<FormattedSection> sections,
ArrayList<int[]> excludedAreas, ArrayList<int[]> 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<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) {
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<FormattedSection> 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)
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 (formatter.italic)
if (settings.italic)
newtp.setItalic(true);
if (formatter.underlined)
if (settings.underlined)
newtp.setUnderlined(true);
if (formatter.strikethrough)
if (settings.strikethrough)
newtp.setStrikethrough(true);
if (formatter.obfuscated)
if (settings.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));
}
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<FormattedSection> 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");
}

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;
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<ChatFormatter> Formatters = new ArrayList<ChatFormatter>();
public ArrayList<String> Matches = new ArrayList<String>();
public ChatFormatter.Type type;
public FormatSettings Settings;
public List<String> Matches = new ArrayList<>();
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;
End = end;
Formatters.add(formatter);
Settings = FormatSettings.builder().build();
Settings.copyFrom(settings);
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();
}
@SuppressWarnings("ConstantConditions")
@Override
public void write(JsonWriter writer, Boolean val) throws IOException {
if (val)

View file

@ -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(" <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
public boolean def(Player player, String... colornames) {

View file

@ -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<PluginMain> 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<PluginMain> implements Listene
public void onPlayerJoin(TBMCPlayerJoinEvent event) {
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
main: buttondevteam.chat.PluginMain
version: 4.0
version: '4.0'
commands:
u:
description: Auto-flair system. Accept or ignore flair.

View file

@ -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<Object> objectList;
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.
*/
public ObjectTestRunner(Class<?> klass) throws Throwable {
super(klass, Collections.<Runner>emptyList());
super(klass, Collections.emptyList());
List<Object> objectsList = getObjectsList(getTestClass());
for (int i = 0; i < objectsList.size(); 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.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<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").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<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);
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);