Compare commits

..

78 commits

Author SHA1 Message Date
d1515c2ab2
Fix using delegated config properties from the Core 2023-07-01 20:11:19 +02:00
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
b5fd834dc7
Fix chat tests with MockBukkit and fix Paper version
#130
2023-06-27 01:39:35 +02:00
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
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
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
522f29ba58
Update Chroma-Core to v2.0.0 2023-04-26 00:03:18 +02:00
10bf0e98df
Update Towny dependency and replace methods that were removed (#128) 2023-03-13 00:47:40 +01:00
6c1378f370
Update dependencies and fix tellRaw for 1.17+ 2021-12-30 21:19:22 +01:00
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
724858ac70
Fix tellRaw for 1.16.4/1.16.5 2021-01-18 17:18:35 +01:00
9a10dcd1fc
Fix plugin.yml 2020-10-31 18:24:52 +01:00
dc6df53c96
Improve test coverage, preparations, fixes
Removed an unnnecessary bit of code from ChatFormatter
2020-10-29 20:31:38 +01:00
7646db4487
Fix player data plugin name, Spotbugs fixes
Also set the version
2020-10-28 00:28:15 +01:00
8e9989caee
Convert all configs 2020-10-26 19:56:22 +01:00
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
c52a6873fe
Exception stuff and player data stuff 2020-10-25 01:18:36 +02:00
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
a63f2bb256
Greentext formatter, doc and stuff 2020-09-06 22:46:24 +02:00
59dbc78b52
Fix #122
It will have to be fixed again at a later date
2020-09-04 03:56:25 +02:00
b483b7df0c
Fix /waitwhat and /me 2020-09-04 03:41:23 +02:00
4d4448fca1
Remove htmlcleaner and fix #117 2020-08-26 22:19:56 +02:00
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
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
7448eb7e3a
1.16 updates, fixes 2020-06-27 03:00:08 +02:00
c688ec9243
Finally fix Towny errors for 1.12 - use the intended API everywhere 2020-04-08 01:10:49 +02:00
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
43525bd93c
Add missing backslash after "From X" and before "n" 2020-03-20 02:06:07 +01:00
052149bcb7
Add tabcompletes, fix some commands and help texts 2020-03-20 01:26:14 +01:00
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
f3ec9e7870
Merge branch 'dev'
# Conflicts:
#	.idea/ButtonChat.iml
2020-03-04 23:11:31 +01:00
0421cc6d16
Small formatter fixes, docs 2020-03-04 23:07:56 +01:00
aad65e0222 Remove 2 TODOs 2020-03-04 10:45:26 +01:00
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
661534b92d Update README.md 2020-03-02 12:40:16 +01:00
6af2ad79a7
Remove old loading code, use inherited registerCommand() 2020-02-16 20:28:19 +01:00
f19aa41205
Configure CI build for GitHub Packages
Also fixed a couple issues based on an analysis
Removed debug souts
2020-02-15 00:23:04 +01:00
4952923f9b
Fix all remaining issues with the chat processing
Checking regex formatters first, then string, then range
Fix section combining
24/24
#71
2020-02-13 17:20:17 +01:00
4d0c7c170e
Fix many issues with the chat processing
#71
2020-02-13 00:24:31 +01:00
0dbfaa65a5 Fix remaining compiler errors
The italics test works so far
2020-02-12 19:16:05 +01:00
30ee99d495 Converted formatters to match providers 2020-02-12 12:45:42 +01:00
1dc801575a GH Releases, formatter work, removed old code
TBMCPlugins/ChromaCore#76
2020-02-10 13:46:39 +01:00
0a4cdd01ce
New approach for chat processing
Created different providers for regex, range and string matching
Handling one match at a time per provider

TODO:
Formatters should be executed one by one in declaration order
Add enitre message by default
Loop until all of the providers are finished
Ignore any areas returned by the providers

#71
2020-02-08 01:46:00 +01:00
b452170ff7 Rename 2020-02-04 17:43:35 +01:00
c66b212b0c
Merge pull request #114 from TBMCPlugins/dev
Misc. fixes, chat history config, clickable announcements
2020-02-01 20:13:05 +01:00
2d874d7bc9
Clickable announcements and some fixes
Fixed announcement storage (#112)
Disabled some components by default
Made announcements clickable and copiable (#95)
Fixed /mwiki (#110)
Fixed /u info (#111)
Added test for rainbow chat (#103)
Fixed crash if Dynmap-Towny isn't present
Removed old command annotations
2020-01-31 22:01:29 +01:00
cda2a36da1
Ping sound config, chat history config, lot of descriptions
Moved ping sound configs to the FormatterComponent
Made an option to disable the chat history
Added descriptions for each config option
2020-01-25 03:52:25 +01:00
d5f500aece
Fix Ftask, display error, fix history
Fixed Ftask not being assinged so it couldn't be handled on a new event
Displaying an error to everyone in-game who will see the errored chat message
Fixed message history race condition
#113
2020-01-25 02:14:01 +01:00
2a9bc58157
F i x & clean combining and attempt to fix range conversion
It might do too many sorts but hey
#71
2019-12-15 03:49:32 +01:00
22c7fed75d
F i x remchars, improve combine
Also a newCombine method from yesterday
#71
2019-11-17 03:14:28 +01:00
825e327903
Fix underscore pattern - it was wrong all along
Fix test - in IntelliJ anyways
2019-11-16 01:08:20 +01:00
fd3d3df108
Merge pull request #105 from TBMCPlugins/dev
1.14 support, fixes
2019-10-30 19:35:42 +01:00
c0128831a7
Use parent POM 2019-10-23 02:46:15 +02:00
013287cb47
Update to Java 10/11 2019-10-20 20:53:15 +02:00
cc6c477add
Fixed TownyAnnouncer
It took way too long to figure out how to add an appender that actually takes effect
Also changed Towny dep, it'll likely break Travis builds
2019-10-20 02:32:10 +02:00
a91ee1b842 Registering the formatter component
Only disabling the chat handler if the component is enabled
2019-09-30 15:55:25 +02:00
aa455e373d
Only send the message to the same group
#106
2019-09-30 11:52:32 +02:00
6a16a3a1d5 Actually setting names for the formatters
Also made the name and the pattern mandatory
#85
2019-09-23 13:41:24 +02:00
750b71de65 Created FormatterComponent & formatters can be disabled
It allows disabling chat processing as a whole or formatting
Formatters can be disabled one by one
#85
2019-09-23 13:24:39 +02:00
bd656015bf
Fixes, improved color mode command
Renaming
Fixed TownyAnnouncer once again - but it still doesn't work
Improved color mode (separated "unknown color" and "reset")
Fixed ping sound hiding for nicknames (#90)
2019-09-21 02:21:53 +02:00
fa375c4912 Updated code relying on Towny & updated Lombok version
Towny now uses log4j
Newer Lombok supports Java 11
2019-09-16 15:35:28 +02:00
a964ce6595 Fix Towny dep 2019-09-16 13:39:33 +02:00
4a456d9b1e Fixed 3 issues (hopefully)
Fixed #104
Fixed #101 and also allowing spaces in the action
Fixed #90
2019-09-16 11:49:03 +02:00
a4b7f689c0 Fix Travis 2019-09-02 15:38:04 +02:00
b3939f3c92
Small stuff
Pretty much just renamed the plugin
2019-08-27 03:04:21 +02:00
1275cdafd0
Removed scoreboard that has been there for a long time
PluginMain.java:40
Also made the channel scores unnecessary
As well as the recently introduced dispatchConsoleCommand method
Also probably fixed Towny spy (#100)
2019-08-13 15:02:34 +02:00
f3904a8448
tellRaw continue
Didn't commit it earlier today, but I'm not gonna work on it now
2019-08-13 01:19:55 +02:00
415e61a401
isChatOn fix, tellRaw start 2019-08-12 02:50:18 +02:00
7b2ecdbf7b
Renamed config methods to match the keys 2019-08-07 18:28:47 +02:00
4956837e5d
Not processing messages when nobody is online 2019-07-22 22:37:23 +02:00
3ad2346990
Finally fixed #99
Totally didn't forget about it
2019-07-10 15:26:46 +02:00
8907d5308f
1.14 prep & chat formatter organizing
(1.14 prep is older code)
2019-06-30 22:04:33 +02:00
bbe9a8c146
Added snap command from master 2019-06-15 00:40:48 +02:00
80e45aa1a9
Added snap command 2019-04-24 23:57:52 +02:00
049ad55168
Town/nation color fixes, reload command 2019-03-16 14:33:03 +01:00
69 changed files with 2830 additions and 2640 deletions

5
.gitignore vendored
View file

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

View file

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:21.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.19" level="project" />
<orderEntry type="library" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.github.TBMCPlugins.ButtonCore:ButtonCore:master-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: net.sourceforge.htmlcleaner:htmlcleaner:2.16" level="project" />
<orderEntry type="library" name="Maven: org.jdom:jdom2:2.0.5" level="project" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.19.0-GA" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" name="Maven: net.ess3:Essentials:2.13.1" level="project" />
<orderEntry type="library" name="Maven: com.github.TBMCPlugins.ButtonCore:Towny:8d3b6b6" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.milkbowl:VaultAPI:master-68f14eca20-1" level="project" />
<orderEntry type="library" name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.16.16" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.github.webbukkit:Dynmap-Towny:master-0.60-g924051d-7" level="project" />
<orderEntry type="library" name="Maven: com.github.webbukkit:Dynmap:v2.5" level="project" />
<orderEntry type="library" name="Maven: com.nijikokun.bukkit:Permissions:3.1.6" level="project" />
<orderEntry type="library" name="Maven: ru.tehkode:PermissionsEx:1.19.1" level="project" />
<orderEntry type="library" name="Maven: de.bananaco:bPermissions:2.9.1" level="project" />
<orderEntry type="library" name="Maven: com.platymuus.bukkit.permissions:PermissionsBukkit:1.6" level="project" />
<orderEntry type="library" name="Maven: org.anjocaido:EssentialsGroupManager:2.10.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.6" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:21.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.19" level="project" />
<orderEntry type="library" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.github.TBMCPlugins.ButtonCore:ButtonCore:master-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: net.sourceforge.htmlcleaner:htmlcleaner:2.16" level="project" />
<orderEntry type="library" name="Maven: org.jdom:jdom2:2.0.5" level="project" />
<orderEntry type="library" name="Maven: org.reflections:reflections:0.9.10" level="project" />
<orderEntry type="library" name="Maven: org.javassist:javassist:3.19.0-GA" level="project" />
<orderEntry type="library" name="Maven: com.google.code.findbugs:annotations:2.0.1" level="project" />
<orderEntry type="library" name="Maven: net.ess3:Essentials:2.13.1" level="project" />
<orderEntry type="library" name="Maven: com.github.TBMCPlugins.ButtonCore:Towny:8d3b6b6" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: com.github.milkbowl:VaultAPI:master-68f14eca20-1" level="project" />
<orderEntry type="library" name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.16.16" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot:1.12.2-R0.1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: com.github.webbukkit:Dynmap-Towny:master-0.60-g924051d-7" level="project" />
<orderEntry type="library" name="Maven: com.github.webbukkit:Dynmap:v2.5" level="project" />
<orderEntry type="library" name="Maven: com.nijikokun.bukkit:Permissions:3.1.6" level="project" />
<orderEntry type="library" name="Maven: ru.tehkode:PermissionsEx:1.19.1" level="project" />
<orderEntry type="library" name="Maven: de.bananaco:bPermissions:2.9.1" level="project" />
<orderEntry type="library" name="Maven: com.platymuus.bukkit.permissions:PermissionsBukkit:1.6" level="project" />
<orderEntry type="library" name="Maven: org.anjocaido:EssentialsGroupManager:2.10.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
</component>
</module>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<projectDescription> <projectDescription>
<name>ButtonChat</name> <name>Chroma-Chat</name>
<comment></comment> <comment></comment>
<projects> <projects>
</projects> </projects>

View file

@ -9,18 +9,13 @@ before_install: | # Wget BuildTools and run if cached folder not found
fi fi
language: java language: java
jdk: jdk:
- oraclejdk8 - oraclejdk11
sudo: true sudo: true
deploy: deploy:
# deploy develop to the staging environment - provider: releases
- provider: script api_key:
script: chmod +x deploy.sh && sh deploy.sh staging secure: "F5YiEuD6LyRENUDMCslcSl0O0dg4IDk+nNeb4X2VLYlmb8dW9beMuIgjH8efTMeaQ3D/ntIkN0Dtf2GKvpOduhwkSbAgw4WM028X60SY9f2hmpEO3LmM4T1tKoDlI1T3BmhYP4KeTKBYn+etV1mSPbT07vUybCm/vGzvr96yMZGNFEoKsWLaEu7dZfBFULj4tXOwrLh/KO6BsdAHvZcGKWNVupPq3YoUVT0dpGcUudf5cpn+aaqMwyd709zgMbyCuqf+c5Udps43q4EKvr9z7TWxFUkGTPVVAcUVygJsi2ytuyA8TLMPq/KhYe9htnkNUnizbqv/j49xww0gVaD7OJXENJ4hAUTV4sdn1DXG45JXW+dir3V7YzbRYn3M+eCuKB2O77SXRZBkxcGtTMtCmghP9/tcRAQlXDXnxu7oAnlUVp17g/+aFApvlzZEZVx2N+fkyEe7JrUFlRCixtHyrmTLWhyV0Px9p0FHJpvSSCL0S0UKVAT/sNHYHhD5gouK7owEomEbG58XCsRDH6Et7RuDksB98ekK8brZp6S7dNIS2CVuVx1vIkXC8PzUGcpJQoztvEYUE20Axahh5s8AkE9n/O9jzs9ajcfYaHhWzYeUZzHdHllOYF9l6VoCUitTk4Sl8eJifSq3GzI+T6wGMBepZHLpe230MvBIrqGZ+Vg="
file: 'target/Chroma-Chat.jar'
on: on:
branch: dev tags: true
skip_cleanup: true
# deploy master to production
- provider: script
script: chmod +x deploy.sh && sh deploy.sh production
on:
branch: master
skip_cleanup: true skip_cleanup: true

View file

@ -1 +0,0 @@
apache-maven-3.2.5/bin/mvn install:install-file -Dfile=Towny.jar -DgroupId=com.palmergames -DartifactId=Towny -Dversion=0.90.0.0 -Dpackaging=jar

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 ## Components
### 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.
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) ### Flair
You can use this command to hide the notice showing up after you log in if you don't have a flair accepted. 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 ### Formatter
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. 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) ### Towny
You can see a player's username if they have a flair shown. This component is needed by the town colors component. Besides, it reads Towny events to be broadcasted to Discord, if that plugin is used.
#### 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.

View file

@ -1,10 +0,0 @@
#!/bin/sh
FILENAME=$(find target/ -maxdepth 1 ! -name '*original*' -name '*.jar')
echo Found file: $FILENAME
if [ $1 = 'production' ]; then
echo Production mode
echo $UPLOAD_KEY > upload_key
chmod 400 upload_key
yes | scp -B -i upload_key -o StrictHostKeyChecking=no $FILENAME travis@server.figytuna.com:/minecraft/main/TBMC/pluginupdates
fi

334
pom.xml
View file

@ -1,60 +1,30 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <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"> 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> <modelVersion>4.0.0</modelVersion>
<version>0.0.1-SNAPSHOT</version> <parent>
<name>The Button Minecraft Chat Plugin</name> <groupId>com.github.TBMCPlugins.ChromaCore</groupId>
<description>The Button Minecraft Chat Plugin</description> <artifactId>CorePOM</artifactId>
<build> <version>master-SNAPSHOT</version>
<sourceDirectory>src/main/java</sourceDirectory> </parent>
<resources> <version>v${noprefix.version}-SNAPSHOT</version>
<resource> <name>Chroma-Chat Plugin</name>
<directory>src</directory> <description>Chroma-Chat Plugin</description>
<excludes> <build>
<exclude>**/*.java</exclude> <sourceDirectory>src/main/java</sourceDirectory>
</excludes> <resources>
</resource> <resource>
<resource> <directory>src/main/resources</directory>
<directory>src/main/resources</directory> <includes>
<includes> <include>*.properties</include>
<include>*.properties</include> <include>*.yml</include>
<include>*.yml</include> <include>*.csv</include>
<include>*.csv</include> <include>*.txt</include>
<include>*.txt</include> </includes>
</includes> <filtering>true</filtering>
<filtering>true</filtering>
</resource> </resource>
</resources> </resources>
<finalName>ButtonChat</finalName> <finalName>Chroma-Chat</finalName>
<plugins> <plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>net.sourceforge.htmlcleaner:htmlcleaner</include>
<include>org.javassist:javassist</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId> <artifactId>maven-resources-plugin</artifactId>
@ -96,49 +66,6 @@
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 --> </useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
</configuration> </configuration>
</plugin> </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> -->
</plugins> </plugins>
</build> </build>
<groupId>buttondevteam</groupId> <groupId>buttondevteam</groupId>
@ -151,97 +78,125 @@
<id>jitpack</id> <id>jitpack</id>
<url>https://jitpack.io/</url> <url>https://jitpack.io/</url>
</repository> </repository>
<repository> <repository>
<id>Essentials</id> <id>ess-repo</id>
<url>http://repo.ess3.net/content/repositories/essrel/</url> <url>https://repo.essentialsx.net/releases/</url>
</repository> </repository>
<repository> <!-- <repository>
<id>Minigames</id> <id>Minigames</id>
<url>http://maven.addstar.com.au/artifactory/release</url> <url>http://maven.addstar.com.au/artifactory/release</url>
</repository> </repository> -->
<!-- <repository> <!-- <repository>
<id>vault-repo</id> <id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases</url> <url>http://nexus.hc.to/content/repositories/pub_releases</url>
</repository> --> </repository> -->
<!-- <repository> <id>WorldEdit</id> <url>http://maven.sk89q.com/artifactory/repo</url> <!-- <repository> <id>WorldEdit</id> <url>http://maven.sk89q.com/artifactory/repo</url>
</repository> --> </repository> -->
<repository> <repository>
<id>projectlombok.org</id> <id>projectlombok.org</id>
<url>http://projectlombok.org/mavenrepo</url> <url>https://projectlombok.org/mavenrepo</url>
</repository> </repository>
</repositories> <repository>
<dependencies> <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> <!-- 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>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.19.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.TownyAdvanced</groupId>
<artifactId>Towny</artifactId>
<!-- <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>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.16.4-R0.1-SNAPSHOT</version>
</dependency> -->
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>com.github.TownyAdvanced</groupId>
<artifactId>spigot-api</artifactId>
<version>1.12.2-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>ButtonCore</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>
<dependency>
<groupId>net.ess3</groupId>
<artifactId>Essentials</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>Towny</artifactId>
<version>8d3b6b6</version>
</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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</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>com.github.webbukkit</groupId>
<artifactId>Dynmap-Towny</artifactId> <artifactId>Dynmap-Towny</artifactId>
<version>master-SNAPSHOT</version> <version>0.89</version>
</dependency>
<dependency>
<groupId>com.github.webbukkit</groupId>
<artifactId>Dynmap</artifactId>
<version>v2.5</version>
</dependency> </dependency>
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>dynmap-api</artifactId>
<version>3.2-beta-1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit --> <!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.12</version> <version>4.13.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> <!-- Included in vanilla minecraft's JAR -->
<artifactId>ButtonChat</artifactId> <dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<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> <organization>
<name>TBMCPlugins</name> <name>TBMCPlugins</name>
<url>https://github.com/TBMCPlugins</url> <url>https://github.com/TBMCPlugins</url>
@ -250,23 +205,6 @@
<!-- github server corresponds to entry in ~/.m2/settings.xml --> <!-- github server corresponds to entry in ~/.m2/settings.xml -->
<github.global.server>githubo</github.global.server> <github.global.server>githubo</github.global.server>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<branch> <noprefix.version>1.0.0</noprefix.version>
master
</branch> <!-- Should be master if building ButtonCore locally - the CI will overwrite it (see below) -->
</properties> </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> </project>

View file

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

View file

@ -1,333 +0,0 @@
package buttondevteam.chat;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.components.chatonly.ChatOnlyComponent;
import buttondevteam.chat.components.fun.FunComponent;
import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.chat.formatting.ChatFormatter;
import buttondevteam.chat.formatting.TellrawEvent;
import buttondevteam.chat.formatting.TellrawPart;
import buttondevteam.chat.formatting.TellrawSerializer;
import buttondevteam.chat.listener.PlayerListener;
import buttondevteam.core.ComponentManager;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Priority;
import buttondevteam.lib.chat.TellrawSerializableEnum;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.earth2me.essentials.User;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.Objective;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.regex.Pattern;
public class ChatProcessing {
private static final Pattern NULL_MENTION_PATTERN = Pattern.compile("null");
private static final Pattern CYAN_PATTERN = Pattern.compile("cyan");
private static final Pattern ESCAPE_PATTERN = Pattern.compile("\\\\");
private static final Pattern CONSOLE_PING_PATTERN = Pattern.compile("(?i)" + Pattern.quote("@console"));
private static final Pattern HASHTAG_PATTERN = Pattern.compile("#(\\w+)");
private static final Pattern URL_PATTERN = Pattern.compile("(http[\\w:/?=$\\-_.+!*'(),&]+(?:#[\\w]+)?)");
public static final Pattern ENTIRE_MESSAGE_PATTERN = Pattern.compile(".+");
private static final Pattern UNDERLINED_PATTERN = Pattern.compile("_");
private static final Pattern ITALIC_PATTERN = Pattern.compile("\\*");
private static final Pattern BOLD_PATTERN = Pattern.compile("\\*\\*");
private static final Pattern CODE_PATTERN = Pattern.compile("`");
private static final Pattern MASKED_LINK_PATTERN = Pattern.compile("\\[([^\\[\\]]+)]\\(([^()]+)\\)");
private static final Pattern SOMEONE_PATTERN = Pattern.compile("@someone"); //TODO
private static final Pattern STRIKETHROUGH_PATTERN = Pattern.compile("~~");
private static final Pattern SPOILER_PATTERN = Pattern.compile("\\|\\|");
private static final Color[] RainbowPresserColors = new Color[]{Color.Red, Color.Gold, Color.Yellow, Color.Green,
Color.Blue, Color.DarkPurple};
private static final Pattern WORD_PATTERN = Pattern.compile("\\S+");
private static boolean pingedconsole = false;
public static final ChatFormatter ESCAPE_FORMATTER = ChatFormatter.builder().regex(ESCAPE_PATTERN).build();
private static ArrayList<ChatFormatter> commonFormatters = Lists.newArrayList(
ChatFormatter.builder().regex(BOLD_PATTERN).bold(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
.priority(Priority.High).build(),
ChatFormatter.builder().regex(ITALIC_PATTERN).italic(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range).build(),
ChatFormatter.builder().regex(UNDERLINED_PATTERN).underlined(true).removeCharCount((short) 1).type(ChatFormatter.Type.Range)
.build(),
ChatFormatter.builder().regex(STRIKETHROUGH_PATTERN).strikethrough(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
.build(),
ChatFormatter.builder().regex(SPOILER_PATTERN).obfuscated(true).removeCharCount((short) 2).type(ChatFormatter.Type.Range)
.onmatch((match, cf, fs) -> {
cf.setHoverText(match);
return match;
}).build(),
ESCAPE_FORMATTER, ChatFormatter.builder().regex(NULL_MENTION_PATTERN).color(Color.DarkRed).build(), // Properly added a bug as a feature
ChatFormatter.builder().regex(CONSOLE_PING_PATTERN).color(Color.Aqua).onmatch((match, builder, section) -> {
if (!pingedconsole) {
System.out.print("\007");
pingedconsole = true; // Will set it to false in ProcessChat
}
return match;
}).priority(Priority.High).build(),
ChatFormatter.builder().regex(HASHTAG_PATTERN).color(Color.Blue).openlink("https://twitter.com/hashtag/$1")
.priority(Priority.High).build(),
ChatFormatter.builder().regex(CYAN_PATTERN).color(Color.Aqua).build(), // #55
ChatFormatter.builder().regex(CODE_PATTERN).color(Color.DarkGray).removeCharCount((short) 1).type(ChatFormatter.Type.Range)
.build(),
ChatFormatter.builder().regex(MASKED_LINK_PATTERN).underlined(true).onmatch((match, builder, section) -> {
String text, link;
if (section.Matches.size() < 2 || (text = section.Matches.get(0)).length() == 0 || (link = section.Matches.get(1)).length() == 0)
return "";
builder.setOpenlink(link);
return text;
}).type(ChatFormatter.Type.Excluder).build(),
ChatFormatter.builder().regex(URL_PATTERN).underlined(true).openlink("$1").type(ChatFormatter.Type.Excluder).build());
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();
private static final String[] testPlayers = {"Koiiev", "iie", "Alisolarflare", "NorbiPeti", "Arsen_Derby_FTW", "carrot_lynx"};
public static final String MCORIGIN = "Minecraft"; //Shouldn't change, like ever - TBMCPlayer.getFolderForType(TBMCPlayer.class) capitalized
private ChatProcessing() {
}
public static boolean ProcessChat(TBMCChatEvent e) {
Channel channel = e.getChannel();
CommandSender sender = e.getSender();
String message = e.getMessage();
long processstart = System.nanoTime();
Player player = (sender instanceof Player ? (Player) sender : null);
User user = PluginMain.essentials.getUser(player);
if (player != null) {
user.updateActivity(true); //Could talk in a private channel, so broadcast
if (user.isMuted())
return true;
}
doFunStuff(sender, e, message);
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
ArrayList<ChatFormatter> formatters = addFormatters(colormode);
if (colormode == channel.Color().get() && mp != null && mp.RainbowPresserColorMode) { // Only overwrite channel color
final AtomicInteger rpc = new AtomicInteger(0);
formatters.add(ChatFormatter.builder().regex(WORD_PATTERN).color(colormode).onmatch((match, cf, s) -> {
cf.setColor(RainbowPresserColors[rpc.getAndUpdate(i -> ++i < RainbowPresserColors.length ? i : 0)]);
return match;
}).build());
}
pingedconsole = false; // Will set it to true onmatch (static constructor)
final String channelidentifier = getChannelID(channel, e.getOrigin());
TellrawPart json = createTellraw(sender, message, player, mp, e.getUser(), channelidentifier, e.getOrigin());
long combinetime = System.nanoTime();
ChatFormatter.Combine(formatters, message, json);
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()) {
Objective obj = PluginMain.SB.getObjective(channel.ID);
int score = -1;
for (Player p : Bukkit.getOnlinePlayers()) {
final int mcScore;
if (player != null
&& PluginMain.essentials.getUser(p).isIgnoredPlayer(PluginMain.essentials.getUser(player)))
mcScore = -1; // Don't send the message to them
else
mcScore = VanillaUtils.getMCScoreIfChatOn(p, e);
obj.getScore(p.getName())
.setScore(p.getUniqueId().equals(player == null ? null : player.getUniqueId()) // p.UniqueID==player?.UniqueID
? score = mcScore : mcScore);
}
if (score == -1) // Even if the player object isn't null, it may not be in OnlinePlayers
score = e.getMCScore(sender);
if (score < 0) // Never send messages to score below 0
sender.sendMessage("§cYou don't have permission to send this message or something went wrong");
else {
PluginMain.Instance.getServer().dispatchCommand(PluginMain.Console,
String.format("tellraw @a[score_%s=%d,score_%s_min=%d] %s", channel.ID, score, channel.ID,
score, jsonstr));
val tc = ComponentManager.getIfEnabled(TownyComponent.class);
if (tc != null) tc.handleSpies(channel, json, ChatProcessing::toJson);
}
} else
PluginMain.Instance.getServer().dispatchCommand(PluginMain.Console,
String.format("tellraw @a %s", jsonstr));
} catch (Exception ex) {
TBMCCoreAPI.SendException("An error occured while sending a chat message!", ex);
sender.sendMessage("§cAn error occured while sending the message.");
return true;
}
PluginMain.Instance.getServer().getConsoleSender()
.sendMessage(String.format("%s <%s§r> %s", channelidentifier, getSenderName(sender, player), message));
DebugCommand.SendDebugMessage(
"-- Full ChatProcessing time: " + (System.nanoTime() - processstart) / 1000000f + " ms");
DebugCommand.SendDebugMessage("-- ChatFormatter.Combine time: " + combinetime / 1000000f + " ms");
return false;
}
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((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("> "));
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 ("[" + (MCORIGIN.equals(origin) ? "" : "§8" + origin.substring(0, 1) + "§r|") + channel.DisplayName().get())
+ "]";
}
static ArrayList<ChatFormatter> addFormatters(Color colormode) {
@SuppressWarnings("unchecked")
ArrayList<ChatFormatter> formatters = (ArrayList<ChatFormatter>) commonFormatters.clone();
formatters.add(
ChatFormatter.builder().regex(ENTIRE_MESSAGE_PATTERN).color(colormode).priority(Priority.Low).build());
boolean nottest; //Not assigning a default value, so that it can only be used in the if
if ((nottest = Bukkit.getOnlinePlayers().size() > 0) || Bukkit.getVersion().equals("test")) {
StringBuilder namesb = new StringBuilder("(?i)(");
if (nottest)
for (Player p : Bukkit.getOnlinePlayers())
namesb.append(p.getName()).append("|");
else
for (String testPlayer : testPlayers)
namesb.append(testPlayer).append("|");
namesb.deleteCharAt(namesb.length() - 1);
namesb.append(")");
StringBuilder nicksb = new StringBuilder("(?i)(");
boolean addNickFormatter = false;
for (Player p : Bukkit.getOnlinePlayers()) {
final String nick = PlayerListener.nicknames.inverse().get(p.getUniqueId());
if (nick != null) {
nicksb.append(nick).append("|");
addNickFormatter = true; //Add it even if there's only 1 player online (it was in the if)
}
}
nicksb.deleteCharAt(nicksb.length() - 1);
nicksb.append(")");
Consumer<String> error = message -> {
if (PluginMain.Instance != null)
PluginMain.Instance.getLogger().warning(message);
else
System.out.println(message);
};
formatters.add(ChatFormatter.builder().regex(Pattern.compile(namesb.toString())).color(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()) {
error.accept("Error: Can't find player " + match + " but was reported as online.");
return "§c" + match + "§r";
}
ChatPlayer mpp = TBMCPlayer.getPlayer(nottest ? p.getUniqueId() : new UUID(0, 0), ChatPlayer.class);
if (nottest) {
playPingSound(p);
}
String color = String.format("§%x", (mpp.GetFlairColor() == 0x00 ? 0xb : mpp.GetFlairColor()));
return color + (nottest ? p.getName() : pn.get()) + "§r"; //Fix name casing, except when testing
}).priority(Priority.High).type(ChatFormatter.Type.Excluder).build());
if (addNickFormatter)
formatters.add(ChatFormatter.builder().regex((Pattern.compile(nicksb.toString()))).color(Color.Aqua)
.onmatch((match, builder, section) -> {
if (PlayerListener.nicknames.containsKey(match.toLowerCase())) { //Made a stream and all that but I can actually store it lowercased
Player p = Bukkit.getPlayer(PlayerListener.nicknames.get(match.toLowerCase()));
if (p == null) {
error.accept("Error: Can't find player nicknamed "
+ match.toLowerCase() + " but was reported as online.");
return "§c" + match + "§r";
}
playPingSound(p);
return PluginMain.essentials.getUser(p).getNickname();
}
error.accept("Player nicknamed " + match.toLowerCase()
+ " not found in nickname map but was reported as online.");
return "§c" + match + "§r";
}).priority(Priority.High).type(ChatFormatter.Type.Excluder).build());
}
return formatters;
}
private static void playPingSound(Player p) {
if (PluginMain.Instance.notificationSound().get().length() == 0)
p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 0.5f); // TODO: Airhorn
else
p.playSound(p.getLocation(), PluginMain.Instance.notificationSound().get(), 1.0f,
PluginMain.Instance.notificationPitch().get());
}
static void doFunStuff(CommandSender sender, TBMCChatEventBase event, String message) {
val fc=ComponentManager.getIfEnabled(FunComponent.class);
if(fc!=null) fc.onChat(sender, event, message);
}
}

View file

@ -0,0 +1,65 @@
package buttondevteam.chat;
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;
public final class ChatUtils {
public static final String MCORIGIN = "Minecraft"; //Shouldn't change, like ever - TBMCPlayer.getFolderForType(TBMCPlayer.class) capitalized
private ChatUtils() {
}
/**
* Dispatch a console command.
*
* @param command The command
* @param async Whether the caller is async
*/
public static void dispatchConsoleCommand(String command, boolean async) {
if (async)
Bukkit.getScheduler().runTask(PluginMain.Instance, () -> Bukkit.dispatchCommand(PluginMain.Console, command));
else
Bukkit.dispatchCommand(PluginMain.Console, command);
}
/**
* Returns the string between the start and end strings (exclusive).
*
* @param str The original string
* @param start The start string
* @param end The end string
* @return The result string
*/
public static Optional<String> coolSubstring(String str, String start, String end) {
int a = str.indexOf(start) + start.length();
int b = str.indexOf(end, a);
return a != -1 && b != -1 ? Optional.of(str.substring(a, b)) : Optional.empty();
}
/**
* Sends a regular (non-Markdown) chat message. Used as a fallback if the chat processing fails.
*
* @param e The chat event
*/
public static void sendChatMessage(TBMCChatEvent e) {
var str = getMessageString(e.getChannel(), e.getUser(), e.getMessage());
for (Player p : Bukkit.getOnlinePlayers())
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,49 +1,44 @@
package buttondevteam.chat; package buttondevteam.chat;
import buttondevteam.chat.commands.MWikiCommand; 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.HelpCommand;
import buttondevteam.chat.commands.ucmds.HistoryCommand; import buttondevteam.chat.commands.ucmds.HistoryCommand;
import buttondevteam.chat.commands.ucmds.InfoCommand; import buttondevteam.chat.commands.ucmds.InfoCommand;
import buttondevteam.chat.commands.ucmds.ReloadCommand;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand; import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.components.announce.AnnouncerComponent; import buttondevteam.chat.components.announce.AnnouncerComponent;
import buttondevteam.chat.components.appendext.AppendTextComponent; import buttondevteam.chat.components.appendext.AppendTextComponent;
import buttondevteam.chat.components.flair.FlairComponent; import buttondevteam.chat.components.flair.FlairComponent;
import buttondevteam.chat.components.formatter.FormatterComponent;
import buttondevteam.chat.components.fun.FunComponent; import buttondevteam.chat.components.fun.FunComponent;
import buttondevteam.chat.components.towncolors.TownColorComponent; import buttondevteam.chat.components.towncolors.TownColorComponent;
import buttondevteam.chat.components.towny.TownyComponent; import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.chat.listener.PlayerJoinLeaveListener; import buttondevteam.chat.listener.PlayerJoinLeaveListener;
import buttondevteam.chat.listener.PlayerListener; import buttondevteam.chat.listener.PlayerListener;
import buttondevteam.core.MainPlugin;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin; import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component; 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.Color;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import com.earth2me.essentials.Essentials; import com.earth2me.essentials.Essentials;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.scoreboard.Scoreboard;
public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15. 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 PluginMain Instance;
public static ConsoleCommandSender Console; public static ConsoleCommandSender Console;
public static Scoreboard SB; /**
* If enabled, stores and displays the last 10 messages the player can see (public, their town chat etc.)
public ConfigData<String> notificationSound() { * Can be used with the Discord plugin so players can see some of the conversation they missed that's visible on Discord anyways.
return getIConfig().getData("notificationSound", ""); */
} public IConfigData<Boolean> storeChatHistory = getIConfig().getData("storeChatHistory", true);
public ConfigData<Float> notificationPitch() {
return getIConfig().getData("notificationPitch", 1.0f);
}
// Fired when plugin is first enabled // Fired when plugin is first enabled
@Override @Override
@ -53,18 +48,15 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this); TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this);
TBMCCoreAPI.RegisterEventsForExceptions(new PlayerJoinLeaveListener(), this); TBMCCoreAPI.RegisterEventsForExceptions(new PlayerJoinLeaveListener(), this);
MainPlugin.Instance.setChatHandlerEnabled(false); //Disable Core chat handler
Console = this.getServer().getConsoleSender(); Console = this.getServer().getConsoleSender();
SB = getServer().getScoreboardManager().getMainScoreboard(); // Main can be detected with @a[score_...]
if (Bukkit.getPluginManager().isPluginEnabled("Towny")) if (Bukkit.getPluginManager().isPluginEnabled("Towny"))
Component.registerComponent(this, new TownyComponent()); 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()) if (!setupPermissions())
TBMCCoreAPI.SendException("We're in trouble", new Exception("Failed to set up economy or permissions!")); TBMCCoreAPI.SendException("We're in trouble", new Exception("Failed to set up permissions!"), this);
if (Bukkit.getPluginManager().isPluginEnabled("Towny")) if (Bukkit.getPluginManager().isPluginEnabled("Towny"))
Component.registerComponent(this, new TownColorComponent()); Component.registerComponent(this, new TownColorComponent());
@ -72,11 +64,15 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
Component.registerComponent(this, new AnnouncerComponent()); Component.registerComponent(this, new AnnouncerComponent());
Component.registerComponent(this, new FunComponent()); Component.registerComponent(this, new FunComponent());
Component.registerComponent(this, new AppendTextComponent()); Component.registerComponent(this, new AppendTextComponent());
getCommand2MC().registerCommand(new DebugCommand()); Component.registerComponent(this, new FormatterComponent());
getCommand2MC().registerCommand(new HelpCommand()); registerCommand(new DebugCommand());
getCommand2MC().registerCommand(new HistoryCommand()); registerCommand(new HelpCommand());
getCommand2MC().registerCommand(new InfoCommand()); registerCommand(new HistoryCommand());
getCommand2MC().registerCommand(new MWikiCommand()); registerCommand(new InfoCommand());
registerCommand(new MWikiCommand());
registerCommand(new ReloadCommand());
registerCommand(new SnapCommand());
registerCommand(new MeCommand());
} }
public static Essentials essentials = null; public static Essentials essentials = null;
@ -87,7 +83,6 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
} }
public static Permission permission = null; public static Permission permission = null;
public static Economy economy = null;
private boolean setupPermissions() { private boolean setupPermissions() {
RegisteredServiceProvider<Permission> permissionProvider = getServer().getServicesManager() RegisteredServiceProvider<Permission> permissionProvider = getServer().getServicesManager()
@ -98,14 +93,4 @@ public class PluginMain extends ButtonPlugin { // Translated to Java: 2015.07.15
return (permission != null); 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

@ -1,18 +1,141 @@
package buttondevteam.chat; package buttondevteam.chat;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; 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 org.bukkit.entity.Player;
import buttondevteam.lib.TBMCChatEvent; import java.lang.reflect.Array;
import lombok.experimental.UtilityClass; import java.lang.reflect.Method;
import net.minecraft.server.v1_12_R1.EntityHuman.EnumChatVisibility; import java.util.UUID;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
@UtilityClass @UtilityClass
public class VanillaUtils { public class VanillaUtils {
public int getMCScoreIfChatOn(Player p, TBMCChatEvent e) { public String getGroupIfChatOn(Player p, TBMCChatEvent e) {
if (!(p instanceof CraftPlayer) || ((CraftPlayer) p).getHandle().getChatFlags() == EnumChatVisibility.FULL) // Only send if client allows chat try {
return e.getMCScore(p); if (!isChatOn(p)) // Only send if client allows chat
else return null;
return -1; } catch (NoClassDefFoundError ex) {
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;
private boolean isChatOn(Player p) {
try {
if (isChatOn == null) {
val cl = p.getClass();
if (notCraftPlayer(cl)) return true; // p instanceof CraftPlayer
val hm = cl.getMethod("getHandle");
val handle = hm.invoke(p); //p.getHandle()
val vpcl = handle.getClass();
val gcfm = vpcl.getMethod("getChatFlags");
Class<?> encl;
try {
encl = Class.forName(handle.getClass().getPackage().getName() + ".EnumChatVisibility");
} catch (ClassNotFoundException e) {
encl = Class.forName(handle.getClass().getPackage().getName() + ".EntityHuman$EnumChatVisibility");
}
val ff = encl.getField("FULL");
val full = ff.get(null); // EnumChatVisibility.FULL
isChatOn = pl -> {
try {
if (notCraftPlayer(pl.getClass())) return true; //Need to check each time
val ph = hm.invoke(pl); //pl.getHandle()
val flags = gcfm.invoke(ph); //handle.getChatFlags()
return flags == full;
} catch (Exception e) {
e.printStackTrace();
return true;
}
};
}
return isChatOn.test(p);
} catch (Exception e) {
e.printStackTrace();
return true;
}
}
/*private String version;
public short getMCVersion() {
if (version != null) return version;
val v = ChatUtils.coolSubstring(Bukkit.getServer().getVersion().getClass().getPackage().getName(),
"org.bukkit.craftbukkit.v", "_R1").orElse("1_8").replace("_", "");
return Short.parseShort(v);
}*/
private BiPredicate<Player, String> tellRaw;
public boolean tellRaw(Player p, String json) {
try {
if (tellRaw == null) {
val pcl = p.getClass();
if (notCraftPlayer(pcl)) return false;
val hm = pcl.getMethod("getHandle");
val handle = hm.invoke(p);
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");
val encl = Class.forName(nms + ".Entity");
val ffdm = ccucl.getMethod("filterForDisplay", iclcl, chatcompcl, encl);*/
val cscl = Class.forName(chatcompcl.getName() + "$ChatSerializer");
val am = cscl.getMethod("a", String.class);
tellRaw = (pl, jsonStr) -> {
if (notCraftPlayer(pl.getClass())) return false;
try {
val hhandle = hm.invoke(pl);
val deserialized = am.invoke(null, jsonStr);
//val filtered = ffdm.invoke(null, hhandle, deserialized, hhandle);
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();
return false;
}
};
}
/*((CraftPlayer) p).getHandle().sendMessage(ChatComponentUtils
.filterForDisplay(((CraftPlayer) p).getHandle(),
IChatBaseComponent.ChatSerializer.a(json), ((CraftPlayer) p).getHandle()));*/
return tellRaw.test(p, json);
} catch (Exception e) {
PluginMain.Instance.getLogger().warning("Could not use tellRaw: " + e.getMessage());
return false;
}
}
private boolean notCraftPlayer(Class<?> cl) {
return !cl.getSimpleName().contains("CraftPlayer");
} }
} }

View file

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

@ -0,0 +1,30 @@
package buttondevteam.chat.commands;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.Random;
@CommandClass(modOnly = true, helpText = {
"Snap",
"Perfectly balanced as all things should be."
})
public class SnapCommand extends ICommand2MC {
@Command2.Subcommand
public void def(CommandSender sender) {
val pls = new ArrayList<>(Bukkit.getOnlinePlayers());
int target = pls.size() / 2;
Random rand = new Random();
//noinspection SuspiciousMethodCalls
if (pls.remove(sender) && target > 0)
target--; //The sender isn't kicked, so kick someone else
while (pls.size() > target)
pls.remove(rand.nextInt(pls.size())).kickPlayer("You were saved by Thanos.");
sender.sendMessage(target + " grateful players remain.");
}
}

View file

@ -1,14 +1,9 @@
package buttondevteam.chat.commands.ucmds; package buttondevteam.chat.commands.ucmds;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.CustomTabComplete;
import buttondevteam.lib.chat.TBMCCommandBase;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
@CommandClass(modOnly = false, helpText = { @CommandClass(modOnly = false, helpText = {
"Help", "Help",
@ -16,10 +11,11 @@ import java.util.ArrayList;
}) })
public final class HelpCommand extends UCommandBase { public final class HelpCommand extends UCommandBase {
@Command2.Subcommand @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) { if (topicOrCommand == null) {
sender.sendMessage(new String[]{ sender.sendMessage(new String[]{
"§6---- Thorpe Help ----", "§6---- Chroma Help ----",
"Do /u help <topic> for more info", "Do /u help <topic> for more info",
"Do /u help <commandname> [subcommands] for more info about a command", "Do /u help <commandname> [subcommands] for more info about a command",
"Topics:", "Topics:",
@ -31,45 +27,33 @@ public final class HelpCommand extends UCommandBase {
} }
if (topicOrCommand.equalsIgnoreCase("chat")) if (topicOrCommand.equalsIgnoreCase("chat"))
sender.sendMessage(new String[]{"§6---- Chat features ----", 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)", "-- [TC] Town chat (/tc)", "-- [NC] Nation chat (/nc)",
"- Playernames: Hover over them to get some player info", "- 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")) { else if (topicOrCommand.equalsIgnoreCase("commands")) {
ArrayList<String> text = new ArrayList<String>(); sender.sendMessage(getManager().getCommandsText());
text.add("§6---- Command list ----");
for (TBMCCommandBase cmd : TBMCChatAPI.GetCommands().values())
if (!cmd.getClass().getAnnotation(CommandClass.class).modOnly() || PluginMain.permission.has(sender, "tbmc.admin"))
if (!cmd.isPlayerOnly() || sender instanceof Player)
if (!cmd.GetCommandPath().contains(" "))
text.add("/" + cmd.GetCommandPath());
else {
final String topcmd = cmd.GetCommandPath().substring(0, cmd.GetCommandPath().indexOf(' '));
if (!text.contains("/" + topcmd))
text.add("/" + topcmd);
}
sender.sendMessage(text.toArray(new String[0]));
} else if (topicOrCommand.equalsIgnoreCase("colors")) { } else if (topicOrCommand.equalsIgnoreCase("colors")) {
sender.sendMessage(new String[]{"§6---- Chat colors/formats ----", // sender.sendMessage(new String[]{"§6---- Chat colors/formats ----", //
"Tellraw name - Code | Tellraw name - Code", // "Tellraw name - Code | Tellraw name - Code", //
"§0black - &0 | §1dark_blue - &1", // "§0black - &0§r | §1dark_blue - &1§r", //
"§2dark_green - &2 | §3dark_aqua - &3", // "§2dark_green - &2§r | §3dark_aqua - &3§r", //
"§4dark_red - &4 | §5dark_purple - &5", // "§4dark_red - &4§r | §5dark_purple - &5§r", //
"§6gold - &6 | §7gray - &7", // "§6gold - &6§r | §7gray - &7§r", //
"§8dark_gray - &8 | §9blue - &9", // "§8dark_gray - &8§r | §9blue - &9§r", //
"§agreen - &a | §baqua - &b", // "§agreen - &a§r | §baqua - &b§r", //
"§cred - &c | §dlight_purple - &d", // "§cred - &c§r | §dlight_purple - &d§r", //
"§eyellow - &e | §fwhite - &f", // "§eyellow - &e§r | §fwhite - &f§r", //
"§rreset - &r | §kk§robfuscated - &k", // "§rreset - &r§r | §kk§robfuscated - &k§r", //
"§lbold - &l | §mstrikethrough - &m", // "§lbold - &l§r | §mstrikethrough - &m§r", //
"§nunderline - &n | §oitalic - &o", // "§nunderline - &n§r | §oitalic - &o§r", //
"The format codes in tellraw should be used like \"italic\":\"true\""}); // "The format codes in tellraw should be used like \"italic\":\"true\""}); //
} else { } else {
String[] text = getManager().getHelpText(topicOrCommand); String[] text = getManager().getCommandNode(topicOrCommand).getData().getHelpText(sender); // TODO: This only works for the main command, not subcommands
if (text == null) if (text == null) // TODO: Null check for command node
sender.sendMessage( sender.sendMessage(
new String[]{"§cError: Command not found: " + topicOrCommand, new String[]{"§cError: Command not found: " + topicOrCommand,
"Usage example: /u accept --> /u help u accept"}); "Usage example: /u accept --> /u help u accept"});
else else
sender.sendMessage(text); sender.sendMessage(text);
} }

View file

@ -1,13 +1,13 @@
package buttondevteam.chat.commands.ucmds; package buttondevteam.chat.commands.ucmds;
import buttondevteam.chat.PluginMain;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.ChatMessage; import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import lombok.RequiredArgsConstructor; import buttondevteam.lib.chat.CustomTabCompleteMethod;
import lombok.experimental.var; import buttondevteam.lib.player.ChromaGamerBase;
import lombok.val; import lombok.val;
import org.bukkit.command.CommandSender;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -15,28 +15,32 @@ import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
@CommandClass(helpText = { @CommandClass(helpText = {
"§6--- Chat History ----", // "Chat History", //
"Returns the last 10 messages the player can see." // "Returns the last 10 messages the player can see." //
}) })
public class HistoryCommand extends UCommandBase { public class HistoryCommand extends UCommandBase {
/** /**
* Key: ChannelID_groupID * Key: ChannelID_groupID
*/ */
private static HashMap<String, LinkedList<HistoryEntry>> messages = new HashMap<>(); private static final HashMap<String, LinkedList<HistoryEntry>> messages = new HashMap<>();
@Command2.Subcommand @Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg String channel) { public boolean def(ChromaGamerBase sender, @Command2.OptionalArg String channel) {
return showHistory(sender, channel); return showHistory(sender, channel);
} }
public static boolean showHistory(CommandSender sender, String channel) { public static boolean showHistory(ChromaGamerBase sender, String channel) {
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 if (!PluginMain.Instance.storeChatHistory.get()) {
sender.sendMessage("§6Chat history is disabled");
return true;
}
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 ----"); sender.sendMessage("§6---- Chat History ----");
Stream<Channel> stream; Stream<Channel> stream;
if (channel == null) { if (channel == null) {
stream = Channel.getChannels(); stream = Channel.getChannels();
} else { } 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()) { if (!och.isPresent()) {
sender.sendMessage("§cChannel not found. Use the ID, for example: /u history g"); sender.sendMessage("§cChannel not found. Use the ID, for example: /u history g");
return true; return true;
@ -44,35 +48,41 @@ public class HistoryCommand extends UCommandBase {
stream = Stream.of(och.get()); stream = Stream.of(och.get());
} }
AtomicBoolean sent = new AtomicBoolean(); AtomicBoolean sent = new AtomicBoolean();
val arr = stream.map(getThem).filter(Objects::nonNull).flatMap(Collection::stream) synchronized (messages) {
val arr = stream.map(getThem).filter(Objects::nonNull).flatMap(Collection::stream)
.sorted(Comparator.comparingLong(he -> he.timestamp)).toArray(HistoryEntry[]::new); .sorted(Comparator.comparingLong(he -> he.timestamp)).toArray(HistoryEntry[]::new);
for (int i = Math.max(0, arr.length - 10); i < arr.length; i++) { for (int i = Math.max(0, arr.length - 10); i < arr.length; i++) {
HistoryEntry e = arr[i]; HistoryEntry e = arr[i];
val cm = e.chatMessage; 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); sent.set(true);
}
} }
if (!sent.get()) if (!sent.get())
sender.sendMessage("No messages can be found."); sender.sendMessage("No messages can be found.");
return true; return true;
} }
@RequiredArgsConstructor @CustomTabCompleteMethod(param = "channel")
private static class HistoryEntry { public Iterable<String> def() {
/** return Channel.getChannels().map(Channel::getIdentifier)::iterator;
* System.nanoTime() }
*/
private final long timestamp; /**
private final ChatMessage chatMessage; * @param timestamp System.nanoTime()
private final Channel channel; */
private record HistoryEntry(long timestamp, ChatMessage chatMessage, Channel channel) {
} }
public static void addChatMessage(ChatMessage chatMessage, Channel channel) { public static void addChatMessage(ChatMessage chatMessage, Channel channel) {
if (!PluginMain.Instance.storeChatHistory.get()) return;
val groupID = channel.getGroupID(chatMessage.getPermCheck()); val groupID = channel.getGroupID(chatMessage.getPermCheck());
if (groupID == null) return; //Just to be sure if (groupID == null) return; //Just to be sure
var ll = messages.computeIfAbsent(channel.ID + "_" + groupID, k -> new LinkedList<>()); //<-- TIL synchronized (messages) {
ll.add(new HistoryEntry(System.nanoTime(), chatMessage, channel)); //Adds as last element var ll = messages.computeIfAbsent(channel.getIdentifier() + "_" + groupID, k -> new LinkedList<>()); //<-- TIL
while (ll.size() > 10) ll.add(new HistoryEntry(System.nanoTime(), chatMessage, channel)); //Adds as last element
ll.remove(); //Removes the first element while (ll.size() > 10)
ll.remove(); //Removes the first element
}
} }
} }

View file

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

View file

@ -0,0 +1,20 @@
package buttondevteam.chat.commands.ucmds;
import buttondevteam.chat.PluginMain;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import org.bukkit.command.CommandSender;
@CommandClass(helpText = {
"Reload",
"Reloads the config"
}, modOnly = true)
public class ReloadCommand extends UCommandBase {
@Command2.Subcommand
public void def(CommandSender sender) {
if (PluginMain.Instance.tryReloadConfig())
sender.sendMessage("§bReloaded config");
else
sender.sendMessage("§cFailed to reload config.");
}
}

View file

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

View file

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

View file

@ -1,15 +1,22 @@
package buttondevteam.chat.components.announce; package buttondevteam.chat.components.announce;
import buttondevteam.chat.commands.ucmds.UCommandBase; import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.formatter.FormatterComponent;
import buttondevteam.core.ComponentManager;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; 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) @CommandClass(modOnly = true)
@OptionallyPlayerCommandClass(playerOnly = false)
@RequiredArgsConstructor @RequiredArgsConstructor
public class AnnounceCommand extends UCommandBase { public class AnnounceCommand extends UCommandBase {
private final AnnouncerComponent component; private final AnnouncerComponent component;
@ -20,7 +27,7 @@ public class AnnounceCommand extends UCommandBase {
}) })
public boolean add(CommandSender sender, @Command2.TextArg String text) { public boolean add(CommandSender sender, @Command2.TextArg String text) {
String finalmessage = text.replace('&', '§'); String finalmessage = text.replace('&', '§');
component.AnnounceMessages().get().add(finalmessage); component.announceMessages.get().add(finalmessage);
sender.sendMessage("§bAnnouncement added.§r"); sender.sendMessage("§bAnnouncement added.§r");
return true; return true;
} }
@ -28,15 +35,15 @@ public class AnnounceCommand extends UCommandBase {
@Command2.Subcommand(helpText = { @Command2.Subcommand(helpText = {
"Edit announcement", "Edit announcement",
"This command lets you edit an announcement by its index.", "This command lets you edit an announcement by its index.",
"Shouldn't be used directly, use either command blocks or click on an announcement in /u announce list (WIP) instead." //TODO: <-- "Shouldn't be used directly, use either command blocks or click on an announcement in /u announce list instead."
}) })
public boolean edit(CommandSender sender, byte index, @Command2.TextArg String text) { public boolean edit(CommandSender sender, byte index, @Command2.TextArg String text) {
String finalmessage1 = text.replace('&', '§'); String finalmessage1 = text.replace('&', '§');
if (index > 100) if (index > 100)
return false; return false;
while (component.AnnounceMessages().get().size() <= index) while (component.announceMessages.get().size() <= index)
component.AnnounceMessages().get().add(""); component.announceMessages.get().add("");
component.AnnounceMessages().get().set(index, finalmessage1); component.announceMessages.get().set(index, finalmessage1);
sender.sendMessage("Announcement edited."); sender.sendMessage("Announcement edited.");
return true; return true;
} }
@ -49,10 +56,19 @@ public class AnnounceCommand extends UCommandBase {
sender.sendMessage("§bList of announce messages:§r"); sender.sendMessage("§bList of announce messages:§r");
sender.sendMessage("§bFormat: [index] message§r"); sender.sendMessage("§bFormat: [index] message§r");
int i = 0; int i = 0;
for (String message : component.AnnounceMessages().get()) for (String message : component.announceMessages.get()) {
sender.sendMessage("[" + i++ + "] " + message); String msg = "[" + i++ + "] " + message;
//noinspection SuspiciousMethodCalls
if (!ComponentManager.isEnabled(FormatterComponent.class) || !Bukkit.getOnlinePlayers().contains(sender)) {
sender.sendMessage(msg);
continue;
}
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: " 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; return true;
} }
@ -61,7 +77,7 @@ public class AnnounceCommand extends UCommandBase {
"This command removes an announcement" "This command removes an announcement"
}) })
public boolean remove(CommandSender sender, int index) { 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; if (index < 0 || index > msgs.size()) return false;
msgs.remove(index); msgs.remove(index);
sender.sendMessage("Announcement removed."); sender.sendMessage("Announcement removed.");
@ -73,7 +89,7 @@ public class AnnounceCommand extends UCommandBase {
"This command sets the time between the announcements" "This command sets the time between the announcements"
}) })
public boolean settime(CommandSender sender, int minutes) { 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"); sender.sendMessage("Time set between announce messages to " + minutes + " minutes");
return true; return true;
} }

View file

@ -4,38 +4,46 @@ import buttondevteam.chat.PluginMain;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.architecture.config.IListConfigData;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import java.util.ArrayList; 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 { public class AnnouncerComponent extends Component<PluginMain> implements Runnable {
public ConfigData<ArrayList<String>> AnnounceMessages() { /**
return getConfig().getData("announceMessages", new ArrayList<>(0)); * The messages to display to players.
} */
public IListConfigData<String> announceMessages = getConfig().getListData("announceMessages", Collections.emptyList());
public ConfigData<Integer> AnnounceTime() { /**
return getConfig().getData("announceTime", 15 * 60 * 1000); * The time in milliseconds between the messages. Use /u announce settime to set minutes.
} */
public IConfigData<Integer> announceTime = getConfig().getData("announceTime", 15 * 60 * 1000);
private TBMCSystemChatEvent.BroadcastTarget target; private TBMCSystemChatEvent.BroadcastTarget target;
private static int AnnounceMessageIndex = 0; private int AnnounceMessageIndex = 0;
@Override @Override
public void run() { public void run() {
while (isEnabled()) { while (isEnabled()) {
try { try {
Thread.sleep(AnnounceTime().get()); Thread.sleep(announceTime.get());
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
if (Bukkit.getOnlinePlayers().size() == 0) continue; //Don't post to Discord if nobody is on if (Bukkit.getOnlinePlayers().size() == 0) continue; //Don't post to Discord if nobody is on
if (AnnounceMessages().get().size() > AnnounceMessageIndex) { if (announceMessages.get().size() > AnnounceMessageIndex) {
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, AnnounceMessages().get().get(AnnounceMessageIndex), target); TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, announceMessages.get().get(AnnounceMessageIndex), target);
AnnounceMessageIndex++; AnnounceMessageIndex++;
if (AnnounceMessageIndex == AnnounceMessages().get().size()) if (AnnounceMessageIndex == announceMessages.get().size())
AnnounceMessageIndex = 0; AnnounceMessageIndex = 0;
} }
} }
@ -43,7 +51,7 @@ public class AnnouncerComponent extends Component<PluginMain> implements Runnabl
@Override @Override
protected void enable() { protected void enable() {
target= TBMCSystemChatEvent.BroadcastTarget.add("announcements"); target = TBMCSystemChatEvent.BroadcastTarget.add("announcements");
registerCommand(new AnnounceCommand(this)); registerCommand(new AnnounceCommand(this));
new Thread(this).start(); new Thread(this).start();
} }

View file

@ -2,12 +2,10 @@ package buttondevteam.chat.components.appendext;
import buttondevteam.chat.PluginMain; import buttondevteam.chat.PluginMain;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.IHaveConfig; import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.chat.*; import buttondevteam.lib.chat.*;
import buttondevteam.lib.player.ChromaGamerBase;
import lombok.val; import lombok.val;
import org.bukkit.command.CommandSender;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;
@ -15,18 +13,22 @@ import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.function.Consumer; import java.util.function.Consumer;
/**
* Allows players to append tableflips and other things to their messages. Everything is configurable here.
*/
public class AppendTextComponent extends Component<PluginMain> { public class AppendTextComponent extends Component<PluginMain> {
private Map<String, IHaveConfig> appendTexts; 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", // "Tableflip", //
"This command appends a tableflip after your message", // "This command appends a tableflip after your message", //
"Or just makes you tableflip", // "Or just makes you tableflip", //
}); });
} }
private ConfigData<String> appendedText(IHaveConfig config) { private IConfigData<String> appendedText(IHaveConfig config) {
return config.getData("appendedText", () -> "tableflip"); return config.getData("appendedText", "tableflip");
} }
@Override @Override
@ -64,7 +66,7 @@ public class AppendTextComponent extends Component<PluginMain> {
}); });
appendedText(conf).set("( ͡° ͜ʖ ͡°)"); appendedText(conf).set("( ͡° ͜ʖ ͡°)");
}); });
map.put("ww", conf -> { map.put("waitwhat", conf -> {
helpText(conf).set(new String[]{ helpText(conf).set(new String[]{
"Wait what", // "Wait what", //
"Wait what" // "Wait what" //
@ -97,9 +99,9 @@ public class AppendTextComponent extends Component<PluginMain> {
} }
@Command2.Subcommand @Command2.Subcommand
public void def(CommandSender sender, @Command2.OptionalArg @Command2.TextArg String message) { public void def(Command2MCSender sender, @Command2.OptionalArg @Command2.TextArg String message) {
TBMCChatAPI.SendChatMessage(ChatMessage.builder(sender, ChromaGamerBase.getFromSender(sender), TBMCChatAPI.sendChatMessage(ChatMessage.builder(sender.getSender(),
(message == null ? "" : message + " ") + appendedText).fromCommand(true).build()); (message == null ? "" : message + " ") + appendedText).fromCommand(true).permCheck(sender.getPermCheck()).build());
} }
@Override @Override

View file

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

View file

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

View file

@ -6,10 +6,8 @@ import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Timer; import java.util.Timer;
@ -19,30 +17,28 @@ import java.util.Timer;
"Accepts a flair from Reddit", // "Accepts a flair from Reddit", //
"Use /u accept <username> if you commented from multiple accounts" "Use /u accept <username> if you commented from multiple accounts"
}) })
@OptionallyPlayerCommandClass(playerOnly = true)
@RequiredArgsConstructor @RequiredArgsConstructor
public class AcceptCommand extends UCommandBase { public class AcceptCommand extends UCommandBase {
private final FlairComponent component; private final FlairComponent component;
@Command2.Subcommand @Command2.Subcommand
public boolean def(CommandSender sender, @Command2.OptionalArg String username) { public boolean def(Player player, @Command2.OptionalArg String username) {
final Player player = (Player) sender;
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class); 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>"); player.sendMessage("§9Multiple users commented your name. §bPlease pick one using /u accept <username>");
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("§6Usernames:"); sb.append("§6Usernames:");
for (String name : p.UserNames()) for (String name : p.UserNames.get())
sb.append(" ").append(name); sb.append(" ").append(name);
player.sendMessage(sb.toString()); player.sendMessage(sb.toString());
return true; 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("§cError: You need to write your username to the reddit thread§r");
player.sendMessage(component.FlairThreadURL().get()); player.sendMessage(component.flairThreadURL.get());
return true; return true;
} }
if (username != null && !p.UserNames().contains(username)) { if (username != null && !p.UserNames.get().contains(username)) {
player.sendMessage("§cError: Unknown name: " + username + "§r"); player.sendMessage("§cError: Unknown name: " + username + "§r");
return true; return true;
} }
@ -51,14 +47,14 @@ public class AcceptCommand extends UCommandBase {
return true; 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"); player.sendMessage("§cYou already have this user's flair.§r");
return true; return true;
} }
if (username != null) if (username != null)
p.UserName().set(username); p.UserName.set(username);
else else
p.UserName().set(p.UserNames().get(0)); p.UserName.set(p.UserNames.get().get(0));
player.sendMessage("§bObtaining flair..."); player.sendMessage("§bObtaining flair...");
p.Working = true; p.Working = true;
@ -69,22 +65,19 @@ public class AcceptCommand extends UCommandBase {
try { try {
component.DownloadFlair(mp); component.DownloadFlair(mp);
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException( TBMCCoreAPI.SendException("An error occured while downloading flair for " + player.getCustomName() + "!", e, component);
"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.");
player.sendMessage(
"Sorry, but an error occured while trying to get your flair. Please contact a mod.");
mp.Working = false; mp.Working = false;
return; return;
} }
if (mp.FlairState().get().equals(FlairStates.Commented)) { if (mp.FlairState.get().equals(FlairStates.Commented)) {
player.sendMessage( 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.");
"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; mp.Working = false;
return; return;
} }
String flair = mp.GetFormattedFlair(); String flair = mp.GetFormattedFlair();
mp.FlairState().set(FlairStates.Accepted); mp.FlairState.set(FlairStates.Accepted);
FlairComponent.ConfirmUserMessage(mp); FlairComponent.ConfirmUserMessage(mp);
player.sendMessage("§bYour flair has been set:§r " + flair); player.sendMessage("§bYour flair has been set:§r " + flair);
mp.Working = false; mp.Working = false;

View file

@ -4,28 +4,30 @@ import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.PluginMain; import buttondevteam.chat.PluginMain;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.player.TBMCPlayerBase; import buttondevteam.lib.player.TBMCPlayerBase;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.text.SimpleDateFormat; import java.util.HashSet;
import java.util.*; 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.
* This was the original goal of this plugin when it was made.
*/
@ComponentMetadata(enabledByDefault = false)
public class FlairComponent extends Component<PluginMain> { public class FlairComponent extends Component<PluginMain> {
ConfigData<String> FlairThreadURL() { /**
return getConfig().getData("flairThreadURL", "https://www.reddit.com/r/Chromagamers/comments/51ys94/flair_thread_for_the_mc_server/"); * The Reddit thread to check for account connections. Re-enable the component if this was empty.
} */
IConfigData<String> flairThreadURL = getConfig().getData("flairThreadURL", "");
/** /**
* <p> * <p>
@ -35,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 * It's used because normally it has to load all associated player files every time to read the flair state
* </p> * </p>
*/ */
private Set<String> PlayersWithFlairs = new HashSet<>(); private final Set<String> PlayersWithFlairs = new HashSet<>();
@Override @Override
protected void enable() { protected void enable() {
@ -52,9 +54,9 @@ public class FlairComponent extends Component<PluginMain> {
private void FlairGetterThreadMethod() { private void FlairGetterThreadMethod() {
int errorcount = 0; int errorcount = 0;
while (isEnabled()) { while (isEnabled() && flairThreadURL.get().length() > 0) {
try { 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") JsonArray json = new JsonParser().parse(body).getAsJsonArray().get(1).getAsJsonObject().get("data")
.getAsJsonObject().get("children").getAsJsonArray(); .getAsJsonObject().get("children").getAsJsonArray();
for (Object obj : json) { for (Object obj : json) {
@ -74,20 +76,19 @@ public class FlairComponent extends Component<PluginMain> {
ign = ign.trim(); ign = ign.trim();
if (PlayersWithFlairs.contains(ign)) if (PlayersWithFlairs.contains(ign))
continue; continue;
try (ChatPlayer mp = TBMCPlayerBase.getFromName(ign, ChatPlayer.class)) { // Loads player file ChatPlayer mp = TBMCPlayerBase.getFromName(ign, ChatPlayer.class); // Loads player file
if (mp == null) if (mp == null)
continue; continue;
/* /*
* if (!JoinedBefore(mp, 2015, 6, 5)) continue; * if (!JoinedBefore(mp, 2015, 6, 5)) continue;
*/ */
if (!mp.UserNames().contains(author)) if (!mp.UserNames.get().contains(author))
mp.UserNames().add(author); mp.UserNames.get().add(author);
if (mp.FlairState().get().equals(FlairStates.NoComment)) { if (mp.FlairState.get().equals(FlairStates.NoComment)) {
mp.FlairState().set(FlairStates.Commented); mp.FlairState.set(FlairStates.Commented);
ConfirmUserMessage(mp); ConfirmUserMessage(mp);
}
PlayersWithFlairs.add(ign); // Don't redownload even if flair isn't accepted
} }
PlayersWithFlairs.add(ign); // Don't redownload even if flair isn't accepted
} }
} catch (Exception e) { } catch (Exception e) {
errorcount++; errorcount++;
@ -95,7 +96,7 @@ public class FlairComponent extends Component<PluginMain> {
errorcount = 0; errorcount = 0;
if (!e.getMessage().contains("Server returned HTTP response code") if (!e.getMessage().contains("Server returned HTTP response code")
&& !(e instanceof UnknownHostException)) && !(e instanceof UnknownHostException))
TBMCCoreAPI.SendException("Error while getting flairs from Reddit!", e); TBMCCoreAPI.SendException("Error while getting flairs from Reddit!", e, this);
} }
} }
try { try {
@ -104,11 +105,12 @@ public class FlairComponent extends Component<PluginMain> {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
} }
} }
void DownloadFlair(ChatPlayer mp) throws IOException { void DownloadFlair(ChatPlayer mp) throws IOException {
String[] flairdata = TBMCCoreAPI 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(":"); .replace("\"", "").split(":");
String flair; String flair;
if (flairdata.length > 1) if (flairdata.length > 1)
@ -120,12 +122,12 @@ public class FlairComponent extends Component<PluginMain> {
flairclass = flairdata[2]; flairclass = flairdata[2];
else else
flairclass = "unknown"; 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) { private void SetFlair(ChatPlayer p, String text, String flairclass, String username) {
p.UserName().set(username); p.UserName.set(username);
p.FlairState().set(FlairStates.Recognised); p.FlairState.set(FlairStates.Recognised);
switch (flairclass) { switch (flairclass) {
case "cheater": case "cheater":
p.SetFlair(Short.parseShort(text), true); p.SetFlair(Short.parseShort(text), true);
@ -141,9 +143,9 @@ public class FlairComponent extends Component<PluginMain> {
p.SetFlair(ChatPlayer.FlairTimeCantPress); p.SetFlair(ChatPlayer.FlairTimeCantPress);
} }
} catch (Exception e) { } catch (Exception e) {
p.FlairState().set(FlairStates.Commented); // Flair unknown p.FlairState.set(FlairStates.Commented); // Flair unknown
p.SetFlair(ChatPlayer.FlairTimeNone); 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; return;
default: default:
@ -157,7 +159,7 @@ public class FlairComponent extends Component<PluginMain> {
} }
private static boolean JoinedBefore(ChatPlayer mp, int year, int month, int day) throws Exception { 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(); URLConnection con = url.openConnection();
con.setRequestProperty("User-Agent", "TheButtonAutoFlair"); con.setRequestProperty("User-Agent", "TheButtonAutoFlair");
InputStream in = con.getInputStream(); InputStream in = con.getInputStream();
@ -171,13 +173,14 @@ public class FlairComponent extends Component<PluginMain> {
joindate = joindate.split("T")[0]; joindate = joindate.split("T")[0];
Date date = parserSDF.parse(joindate); Date date = parserSDF.parse(joindate);
return date.before(new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("UTC")).setDate(year, month, day) 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) { public static void ConfirmUserMessage(ChatPlayer mp) {
Player p = Bukkit.getPlayer(mp.getUUID()); Player p = Bukkit.getPlayer(mp.getUniqueId());
if (mp.FlairState().get().equals(FlairStates.Commented) && p != null) if (mp.FlairState.get().equals(FlairStates.Commented) && p != null)
if (mp.UserNames().size() > 1) if (mp.UserNames.get().size() > 1)
p.sendMessage( p.sendMessage(
"§9Multiple Reddit users commented your name. You can select with /u accept.§r §6Type /u accept or /u ignore§r"); "§9Multiple Reddit users commented your name. You can select with /u accept.§r §6Type /u accept or /u ignore§r");
else else

View file

@ -4,33 +4,29 @@ import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.commands.ucmds.UCommandBase; import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@CommandClass(modOnly = false, helpText = { @CommandClass(modOnly = false, helpText = {
"Ignore flair", "Ignore flair",
"Stop the \"write your name in the thread\" message from showing up" "Stop the \"write your name in the thread\" message from showing up"
}) })
@OptionallyPlayerCommandClass(playerOnly = true)
public final class IgnoreCommand extends UCommandBase { public final class IgnoreCommand extends UCommandBase {
@Command2.Subcommand @Command2.Subcommand
public boolean def(CommandSender sender) { public boolean def(Player player) {
final Player player = (Player) sender;
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class); 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."); player.sendMessage("§cYou can only ignore the \"write your name in the thread\" message.");
return true; 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."); player.sendMessage("Sorry, but your flair isn't recorded. Please ask a mod to set it for you.");
return true; return true;
} }
if (!p.FlairState().get().equals(FlairStates.Ignored)) { if (!p.FlairState.get().equals(FlairStates.Ignored)) {
p.FlairState().set(FlairStates.Ignored); p.FlairState.set(FlairStates.Ignored);
p.SetFlair(ChatPlayer.FlairTimeNone); p.SetFlair(ChatPlayer.FlairTimeNone);
p.UserName().set(""); p.UserName.set("");
player.sendMessage("§bYou have ignored the message.§r"); player.sendMessage("§bYou have ignored the message.§r");
} else } else
player.sendMessage("§cYou already ignored the message.§r"); player.sendMessage("§cYou already ignored the message.§r");

View file

@ -10,7 +10,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@CommandClass(helpText = { @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]", "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 1: /u admin setflair NorbiPeti 19 false NorbiPeti --> orange (19s)",
"Example 2: /u admin setflair iie 0 true asde --> purple (0s)" "Example 2: /u admin setflair iie 0 true asde --> purple (0s)"
@ -23,7 +23,7 @@ public class SetFlairCommand extends AdminCommandBase {
sender.sendMessage("§cPlayer not found.&r"); sender.sendMessage("§cPlayer not found.&r");
return true; return true;
} }
short ft = 0x00; short ft;
if (flairtime.equalsIgnoreCase("non-presser")) if (flairtime.equalsIgnoreCase("non-presser"))
ft = ChatPlayer.FlairTimeNonPresser; ft = ChatPlayer.FlairTimeNonPresser;
else if (flairtime.equalsIgnoreCase("cant-press")) else if (flairtime.equalsIgnoreCase("cant-press"))
@ -35,22 +35,21 @@ public class SetFlairCommand extends AdminCommandBase {
ft = Short.parseShort(flairtime); ft = Short.parseShort(flairtime);
} catch (Exception e) { } catch (Exception e) {
sender.sendMessage( 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; return true;
} }
} }
ChatPlayer mp = TBMCPlayerBase.getPlayer(p.getUniqueId(), ChatPlayer.class); ChatPlayer mp = TBMCPlayerBase.getPlayer(p.getUniqueId(), ChatPlayer.class);
mp.SetFlair(ft, cheater); mp.SetFlair(ft, cheater);
mp.FlairState().set(FlairStates.Accepted); mp.FlairState.set(FlairStates.Accepted);
if (username == null) if (username == null)
mp.UserName().set(""); mp.UserName.set("");
else { else {
mp.UserName().set(username); mp.UserName.set(username);
if (!mp.UserNames().contains(username)) if (!mp.UserNames.get().contains(username))
mp.UserNames().add(username); mp.UserNames.get().add(username);
} }
sender.sendMessage( sender.sendMessage("§bThe flair has been set. Player: " + mp.getPlayerName() + " Flair: " + mp.GetFormattedFlair() + "§r");
"§bThe flair has been set. Player: " + mp.PlayerName() + " Flair: " + mp.GetFormattedFlair() + "§r");
return true; return true;
} }

View file

@ -0,0 +1,318 @@
package buttondevteam.chat.components.formatter;
import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.ChatUtils;
import buttondevteam.chat.PluginMain;
import buttondevteam.chat.VanillaUtils;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.components.chatonly.ChatOnlyComponent;
import buttondevteam.chat.components.formatter.formatting.*;
import buttondevteam.chat.components.fun.FunComponent;
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.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 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;
import org.bukkit.entity.Player;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
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 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 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()),
new RangeMatchProvider("italic2", "_", FormatSettings.builder().italic(true).build()),
new RangeMatchProvider("strikethrough", "~~", FormatSettings.builder().strikethrough(true).build()),
new RangeMatchProvider("spoiler", "||", FormatSettings.builder().obfuscated(true)
.onmatch((match, cf, fs) -> {
cf.setHoverText(match);
return match;
}).build()),
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");
pingedconsole = true; // Will set it to false in ProcessChat
}
return "@console";
}).build(), true, "@console"),
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 "[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(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());
var playerC = new Random().nextInt(players.size());
var player = players.get(playerC);
playPingSound(player, ComponentManager.getIfEnabled(FormatterComponent.class));
return "@someone (" + player.getDisplayName() + "§r)";
}).build(), true, "@someone"),
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() {
}
public static boolean ProcessChat(TBMCChatEvent e, FormatterComponent component) {
Channel channel = e.getChannel();
ChromaGamerBase cuser = e.getUser();
String message = e.getMessage();
long processstart = System.nanoTime();
Player player = (cuser instanceof TBMCPlayerBase ? ((TBMCPlayerBase) cuser).getPlayer() : null);
User user = PluginMain.essentials.getUser(player);
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;
}
ChatPlayer mp;
if (player != null)
mp = TBMCPlayerBase.getPlayer(player.getUniqueId(), ChatPlayer.class);
else //Due to the online player map, getPlayer() can be more efficient than getAs()
mp = e.getUser().getAs(ChatPlayer.class); //May be null
if (mp != null) {
if (System.nanoTime() - mp.LastMessageTime < 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(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();
TextComponent.Builder builder = createEmptyMessageLine(cuser, message, player, channelidentifier, e.getOrigin());
long combinetime = System.nanoTime();
ChatFormatter.Combine(formatters, message, builder, component.getConfig(), FormatSettings.builder().color(colormode).build());
combinetime = System.nanoTime() - combinetime;
try {
if (!channel.isGlobal()) {
String senderGroup = e.getGroupID(cuser);
if (senderGroup == null) { // Never send messages if the group is null
cuser.sendMessage("§cYou don't have permission to send this message or something went wrong");
return true;
}
val tc = ComponentManager.getIfEnabled(TownyComponent.class);
Consumer<Player> spyConsumer = null;
if (tc != null)
spyConsumer = tc.handleSpiesInit(channel, builder);
for (Player p : Bukkit.getOnlinePlayers()) {
final String group;
if (player != null
&& PluginMain.essentials.getUser(p).isIgnoredPlayer(PluginMain.essentials.getUser(player)))
group = null; // Don't send the message to them
else
group = VanillaUtils.getGroupIfChatOn(p, e);
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())
p.sendMessage(builder.build());
} catch (Exception ex) {
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(
"-- Full ChatProcessing time: " + (System.nanoTime() - processstart) / 1000000f + " ms");
DebugCommand.SendDebugMessage("-- ChatFormatter.Combine time: " + combinetime / 1000000f + " ms");
return false;
}
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)]);
return match;
}).build()));
}
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;
}
static String getChannelID(Channel channel, String origin) {
return ("[" + (ChatUtils.MCORIGIN.equals(origin) ? "" : "§8" + origin.charAt(0) + "§r|") + channel.displayName.get())
+ "]";
}
static ArrayList<MatchProviderBase> addFormatters(Predicate<Player> canSee, @Nullable FormatterComponent component) {
@SuppressWarnings("unchecked")
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) || ChromaUtils.isTest()) {
String[] names;
if (nottest)
names = Bukkit.getOnlinePlayers().stream().filter(canSee).map(CommandSender::getName).toArray(String[]::new);
else {
names = new String[testPlayers.length];
System.arraycopy(testPlayers, 0, names, 0, testPlayers.length);
}
String[] nicknames = Bukkit.getOnlinePlayers().stream().filter(canSee).map(Player::getUniqueId).map(PlayerListener.nicknames.inverse()::get)
.filter(Objects::nonNull).toArray(String[]::new);
Consumer<String> error = message -> {
if (PluginMain.Instance != null)
PluginMain.Instance.getLogger().warning(message);
else
System.out.println(message);
};
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(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.isEmpty()) {
error.accept("Error: Can't find player " + match + " but was reported as online.");
return "§c" + match + "§r";
}
ChatPlayer mpp = TBMCPlayer.getPlayer(nottest ? p.getUniqueId() : new UUID(0, 0), ChatPlayer.class);
if (nottest) {
playPingSound(p, component);
}
String color = String.format("§%x", (mpp.GetFlairColor() == 0x00 ? 0xb : mpp.GetFlairColor()));
return color + (nottest ? p.getName() : pn.get()) + "§r"; //Fix name casing, except when testing
}).build(), true, names));
if (nicknames.length > 0) //Add as first so it handles special characters
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()));
if (p == null) {
error.accept("Error: Can't find player nicknamed "
+ match.toLowerCase() + " but was reported as online.");
return "§c" + match + "§r";
}
playPingSound(p, component);
return PluginMain.essentials.getUser(p).getNickname();
}
error.accept("Player nicknamed " + match.toLowerCase()
+ " not found in nickname map but was reported as online.");
return "§c" + match + "§r";
}).build(), true, nicknames));
}
return formatters;
}
private static void playPingSound(Player p, @Nullable FormatterComponent component) {
if (component == null || component.notificationSound.get().length() == 0)
p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1.0f, 0.5f); // TODO: Airhorn
else
p.playSound(p.getLocation(), component.notificationSound.get(), 1.0f,
component.notificationPitch.get());
}
static void doFunStuff(ChromaGamerBase user, TBMCChatEventBase event, String message) {
val fc = ComponentManager.getIfEnabled(FunComponent.class);
if (fc != null) fc.onChat(user, event, message);
}
}

View file

@ -0,0 +1,57 @@
package buttondevteam.chat.components.formatter;
import buttondevteam.chat.PluginMain;
import buttondevteam.core.ComponentManager;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.architecture.Component;
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 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.
*/
IConfigData<Boolean> allowFormatting = getConfig().getData("allowFormatting", true);
/**
* The sound to play when a player is mentioned. Leave empty to use default.
*/
public IConfigData<String> notificationSound = getConfig().getData("notificationSound", "");
/**
* The pitch of the notification sound.
*/
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.getInstance().setChatHandlerEnabled(false); //Disable Core chat handler - if this component is disabled then let it do its job
}
@Override
protected void disable() {
MainPlugin.getInstance().setChatHandlerEnabled(true);
}
/**
* Handles the chat if the component is enabled.
*
* @param event The chat event
* @return Whether the chat message shouldn't be sent for some reason
*/
public static boolean handleChat(TBMCChatEvent event) {
FormatterComponent component = ComponentManager.getIfEnabled(FormatterComponent.class);
if (component == null) return false;
return ChatProcessing.ProcessChat(event, component);
}
}

View file

@ -0,0 +1,31 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import java.util.ArrayList;
import java.util.Arrays;
public final class ChatFormatUtils {
private ChatFormatUtils() {}
static void sendMessageWithPointer(String str, int... pointer) {
DebugCommand.SendDebugMessage(str);
StringBuilder sb = new StringBuilder(str.length());
Arrays.sort(pointer);
for (int i = 0; i < pointer.length; i++) {
for (int j = 0; j < pointer[i] - (i > 0 ? pointer[i - 1] + 1 : 0); j++)
sb.append(' ');
if (pointer[i] == (i > 0 ? pointer[i - 1] : -1))
continue;
sb.append('^');
}
DebugCommand.SendDebugMessage(sb.toString());
}
/**
* Check if the given start and end position is inside any of the ranges
*/
static boolean isInRange(int start, int end, ArrayList<int[]> ranges) {
return ranges.stream().anyMatch(range -> range[1] >= start && range[0] <= end);
}
}

View file

@ -0,0 +1,264 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.lib.architecture.IHaveConfig;
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, 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.
*
* @author NorbiPeti
*/
public final class ChatFormatter {
private ChatFormatter() {
}
@FunctionalInterface
public interface TriFunc<T1, T2, T3, R> {
R apply(T1 x1, T2 x2, T3 x3);
}
//synchronized: Some of the formatters are reused, see createSections(...)
public static synchronized void Combine(List<MatchProviderBase> formatters, String str, TextComponent.Builder tp, IHaveConfig config, FormatSettings defaults) {
/*
* A global formatter is no longer needed
*/
header("ChatFormatter.Combine begin");
ArrayList<FormattedSection> sections = new ArrayList<>();
if (config != null) //null if testing
formatters.removeIf(cf -> !cf.enabled(config).get()); //Remove disabled formatters
var excluded = new ArrayList<int[]>();
/*
* 0: Start - 1: End index
*/
val remchars = new ArrayList<int[]>();
escapeThings(str, excluded, remchars);
sections.add(new FormattedSection(defaults, 0, str.length() - 1, Collections.emptyList())); //Add entire message
var providers = formatters.stream().filter(mp -> mp instanceof RegexMatchProvider).collect(Collectors.toList());
createSections(providers, str, sections, excluded, remchars);
providers = formatters.stream().filter(mp -> mp instanceof StringMatchProvider).collect(Collectors.toList());
createSections(providers, str, sections, excluded, remchars);
providers = formatters.stream().filter(mp -> mp instanceof RangeMatchProvider).collect(Collectors.toList());
createSections(providers, str, sections, excluded, remchars);
sortSections(sections);
header("Section combining");
combineSections(str, sections);
header("Section applying");
applySections(str, tp, sections, remchars);
header("ChatFormatter.Combine done");
}
private static void escapeThings(String str, ArrayList<int[]> ignoredAreas, ArrayList<int[]> remchars) {
boolean escaped = false;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '\\') {
remchars.add(new int[]{i, i});
ignoredAreas.add(new int[]{i + 1, i + 1});
i++; //Ignore a potential second slash
}
}
}
private static void createSections(List<MatchProviderBase> formatters, String str, ArrayList<FormattedSection> sections,
ArrayList<int[]> excludedAreas, ArrayList<int[]> removedCharacters) {
formatters.forEach(MatchProviderBase::reset); //Reset state information, as we aren't doing deep cloning
while (formatters.size() > 0) {
for (var iterator = formatters.iterator(); iterator.hasNext(); ) {
MatchProviderBase formatter = iterator.next();
DebugCommand.SendDebugMessage("Checking provider: " + formatter);
var sect = formatter.getNextSection(str, excludedAreas, removedCharacters);
if (sect != null) //Not excluding the area here because the range matcher shouldn't take it all
sections.add(sect);
if (formatter.isFinished()) {
DebugCommand.SendDebugMessage("Provider finished");
iterator.remove();
}
}
}
}
private static void combineSections(String str, ArrayList<FormattedSection> sections) {
for (int i = 1; i < sections.size(); i++) {
DebugCommand.SendDebugMessage("i: " + i);
final FormattedSection firstSection;
final FormattedSection lastSection;
{
FormattedSection firstSect = sections.get(i - 1);
FormattedSection lastSect = sections.get(i);
if (firstSect.Start > lastSect.Start //The first can't start later
|| (firstSect.Start == lastSect.Start && firstSect.End < lastSect.End)) {
var section = firstSect;
firstSect = lastSect;
lastSect = section;
}
firstSection = firstSect;
lastSection = lastSect;
}
DebugCommand.SendDebugMessage("Combining sections " + firstSection);
ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
DebugCommand.SendDebugMessage(" and " + lastSection);
ChatFormatUtils.sendMessageWithPointer(str, lastSection.Start, lastSection.End);
if (firstSection.Start == lastSection.Start && firstSection.End == lastSection.End) {
firstSection.Settings.copyFrom(lastSection.Settings);
firstSection.Matches.addAll(lastSection.Matches);
DebugCommand.SendDebugMessage("To section " + firstSection);
ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
sections.remove(i);
i = 0;
sortSections(sections);
continue;
} else if (firstSection.End >= lastSection.Start && firstSection.Start <= lastSection.End) {
int 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 (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);
}
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) {
DebugCommand.SendDebugMessage(" Removed: " + s);
ChatFormatUtils.sendMessageWithPointer(str, s.Start, s.End);
sections.remove(s);
return true;
}
return false;
};
DebugCommand.SendDebugMessage("To sections");
if (!removeIfNeeded.test(firstSection)) {
DebugCommand.SendDebugMessage(" 1:" + firstSection);
ChatFormatUtils.sendMessageWithPointer(str, firstSection.Start, firstSection.End);
}
if (!removeIfNeeded.test(section)) {
DebugCommand.SendDebugMessage(" 2:" + section);
ChatFormatUtils.sendMessageWithPointer(str, section.Start, section.End);
}
if (!removeIfNeeded.test(lastSection)) {
DebugCommand.SendDebugMessage(" 3:" + lastSection);
ChatFormatUtils.sendMessageWithPointer(str, lastSection.Start, lastSection.End);
}
i = 0;
}
sortSections(sections);
}
}
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);
String originaltext;
int start = section.Start, end = section.End;
DebugCommand.SendDebugMessage("Start: " + start + " - End: " + end);
ChatFormatUtils.sendMessageWithPointer(str, start, end);
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);
DebugCommand.SendDebugMessage("Applying RC: " + Arrays.stream(rci).map(Arrays::toString).collect(Collectors.joining(", ", "[", "]")));
originaltext = str.substring(start, end + 1);
val sb = new StringBuilder(originaltext);
for (int x = rci.length - 1; x >= 0; x--)
sb.delete(Math.max(rci[x][0] - start, 0), Math.min(rci[x][1] - start, end) + 1); //Delete going backwards
originaltext = sb.toString();
if (originaltext.length() == 0) {
DebugCommand.SendDebugMessage("Skipping section because of remchars");
continue;
}
DebugCommand.SendDebugMessage("Section text: " + originaltext);
String openlink = null;
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.color(settings.color);
if (settings.bold)
newtp.decorate(TextDecoration.BOLD);
if (settings.italic)
newtp.decorate(TextDecoration.ITALIC);
if (settings.underlined)
newtp.decorate(TextDecoration.UNDERLINED);
if (settings.strikethrough)
newtp.decorate(TextDecoration.STRIKETHROUGH);
if (settings.obfuscated)
newtp.decorate(TextDecoration.OBFUSCATED);
if (settings.openlink != null)
openlink = settings.openlink;
if (settings.hoverText != null)
newtp.hoverEvent(hoverEvent(SHOW_TEXT, text(settings.hoverText)));
if (lasttp != null) tp.append(lasttp);
lastlink = openlink;
newtp.content(originaltext);
if (openlink != null && openlink.length() > 0) {
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)));
}
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) {
sections.sort(
(s1, s2) -> s1.Start == s2.Start
? s1.End == s2.End ? 0 : Integer.compare(s1.End, s2.End)
: Integer.compare(s1.Start, s2.Start));
}
private static void header(String message) {
DebugCommand.SendDebugMessage("\n--------\n" + message + "\n--------\n");
}
}

View file

@ -0,0 +1,37 @@
package buttondevteam.chat.components.formatter.formatting;
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.
*/
@Data
@Builder
public class FormatSettings {
boolean italic;
boolean bold;
boolean underlined;
boolean strikethrough;
boolean obfuscated;
TextColor color;
ChatFormatter.TriFunc<String, FormatSettings, FormattedSection, String> onmatch;
String openlink;
String hoverText;
public void copyFrom(FormatSettings settings) {
try {
for (var field : FormatSettings.class.getDeclaredFields()) {
if (field.getType() == boolean.class) {
if (field.getBoolean(settings))
field.setBoolean(this, true); //Set to true if either of them are true
} else if (field.get(settings) != null) {
field.set(this, field.get(settings));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,22 @@
package buttondevteam.chat.components.formatter.formatting;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@ToString
public class FormattedSection {
public int Start;
public int End;
public FormatSettings Settings;
public List<String> Matches = new ArrayList<>();
FormattedSection(FormatSettings settings, int start, int end, List<String> matches) {
Start = start;
End = end;
Settings = FormatSettings.builder().build();
Settings.copyFrom(settings);
Matches.addAll(matches);
}
}

View file

@ -0,0 +1,21 @@
package buttondevteam.chat.components.formatter.formatting;
import javax.annotation.Nullable;
import java.util.ArrayList;
/**
* Attempts to find a match for the provided message, returning null if none was found.
*/
public interface MatchProvider {
@Nullable
FormattedSection getNextSection(String message, ArrayList<int[]> ignoredAreas, ArrayList<int[]> removedCharacters);
boolean isFinished();
String getName();
@Override
String toString();
void reset();
}

View file

@ -0,0 +1,38 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.architecture.config.IConfigData;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.annotation.Nullable;
import java.util.ArrayList;
@RequiredArgsConstructor
public abstract class MatchProviderBase implements MatchProvider {
@Getter
protected boolean finished;
@Getter
private final String name;
@Nullable
@Override
public abstract FormattedSection getNextSection(String message, ArrayList<int[]> ignoredAreas, ArrayList<int[]> removedCharacters);
@Override
public String toString() {
return name;
}
protected abstract void resetSubclass();
public void reset() {
finished = false;
resetSubclass();
}
IConfigData<Boolean> enabled(IHaveConfig config) {
return config.getData(name + ".enabled", true);
}
}

View file

@ -0,0 +1,61 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import lombok.ToString;
import java.util.ArrayList;
import java.util.Collections;
public class RangeMatchProvider extends MatchProviderBase {
private final String pattern;
@ToString.Exclude
private final FormatSettings settings;
private int nextIndex = 0;
private FormattedSection startedSection;
public RangeMatchProvider(String name, String pattern, FormatSettings settings) {
super(name);
this.pattern = pattern;
this.settings = settings;
}
@Override
public FormattedSection getNextSection(String message, ArrayList<int[]> ignoredAreas, ArrayList<int[]> removedCharacters) {
int i, len;
i = message.indexOf(pattern, nextIndex);
len = pattern.length();
nextIndex = i + len; //Set for the next method call
if (i == -1) {
finished = true; //Won't find any more - unfinished sections will be garbage collected
return null;
}
if (ChatFormatUtils.isInRange(i, i + len - 1, ignoredAreas)) {
DebugCommand.SendDebugMessage("Range start is in ignored area, skipping");
return null; //Not setting finished to true, so it will go to the next match
}
ignoredAreas.add(new int[]{i, i + len - 1});
if (startedSection == null) {
DebugCommand.SendDebugMessage("Started range match from " + i + " to " + (i + len - 1));
DebugCommand.SendDebugMessage("With settings: " + settings);
ChatFormatUtils.sendMessageWithPointer(message, i, i + len - 1);
startedSection = new FormattedSection(settings, i, i + len - 1, Collections.emptyList());
return null;
} else {
var section = startedSection;
DebugCommand.SendDebugMessage("Finished range match from " + section.Start + " to " + (i + len - 1));
DebugCommand.SendDebugMessage("With settings: " + settings);
ChatFormatUtils.sendMessageWithPointer(message, section.Start, i + len - 1);
section.End = i + len - 1;
removedCharacters.add(new int[]{section.Start, section.Start + len - 1});
removedCharacters.add(new int[]{i, i + len - 1});
startedSection = null; //Reset so next find creates a new one
return section;
}
}
@Override
public void resetSubclass() {
nextIndex = 0;
startedSection = null;
}
}

View file

@ -0,0 +1,53 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import lombok.ToString;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatchProvider extends MatchProviderBase {
private final Pattern pattern;
@ToString.Exclude
private final FormatSettings settings;
private Matcher matcher;
public RegexMatchProvider(String name, Pattern pattern, FormatSettings settings) {
super(name);
this.pattern = pattern;
this.settings = settings;
}
@Nullable
@Override
public FormattedSection getNextSection(String message, ArrayList<int[]> ignoredAreas, ArrayList<int[]> removedCharacters) {
if (matcher == null)
matcher = pattern.matcher(message);
if (!matcher.find()) {
finished = true;
return null;
}
int start = matcher.start(), end = matcher.end() - 1;
DebugCommand.SendDebugMessage("Found regex match from " + start + " to " + end);
DebugCommand.SendDebugMessage("With settings: " + settings);
ChatFormatUtils.sendMessageWithPointer(message, start, end);
if (ChatFormatUtils.isInRange(start, end, ignoredAreas)) {
DebugCommand.SendDebugMessage("Match is in ignored area, skipping");
return null; //Not setting finished to true, so it will go to the next match
}
ArrayList<String> groups = new ArrayList<>();
for (int i = 0; i < matcher.groupCount(); i++)
groups.add(matcher.group(i + 1));
if (groups.size() > 0)
DebugCommand.SendDebugMessage("First group: " + groups.get(0));
ignoredAreas.add(new int[]{start, end});
return new FormattedSection(settings, matcher.start(), matcher.end() - 1, groups);
}
@Override
public void resetSubclass() {
matcher = null;
}
}

View file

@ -0,0 +1,66 @@
package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import lombok.ToString;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
public class StringMatchProvider extends MatchProviderBase {
private final String[] strings;
@ToString.Exclude
private final FormatSettings settings;
private int nextIndex = 0;
private final boolean ignoreCase;
/**
* Matches the given strings in the order given
*
* @param settings The format settings
* @param strings The strings to match in the correct order
*/
public StringMatchProvider(String name, FormatSettings settings, boolean ignoreCase, String... strings) {
super(name);
this.settings = settings;
this.strings = strings;
this.ignoreCase = ignoreCase;
if (ignoreCase) {
for (int i = 0; i < strings.length; i++) {
strings[i] = strings[i].toLowerCase();
}
}
}
@Nullable
@Override
public FormattedSection getNextSection(String message, ArrayList<int[]> ignoredAreas, ArrayList<int[]> removedCharacters) {
if (ignoreCase)
message = message.toLowerCase();
int i = -1, len = 0;
for (String string : strings) {
i = message.indexOf(string, nextIndex);
len = string.length();
if (i != -1) break;
}
if (i == -1) {
finished = true; //Won't find any more
return null;
}
nextIndex = i + len;
if (ChatFormatUtils.isInRange(i, i + len - 1, ignoredAreas)) {
DebugCommand.SendDebugMessage("String is in ignored area, skipping");
return null; //Not setting finished to true, so it will go to the next match
}
DebugCommand.SendDebugMessage("Found string match from " + i + " to " + (i + len - 1));
DebugCommand.SendDebugMessage("With settings: " + settings);
ChatFormatUtils.sendMessageWithPointer(message, i, i + len - 1);
ignoredAreas.add(new int[]{i, i + len - 1});
return new FormattedSection(settings, i, i + len - 1, Collections.emptyList());
}
@Override
public void resetSubclass() {
nextIndex = 0;
}
}

View file

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

View file

@ -1,11 +1,11 @@
package buttondevteam.chat.formatting; package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.lib.chat.Color;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import buttondevteam.lib.chat.*;
public final class TellrawPart implements Serializable { public final class TellrawPart implements Serializable {
private static final long serialVersionUID = 4125357644462144024L; private static final long serialVersionUID = 4125357644462144024L;
private Color color; private Color color;
@ -14,7 +14,7 @@ public final class TellrawPart implements Serializable {
private boolean underlined; private boolean underlined;
private boolean strikethrough; private boolean strikethrough;
private boolean obfuscated; private boolean obfuscated;
private List<TellrawPart> extra = new ArrayList<>(); private final List<TellrawPart> extra = new ArrayList<>();
private String text; private String text;
private TellrawEvent<TellrawEvent.HoverAction> hoverEvent; private TellrawEvent<TellrawEvent.HoverAction> hoverEvent;
private TellrawEvent<TellrawEvent.ClickAction> clickEvent; private TellrawEvent<TellrawEvent.ClickAction> clickEvent;

View file

@ -1,14 +1,14 @@
package buttondevteam.chat.formatting; package buttondevteam.chat.components.formatter.formatting;
import buttondevteam.lib.chat.TellrawSerializableEnum;
import com.google.gson.*;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Collection; import java.util.Collection;
import com.google.gson.*;
import com.google.gson.stream.*;
import buttondevteam.lib.chat.TellrawSerializableEnum;
public abstract class TellrawSerializer { public abstract class TellrawSerializer {
public static class TwEnum extends TypeAdapter<TellrawSerializableEnum> { public static class TwEnum extends TypeAdapter<TellrawSerializableEnum> {
@Override @Override
@ -48,6 +48,7 @@ public abstract class TellrawSerializer {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@SuppressWarnings("ConstantConditions")
@Override @Override
public void write(JsonWriter writer, Boolean val) throws IOException { public void write(JsonWriter writer, Boolean val) throws IOException {
if (val) if (val)

View file

@ -6,17 +6,20 @@ import buttondevteam.lib.chat.*;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Stream;
@CommandClass(path = "u c", helpText = { @CommandClass(path = "u c", helpText = {
"Rainbow mode", "Rainbow mode",
"This command allows you to talk in rainbow colors" "This command allows you to talk in rainbow colors"
}) })
@OptionallyPlayerCommandClass(playerOnly = true)
public class CCommand extends ICommand2MC { public class CCommand extends ICommand2MC {
@Command2.Subcommand @Command2.Subcommand
public boolean def(Player player, @Command2.OptionalArg String color) { public boolean def(Player player, @Command2.OptionalArg String color) {
ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class); ChatPlayer p = TBMCPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
if (color == null) { if (color == null) {
if (PluginMain.permission.has(player, "thorpe.color.rainbow")) { if (PluginMain.permission.has(player, "chroma.color.rainbow")) {
p.RainbowPresserColorMode = !p.RainbowPresserColorMode; p.RainbowPresserColorMode = !p.RainbowPresserColorMode;
p.OtherColorMode = null; p.OtherColorMode = null;
if (p.RainbowPresserColorMode) if (p.RainbowPresserColorMode)
@ -28,19 +31,23 @@ public class CCommand extends ICommand2MC {
return true; return true;
} }
} else { } else {
if (PluginMain.permission.has(player, "thorpe.color.custom")) { if (PluginMain.permission.has(player, "chroma.color.custom")) {
p.RainbowPresserColorMode = false; String x = color.toLowerCase();
p.OtherColorMode = null; if ("off".equals(x)) {
try { p.OtherColorMode = null;
p.OtherColorMode = Color.valueOf(color.toLowerCase()); player.sendMessage("§eMessage color reset.");
} catch (Exception e) { return true;
}
Optional<Color> oc = Arrays.stream(Color.values()).filter(c -> c.getName().equals(x)).findAny();
if (!oc.isPresent()) {
player.sendMessage("§cUnknown message color: " + color); player.sendMessage("§cUnknown message color: " + color);
player.sendMessage("§cUse color names, like blue, or dark_aqua"); player.sendMessage("§cUse color names, like blue, or dark_aqua");
player.sendMessage("§cOr use 'off' to disable");
return true;
} }
if (p.OtherColorMode != null) p.RainbowPresserColorMode = false;
player.sendMessage(String.format("§eMessage color set to %s", p.OtherColorMode)); p.OtherColorMode = oc.get();
else player.sendMessage(String.format("§eMessage color set to %s", p.OtherColorMode));
player.sendMessage("§eMessage color reset.");
} else { } else {
player.sendMessage("§cYou don't have permission for this command."); player.sendMessage("§cYou don't have permission for this command.");
return true; return true;
@ -48,4 +55,9 @@ public class CCommand extends ICommand2MC {
} }
return true; 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

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

View file

@ -6,15 +6,16 @@ import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCChatEventBase; import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCCommandPreprocessEvent; import buttondevteam.lib.TBMCCommandPreprocessEvent;
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.ThorpeUtils;
import buttondevteam.lib.architecture.Component; 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.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.google.common.collect.Lists;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
@ -26,29 +27,39 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList; import java.util.HashSet;
import java.util.Random; import java.util.Random;
/**
* Random things I added over the years.
*/
public class FunComponent extends Component<PluginMain> implements Listener { public class FunComponent extends Component<PluginMain> implements Listener {
private boolean ActiveF = false; private boolean ActiveF = false;
private ChatPlayer FPlayer = null; private ChatPlayer FPlayer = null;
private BukkitTask Ftask = null; private BukkitTask Ftask = null;
private ArrayList<CommandSender> Fs = new ArrayList<>(); private final HashSet<ChromaGamerBase> Fs = new HashSet<>();
private UnlolCommand command; private UnlolCommand command;
private TBMCSystemChatEvent.BroadcastTarget unlolTarget; private TBMCSystemChatEvent.BroadcastTarget unlolTarget;
private TBMCSystemChatEvent.BroadcastTarget fTarget; private TBMCSystemChatEvent.BroadcastTarget fTarget;
private final Random random = new Random();
private ConfigData<String[]> laughStrings() { /**
return getConfig().getData("laughStrings", () -> new String[]{"xd", "lel", "lawl", "kek", "lmao", "hue", "hah", "rofl"}); * The strings that count as laughs, see unlol.
} */
private final IListConfigData<String> laughStrings = getConfig().getListData("laughStrings", Lists.newArrayList("xd", "lel", "lawl", "kek", "lmao", "hue", "hah", "rofl"));
private ConfigData<Boolean> respect() { /**
return getConfig().getData("respect", true); * 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 final IConfigData<Boolean> respect = getConfig().getData("respect", true);
private ConfigData<Boolean> unlol() { /**
return getConfig().getData("unlol", 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 final IConfigData<Boolean> unlol = getConfig().getData("unlol", true);
@Override @Override
protected void enable() { protected void enable() {
@ -57,7 +68,7 @@ public class FunComponent extends Component<PluginMain> implements Listener {
val pc = new PressCommand(); val pc = new PressCommand();
registerCommand(pc); registerCommand(pc);
registerListener(pc); registerListener(pc);
registerCommand(command=new UnlolCommand(unlolTarget)); registerCommand(command = new UnlolCommand(unlolTarget));
registerListener(this); registerListener(this);
registerCommand(new FTopCommand()); registerCommand(new FTopCommand());
registerCommand(new OpmeCommand()); registerCommand(new OpmeCommand());
@ -70,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")) if (ActiveF && !Fs.contains(sender) && message.equalsIgnoreCase("F"))
Fs.add(sender); Fs.add(sender);
if (unlol().get()) { if (unlol.get()) {
String msg = message.toLowerCase(); String msg = message.toLowerCase();
val lld = new UnlolCommand.LastlolData(sender, event, System.nanoTime()); val lld = new UnlolCommand.LastlolData(sender, event, System.nanoTime());
boolean add; boolean add = msg.contains("lol");
if (add = msg.contains("lol")) if (add)
lld.setLolornot(true); lld.setLolornot(true);
else { else {
String[] laughs = laughStrings().get(); val laughs = laughStrings.get();
for (String laugh : laughs) { for (String laugh : laughs) {
if (add = msg.contains(laugh)) { add = msg.contains(laugh);
if (add) {
lld.setLolornot(false); lld.setLolornot(false);
break; break;
} }
@ -97,15 +109,15 @@ public class FunComponent extends Component<PluginMain> implements Listener {
@EventHandler @EventHandler
public void onPlayerDeath(PlayerDeathEvent e) { public void onPlayerDeath(PlayerDeathEvent e) {
// MinigamePlayer mgp = Minigames.plugin.pdata.getMinigamePlayer(e.getEntity()); // 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 = () -> { Runnable tt = () -> {
if (ActiveF) { if (ActiveF) {
ActiveF = false; ActiveF = false;
if (FPlayer != null && FPlayer.FCount().get() < Integer.MAX_VALUE - 1) if (FPlayer != null && FPlayer.FCount.get() < Integer.MAX_VALUE - 1)
FPlayer.FCount().set(FPlayer.FCount().get() + Fs.size()); FPlayer.FCount.set(FPlayer.FCount.get() + Fs.size());
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL,
"§b" + Fs.size() + " " + (Fs.size() == 1 ? "person" : "people") "§b" + Fs.size() + " " + (Fs.size() == 1 ? "person" : "people")
+ " paid their respects.§r", fTarget); + " paid their respects.§r", fTarget);
Fs.clear(); Fs.clear();
} }
}; };
@ -116,22 +128,23 @@ public class FunComponent extends Component<PluginMain> implements Listener {
ActiveF = true; ActiveF = true;
Fs.clear(); Fs.clear();
FPlayer = TBMCPlayer.getPlayer(e.getEntity().getUniqueId(), ChatPlayer.class); FPlayer = TBMCPlayer.getPlayer(e.getEntity().getUniqueId(), ChatPlayer.class);
FPlayer.FDeaths().set(FPlayer.FDeaths().get() + 1); FPlayer.FDeaths.set(FPlayer.FDeaths.get() + 1);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL,
"§bPress F to pay respects.§r", fTarget); "§bPress F to pay respects.§r", fTarget);
Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, tt, 15 * 20); Ftask = Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, tt, 15 * 20);
} }
} }
@EventHandler @EventHandler
public void onPlayerLeave(PlayerQuitEvent event) { public void onPlayerLeave(PlayerQuitEvent event) {
if (unlol().get()) if (unlol.get())
command.Lastlol.values().removeIf(lld -> lld.getLolowner().equals(event.getPlayer())); command.Lastlol.values().removeIf(lld -> lld.getLolowner().equals(ChromaGamerBase.getFromSender(event.getPlayer())));
} }
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onCommandPreprocess(TBMCCommandPreprocessEvent event) { public void onCommandPreprocess(TBMCCommandPreprocessEvent event) {
if (event.isCancelled()) return; if (event.isCancelled()) return;
if (!unlol().get()) return; if (!unlol.get()) return;
final String cmd = event.getMessage(); final String cmd = event.getMessage();
// We don't care if we have arguments // We don't care if we have arguments
if (cmd.toLowerCase().startsWith("/un")) { if (cmd.toLowerCase().startsWith("/un")) {
@ -139,23 +152,29 @@ public class FunComponent extends Component<PluginMain> implements Listener {
if (ht.getName().equalsIgnoreCase(cmd)) if (ht.getName().equalsIgnoreCase(cmd))
return; return;
} }
if (PluginMain.permission.has(event.getSender(), "thorpe.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); event.setCancelled(true);
String s = cmd.substring(3); int index = cmd.lastIndexOf(' ');
int index = event.getMessage().indexOf(' ');
if (index == -1) { if (index == -1) {
event.getSender().sendMessage("§cUsage: /un" + s + " <player>"); event.getSender().sendMessage("§cUsage: " + cmd + " <player>");
return; return;
} }
Player target = Bukkit.getPlayer(event.getMessage().substring(index + 1)); String s = cmd.substring(3, index);
Player target = Bukkit.getPlayer(cmd.substring(index + 1));
if (target == null) { if (target == null) {
event.getSender().sendMessage("§cError: Player not found. (/un" + s + " <player>)"); event.getSender().sendMessage("§cError: Player not found. (/un" + s + " <player>)");
return; return;
} }
val user = ChromaGamerBase.getFromSender(event.getSender());
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 10 * 20, 5, false, false)); target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 10 * 20, 5, false, false));
val chan = user.channel().get(); val chan = user.getChannel().get();
TBMCChatAPI.SendSystemMessage(chan, chan.getRTR(event.getSender()), ThorpeUtils.getDisplayName(event.getSender()) + " un" + s TBMCChatAPI.SendSystemMessage(chan, chan.getRTR(event.getSender()), event.getSender().getName() + " un" + s
+ "'d " + target.getDisplayName(), unlolTarget); + "'d " + target.getDisplayName(), unlolTarget);
} }
} }

View file

@ -3,8 +3,8 @@ package buttondevteam.chat.components.fun;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.restart.RestartComponent; import buttondevteam.core.component.restart.RestartComponent;
import buttondevteam.core.component.restart.ScheduledRestartCommand; import buttondevteam.core.component.restart.ScheduledRestartCommand;
import buttondevteam.lib.ChromaUtils;
import buttondevteam.lib.ScheduledServerRestartEvent; import buttondevteam.lib.ScheduledServerRestartEvent;
import buttondevteam.lib.ThorpeUtils;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.ICommand2MC; import buttondevteam.lib.chat.ICommand2MC;
@ -16,7 +16,11 @@ import org.bukkit.event.Listener;
import java.util.HashSet; 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 { public class PressCommand extends ICommand2MC implements Listener {
private HashSet<CommandSender> pressers; //Will be cleared with this class on shutdown/disable private HashSet<CommandSender> pressers; //Will be cleared with this class on shutdown/disable
private ScheduledRestartCommand command; private ScheduledRestartCommand command;
@ -33,7 +37,7 @@ public class PressCommand extends ICommand2MC implements Listener {
return; return;
} }
pressers.add(sender); pressers.add(sender);
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, String.format("§b-- %s §bpressed at %.0fs", ThorpeUtils.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); command.setRestartCounter(startTicks);
} }
@ -42,5 +46,7 @@ public class PressCommand extends ICommand2MC implements Listener {
command = event.getCommand(); command = event.getCommand();
pressers = new HashSet<>(); pressers = new HashSet<>();
startTicks = event.getRestartTicks(); 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

@ -3,13 +3,14 @@ package buttondevteam.chat.components.fun;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCChatEventBase; import buttondevteam.lib.TBMCChatEventBase;
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.ThorpeUtils; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.*; 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.Data;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
@ -18,8 +19,8 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
@CommandClass(modOnly = false, helpText = { @CommandClass(modOnly = false, helpText = {
"§6---- Unlol/unlaugh ----", "Unlol/unlaugh",
"This command is based on a joke between NorbiPeti and Ghostise", "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", "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" "Note that you can only unlaugh laughs that weren't unlaughed before"
}) })
@ -31,18 +32,18 @@ public final class UnlolCommand extends ICommand2MC {
private final TBMCSystemChatEvent.BroadcastTarget target; private final TBMCSystemChatEvent.BroadcastTarget target;
@Command2.Subcommand @Command2.Subcommand
public boolean def(CommandSender sender) { public boolean def(ChromaGamerBase sender) {
LastlolData lol = Lastlol.values().stream().filter(lld -> lld.Chatevent.shouldSendTo(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) if (lol == null)
return true; return true;
if (lol.Lolowner instanceof Player) if (lol.Lolowner instanceof TBMCPlayerBase) {
((Player) lol.Lolowner) var player = ((TBMCPlayerBase) lol.Lolowner).getPlayer();
.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 2 * 20, 5, false, false)); if (player != null)
String msg = ThorpeUtils.getDisplayName(sender) player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 2 * 20, 5, false, false));
+ (lol.Lolornot ? " unlolled " : " unlaughed ") }
+ ThorpeUtils.getDisplayName(lol.Lolowner); String msg = sender.getName() + (lol.Lolornot ? " unlolled " : " unlaughed ") + lol.Lolowner.getName();
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, Channel.RecipientTestResult.ALL, msg, target); TBMCChatAPI.SendSystemMessage(Channel.globalChat, Channel.RecipientTestResult.ALL, msg, target);
Lastlol.remove(lol.Chatevent.getChannel()); Lastlol.remove(lol.Chatevent.getChannel());
return true; return true;
} }
@ -50,7 +51,7 @@ public final class UnlolCommand extends ICommand2MC {
@Data @Data
public static class LastlolData { public static class LastlolData {
private boolean Lolornot; private boolean Lolornot;
private final CommandSender Lolowner; private final ChromaGamerBase Lolowner;
private final TBMCChatEventBase Chatevent; private final TBMCChatEventBase Chatevent;
private final long Loltime; private final long Loltime;
} }

View file

@ -6,17 +6,14 @@ import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass;
import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town; import com.palmergames.bukkit.towny.object.Town;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@OptionallyPlayerCommandClass(playerOnly = true)
@CommandClass(helpText = { @CommandClass(helpText = {
"Name color", // "Name color", //
"This command allows you to set how the town colors look on your name.", // "This command allows you to set how the town colors look on your name.", //
@ -30,8 +27,8 @@ public class NColorCommand extends UCommandBase {
Resident res; Resident res;
Town town; Town town;
try { try {
if ((res = TownyComponent.TU.getResidentMap().get(player.getName().toLowerCase())) == null || !res.hasTown() if ((res = TownyComponent.dataSource.getResident(player.getName())) == null || !res.hasTown()
|| (town = res.getTown()) == null) { || (town = res.getTown()) == null) {
player.sendMessage("§cYou need to be in a town."); player.sendMessage("§cYou need to be in a town.");
return true; return true;
} }
@ -43,7 +40,7 @@ public class NColorCommand extends UCommandBase {
//Don't add ~ for nicknames //Don't add ~ for nicknames
if (!nameWithLines.replace("|", "").replace(":", "").equalsIgnoreCase(name)) { if (!nameWithLines.replace("|", "").replace(":", "").equalsIgnoreCase(name)) {
player.sendMessage("§cThe name you gave doesn't match your name. Make sure to use " 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; return true;
} }
String[] nameparts = nameWithLines.split("[|:]"); String[] nameparts = nameWithLines.split("[|:]");
@ -52,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."); player.sendMessage("§cYour town doesn't have a color set. The town mayor can set it using /u towncolor.");
return true; 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 player.sendMessage("§cYou need more vertical lines (|) or colons (:) in your name. (Should have " + (towncolors.length - 1 + 1) + ")"); //Nation color
return true; 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) + ")"); player.sendMessage("§cYou have waay too many vertical lines (|) or colons (:) in your name. (Should have " + (towncolors.length - 1 + 1) + ")");
return true; 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) + ")"); player.sendMessage("§cYou have too many vertical lines (|) or colons (:) in your name. (Should have " + (towncolors.length - 1 + 1) + ")");
return true; return true;
} }
ChatPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class).NameColorLocations() var cp = ChatPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class);
.set(new ArrayList<>(Arrays.stream(nameparts).map(String::length).collect(Collectors.toList()))); // No byte[], no TIntArrayList var list = Arrays.stream(nameparts).map(String::length).collect(Collectors.toList());
TownColorComponent.updatePlayerColors(player); if (list.contains(0)) {
player.sendMessage("§bName colors set: " + player.getDisplayName()); 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; return true;
} }
} }

View file

@ -1,11 +1,11 @@
package buttondevteam.chat.components.towncolors; package buttondevteam.chat.components.towncolors;
import buttondevteam.chat.commands.ucmds.UCommandBase; import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.towncolors.admin.TownColorCommand;
import buttondevteam.chat.components.towny.TownyComponent; import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass; import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Resident;
@ -17,24 +17,26 @@ import org.bukkit.entity.Player;
"Each town in the nation will have it's first color (border) set to this color.", // "Each town in the nation will have it's first color (border) set to this color.", //
"See the help text for /u towncolor for more details.", // "See the help text for /u towncolor for more details.", //
}) })
@OptionallyPlayerCommandClass(playerOnly = true)
public class NationColorCommand extends UCommandBase { public class NationColorCommand extends UCommandBase {
@Command2.Subcommand @Command2.Subcommand
public boolean def(Player player, String color) { public boolean def(Player player, String color) {
Resident res; String msg = "§cYou need to be the king of a nation to set it's colors.";
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;
try { 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) { } catch (NotRegisteredException e) {
TBMCCoreAPI.SendException("Failed to set nation color for player " + player + "!", e); player.sendMessage(msg);
player.sendMessage("§cCouldn't find your town/nation... Error reported.");
return true; 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,10 +2,9 @@ package buttondevteam.chat.components.towncolors;
import buttondevteam.chat.commands.ucmds.UCommandBase; import buttondevteam.chat.commands.ucmds.UCommandBase;
import buttondevteam.chat.components.towny.TownyComponent; import buttondevteam.chat.components.towny.TownyComponent;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.OptionallyPlayerCommandClass; import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town; import com.palmergames.bukkit.towny.object.Town;
@ -13,52 +12,40 @@ import lombok.RequiredArgsConstructor;
import lombok.val; import lombok.val;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.lang.reflect.Method;
@CommandClass(helpText = { @CommandClass(helpText = {
"Town Color", // "Town Color", //
"This command allows setting a color for a town.", // "This command allows setting a color for a town.", //
"The town will be shown with this color on Dynmap and all players in the town will appear in chat with these colors.", // "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.", // "The colors will split the name evenly but residents can override that with /u ncolor.", //
}) // TODO: /u u when annotation not present })
@OptionallyPlayerCommandClass(playerOnly = true)
@RequiredArgsConstructor @RequiredArgsConstructor
public class TownColorCommand extends UCommandBase { public class TownColorCommand extends UCommandBase {
private final TownColorComponent component; private final TownColorComponent component;
@Override
public String[] getHelpText(Method method, Command2.Subcommand ann) {
StringBuilder cns = new StringBuilder(" <colorname1>");
for (int i = 2; i <= component.colorCount().get(); i++)
cns.append(" [colorname").append(i).append("]");
return new String[] { //
"§6---- Town Color ----", //
"This command allows setting color(s) for a town.", //
"The town will be shown with this color on Dynmap and all players in the town will appear in chat with these colors.", //
"The colors will split the name evenly.", //
};
}
@Command2.Subcommand @Command2.Subcommand
public boolean def(Player player, String... colornames) { public boolean def(Player player, String... colornames) {
Resident res; String msg = "§cYou need to be the mayor of a town to set its colors.";
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;
try { 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) { } catch (NotRegisteredException e) {
TBMCCoreAPI.SendException("Failed to set town color for player " + player + "!", e); player.sendMessage(msg);
player.sendMessage("§cCouldn't find your town... Error reported.");
return true; 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,26 +8,26 @@ import buttondevteam.core.ComponentManager;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ComponentMetadata; import buttondevteam.lib.architecture.ComponentMetadata;
import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.config.IConfigData;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
import buttondevteam.lib.player.TBMCPlayerJoinEvent; import buttondevteam.lib.player.TBMCPlayer;
import com.earth2me.essentials.User; import com.earth2me.essentials.User;
import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.var;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.Plugin;
import org.dynmap.towny.DTBridge; import org.dynmap.towny.DTBridge;
import org.dynmap.towny.DynmapTownyPlugin;
import java.io.File;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -35,24 +35,31 @@ import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* Town colors for Towny. It allows mayors and kings to set a color for their town/nation (nation can be disabled).
* This color is applied to the player names in chat and on Dynmap, if used.
*/
@ComponentMetadata(depends = TownyComponent.class) @ComponentMetadata(depends = TownyComponent.class)
public class TownColorComponent extends Component<PluginMain> implements Listener { public class TownColorComponent extends Component<PluginMain> implements Listener {
/** /**
* Names lowercased * 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 * 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<>();
public ConfigData<Byte> colorCount() { /**
return getConfig().getData("colorCount", (byte) 1, cc -> ((Integer) cc).byteValue(), Byte::intValue); * 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 final IConfigData<Byte> colorCount = getConfig().getData("colorCount", (byte) 1, cc -> ((Integer) cc).byteValue(), Byte::intValue);
public ConfigData<Boolean> useNationColors() { /**
return getConfig().getData("useNationColors", true); * 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 final IConfigData<Boolean> useNationColors = getConfig().getData("useNationColors", true);
@Getter @Getter
private static TownColorComponent component; private static TownColorComponent component;
@ -61,58 +68,36 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
@Override @Override
protected void enable() { protected void enable() {
component = this; component = this;
//TODO: Don't register all commands automatically (welp)
Consumer<ConfigurationSection> loadTC = cs -> TownColorComponent.TownColors.putAll(cs.getValues(true).entrySet().stream() Consumer<ConfigurationSection> loadTC = cs -> TownColorComponent.TownColors.putAll(cs.getValues(true).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> ((List<String>) v.getValue()).stream() .collect(Collectors.toMap(Map.Entry::getKey, v -> ((List<String>) v.getValue()).stream()
.map(Color::valueOf).toArray(Color[]::new)))); .map(Color::valueOf).toArray(Color[]::new))));
boolean usenc = useNationColors().get(); boolean usenc = useNationColors.get();
Consumer<ConfigurationSection> loadNC = ncs -> Consumer<ConfigurationSection> loadNC = ncs ->
TownColorComponent.NationColor.putAll(ncs.getValues(true).entrySet().stream() TownColorComponent.NationColor.putAll(ncs.getValues(true).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> Color.valueOf((String) v.getValue())))); .collect(Collectors.toMap(Map.Entry::getKey, v -> Color.valueOf((String) v.getValue()))));
var cs = getConfig().getConfig().getConfigurationSection("towncolors"); var cs = getConfig().getConfig().getConfigurationSection("towncolors");
if (cs != null) if (cs != null)
loadTC.accept(cs); loadTC.accept(cs);
else
load_old(loadTC, null); //Load old data
if (usenc) { if (usenc) {
var ncs = getConfig().getConfig().getConfigurationSection("nationcolors"); var ncs = getConfig().getConfig().getConfigurationSection("nationcolors");
if (ncs != null) if (ncs != null)
loadNC.accept(ncs); loadNC.accept(ncs);
else
load_old(null, loadNC); //Why not choose by making different args null
} }
TownColors.keySet().removeIf(t -> !TownyComponent.TU.getTownsMap().containsKey(t)); // Removes town colors for deleted/renamed towns TownColors.keySet().removeIf(t -> TownyComponent.dataSource.getTown(t) == null); // Removes town colors for deleted/renamed towns
if (usenc) 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
Bukkit.getScheduler().runTask(getPlugin(), () -> { initDynmap();
val dtp = (DynmapTownyPlugin) Bukkit.getPluginManager().getPlugin("Dynmap-Towny");
if (dtp == null)
return;
for (val entry : TownColors.entrySet()) {
try {
val town = TownyComponent.TU.getTownsMap().get(entry.getKey());
Nation nation;
Color nc;
if (!useNationColors().get())
nc = null;
else if (!town.hasNation() || (nation = town.getNation()) == null || (nc = NationColor.get(nation.getName().toLowerCase())) == null)
nc = Color.White;
setTownColor(dtp, buttondevteam.chat.components.towncolors.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue(), nc);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting town color for town " + entry.getKey() + "!", e);
}
}
});
registerCommand(new TownColorCommand(this)); registerCommand(new TownColorCommand(this));
if (useNationColors().get()) if (useNationColors.get())
registerCommand(new NationColorCommand()); registerCommand(new NationColorCommand());
registerCommand(new buttondevteam.chat.components.towncolors.admin.TownColorCommand()); registerCommand(new buttondevteam.chat.components.towncolors.admin.TownColorCommand());
if (useNationColors().get()) if (useNationColors.get())
registerCommand(new buttondevteam.chat.components.towncolors.admin.NationColorCommand()); registerCommand(new buttondevteam.chat.components.towncolors.admin.NationColorCommand());
registerCommand(new TCCount()); registerCommand(new TCCount());
registerCommand(new NColorCommand());
registerListener(new TownyListener()); registerListener(new TownyListener());
registerListener(this); registerListener(this);
} }
@ -121,9 +106,32 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
protected void disable() { protected void disable() {
getConfig().getConfig().createSection("towncolors", TownColors.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, getConfig().getConfig().createSection("towncolors", TownColors.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
v -> Arrays.stream(v.getValue()).map(Enum::toString).toArray(String[]::new)))); 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, getConfig().getConfig().createSection("nationcolors", NationColor.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
v -> v.getValue().toString()))); v -> v.getValue().toString())));
getConfig().signalChange();
}
private void initDynmap() {
Bukkit.getScheduler().runTask(getPlugin(), () -> {
val dtp = Bukkit.getPluginManager().getPlugin("Dynmap-Towny");
if (dtp == null)
return;
for (val entry : TownColors.entrySet()) {
try {
val town = TownyComponent.dataSource.getTown(entry.getKey());
Nation nation;
Color nc;
if (!useNationColors.get())
nc = null;
else if (!town.hasNation() || (nation = town.getNation()) == null || (nc = NationColor.get(nation.getName().toLowerCase())) == null)
nc = Color.White;
setTownColor(dtp, buttondevteam.chat.components.towncolors.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue(), nc);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting town color for town " + entry.getKey() + "!", e, this);
}
}
});
} }
/** /**
@ -133,14 +141,13 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
* @param town The town's name using the correct casing * @param town The town's name using the correct casing
* @param colors The town's colors * @param colors The town's colors
*/ */
public static void setTownColor(Plugin dtp, String town, Color[] colors, Color nationcolor) {
public static void setTownColor(DynmapTownyPlugin dtp, String town, Color[] colors, Color nationcolor) {
Function<Color, Integer> c2i = c -> c.getRed() << 16 | c.getGreen() << 8 | c.getBlue(); Function<Color, Integer> c2i = c -> c.getRed() << 16 | c.getGreen() << 8 | c.getBlue();
try { try {
DTBridge.setTownColor(dtp, town, c2i.apply(nationcolor == null ? colors[0] : nationcolor), DTBridge.setTownColor(dtp, town, c2i.apply(nationcolor == null ? colors[0] : nationcolor),
c2i.apply(colors.length > 1 && nationcolor != null ? colors[1] : colors[0])); c2i.apply(colors.length > 1 && nationcolor != null ? colors[1] : colors[0]));
} catch (Exception e) { } 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);
} }
} }
@ -149,7 +156,7 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
if (nickname.contains("~")) //StartsWith doesn't work because of color codes if (nickname.contains("~")) //StartsWith doesn't work because of color codes
nickname = nickname.replace("~", ""); //It gets stacked otherwise nickname = nickname.replace("~", ""); //It gets stacked otherwise
String name = ChatColor.stripColor(nickname); //Enforce "town colors" on non-members 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()) if (res == null || !res.hasTown())
return name; return name;
try { try {
@ -171,8 +178,8 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
len = name.length() / (clrs.length+1); len = name.length() / (clrs.length+1);
else else
len = name.length() / clrs.length;*/ len = name.length() / clrs.length;*/
boolean usenc = component.useNationColors().get(); boolean usenc = component.useNationColors.get();
val nclar = cp.NameColorLocations().get(); val nclar = cp.NameColorLocations.get();
int[] ncl = nclar == null ? null : nclar.stream().mapToInt(Integer::intValue).toArray(); 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 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 ncl = null; // Reset if name length changed
@ -194,14 +201,14 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
/** /**
* Checks if the component is enabled * 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)); updatePlayerColors(player, ChatPlayer.getPlayer(player.getUniqueId(), ChatPlayer.class));
} }
/** /**
* Checks if the component is enabled * 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)) if (!ComponentManager.isEnabled(TownColorComponent.class))
return; return;
User user = PluginMain.essentials.getUser(player); User user = PluginMain.essentials.getUser(player);
@ -210,29 +217,8 @@ public class TownColorComponent extends Component<PluginMain> implements Listene
cp.FlairUpdate(); //Update in list cp.FlairUpdate(); //Update in list
} }
@EventHandler @EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(TBMCPlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
updatePlayerColors(event.getPlayer(), event.GetPlayer().asPluginPlayer(ChatPlayer.class)); updatePlayerColors(event.getPlayer(), TBMCPlayer.getPlayer(event.getPlayer().getUniqueId(), ChatPlayer.class));
}
private static void load_old(Consumer<ConfigurationSection> loadTC,
Consumer<ConfigurationSection> loadNC) {
PluginMain.Instance.getLogger().info("Loading files...");
try {
File file = new File("TBMC/chatsettings.yml");
if (file.exists()) {
YamlConfiguration yc = new YamlConfiguration();
yc.load(file);
ConfigurationSection cs;
if (loadTC != null && (cs = yc.getConfigurationSection("towncolors")) != null)
loadTC.accept(cs);
if (loadNC != null && (cs = yc.getConfigurationSection("nationcolors")) != null)
loadNC.accept(cs);
PluginMain.Instance.getLogger().info("Loaded files!");
} else
PluginMain.Instance.getLogger().info("No files to load, first run probably.");
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while loading chat files!", e);
}
} }
} }

View file

@ -61,7 +61,7 @@ public class TownyListener implements Listener {
@EventHandler @EventHandler
public void onNationRename(RenameNationEvent event) { 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()); val clrs = TownColorComponent.NationColor.remove(event.getOldName().toLowerCase());
if (clrs != null) if (clrs != null)
TownColorComponent.NationColor.put(event.getNation().getName().toLowerCase(), clrs); 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 @EventHandler //Gets called on town load as well
public void onNationJoin(NationAddTownEvent event) { public void onNationJoin(NationAddTownEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return; if (!TownColorComponent.getComponent().useNationColors.get()) return;
updateTownMembers(event.getTown()); updateTownMembers(event.getTown());
} }
@EventHandler @EventHandler
public void onNationLeave(NationRemoveTownEvent event) { 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 updateTownMembers(event.getTown()); //The town still has it's colours
} }
@EventHandler @EventHandler
public void onNationDelete(DeleteNationEvent event) { public void onNationDelete(DeleteNationEvent event) {
if (!TownColorComponent.getComponent().useNationColors().get()) return; if (!TownColorComponent.getComponent().useNationColors.get()) return;
TownColorComponent.NationColor.remove(event.getNationName().toLowerCase()); TownColorComponent.NationColor.remove(event.getNationName().toLowerCase());
} }
@EventHandler @EventHandler
public void onNationCreate(NewNationEvent event) { 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()); Player p = Bukkit.getPlayer(event.getNation().getCapital().getMayor().getName());
if (p != null) if (p != null)
p.sendMessage("§6Use /u nationcolor to set a color for the nation."); 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.Color;
import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.chat.CustomTabCompleteMethod;
import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Town; import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.TownyObject;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -21,7 +23,7 @@ import org.bukkit.command.CommandSender;
public class NationColorCommand extends AdminCommandBase { public class NationColorCommand extends AdminCommandBase {
@Command2.Subcommand @Command2.Subcommand
public boolean def(CommandSender sender, String nation, String color) { 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) { if (n == null) {
sender.sendMessage("§cThe nation '" + nation + "' cannot be found."); sender.sendMessage("§cThe nation '" + nation + "' cannot be found.");
return true; return true;
@ -29,13 +31,23 @@ public class NationColorCommand extends AdminCommandBase {
return SetNationColor(sender, n, color); 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) { public static boolean SetNationColor(CommandSender sender, Nation nation, String color) {
val c = TownColorCommand.getColorOrSendError(color, sender); val c = TownColorCommand.getColorOrSendError(color, sender);
if (!c.isPresent()) return true; if (!c.isPresent()) return true;
if (!c.get().getName().equals(Color.White.getName())) { //Default nation color if (!c.get().getName().equals(Color.White.getName())) { //Default nation color
for (val e : TownColorComponent.NationColor.entrySet()) { for (val e : TownColorComponent.NationColor.entrySet()) {
if (e.getValue().getName().equals(c.get().getName())) { if (e.getValue().getName().equals(c.get().getName())) {
sender.sendMessage("§The nation " + e.getKey() + " already uses this color!"); sender.sendMessage("§cThe nation " + e.getKey() + " already uses this color!");
return true; return true;
} }
} }

View file

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

View file

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

@ -1,23 +1,33 @@
package buttondevteam.chat.components.towny; package buttondevteam.chat.components.towny;
import buttondevteam.chat.ChatProcessing; import buttondevteam.chat.ChatUtils;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import com.palmergames.bukkit.towny.TownyLogger;
import lombok.val; import lombok.val;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.filter.LevelRangeFilter;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class TownyAnnouncer { public class TownyAnnouncer {
private static final Pattern LOG_TYPE_PATTERN = Pattern.compile("\\[(\\w+) (?:Msg|Message)](?: (\\w+):)?"); private static final Pattern LOG_TYPE_PATTERN = Pattern.compile("\\[(\\w+) (?:Msg|Message)](?: (\\w+):)?");
private static final Handler HANDLER = new Handler() { private static final String APPENDER_NAME = "Chroma";
private static final AbstractAppender HANDLER = new AbstractAppender(APPENDER_NAME,
LevelRangeFilter.createFilter(Level.INFO, Level.INFO, Filter.Result.ACCEPT, Filter.Result.ACCEPT),
PatternLayout.createDefaultLayout()) {
@Override @Override
public void publish(LogRecord logRecord) { public void append(LogEvent logRecord) {
if (logRecord.getMessage() == null) return; if (logRecord.getMessage() == null) return;
val m = LOG_TYPE_PATTERN.matcher(logRecord.getMessage()); String message = logRecord.getMessage().getFormattedMessage();
val m = LOG_TYPE_PATTERN.matcher(message);
if (!m.find()) return; if (!m.find()) return;
String groupID = m.group(2); //The group ID is correctly cased String groupID = m.group(2); //The group ID is correctly cased
switch (String.valueOf(m.group(1))) { //valueOf: Handles null switch (String.valueOf(m.group(1))) { //valueOf: Handles null
@ -25,31 +35,21 @@ public class TownyAnnouncer {
if (townChannel == null) return; if (townChannel == null) return;
TBMCChatAPI.SendSystemMessage(townChannel, TBMCChatAPI.SendSystemMessage(townChannel,
new Channel.RecipientTestResult(TownyComponent.getTownNationIndex(groupID, false), groupID), new Channel.RecipientTestResult(TownyComponent.getTownNationIndex(groupID, false), groupID),
logRecord.getMessage(), target, ChatProcessing.MCORIGIN); message, target, ChatUtils.MCORIGIN);
break; break;
case "Nation": case "Nation":
if (nationChannel == null) return; if (nationChannel == null) return;
TBMCChatAPI.SendSystemMessage(nationChannel, TBMCChatAPI.SendSystemMessage(nationChannel,
new Channel.RecipientTestResult(TownyComponent.getTownNationIndex(groupID, true), groupID), new Channel.RecipientTestResult(TownyComponent.getTownNationIndex(groupID, true), groupID),
logRecord.getMessage(), target, ChatProcessing.MCORIGIN); message, target, ChatUtils.MCORIGIN);
break; break;
case "Global": case "Global":
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, TBMCChatAPI.SendSystemMessage(Channel.globalChat,
Channel.RecipientTestResult.ALL, Channel.RecipientTestResult.ALL,
logRecord.getMessage(), target, ChatProcessing.MCORIGIN); message, target, ChatUtils.MCORIGIN);
break; break;
} }
} }
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}; };
private static TBMCSystemChatEvent.BroadcastTarget target; private static TBMCSystemChatEvent.BroadcastTarget target;
@ -60,7 +60,23 @@ public class TownyAnnouncer {
target = TBMCSystemChatEvent.BroadcastTarget.add("towny"); target = TBMCSystemChatEvent.BroadcastTarget.add("towny");
TownyAnnouncer.townChannel = townChannel; TownyAnnouncer.townChannel = townChannel;
TownyAnnouncer.nationChannel = nationChannel; TownyAnnouncer.nationChannel = nationChannel;
TownyLogger.log.addHandler(HANDLER); /*System.out.println(LogManager.getLogger("com.palmergames.bukkit.towny"));
((Logger) LogManager.getLogger("com.palmergames.bukkit.towny")).getContext().getConfiguration().addAppender(HANDLER);
System.out.println(((Logger) LogManager.getLogger("com.palmergames.bukkit.towny")).getAppenders());
((LoggerContext)LogManager.getContext(false)).updateLoggers();
System.out.println(((Logger) LogManager.getLogger("com.palmergames.bukkit.towny")).getAppenders());
((Logger) LogManager.getLogger("com.palmergames.bukkit.towny")).getContext().updateLoggers();
System.out.println(((Logger) LogManager.getLogger("com.palmergames.bukkit.towny")).getAppenders());*/
val lc = (LoggerContext) LogManager.getContext(false);
HANDLER.start();
lc.getConfiguration().addAppender(HANDLER);
Logger logger = lc.getLogger("com.palmergames.bukkit.towny");
//System.out.println(logger);
//System.out.println(lc.getConfiguration().<Appender>getAppender(HANDLER.getName())); //T defaults to String because of the context which results in a cast exception
logger.addAppender(lc.getConfiguration().getAppender(HANDLER.getName()));
logger.get().addAppender(HANDLER, Level.INFO, HANDLER.getFilter());
lc.updateLoggers();
//System.out.println(logger.getAppenders());
} }
public static void setdown() { public static void setdown() {
@ -68,6 +84,6 @@ public class TownyAnnouncer {
target = null; target = null;
TownyAnnouncer.townChannel = null; TownyAnnouncer.townChannel = null;
TownyAnnouncer.nationChannel = null; TownyAnnouncer.nationChannel = null;
TownyLogger.log.removeHandler(HANDLER); ((Logger) LogManager.getLogger("com.palmergames.bukkit.towny")).getAppenders().remove(APPENDER_NAME);
} }
} }

View file

@ -1,29 +1,33 @@
package buttondevteam.chat.components.towny; package buttondevteam.chat.components.towny;
import buttondevteam.chat.PluginMain; import buttondevteam.chat.PluginMain;
import buttondevteam.chat.formatting.TellrawPart;
import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.chat.Color; import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import com.palmergames.bukkit.towny.Towny; 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.exceptions.NotRegisteredException;
import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Nation;
import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Resident;
import com.palmergames.bukkit.towny.object.Town; import com.palmergames.bukkit.towny.object.Town;
import com.palmergames.bukkit.towny.object.TownyUniverse;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import net.kyori.adventure.text.TextComponent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors; 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 class TownyComponent extends Component<PluginMain> {
public static TownyUniverse TU; public static TownyAPI dataSource;
private static ArrayList<String> Towns; private static ArrayList<String> Towns;
private static ArrayList<String> Nations; private static ArrayList<String> Nations;
@ -32,12 +36,12 @@ public class TownyComponent extends Component<PluginMain> {
@Override @Override
protected void enable() { protected void enable() {
TU = ((Towny) Bukkit.getPluginManager().getPlugin("Towny")).getTownyUniverse(); dataSource = TownyAPI.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 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 = TU.getNationsMap().values().stream().map(Nation::getName).collect(Collectors.toCollection(ArrayList::new)); // Same here but with nations Nations = TownyUniverse.getInstance().getNations().stream().map(Nation::getName).collect(Collectors.toCollection(ArrayList::new)); // Same here but with nations
TBMCChatAPI.RegisterChatChannel( TBMCChatAPI.registerChatChannel(
TownChat = new Channel("§3TC§f", Color.DarkAqua, "tc", s -> checkTownNationChat(s, false))); 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))); NationChat = new Channel("§6NC§f", Color.Gold, "nc", s -> checkTownNationChat(s, true)));
TownyAnnouncer.setup(TownChat, NationChat); TownyAnnouncer.setup(TownChat, NationChat);
} }
@ -47,22 +51,34 @@ public class TownyComponent extends Component<PluginMain> {
TownyAnnouncer.setdown(); TownyAnnouncer.setdown();
} }
public void handleSpies(Channel channel, TellrawPart json, Function<TellrawPart, String> toJson) { public Consumer<Player> handleSpiesInit(Channel channel, TextComponent.Builder json) {
if (channel.ID.equals(TownChat.ID) || channel.ID.equals(NationChat.ID)) { if (channel.getIdentifier().equals(TownChat.getIdentifier()) || channel.getIdentifier().equals(NationChat.getIdentifier())) {
((List<TellrawPart>) json.getExtra()).add(0, new TellrawPart("[SPY]")); // TODO: Cannot prepend to json, so we need to run this ealier
String jsonstr = toJson.apply(json); //((List<TellrawPart>) json.getExtra()).add(0, new TellrawPart("[SPY]"));
Bukkit.getServer().dispatchCommand(PluginMain.Console, String.format( return p -> handleSpies(channel, p, json);
"tellraw @a[score_%s=1000,score_%s_min=1000] %s", channel.ID, channel.ID, jsonstr)); }
return p -> {};
}
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 * 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) { private static Channel.RecipientTestResult checkTownNationChat(ChromaGamerBase user, boolean nationchat) {
if (!(sender instanceof Player)) if (!(user instanceof TBMCPlayerBase))
return new Channel.RecipientTestResult("§cYou are not a player!"); 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); 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 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 result = new Channel.RecipientTestResult(1000, "allspies"); // There won't be more than a thousand towns/nations probably

View file

@ -1,401 +0,0 @@
package buttondevteam.chat.formatting;
import buttondevteam.chat.ChatProcessing;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.lib.chat.Color;
import buttondevteam.lib.chat.Priority;
import lombok.Builder;
import lombok.Data;
import lombok.val;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* A {@link ChatFormatter} shows what formatting to use based on regular expressions. {@link ChatFormatter#Combine(List, String, TellrawPart)} is used to turn it into a {@link TellrawPart}, combining
* intersecting parts found, for example when {@code _abc*def*ghi_} is said in chat, it'll turn it into an underlined part, then an underlined <i>and italics</i> part, finally an underlined part
* again.
*
* @author NorbiPeti
*
*/
@Data
@Builder
public final class ChatFormatter {
Pattern regex;
boolean italic;
boolean bold;
boolean underlined;
boolean strikethrough;
boolean obfuscated;
Color color;
TriFunc<String, ChatFormatter, FormattedSection, String> onmatch;
String openlink;
@Builder.Default
Priority priority = Priority.Normal;
@Builder.Default
short removeCharCount = 0;
@Builder.Default
Type type = Type.Normal;
String hoverText;
public enum Type {
Normal,
/**
* Matches a start and an end section which gets converted to one section (for example see italics)
*/
Range,
/**
* Exclude matching area from further processing (besides this formatter)
*/
Excluder
}
@FunctionalInterface
public interface TriFunc<T1, T2, T3, R> {
R apply(T1 x1, T2 x2, T3 x3);
}
public static void Combine(List<ChatFormatter> formatters, String str, TellrawPart tp) {
/*
* This method assumes that there is always a global formatter
*/
header("ChatFormatter.Combine begin");
ArrayList<FormattedSection> sections = new ArrayList<FormattedSection>();
for (ChatFormatter formatter : formatters) {
if (formatter.type != Type.Excluder)
continue;
Matcher matcher = formatter.regex.matcher(str);
while (matcher.find()) {
DebugCommand.SendDebugMessage("Found match from " + matcher.start() + " to " + (matcher.end() - 1));
DebugCommand.SendDebugMessage("With excluder formatter:" + formatter);
sendMessageWithPointer(str, matcher.start(), matcher.end() - 1);
if (formatter.regex != ChatProcessing.ENTIRE_MESSAGE_PATTERN && sections.stream().anyMatch(fs -> fs.type == Type.Excluder && (fs.End >= matcher.start() && fs.Start <= matcher.end() - 1))) {
DebugCommand.SendDebugMessage("Ignoring formatter because of an excluder");
continue; //Exclude areas matched by excluders - Range sections are correctly handled afterwards
}
ArrayList<String> groups = new ArrayList<String>();
for (int i = 0; i < matcher.groupCount(); i++)
groups.add(matcher.group(i + 1));
if (groups.size() > 0)
DebugCommand.SendDebugMessage("First group: " + groups.get(0));
FormattedSection section = new FormattedSection(formatter, matcher.start(), matcher.end() - 1, groups,
formatter.type);
sections.add(section);
}
}
header("Section creation (excluders done)");
for (ChatFormatter formatter : formatters) {
if (formatter.type == Type.Excluder)
continue;
Matcher matcher = formatter.regex.matcher(str);
while (matcher.find()) {
DebugCommand.SendDebugMessage("Found match from " + matcher.start() + " to " + (matcher.end() - 1));
DebugCommand.SendDebugMessage("With formatter:" + formatter);
sendMessageWithPointer(str, matcher.start(), matcher.end() - 1);
if (formatter.regex != ChatProcessing.ENTIRE_MESSAGE_PATTERN && sections.stream().anyMatch(fs -> fs.type == Type.Excluder && (fs.End >= matcher.start() && fs.Start <= matcher.end() - 1))) {
DebugCommand.SendDebugMessage("Ignoring formatter because of an excluder");
continue; //Exclude areas matched by excluders - Range sections are correctly handled afterwards
}
ArrayList<String> groups = new ArrayList<String>();
for (int i = 0; i < matcher.groupCount(); i++)
groups.add(matcher.group(i + 1));
if (groups.size() > 0)
DebugCommand.SendDebugMessage("First group: " + groups.get(0));
FormattedSection section = new FormattedSection(formatter, matcher.start(), matcher.end() - 1, groups,
formatter.type);
sections.add(section);
}
}
sections.sort(
(s1, s2) -> s1.Start == s2.Start
? s1.End == s2.End ? Integer.compare(s2.Formatters.get(0).priority.GetValue(),
s1.Formatters.get(0).priority.GetValue()) : Integer.compare(s2.End, s1.End)
: Integer.compare(s1.Start, s2.Start));
/**
* 0: Start - 1: End index
*/
val remchars = new ArrayList<int[]>();
header("Range section conversion");
ArrayList<FormattedSection> combined = new ArrayList<>();
Map<ChatFormatter, FormattedSection> nextSection = new HashMap<>();
boolean escaped = false;
int takenStart = -1, takenEnd = -1;
ChatFormatter takenFormatter = null;
for (int i = 0; i < sections.size(); i++) {
// Set ending to -1 until closed with another 1 long "section" - only do this if IsRange is true
final FormattedSection section = sections.get(i);
if (section.type!=Type.Range) {
escaped = section.Formatters.contains(ChatProcessing.ESCAPE_FORMATTER) && !escaped; // Enable escaping on first \, disable on second
if (escaped) {// Don't add the escape character
remchars.add(new int[]{section.Start, section.Start});
DebugCommand.SendDebugMessage("Found escaper section: " + section);
} else {
combined.add(section); // The above will delete the \
DebugCommand.SendDebugMessage("Added section: " + section);
}
sendMessageWithPointer(str, section.Start, section.End);
continue;
}
if (!escaped) {
if (combined.stream().anyMatch(s -> section.type != Type.Range && (s.Start == section.Start
|| (s.Start < section.Start ? s.End >= section.Start : s.Start <= section.End)))) {
DebugCommand.SendDebugMessage("Range " + section + " overlaps with a combined section, ignoring.");
sendMessageWithPointer(str, section.Start, section.End);
continue;
}
if (section.Start == takenStart || (section.Start > takenStart && section.Start < takenEnd)) {
/*
* if (nextSection.containsKey(section.Formatters.get(0)) ? section.RemCharFromStart <= takenEnd - takenStart : section.RemCharFromStart > takenEnd - takenStart) {
*/
if (section.Formatters.get(0).removeCharCount < takenEnd - takenStart) {
DebugCommand.SendDebugMessage("Lose: " + section);
sendMessageWithPointer(str, section.Start, section.End);
DebugCommand.SendDebugMessage("And win: " + takenFormatter);
continue; // The current section loses
}
nextSection.remove(takenFormatter); // The current section wins
DebugCommand.SendDebugMessage("Win: " + section);
sendMessageWithPointer(str, section.Start, section.End);
DebugCommand.SendDebugMessage("And lose: " + takenFormatter);
}
takenStart = section.Start;
takenEnd = section.Start + section.Formatters.get(0).removeCharCount;
takenFormatter = section.Formatters.get(0);
if (nextSection.containsKey(section.Formatters.get(0))) {
FormattedSection s = nextSection.remove(section.Formatters.get(0));
// section: the ending marker section - s: the to-be full section
s.End = takenEnd - 1; //Take the remCharCount into account as well
// s.IsRange = false; // IsRange means it's a 1 long section indicating a start or an end
combined.add(s);
DebugCommand.SendDebugMessage("Finished section: " + s);
sendMessageWithPointer(str, s.Start, s.End);
} else {
DebugCommand.SendDebugMessage("Adding next section: " + section);
sendMessageWithPointer(str, section.Start, section.End);
nextSection.put(section.Formatters.get(0), section);
}
DebugCommand
.SendDebugMessage("New area taken: (" + takenStart + "-" + takenEnd + ") " + takenFormatter);
sendMessageWithPointer(str, takenStart, takenEnd);
} else {
DebugCommand.SendDebugMessage("Skipping section: " + section); // This will keep the text (character)
sendMessageWithPointer(str, section.Start, section.End);
escaped = false; // Reset escaping if applied, like if we're at the '*' in '\*'
}
}
//Do not finish unfinished sections, ignore them
sections = combined;
header("Adding remove chars (RC)"); // Important to add after the range section conversion
sections.stream()
.flatMap(fs -> fs.Formatters.stream().filter(cf -> cf.removeCharCount > 0)
.mapToInt(cf -> cf.removeCharCount).mapToObj(rcc -> new int[]{fs.Start, fs.Start + rcc - 1}))
.forEach(rc -> remchars.add(rc));
sections.stream()
.flatMap(fs -> fs.Formatters.stream().filter(cf -> cf.removeCharCount > 0)
.mapToInt(cf -> cf.removeCharCount).mapToObj(rcc -> new int[]{fs.End - rcc + 1, fs.End}))
.forEach(rc -> remchars.add(rc));
DebugCommand.SendDebugMessage("Added remchars:");
DebugCommand
.SendDebugMessage(remchars.stream().map(rc -> Arrays.toString(rc)).collect(Collectors.joining("; ")));
header("Section combining");
boolean cont = true;
boolean found = false;
for (int i = 1; cont;) {
int nextindex = i + 1;
if (sections.size() < 2)
break;
DebugCommand.SendDebugMessage("i: " + i);
FormattedSection firstSection = sections.get(i - 1);
DebugCommand.SendDebugMessage("Combining sections " + firstSection);
sendMessageWithPointer(str, firstSection.Start, firstSection.End);
DebugCommand.SendDebugMessage(" and " + sections.get(i));
sendMessageWithPointer(str, sections.get(i).Start, sections.get(i).End);
if (firstSection.Start == sections.get(i).Start && firstSection.End == sections.get(i).End) {
firstSection.Formatters.addAll(sections.get(i).Formatters);
firstSection.Matches.addAll(sections.get(i).Matches);
DebugCommand.SendDebugMessage("To section " + firstSection);
sendMessageWithPointer(str, firstSection.Start, firstSection.End);
sections.remove(i);
found = true;
} else if (firstSection.End > sections.get(i).Start && firstSection.Start < sections.get(i).End) {
int origend = firstSection.End;
firstSection.End = sections.get(i).Start - 1;
int origend2 = sections.get(i).End;
boolean switchends;
if (switchends = origend2 < origend) {
int tmp = origend;
origend = origend2;
origend2 = tmp;
}
FormattedSection section = new FormattedSection(firstSection.Formatters, sections.get(i).Start, origend,
firstSection.Matches, Type.Normal);
section.Formatters.addAll(sections.get(i).Formatters);
section.Matches.addAll(sections.get(i).Matches); // TODO: Clean
sections.add(i, section);
nextindex++;
FormattedSection thirdFormattedSection = sections.get(i + 1);
if (switchends) { // Use the properties of the first section not the second one
thirdFormattedSection.Formatters.clear();
thirdFormattedSection.Formatters.addAll(firstSection.Formatters);
thirdFormattedSection.Matches.clear();
thirdFormattedSection.Matches.addAll(firstSection.Matches);
}
thirdFormattedSection.Start = origend + 1;
thirdFormattedSection.End = origend2;
ArrayList<FormattedSection> sts = sections;
Predicate<FormattedSection> removeIfNeeded = s -> {
if (s.Start < 0 || s.End < 0 || s.Start > s.End) {
DebugCommand.SendDebugMessage("Removing section: " + s);
sendMessageWithPointer(str, s.Start, s.End);
sts.remove(s);
return true;
}
return false;
};
DebugCommand.SendDebugMessage("To sections");
if (!removeIfNeeded.test(firstSection)) {
DebugCommand.SendDebugMessage(" 1:" + firstSection + "");
sendMessageWithPointer(str, firstSection.Start, firstSection.End);
}
if (!removeIfNeeded.test(section)) {
DebugCommand.SendDebugMessage(" 2:" + section + "");
sendMessageWithPointer(str, section.Start, section.End);
}
if (!removeIfNeeded.test(thirdFormattedSection)) {
DebugCommand.SendDebugMessage(" 3:" + thirdFormattedSection);
sendMessageWithPointer(str, thirdFormattedSection.Start, thirdFormattedSection.End);
}
found = true;
}
for (int j = i - 1; j <= i + 1; j++) {
if (j < sections.size() && sections.get(j).End < sections.get(j).Start) {
DebugCommand.SendDebugMessage("Removing section: " + sections.get(j));
sendMessageWithPointer(str, sections.get(j).Start, sections.get(j).End);
sections.remove(j);
j--;
found = true;
}
}
i = nextindex - 1;
i++;
if (i >= sections.size()) {
if (found) {
i = 1;
found = false;
sections.sort(
(s1, s2) -> s1.Start == s2.Start
? s1.End == s2.End
? Integer.compare(s2.Formatters.get(0).priority.GetValue(),
s1.Formatters.get(0).priority.GetValue())
: Integer.compare(s2.End, s1.End)
: Integer.compare(s1.Start, s2.Start));
} else
cont = false;
}
}
header("Section applying");
TellrawPart lasttp = null; String lastlink = null;
for (FormattedSection section : sections) {
DebugCommand.SendDebugMessage("Applying section: " + section);
String originaltext;
int start = section.Start, end = section.End;
DebugCommand.SendDebugMessage("Start: " + start + " - End: " + end);
sendMessageWithPointer(str, start, end);
val rcs = remchars.stream().filter(rc -> rc[0] <= start && start <= rc[1]).findAny();
val rce = remchars.stream().filter(rc -> rc[0] <= end && end <= rc[1]).findAny();
val rci = remchars.stream().filter(rc -> start < rc[0] && rc[1] < end).toArray(int[][]::new);
int s = start, e = end;
if (rcs.isPresent())
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;
}
originaltext = str.substring(s, e + 1);
val sb = new StringBuilder(originaltext);
for (int x = rci.length - 1; x >= 0; x--)
sb.delete(rci[x][0] - start - 1, rci[x][1] - start); //Delete going backwards
originaltext = sb.toString();
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("");
for (ChatFormatter formatter : section.Formatters) {
DebugCommand.SendDebugMessage("Applying formatter: " + formatter);
if (formatter.onmatch != null)
originaltext = formatter.onmatch.apply(originaltext, formatter, section);
if (formatter.color != null)
newtp.setColor(formatter.color);
if (formatter.bold)
newtp.setBold(formatter.bold);
if (formatter.italic)
newtp.setItalic(formatter.italic);
if (formatter.underlined)
newtp.setUnderlined(formatter.underlined);
if (formatter.strikethrough)
newtp.setStrikethrough(formatter.strikethrough);
if (formatter.obfuscated)
newtp.setObfuscated(formatter.obfuscated);
if (formatter.openlink != null)
openlink = formatter.openlink;
if (formatter.hoverText != null)
newtp.setHoverEvent(TellrawEvent.create(TellrawEvent.HoverAction.SHOW_TEXT, formatter.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.setText(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)));
}
tp.addExtra(newtp);
lasttp = newtp;
}
header("ChatFormatter.Combine done");
}
private static void sendMessageWithPointer(String str, int... pointer) {
DebugCommand.SendDebugMessage(str);
StringBuilder sb = new StringBuilder(str.length());
Arrays.sort(pointer);
for (int i = 0; i < pointer.length; i++) {
for (int j = 0; j < pointer[i] - (i > 0 ? pointer[i - 1] + 1 : 0); j++)
sb.append(' ');
if (pointer[i] == (i > 0 ? pointer[i - 1] : -1))
continue;
sb.append('^');
}
DebugCommand.SendDebugMessage(sb.toString());
}
private static void header(String message) {
DebugCommand.SendDebugMessage("\n--------\n" + message + "\n--------\n");
}
}

View file

@ -1,36 +0,0 @@
package buttondevteam.chat.formatting;
import java.util.ArrayList;
import java.util.Collection;
public class FormattedSection {
public int Start;
public int End;
public ArrayList<ChatFormatter> Formatters = new ArrayList<ChatFormatter>();
public ArrayList<String> Matches = new ArrayList<String>();
public ChatFormatter.Type type;
FormattedSection(ChatFormatter formatter, int start, int end, ArrayList<String> matches, ChatFormatter.Type type) {
Start = start;
End = end;
Formatters.add(formatter);
Matches.addAll(matches);
this.type = type;
}
FormattedSection(Collection<ChatFormatter> formatters, int start, int end, ArrayList<String> matches,
ChatFormatter.Type type) {
Start = start;
End = end;
Formatters.addAll(formatters);
Matches.addAll(matches);
this.type = type;
}
@Override
public String toString() {
return "Section(" + Start + ", " + End + ", formatters: " +
Formatters.toString() + ", matches: " + Matches.toString() + ", " +
type + ")";
}
}

View file

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

View file

@ -1,129 +1,46 @@
package buttondevteam.chat.listener; package buttondevteam.chat.listener;
import buttondevteam.chat.ChatPlayer; import buttondevteam.chat.ChatPlayer;
import buttondevteam.chat.ChatProcessing; import buttondevteam.chat.ChatUtils;
import buttondevteam.chat.PluginMain; import buttondevteam.chat.PluginMain;
import buttondevteam.chat.commands.ucmds.HistoryCommand; import buttondevteam.chat.commands.ucmds.HistoryCommand;
import buttondevteam.chat.components.flair.FlairComponent; import buttondevteam.chat.components.flair.FlairComponent;
import buttondevteam.chat.components.formatter.FormatterComponent;
import buttondevteam.chat.components.towncolors.TownColorComponent; import buttondevteam.chat.components.towncolors.TownColorComponent;
import buttondevteam.core.ComponentManager; import buttondevteam.core.ComponentManager;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.ChatChannelRegisterEvent;
import buttondevteam.core.component.channel.ChatRoom;
import buttondevteam.lib.TBMCChatEvent; import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.ThorpeUtils;
import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget; import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
import buttondevteam.lib.player.TBMCPlayerGetInfoEvent; import buttondevteam.lib.player.TBMCPlayerGetInfoEvent;
import com.google.common.collect.BiMap; import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap; import com.google.common.collect.HashBiMap;
import lombok.val;
import net.ess3.api.events.NickChangeEvent; import net.ess3.api.events.NickChangeEvent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerChatTabCompleteEvent; 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.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.function.BiPredicate;
public class PlayerListener implements Listener { public class PlayerListener implements Listener {
/** /**
* Does not contain format codes, lowercased * 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) @EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerChat(AsyncPlayerChatEvent event) { public void onPlayerChat(AsyncPlayerChatEvent event) {
if (event.isCancelled()) if (event.isCancelled())
return; return;
//The custom event is called in the core, but doesn't cancel the MC event //The custom event is called in the core, but doesn't cancel the MC event
event.setCancelled(true); // The custom event should only be cancelled when muted or similar if (ComponentManager.isEnabled(FormatterComponent.class)) //If not enabled, then let the other plugins deal with the message
} 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", ThorpeUtils.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 @EventHandler
@ -131,66 +48,55 @@ public class PlayerListener implements Listener {
String name = e.getLastToken(); String name = e.getLastToken();
for (Entry<String, UUID> nicknamekv : nicknames.entrySet()) { for (Entry<String, UUID> nicknamekv : nicknames.entrySet()) {
if (nicknamekv.getKey().startsWith(name.toLowerCase())) if (nicknamekv.getKey().startsWith(name.toLowerCase()))
e.getTabCompletions().add(PluginMain.essentials.getUser(Bukkit.getPlayer(nicknamekv.getValue())).getNick(true)); //Tabcomplete with the correct case e.getTabCompletions().add(PluginMain.essentials.getUser(Bukkit.getPlayer(nicknamekv.getValue())).getNick(true)); //Tabcomplete with the correct case
} }
} }
@EventHandler(priority = EventPriority.HIGHEST)
public void onConsoleCommand(ServerCommandEvent event) {
if (onCommandPreprocess(event.getSender(), event.getCommand()))
event.setCommand("dontrunthiscmd");
}
@EventHandler @EventHandler
public void onGetInfo(TBMCPlayerGetInfoEvent e) { public void onGetInfo(TBMCPlayerGetInfoEvent e) {
try (ChatPlayer cp = e.getPlayer().getAs(ChatPlayer.class)) { ChatPlayer cp = e.getPlayer().getAs(ChatPlayer.class);
if (cp == null) if (cp == null)
return; return;
e.addInfo("Minecraft name: " + cp.PlayerName().get()); e.addInfo("Minecraft name: " + cp.getPlayerName());
if (cp.UserName().get() != null && cp.UserName().get().length() > 0) if (cp.UserName.get() != null && cp.UserName.get().length() > 0)
e.addInfo("Reddit name: " + cp.UserName().get()); e.addInfo("Reddit name: " + cp.UserName.get());
if (ComponentManager.isEnabled(FlairComponent.class)) { if (ComponentManager.isEnabled(FlairComponent.class)) {
final String flair = cp.GetFormattedFlair(e.getTarget() != InfoTarget.MCCommand); final String flair = cp.GetFormattedFlair(e.getTarget() != InfoTarget.MCCommand);
if (flair.length() > 0) if (flair.length() > 0)
e.addInfo("/r/TheButton flair: " + flair); 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);
} }
e.addInfo(String.format("Respect: %.2f", cp.getF()));
} }
private long lastError = 0;
@EventHandler @EventHandler
public void onPlayerTBMCChat(TBMCChatEvent e) { public void onPlayerTBMCChat(TBMCChatEvent e) {
try { try {
if (e.isCancelled()) if (e.isCancelled())
return; return;
HistoryCommand.addChatMessage(e.getCm(), e.getChannel()); HistoryCommand.addChatMessage(e.getChatMessage(), e.getChannel());
e.setCancelled(ChatProcessing.ProcessChat(e)); e.setCancelled(FormatterComponent.handleChat(e));
} catch (NoClassDefFoundError | Exception ex) { // Weird things can happen } catch (NoClassDefFoundError | Exception ex) { // Weird things can happen
val str = "§c!§r[" + e.getChannel().DisplayName().get() + "] <" if (lastError < System.nanoTime() - 1000L * 1000L * 1000L * 60 * 60 //60 mins
+ ThorpeUtils.getDisplayName(e.getSender()) + "> " + e.getMessage(); && Bukkit.getOnlinePlayers().size() > 0) { //If there are no players on, display to the first person
for (Player p : Bukkit.getOnlinePlayers()) lastError = System.nanoTime(); //I put the whole thing in the if to protect against race conditions
if (e.shouldSendTo(p)) for (Player p : Bukkit.getOnlinePlayers())
p.sendMessage(str); if (e.shouldSendTo(ChromaGamerBase.getFromSender(p)))
Bukkit.getConsoleSender().sendMessage(str); p.sendMessage("[" + e.getChannel().displayName.get() + "] §cSome features in the message below might be unavailable due to an error.");
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);
} }
} }
@EventHandler
public void onChannelRegistered(ChatChannelRegisterEvent e) {
if (!e.getChannel().isGlobal() && PluginMain.SB.getObjective(e.getChannel().ID) == null) // Not global chat and doesn't exist yet
PluginMain.SB.registerNewObjective(e.getChannel().ID, "dummy");
}
@EventHandler @EventHandler
public void onNickChange(NickChangeEvent e) { public void onNickChange(NickChangeEvent e) {
String nick = e.getValue(); String nick = e.getValue();
if (nick == null) if (nick == null)
nicknames.inverse().remove(e.getAffected().getBase().getUniqueId()); nicknames.inverse().remove(e.getController().getBase().getUniqueId()); //EssentialsX has it swapped
else 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, () -> { Bukkit.getScheduler().runTaskLater(PluginMain.Instance, () -> {
TownColorComponent.updatePlayerColors(e.getAffected().getBase()); //Won't fire this event again TownColorComponent.updatePlayerColors(e.getAffected().getBase()); //Won't fire this event again

View file

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

View file

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

View file

@ -1,106 +0,0 @@
package buttondevteam.chat;
import buttondevteam.chat.ObjectTestRunner.Objects;
import buttondevteam.chat.commands.ucmds.admin.DebugCommand;
import buttondevteam.chat.formatting.ChatFormatter;
import buttondevteam.chat.formatting.TellrawEvent;
import buttondevteam.chat.formatting.TellrawEvent.ClickAction;
import buttondevteam.chat.formatting.TellrawEvent.HoverAction;
import buttondevteam.chat.formatting.TellrawPart;
import buttondevteam.core.TestPrepare;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.lib.chat.Color;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.command.CommandSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
@RunWith(ObjectTestRunner.class)
public class ChatFormatIT {
@Objects
public static List<Object> data() {
TestPrepare.PrepareServer();
final CommandSender sender = Mockito.mock(CommandSender.class);
DebugCommand.DebugMode = true;
PluginMain.permission = Mockito.mock(Permission.class);
List<Object> list = new ArrayList<Object>();
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, "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/")), new TellrawPart(" heh").setItalic(true)));
list.add(new ChatFormatIT(sender, "*test _test_ test*", new TellrawPart("test ").setItalic(true).setColor(Color.White),
new TellrawPart("test").setItalic(true).setUnderlined(true).setColor(Color.White), new TellrawPart(" test").setItalic(true).setColor(Color.White)));
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"))));
return list;
}
private final CommandSender sender;
private final String message;
private final TellrawPart[] extras;
public ChatFormatIT(CommandSender sender, String message, TellrawPart... expectedextras) {
this.sender = sender;
this.message = message;
this.extras = expectedextras;
}
@Test
public void testMessage() {
ArrayList<ChatFormatter> cfs = ChatProcessing.addFormatters(Color.White);
final String chid = ChatProcessing.getChannelID(Channel.GlobalChat, ChatProcessing.MCORIGIN);
final TellrawPart tp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatProcessing.MCORIGIN);
ChatFormatter.Combine(cfs, message, tp);
System.out.println("Testing: " + message);
// System.out.println(ChatProcessing.toJson(tp));
final TellrawPart expectedtp = ChatProcessing.createTellraw(sender, message, null, null, null, chid, ChatProcessing.MCORIGIN);
// System.out.println("Raw: " + ChatProcessing.toJson(expectedtp));
for (TellrawPart extra : extras)
expectedtp.addExtra(extra);
assertEquals(ChatProcessing.toJson(expectedtp), ChatProcessing.toJson(tp));
}
}

View file

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

View file

@ -0,0 +1,148 @@
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.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.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 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() {
mock();
load(MainPlugin.class, true);
var sender = ChromaGamerBase.getUser(UUID.randomUUID().toString(), ChatPlayer.class);
DebugCommand.DebugMode = true;
PluginMain.permission = new TestPermissions();
List<Object> list = new ArrayList<>();
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/",
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 ChromaGamerBase sender;
private final String message;
private final Component[] extras;
private boolean rainbowMode;
public ChatFormatIT(ChromaGamerBase sender, String message, Component... expectedExtras) {
this.sender = sender;
this.message = message;
this.extras = expectedExtras;
}
private ChatFormatIT setRainbowMode() {
rainbowMode = true;
return this;
}
@Test
public void testMessage() {
System.out.println("Testing: " + message);
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());
}
}