Compare commits

...

36 commits
dev ... master

Author SHA1 Message Date
Norbi Peti d1515c2ab2
Fix using delegated config properties from the Core 2023-07-01 20:11:19 +02:00
Norbi Peti 512a62d75c
Fix remaining test cases for formatting
- Fixed name mentioning test check
- Removed legacy tell raw stuff
- Fixed RPC sections getting combined because the settings weren't updated before checking if they are the same
- Fixed spoiler test assuming and expecting the hover text to be white
#130
2023-06-28 23:56:35 +02:00
Norbi Peti b5fd834dc7
Fix chat tests with MockBukkit and fix Paper version
#130
2023-06-27 01:39:35 +02:00
Norbi Peti e35e94e87c
Migrate chat formatting to Paper's text component API (#130)
- Also updated the configs to use the interfaces
- Also updated the fun component to use a list config
- The text component change adds Paper as a requirement for this plugin
2023-06-19 00:42:29 +02:00
Norbi Peti 51cf31713b
Start fixing errors related to player API changes
- The fancy chat API would be useful here, I don't want to update my old code
2023-06-09 01:25:09 +02:00
Norbi Peti 2242655c41
Merge branch 'hotfix/1.0.1'
# Conflicts:
#	src/main/java/buttondevteam/chat/components/towny/TownyComponent.java
2023-06-07 23:37:10 +02:00
Norbi Peti 522f29ba58
Update Chroma-Core to v2.0.0 2023-04-26 00:03:18 +02:00
Norbi Peti 10bf0e98df
Update Towny dependency and replace methods that were removed (#128) 2023-03-13 00:47:40 +01:00
Norbi Peti 6c1378f370
Update dependencies and fix tellRaw for 1.17+ 2021-12-30 21:19:22 +01:00
Norbi Peti 56b9296fbb
Merge pull request #126 from TBMCPlugins/dependabot/maven/org.apache.logging.log4j-log4j-core-2.17.0
Bump log4j-core from 2.13.2 to 2.17.0
2021-12-30 16:47:37 +01:00
dependabot[bot] 5c399a08ad
Bump log4j-core from 2.13.2 to 2.17.0
Bumps log4j-core from 2.13.2 to 2.17.0.

---
updated-dependencies:
- dependency-name: org.apache.logging.log4j:log4j-core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-18 18:20:48 +00:00
Norbi Peti 724858ac70
Fix tellRaw for 1.16.4/1.16.5 2021-01-18 17:18:35 +01:00
Norbi Peti 9a10dcd1fc
Fix plugin.yml 2020-10-31 18:24:52 +01:00
Norbi Peti dc6df53c96
Improve test coverage, preparations, fixes
Removed an unnnecessary bit of code from ChatFormatter
2020-10-29 20:31:38 +01:00
Norbi Peti 7646db4487
Fix player data plugin name, Spotbugs fixes
Also set the version
2020-10-28 00:28:15 +01:00
Norbi Peti 8e9989caee
Convert all configs 2020-10-26 19:56:22 +01:00
Norbi Peti 77b5a069c5
Merge pull request #123 from TBMCPlugins/dependabot/maven/junit-junit-4.13.1
Bump junit from 4.12 to 4.13.1
2020-10-25 02:00:58 +02:00
Norbi Peti c52a6873fe
Exception stuff and player data stuff 2020-10-25 01:18:36 +02:00
Norbi Peti c6ba0ec607
Create codeql-analysis.yml 2020-10-13 18:02:45 +02:00
dependabot[bot] 737016bf00
Bump junit from 4.12 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 15:56:57 +00:00
Norbi Peti a63f2bb256
Greentext formatter, doc and stuff 2020-09-06 22:46:24 +02:00
Norbi Peti 59dbc78b52
Fix #122
It will have to be fixed again at a later date
2020-09-04 03:56:25 +02:00
Norbi Peti b483b7df0c
Fix /waitwhat and /me 2020-09-04 03:41:23 +02:00
Norbi Peti 4d4448fca1
Remove htmlcleaner and fix #117 2020-08-26 22:19:56 +02:00
Norbi Peti 02423153de
Merge pull request #119 from TBMCPlugins/dependabot/maven/org.apache.logging.log4j-log4j-core-2.13.2
Bump log4j-core from 2.8.1 to 2.13.2
2020-07-27 01:15:05 +02:00
Norbi Peti ce4f40c2c4
Fix /u ncolor (#118) 2020-07-25 14:17:11 +02:00
dependabot[bot] 372b2f49b3
Bump log4j-core from 2.8.1 to 2.13.2
Bumps log4j-core from 2.8.1 to 2.13.2.

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-01 18:27:01 +00:00
Norbi Peti 7448eb7e3a
1.16 updates, fixes 2020-06-27 03:00:08 +02:00
Norbi Peti c688ec9243
Finally fix Towny errors for 1.12 - use the intended API everywhere 2020-04-08 01:10:49 +02:00
Norbi Peti e4b47efd3f
Fix case when the second section goes further when combining
And added a test for it
2020-04-07 23:08:48 +02:00
Norbi Peti 43525bd93c
Add missing backslash after "From X" and before "n" 2020-03-20 02:06:07 +01:00
Norbi Peti 052149bcb7
Add tabcompletes, fix some commands and help texts 2020-03-20 01:26:14 +01:00
Norbi Peti 5d8ae7fbd0
Command fixes, remove old preprocessor
Fixed command channels
/tpahere message removed
Made /me an actual command
Fixed some help texts
2020-03-15 03:09:00 +01:00
Norbi Peti f3ec9e7870
Merge branch 'dev'
# Conflicts:
#	.idea/ButtonChat.iml
2020-03-04 23:11:31 +01:00
Norbi Peti 03b91d2fdb Don't allow sending messages too fast
The player needs to wait the configured amount of milliseconds between messages
#115
2020-03-03 16:13:39 +01:00
Norbi Peti 661534b92d Update README.md 2020-03-02 12:40:16 +01:00
52 changed files with 972 additions and 1225 deletions

0
.idea/ButtonChat.iml Normal file
View file

View file

@ -7,17 +7,15 @@ 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
- oraclejdk11
sudo: true
dist: trusty # Needed for Java 8, although we might not need Java 8
deploy:
- 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'
file: 'target/Chroma-Chat.jar'
on:
tags: true
skip_cleanup: true

143
README.md
View file

@ -1,133 +1,28 @@
# The Button Minecraft server chat plugin
# Chroma-Chat plugin
A plugin that provides markdown formatting, name mentioning and other chat features.
## How to use
### Players
#### Obtaining the flair (/u accept)
At first, you need to connect your Reddit account with your Minecraft account. This is done by writing your Minecraft name to [this thread](https://www.reddit.com/r/Chromagamers/comments/51ys94/flair_thread_for_the_mc_server/), following the instructions in the post.
## Components
When you're done, connect to the server, if you aren't already on. You should see a message after a while (max. 10 seconds) asking if you're the correct Reddit user. Type /u accept to confirm it and accept your flair.
### Announcer
This component simply sends messages globally based on a configurable list in a given interval.
__Note__: You can comment your ingame name from multiple Reddit accounts. In this case, you'll need to specify the exact username you want to pair with your Minecraft account. For example:
### Append text
This component allows the players to put tableflips and other texts after their messages using /tableflip and other commands.
/u accept NorbiPeti
### Chat-only
This component lets a player become invincible at the cost of not being able to move. It was made for chat clients.
#### Hiding the message (/u ignore)
You can use this command to hide the notice showing up after you log in if you don't have a flair accepted.
### Flair
This component downloads /r/TheButton flairs from Reddit for users who comment in a specific thread. This was the original function of the plugin.
#### Flair not showing up
Please note that in some cases your flair cannot be obtained (specifically, if it's not stored by karmadecay.com, in which case possibly Karmancer can't show it as well). In this case, there are two possibilities.
### Formatter
This component provides the Markdown and other formatting and name mentioning.
If you're a non-presser or a can't press and only your time is recorded, it will automatically decide based on your account creation date.
### Fun
This component has some random things I added for fun, including "Press F to pay respects" and rainbow chat.
If nothing is known about your flair, you need to ask an admin to set the flair for you. Please prepare a link to a comment you made on /r/thebutton, if possible.
### Town colors
This component allows mayors and kings to set a color for their town/nation, which sets each resident's name colors and also shows on Dynmap.
#### Getting someone's Reddit username (/u name)
You can see a player's username if they have a flair shown.
#### Name mentioning
If you simply say any online player's full playername or nickname, it'll highlight it and play a sound for the target player.
If you say a nickname, it'll show it's original colors, if you say a username, then it will choose based on flair color if known, otherwise it'll use the aqua color.
You can also use @console to ping the console. If someone is there, they'll receive an audible bell signal.
#### RP/OOC mode (/nrp or /ooc)
You can use /ooc <message> to say something Out-of-Character. Otherwise everything you speak should be treated as said in RP, except when it is obvious it's not in RP (like talking about the server).
#### Greentext support (>message)
Start your message with '>' to make it green.
#### Hashtags (#hashtag)
If you say a hashtag in global chat, it'll highlight it and makes it clickable, linking to the hashtag page on Twitter.
#### Paying respects (F)
If a player dies, sometimes the plugin will tell everyone "Press F to pay respects.". After a few seconds, a message will tell everyone how many people paid their respects.
If you hover over a player's name in chat, you can see how much respect they gained this way, divided by the number of eligible deaths.
#### Copy messages
To copy a message from chat, click the channel identifier (for example: [g] or [TC]) at the beginning of the message.
#### Tableflipping (/tableflip and /unflip) and shrug (/shrug)
The idea of this command came from Discord.
Examples:
/tableflip - (╯°□°)╯︵ ┻━┻
/tableflip test - test(╯°□°)╯︵ ┻━┻
/unflip - ┬─┬ ( ゜-゜ノ)
/unflip test - test┬─┬ ( ゜-゜ノ)
#### Chat only (/chatonly)
You can use this mode to protect yourself if you connect from a chat-only client. This will make you invincible, but unable to move, teleport or interact with the world in any way.
#### Rainbow chat (/u c)
Chat in rainbow/Presser colors.
### Admins
Type /u admin for a list of the commands.
#### Seeing player information (/u admin playerinfo)
You can check someone's flair status and other infos in case something goes wrong.
It outputs the player name (useful if something goes *really* wrong), the player's current ingame flair, the Reddit username, and their flair status:
* Accepted: The user has a flair shown after their name.
* Ignored: The user chose to hide their flair, but they have one.
* Recognised: The user has recognised flair(s) but haven't accepted any.
* Commented: The user commented, but their flair isn't known.
* NoComment: The user haven't commented in the thread.
#### Reloading the plugin (/u admin reload)
This is useful if you want to change a file related to the plugin.
Be careful and make sure you do /u admin save before you reload the plugin. You need to confirm your action (/u admin confirm) to make sure no setting is lost.
If you want to edit a file, you need to do /u admin save, then edit the file you want, then do /u admin reload.
#### Setting the flair by hand (/u admin setflair)
This allows you to set any flair you want to any player. This will override the automatic flairs, though it's not recommended to do so. However, the player can reset the automatic flair at any time (see /u accept).
* To remove a user's flair:
/u admin setflair <playername> none false
* To set a non-presser or can't press flair:
/u admin setflair <playername> non-presser/cant-press false
* To set a cheater flair:
/u admin setflair <playername> <time> true
* To also set the username for the flair:
/u admin setflair <playername> <time> <cheater> <username>
Usage:
/u admin setflair <playername> <time (without s) or non-presser, can't press, none> <cheater (true/false)>> [username]
#### Updating the plugin (/u admin updateplugin)
I've made a simple command to allow updating the plugin easily. After running this command, the server needs to get restarted for the changes to take effect.
This command will not do any other thing than downloading the JAR file from here to the plugins directory. Do not spam it, because it will then generate unnecessary network traffic on the server.
#### Toggle settings
/u admin togglerpshow - Toggles [RP] tag shown at each message except /ooc or /nrp.
/u admin debug - Toggles debug mode. Currently it outputs a lot of info about the chat formatting.
#### Setting the sound played on name mentioning
You can set the sound played by editing "notificationsound" and "notificationpitch" in the config file (thebuttonmc.yml).
#### Announcements (/u announce)
You can make announcements broadcasted every n minutes where you can set n and it defaults to 15.
/u announce add <message> - Adds a new announcement. It supports formatting codes with &.
/u announce remove <index> - Remove announcement by index (see below).
/u announce list - List announcements with indexes.
/u announce settime <minutes> - Set the time between announcements in minutes.
/u announce edit <index> <message> - Directly edits the announcement at the specified index. If there are less announcements than index, it'll create enough announcements.
#### Color modes (/u c)
Use /u c \<colorname\> to set the color of your messages.
### Towny
This component is needed by the town colors component. Besides, it reads Towny events to be broadcasted to Discord, if that plugin is used.

View file

@ -1,36 +0,0 @@
<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>

280
pom.xml
View file

@ -1,56 +1,30 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
<artifactId>CorePOM</artifactId>
<version>master-SNAPSHOT</version>
</parent>
<version>0.0.1-SNAPSHOT</version>
<name>Chroma-Chat Plugin</name>
<description>Chroma-Chat Plugin</description>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*.properties</include>
<include>*.yml</include>
<include>*.csv</include>
<include>*.txt</include>
</includes>
<filtering>true</filtering>
<version>v${noprefix.version}-SNAPSHOT</version>
<name>Chroma-Chat Plugin</name>
<description>Chroma-Chat Plugin</description>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*.properties</include>
<include>*.yml</include>
<include>*.csv</include>
<include>*.txt</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<finalName>Chroma-Chat</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>net.sourceforge.htmlcleaner:htmlcleaner</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
@ -92,64 +66,6 @@
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
</configuration>
</plugin>
<!-- <plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.5</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
*NOTE*: The default phase of revision is initialize, but in case you want to change it, you can do so by adding the phase here
<phase>initialize</phase>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
</configuration>
</execution>
<execution>
<id>validate-the-git-infos</id>
<goals>
<goal>validateRevision</goal>
</goals>
*NOTE*: The default phase of validateRevision is verify, but in case you want to change it, you can do so by adding the phase here
<phase>package</phase>
<configuration>
<validationProperties>
- <validationProperty>
<name>validating git dirty</name>
<value>${git.branch}</value>
<shouldMatchTo>dev</shouldMatchTo>
</validationProperty> -
</validationProperties>
</configuration>
</execution>
</executions>
<configuration>
<injectAllReactorProjects>true</injectAllReactorProjects>
<verbose>true</verbose>
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
</configuration>
</plugin> -->
<!-- <plugin> <groupId>org.basepom.maven</groupId> <artifactId>duplicate-finder-maven-plugin</artifactId>
<version>1.2.1</version> <executions> <execution> <goals> <goal>check</goal>
</goals> </execution> </executions> </plugin> -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-testCompile</id>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<groupId>buttondevteam</groupId>
@ -164,110 +80,121 @@
</repository>
<repository>
<id>ess-repo</id>
<url>https://ci.ender.zone/plugin/repository/everything/</url>
<url>https://repo.essentialsx.net/releases/</url>
</repository>
<!-- <repository>
<id>Minigames</id>
<url>http://maven.addstar.com.au/artifactory/release</url>
</repository> -->
<!-- <repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository> -->
<!-- <repository> <id>WorldEdit</id> <url>http://maven.sk89q.com/artifactory/repo</url>
</repository> -->
<repository>
<id>projectlombok.org</id>
<url>http://projectlombok.org/mavenrepo</url>
</repository>
<!-- <repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository> -->
<!-- <repository> <id>WorldEdit</id> <url>http://maven.sk89q.com/artifactory/repo</url>
</repository> -->
<repository>
<id>projectlombok.org</id>
<url>https://projectlombok.org/mavenrepo</url>
</repository>
<repository>
<id>Dynmap</id>
<url>https://repo.mikeprimm.com</url>
</repository>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.12.2-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
<artifactId>Chroma-Core</artifactId>
<version>${branch}-SNAPSHOT</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.htmlcleaner/htmlcleaner -->
<dependency>
<groupId>net.sourceforge.htmlcleaner</groupId>
<artifactId>htmlcleaner</artifactId>
<version>2.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
<dependencies>
<dependency> <!-- TODO: Can't update MockBukkit because of a ByteBuddy exception and the old version relies on 1.19.1 -->
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.19.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.ess3</groupId>
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
<artifactId>Chroma-Core</artifactId>
<version>v2.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.essentialsx</groupId>
<artifactId>EssentialsX</artifactId>
<version>2.17.1</version>
<version>2.19.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.palmergames.bukkit.towny</groupId>
<groupId>com.github.TownyAdvanced</groupId>
<artifactId>Towny</artifactId>
<version>0.95.2.0</version>
<!-- <version>8d3b6b6</version> ButtonCore repo -->
<version>0.98.6.0</version>
<scope>provided</scope>
</dependency>
<!-- <dependency> <groupId>au.com.mineauz</groupId> <artifactId>Minigames</artifactId>
<version>1.8.0</version> </dependency> -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<scope>provided</scope>
</dependency>
<!-- <dependency> <groupId>au.com.mineauz</groupId> <artifactId>Minigames</artifactId>
<version>1.8.0</version> </dependency> -->
<dependency>
<groupId>com.github.milkbowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>master-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.12.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<version>1.16.4-R0.1-SNAPSHOT</version>
</dependency> -->
<dependency>
<groupId>com.github.webbukkit</groupId>
<groupId>com.github.TownyAdvanced</groupId>
<artifactId>Dynmap-Towny</artifactId>
<version>master-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.webbukkit</groupId>
<artifactId>Dynmap</artifactId>
<version>v2.5</version>
<version>0.89</version>
</dependency>
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>dynmap-api</artifactId>
<version>3.2-beta-1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Included in vanilla minecraft's JAR -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.1</version>
<version>2.17.0</version>
<scope>provided</scope>
</dependency>
<!-- TestPrepare also needs it (ChromaCore) so the versions should match -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mojang/brigadier -->
<dependency>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
<version>1.0.500</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.seeseemelk</groupId>
<artifactId>MockBukkit-v1.19</artifactId>
<version>2.29.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<artifactId>Chroma-Chat</artifactId>
<organization>
@ -278,23 +205,6 @@
<!-- github server corresponds to entry in ~/.m2/settings.xml -->
<github.global.server>githubo</github.global.server>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<branch>
master
</branch> <!-- Should be master if building ButtonCore locally - the CI will overwrite it (see below) -->
<noprefix.version>1.0.0</noprefix.version>
</properties>
<profiles>
<profile>
<id>ci</id>
<activation>
<property>
<name>env.TRAVIS_BRANCH</name>
</property>
</activation>
<properties>
<!-- Override only if necessary -->
<branch>${env.TRAVIS_BRANCH}</branch>
</properties>
</profile>
</profiles>
</project>

View file

@ -1,56 +1,41 @@
package buttondevteam.chat;
import buttondevteam.chat.components.flair.FlairStates;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.architecture.config.IListConfigData;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.player.EnumPlayerData;
import buttondevteam.lib.player.PlayerClass;
import buttondevteam.lib.player.PlayerData;
import buttondevteam.lib.player.TBMCPlayerBase;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
@PlayerClass(pluginname = "Button1Chat")
@PlayerClass(pluginname = "Chroma-Chat")
public class ChatPlayer extends TBMCPlayerBase {
public PlayerData<String> UserName() {
return data(null);
}
public final IConfigData<String> UserName = getConfig().getData("UserName", "");
public List<String> UserNames() {
return data(new ArrayList<String>()).get();
}
public final IListConfigData<String> UserNames = getConfig().getListData("UserNames", Collections.emptyList());
public PlayerData<Integer> FlairTime() {
return data(FlairTimeNone);
}
public final IConfigData<Integer> FlairTime = getConfig().getData("FlairTime", FlairTimeNone);
public EnumPlayerData<FlairStates> FlairState() {
return dataEnum(FlairStates.class, FlairStates.NoComment);
}
public final IConfigData<FlairStates> FlairState = getConfig().getData("FlairState", FlairStates.NoComment,
fs -> FlairStates.valueOf((String) fs), FlairStates::toString);
public PlayerData<Integer> FCount() {
return data(0);
}
public final IConfigData<Integer> FCount = getConfig().getData("FCount", 0);
public PlayerData<Integer> FDeaths() {
return data(0);
}
public final IConfigData<Integer> FDeaths = getConfig().getData("FDeaths", 0);
public PlayerData<Boolean> FlairCheater() {
return data(false);
}
public final IConfigData<Boolean> FlairCheater = getConfig().getData("FlairCheater", false);
public PlayerData<ArrayList<Integer>> NameColorLocations() { // No byte[], no TIntArrayList
return data(null);
}
public final IListConfigData<Integer> NameColorLocations = getConfig().getListData("NameColorLocations", Collections.emptyList()); // No byte[], no TIntArrayList
public boolean Working;
// public int Tables = 10;
public boolean RainbowPresserColorMode = false;
public Color OtherColorMode = null;
public boolean ChatOnly = false;
public long LastMessageTime = 0L;
public static final int FlairTimeNonPresser = -1;
public static final int FlairTimeCantPress = -2;
@ -58,13 +43,12 @@ public class ChatPlayer extends TBMCPlayerBase {
/**
* Gets the player's flair, optionally formatting for Minecraft.
*
* @param noformats
* The MC formatting codes will be only applied if false
*
* @param noformats The MC formatting codes will be only applied if false
* @return The flair
*/
public String GetFormattedFlair(boolean noformats) {
int time = FlairTime().get();
int time = FlairTime.get();
if (time == FlairTimeCantPress)
return noformats ? "(can't press)" : "§r(--s)§r";
if (time == FlairTimeNonPresser)
@ -76,7 +60,7 @@ public class ChatPlayer extends TBMCPlayerBase {
/**
* Gets the player's flair, formatted for Minecraft.
*
*
* @return The flair
*/
public String GetFormattedFlair() {
@ -84,13 +68,13 @@ public class ChatPlayer extends TBMCPlayerBase {
}
public void SetFlair(int time) {
FlairTime().set(time);
FlairTime.set(time);
FlairUpdate();
}
public void SetFlair(int time, boolean cheater) {
FlairTime().set(time);
FlairCheater().set(cheater);
FlairTime.set(time);
FlairCheater.set(cheater);
FlairUpdate();
}
@ -98,15 +82,15 @@ public class ChatPlayer extends TBMCPlayerBase {
// Flairs from Command Block The Button - Teams
// PluginMain.Instance.getServer().getScoreboardManager().getMainScoreboard().getTeams().add()
Player p = Bukkit.getPlayer(uuid);
Player p = Bukkit.getPlayer(getUniqueId());
if (p != null)
p.setPlayerListName(String.format("%s%s", p.getDisplayName(), GetFormattedFlair()));
p.setPlayerListName(String.format("%s%s", p.getDisplayName(), GetFormattedFlair()));
}
public short GetFlairColor() {
if (FlairCheater().get())
if (FlairCheater.get())
return 0x5;
final int flairTime = FlairTime().get();
final int flairTime = FlairTime.get();
if (flairTime == FlairTimeNonPresser)
return 0x7;
else if (flairTime == FlairTimeCantPress)
@ -127,6 +111,6 @@ public class ChatPlayer extends TBMCPlayerBase {
}
public double getF() {
return (double) FCount().get() / (double) FDeaths().get();
return (double) FCount.get() / (double) FDeaths.get();
}
}

View file

@ -1,12 +1,13 @@
package buttondevteam.chat;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.player.ChromaGamerBase;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Optional;
import java.util.function.Function;
public final class ChatUtils {
public static final String MCORIGIN = "Minecraft"; //Shouldn't change, like ever - TBMCPlayer.getFolderForType(TBMCPlayer.class) capitalized
@ -44,16 +45,21 @@ public final class ChatUtils {
/**
* Sends a regular (non-Markdown) chat message. Used as a fallback if the chat processing fails.
*
* @param e The chat event
* @param modifier A function that alters the message to be displayed to the player
* @param e The chat event
*/
public static void sendChatMessage(TBMCChatEvent e, Function<String, String> modifier) {
var str = "[" + e.getChannel().DisplayName().get() + "] <"
+ ChromaUtils.getDisplayName(e.getSender()) + "> " + e.getMessage();
str = modifier.apply(str);
public static void sendChatMessage(TBMCChatEvent e) {
var str = getMessageString(e.getChannel(), e.getUser(), e.getMessage());
for (Player p : Bukkit.getOnlinePlayers())
if (e.shouldSendTo(p))
if (e.shouldSendTo(ChromaGamerBase.getFromSender(p)))
p.sendMessage(str);
Bukkit.getConsoleSender().sendMessage(str);
}
public static String getMessageString(Channel channel, ChromaGamerBase sender, String message) {
return "§c!§r[" + channel.displayName.get() + "] <" + sender.getName() + "> " + message;
}
public static void sendChatMessage(Channel channel, ChromaGamerBase sender, String message, CommandSender to) {
to.sendMessage(getMessageString(channel, sender, message));
}
}

View file

@ -1,6 +1,7 @@
package buttondevteam.chat;
import buttondevteam.chat.commands.MWikiCommand;
import buttondevteam.chat.commands.MeCommand;
import buttondevteam.chat.commands.SnapCommand;
import buttondevteam.chat.commands.ucmds.HelpCommand;
import buttondevteam.chat.commands.ucmds.HistoryCommand;
@ -20,19 +21,16 @@ import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI;
import com.earth2me.essentials.Essentials;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.plugin.RegisteredServiceProvider;
public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15.
// A user, which flair isn't obtainable:
// https://www.reddit.com/r/thebutton/comments/31c32v/i_pressed_the_button_without_really_thinking/
public static PluginMain Instance;
public static ConsoleCommandSender Console;
@ -40,9 +38,7 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
* If enabled, stores and displays the last 10 messages the player can see (public, their town chat etc.)
* Can be used with the Discord plugin so players can see some of the conversation they missed that's visible on Discord anyways.
*/
public ConfigData<Boolean> storeChatHistory() {
return getIConfig().getData("storeChatHistory", true);
}
public IConfigData<Boolean> storeChatHistory = getIConfig().getData("storeChatHistory", true);
// Fired when plugin is first enabled
@Override
@ -57,10 +53,10 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
if (Bukkit.getPluginManager().isPluginEnabled("Towny"))
Component.registerComponent(this, new TownyComponent());
TBMCChatAPI.RegisterChatChannel(new Channel("§7RP§f", Color.Gray, "rp", null)); //Since it's null, it's recognised as global
TBMCChatAPI.registerChatChannel(new Channel("§7RP§f", Color.Gray, "rp", null)); //Since it's null, it's recognised as global
if (!setupEconomy() || !setupPermissions())
TBMCCoreAPI.SendException("We're in trouble", new Exception("Failed to set up economy or permissions!"));
if (!setupPermissions())
TBMCCoreAPI.SendException("We're in trouble", new Exception("Failed to set up permissions!"), this);
if (Bukkit.getPluginManager().isPluginEnabled("Towny"))
Component.registerComponent(this, new TownColorComponent());
@ -76,6 +72,7 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
registerCommand(new MWikiCommand());
registerCommand(new ReloadCommand());
registerCommand(new SnapCommand());
registerCommand(new MeCommand());
}
public static Essentials essentials = null;
@ -86,7 +83,6 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
}
public static Permission permission = null;
public static Economy economy = null;
private boolean setupPermissions() {
RegisteredServiceProvider<Permission> permissionProvider = getServer().getServicesManager()
@ -97,14 +93,4 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
return (permission != null);
}
private boolean setupEconomy() {
RegisteredServiceProvider<Economy> economyProvider = getServer().getServicesManager()
.getRegistration(net.milkbowl.vault.economy.Economy.class);
if (economyProvider != null) {
economy = economyProvider.getProvider();
}
return (economy != null);
}
}

View file

@ -2,10 +2,14 @@ package buttondevteam.chat;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.player.ChromaGamerBase;
import lombok.experimental.UtilityClass;
import lombok.val;
import org.bukkit.entity.Player;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.UUID;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
@ -13,14 +17,12 @@ import java.util.function.Predicate;
public class VanillaUtils {
public String getGroupIfChatOn(Player p, TBMCChatEvent e) {
try {
if (isChatOn(p)) // Only send if client allows chat
return e.getGroupID(p);
else
if (!isChatOn(p)) // Only send if client allows chat
return null;
} catch (NoClassDefFoundError ex) {
MainPlugin.Instance.getLogger().warning("Compatibility error, can't check if the chat is hidden by the player.");
return e.getGroupID(p);
MainPlugin.getInstance().getLogger().warning("Compatibility error, can't check if the chat is hidden by the player.");
}
return e.getGroupID(ChromaGamerBase.getFromSender(p));
}
private Predicate<Player> isChatOn;
@ -79,9 +81,22 @@ public class VanillaUtils {
if (notCraftPlayer(pcl)) return false;
val hm = pcl.getMethod("getHandle");
val handle = hm.invoke(p);
val nms = handle.getClass().getPackage().getName();
val chatcompcl = Class.forName(nms + ".IChatBaseComponent");
val sendmsg = handle.getClass().getMethod("sendMessage", chatcompcl);
var nmsOrChat = handle.getClass().getPackage().getName();
if (!nmsOrChat.contains(".v1_"))
nmsOrChat = "net.minecraft.network.chat";
val chatcompcl = Class.forName(nmsOrChat + ".IChatBaseComponent");
//val chatcomarrcl = Class.forName("[L" + chatcompcl.getName() + ";");
val chatcomparr = Array.newInstance(chatcompcl, 1);
final Method sendmsg;
{
Method sendmsg1;
try {
sendmsg1 = handle.getClass().getMethod("sendMessage", UUID.class, chatcomparr.getClass());
} catch (NoSuchMethodException e) {
sendmsg1 = handle.getClass().getMethod("sendMessage", chatcomparr.getClass());
}
sendmsg = sendmsg1;
}
/*val ccucl = Class.forName(nms + ".ChatComponentUtils");
val iclcl = Class.forName(nms + ".ICommandListener");
@ -97,7 +112,11 @@ public class VanillaUtils {
val hhandle = hm.invoke(pl);
val deserialized = am.invoke(null, jsonStr);
//val filtered = ffdm.invoke(null, hhandle, deserialized, hhandle);
sendmsg.invoke(hhandle, deserialized);
Array.set(chatcomparr, 0, deserialized);
if (sendmsg.getParameterCount() == 2)
sendmsg.invoke(hhandle, null, chatcomparr); //
else
sendmsg.invoke(hhandle, chatcomparr);
return true;
} catch (Exception e) {
e.printStackTrace();

View file

@ -0,0 +1,15 @@
package buttondevteam.chat.commands;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.*;
@CommandClass(helpText = {
"Me",
"Displays a message starting with your name in your current channel."
})
public class MeCommand extends ICommand2MC {
@Command2.Subcommand
public void def(Command2MCSender sender, @Command2.TextArg String message) {
TBMCChatAPI.SendSystemMessage(sender.getChannel(), sender.getChannel().getRTR(sender.getPermCheck()), "§5* " + sender.getSender().getName() + " " + message, TBMCSystemChatEvent.BroadcastTarget.ALL);
}
}

View file

@ -10,7 +10,10 @@ import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.Random;
@CommandClass(modOnly = true)
@CommandClass(modOnly = true, helpText = {
"Snap",
"Perfectly balanced as all things should be."
})
public class SnapCommand extends ICommand2MC {
@Command2.Subcommand
public void def(CommandSender sender) {

View file

@ -2,6 +2,7 @@ package buttondevteam.chat.commands.ucmds;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabComplete;
import org.bukkit.command.CommandSender;
@CommandClass(modOnly = false, helpText = {
@ -10,7 +11,8 @@ import org.bukkit.command.CommandSender;
})
public final class HelpCommand extends UCommandBase {
@Command2.Subcommand
public boolean def(CommandSender sender, @Command2.TextArg @Command2.OptionalArg String topicOrCommand) {
public boolean def(CommandSender sender, @Command2.TextArg @Command2.OptionalArg
@CustomTabComplete({"commands", "chat", "colors"}) String topicOrCommand) {
if (topicOrCommand == null) {
sender.sendMessage(new String[]{
"§6---- Chroma Help ----",
@ -25,33 +27,33 @@ public final class HelpCommand extends UCommandBase {
}
if (topicOrCommand.equalsIgnoreCase("chat"))
sender.sendMessage(new String[]{"§6---- Chat features ----",
"- [g] Channel identifier: Click it to copy message", "-- [g]: Global chat (/g)",
"- [g] Channel identifier: Click it to copy message", "-- [g] Global chat (/g)",
"-- [TC] Town chat (/tc)", "-- [NC] Nation chat (/nc)",
"- Playernames: Hover over them to get some player info",
"-- Respect: This is the number of paid respects divided by eliglble deaths. This is a reference to CoD:AW's \"Press F to pay respects\""});
"-- Respect: This is the number of paid respects divided by eligible deaths. This is a reference to CoD:AW's \"Press F to pay respects\""});
else if (topicOrCommand.equalsIgnoreCase("commands")) {
sender.sendMessage(getManager().getCommandsText());
} else if (topicOrCommand.equalsIgnoreCase("colors")) {
sender.sendMessage(new String[]{"§6---- Chat colors/formats ----", //
"Tellraw name - Code | Tellraw name - Code", //
"§0black - &0 | §1dark_blue - &1", //
"§2dark_green - &2 | §3dark_aqua - &3", //
"§4dark_red - &4 | §5dark_purple - &5", //
"§6gold - &6 | §7gray - &7", //
"§8dark_gray - &8 | §9blue - &9", //
"§agreen - &a | §baqua - &b", //
"§cred - &c | §dlight_purple - &d", //
"§eyellow - &e | §fwhite - &f", //
"§rreset - &r | §kk§robfuscated - &k", //
"§lbold - &l | §mstrikethrough - &m", //
"§nunderline - &n | §oitalic - &o", //
"Tellraw name - Code | Tellraw name - Code", //
"§0black - &0§r | §1dark_blue - &1§r", //
"§2dark_green - &2§r | §3dark_aqua - &3§r", //
"§4dark_red - &4§r | §5dark_purple - &5§r", //
"§6gold - &6§r | §7gray - &7§r", //
"§8dark_gray - &8§r | §9blue - &9§r", //
"§agreen - &a§r | §baqua - &b§r", //
"§cred - &c§r | §dlight_purple - &d§r", //
"§eyellow - &e§r | §fwhite - &f§r", //
"§rreset - &r | §kk§robfuscated - &k§r", //
"§lbold - &l§r | §mstrikethrough - &m§r", //
"§nunderline - &n§r | §oitalic - &o§r", //
"The format codes in tellraw should be used like \"italic\":\"true\""}); //
} else {
String[] text = getManager().getHelpText(topicOrCommand);
if (text == null)
sender.sendMessage(
new String[]{"§cError: Command not found: " + topicOrCommand,
"Usage example: /u accept --> /u help u accept"});
String[] text = getManager().getCommandNode(topicOrCommand).getData().getHelpText(sender); // TODO: This only works for the main command, not subcommands
if (text == null) // TODO: Null check for command node
sender.sendMessage(
new String[]{"§cError: Command not found: " + topicOrCommand,
"Usage example: /u accept --> /u help u accept"});
else
sender.sendMessage(text);
}

View file

@ -5,9 +5,9 @@ import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import lombok.RequiredArgsConstructor;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import buttondevteam.lib.player.ChromaGamerBase;
import lombok.val;
import org.bukkit.command.CommandSender;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@ -15,7 +15,7 @@ import java.util.function.Function;
import java.util.stream.Stream;
@CommandClass(helpText = {
"§6--- Chat History ----", //
"Chat History", //
"Returns the last 10 messages the player can see." //
})
public class HistoryCommand extends UCommandBase {
@ -25,22 +25,22 @@ public class HistoryCommand extends UCommandBase {
private static final HashMap<String, LinkedList<HistoryEntry>> messages = new HashMap<>();
@Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg String channel) {
public boolean def(ChromaGamerBase sender, @Command2.OptionalArg String channel) {
return showHistory(sender, channel);
}
public static boolean showHistory(CommandSender sender, String channel) {
if (!PluginMain.Instance.storeChatHistory().get()) {
public static boolean showHistory(ChromaGamerBase sender, String channel) {
if (!PluginMain.Instance.storeChatHistory.get()) {
sender.sendMessage("§6Chat history is disabled");
return true;
}
Function<Channel, LinkedList<HistoryEntry>> getThem = ch -> messages.get(ch.ID + "_" + ch.getGroupID(sender)); //If can't see, groupID is null, and that shouldn't be in the map
Function<Channel, LinkedList<HistoryEntry>> getThem = ch -> messages.get(ch.getIdentifier() + "_" + ch.getGroupID(sender)); //If can't see, groupID is null, and that shouldn't be in the map
sender.sendMessage("§6---- Chat History ----");
Stream<Channel> stream;
if (channel == null) {
stream = Channel.getChannels();
} else {
Optional<Channel> och = Channel.getChannels().filter(chan -> chan.ID.equalsIgnoreCase(channel)).findAny();
Optional<Channel> och = Channel.getChannels().filter(chan -> chan.getIdentifier().equalsIgnoreCase(channel)).findAny();
if (!och.isPresent()) {
sender.sendMessage("§cChannel not found. Use the ID, for example: /u history g");
return true;
@ -54,7 +54,7 @@ public class HistoryCommand extends UCommandBase {
for (int i = Math.max(0, arr.length - 10); i < arr.length; i++) {
HistoryEntry e = arr[i];
val cm = e.chatMessage;
sender.sendMessage("[" + e.channel.DisplayName().get() + "] " + cm.getSender().getName() + ": " + cm.getMessage());
sender.sendMessage("[" + e.channel.displayName.get() + "] " + cm.getUser().getName() + ": " + cm.getMessage());
sent.set(true);
}
}
@ -63,22 +63,23 @@ public class HistoryCommand extends UCommandBase {
return true;
}
@RequiredArgsConstructor
private static class HistoryEntry {
/**
* System.nanoTime()
*/
private final long timestamp;
private final ChatMessage chatMessage;
private final Channel channel;
@CustomTabCompleteMethod(param = "channel")
public Iterable<String> def() {
return Channel.getChannels().map(Channel::getIdentifier)::iterator;
}
/**
* @param timestamp System.nanoTime()
*/
private record HistoryEntry(long timestamp, ChatMessage chatMessage, Channel channel) {
}
public static void addChatMessage(ChatMessage chatMessage, Channel channel) {
if (!PluginMain.Instance.storeChatHistory().get()) return;
if (!PluginMain.Instance.storeChatHistory.get()) return;
val groupID = channel.getGroupID(chatMessage.getPermCheck());
if (groupID == null) return; //Just to be sure
synchronized (messages) {
var ll = messages.computeIfAbsent(channel.ID + "_" + groupID, k -> new LinkedList<>()); //<-- TIL
var ll = messages.computeIfAbsent(channel.getIdentifier() + "_" + groupID, k -> new LinkedList<>()); //<-- TIL
ll.add(new HistoryEntry(System.nanoTime(), chatMessage, channel)); //Adds as last element
while (ll.size() > 10)
ll.remove(); //Removes the first element

View file

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

View file

@ -13,7 +13,7 @@ public class DebugCommand extends AdminCommandBase {
public static boolean DebugMode = false;
@Command2.Subcommand
public boolean def(CommandSender sender, String alias, String[] args) {
public boolean def(CommandSender sender) {
sender.sendMessage("§eDebug mode " + ((DebugMode = !DebugMode) ? "§aenabled." : "§cdisabled."));
return true;
}

View file

@ -1,10 +1,7 @@
package buttondevteam.chat.components.announce;
import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.formatter.ChatProcessing;
import buttondevteam.chat.components.formatter.FormatterComponent;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent;
import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
@ -13,6 +10,12 @@ import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.ClickEvent.Action.SUGGEST_COMMAND;
import static net.kyori.adventure.text.event.ClickEvent.clickEvent;
import static net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT;
import static net.kyori.adventure.text.event.HoverEvent.hoverEvent;
@CommandClass(modOnly = true)
@RequiredArgsConstructor
public class AnnounceCommand extends UCommandBase {
@ -24,7 +27,7 @@ public class AnnounceCommand extends UCommandBase {
})
public boolean add(CommandSender sender, @Command2.TextArg String text) {
String finalmessage = text.replace('&', '§');
component.announceMessages().get().add(finalmessage);
component.announceMessages.get().add(finalmessage);
sender.sendMessage("§bAnnouncement added.§r");
return true;
}
@ -38,9 +41,9 @@ public class AnnounceCommand extends UCommandBase {
String finalmessage1 = text.replace('&', '§');
if (index > 100)
return false;
while (component.announceMessages().get().size() <= index)
component.announceMessages().get().add("");
component.announceMessages().get().set(index, finalmessage1);
while (component.announceMessages.get().size() <= index)
component.announceMessages.get().add("");
component.announceMessages.get().set(index, finalmessage1);
sender.sendMessage("Announcement edited.");
return true;
}
@ -53,21 +56,19 @@ public class AnnounceCommand extends UCommandBase {
sender.sendMessage("§bList of announce messages:§r");
sender.sendMessage("§bFormat: [index] message§r");
int i = 0;
for (String message : component.announceMessages().get()) {
for (String message : component.announceMessages.get()) {
String msg = "[" + i++ + "] " + message;
//noinspection SuspiciousMethodCalls
if (!ComponentManager.isEnabled(FormatterComponent.class) || !Bukkit.getOnlinePlayers().contains(sender)) {
sender.sendMessage(msg);
continue;
}
String json = ChatProcessing.toJson(new TellrawPart(msg)
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, "Click to edit"))
.setClickEvent(TellrawEvent.create(TellrawEvent.ClickAction.SUGGEST_COMMAND,
"/" + getCommandPath() + " edit " + (i - 1) + " " + message.replace('§', '&'))));
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName() + " " + json);
sender.sendMessage(text(msg)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Click to edit")))
.clickEvent(clickEvent(SUGGEST_COMMAND, "/" + getCommandPath() + " edit " + (i - 1) + " " + message.replace('§', '&'))));
}
sender.sendMessage("§bCurrent wait time between announcements: "
+ component.announceTime().get() / 60 / 1000 + " minute(s)§r");
+ component.announceTime.get() / 60 / 1000 + " minute(s)§r");
return true;
}
@ -76,7 +77,7 @@ public class AnnounceCommand extends UCommandBase {
"This command removes an announcement"
})
public boolean remove(CommandSender sender, int index) {
val msgs = component.announceMessages().get();
val msgs = component.announceMessages.get();
if (index < 0 || index > msgs.size()) return false;
msgs.remove(index);
sender.sendMessage("Announcement removed.");
@ -88,7 +89,7 @@ public class AnnounceCommand extends UCommandBase {
"This command sets the time between the announcements"
})
public boolean settime(CommandSender sender, int minutes) {
component.announceTime().set(minutes * 60 * 1000);
component.announceTime.set(minutes * 60 * 1000);
sender.sendMessage("Time set between announce messages to " + minutes + " minutes");
return true;
}

View file

@ -4,28 +4,28 @@ import buttondevteam.chat.PluginMain;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.ListConfigData;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.architecture.config.IListConfigData;
import buttondevteam.lib.chat.TBMCChatAPI;
import org.bukkit.Bukkit;
import java.util.Collections;
/**
* Displays the configured messages at the set interval when someone is online.
*/
@ComponentMetadata(enabledByDefault = false)
public class AnnouncerComponent extends Component<PluginMain> implements Runnable {
/**
* The messages to display to players.
*/
public ListConfigData<String> announceMessages() {
return getConfig().getListData("announceMessages");
}
public IListConfigData<String> announceMessages = getConfig().getListData("announceMessages", Collections.emptyList());
/**
* The time in milliseconds between the messages. Use /u announce settime to set minutes.
*/
public ConfigData<Integer> announceTime() {
return getConfig().getData("announceTime", 15 * 60 * 1000);
}
public IConfigData<Integer> announceTime = getConfig().getData("announceTime", 15 * 60 * 1000);
private TBMCSystemChatEvent.BroadcastTarget target;
@ -35,15 +35,15 @@ public class AnnouncerComponent extends Component<PluginMain> implements Runnabl
public void run() {
while (isEnabled()) {
try {
Thread.sleep(announceTime().get());
Thread.sleep(announceTime.get());
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
if (Bukkit.getOnlinePlayers().size() == 0) continue; //Don't post to Discord if nobody is on
if (announceMessages().get().size() > AnnounceMessageIndex) {
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, announceMessages().get().get(AnnounceMessageIndex), target);
if (announceMessages.get().size() > AnnounceMessageIndex) {
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, announceMessages.get().get(AnnounceMessageIndex), target);
AnnounceMessageIndex++;
if (AnnounceMessageIndex == announceMessages().get().size())
if (AnnounceMessageIndex == announceMessages.get().size())
AnnounceMessageIndex = 0;
}
}

View file

@ -2,12 +2,10 @@ package buttondevteam.chat.components.appendext;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.chat.*;
import buttondevteam.lib.player.ChromaGamerBase;
import lombok.val;
import org.bukkit.command.CommandSender;
import java.lang.reflect.Method;
import java.util.HashMap;
@ -21,16 +19,16 @@ import java.util.function.Consumer;
public class AppendTextComponent extends Component<PluginMain> {
private Map<String, IHaveConfig> appendTexts;
private ConfigData<String[]> helpText(IHaveConfig config) {
return config.getData("helpText", () -> new String[]{
private IConfigData<String[]> helpText(IHaveConfig config) {
return config.getData("helpText", new String[]{
"Tableflip", //
"This command appends a tableflip after your message", //
"Or just makes you tableflip", //
});
}
private ConfigData<String> appendedText(IHaveConfig config) {
return config.getData("appendedText", () -> "tableflip");
private IConfigData<String> appendedText(IHaveConfig config) {
return config.getData("appendedText", "tableflip");
}
@Override
@ -68,7 +66,7 @@ public class AppendTextComponent extends Component<PluginMain> {
});
appendedText(conf).set("( ͡° ͜ʖ ͡°)");
});
map.put("ww", conf -> {
map.put("waitwhat", conf -> {
helpText(conf).set(new String[]{
"Wait what", //
"Wait what" //
@ -101,9 +99,9 @@ public class AppendTextComponent extends Component<PluginMain> {
}
@Command2.Subcommand
public void def(CommandSender sender, @Command2.OptionalArg @Command2.TextArg String message) {
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, ChromaGamerBase.getFromSender(sender),
(message == null ? "" : message + " ") + appendedText).fromCommand(true).build());
public void def(Command2MCSender sender, @Command2.OptionalArg @Command2.TextArg String message) {
TBMCChatAPI.sendChatMessage(ChatMessage.builder(sender.getSender(),
(message == null ? "" : message + " ") + appendedText).fromCommand(true).permCheck(sender.getPermCheck()).build());
}
@Override

View file

@ -2,23 +2,26 @@ 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;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
import lombok.val;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.GameMode;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT;
import static net.kyori.adventure.text.event.HoverEvent.hoverEvent;
/**
* 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 moving.
*/
@ComponentMetadata(enabledByDefault = false)
public class ChatOnlyComponent extends Component<PluginMain> implements Listener {
@ -34,21 +37,20 @@ public class ChatOnlyComponent extends Component<PluginMain> implements Listener
}
@EventHandler
public void playerJoin(TBMCPlayerJoinEvent event) {
public void playerJoin(PlayerJoinEvent event) {
val p = event.getPlayer();
val cp = event.GetPlayer().asPluginPlayer(ChatPlayer.class);
val cp = TBMCPlayer.getPlayer(p.getUniqueId(), ChatPlayer.class);
if (cp.ChatOnly || p.getGameMode().equals(GameMode.SPECTATOR)) {
cp.ChatOnly = false;
p.setGameMode(GameMode.SURVIVAL);
}
}
public static void tellrawCreate(ChatPlayer mp, TellrawPart json) {
if(!ComponentManager.isEnabled(ChatOnlyComponent.class))
public static void tellrawCreate(ChatPlayer mp, TextComponent.Builder json) {
if (!ComponentManager.isEnabled(ChatOnlyComponent.class))
return;
if (mp != null && mp.ChatOnly) {
json.addExtra(new TellrawPart("[C]")
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, "Chat only")));
json.append(text("[C]").hoverEvent(hoverEvent(SHOW_TEXT, text("Chat only"))));
}
}

View file

@ -1,6 +1,7 @@
package buttondevteam.chat.components.chatonly;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.player.TBMCPlayer;
@ -8,13 +9,14 @@ import org.bukkit.GameMode;
import org.bukkit.entity.Player;
@CommandClass(modOnly = false, helpText = {
"§6---- Chat-only mode ----", //
"Chat-only mode", //
"This mode makes you invincible but unable to move, teleport or interact with the world in any way", //
"It was designed for chat clients", //
"Once enabled, the only way of disabling it is by relogging to the server" //
})
public final class ChatonlyCommand extends ICommand2MC {
@Command2.Subcommand
public boolean def(Player player) {
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
p.ChatOnly = true;

View file

@ -24,21 +24,21 @@ public class AcceptCommand extends UCommandBase {
@Command2.Subcommand
public boolean def(Player player, @Command2.OptionalArg String username) {
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
if (username == null && p.UserNames().size() > 1) {
if (username == null && p.UserNames.get().size() > 1) {
player.sendMessage("§9Multiple users commented your name. §bPlease pick one using /u accept <username>");
StringBuilder sb = new StringBuilder();
sb.append("§6Usernames:");
for (String name : p.UserNames())
for (String name : p.UserNames.get())
sb.append(" ").append(name);
player.sendMessage(sb.toString());
return true;
}
if (p.FlairState().get().equals(FlairStates.NoComment) || p.UserNames().size() == 0) {
if (p.FlairState.get().equals(FlairStates.NoComment) || p.UserNames.get().size() == 0) {
player.sendMessage("§cError: You need to write your username to the reddit thread§r");
player.sendMessage(component.flairThreadURL().get());
player.sendMessage(component.flairThreadURL.get());
return true;
}
if (username != null && !p.UserNames().contains(username)) {
if (username != null && !p.UserNames.get().contains(username)) {
player.sendMessage("§cError: Unknown name: " + username + "§r");
return true;
}
@ -47,14 +47,14 @@ public class AcceptCommand extends UCommandBase {
return true;
}
if ((username != null ? username : p.UserNames().get(0)).equals(p.UserName().get())) {
if ((username != null ? username : p.UserNames.get().get(0)).equals(p.UserName.get())) {
player.sendMessage("§cYou already have this user's flair.§r");
return true;
}
if (username != null)
p.UserName().set(username);
p.UserName.set(username);
else
p.UserName().set(p.UserNames().get(0));
p.UserName.set(p.UserNames.get().get(0));
player.sendMessage("§bObtaining flair...");
p.Working = true;
@ -65,22 +65,19 @@ public class AcceptCommand extends UCommandBase {
try {
component.DownloadFlair(mp);
} catch (Exception e) {
TBMCCoreAPI.SendException(
"An error occured while downloading flair for " + player.getCustomName() + "!", e);
player.sendMessage(
"Sorry, but an error occured while trying to get your flair. Please contact a mod.");
TBMCCoreAPI.SendException("An error occured while downloading flair for " + player.getCustomName() + "!", e, component);
player.sendMessage("Sorry, but an error occured while trying to get your flair. Please contact a mod.");
mp.Working = false;
return;
}
if (mp.FlairState().get().equals(FlairStates.Commented)) {
player.sendMessage(
"Sorry, but your flair isn't recorded. Please ask an admin to set it for you. Also, prepare a comment on /r/thebutton, if possible.");
if (mp.FlairState.get().equals(FlairStates.Commented)) {
player.sendMessage("Sorry, but your flair isn't recorded. Please ask an admin to set it for you. Also, prepare a comment on /r/thebutton, if possible.");
mp.Working = false;
return;
}
String flair = mp.GetFormattedFlair();
mp.FlairState().set(FlairStates.Accepted);
mp.FlairState.set(FlairStates.Accepted);
FlairComponent.ConfirmUserMessage(mp);
player.sendMessage("§bYour flair has been set:§r " + flair);
mp.Working = false;

View file

@ -5,23 +5,18 @@ import buttondevteam.chat.PluginMain;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.HashSet;
import java.util.Set;
/**
* This component checks a specific Reddit thread every 10 seconds for comments such as "IGN: NorbiPeti" to link Reddit accounts and to determine their /r/thebutton flair.
@ -32,9 +27,7 @@ public class FlairComponent extends Component<PluginMain> {
/**
* The Reddit thread to check for account connections. Re-enable the component if this was empty.
*/
ConfigData<String> flairThreadURL() {
return getConfig().getData("flairThreadURL", "");
}
IConfigData<String> flairThreadURL = getConfig().getData("flairThreadURL", "");
/**
* <p>
@ -44,7 +37,7 @@ public class FlairComponent extends Component<PluginMain> {
* It's used because normally it has to load all associated player files every time to read the flair state
* </p>
*/
private Set<String> PlayersWithFlairs = new HashSet<>();
private final Set<String> PlayersWithFlairs = new HashSet<>();
@Override
protected void enable() {
@ -61,9 +54,9 @@ public class FlairComponent extends Component<PluginMain> {
private void FlairGetterThreadMethod() {
int errorcount = 0;
while (isEnabled() && flairThreadURL().get().length() > 0) {
while (isEnabled() && flairThreadURL.get().length() > 0) {
try {
String body = TBMCCoreAPI.DownloadString(flairThreadURL().get() + ".json?limit=1000");
String body = TBMCCoreAPI.DownloadString(flairThreadURL.get() + ".json?limit=1000");
JsonArray json = new JsonParser().parse(body).getAsJsonArray().get(1).getAsJsonObject().get("data")
.getAsJsonObject().get("children").getAsJsonArray();
for (Object obj : json) {
@ -83,20 +76,19 @@ public class FlairComponent extends Component<PluginMain> {
ign = ign.trim();
if (PlayersWithFlairs.contains(ign))
continue;
try (ChatPlayer mp = TBMCPlayerBase.getFromName(ign, ChatPlayer.class)) { // Loads player file
if (mp == null)
continue;
/*
* if (!JoinedBefore(mp, 2015, 6, 5)) continue;
*/
if (!mp.UserNames().contains(author))
mp.UserNames().add(author);
if (mp.FlairState().get().equals(FlairStates.NoComment)) {
mp.FlairState().set(FlairStates.Commented);
ConfirmUserMessage(mp);
}
PlayersWithFlairs.add(ign); // Don't redownload even if flair isn't accepted
ChatPlayer mp = TBMCPlayerBase.getFromName(ign, ChatPlayer.class); // Loads player file
if (mp == null)
continue;
/*
* if (!JoinedBefore(mp, 2015, 6, 5)) continue;
*/
if (!mp.UserNames.get().contains(author))
mp.UserNames.get().add(author);
if (mp.FlairState.get().equals(FlairStates.NoComment)) {
mp.FlairState.set(FlairStates.Commented);
ConfirmUserMessage(mp);
}
PlayersWithFlairs.add(ign); // Don't redownload even if flair isn't accepted
}
} catch (Exception e) {
errorcount++;
@ -104,7 +96,7 @@ public class FlairComponent extends Component<PluginMain> {
errorcount = 0;
if (!e.getMessage().contains("Server returned HTTP response code")
&& !(e instanceof UnknownHostException))
TBMCCoreAPI.SendException("Error while getting flairs from Reddit!", e);
TBMCCoreAPI.SendException("Error while getting flairs from Reddit!", e, this);
}
}
try {
@ -113,11 +105,12 @@ public class FlairComponent extends Component<PluginMain> {
Thread.currentThread().interrupt();
}
}
}
void DownloadFlair(ChatPlayer mp) throws IOException {
String[] flairdata = TBMCCoreAPI
.DownloadString("http://karmadecay.com/thebutton-data.php?users=" + mp.UserName().get())
.DownloadString("http://karmadecay.com/thebutton-data.php?users=" + mp.UserName.get())
.replace("\"", "").split(":");
String flair;
if (flairdata.length > 1)
@ -129,12 +122,12 @@ public class FlairComponent extends Component<PluginMain> {
flairclass = flairdata[2];
else
flairclass = "unknown";
SetFlair(mp, flair, flairclass, mp.UserName().get());
SetFlair(mp, flair, flairclass, mp.UserName.get());
}
private static void SetFlair(ChatPlayer p, String text, String flairclass, String username) {
p.UserName().set(username);
p.FlairState().set(FlairStates.Recognised);
private void SetFlair(ChatPlayer p, String text, String flairclass, String username) {
p.UserName.set(username);
p.FlairState.set(FlairStates.Recognised);
switch (flairclass) {
case "cheater":
p.SetFlair(Short.parseShort(text), true);
@ -150,9 +143,9 @@ public class FlairComponent extends Component<PluginMain> {
p.SetFlair(ChatPlayer.FlairTimeCantPress);
}
} catch (Exception e) {
p.FlairState().set(FlairStates.Commented); // Flair unknown
p.FlairState.set(FlairStates.Commented); // Flair unknown
p.SetFlair(ChatPlayer.FlairTimeNone);
TBMCCoreAPI.SendException("Error while checking join date for player " + p.PlayerName() + "!", e);
TBMCCoreAPI.SendException("Error while checking join date for player " + p.getPlayerName() + "!", e, this);
}
return;
default:
@ -166,7 +159,7 @@ public class FlairComponent extends Component<PluginMain> {
}
private static boolean JoinedBefore(ChatPlayer mp, int year, int month, int day) throws Exception {
URL url = new URL("https://www.reddit.com/u/" + mp.UserName());
/*URL url = new URL("https://www.reddit.com/u/" + mp.UserName());
URLConnection con = url.openConnection();
con.setRequestProperty("User-Agent", "TheButtonAutoFlair");
InputStream in = con.getInputStream();
@ -180,13 +173,14 @@ public class FlairComponent extends Component<PluginMain> {
joindate = joindate.split("T")[0];
Date date = parserSDF.parse(joindate);
return date.before(new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("UTC")).setDate(year, month, day)
.build().getTime());
.build().getTime());*/
return true;
}
public static void ConfirmUserMessage(ChatPlayer mp) {
Player p = Bukkit.getPlayer(mp.getUUID());
if (mp.FlairState().get().equals(FlairStates.Commented) && p != null)
if (mp.UserNames().size() > 1)
Player p = Bukkit.getPlayer(mp.getUniqueId());
if (mp.FlairState.get().equals(FlairStates.Commented) && p != null)
if (mp.UserNames.get().size() > 1)
p.sendMessage(
"§9Multiple Reddit users commented your name. You can select with /u accept.§r §6Type /u accept or /u ignore§r");
else

View file

@ -15,18 +15,18 @@ public final class IgnoreCommand extends UCommandBase {
@Command2.Subcommand
public boolean def(Player player) {
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
if (p.FlairState().get().equals(FlairStates.Accepted)) {
if (p.FlairState.get().equals(FlairStates.Accepted)) {
player.sendMessage("§cYou can only ignore the \"write your name in the thread\" message.");
return true;
}
if (p.FlairState().get().equals(FlairStates.Commented)) {
if (p.FlairState.get().equals(FlairStates.Commented)) {
player.sendMessage("Sorry, but your flair isn't recorded. Please ask a mod to set it for you.");
return true;
}
if (!p.FlairState().get().equals(FlairStates.Ignored)) {
p.FlairState().set(FlairStates.Ignored);
if (!p.FlairState.get().equals(FlairStates.Ignored)) {
p.FlairState.set(FlairStates.Ignored);
p.SetFlair(ChatPlayer.FlairTimeNone);
p.UserName().set("");
p.UserName.set("");
player.sendMessage("§bYou have ignored the message.§r");
} else
player.sendMessage("§cYou already ignored the message.§r");

View file

@ -10,7 +10,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@CommandClass(helpText = {
"§6---- Set flair -----", "Set a flair for a player",
"Set flair", "Set a flair for a player",
"Usage: /u admin setflair <player> <flairtime (or non-presser, cant-press, none)> <cheater(true/false)> [username]",
"Example 1: /u admin setflair NorbiPeti 19 false NorbiPeti --> orange (19s)",
"Example 2: /u admin setflair iie 0 true asde --> purple (0s)"
@ -35,22 +35,21 @@ public class SetFlairCommand extends AdminCommandBase {
ft = Short.parseShort(flairtime);
} catch (Exception e) {
sender.sendMessage(
"§cFlairtime must be a number, \"non-presser\", \"cant-press\" or \"none\". Run without args to see usage.");
"§cFlairtime must be a number, \"non-presser\", \"cant-press\" or \"none\". Run without args to see usage.");
return true;
}
}
ChatPlayer mp = TBMCPlayerBase.getPlayer(p.getUniqueId(), ChatPlayer.class);
mp.SetFlair(ft, cheater);
mp.FlairState().set(FlairStates.Accepted);
mp.FlairState.set(FlairStates.Accepted);
if (username == null)
mp.UserName().set("");
mp.UserName.set("");
else {
mp.UserName().set(username);
if (!mp.UserNames().contains(username))
mp.UserNames().add(username);
mp.UserName.set(username);
if (!mp.UserNames.get().contains(username))
mp.UserNames.get().add(username);
}
sender.sendMessage(
"§bThe flair has been set. Player: " + mp.PlayerName() + " Flair: " + mp.GetFormattedFlair() + "§r");
sender.sendMessage("§bThe flair has been set. Player: " + mp.getPlayerName() + " Flair: " + mp.GetFormattedFlair() + "§r");
return true;
}

View file

@ -12,20 +12,22 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.chat.listener.PlayerListener;
import buttondevteam.core.ComponentManager;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TellrawSerializableEnum;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.earth2me.essentials.User;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.val;
import net.ess3.api.events.AfkStatusChangeEvent;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
@ -38,16 +40,23 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.ClickEvent.suggestCommand;
import static net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT;
import static net.kyori.adventure.text.event.HoverEvent.hoverEvent;
import static net.kyori.adventure.text.format.NamedTextColor.*;
public class ChatProcessing {
private static final Pattern HASHTAG_PATTERN = Pattern.compile("#(\\w+)");
private static final Pattern URL_PATTERN = Pattern.compile("(http[\\w:/?=$\\-_.+!*'(),&]+(?:#[\\w]+)?)");
private static final Pattern MASKED_LINK_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 NamedTextColor[] RainbowPresserColors = new NamedTextColor[]{RED, GOLD, YELLOW, GREEN,
BLUE, DARK_PURPLE};
private static final Pattern WORD_PATTERN = Pattern.compile("\\S+");
private static final Pattern GREENTEXT_PATTERN = Pattern.compile("^>(?:.|\\s)*");
private static boolean pingedconsole = false;
private static ArrayList<MatchProviderBase> commonFormatters = Lists.newArrayList(
private static final 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()),
@ -58,8 +67,8 @@ public class ChatProcessing {
cf.setHoverText(match);
return match;
}).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)
new StringMatchProvider("nullMention", FormatSettings.builder().color(DARK_RED).build(), true, "null"), // Properly added a bug as a feature
new StringMatchProvider("consolePing", FormatSettings.builder().color(AQUA)
.onmatch((match, builder, section) -> {
if (!pingedconsole) {
System.out.print("\007");
@ -68,19 +77,19 @@ public class ChatProcessing {
return "@console";
}).build(), true, "@console"),
new StringMatchProvider("cyan", FormatSettings.builder().color(Color.Aqua).build(), true, "cyan"), // #55
new RangeMatchProvider("code", "`", FormatSettings.builder().color(Color.DarkGray).build()),
new StringMatchProvider("cyan", FormatSettings.builder().color(AQUA).build(), true, "cyan"), // #55
new RangeMatchProvider("code", "`", FormatSettings.builder().color(DARK_GRAY).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 "";
return "[MISSING LINK]"; //Doesn't actually happen, because of the regex
builder.setOpenlink(link);
return text;
}).build()),
new RegexMatchProvider("url", URL_PATTERN, FormatSettings.builder().underlined(true).openlink("$1").build()),
new RegexMatchProvider("hashtag", HASHTAG_PATTERN, FormatSettings.builder().color(Color.Blue).openlink("https://twitter.com/hashtag/$1").build()),
new StringMatchProvider("someone", FormatSettings.builder().color(Color.Aqua)
new RegexMatchProvider("hashtag", HASHTAG_PATTERN, FormatSettings.builder().color(BLUE).openlink("https://twitter.com/hashtag/$1").build()),
new StringMatchProvider("someone", FormatSettings.builder().color(AQUA)
.onmatch((match, builder, section) -> {
if (Bukkit.getOnlinePlayers().size() == 0) return match;
var players = ImmutableList.copyOf(Bukkit.getOnlinePlayers());
@ -88,12 +97,8 @@ public class ChatProcessing {
var player = players.get(playerC);
playPingSound(player, ComponentManager.getIfEnabled(FormatterComponent.class));
return "@someone (" + player.getDisplayName() + "§r)";
}).build(), true, "@someone"));
private static Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(TellrawSerializableEnum.class, new TellrawSerializer.TwEnum())
.registerTypeHierarchyAdapter(Collection.class, new TellrawSerializer.TwCollection())
.registerTypeAdapter(Boolean.class, new TellrawSerializer.TwBool())
.registerTypeAdapter(boolean.class, new TellrawSerializer.TwBool()).disableHtmlEscaping().create();
}).build(), true, "@someone"),
new RegexMatchProvider("greentext", GREENTEXT_PATTERN, FormatSettings.builder().color(GREEN).build()));
private static final String[] testPlayers = {"Koiiev", "iie", "Alisolarflare", "NorbiPeti", "Arsen_Derby_FTW", "carrot_lynx"};
private ChatProcessing() {
@ -101,70 +106,80 @@ public class ChatProcessing {
public static boolean ProcessChat(TBMCChatEvent e, FormatterComponent component) {
Channel channel = e.getChannel();
CommandSender sender = e.getSender();
ChromaGamerBase cuser = e.getUser();
String message = e.getMessage();
long processstart = System.nanoTime();
Player player = (sender instanceof Player ? (Player) sender : null);
Player player = (cuser instanceof TBMCPlayerBase ? ((TBMCPlayerBase) cuser).getPlayer() : null);
User user = PluginMain.essentials.getUser(player);
if (player != null) {
user.updateActivity(true); //Could talk in a private channel, so broadcast
if (player != null && PluginMain.essentials.getSettings().cancelAfkOnInteract()) {
user.updateActivity(true, AfkStatusChangeEvent.Cause.CHAT); //Could talk in a private channel, so broadcast
if (user.isMuted())
return true;
}
doFunStuff(sender, e, message);
final String channelidentifier = getChannelID(channel, e.getOrigin());
PluginMain.Instance.getServer().getConsoleSender()
.sendMessage(String.format("%s <%s§r> %s", channelidentifier, getSenderName(sender, player), message));
if (Bukkit.getOnlinePlayers().size() == 0) return false; //Don't try to send to nobody (errors on 1.14)
ChatPlayer mp;
if (player != null)
mp = TBMCPlayerBase.getPlayer(player.getUniqueId(), ChatPlayer.class);
else //Due to the online player map, getPlayer() can be more efficient than getAs()
mp = e.getUser().getAs(ChatPlayer.class); //May be null
Color colormode = channel.Color().get();
if (mp != null && mp.OtherColorMode != null)
colormode = mp.OtherColorMode;
if (message.startsWith(">"))
colormode = Color.Green;
// If greentext, ignore channel or player colors
if (mp != null) {
if (System.nanoTime() - mp.LastMessageTime < 1000L * 1000L * component.minTimeBetweenMessages.get()) { //0.1s by default
cuser.sendMessage("§cYou are sending messages too quickly!");
return true;
}
mp.LastMessageTime = System.nanoTime();
}
//DimensionManager.a()
//IRegistry.ae
//Bukkit.createWorld()
//MinecraftServer.reload()
//IRegistry
//CraftServer
doFunStuff(cuser, e, message);
final String channelidentifier = getChannelID(channel, e.getOrigin());
PluginMain.Instance.getServer().getConsoleSender()
.sendMessage(String.format("%s <%s§r> %s", channelidentifier, cuser.getName(), message));
if (Bukkit.getOnlinePlayers().size() == 0) return false; //Don't try to send to nobody (errors on 1.14)
TextColor colormode = NAMES.value(channel.color.get().getName());
boolean colorModeChanged = false;
if (mp != null && mp.OtherColorMode != null) {
colormode = NAMES.value(mp.OtherColorMode.getName());
colorModeChanged = true;
}
ArrayList<MatchProviderBase> formatters;
if (component.allowFormatting().get()) {
formatters = addFormatters(e::shouldSendTo, component);
if (colormode == channel.Color().get() && mp != null && mp.RainbowPresserColorMode) { // Only overwrite channel color
if (component.allowFormatting.get()) {
formatters = addFormatters(sender -> e.shouldSendTo(ChromaGamerBase.getFromSender(sender)), component);
if (colorModeChanged && mp.RainbowPresserColorMode) { // Only overwrite channel color
createRPC(colormode, formatters);
}
pingedconsole = false; // Will set it to true onmatch (static constructor)
} else
formatters = Lists.newArrayList();
TellrawPart json = createTellraw(sender, message, player, mp, e.getUser(), channelidentifier, e.getOrigin());
TextComponent.Builder builder = createEmptyMessageLine(cuser, message, player, channelidentifier, e.getOrigin());
long combinetime = System.nanoTime();
ChatFormatter.Combine(formatters, message, json, component.getConfig(), FormatSettings.builder().color(colormode).build());
ChatFormatter.Combine(formatters, message, builder, component.getConfig(), FormatSettings.builder().color(colormode).build());
combinetime = System.nanoTime() - combinetime;
String jsonstr = toJson(json);
if (jsonstr.length() >= 32767) {
sender.sendMessage(
"§cError: Message too long. Try shortening it, or remove hashtags and other formatting.");
return true;
}
DebugCommand.SendDebugMessage(jsonstr);
try {
if (!channel.isGlobal()) {
String senderGroup = e.getGroupID(sender);
String senderGroup = e.getGroupID(cuser);
if (senderGroup == null) { // Never send messages if the group is null
sender.sendMessage("§cYou don't have permission to send this message or something went wrong");
cuser.sendMessage("§cYou don't have permission to send this message or something went wrong");
return true;
}
val tc = ComponentManager.getIfEnabled(TownyComponent.class);
if (tc != null) tc.handleSpiesInit(channel, json, ChatProcessing::toJson);
Consumer<Player> spyConsumer = null;
if (tc != null)
spyConsumer = tc.handleSpiesInit(channel, builder);
for (Player p : Bukkit.getOnlinePlayers()) {
final String group;
if (player != null
@ -172,17 +187,18 @@ public class ChatProcessing {
group = null; // Don't send the message to them
else
group = VanillaUtils.getGroupIfChatOn(p, e);
if (senderGroup.equals(group))
VanillaUtils.tellRaw(p, jsonstr);
else if (tc != null) tc.handleSpies(channel, p);
if (senderGroup.equals(group)) {
p.sendMessage(builder.build());
if (tc != null) spyConsumer.accept(p);
}
//Only sends if didn't send normally
}
} else
for (Player p : Bukkit.getOnlinePlayers())
VanillaUtils.tellRaw(p, jsonstr);
p.sendMessage(builder.build());
} catch (Exception ex) {
TBMCCoreAPI.SendException("An error occured while sending a chat message!", ex);
sender.sendMessage("§cAn error occured while sending the message.");
TBMCCoreAPI.SendException("An error occured while sending a chat message!", ex, PluginMain.Instance);
cuser.sendMessage("§cAn error occured while sending the message.");
return true;
}
DebugCommand.SendDebugMessage(
@ -191,7 +207,7 @@ public class ChatProcessing {
return false;
}
static void createRPC(Color colormode, ArrayList<MatchProviderBase> formatters) {
static void createRPC(TextColor colormode, ArrayList<MatchProviderBase> formatters) {
final AtomicInteger rpc = new AtomicInteger(0);
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)]);
@ -199,46 +215,29 @@ public class ChatProcessing {
}).build()));
}
public static String toJson(TellrawPart json) {
return gson.toJson(json);
}
static TellrawPart createTellraw(CommandSender sender, String message, @Nullable Player player,
@Nullable ChatPlayer mp, @Nullable ChromaGamerBase cg, final String channelidentifier,
String origin) {
TellrawPart json = new TellrawPart("");
ChatOnlyComponent.tellrawCreate(mp, json); //TODO: Make nice API
json.addExtra(
new TellrawPart(channelidentifier)
.setHoverEvent(
TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT,
new TellrawPart((ChatUtils.MCORIGIN.equals(origin) ? "" : "From " + origin + "n")
+ "Copy message").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(TellrawEvent.ClickAction.SUGGEST_COMMAND, message)));
if (PluginMain.permission.has(sender, "tbmc.badge.diamond"))
json.addExtra(new TellrawPart("[P]").setColor(Color.Aqua).setBold(true)
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, "Diamond Patreon supporter")));
else if (PluginMain.permission.has(sender, "tbmc.badge.gold"))
json.addExtra(new TellrawPart("[P]").setColor(Color.Gold).setBold(true)
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, "Gold Patreon supporter")));
json.addExtra(new TellrawPart(" <"));
TellrawPart hovertp = new TellrawPart("");
if (cg != null)
hovertp.addExtra(new TellrawPart(cg.getInfo(ChromaGamerBase.InfoTarget.MCHover)));
json.addExtra(new TellrawPart(getSenderName(sender, player))
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, hovertp)));
json.addExtra(new TellrawPart("> "));
static TextComponent.Builder createEmptyMessageLine(ChromaGamerBase user, String message, @Nullable Player player,
final String channelidentifier, String origin) {
val json = text();
ChatOnlyComponent.tellrawCreate(user.getAs(ChatPlayer.class), json);
val channelHover = (ChatUtils.MCORIGIN.equals(origin) ? "" : "From " + origin + "\n") + "Copy message";
json.append(text(channelidentifier)
.hoverEvent(hoverEvent(SHOW_TEXT, text(channelHover).color(BLUE))).clickEvent(suggestCommand(message)));
if (player != null) {
if (PluginMain.permission.has(player, "tbmc.badge.diamond")) // TODO: Cross-platform permissions
json.append(text("[P]").color(AQUA).decorate(TextDecoration.BOLD)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Diamond Patreon supporter"))));
else if (PluginMain.permission.has(player, "tbmc.badge.gold"))
json.append(text("[P]").color(GOLD).decorate(TextDecoration.BOLD)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Gold Patreon supporter"))));
}
json.append(text(" <"));
json.append(text(user.getName()).hoverEvent(hoverEvent(SHOW_TEXT, text(user.getInfo(ChromaGamerBase.InfoTarget.MCHover)))));
json.append(text("> "));
return json;
}
private static String getSenderName(CommandSender sender, Player player) {
if (player == null)
return sender.getName();
return player.getDisplayName();
}
static String getChannelID(Channel channel, String origin) {
return ("[" + (ChatUtils.MCORIGIN.equals(origin) ? "" : "§8" + origin.substring(0, 1) + "§r|") + channel.DisplayName().get())
return ("[" + (ChatUtils.MCORIGIN.equals(origin) ? "" : "§8" + origin.charAt(0) + "§r|") + channel.displayName.get())
+ "]";
}
@ -247,7 +246,7 @@ public class ChatProcessing {
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")) {
if ((nottest = Bukkit.getOnlinePlayers().size() > 0) || ChromaUtils.isTest()) {
String[] names;
if (nottest)
names = Bukkit.getOnlinePlayers().stream().filter(canSee).map(CommandSender::getName).toArray(String[]::new);
@ -266,12 +265,12 @@ public class ChatProcessing {
};
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)
formatters.add(0, new StringMatchProvider("name", FormatSettings.builder().color(AQUA)
.onmatch((match, builder, section) -> {
Player p = Bukkit.getPlayer(match);
Optional<String> pn = nottest ? Optional.empty()
: Arrays.stream(testPlayers).filter(tp -> tp.equalsIgnoreCase(match)).findAny();
if (nottest ? p == null : !pn.isPresent()) {
if (nottest ? p == null : pn.isEmpty()) {
error.accept("Error: Can't find player " + match + " but was reported as online.");
return "§c" + match + "§r";
}
@ -284,7 +283,7 @@ public class ChatProcessing {
}).build(), true, names));
if (nicknames.length > 0) //Add as first so it handles special characters
formatters.add(0, new StringMatchProvider("nickname", FormatSettings.builder().color(Color.Aqua)
formatters.add(0, new StringMatchProvider("nickname", FormatSettings.builder().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()));
@ -305,15 +304,15 @@ public class ChatProcessing {
}
private static void playPingSound(Player p, @Nullable FormatterComponent component) {
if (component == null || component.notificationSound().get().length() == 0)
if (component == null || component.notificationSound.get().length() == 0)
p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 0.5f); // TODO: Airhorn
else
p.playSound(p.getLocation(), component.notificationSound().get(), 1.0f,
component.notificationPitch().get());
p.playSound(p.getLocation(), component.notificationSound.get(), 1.0f,
component.notificationPitch.get());
}
static void doFunStuff(CommandSender sender, TBMCChatEventBase event, String message) {
static void doFunStuff(ChromaGamerBase user, TBMCChatEventBase event, String message) {
val fc = ComponentManager.getIfEnabled(FunComponent.class);
if (fc != null) fc.onChat(sender, event, message);
if (fc != null) fc.onChat(user, event, message);
}
}

View file

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

View file

@ -2,15 +2,24 @@ package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.chat.Color;
import lombok.val;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.ClickEvent.Action.OPEN_URL;
import static net.kyori.adventure.text.event.ClickEvent.clickEvent;
import static net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT;
import static net.kyori.adventure.text.event.HoverEvent.hoverEvent;
/**
* 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
* A {@link MatchProvider} finds where the given {@link FormatSettings} need to be applied. {@link ChatFormatter#Combine(List, String, TextComponent.Builder, 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.
*
@ -26,7 +35,7 @@ public final class ChatFormatter {
}
//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) {
public static synchronized void Combine(List<MatchProviderBase> formatters, String str, TextComponent.Builder tp, IHaveConfig config, FormatSettings defaults) {
/*
* A global formatter is no longer needed
*/
@ -120,22 +129,25 @@ public final class ChatFormatter {
sortSections(sections);
continue;
} else if (firstSection.End >= lastSection.Start && firstSection.Start <= lastSection.End) {
int firstSectEnd = firstSection.End;
firstSection.End = lastSection.Start - 1;
int lastSectEnd = lastSection.End;
FormattedSection section = new FormattedSection(firstSection.Settings, lastSection.Start, lastSectEnd,
int middleStart = lastSection.Start;
// |----|-- |------|
// --|----| -|----|-
int middleEnd = Math.min(lastSection.End, firstSection.End);
int lastSectEnd = Math.max(lastSection.End, firstSection.End);
FormattedSection section = new FormattedSection(firstSection.Settings, middleStart, middleEnd,
firstSection.Matches);
section.Settings.copyFrom(lastSection.Settings);
section.Matches.addAll(lastSection.Matches);
sections.add(i, section);
if (firstSectEnd > lastSection.End) { //Copy first section info to last as the lastSection initially cuts the firstSection in half
if (firstSection.End > 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;
firstSection.End = middleStart - 1;
lastSection.Start = middleEnd + 1;
lastSection.End = lastSectEnd;
Predicate<FormattedSection> removeIfNeeded = s -> {
if (s.Start < 0 || s.End < 0 || s.Start > s.End) {
@ -149,11 +161,11 @@ public final class ChatFormatter {
DebugCommand.SendDebugMessage("To sections");
if (!removeIfNeeded.test(firstSection)) {
DebugCommand.SendDebugMessage(" 1:" + firstSection + "");
DebugCommand.SendDebugMessage(" 1:" + firstSection);
ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
}
if (!removeIfNeeded.test(section)) {
DebugCommand.SendDebugMessage(" 2:" + section + "");
DebugCommand.SendDebugMessage(" 2:" + section);
ChatFormatUtils.sendMessageWithPointer(str, section.Start, section.End);
}
if (!removeIfNeeded.test(lastSection)) {
@ -163,21 +175,11 @@ public final class ChatFormatter {
i = 0;
}
sortSections(sections);
if (i == 0) continue;
for (int j = i - 1; j <= i + 1; j++) {
if (j < sections.size() && sections.get(j).End < sections.get(j).Start) {
DebugCommand.SendDebugMessage("Removing section: " + sections.get(j));
ChatFormatUtils.sendMessageWithPointer(str, sections.get(j).Start, sections.get(j).End);
sections.remove(j);
j--;
i = 0;
}
}
}
}
private static void applySections(String str, TellrawPart tp, ArrayList<FormattedSection> sections, ArrayList<int[]> remchars) {
TellrawPart lasttp = null;
private static void applySections(String str, TextComponent.Builder tp, ArrayList<FormattedSection> sections, ArrayList<int[]> remchars) {
TextComponent lasttp = null;
String lastlink = null;
for (FormattedSection section : sections) {
DebugCommand.SendDebugMessage("Applying section: " + section);
@ -185,21 +187,9 @@ public final class ChatFormatter {
int start = section.Start, end = section.End;
DebugCommand.SendDebugMessage("Start: " + start + " - End: " + 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());*/
val rci = remchars.stream().filter(rc -> (rc[0] <= start && rc[1] >= start)
|| (rc[0] >= start && rc[1] <= end)
|| (rc[0] <= end && rc[1] >= end)).sorted(Comparator.comparingInt(rc -> rc[0] * 10000 + rc[1])).toArray(int[][]::new);
/*if (rcs.isPresent())
s = rcs.get()[1] + 1;
if (rce.isPresent())
e = rce.get()[0] - 1;
DebugCommand.SendDebugMessage("After RC - Start: " + s + " - End: " + e);
if (e - s < 0) { //e-s==0 means the end char is the same as start char, so one char message
DebugCommand.SendDebugMessage("Skipping section because of remchars (length would be " + (e - s + 1) + ")");
continue;
}*/
DebugCommand.SendDebugMessage("Applying RC: " + Arrays.stream(rci).map(Arrays::toString).collect(Collectors.joining(", ", "[", "]")));
originaltext = str.substring(start, end + 1);
val sb = new StringBuilder(originaltext);
@ -212,50 +202,53 @@ 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
TellrawPart newtp = new TellrawPart("");
var settings = section.Settings;
DebugCommand.SendDebugMessage("Applying settings: " + settings);
if (lasttp != null && hasSameDecorations(lasttp, settings) && Objects.equals(lastlink, settings.openlink)
&& settings.onmatch == null) { // The onmatch function can change the settings
DebugCommand.SendDebugMessage("This part has the same properties as the previous one, combining.");
lasttp = lasttp.content(lasttp.content() + originaltext);
continue; //Combine parts with the same properties
}
TextComponent.@NotNull Builder newtp = text();
if (settings.onmatch != null)
originaltext = settings.onmatch.apply(originaltext, settings, section);
if (settings.color != null)
newtp.setColor(settings.color);
newtp.color(settings.color);
if (settings.bold)
newtp.setBold(true);
newtp.decorate(TextDecoration.BOLD);
if (settings.italic)
newtp.setItalic(true);
newtp.decorate(TextDecoration.ITALIC);
if (settings.underlined)
newtp.setUnderlined(true);
newtp.decorate(TextDecoration.UNDERLINED);
if (settings.strikethrough)
newtp.setStrikethrough(true);
newtp.decorate(TextDecoration.STRIKETHROUGH);
if (settings.obfuscated)
newtp.setObfuscated(true);
newtp.decorate(TextDecoration.OBFUSCATED);
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()
&& newtp.isUnderlined() == lasttp.isUnderlined()
&& newtp.isStrikethrough() == lasttp.isStrikethrough()
&& newtp.isObfuscated() == lasttp.isObfuscated()
&& Objects.equals(openlink, lastlink)) {
DebugCommand.SendDebugMessage("This part has the same properties as the previous one, combining.");
lasttp.setText(lasttp.getText() + originaltext);
continue; //Combine parts with the same properties
}
newtp.hoverEvent(hoverEvent(SHOW_TEXT, text(settings.hoverText)));
if (lasttp != null) tp.append(lasttp);
lastlink = openlink;
newtp.setText(originaltext);
newtp.content(originaltext);
if (openlink != null && openlink.length() > 0) {
newtp.setClickEvent(TellrawEvent.create(TellrawEvent.ClickAction.OPEN_URL,
(section.Matches.size() > 0 ? openlink.replace("$1", section.Matches.get(0)) : openlink)))
.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)));
if (section.Matches.size() > 0)
openlink = openlink.replace("$1", section.Matches.get(0));
newtp.clickEvent(clickEvent(OPEN_URL, openlink)).hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(NamedTextColor.BLUE)));
}
tp.addExtra(newtp);
lasttp = newtp;
lasttp = newtp.build();
}
if (lasttp != null) tp.append(lasttp);
}
private static boolean hasSameDecorations(TextComponent c1, FormatSettings settings) {
return Objects.equals(c1.color(), settings.color)
&& c1.hasDecoration(TextDecoration.BOLD) == settings.bold
&& c1.hasDecoration(TextDecoration.ITALIC) == settings.italic
&& c1.hasDecoration(TextDecoration.UNDERLINED) == settings.underlined
&& c1.hasDecoration(TextDecoration.STRIKETHROUGH) == settings.strikethrough
&& c1.hasDecoration(TextDecoration.OBFUSCATED) == settings.obfuscated;
}
private static void sortSections(ArrayList<FormattedSection> sections) {

View file

@ -1,8 +1,8 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.lib.chat.Color;
import lombok.Builder;
import lombok.Data;
import net.kyori.adventure.text.format.TextColor;
/**
* Describes how a matched section of the message should look. May be combined with other format settings.
@ -15,7 +15,7 @@ public class FormatSettings {
boolean underlined;
boolean strikethrough;
boolean obfuscated;
Color color;
TextColor color;
ChatFormatter.TriFunc<String, FormatSettings, FormattedSection, String> onmatch;
String openlink;
String hoverText;
@ -27,7 +27,6 @@ public class FormatSettings {
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));
}
}

View file

@ -1,7 +1,7 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.architecture.config.IConfigData;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -31,7 +31,7 @@ public abstract class MatchProviderBase implements MatchProvider {
resetSubclass();
}
ConfigData<Boolean> enabled(IHaveConfig config) {
IConfigData<Boolean> enabled(IHaveConfig config) {
return config.getData(name + ".enabled", true);
}

View file

@ -12,7 +12,7 @@ public class StringMatchProvider extends MatchProviderBase {
@ToString.Exclude
private final FormatSettings settings;
private int nextIndex = 0;
private boolean ignoreCase;
private final boolean ignoreCase;
/**
* Matches the given strings in the order given

View file

@ -6,9 +6,9 @@ import java.io.Serializable;
public final class TellrawEvent<T extends TellrawEvent.Action> implements Serializable {
private static final long serialVersionUID = -1681364161210561505L;
private transient boolean hoverEvent;
private T action;
private Object value;
private final transient boolean hoverEvent;
private final T action;
private final Object value;
private TellrawEvent(T action, String value) {
this.hoverEvent = action instanceof HoverAction;
@ -44,7 +44,7 @@ public final class TellrawEvent<T extends TellrawEvent.Action> implements Serial
public enum ClickAction implements Action {
OPEN_URL("open_url"), RUN_COMMAND("run_command"), SUGGEST_COMMAND("suggest_command");
private String action;
private final String action;
ClickAction(String action) {
this.action = action;
@ -58,8 +58,8 @@ public final class TellrawEvent<T extends TellrawEvent.Action> implements Serial
public enum HoverAction implements Action {
SHOW_TEXT("show_text"), SHOW_ITEM("show_item"), SHOW_ACHIEVEMENT("show_achievement"), SHOW_ENTITY(
"show_entity");
private String action;
"show_entity");
private final String action;
HoverAction(String action) {
this.action = action;

View file

@ -14,7 +14,7 @@ public final class TellrawPart implements Serializable {
private boolean underlined;
private boolean strikethrough;
private boolean obfuscated;
private List<TellrawPart> extra = new ArrayList<>();
private final List<TellrawPart> extra = new ArrayList<>();
private String text;
private TellrawEvent<TellrawEvent.HoverAction> hoverEvent;
private TellrawEvent<TellrawEvent.ClickAction> clickEvent;

View file

@ -2,15 +2,13 @@ package buttondevteam.chat.components.fun;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.*;
import buttondevteam.lib.player.TBMCPlayer;
import org.bukkit.entity.Player;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
@CommandClass(path = "u c", helpText = {
"Rainbow mode",
@ -57,4 +55,9 @@ public class CCommand extends ICommand2MC {
}
return true;
}
@CustomTabCompleteMethod(param = "color")
public Iterable<String> def() {
return Stream.concat(Stream.of("off"), Arrays.stream(Color.values()).map(Color::getName))::iterator;
}
}

View file

@ -18,46 +18,47 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@CommandClass(helpText = {
"§6---- F Top ----", //
"F Top", //
"Shows the respect leaderboard" //
})
public class FTopCommand extends ICommand2MC {
private final File playerdir = new File(TBMCPlayerBase.TBMC_PLAYERS_DIR);
private ChatPlayer[] cached;
private long lastcache = 0;
private final File playerdir = new File("TBMC/players/");
private ChatPlayer[] cached;
private long lastcache = 0;
public boolean def(CommandSender sender, @Command2.OptionalArg int page) {
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
if (cached == null || lastcache < System.nanoTime() - 60000000000L) { // 1m - (no guarantees of nanoTime's relation to 0, so we need the null check too)
cached = Arrays.stream(Objects.requireNonNull(playerdir.listFiles())).sequential()
.filter(f -> f.getName().length() > 4)
.map(f -> {
try {
return TBMCPlayerBase.getPlayer(
UUID.fromString(f.getName().substring(0, f.getName().length() - 4)), ChatPlayer.class);
} catch (Exception e) {
return null;
}
})
.filter(Objects::nonNull)
.sorted((cp1, cp2) -> Double.compare(cp2.getF(), cp1.getF()))
.toArray(ChatPlayer[]::new); // TODO: Properly implement getting all players
lastcache = System.nanoTime();
}
int i;
try {
i = page<1?1:page;
@Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg int page) {
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
if (cached == null || lastcache < System.nanoTime() - 60000000000L) { // 1m - (no guarantees of nanoTime's relation to 0, so we need the null check too)
cached = Arrays.stream(Objects.requireNonNull(playerdir.listFiles())).sequential()
.filter(f -> f.getName().length() > 4)
.map(f -> {
try {
return TBMCPlayerBase.getPlayer(
UUID.fromString(f.getName().substring(0, f.getName().length() - 4)), ChatPlayer.class);
} catch (Exception e) {
return null;
}
})
.filter(Objects::nonNull)
.sorted((cp1, cp2) -> Double.compare(cp2.getF(), cp1.getF()))
.toArray(ChatPlayer[]::new); // TODO: Properly implement getting all players
lastcache = System.nanoTime();
}
int i;
try {
i = page < 1 ? 1 : page;
} catch (Exception e) {
i = 1;
}
val ai = new AtomicInteger();
sender.sendMessage("§6---- Top Fs ----");
sender.sendMessage(Arrays.stream(cached).skip((i - 1) * 10).limit(i * 10)
.map(cp -> String.format("%d. %s - %f.2", ai.incrementAndGet(), cp.PlayerName().get(), cp.getF()))
.collect(Collectors.joining("\n")));
});
return true;
}
val ai = new AtomicInteger();
sender.sendMessage("§6---- Top Fs ----");
sender.sendMessage(Arrays.stream(cached).skip((i - 1) * 10L).limit(i * 10L)
.map(cp -> String.format("%d. %s - %f.2", ai.incrementAndGet(), cp.getPlayerName(), cp.getF()))
.collect(Collectors.joining("\n")));
});
return true;
}
}

View file

@ -3,18 +3,19 @@ package buttondevteam.chat.components.fun;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.PluginMain;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCCommandPreprocessEvent;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.architecture.config.IListConfigData;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.google.common.collect.Lists;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@ -36,34 +37,29 @@ public class FunComponent extends Component<PluginMain> implements Listener {
private boolean ActiveF = false;
private ChatPlayer FPlayer = null;
private BukkitTask Ftask = null;
private HashSet<CommandSender> Fs = new HashSet<>();
private final HashSet<ChromaGamerBase> Fs = new HashSet<>();
private UnlolCommand command;
private TBMCSystemChatEvent.BroadcastTarget unlolTarget;
private TBMCSystemChatEvent.BroadcastTarget fTarget;
private final Random random = new Random();
/**
* The strings that count as laughs, see unlol.
*/
private ConfigData<String[]> laughStrings() {
return getConfig().getData("laughStrings", () -> new String[]{"xd", "lel", "lawl", "kek", "lmao", "hue", "hah", "rofl"});
}
private final IListConfigData<String> laughStrings = getConfig().getListData("laughStrings", Lists.newArrayList("xd", "lel", "lawl", "kek", "lmao", "hue", "hah", "rofl"));
/**
* The "Press F to pay respects" meme in Minecraft. It will randomly appear on player death and keep track of how many "F"s are said in chat.
* You can hover over a player's name to see their respect.
*/
private ConfigData<Boolean> respect() {
return getConfig().getData("respect", true);
}
private final IConfigData<Boolean> respect = getConfig().getData("respect", true);
/**
* This is an inside joke on our server.
* It keeps track of laughs (lols and what's defined in laughStrings) and if someone does /unlol or /unlaugh it will unlaugh the last person who laughed.
* Which also blinds the laughing person for a few seconds. This action can only be performed once per laugh.
*/
private ConfigData<Boolean> unlol() {
return getConfig().getData("unlol", true);
}
private final IConfigData<Boolean> unlol = getConfig().getData("unlol", true);
@Override
protected void enable() {
@ -72,7 +68,7 @@ public class FunComponent extends Component<PluginMain> implements Listener {
val pc = new PressCommand();
registerCommand(pc);
registerListener(pc);
registerCommand(command=new UnlolCommand(unlolTarget));
registerCommand(command = new UnlolCommand(unlolTarget));
registerListener(this);
registerCommand(new FTopCommand());
registerCommand(new OpmeCommand());
@ -85,20 +81,21 @@ public class FunComponent extends Component<PluginMain> implements Listener {
}
public void onChat(CommandSender sender, TBMCChatEventBase event, String message) {
public void onChat(ChromaGamerBase sender, TBMCChatEventBase event, String message) {
if (ActiveF && !Fs.contains(sender) && message.equalsIgnoreCase("F"))
Fs.add(sender);
if (unlol().get()) {
if (unlol.get()) {
String msg = message.toLowerCase();
val lld = new UnlolCommand.LastlolData(sender, event, System.nanoTime());
boolean add;
if (add = msg.contains("lol"))
boolean add = msg.contains("lol");
if (add)
lld.setLolornot(true);
else {
String[] laughs = laughStrings().get();
val laughs = laughStrings.get();
for (String laugh : laughs) {
if (add = msg.contains(laugh)) {
add = msg.contains(laugh);
if (add) {
lld.setLolornot(false);
break;
}
@ -112,13 +109,13 @@ public class FunComponent extends Component<PluginMain> implements Listener {
@EventHandler
public void onPlayerDeath(PlayerDeathEvent e) {
// MinigamePlayer mgp = Minigames.plugin.pdata.getMinigamePlayer(e.getEntity());
if (e.getDeathMessage().length() > 0 && respect().get() && new Random().nextBoolean()) { // Don't store Fs for NPCs
if (e.getDeathMessage().length() > 0 && respect.get() && random.nextBoolean()) { // Don't store Fs for NPCs
Runnable tt = () -> {
if (ActiveF) {
ActiveF = false;
if (FPlayer != null && FPlayer.FCount().get() < Integer.MAX_VALUE - 1)
FPlayer.FCount().set(FPlayer.FCount().get() + Fs.size());
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL,
if (FPlayer != null && FPlayer.FCount.get() < Integer.MAX_VALUE - 1)
FPlayer.FCount.set(FPlayer.FCount.get() + Fs.size());
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL,
"§b" + Fs.size() + " " + (Fs.size() == 1 ? "person" : "people")
+ " paid their respects.§r", fTarget);
Fs.clear();
@ -131,8 +128,8 @@ public class FunComponent extends Component<PluginMain> implements Listener {
ActiveF = true;
Fs.clear();
FPlayer = TBMCPlayer.getPlayer(e.getEntity().getUniqueId(), ChatPlayer.class);
FPlayer.FDeaths().set(FPlayer.FDeaths().get() + 1);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL,
FPlayer.FDeaths.set(FPlayer.FDeaths.get() + 1);
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL,
"§bPress F to pay respects.§r", fTarget);
Ftask = Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, tt, 15 * 20);
}
@ -140,14 +137,14 @@ public class FunComponent extends Component<PluginMain> implements Listener {
@EventHandler
public void onPlayerLeave(PlayerQuitEvent event) {
if (unlol().get())
command.Lastlol.values().removeIf(lld -> lld.getLolowner().equals(event.getPlayer()));
if (unlol.get())
command.Lastlol.values().removeIf(lld -> lld.getLolowner().equals(ChromaGamerBase.getFromSender(event.getPlayer())));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onCommandPreprocess(TBMCCommandPreprocessEvent event) {
if (event.isCancelled()) return;
if (!unlol().get()) return;
if (!unlol.get()) return;
final String cmd = event.getMessage();
// We don't care if we have arguments
if (cmd.toLowerCase().startsWith("/un")) {
@ -155,7 +152,14 @@ public class FunComponent extends Component<PluginMain> implements Listener {
if (ht.getName().equalsIgnoreCase(cmd))
return;
}
if (PluginMain.permission.has(event.getSender(), "chroma.unanything")) {
val user = event.getSender();
if (!(user instanceof TBMCPlayerBase)) {
// TODO: Cross-platform permission check; console is not supported here
user.sendMessage("§cError: You must be a player to use this command.");
return;
}
val player = ((TBMCPlayerBase) user).getPlayer();
if (player != null && PluginMain.permission.has(player, "chroma.unanything")) {
event.setCancelled(true);
int index = cmd.lastIndexOf(' ');
if (index == -1) {
@ -168,10 +172,9 @@ public class FunComponent extends Component<PluginMain> implements Listener {
event.getSender().sendMessage("§cError: Player not found. (/un" + s + " <player>)");
return;
}
val user = ChromaGamerBase.getFromSender(event.getSender());
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 10 * 20, 5, false, false));
val chan = user.channel().get();
TBMCChatAPI.SendSystemMessage(chan, chan.getRTR(event.getSender()), ChromaUtils.getDisplayName(event.getSender()) + " un" + s
val chan = user.getChannel().get();
TBMCChatAPI.SendSystemMessage(chan, chan.getRTR(event.getSender()), event.getSender().getName() + " un" + s
+ "'d " + target.getDisplayName(), unlolTarget);
}
}

View file

@ -1,6 +1,7 @@
package buttondevteam.chat.components.fun;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.restart.RestartComponent;
import buttondevteam.core.component.restart.ScheduledRestartCommand;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.ScheduledServerRestartEvent;
@ -8,13 +9,18 @@ import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCChatAPI;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.HashSet;
@CommandClass
@CommandClass(helpText = {
"Press",
"This command resets the restart countdown if it's active. Can only be used once per player.",
"It's based on Reddit's /r/thebutton"
})
public class PressCommand extends ICommand2MC implements Listener {
private HashSet<CommandSender> pressers; //Will be cleared with this class on shutdown/disable
private ScheduledRestartCommand command;
@ -31,7 +37,7 @@ public class PressCommand extends ICommand2MC implements Listener {
return;
}
pressers.add(sender);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, String.format("§b-- %s §bpressed at %.0fs", ChromaUtils.getDisplayName(sender), command.getRestartCounter() / 20f), command.getComponent().getRestartBroadcast());
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, String.format("§b-- %s §bpressed at %.0fs", ChromaUtils.getDisplayName(sender), command.getRestartCounter() / 20f), ((RestartComponent) command.getComponent()).getRestartBroadcast());
command.setRestartCounter(startTicks);
}
@ -40,5 +46,7 @@ public class PressCommand extends ICommand2MC implements Listener {
command = event.getCommand();
pressers = new HashSet<>();
startTicks = event.getRestartTicks();
if (Bukkit.getOnlinePlayers().size() > 0)
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, "§b-- Do /press to reset the timer. You may only press once.", ((RestartComponent) command.getComponent()).getRestartBroadcast());
}
}

View file

@ -1,17 +1,16 @@
package buttondevteam.chat.components.fun;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayerBase;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
@ -20,8 +19,8 @@ import java.util.HashMap;
import java.util.Map;
@CommandClass(modOnly = false, helpText = {
"§6---- Unlol/unlaugh ----",
"This command is based on a joke between NorbiPeti and Ghostise",
"Unlol/unlaugh",
"This command is based on an inside joke",
"It will make the last person saying one of the recognized laugh strings blind for a few seconds",
"Note that you can only unlaugh laughs that weren't unlaughed before"
})
@ -33,18 +32,18 @@ public final class UnlolCommand extends ICommand2MC {
private final TBMCSystemChatEvent.BroadcastTarget target;
@Command2.Subcommand
public boolean def(CommandSender sender) {
public boolean def(ChromaGamerBase sender) {
LastlolData lol = Lastlol.values().stream().filter(lld -> lld.Chatevent.shouldSendTo(sender))
.max(Comparator.comparingLong(lld -> lld.Loltime)).orElse(null);
.max(Comparator.comparingLong(lld -> lld.Loltime)).orElse(null);
if (lol == null)
return true;
if (lol.Lolowner instanceof Player)
((Player) lol.Lolowner)
.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 2 * 20, 5, false, false));
String msg = ChromaUtils.getDisplayName(sender)
+ (lol.Lolornot ? " unlolled " : " unlaughed ")
+ ChromaUtils.getDisplayName(lol.Lolowner);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, msg, target);
if (lol.Lolowner instanceof TBMCPlayerBase) {
var player = ((TBMCPlayerBase) lol.Lolowner).getPlayer();
if (player != null)
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 2 * 20, 5, false, false));
}
String msg = sender.getName() + (lol.Lolornot ? " unlolled " : " unlaughed ") + lol.Lolowner.getName();
TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, msg, target);
Lastlol.remove(lol.Chatevent.getChannel());
return true;
}
@ -52,7 +51,7 @@ public final class UnlolCommand extends ICommand2MC {
@Data
public static class LastlolData {
private boolean Lolornot;
private final CommandSender Lolowner;
private final ChromaGamerBase Lolowner;
private final TBMCChatEventBase Chatevent;
private final long Loltime;
}

View file

@ -11,7 +11,6 @@ import com.palmergames.bukkit.towny.object.Town;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;
@ -28,8 +27,8 @@ public class NColorCommand extends UCommandBase {
Resident res;
Town town;
try {
if ((res = TownyComponent.TU.getResidentMap().get(player.getName().toLowerCase())) == null || !res.hasTown()
|| (town = res.getTown()) == null) {
if ((res = TownyComponent.dataSource.getResident(player.getName())) == null || !res.hasTown()
|| (town = res.getTown()) == null) {
player.sendMessage("§cYou need to be in a town.");
return true;
}
@ -41,7 +40,7 @@ public class NColorCommand extends UCommandBase {
//Don't add ~ for nicknames
if (!nameWithLines.replace("|", "").replace(":", "").equalsIgnoreCase(name)) {
player.sendMessage("§cThe name you gave doesn't match your name. Make sure to use "
+ name + "§c with added vertical lines (|) or colons (:).");
+ name + "§c with added vertical lines (|) or colons (:).");
return true;
}
String[] nameparts = nameWithLines.split("[|:]");
@ -50,22 +49,31 @@ public class NColorCommand extends UCommandBase {
player.sendMessage("§cYour town doesn't have a color set. The town mayor can set it using /u towncolor.");
return true;
}
if (nameparts.length < towncolors.length + 1) { //+1: Nation color
var component = TownColorComponent.getComponent();
byte nationColors = (byte) (component.useNationColors.get() ? 1 : 0);
if (nameparts.length < towncolors.length + nationColors) { //+1: Nation color
player.sendMessage("§cYou need more vertical lines (|) or colons (:) in your name. (Should have " + (towncolors.length - 1 + 1) + ")"); //Nation color
return true;
}
if (nameparts.length > (towncolors.length + 1) * 2) {
if (nameparts.length > (towncolors.length + nationColors) * 2) {
player.sendMessage("§cYou have waay too many vertical lines (|) or colons (:) in your name. (Should have " + (towncolors.length - 1 + 1) + ")");
return true;
}
if (nameparts.length > towncolors.length + 1) {
if (nameparts.length > towncolors.length + nationColors) {
player.sendMessage("§cYou have too many vertical lines (|) or colons (:) in your name. (Should have " + (towncolors.length - 1 + 1) + ")");
return true;
}
ChatPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class).NameColorLocations()
.set(new ArrayList<>(Arrays.stream(nameparts).map(String::length).collect(Collectors.toList()))); // No byte[], no TIntArrayList
TownColorComponent.updatePlayerColors(player);
player.sendMessage("§bName colors set: " + player.getDisplayName());
var cp = ChatPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
var list = Arrays.stream(nameparts).map(String::length).collect(Collectors.toList());
if (list.contains(0)) {
player.sendMessage("§cAll colors need to be represented in your name. (Use as Test|name|123 instead of |Testname123|)");
return true;
}
var clist = cp.NameColorLocations.get(); // No byte[], no TIntArrayList
clist.clear();
clist.addAll(list);
TownColorComponent.updatePlayerColors(player, cp);
player.sendMessage("§bName colors set: " + player.getDisplayName());
return true;
}
}

View file

@ -1,10 +1,11 @@
package buttondevteam.chat.components.towncolors;
import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.towncolors.admin.TownColorCommand;
import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident;
@ -19,20 +20,23 @@ import org.bukkit.entity.Player;
public class NationColorCommand extends UCommandBase {
@Command2.Subcommand
public boolean def(Player player, String color) {
Resident res;
if (!(TownyComponent.TU.getResidentMap().containsKey(player.getName().toLowerCase())
&& (res = TownyComponent.TU.getResidentMap().get(player.getName().toLowerCase())).isKing())) {
player.sendMessage("§cYou need to be the king of a nation to set it's colors.");
return true;
}
final Nation n;
String msg = "§cYou need to be the king of a nation to set it's colors.";
try {
n = res.getTown().getNation();
Resident res = TownyComponent.dataSource.getResident(player.getName());
if (!res.isKing()) {
player.sendMessage(msg);
return true;
}
final Nation n = res.getTown().getNation();
return buttondevteam.chat.components.towncolors.admin.NationColorCommand.SetNationColor(player, n, color);
} catch (NotRegisteredException e) {
TBMCCoreAPI.SendException("Failed to set nation color for player " + player + "!", e);
player.sendMessage("§cCouldn't find your town/nation... Error reported.");
player.sendMessage(msg);
return true;
}
return buttondevteam.chat.components.towncolors.admin.NationColorCommand.SetNationColor(player, n, color);
}
@CustomTabCompleteMethod(param = "color")
public Iterable<String> def() {
return TownColorCommand.tabcompleteColor();
}
}

View file

@ -2,9 +2,9 @@ package buttondevteam.chat.components.towncolors;
import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
@ -17,32 +17,35 @@ import org.bukkit.entity.Player;
"This command allows setting a color 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 but residents can override that with /u ncolor.", //
}) // TODO: /u u when annotation not present
})
@RequiredArgsConstructor
public class TownColorCommand extends UCommandBase {
private final TownColorComponent component;
@Command2.Subcommand
public boolean def(Player player, String... colornames) {
Resident res;
if (!(TownyComponent.TU.getResidentMap().containsKey(player.getName().toLowerCase())
&& (res = TownyComponent.TU.getResidentMap().get(player.getName().toLowerCase())).isMayor())) {
player.sendMessage("§cYou need to be the mayor of a town to set its colors.");
return true;
}
val cc = component.colorCount().get();
if (colornames.length > cc) {
player.sendMessage("You can only use " + cc + " color" + (cc > 1 ? "s" : "") + ".");
return true;
}
final Town t;
String msg = "§cYou need to be the mayor of a town to set its colors.";
try {
t = res.getTown();
Resident res = TownyComponent.dataSource.getResident(player.getName());
if (!res.isMayor()) {
player.sendMessage(msg);
return true;
}
val cc = component.colorCount.get();
if (colornames.length > cc) {
player.sendMessage("You can only use " + cc + " color" + (cc > 1 ? "s" : "") + ".");
return true;
}
final Town t = res.getTown();
return buttondevteam.chat.components.towncolors.admin.TownColorCommand.SetTownColor(player, t, colornames);
} catch (NotRegisteredException e) {
TBMCCoreAPI.SendException("Failed to set town color for player " + player + "!", e);
player.sendMessage("§cCouldn't find your town... Error reported.");
player.sendMessage(msg);
return true;
}
return buttondevteam.chat.components.towncolors.admin.TownColorCommand.SetTownColor(player, t, colornames);
}
@CustomTabCompleteMethod(param = "colornames")
public Iterable<String> def() {
return buttondevteam.chat.components.towncolors.admin.TownColorCommand.tabcompleteColor();
}
}

View file

@ -8,12 +8,13 @@ import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
import buttondevteam.lib.player.TBMCPlayer;
import com.earth2me.essentials.User;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident;
import lombok.Getter;
import lombok.val;
import org.bukkit.Bukkit;
@ -21,7 +22,9 @@ import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.Plugin;
import org.dynmap.towny.DTBridge;
@ -41,26 +44,22 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
/**
* Names lowercased
*/
public static Map<String, Color[]> TownColors = new HashMap<>();
public static final Map<String, Color[]> TownColors = new HashMap<>();
/**
* Names lowercased - nation color gets added to town colors when needed
*/
public static Map<String, Color> NationColor = new HashMap<>();
public static final Map<String, Color> NationColor = new HashMap<>();
/**
* The amount of town colors allowed. If more than one is used, players can change how many letters to be in a specific color using /u ncolor.
* The amount of town colors allowed. If more than one is used (or nation colors are enabled), players can change how many letters to be in a specific color using /u ncolor.
*/
public ConfigData<Byte> colorCount() {
return getConfig().getData("colorCount", (byte) 1, cc -> ((Integer) cc).byteValue(), Byte::intValue);
}
public final IConfigData<Byte> colorCount = getConfig().getData("colorCount", (byte) 1, cc -> ((Integer) cc).byteValue(), Byte::intValue);
/**
* If enabled, players will have a nation-defined color in addition to town colors, white by default.
* They can change how much of each color they want with this as well.
*/
public ConfigData<Boolean> useNationColors() {
return getConfig().getData("useNationColors", true);
}
public final IConfigData<Boolean> useNationColors = getConfig().getData("useNationColors", true);
@Getter
private static TownColorComponent component;
@ -72,7 +71,7 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
Consumer<ConfigurationSection> loadTC = cs -> TownColorComponent.TownColors.putAll(cs.getValues(true).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> ((List<String>) v.getValue()).stream()
.map(Color::valueOf).toArray(Color[]::new))));
boolean usenc = useNationColors().get();
boolean usenc = useNationColors.get();
Consumer<ConfigurationSection> loadNC = ncs ->
TownColorComponent.NationColor.putAll(ncs.getValues(true).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> Color.valueOf((String) v.getValue()))));
@ -85,17 +84,17 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
loadNC.accept(ncs);
}
TownColors.keySet().removeIf(t -> !TownyComponent.TU.getTownsMap().containsKey(t)); // Removes town colors for deleted/renamed towns
TownColors.keySet().removeIf(t -> TownyComponent.dataSource.getTown(t) == null); // Removes town colors for deleted/renamed towns
if (usenc)
NationColor.keySet().removeIf(n -> !TownyComponent.TU.getNationsMap().containsKey(n)); // Removes nation colors for deleted/renamed nations
NationColor.keySet().removeIf(n -> TownyComponent.dataSource.getNation(n) == null); // Removes nation colors for deleted/renamed nations
initDynmap();
registerCommand(new TownColorCommand(this));
if (useNationColors().get())
if (useNationColors.get())
registerCommand(new NationColorCommand());
registerCommand(new buttondevteam.chat.components.towncolors.admin.TownColorCommand());
if (useNationColors().get())
if (useNationColors.get())
registerCommand(new buttondevteam.chat.components.towncolors.admin.NationColorCommand());
registerCommand(new TCCount());
registerCommand(new NColorCommand());
@ -107,9 +106,10 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
protected void disable() {
getConfig().getConfig().createSection("towncolors", TownColors.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
v -> Arrays.stream(v.getValue()).map(Enum::toString).toArray(String[]::new))));
if (useNationColors().get())
if (useNationColors.get())
getConfig().getConfig().createSection("nationcolors", NationColor.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
v -> v.getValue().toString())));
getConfig().signalChange();
}
private void initDynmap() {
@ -119,16 +119,16 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
return;
for (val entry : TownColors.entrySet()) {
try {
val town = TownyComponent.TU.getTownsMap().get(entry.getKey());
val town = TownyComponent.dataSource.getTown(entry.getKey());
Nation nation;
Color nc;
if (!useNationColors().get())
if (!useNationColors.get())
nc = null;
else if (!town.hasNation() || (nation = town.getNation()) == null || (nc = NationColor.get(nation.getName().toLowerCase())) == null)
nc = Color.White;
setTownColor(dtp, buttondevteam.chat.components.towncolors.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue(), nc);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting town color for town " + entry.getKey() + "!", e);
TBMCCoreAPI.SendException("Error while setting town color for town " + entry.getKey() + "!", e, this);
}
}
});
@ -147,7 +147,7 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
DTBridge.setTownColor(dtp, town, c2i.apply(nationcolor == null ? colors[0] : nationcolor),
c2i.apply(colors.length > 1 && nationcolor != null ? colors[1] : colors[0]));
} catch (Exception e) {
TBMCCoreAPI.SendException("Failed to set town color for town " + town + "!", e);
TBMCCoreAPI.SendException("Failed to set town color for town " + town + "!", e, component);
}
}
@ -156,7 +156,7 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
if (nickname.contains("~")) //StartsWith doesn't work because of color codes
nickname = nickname.replace("~", ""); //It gets stacked otherwise
String name = ChatColor.stripColor(nickname); //Enforce "town colors" on non-members
val res = TownyComponent.TU.getResidentMap().get(player.getName().toLowerCase());
Resident res = TownyComponent.dataSource.getResident(player.getName());
if (res == null || !res.hasTown())
return name;
try {
@ -178,8 +178,8 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
len = name.length() / (clrs.length+1);
else
len = name.length() / clrs.length;*/
boolean usenc = component.useNationColors().get();
val nclar = cp.NameColorLocations().get();
boolean usenc = component.useNationColors.get();
val nclar = cp.NameColorLocations.get();
int[] ncl = nclar == null ? null : nclar.stream().mapToInt(Integer::intValue).toArray();
if (ncl != null && (Arrays.stream(ncl).sum() != name.length() || ncl.length != clrs.length + (usenc ? 1 : 0))) //+1: Nation color
ncl = null; // Reset if name length changed
@ -201,14 +201,14 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
/**
* Checks if the component is enabled
*/
public static void updatePlayerColors(Player player) { //Probably while ingame (/u ncolor)
public static void updatePlayerColors(Player player) { //Probably while ingame (/u ncolor - not anymore)
updatePlayerColors(player, ChatPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class));
}
/**
* Checks if the component is enabled
*/
private static void updatePlayerColors(Player player, ChatPlayer cp) { //Probably at join - nop, nicknames
public static void updatePlayerColors(Player player, ChatPlayer cp) { //Probably at join - nop, nicknames
if (!ComponentManager.isEnabled(TownColorComponent.class))
return;
User user = PluginMain.essentials.getUser(player);
@ -217,8 +217,8 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
cp.FlairUpdate(); //Update in list
}
@EventHandler
public void onPlayerJoin(TBMCPlayerJoinEvent event) {
updatePlayerColors(event.getPlayer(), event.GetPlayer().asPluginPlayer(ChatPlayer.class));
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(PlayerJoinEvent event) {
updatePlayerColors(event.getPlayer(), TBMCPlayer.getPlayer(event.getPlayer().getUniqueId(), ChatPlayer.class));
}
}

View file

@ -61,7 +61,7 @@ public class TownyListener implements Listener {
@EventHandler
public void onNationRename(RenameNationEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return;
if (!TownColorComponent.getComponent().useNationColors.get()) return;
val clrs = TownColorComponent.NationColor.remove(event.getOldName().toLowerCase());
if (clrs != null)
TownColorComponent.NationColor.put(event.getNation().getName().toLowerCase(), clrs);
@ -69,25 +69,25 @@ public class TownyListener implements Listener {
@EventHandler //Gets called on town load as well
public void onNationJoin(NationAddTownEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return;
if (!TownColorComponent.getComponent().useNationColors.get()) return;
updateTownMembers(event.getTown());
}
@EventHandler
public void onNationLeave(NationRemoveTownEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return;
if (!TownColorComponent.getComponent().useNationColors.get()) return;
updateTownMembers(event.getTown()); //The town still has it's colours
}
@EventHandler
public void onNationDelete(DeleteNationEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return;
if (!TownColorComponent.getComponent().useNationColors.get()) return;
TownColorComponent.NationColor.remove(event.getNationName().toLowerCase());
}
@EventHandler
public void onNationCreate(NewNationEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return;
if (!TownColorComponent.getComponent().useNationColors.get()) return;
Player p = Bukkit.getPlayer(event.getNation().getCapital().getMayor().getName());
if (p != null)
p.sendMessage("§6Use /u nationcolor to set a color for the nation.");

View file

@ -8,8 +8,10 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.TownyObject;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@ -21,7 +23,7 @@ import org.bukkit.command.CommandSender;
public class NationColorCommand extends AdminCommandBase {
@Command2.Subcommand
public boolean def(CommandSender sender, String nation, String color) {
final Nation n = TownyComponent.TU.getNationsMap().get(nation.toLowerCase());
final Nation n = TownyComponent.dataSource.getNation(nation);
if (n == null) {
sender.sendMessage("§cThe nation '" + nation + "' cannot be found.");
return true;
@ -29,6 +31,16 @@ public class NationColorCommand extends AdminCommandBase {
return SetNationColor(sender, n, color);
}
@CustomTabCompleteMethod(param = "color")
public Iterable<String> def(String nation) {
return TownColorCommand.tabcompleteColor();
}
@CustomTabCompleteMethod(param = "nation")
public Iterable<String> def() {
return TownyComponent.dataSource.getNations().stream().map(TownyObject::getName)::iterator;
}
public static boolean SetNationColor(CommandSender sender, Nation nation, String color) {
val c = TownColorCommand.getColorOrSendError(color, sender);
if (!c.isPresent()) return true;

View file

@ -15,7 +15,7 @@ public class TCCount extends AdminCommandBase {
@Command2.Subcommand
public boolean def(CommandSender sender, byte count) {
val comp = TownColorComponent.getComponent();
comp.colorCount().set(count);
comp.colorCount.set(count);
sender.sendMessage("Color count set to " + count);
return true;
}

View file

@ -7,7 +7,9 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.TownyObject;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@ -23,17 +25,31 @@ import java.util.stream.Collectors;
"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.", //
})
public class TownColorCommand extends AdminCommandBase { //TODO: Command path aliases
public class TownColorCommand extends AdminCommandBase {
@Command2.Subcommand
public boolean def(CommandSender sender, String town, String... colornames) {
if (!TownyComponent.TU.getTownsMap().containsKey(town.toLowerCase())) {
if (TownyComponent.dataSource.getTown(town) == null) {
sender.sendMessage("§cThe town '" + town + "' cannot be found.");
return true;
}
Town targetTown = TownyComponent.dataSource.getTown(town);
if (targetTown == null) {
sender.sendMessage("§cThe town '" + town + "' cannot be found.");
return true;
}
Town targetTown = TownyComponent.TU.getTownsMap().get(town.toLowerCase());
return SetTownColor(sender, targetTown, colornames);
}
@CustomTabCompleteMethod(param = "colornames")
public Iterable<String> def(String town) {
return TownColorCommand.tabcompleteColor();
}
@CustomTabCompleteMethod(param = "town")
public Iterable<String> def() {
return TownyComponent.dataSource.getTowns().stream().map(TownyObject::getName)::iterator;
}
public static boolean SetTownColor(CommandSender sender, Town town, String[] colors) {
Color[] clrs = new Color[colors.length];
for (int i = 0; i < colors.length; i++) {
@ -43,7 +59,7 @@ public class TownColorCommand extends AdminCommandBase { //TODO: Command path al
clrs[i] = c.get();
}
Color tnc;
boolean usenc = TownColorComponent.getComponent().useNationColors().get();
boolean usenc = TownColorComponent.getComponent().useNationColors.get();
if (usenc) {
try {
tnc = TownColorComponent.NationColor.get(town.getNation().getName().toLowerCase());
@ -56,7 +72,7 @@ public class TownColorCommand extends AdminCommandBase { //TODO: Command path al
Color nc;
if (usenc) {
try {
nc = TownColorComponent.NationColor.get(TownyComponent.TU.getTownsMap().get(other.getKey()).getNation().getName().toLowerCase());
nc = TownColorComponent.NationColor.get(TownyComponent.dataSource.getTown(other.getKey()).getNation().getName().toLowerCase());
} catch (Exception e) { //Too lazy for lots of null-checks and it may throw exceptions anyways
nc = null;
}
@ -100,6 +116,14 @@ public class TownColorCommand extends AdminCommandBase { //TODO: Command path al
}
public static String getTownNameCased(String name) {
return TownyComponent.TU.getTownsMap().get(name.toLowerCase()).getName();
val town = TownyComponent.dataSource.getTown(name);
if (town == null) {
return null;
}
return town.getName();
}
public static Iterable<String> tabcompleteColor() {
return Arrays.stream(Color.values()).skip(1).map(Color::getName)::iterator;
}
}

View file

@ -44,7 +44,7 @@ public class TownyAnnouncer {
message, target, ChatUtils.MCORIGIN);
break;
case "Global":
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat,
TBMCChatAPI.SendSystemMessage(Channel.globalChat,
Channel.RecipientTestResult.ALL,
message, target, ChatUtils.MCORIGIN);
break;

View file

@ -1,33 +1,33 @@
package buttondevteam.chat.components.towny;
import buttondevteam.chat.PluginMain;
import buttondevteam.chat.VanillaUtils;
import buttondevteam.chat.components.formatter.formatting.TellrawPart;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.palmergames.bukkit.towny.TownyAPI;
import com.palmergames.bukkit.towny.TownyUniverse;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town;
import lombok.val;
import org.bukkit.command.CommandSender;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* This component manages the town and nation chat. It's also needed for the TownColorComponent.
* It provides the TC and NC channels, and posts Towny messages (global, town, nation) to the correct channels for other platforms like Discord.
* You can disable /tc and /nc in Chroma-Core's config if you only want to use the TownColorComponent.
*/
public class TownyComponent extends Component<PluginMain> {
public static TownyUniverse TU;
public static TownyAPI dataSource;
private static ArrayList<String> Towns;
private static ArrayList<String> Nations;
@ -36,12 +36,12 @@ public class TownyComponent extends Component<PluginMain> {
@Override
protected void enable() {
TU = TownyUniverse.getInstance();
Towns = TU.getTownsMap().values().stream().map(Town::getName).collect(Collectors.toCollection(ArrayList::new)); // Creates a snapshot of towns, new towns will be added when needed
Nations = TU.getNationsMap().values().stream().map(Nation::getName).collect(Collectors.toCollection(ArrayList::new)); // Same here but with nations
TBMCChatAPI.RegisterChatChannel(
dataSource = TownyAPI.getInstance();
Towns = TownyUniverse.getInstance().getTowns().stream().map(Town::getName).collect(Collectors.toCollection(ArrayList::new)); // Creates a snapshot of towns, new towns will be added when needed
Nations = TownyUniverse.getInstance().getNations().stream().map(Nation::getName).collect(Collectors.toCollection(ArrayList::new)); // Same here but with nations
TBMCChatAPI.registerChatChannel(
TownChat = new Channel("§3TC§f", Color.DarkAqua, "tc", s -> checkTownNationChat(s, false)));
TBMCChatAPI.RegisterChatChannel(
TBMCChatAPI.registerChatChannel(
NationChat = new Channel("§6NC§f", Color.Gold, "nc", s -> checkTownNationChat(s, true)));
TownyAnnouncer.setup(TownChat, NationChat);
}
@ -51,30 +51,34 @@ public class TownyComponent extends Component<PluginMain> {
TownyAnnouncer.setdown();
}
public void handleSpiesInit(Channel channel, TellrawPart json, Function<TellrawPart, String> toJson) {
if (channel.ID.equals(TownChat.ID) || channel.ID.equals(NationChat.ID)) {
((List<TellrawPart>) json.getExtra()).add(0, new TellrawPart("[SPY]"));
jsonstr = toJson.apply(json);
public Consumer<Player> handleSpiesInit(Channel channel, TextComponent.Builder json) {
if (channel.getIdentifier().equals(TownChat.getIdentifier()) || channel.getIdentifier().equals(NationChat.getIdentifier())) {
// TODO: Cannot prepend to json, so we need to run this ealier
//((List<TellrawPart>) json.getExtra()).add(0, new TellrawPart("[SPY]"));
return p -> handleSpies(channel, p, json);
}
return p -> {};
}
private String jsonstr;
public void handleSpies(Channel channel, Player p) {
if (channel.ID.equals(TownChat.ID) || channel.ID.equals(NationChat.ID)) {
if (Optional.ofNullable(TU.getResidentMap().get(p.getName().toLowerCase()))
.filter(r -> r.hasMode("spy")).isPresent())
VanillaUtils.tellRaw(p, jsonstr);
private void handleSpies(Channel channel, Player p, TextComponent.Builder jsonstr) {
if (channel.getIdentifier().equals(TownChat.getIdentifier()) || channel.getIdentifier().equals(NationChat.getIdentifier())) {
val res = dataSource.getResident(p.getName());
if (res == null) {
return;
}
if (res.hasMode("spy"))
p.sendMessage(jsonstr.build());
}
}
/**
* Return the error message for the message sender if they can't send it and the score
*/
private static Channel.RecipientTestResult checkTownNationChat(CommandSender sender, boolean nationchat) {
if (!(sender instanceof Player))
private static Channel.RecipientTestResult checkTownNationChat(ChromaGamerBase user, boolean nationchat) {
if (!(user instanceof TBMCPlayerBase))
return new Channel.RecipientTestResult("§cYou are not a player!");
Resident resident = TU.getResidentMap().get(sender.getName().toLowerCase());
val sender = ((TBMCPlayerBase) user).getOfflinePlayer();
Resident resident = dataSource.getResident(sender.getName());
Channel.RecipientTestResult result = checkTownNationChatInternal(nationchat, resident);
if (result.errormessage != null && resident != null && resident.getModes().contains("spy")) // Only use spy if they wouldn't see it
result = new Channel.RecipientTestResult(1000, "allspies"); // There won't be more than a thousand towns/nations probably

View file

@ -7,31 +7,27 @@ import buttondevteam.chat.commands.ucmds.HistoryCommand;
import buttondevteam.chat.components.flair.FlairComponent;
import buttondevteam.chat.components.flair.FlairStates;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
import buttondevteam.lib.player.TBMCPlayerLoadEvent;
import buttondevteam.lib.player.TBMCPlayerSaveEvent;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayerBase;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.Timer;
public class PlayerJoinLeaveListener implements Listener {
@EventHandler
public void onPlayerLoad(TBMCPlayerLoadEvent e) {
ChatPlayer cp = e.GetPlayer().asPluginPlayer(ChatPlayer.class);
cp.FlairUpdate();
}
@EventHandler
public void onPlayerTBMCJoin(TBMCPlayerJoinEvent e) {
ChatPlayer cp = e.GetPlayer().asPluginPlayer(ChatPlayer.class);
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(PlayerJoinEvent e) {
Player p = e.getPlayer();
ChatPlayer cp = TBMCPlayerBase.getPlayer(p.getUniqueId(), ChatPlayer.class);
cp.FlairUpdate();
if (ComponentManager.isEnabled(FlairComponent.class)) {
if (!cp.FlairState().get().equals(FlairStates.NoComment)) {
if (!cp.FlairState.get().equals(FlairStates.NoComment)) {
FlairComponent.ConfirmUserMessage(cp);
Timer timer = new Timer();
PlayerJoinTimerTask tt = new PlayerJoinTimerTask() {
@ -55,14 +51,10 @@ public class PlayerJoinLeaveListener implements Listener {
nwithoutformatting = nwithoutformatting.replace("§" + nwithoutformatting.charAt(index + 1), "");
} else
nwithoutformatting = p.getName();
PlayerListener.nicknames.forcePut(nwithoutformatting.toLowerCase(), p.getUniqueId()); //TODO: FormatterComponent
PlayerListener.nicknames.forcePut(nwithoutformatting.toLowerCase(), p.getUniqueId());
if (PluginMain.Instance.storeChatHistory().get())
HistoryCommand.showHistory(e.getPlayer(), null);
}
@EventHandler
public void onPlayerSave(TBMCPlayerSaveEvent e) {
if (PluginMain.Instance.storeChatHistory.get())
HistoryCommand.showHistory(ChromaGamerBase.getFromSender(e.getPlayer()), null);
}
@EventHandler

View file

@ -8,44 +8,31 @@ import buttondevteam.chat.components.flair.FlairComponent;
import buttondevteam.chat.components.formatter.FormatterComponent;
import buttondevteam.chat.components.towncolors.TownColorComponent;
import buttondevteam.core.ComponentManager;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.ChatRoom;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
import buttondevteam.lib.player.TBMCPlayerGetInfoEvent;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.val;
import net.ess3.api.events.NickChangeEvent;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerChatTabCompleteEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.server.ServerCommandEvent;
import java.util.Arrays;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.function.BiPredicate;
public class PlayerListener implements Listener {
/**
* Does not contain format codes, lowercased
*/
public static BiMap<String, UUID> nicknames = HashBiMap.create();
public static final BiMap<String, UUID> nicknames = HashBiMap.create();
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerChat(AsyncPlayerChatEvent event) {
@ -56,77 +43,6 @@ public class PlayerListener implements Listener {
event.setCancelled(true); // The custom event should only be cancelled when muted or similar
}
@EventHandler(priority = EventPriority.HIGHEST)
public void PlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
if (!event.isCancelled())
event.setCancelled(onCommandPreprocess(event.getPlayer(), event.getMessage()));
}
private boolean onCommandPreprocess(CommandSender sender, String message) {
if (message.length() < 2)
return false;
int index = message.indexOf(" ");
val mp = ChromaGamerBase.getFromSender(sender);
String cmd;
final BiPredicate<Channel, String> checkchid = (chan, cmd1) -> cmd1.equalsIgnoreCase(chan.ID) || (Arrays.stream(chan.IDs().get()).anyMatch(cmd1::equalsIgnoreCase));
if (index == -1) { // Only the command is run
if (!(sender instanceof Player || sender instanceof ConsoleCommandSender))
return false;
// ^^ We can only store player or console channels - Directly sending to channels would still work if they had an event
cmd = sender instanceof ConsoleCommandSender ? message : message.substring(1);
for (Channel channel : ((Iterable<Channel>) Channel.getChannels()::iterator)) { //Using Stream.forEach would be too easy
if (checkchid.test(channel, cmd)) {
Channel oldch = mp.channel().get();
if (oldch instanceof ChatRoom)
((ChatRoom) oldch).leaveRoom(sender);
if (oldch.equals(channel))
mp.channel().set(Channel.GlobalChat);
else {
mp.channel().set(channel);
if (channel instanceof ChatRoom)
((ChatRoom) channel).joinRoom(sender);
}
sender.sendMessage("§6You are now talking in: §b" + mp.channel().get().DisplayName().get());
return true;
}
}
} else { // We have arguments
cmd = sender instanceof ConsoleCommandSender ? message.substring(0, index) : message.substring(1, index);
if (cmd.equalsIgnoreCase("tpahere")) {
Player player = Bukkit.getPlayer(message.substring(index + 1));
if (player != null && sender instanceof Player)
player.sendMessage("§b" + ((Player) sender).getDisplayName() + " §bis in this world: " //TODO: Move to the Core
+ ((Player) sender).getWorld().getName());
} else if (cmd.equalsIgnoreCase("minecraft:me")) {
if (!(sender instanceof Player) || !PluginMain.essentials.getUser((Player) sender).isMuted()) {
String msg = message.substring(index + 1);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, String.format("* %s %s", ChromaUtils.getDisplayName(sender), msg), TBMCSystemChatEvent.BroadcastTarget.ALL); //TODO: Don't send to all
return true;
} else {
sender.sendMessage("§cCan't use /minecraft:me while muted.");
return true;
}
} else if (cmd.equalsIgnoreCase("me")) { //Take over for Discord broadcast
if (!(sender instanceof Player) || !PluginMain.essentials.getUser((Player) sender).isMuted()) {
String msg = message.substring(index + 1);
Bukkit.broadcastMessage(String.format("§5* %s %s", sender instanceof Player ? ((Player) sender).getDisplayName() : sender.getName(), msg));
return true;
} else {
sender.sendMessage("§cCan't use /me while muted.");
return true;
}
} else
for (Channel channel : (Iterable<Channel>) Channel.getChannels()::iterator) {
if (checkchid.test(channel, cmd)) { //Apparently method references don't require final variables
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, mp, message.substring(index + 1)).build(), channel);
return true;
}
}
// TODO: Target selectors
}
return false;
}
@EventHandler
public void onTabComplete(PlayerChatTabCompleteEvent e) {
String name = e.getLastToken();
@ -136,29 +52,20 @@ public class PlayerListener implements Listener {
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onConsoleCommand(ServerCommandEvent event) {
if (onCommandPreprocess(event.getSender(), event.getCommand()))
event.setCommand("dontrunthiscmd");
}
@EventHandler
public void onGetInfo(TBMCPlayerGetInfoEvent e) {
try (ChatPlayer cp = e.getPlayer().getAs(ChatPlayer.class)) {
if (cp == null)
return;
e.addInfo("Minecraft name: " + cp.PlayerName().get());
if (cp.UserName().get() != null && cp.UserName().get().length() > 0)
e.addInfo("Reddit name: " + cp.UserName().get());
if (ComponentManager.isEnabled(FlairComponent.class)) {
final String flair = cp.GetFormattedFlair(e.getTarget() != InfoTarget.MCCommand);
if (flair.length() > 0)
e.addInfo("/r/TheButton flair: " + flair);
}
e.addInfo(String.format("Respect: %.2f", cp.getF()));
} catch (Exception ex) {
TBMCCoreAPI.SendException("Error while providing chat info for player " + e.getPlayer().getFileName(), ex);
ChatPlayer cp = e.getPlayer().getAs(ChatPlayer.class);
if (cp == null)
return;
e.addInfo("Minecraft name: " + cp.getPlayerName());
if (cp.UserName.get() != null && cp.UserName.get().length() > 0)
e.addInfo("Reddit name: " + cp.UserName.get());
if (ComponentManager.isEnabled(FlairComponent.class)) {
final String flair = cp.GetFormattedFlair(e.getTarget() != InfoTarget.MCCommand);
if (flair.length() > 0)
e.addInfo("/r/TheButton flair: " + flair);
}
e.addInfo(String.format("Respect: %.2f", cp.getF()));
}
private long lastError = 0;
@ -168,18 +75,18 @@ public class PlayerListener implements Listener {
try {
if (e.isCancelled())
return;
HistoryCommand.addChatMessage(e.getCm(), e.getChannel());
HistoryCommand.addChatMessage(e.getChatMessage(), e.getChannel());
e.setCancelled(FormatterComponent.handleChat(e));
} catch (NoClassDefFoundError | Exception ex) { // Weird things can happen
if (lastError < System.nanoTime() - 1000L * 1000L * 1000L * 60 * 60 //60 mins
&& Bukkit.getOnlinePlayers().size() > 0) { //If there are no players on, display to the first person
lastError = System.nanoTime(); //I put the whole thing in the if to protect against race conditions
for (Player p : Bukkit.getOnlinePlayers())
if (e.shouldSendTo(p))
p.sendMessage("[" + e.getChannel().DisplayName().get() + "] §cSome features in the message below might be unavailable due to an error.");
if (e.shouldSendTo(ChromaGamerBase.getFromSender(p)))
p.sendMessage("[" + e.getChannel().displayName.get() + "] §cSome features in the message below might be unavailable due to an error.");
}
ChatUtils.sendChatMessage(e, s -> "§c!§r" + s);
TBMCCoreAPI.SendException("An error occured while processing a chat message!", ex);
ChatUtils.sendChatMessage(e);
TBMCCoreAPI.SendException("An error occured while processing a chat message!", ex, PluginMain.Instance);
}
}
@ -187,9 +94,9 @@ public class PlayerListener implements Listener {
public void onNickChange(NickChangeEvent e) {
String nick = e.getValue();
if (nick == null)
nicknames.inverse().remove(e.getAffected().getBase().getUniqueId());
nicknames.inverse().remove(e.getController().getBase().getUniqueId()); //EssentialsX has it swapped
else
nicknames.inverse().forcePut(e.getAffected().getBase().getUniqueId(), ChatColor.stripColor(nick).toLowerCase());
nicknames.inverse().forcePut(e.getController().getBase().getUniqueId(), ChatColor.stripColor(nick).toLowerCase());
Bukkit.getScheduler().runTaskLater(PluginMain.Instance, () -> {
TownColorComponent.updatePlayerColors(e.getAffected().getBase()); //Won't fire this event again

View file

@ -4,7 +4,7 @@ import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin;
import org.dynmap.bukkit.DynmapPlugin;
import org.dynmap.DynmapCommonAPI;
import org.dynmap.markers.MarkerAPI;
import java.lang.reflect.Constructor;
@ -35,7 +35,7 @@ public class DTBridge {
Constructor<?> c = cl.getDeclaredConstructor(FileConfiguration.class, String.class, MarkerAPI.class);
c.setAccessible(true);
style = c.newInstance(dtp.getConfig(), "custstyle." + townname,
((DynmapPlugin) Bukkit.getPluginManager().getPlugin("dynmap")).getMarkerAPI());
((DynmapCommonAPI) Bukkit.getPluginManager().getPlugin("dynmap")).getMarkerAPI());
map.put(townname, style);
}
set(cl, style, "fillcolor", fillcolor);

View file

@ -1,18 +1,14 @@
name: Chroma-Chat
main: buttondevteam.chat.PluginMain
version: '4.0'
version: '${noprefix.version}'
commands:
u:
description: Auto-flair system. Accept or ignore flair.
ooc:
description: Send message Out-of-Character.
alias: nrp
description: The main command for Chroma-Chat.
unlol:
description: Unlaugh the last laugh.
alias: unlaugh
mwiki:
description: Search the wiki.
dontrunthiscmd: null
tableflip:
description: Flip a table.
unflip:
@ -30,19 +26,22 @@ commands:
description: Lenny face.
ftop:
description: Top respect.
me:
description: Me command with Chroma support.
author: NorbiPeti
depend:
- Essentials
- Vault
- ChromaCore
soft-depend:
- Minigames
- Dynmap-Towny
- Towny
- Essentials
- Vault
- Chroma-Core
softdepend:
- Dynmap-Towny
- Towny
- dynmap
permissions:
tbmc.badge.gold:
description: Gives a patron badge.
default: false
tbmc.badge.diamond:
description: Gives a cool patron badge.
default: false
default: false
api-version: '1.13'

View file

@ -1,103 +1,129 @@
package buttondevteam.chat.components.formatter;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.ChatUtils;
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.*;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent.ClickAction;
import buttondevteam.chat.components.formatter.formatting.TellrawEvent.HoverAction;
import buttondevteam.core.TestPrepare;
import buttondevteam.chat.components.formatter.formatting.ChatFormatter;
import buttondevteam.chat.components.formatter.formatting.FormatSettings;
import buttondevteam.chat.components.formatter.formatting.MatchProviderBase;
import buttondevteam.core.MainPlugin;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.Color;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.command.CommandSender;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.test.TestPermissions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import static be.seeseemelk.mockbukkit.MockBukkit.load;
import static be.seeseemelk.mockbukkit.MockBukkit.mock;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.event.ClickEvent.Action.OPEN_URL;
import static net.kyori.adventure.text.event.ClickEvent.clickEvent;
import static net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT;
import static net.kyori.adventure.text.event.HoverEvent.hoverEvent;
import static net.kyori.adventure.text.format.NamedTextColor.*;
import static net.kyori.adventure.text.format.TextDecoration.*;
@RunWith(ObjectTestRunner.class)
public class ChatFormatIT {
@Objects
public static List<Object> data() {
TestPrepare.PrepareServer();
final CommandSender sender = Mockito.mock(CommandSender.class);
mock();
load(MainPlugin.class, true);
var sender = ChromaGamerBase.getUser(UUID.randomUUID().toString(), ChatPlayer.class);
DebugCommand.DebugMode = true;
PluginMain.permission = Mockito.mock(Permission.class);
PluginMain.permission = new TestPermissions();
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)));
list.add(new ChatFormatIT(sender, "***test***",
new TellrawPart("test").setBold(true).setItalic(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***__test__***",
new TellrawPart("test").setBold(true).setItalic(true).setUnderlined(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***__~~test~~__***", new TellrawPart("test").setBold(true).setItalic(true)
.setUnderlined(true).setStrikethrough(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "¯\\\\\\_(ツ)\\_/¯", new TellrawPart("¯\\_(ツ)_/¯").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "*test*", text("test").decorate(ITALIC).color(WHITE)));
list.add(new ChatFormatIT(sender, "**test**", text("test").decorate(BOLD).color(WHITE)));
list.add(new ChatFormatIT(sender, "***test***", text("test").decorate(BOLD, ITALIC).color(WHITE)));
list.add(new ChatFormatIT(sender, "***__test__***", text("test").decorate(BOLD, ITALIC, UNDERLINED).color(WHITE)));
list.add(new ChatFormatIT(sender, "***__~~test~~__***", text("test").decorate(BOLD, ITALIC, UNDERLINED, STRIKETHROUGH).color(WHITE)));
list.add(new ChatFormatIT(sender, "¯\\\\\\_(ツ)\\_/¯", text("¯\\_(ツ)_/¯").color(WHITE)));
list.add(new ChatFormatIT(sender, "https://google.hu/",
new TellrawPart("https://google.hu/").setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://google.hu/"))));
list.add(new ChatFormatIT(sender, "*test", new TellrawPart("*test").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "**test*", new TellrawPart("**test*").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "***test", new TellrawPart("***test").setColor(Color.White)));
list.add(new ChatFormatIT(sender, "Koiiev", new TellrawPart("§bKoiiev§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "norbipeti", new TellrawPart("§bNorbiPeti§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "Arsen_Derby_FTW", new TellrawPart("§bArsen_Derby_FTW§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "carrot_lynx", new TellrawPart("§bcarrot_lynx§r").setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "*carrot_lynx*", new TellrawPart("§bcarrot_lynx§r").setItalic(true).setColor(Color.Aqua)));
list.add(new ChatFormatIT(sender, "https://norbipeti.github.io/", new TellrawPart("https://norbipeti.github.io/")
.setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/"))));
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/")).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)));
list.add(new ChatFormatIT(sender, "**test __test__ test**", new TellrawPart("test ").setBold(true).setColor(Color.White),
new TellrawPart("test").setBold(true).setUnderlined(true).setColor(Color.White), new TellrawPart(" test").setBold(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "**test _test_ test**", new TellrawPart("test ").setBold(true).setColor(Color.White),
new TellrawPart("test").setItalic(true).setBold(true).setColor(Color.White), new TellrawPart(" test").setBold(true).setColor(Color.White)));
list.add(new ChatFormatIT(sender, "https://norbipeti.github.io/test?test&test#test", new TellrawPart("https://norbipeti.github.io/test?test&test#test")
.setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/test?test&test#test"))));
list.add(new ChatFormatIT(sender, "[hmm](https://norbipeti.github.io/test)", new TellrawPart("hmm")
.setColor(Color.White).setUnderlined(true)
.setHoverEvent(TellrawEvent.create(HoverAction.SHOW_TEXT,
new TellrawPart("Click to open").setColor(Color.Blue)))
.setClickEvent(TellrawEvent.create(ClickAction.OPEN_URL, "https://norbipeti.github.io/test"))));
TellrawPart space = new TellrawPart(" ").setColor(Color.White);
list.add(new ChatFormatIT(sender, "A rainbow text for testing. O", new TellrawPart("A").setColor(Color.Red),
space, new TellrawPart("rainbow").setColor(Color.Gold), space, new TellrawPart("text").setColor(Color.Yellow),
space, new TellrawPart("for").setColor(Color.Green), space, new TellrawPart("testing.").setColor(Color.Blue),
space, new TellrawPart("O").setColor(Color.DarkPurple)).setRainbowMode());
text("https://google.hu/").color(WHITE).decorate(UNDERLINED)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(BLUE)))
.clickEvent(clickEvent(OPEN_URL, "https://google.hu/"))));
list.add(new ChatFormatIT(sender, "*test", text("*test").color(WHITE)));
list.add(new ChatFormatIT(sender, "**test*", text("**test*").color(WHITE)));
list.add(new ChatFormatIT(sender, "***test", text("***test").color(WHITE)));
list.add(new ChatFormatIT(sender, "Koiiev", text("§bKoiiev§r").color(AQUA)));
list.add(new ChatFormatIT(sender, "norbipeti", text("§bNorbiPeti§r").color(AQUA)));
list.add(new ChatFormatIT(sender, "Arsen_Derby_FTW", text("§bArsen_Derby_FTW§r").color(AQUA)));
list.add(new ChatFormatIT(sender, "carrot_lynx", text("§bcarrot_lynx§r").color(AQUA)));
list.add(new ChatFormatIT(sender, "*carrot_lynx*", text("§bcarrot_lynx§r").decorate(ITALIC).color(AQUA)));
list.add(new ChatFormatIT(sender, "https://norbipeti.github.io/", text("https://norbipeti.github.io/")
.color(WHITE).decorate(UNDERLINED)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(BLUE)))
.clickEvent(clickEvent(OPEN_URL, "https://norbipeti.github.io/"))));
list.add(new ChatFormatIT(sender, "*https://norbipeti.github.io/ heh*", text("https://norbipeti.github.io/").decorate(ITALIC).decorate(UNDERLINED)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(BLUE)))
.clickEvent(clickEvent(OPEN_URL, "https://norbipeti.github.io/")).color(WHITE), text(" heh").decorate(ITALIC).color(WHITE)));
list.add(new ChatFormatIT(sender, "*test _test_ test*", text("test test test").decorate(ITALIC).color(WHITE)));
list.add(new ChatFormatIT(sender, "*test __test__ test*", text("test ").decorate(ITALIC).color(WHITE),
text("test").decorate(ITALIC).decorate(UNDERLINED).color(WHITE), text(" test").decorate(ITALIC).color(WHITE)));
list.add(new ChatFormatIT(sender, "**test __test__ test**", text("test ").decorate(BOLD).color(WHITE),
text("test").decorate(BOLD).decorate(UNDERLINED).color(WHITE), text(" test").decorate(BOLD).color(WHITE)));
list.add(new ChatFormatIT(sender, "**test _test_ test**", text("test ").decorate(BOLD).color(WHITE),
text("test").decorate(ITALIC).decorate(BOLD).color(WHITE), text(" test").decorate(BOLD).color(WHITE)));
list.add(new ChatFormatIT(sender, "https://norbipeti.github.io/test?test&test#test", text("https://norbipeti.github.io/test?test&test#test")
.color(WHITE).decorate(UNDERLINED)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(BLUE)))
.clickEvent(clickEvent(OPEN_URL, "https://norbipeti.github.io/test?test&test#test"))));
list.add(new ChatFormatIT(sender, "[hmm](https://norbipeti.github.io/test)", text("hmm")
.color(WHITE).decorate(UNDERLINED)
.hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(BLUE)))
.clickEvent(clickEvent(OPEN_URL, "https://norbipeti.github.io/test"))));
var space = text(" ").color(WHITE);
list.add(new ChatFormatIT(sender, "A rainbow text for testing. O", text("A").color(RED),
space, text("rainbow").color(GOLD), space, text("text").color(YELLOW),
space, text("for").color(GREEN), space, text("testing.").color(BLUE),
space, text("O").color(DARK_PURPLE)).setRainbowMode());
list.add(new ChatFormatIT(sender, "***test*** test", text("test").color(WHITE)
.decorate(ITALIC).decorate(BOLD), text(" test").color(WHITE)));
list.add(new ChatFormatIT(sender, ">test message\nheh", text(">test message\nheh").color(GREEN)));
list.add(new ChatFormatIT(sender, "[here's a link]()", text("[here's a link]()").color(WHITE)));
list.add(new ChatFormatIT(sender, "[](fakelink)", text("[](fakelink)").color(WHITE)));
list.add(new ChatFormatIT(sender, "||this is a spoiler||", text("this is a spoiler").color(WHITE)
.decorate(OBFUSCATED).hoverEvent(hoverEvent(SHOW_TEXT, text("this is a spoiler")))));
Function<String, TextComponent> whiteBoldItalic = text -> text(text).color(WHITE).decorate(BOLD).decorate(ITALIC);
list.add(new ChatFormatIT(sender, "***some complicated ||test message|| with [links](https://chromagaming.figytuna.com) and other __greatness__ by NorbiPeti***",
whiteBoldItalic.apply("some complicated "),
whiteBoldItalic.apply("test message").decorate(OBFUSCATED).hoverEvent(hoverEvent(SHOW_TEXT, text("test message"))),
whiteBoldItalic.apply(" with "),
whiteBoldItalic.apply("links").clickEvent(clickEvent(OPEN_URL, "https://chromagaming.figytuna.com"))
.decorate(UNDERLINED).hoverEvent(hoverEvent(SHOW_TEXT, text("Click to open").color(BLUE))),
whiteBoldItalic.apply(" and other "),
whiteBoldItalic.apply("greatness").decorate(UNDERLINED),
whiteBoldItalic.apply(" by "),
whiteBoldItalic.apply("§bNorbiPeti§r").color(AQUA))); //§b: flair color
list.add(new ChatFormatIT(sender, "hey @console", text("hey ").color(WHITE),
text("@console").color(AQUA)));
return list;
}
private final CommandSender sender;
private final ChromaGamerBase sender;
private final String message;
private final TellrawPart[] extras;
private final Component[] extras;
private boolean rainbowMode;
public ChatFormatIT(CommandSender sender, String message, TellrawPart... expectedextras) {
public ChatFormatIT(ChromaGamerBase sender, String message, Component... expectedExtras) {
this.sender = sender;
this.message = message;
this.extras = expectedextras;
this.extras = expectedExtras;
}
private ChatFormatIT setRainbowMode() {
@ -107,18 +133,16 @@ public class ChatFormatIT {
@Test
public void testMessage() {
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, 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);
// System.out.println("Raw: " + ChatProcessing.toJson(expectedtp));
for (TellrawPart extra : extras)
expectedtp.addExtra(extra);
Assert.assertEquals(ChatProcessing.toJson(expectedtp), ChatProcessing.toJson(tp));
ArrayList<MatchProviderBase> cfs = ChatProcessing.addFormatters(p -> true, null);
final String chid = ChatProcessing.getChannelID(Channel.globalChat, ChatUtils.MCORIGIN);
if (rainbowMode)
ChatProcessing.createRPC(WHITE, cfs);
final TextComponent.Builder tp = ChatProcessing.createEmptyMessageLine(sender, message, null, chid, ChatUtils.MCORIGIN);
ChatFormatter.Combine(cfs, message, tp, null, FormatSettings.builder().color(WHITE).build());
final TextComponent.Builder expectedtp = ChatProcessing.createEmptyMessageLine(sender, message, null, chid, ChatUtils.MCORIGIN);
for (Component extra : extras)
expectedtp.append(extra);
Assert.assertEquals(expectedtp.build(), tp.build());
}
}