Compare commits
	
		
			309 Commits
		
	
	
		
			patch-loop
			...
			18eae8a1a6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 18eae8a1a6 | ||
|   | 6e1751733f | ||
|   | 6335af840b | ||
|   | 336333acb1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d110f6629b | ||
|   | 6f70b15e2e | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | b30499e2b6 | ||
|   | 5079181c28 | ||
|   | ee02d98cb2 | ||
|   | c7ff3b8a14 | ||
|   | de60af0d7b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a9218a7aa7 | ||
|   | 67c65e0464 | ||
|   | 1be25b6c74 | ||
|   | 8525b44961 | ||
|   | 1fca510a08 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3384185285 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3075d2c19d | ||
|   | bc528d5d98 | ||
|   | 25cf8d682b | ||
|   | 17e23d5c3f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d6c5197cb9 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | dd96f0f878 | ||
|   | 8a9501ffe4 | ||
|   | 5e25c63c5a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | bd963501ec | ||
|   | da795a7094 | ||
|   | 84d0ea73fa | ||
|   | 0851e39197 | ||
|   | 86e6fdf8a2 | ||
|   | 6ab0f5eba7 | ||
|   | f224787222 | ||
|   | 82684c7b6b | ||
|   | c2f73d32b8 | ||
|   | e642b9dde1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | db623d10c5 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 61bb9f5b93 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 9551b45328 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | dc680b87eb | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 156eda78c6 | ||
|   | 31be68af51 | ||
|   | ffa011c7b1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 22536c11bd | ||
|   | 2394e204fa | ||
|   | 1b88a84710 | ||
|   | 7606d4437b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3e9a7e45c4 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | f6c5332c1a | ||
|   | d0fa62d424 | ||
|   | 464ed0184c | ||
|   | eda268b481 | ||
|   | 3e1007527c | ||
|   | b52b14696c | ||
|   | 94d5b0d03c | ||
|   | c3f228f626 | ||
|   | 02c5c1ee76 | ||
|   | c69acf728c | ||
|   | a1cd694363 | ||
|   | 3e2bc8e2d7 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ad7163d2d6 | ||
|   | 19918c694f | ||
|   | 21c8f2815a | ||
|   | 737d545fb6 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | b23a51825e | ||
|   | 708c5b6254 | ||
|   | f5af11193c | ||
|   | b711e4033f | ||
|   | 3deaaadc3a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 51b9a6b0b8 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1a807731a5 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 772ad9951f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 2431c40a5c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 8144ae8d7b | ||
|   | 0757c39a6f | ||
|   | 231024ba42 | ||
|   | 8ce7a7f8b6 | ||
|   | e1462ccdd1 | ||
|   | 70f346c1dc | ||
|   | 197bf13a28 | ||
|   | 0925c06f9b | ||
|   | 16298a75f2 | ||
|   | 39b10c0b16 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | bd8d114992 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 30e12c6fe0 | ||
|   | bd009ca52d | ||
|   | 65d8edf62d | ||
|   | f5157f12a4 | ||
|   | df20effacc | ||
|   | c92581d0dc | ||
|   | e442c3da5c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | f903c54d55 | ||
|   | 0d45378986 | ||
|   | 0f5f09b6c5 | ||
|   | e5c80d0044 | ||
|   | 9cdb2ba3ea | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d0e5cf7ce5 | ||
|   | c8568764f6 | ||
|   | a7dbbc2f0a | ||
|   | 68b2df2b1e | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1ef4d27dbe | ||
|   | 94a1fb5117 | ||
|   | 78aef86a8f | ||
|   | b34cfcde5a | ||
|   | 86e079a4b1 | ||
|   | 1c42c34081 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | fed646d18b | ||
|   | 653f1691d7 | ||
|   | 3cb7a12738 | ||
|   | f3397b3003 | ||
|   | 497c6879e0 | ||
|   | 7b27dfaf5e | ||
|   | f9b75c4a3a | ||
|   | 0509303fd3 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | f486a251f3 | ||
|   | 5a1e342e0d | ||
|   | d9bbdc3281 | ||
|   | cfe00fa47c | ||
|   | d68ebd1eaf | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a7cd79eb41 | ||
|   | 9e83ee6f0c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 7c81d91740 | ||
|   | 5b126b7f4d | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 9fe7d21f4b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 94ea0271ba | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3af672d2f2 | ||
|   | 0dd7b98428 | ||
|   | a793692a2c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 23fb838227 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 2d6d89d668 | ||
|   | 0199cb90ff | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 958cef5084 | ||
|   | 9f5ace9025 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3a6e2631bf | ||
|   | c7adcf9fdf | ||
|   | da3616e636 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | b371fe67a5 | ||
|   | 6324c7d527 | ||
|   | 6263fe283b | ||
|   | 9a7617f9b8 | ||
|   | 9a71358dfa | ||
|   | a96a2e80a1 | ||
|   | 68200133b6 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 188d502c59 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 84ac683c1d | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | b418c94215 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 38e593a698 | ||
|   | 38028e8e90 | ||
|   | 3db27052a1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e24f9223df | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 9e5ed82c99 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 606fa278c4 | ||
|   | 7dd549ff1e | ||
|   | 3c12b04c98 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5545850f9d | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 2f909b44d7 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ff155ebbb4 | ||
|   | a0a4fa0e56 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1b76a26691 | ||
|   | bd7bd2739a | ||
|   | a7ad407f4b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 931ff0fde6 | ||
|   | dfd847f705 | ||
|   | a1fee720b9 | ||
|   | 963854f8d5 | ||
|   | 2ef5e7004b | ||
|   | 2e6f0dd442 | ||
|   | 7790783949 | ||
|   | f4534c8273 | ||
|   | 76673f02a4 | ||
|   | b47ae0944c | ||
|   | f9712cbc7c | ||
|   | 1b6d845530 | ||
|   | 19424aba9d | ||
|   | 71ac9b34fa | ||
|   | 7651d4a249 | ||
|   | f8e0bccdf0 | ||
|   | a5b6eb6afa | ||
|   | 41471da9db | ||
|   | e71767688d | ||
|   | 5467e3a842 | ||
|   | 511017ab35 | ||
|   | c3e8cfac79 | ||
|   | bf2b3c68f8 | ||
|   | 68e74a8c03 | ||
|   | 5b4a540440 | ||
|   | 88da5c05c7 | ||
|   | 2d369e8945 | ||
|   | 02548c4b9b | ||
|   | 71990e3ccc | ||
|   | 5e7dcc48b9 | ||
|   | 5cdba87b87 | ||
|   | 696315615d | ||
|   | dd3f820040 | ||
|   | 78ca16dfe3 | ||
|   | adc32d5a5c | ||
|   | 12e4514813 | ||
|   | 587fb37bdf | ||
|   | d221e52929 | ||
|   | e151a6cf92 | ||
|   | 9ced5ce131 | ||
|   | c8e876bfe2 | ||
|   | 2a716bbc7f | ||
|   | 00590b6c0d | ||
|   | 2ff4be7846 | ||
|   | ff5727c5ef | ||
|   | e46bc343e4 | ||
|   | 5972fd2353 | ||
|   | 8c0e4b1d33 | ||
|   | a737a754d1 | ||
|   | fc8685a042 | ||
|   | cc4765b4fe | ||
|   | eccdf87f22 | ||
|   | 862bb2ac72 | ||
|   | 34d416a4e8 | ||
|   | 410f64bc9f | ||
|   | 978e68fc74 | ||
|   | a17d8f8a66 | ||
|   | 7e47490e70 | ||
|   | f4f94d3b56 | ||
|   | eae9d45c8a | ||
|   | d2d157c1fe | ||
|   | 9c95d4ba43 | ||
|   | 6cbd7404f4 | ||
|   | ad8a8ef5a9 | ||
|   | e6766a1ee2 | ||
|   | b4ccdaa51c | ||
|   | 3a11656909 | ||
|   | 2479fab632 | ||
|   | 51eb1ac623 | ||
|   | 879f37f046 | ||
|   | f2aadd6014 | ||
|   | 1ad81504ca | ||
|   | 425ee4e142 | ||
|   | 42d8300bb7 | ||
|   | a9d75c5255 | ||
|   | 98afd548d1 | ||
|   | 7fc256dba7 | ||
|   | 21b23624ad | ||
|   | 1ace5c0c8b | ||
|   | bee99beab1 | ||
|   | 8b363d3d1f | ||
|   | c7b0c3cd48 | ||
|   | c0c9b28582 | ||
|   | c3fffbc919 | ||
|   | 6613aaea95 | ||
|   | 53ce6b93a2 | ||
|   | d8e293842f | ||
|   | 5cf869df1a | ||
|   | f26f7d8809 | ||
|   | c5a90475af | ||
|   | 3008d7ef2f | ||
|   | 1823f86dbb | ||
|   | 06bf088d27 | ||
|   | 9953698a7c | ||
|   | bda1605627 | ||
|   | 2e0e88db0d | ||
|   | 96482cc0cf | ||
|   | a283aaf724 | ||
|   | 5db276eb52 | ||
|   | c866619f56 | ||
|   | b9da505efe | ||
|   | 061a7c67bd | ||
|   | 6f7331e852 | ||
|   | 1b489bcc11 | ||
|   | da27924a49 | ||
|   | 15b39887c5 | ||
|   | f9583a7652 | ||
|   | cb738188de | ||
|   | a8b2f5268d | ||
|   | ad50fc9ad3 | ||
|   | a25c2b325b | ||
|   | c57bf61114 | ||
|   | b7935d4b14 | ||
|   | 00982f3620 | ||
|   | 088b2045d0 | ||
|   | 633ff1cfc8 | ||
|   | 6cda6b6c10 | ||
|   | 90573625f1 | ||
|   | d49e97c423 | ||
|   | 39a80e414e | ||
|   | ab9153ddc3 | ||
|   | 7ec1f487c1 | ||
|   | c96628b72e | ||
|   | e5ded9a2fb | ||
|   | 5823f47467 | ||
|   | a0b7f09252 | ||
|   | b60a30c705 | ||
|   | 4fc1a9e770 | ||
|   | f0908b663f | ||
|   | 5fa596fee9 | ||
|   | ada1b95ffc | ||
|   | 72b3bdf676 | ||
|   | 71d1246374 | ||
|   | ac371bb596 | ||
|   | 830ee8f27d | ||
|   | 425dd45109 | ||
|   | 6a039de8db | ||
|   | 8d783aa172 | ||
|   | a4e5f5005b | ||
|   | a7c6edeb63 | ||
|   | 4f23b49fef | ||
|   | cfcc8b1a6f | ||
|   | ebec582ce2 | ||
|   | 3d701fbe0e | ||
|   | e95da11115 | ||
|   | 9f6a798ea6 | ||
|   | 36c8df4d2f | ||
|   | baf2f60850 | ||
|   | 9ac39005f8 | 
							
								
								
									
										63
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | name: Bug inside BungeeCord | ||||||
|  | description: Create a bug report about a problem inside BungeeCord. | ||||||
|  | body: | ||||||
|  |   - type: markdown | ||||||
|  |     attributes: | ||||||
|  |       value: | | ||||||
|  |         #### Report a bug inside bungeecord | ||||||
|  |         Issues happening with forks of BungeeCord should **not** be reported here. | ||||||
|  |   - type: input | ||||||
|  |     id: bungee-version | ||||||
|  |     attributes: | ||||||
|  |       label: Bungeecord version | ||||||
|  |       description: The output of the /bungee command (or just the bungee build number) (execute in bungeecord console for easy text copy) | ||||||
|  |       placeholder: e.g. git:BungeeCord-Bootstrap:1.xx-SNAPSHOT:xxxxxxx:xxxx | ||||||
|  |     validations: | ||||||
|  |       required: true | ||||||
|  |   - type: input | ||||||
|  |     id: server-version | ||||||
|  |     attributes: | ||||||
|  |       label: Server version | ||||||
|  |       description: The output of the /version command (execute in server console for easy text copy) | ||||||
|  |       placeholder: "e.g. git-Spigot-xxxxxxx-xxxxxxx (MC: 1.x.x)" | ||||||
|  |   - type: input | ||||||
|  |     id: client-version | ||||||
|  |     attributes: | ||||||
|  |       label: Client version | ||||||
|  |       description: Minecraft Client Version | ||||||
|  |       placeholder: e.g. 1.18.2 | ||||||
|  |   - type: textarea | ||||||
|  |     id: bungee-plugins | ||||||
|  |     attributes: | ||||||
|  |       label: Bungeecord plugins | ||||||
|  |       description: Please list all BungeeCord plugins you are using. | ||||||
|  |     validations: | ||||||
|  |       required: true | ||||||
|  |   - type: textarea | ||||||
|  |     id: the-bug | ||||||
|  |     attributes: | ||||||
|  |       label: The bug | ||||||
|  |       description: Please describe the bug. Include **details** you find neccessary. If you just have a question, please ask it in [SpigotMC Forums](https://www.spigotmc.org) and not here. | ||||||
|  |     validations: | ||||||
|  |       required: true | ||||||
|  |   - type: textarea | ||||||
|  |     id: logs | ||||||
|  |     attributes: | ||||||
|  |       label: Log output (links) | ||||||
|  |       description: Please put your log output inbetween three backticks (```` ``` ````). Upload your log files to [gist.github.com](https://gist.github.com) and put them in here. | ||||||
|  |       placeholder: | | ||||||
|  |         ``` | ||||||
|  |         log output | ||||||
|  |         ``` | ||||||
|  |   - type: checkboxes | ||||||
|  |     id: checkboxes | ||||||
|  |     attributes: | ||||||
|  |       label: Checking | ||||||
|  |       options: | ||||||
|  |         - label: I am using BungeeCord and **not a fork**. Issues with forks should not be reported here. | ||||||
|  |           required: true | ||||||
|  |         - label: I think this is **not** an issue with a bungeecord plugin. | ||||||
|  |           required: true | ||||||
|  |         - label: I have not read these checkboxes and therefore I just ticked them all. | ||||||
|  |         - label: This is not a question or plugin creation help request. | ||||||
|  |           required: true | ||||||
							
								
								
									
										14
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | blank_issues_enabled: false | ||||||
|  | contact_links: | ||||||
|  |   - name: Configuration help | ||||||
|  |     url: https://www.spigotmc.org/forums/bungeecord-help.70/create-thread | ||||||
|  |     about: Help for configuring bungeecord will only be answered in spigotmc.org forums. | ||||||
|  |   - name: I have a problem with a bungee plugin | ||||||
|  |     url: https://www.spigotmc.org/forums/bungeecord-plugin-help.71/create-thread | ||||||
|  |     about: Help about plugins can be recieved in spigotmc.org forums. | ||||||
|  |   - name: Questions and discussions | ||||||
|  |     url: https://www.spigotmc.org/forums/bungeecord-discussion.21/create-thread | ||||||
|  |     about: spigotmc.org forums are the best place to ask your questions regarding bungeecord. | ||||||
|  |   - name: Plugin creation help | ||||||
|  |     url: https://www.spigotmc.org/forums/bungeecord-plugin-development.23/create-thread | ||||||
|  |     about: Plugin creation help for bungee plugins can be recieved in spigotmc.org forums. | ||||||
							
								
								
									
										36
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | name: Feature request | ||||||
|  | description: Suggest a feature which bungeecord should include. | ||||||
|  | body: | ||||||
|  |   - type: textarea | ||||||
|  |     id: the-feature | ||||||
|  |     attributes: | ||||||
|  |       label: Feature description | ||||||
|  |       description: Please describe your feature or improvement. Please include **details**. | ||||||
|  |     validations: | ||||||
|  |       required: true | ||||||
|  |   - type: textarea | ||||||
|  |     id: goal | ||||||
|  |     attributes: | ||||||
|  |       label: Goal of the feature | ||||||
|  |       description: What is the goal of your feature? | ||||||
|  |     validations: | ||||||
|  |       required: true | ||||||
|  |   - type: textarea | ||||||
|  |     id: alternatives | ||||||
|  |     attributes: | ||||||
|  |       label: Unfitting alternatives | ||||||
|  |       description: What alternatives have you considered and why are they not sufficient for your use case? | ||||||
|  |     validations: | ||||||
|  |       required: true | ||||||
|  |   - type: checkboxes | ||||||
|  |     id: checkboxes | ||||||
|  |     attributes: | ||||||
|  |       label: Checking | ||||||
|  |       options: | ||||||
|  |         - label: This is not a question or plugin creation help request. | ||||||
|  |           required: true | ||||||
|  |         - label: This is a **feature or improvement request**. | ||||||
|  |           required: true | ||||||
|  |         - label: I have not read these checkboxes and therefore I just ticked them all. | ||||||
|  |         - label: I did not use this form to report a bug. | ||||||
|  |           required: true | ||||||
							
								
								
									
										27
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | version: 2 | ||||||
|  |  | ||||||
|  | updates: | ||||||
|  |   - package-ecosystem: "maven" | ||||||
|  |     directory: "/" | ||||||
|  |     schedule: | ||||||
|  |       interval: "daily" | ||||||
|  |     open-pull-requests-limit: 50 | ||||||
|  |     ignore: | ||||||
|  |         # Synchronised with Minecraft | ||||||
|  |       - dependency-name: "com.google.code.gson:gson" | ||||||
|  |         # 9.x has performance issues (see, eg, checkstyle/checkstyle#10934) and 10.x is incompatible | ||||||
|  |       - dependency-name: "com.puppycrawl.tools:checkstyle" | ||||||
|  |         # Newer versions have issues, see #1909 and #2050 | ||||||
|  |       - dependency-name: "jline:jline" | ||||||
|  |         # Needs to be synchronised with maven-resolver-provider dependencies | ||||||
|  |       - dependency-name: "org.apache.maven.resolver:maven-resolver-connector-basic" | ||||||
|  |       - dependency-name: "org.apache.maven.resolver:maven-resolver-transport-http" | ||||||
|  |         # Used with maven-resolver dependencies; 2.0 update breaks other providers | ||||||
|  |       - dependency-name: "org.slf4j:slf4j-api" | ||||||
|  |         update-types: ["version-update:semver-major"] | ||||||
|  |  | ||||||
|  |   - package-ecosystem: "github-actions" | ||||||
|  |     directory: "/" | ||||||
|  |     schedule: | ||||||
|  |       interval: "daily" | ||||||
|  |     open-pull-requests-limit: 50 | ||||||
							
								
								
									
										10
									
								
								.github/workflows/maven.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/maven.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,18 +4,20 @@ on: [push, pull_request] | |||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   build: |   build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-22.04 | ||||||
|  |  | ||||||
|     strategy: |     strategy: | ||||||
|  |       fail-fast: false | ||||||
|       matrix: |       matrix: | ||||||
|         java: [8, 11] |         java: [8, 11, 17, 21] | ||||||
|  |  | ||||||
|     name: Java ${{ matrix.java }} |     name: Java ${{ matrix.java }} | ||||||
|  |  | ||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v2 |     - uses: actions/checkout@v4 | ||||||
|     - uses: actions/setup-java@v1 |     - uses: actions/setup-java@v4 | ||||||
|       with: |       with: | ||||||
|  |         distribution: zulu | ||||||
|         java-version: ${{ matrix.java }} |         java-version: ${{ matrix.java }} | ||||||
|     - run: java -version && mvn --version |     - run: java -version && mvn --version | ||||||
|     - run: mvn --activate-profiles dist --no-transfer-progress package |     - run: mvn --activate-profiles dist --no-transfer-progress package | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | [submodule "native/mbedtls"] | ||||||
|  | 	path = native/mbedtls | ||||||
|  | 	url = https://github.com/ARMmbed/mbedtls.git | ||||||
|  | [submodule "native/zlib"] | ||||||
|  | 	path = native/zlib | ||||||
|  | 	url = https://github.com/cloudflare/zlib.git | ||||||
| @@ -23,4 +23,4 @@ Binaries | |||||||
| -------- | -------- | ||||||
| Precompiled binaries are available for end users on [Jenkins](https://www.spigotmc.org/go/bungeecord-dl). | Precompiled binaries are available for end users on [Jenkins](https://www.spigotmc.org/go/bungeecord-dl). | ||||||
|  |  | ||||||
| (c) 2012-2020 SpigotMC Pty. Ltd. | (c) 2012-2024 SpigotMC Pty. Ltd. | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								api/pom.xml
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								api/pom.xml
									
									
									
									
									
								
							| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-api</artifactId> |     <artifactId>bungeecord-api</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-API</name> |     <name>BungeeCord-API</name> | ||||||
| @@ -46,13 +46,33 @@ | |||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>io.netty</groupId> |             <groupId>io.netty</groupId> | ||||||
|             <artifactId>netty-transport-native-unix-common</artifactId> |             <artifactId>netty-transport-native-unix-common</artifactId> | ||||||
|             <version>${netty.version}</version> |  | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.apache.maven</groupId> | ||||||
|  |             <artifactId>maven-resolver-provider</artifactId> | ||||||
|  |             <version>3.9.6</version> | ||||||
|  |             <!-- not part of the API proper --> | ||||||
|  |             <scope>provided</scope> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.apache.maven.resolver</groupId> | ||||||
|  |             <artifactId>maven-resolver-connector-basic</artifactId> | ||||||
|  |             <version>1.9.18</version> | ||||||
|  |             <!-- not part of the API proper --> | ||||||
|  |             <scope>provided</scope> | ||||||
|  |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.apache.maven.resolver</groupId> | ||||||
|  |             <artifactId>maven-resolver-transport-http</artifactId> | ||||||
|  |             <version>1.9.18</version> | ||||||
|  |             <!-- not part of the API proper --> | ||||||
|  |             <scope>provided</scope> | ||||||
|  |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>org.yaml</groupId> |             <groupId>org.yaml</groupId> | ||||||
|             <artifactId>snakeyaml</artifactId> |             <artifactId>snakeyaml</artifactId> | ||||||
|             <version>1.26</version> |             <version>2.2</version> | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|     </dependencies> |     </dependencies> | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ import java.net.InetSocketAddress; | |||||||
| import java.net.SocketAddress; | import java.net.SocketAddress; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||||
|  | import java.util.Locale; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -68,6 +69,17 @@ public class Util | |||||||
|         return String.format( "0x%02X", i ); |         return String.format( "0x%02X", i ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Formats an char as a unicode value. | ||||||
|  |      * | ||||||
|  |      * @param c the character to format | ||||||
|  |      * @return the unicode representation of the character | ||||||
|  |      */ | ||||||
|  |     public static String unicode(char c) | ||||||
|  |     { | ||||||
|  |         return "\\u" + String.format( "%04x", (int) c ).toUpperCase( Locale.ROOT ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Constructs a pretty one line version of a {@link Throwable}. Useful for |      * Constructs a pretty one line version of a {@link Throwable}. Useful for | ||||||
|      * debugging. |      * debugging. | ||||||
| @@ -76,11 +88,24 @@ public class Util | |||||||
|      * @return a string representing information about the {@link Throwable} |      * @return a string representing information about the {@link Throwable} | ||||||
|      */ |      */ | ||||||
|     public static String exception(Throwable t) |     public static String exception(Throwable t) | ||||||
|  |     { | ||||||
|  |         return exception( t, true ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Constructs a pretty one line version of a {@link Throwable}. Useful for | ||||||
|  |      * debugging. | ||||||
|  |      * | ||||||
|  |      * @param t the {@link Throwable} to format. | ||||||
|  |      * @param includeLineNumbers whether to include line numbers | ||||||
|  |      * @return a string representing information about the {@link Throwable} | ||||||
|  |      */ | ||||||
|  |     public static String exception(Throwable t, boolean includeLineNumbers) | ||||||
|     { |     { | ||||||
|         // TODO: We should use clear manually written exceptions |         // TODO: We should use clear manually written exceptions | ||||||
|         StackTraceElement[] trace = t.getStackTrace(); |         StackTraceElement[] trace = t.getStackTrace(); | ||||||
|         return t.getClass().getSimpleName() + " : " + t.getMessage() |         return t.getClass().getSimpleName() + " : " + t.getMessage() | ||||||
|                 + ( ( trace.length > 0 ) ? " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber() : "" ); |                 + ( ( includeLineNumbers && trace.length > 0 ) ? " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber() : "" ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static String csv(Iterable<?> objects) |     public static String csv(Iterable<?> objects) | ||||||
| @@ -88,6 +113,16 @@ public class Util | |||||||
|         return format( objects, ", " ); |         return format( objects, ", " ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns a string of objects, each separated by a separator. | ||||||
|  |      * | ||||||
|  |      * @param objects the objects to join | ||||||
|  |      * @param separators the separator | ||||||
|  |      * @return joined string | ||||||
|  |      * @see String#join(java.lang.CharSequence, java.lang.Iterable) | ||||||
|  |      * @deprecated use {@link String} join methods | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|     public static String format(Iterable<?> objects, String separators) |     public static String format(Iterable<?> objects, String separators) | ||||||
|     { |     { | ||||||
|         return Joiner.on( separators ).join( objects ); |         return Joiner.on( separators ).join( objects ); | ||||||
|   | |||||||
| @@ -28,18 +28,13 @@ public abstract class AbstractReconnectHandler implements ReconnectHandler | |||||||
|  |  | ||||||
|     public static ServerInfo getForcedHost(PendingConnection con) |     public static ServerInfo getForcedHost(PendingConnection con) | ||||||
|     { |     { | ||||||
|         if ( con.getVirtualHost() == null ) |         String forced = ( con.getVirtualHost() == null ) ? null : con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() ); | ||||||
|         { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         String forced = con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() ); |  | ||||||
|  |  | ||||||
|         if ( forced == null && con.getListener().isForceDefault() ) |         if ( forced == null && con.getListener().isForceDefault() ) | ||||||
|         { |         { | ||||||
|             forced = con.getListener().getDefaultServer(); |             forced = con.getListener().getDefaultServer(); | ||||||
|         } |         } | ||||||
|         return ProxyServer.getInstance().getServerInfo( forced ); |         return ( forced == null ) ? null : ProxyServer.getInstance().getServerInfo( forced ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected abstract ServerInfo getStoredServer(ProxiedPlayer player); |     protected abstract ServerInfo getStoredServer(ProxiedPlayer player); | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| package net.md_5.bungee.api; | package net.md_5.bungee.api; | ||||||
|  |  | ||||||
|  | import com.google.common.base.Preconditions; | ||||||
| import com.google.common.io.BaseEncoding; | import com.google.common.io.BaseEncoding; | ||||||
| import com.google.gson.TypeAdapter; | import com.google.gson.TypeAdapter; | ||||||
| import com.google.gson.internal.bind.TypeAdapters; |  | ||||||
| import com.google.gson.stream.JsonReader; | import com.google.gson.stream.JsonReader; | ||||||
|  | import com.google.gson.stream.JsonToken; | ||||||
| import com.google.gson.stream.JsonWriter; | import com.google.gson.stream.JsonWriter; | ||||||
| import java.awt.image.BufferedImage; | import java.awt.image.BufferedImage; | ||||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||||
| @@ -26,13 +27,26 @@ public class Favicon | |||||||
|         @Override |         @Override | ||||||
|         public void write(JsonWriter out, Favicon value) throws IOException |         public void write(JsonWriter out, Favicon value) throws IOException | ||||||
|         { |         { | ||||||
|             TypeAdapters.STRING.write( out, value == null ? null : value.getEncoded() ); |             if ( value == null ) | ||||||
|  |             { | ||||||
|  |                 out.nullValue(); | ||||||
|  |             } else | ||||||
|  |             { | ||||||
|  |                 out.value( value.getEncoded() ); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public Favicon read(JsonReader in) throws IOException |         public Favicon read(JsonReader in) throws IOException | ||||||
|         { |         { | ||||||
|             String enc = TypeAdapters.STRING.read( in ); |             JsonToken peek = in.peek(); | ||||||
|  |             if ( peek == JsonToken.NULL ) | ||||||
|  |             { | ||||||
|  |                 in.nextNull(); | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             String enc = in.nextString(); | ||||||
|             return enc == null ? null : create( enc ); |             return enc == null ? null : create( enc ); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| @@ -59,6 +73,7 @@ public class Favicon | |||||||
|      */ |      */ | ||||||
|     public static Favicon create(BufferedImage image) |     public static Favicon create(BufferedImage image) | ||||||
|     { |     { | ||||||
|  |         Preconditions.checkArgument( image != null, "image is null" ); | ||||||
|         // check size |         // check size | ||||||
|         if ( image.getWidth() != 64 || image.getHeight() != 64 ) |         if ( image.getWidth() != 64 || image.getHeight() != 64 ) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -105,13 +105,13 @@ public class ServerPing | |||||||
|     @Deprecated |     @Deprecated | ||||||
|     public ServerPing(Protocol version, Players players, String description, String favicon) |     public ServerPing(Protocol version, Players players, String description, String favicon) | ||||||
|     { |     { | ||||||
|         this( version, players, new TextComponent( TextComponent.fromLegacyText( description ) ), favicon == null ? null : Favicon.create( favicon ) ); |         this( version, players, TextComponent.fromLegacy( description ), favicon == null ? null : Favicon.create( favicon ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public ServerPing(Protocol version, Players players, String description, Favicon favicon) |     public ServerPing(Protocol version, Players players, String description, Favicon favicon) | ||||||
|     { |     { | ||||||
|         this( version, players, new TextComponent( TextComponent.fromLegacyText( description ) ), favicon ); |         this( version, players, TextComponent.fromLegacy( description ), favicon ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Deprecated |     @Deprecated | ||||||
| @@ -139,7 +139,7 @@ public class ServerPing | |||||||
|     @Deprecated |     @Deprecated | ||||||
|     public void setDescription(String description) |     public void setDescription(String description) | ||||||
|     { |     { | ||||||
|         this.description = new TextComponent( TextComponent.fromLegacyText( description ) ); |         this.description = TextComponent.fromLegacy( description ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Deprecated |     @Deprecated | ||||||
|   | |||||||
| @@ -2,7 +2,9 @@ package net.md_5.bungee.api.connection; | |||||||
|  |  | ||||||
| import java.net.InetSocketAddress; | import java.net.InetSocketAddress; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  | import java.util.concurrent.CompletableFuture; | ||||||
| import net.md_5.bungee.api.config.ListenerInfo; | import net.md_5.bungee.api.config.ListenerInfo; | ||||||
|  | import org.jetbrains.annotations.ApiStatus; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Represents a user attempting to log into the proxy. |  * Represents a user attempting to log into the proxy. | ||||||
| @@ -89,4 +91,26 @@ public interface PendingConnection extends Connection | |||||||
|      * @return Whether the client is using a legacy client. |      * @return Whether the client is using a legacy client. | ||||||
|      */ |      */ | ||||||
|     boolean isLegacy(); |     boolean isLegacy(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Gets if this connection has been transferred from another server. | ||||||
|  |      * | ||||||
|  |      * @return true if the connection has been transferred | ||||||
|  |      */ | ||||||
|  |     @ApiStatus.Experimental | ||||||
|  |     boolean isTransferred(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Retrieves a cookie from this pending connection. | ||||||
|  |      * | ||||||
|  |      * @param cookie the resource location of the cookie, for example | ||||||
|  |      * "bungeecord:my_cookie" | ||||||
|  |      * @return a {@link CompletableFuture} that will be completed when the | ||||||
|  |      * Cookie response is received. If the cookie is not set in the client, the | ||||||
|  |      * {@link CompletableFuture} will complete with a null value | ||||||
|  |      * @throws IllegalStateException if the player's version is not at least | ||||||
|  |      * 1.20.5 | ||||||
|  |      */ | ||||||
|  |     @ApiStatus.Experimental | ||||||
|  |     CompletableFuture<byte[]> retrieveCookie(String cookie); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package net.md_5.bungee.api.connection; | |||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  | import java.util.concurrent.CompletableFuture; | ||||||
| import net.md_5.bungee.api.Callback; | import net.md_5.bungee.api.Callback; | ||||||
| import net.md_5.bungee.api.ChatMessageType; | import net.md_5.bungee.api.ChatMessageType; | ||||||
| import net.md_5.bungee.api.CommandSender; | import net.md_5.bungee.api.CommandSender; | ||||||
| @@ -13,9 +14,10 @@ import net.md_5.bungee.api.chat.BaseComponent; | |||||||
| import net.md_5.bungee.api.config.ServerInfo; | import net.md_5.bungee.api.config.ServerInfo; | ||||||
| import net.md_5.bungee.api.event.ServerConnectEvent; | import net.md_5.bungee.api.event.ServerConnectEvent; | ||||||
| import net.md_5.bungee.api.score.Scoreboard; | import net.md_5.bungee.api.score.Scoreboard; | ||||||
|  | import org.jetbrains.annotations.ApiStatus; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Represents a player who's connection is being connected to somewhere else, |  * Represents a player whose connection is being connected to somewhere else, | ||||||
|  * whether it be a remote or embedded server. |  * whether it be a remote or embedded server. | ||||||
|  */ |  */ | ||||||
| public interface ProxiedPlayer extends Connection, CommandSender | public interface ProxiedPlayer extends Connection, CommandSender | ||||||
| @@ -57,8 +59,7 @@ public interface ProxiedPlayer extends Connection, CommandSender | |||||||
|     String getDisplayName(); |     String getDisplayName(); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Sets this players display name to be used as their nametag and tab list |      * Sets this player's display name to be used by proxy commands and plugins. | ||||||
|      * name. |  | ||||||
|      * |      * | ||||||
|      * @param name the name to set |      * @param name the name to set | ||||||
|      */ |      */ | ||||||
| @@ -335,6 +336,50 @@ public interface ProxiedPlayer extends Connection, CommandSender | |||||||
|      * Get the {@link Scoreboard} that belongs to this player. |      * Get the {@link Scoreboard} that belongs to this player. | ||||||
|      * |      * | ||||||
|      * @return this player's {@link Scoreboard} |      * @return this player's {@link Scoreboard} | ||||||
|  |      * @deprecated for internal use only, setters will not have the expected | ||||||
|  |      * effect, will not update client state, and may corrupt proxy state | ||||||
|      */ |      */ | ||||||
|  |     @Deprecated | ||||||
|     Scoreboard getScoreboard(); |     Scoreboard getScoreboard(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Retrieves a cookie from this player. | ||||||
|  |      * | ||||||
|  |      * @param cookie the resource location of the cookie, for example | ||||||
|  |      * "bungeecord:my_cookie" | ||||||
|  |      * @return a {@link CompletableFuture} that will be completed when the | ||||||
|  |      * Cookie response is received. If the cookie is not set in the client, the | ||||||
|  |      * {@link CompletableFuture} will complete with a null value | ||||||
|  |      * @throws IllegalStateException if the player's version is not at least | ||||||
|  |      * 1.20.5 | ||||||
|  |      */ | ||||||
|  |     @ApiStatus.Experimental | ||||||
|  |     CompletableFuture<byte[]> retrieveCookie(String cookie); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Stores a cookie in this player's client. | ||||||
|  |      * | ||||||
|  |      * @param cookie the resource location of the cookie, for example | ||||||
|  |      * "bungeecord:my_cookie" | ||||||
|  |      * @param data the data to store in the cookie | ||||||
|  |      * @throws IllegalStateException if the player's version is not at least | ||||||
|  |      * 1.20.5 | ||||||
|  |      */ | ||||||
|  |     @ApiStatus.Experimental | ||||||
|  |     void storeCookie(String cookie, byte[] data); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Requests this player to connect to a different server specified by host | ||||||
|  |      * and port. | ||||||
|  |      * | ||||||
|  |      * This is a client-side transfer - host and port should not specify a | ||||||
|  |      * BungeeCord backend server. | ||||||
|  |      * | ||||||
|  |      * @param host the host of the server to transfer to | ||||||
|  |      * @param port the port of the server to transfer to | ||||||
|  |      * @throws IllegalStateException if the players version is not at least | ||||||
|  |      * 1.20.5 | ||||||
|  |      */ | ||||||
|  |     @ApiStatus.Experimental | ||||||
|  |     void transfer(String host, int port); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,7 @@ | |||||||
| package net.md_5.bungee.api.event; | package net.md_5.bungee.api.event; | ||||||
|  |  | ||||||
| import lombok.AccessLevel; |  | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.EqualsAndHashCode; | import lombok.EqualsAndHashCode; | ||||||
| import lombok.Setter; |  | ||||||
| import lombok.ToString; | import lombok.ToString; | ||||||
| import net.md_5.bungee.api.Callback; | import net.md_5.bungee.api.Callback; | ||||||
| import net.md_5.bungee.api.chat.BaseComponent; | import net.md_5.bungee.api.chat.BaseComponent; | ||||||
| @@ -27,8 +25,7 @@ public class LoginEvent extends AsyncEvent<LoginEvent> implements Cancellable | |||||||
|     /** |     /** | ||||||
|      * Message to use when kicking if this event is canceled. |      * Message to use when kicking if this event is canceled. | ||||||
|      */ |      */ | ||||||
|     @Setter(AccessLevel.NONE) |     private BaseComponent reason; | ||||||
|     private BaseComponent[] cancelReasonComponents; |  | ||||||
|     /** |     /** | ||||||
|      * Connection attempting to login. |      * Connection attempting to login. | ||||||
|      */ |      */ | ||||||
| @@ -42,28 +39,44 @@ public class LoginEvent extends AsyncEvent<LoginEvent> implements Cancellable | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return reason to be displayed |      * @return reason to be displayed | ||||||
|      * @deprecated Use component methods instead. |      * @deprecated use component methods instead | ||||||
|      */ |      */ | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public String getCancelReason() |     public String getCancelReason() | ||||||
|     { |     { | ||||||
|         return BaseComponent.toLegacyText( getCancelReasonComponents() ); |         return TextComponent.toLegacyText( getReason() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @param cancelReason reason to be displayed |      * @param cancelReason reason to be displayed | ||||||
|      * @deprecated Use |      * @deprecated use component methods instead | ||||||
|      * {@link #setCancelReason(net.md_5.bungee.api.chat.BaseComponent...)} |  | ||||||
|      * instead. |  | ||||||
|      */ |      */ | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public void setCancelReason(String cancelReason) |     public void setCancelReason(String cancelReason) | ||||||
|     { |     { | ||||||
|         setCancelReason( TextComponent.fromLegacyText( cancelReason ) ); |         setReason( TextComponent.fromLegacy( cancelReason ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return reason to be displayed | ||||||
|  |      * @deprecated use single component methods instead | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|  |     public BaseComponent[] getCancelReasonComponents() | ||||||
|  |     { | ||||||
|  |         return new BaseComponent[] | ||||||
|  |         { | ||||||
|  |             getReason() | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param cancelReason reason to be displayed | ||||||
|  |      * @deprecated use single component methods instead | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|     public void setCancelReason(BaseComponent... cancelReason) |     public void setCancelReason(BaseComponent... cancelReason) | ||||||
|     { |     { | ||||||
|         this.cancelReasonComponents = cancelReason; |         setReason( TextComponent.fromArray( cancelReason ) ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,8 +3,9 @@ package net.md_5.bungee.api.event; | |||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.EqualsAndHashCode; | import lombok.EqualsAndHashCode; | ||||||
| import lombok.ToString; | import lombok.ToString; | ||||||
|  | import net.md_5.bungee.api.Callback; | ||||||
|  | import net.md_5.bungee.api.config.ServerInfo; | ||||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
| import net.md_5.bungee.api.plugin.Event; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Event called as soon as a connection has a {@link ProxiedPlayer} and is ready |  * Event called as soon as a connection has a {@link ProxiedPlayer} and is ready | ||||||
| @@ -13,11 +14,22 @@ import net.md_5.bungee.api.plugin.Event; | |||||||
| @Data | @Data | ||||||
| @ToString(callSuper = false) | @ToString(callSuper = false) | ||||||
| @EqualsAndHashCode(callSuper = false) | @EqualsAndHashCode(callSuper = false) | ||||||
| public class PostLoginEvent extends Event | public class PostLoginEvent extends AsyncEvent<PostLoginEvent> | ||||||
| { | { | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * The player involved with this event. |      * The player involved with this event. | ||||||
|      */ |      */ | ||||||
|     private final ProxiedPlayer player; |     private final ProxiedPlayer player; | ||||||
|  |     /** | ||||||
|  |      * The server to which the player will initially be connected. | ||||||
|  |      */ | ||||||
|  |     private ServerInfo target; | ||||||
|  |  | ||||||
|  |     public PostLoginEvent(ProxiedPlayer player, ServerInfo target, Callback<PostLoginEvent> done) | ||||||
|  |     { | ||||||
|  |         super( done ); | ||||||
|  |         this.player = player; | ||||||
|  |         this.target = target; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,7 @@ | |||||||
| package net.md_5.bungee.api.event; | package net.md_5.bungee.api.event; | ||||||
|  |  | ||||||
| import lombok.AccessLevel; |  | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.EqualsAndHashCode; | import lombok.EqualsAndHashCode; | ||||||
| import lombok.Setter; |  | ||||||
| import lombok.ToString; | import lombok.ToString; | ||||||
| import net.md_5.bungee.api.Callback; | import net.md_5.bungee.api.Callback; | ||||||
| import net.md_5.bungee.api.chat.BaseComponent; | import net.md_5.bungee.api.chat.BaseComponent; | ||||||
| @@ -32,8 +30,7 @@ public class PreLoginEvent extends AsyncEvent<PreLoginEvent> implements Cancella | |||||||
|     /** |     /** | ||||||
|      * Message to use when kicking if this event is canceled. |      * Message to use when kicking if this event is canceled. | ||||||
|      */ |      */ | ||||||
|     @Setter(AccessLevel.NONE) |     private BaseComponent reason; | ||||||
|     private BaseComponent[] cancelReasonComponents; |  | ||||||
|     /** |     /** | ||||||
|      * Connection attempting to login. |      * Connection attempting to login. | ||||||
|      */ |      */ | ||||||
| @@ -47,28 +44,44 @@ public class PreLoginEvent extends AsyncEvent<PreLoginEvent> implements Cancella | |||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @return reason to be displayed |      * @return reason to be displayed | ||||||
|      * @deprecated Use component methods instead. |      * @deprecated use component methods instead | ||||||
|      */ |      */ | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public String getCancelReason() |     public String getCancelReason() | ||||||
|     { |     { | ||||||
|         return BaseComponent.toLegacyText( getCancelReasonComponents() ); |         return BaseComponent.toLegacyText( getReason() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * @param cancelReason reason to be displayed |      * @param cancelReason reason to be displayed | ||||||
|      * @deprecated Use |      * @deprecated Use component methods instead | ||||||
|      * {@link #setCancelReason(net.md_5.bungee.api.chat.BaseComponent...)} |  | ||||||
|      * instead. |  | ||||||
|      */ |      */ | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public void setCancelReason(String cancelReason) |     public void setCancelReason(String cancelReason) | ||||||
|     { |     { | ||||||
|         setCancelReason( TextComponent.fromLegacyText( cancelReason ) ); |         setReason( TextComponent.fromLegacy( cancelReason ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return reason to be displayed | ||||||
|  |      * @deprecated use single component methods instead | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|  |     public BaseComponent[] getCancelReasonComponents() | ||||||
|  |     { | ||||||
|  |         return new BaseComponent[] | ||||||
|  |         { | ||||||
|  |             getReason() | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param cancelReason reason to be displayed | ||||||
|  |      * @deprecated use single component methods instead | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|     public void setCancelReason(BaseComponent... cancelReason) |     public void setCancelReason(BaseComponent... cancelReason) | ||||||
|     { |     { | ||||||
|         this.cancelReasonComponents = cancelReason; |         setReason( TextComponent.fromArray( cancelReason ) ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import net.md_5.bungee.api.ServerPing; | |||||||
| import net.md_5.bungee.api.connection.PendingConnection; | import net.md_5.bungee.api.connection.PendingConnection; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Called when the proxy is pinged with packet 0xFE from the server list. |  * Called when the proxy is queried for status from the server list. | ||||||
|  */ |  */ | ||||||
| @Data | @Data | ||||||
| @ToString(callSuper = false) | @ToString(callSuper = false) | ||||||
|   | |||||||
| @@ -9,6 +9,13 @@ import net.md_5.bungee.api.config.ServerInfo; | |||||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
| import net.md_5.bungee.api.plugin.Event; | import net.md_5.bungee.api.plugin.Event; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Called when the player is disconnected from a server, for example during | ||||||
|  |  * server switching. | ||||||
|  |  * | ||||||
|  |  * If the player is kicked from a server, {@link ServerKickEvent} will be called | ||||||
|  |  * instead. | ||||||
|  |  */ | ||||||
| @Data | @Data | ||||||
| @AllArgsConstructor | @AllArgsConstructor | ||||||
| @ToString(callSuper = false) | @ToString(callSuper = false) | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ public class ServerKickEvent extends Event implements Cancellable | |||||||
|     /** |     /** | ||||||
|      * Kick reason. |      * Kick reason. | ||||||
|      */ |      */ | ||||||
|     private BaseComponent[] kickReasonComponent; |     private BaseComponent reason; | ||||||
|     /** |     /** | ||||||
|      * Server to send player to if this event is cancelled. |      * Server to send player to if this event is cancelled. | ||||||
|      */ |      */ | ||||||
| @@ -63,24 +63,61 @@ public class ServerKickEvent extends Event implements Cancellable | |||||||
|         this( player, player.getServer().getInfo(), kickReasonComponent, cancelServer, state ); |         this( player, player.getServer().getInfo(), kickReasonComponent, cancelServer, state ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Deprecated | ||||||
|     public ServerKickEvent(ProxiedPlayer player, ServerInfo kickedFrom, BaseComponent[] kickReasonComponent, ServerInfo cancelServer, State state) |     public ServerKickEvent(ProxiedPlayer player, ServerInfo kickedFrom, BaseComponent[] kickReasonComponent, ServerInfo cancelServer, State state) | ||||||
|  |     { | ||||||
|  |         this( player, kickedFrom, TextComponent.fromArray( kickReasonComponent ), cancelServer, state ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ServerKickEvent(ProxiedPlayer player, ServerInfo kickedFrom, BaseComponent reason, ServerInfo cancelServer, State state) | ||||||
|     { |     { | ||||||
|         this.player = player; |         this.player = player; | ||||||
|         this.kickedFrom = kickedFrom; |         this.kickedFrom = kickedFrom; | ||||||
|         this.kickReasonComponent = kickReasonComponent; |         this.reason = reason; | ||||||
|         this.cancelServer = cancelServer; |         this.cancelServer = cancelServer; | ||||||
|         this.state = state; |         this.state = state; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return the kick reason | ||||||
|  |      * @deprecated use component methods instead | ||||||
|  |      */ | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public String getKickReason() |     public String getKickReason() | ||||||
|     { |     { | ||||||
|         return BaseComponent.toLegacyText( kickReasonComponent ); |         return BaseComponent.toLegacyText( getReason() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param reason the kick reason | ||||||
|  |      * @deprecated use component methods instead | ||||||
|  |      */ | ||||||
|     @Deprecated |     @Deprecated | ||||||
|     public void setKickReason(String reason) |     public void setKickReason(String reason) | ||||||
|     { |     { | ||||||
|         kickReasonComponent = TextComponent.fromLegacyText( reason ); |         this.setReason( TextComponent.fromLegacy( reason ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return the kick reason | ||||||
|  |      * @deprecated use single component methods instead | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|  |     public BaseComponent[] getKickReasonComponent() | ||||||
|  |     { | ||||||
|  |         return new BaseComponent[] | ||||||
|  |         { | ||||||
|  |             getReason() | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param kickReasonComponent the kick reason | ||||||
|  |      * @deprecated use single component methods instead | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|  |     public void setKickReasonComponent(BaseComponent[] kickReasonComponent) | ||||||
|  |     { | ||||||
|  |         this.setReason( TextComponent.fromArray( kickReasonComponent ) ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										128
									
								
								api/src/main/java/net/md_5/bungee/api/plugin/LibraryLoader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								api/src/main/java/net/md_5/bungee/api/plugin/LibraryLoader.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | package net.md_5.bungee.api.plugin; | ||||||
|  |  | ||||||
|  | import java.io.File; | ||||||
|  | import java.net.MalformedURLException; | ||||||
|  | import java.net.URL; | ||||||
|  | import java.net.URLClassLoader; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.logging.Level; | ||||||
|  | import java.util.logging.Logger; | ||||||
|  | import org.apache.maven.repository.internal.MavenRepositorySystemUtils; | ||||||
|  | import org.eclipse.aether.DefaultRepositorySystemSession; | ||||||
|  | import org.eclipse.aether.RepositorySystem; | ||||||
|  | import org.eclipse.aether.artifact.Artifact; | ||||||
|  | import org.eclipse.aether.artifact.DefaultArtifact; | ||||||
|  | import org.eclipse.aether.collection.CollectRequest; | ||||||
|  | import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; | ||||||
|  | import org.eclipse.aether.graph.Dependency; | ||||||
|  | import org.eclipse.aether.impl.DefaultServiceLocator; | ||||||
|  | import org.eclipse.aether.repository.LocalRepository; | ||||||
|  | import org.eclipse.aether.repository.RemoteRepository; | ||||||
|  | import org.eclipse.aether.repository.RepositoryPolicy; | ||||||
|  | import org.eclipse.aether.resolution.ArtifactResult; | ||||||
|  | import org.eclipse.aether.resolution.DependencyRequest; | ||||||
|  | import org.eclipse.aether.resolution.DependencyResolutionException; | ||||||
|  | import org.eclipse.aether.resolution.DependencyResult; | ||||||
|  | import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; | ||||||
|  | import org.eclipse.aether.spi.connector.transport.TransporterFactory; | ||||||
|  | import org.eclipse.aether.transfer.AbstractTransferListener; | ||||||
|  | import org.eclipse.aether.transfer.TransferCancelledException; | ||||||
|  | import org.eclipse.aether.transfer.TransferEvent; | ||||||
|  | import org.eclipse.aether.transport.http.HttpTransporterFactory; | ||||||
|  |  | ||||||
|  | class LibraryLoader | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private final Logger logger; | ||||||
|  |     private final RepositorySystem repository; | ||||||
|  |     private final DefaultRepositorySystemSession session; | ||||||
|  |     private final List<RemoteRepository> repositories; | ||||||
|  |  | ||||||
|  |     public LibraryLoader(Logger logger) | ||||||
|  |     { | ||||||
|  |         this.logger = logger; | ||||||
|  |  | ||||||
|  |         DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); | ||||||
|  |         locator.addService( RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class ); | ||||||
|  |         locator.addService( TransporterFactory.class, HttpTransporterFactory.class ); | ||||||
|  |  | ||||||
|  |         this.repository = locator.getService( RepositorySystem.class ); | ||||||
|  |         this.session = MavenRepositorySystemUtils.newSession(); | ||||||
|  |  | ||||||
|  |         session.setChecksumPolicy( RepositoryPolicy.CHECKSUM_POLICY_FAIL ); | ||||||
|  |         session.setLocalRepositoryManager( repository.newLocalRepositoryManager( session, new LocalRepository( "libraries" ) ) ); | ||||||
|  |         session.setTransferListener( new AbstractTransferListener() | ||||||
|  |         { | ||||||
|  |             @Override | ||||||
|  |             public void transferStarted(TransferEvent event) throws TransferCancelledException | ||||||
|  |             { | ||||||
|  |                 logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() ); | ||||||
|  |             } | ||||||
|  |         } ); | ||||||
|  |  | ||||||
|  |         // SPIGOT-7638: Add system properties, | ||||||
|  |         // since JdkVersionProfileActivator needs 'java.version' when a profile has the 'jdk' element | ||||||
|  |         // otherwise it will silently fail and not resolves the dependencies in the affected pom. | ||||||
|  |         session.setSystemProperties( System.getProperties() ); | ||||||
|  |         session.setReadOnly(); | ||||||
|  |  | ||||||
|  |         this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2" ).build() ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ClassLoader createLoader(PluginDescription desc) | ||||||
|  |     { | ||||||
|  |         if ( desc.getLibraries().isEmpty() ) | ||||||
|  |         { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         logger.log( Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[] | ||||||
|  |         { | ||||||
|  |             desc.getName(), desc.getLibraries().size() | ||||||
|  |         } ); | ||||||
|  |  | ||||||
|  |         List<Dependency> dependencies = new ArrayList<>(); | ||||||
|  |         for ( String library : desc.getLibraries() ) | ||||||
|  |         { | ||||||
|  |             Artifact artifact = new DefaultArtifact( library ); | ||||||
|  |             Dependency dependency = new Dependency( artifact, null ); | ||||||
|  |  | ||||||
|  |             dependencies.add( dependency ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         DependencyResult result; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             result = repository.resolveDependencies( session, new DependencyRequest( new CollectRequest( (Dependency) null, dependencies, repositories ), null ) ); | ||||||
|  |         } catch ( DependencyResolutionException ex ) | ||||||
|  |         { | ||||||
|  |             throw new RuntimeException( "Error resolving libraries", ex ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         List<URL> jarFiles = new ArrayList<>(); | ||||||
|  |         for ( ArtifactResult artifact : result.getArtifactResults() ) | ||||||
|  |         { | ||||||
|  |             File file = artifact.getArtifact().getFile(); | ||||||
|  |  | ||||||
|  |             URL url; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 url = file.toURI().toURL(); | ||||||
|  |             } catch ( MalformedURLException ex ) | ||||||
|  |             { | ||||||
|  |                 throw new AssertionError( ex ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             jarFiles.add( url ); | ||||||
|  |             logger.log( Level.INFO, "[{0}] Loaded library {1}", new Object[] | ||||||
|  |             { | ||||||
|  |                 desc.getName(), file | ||||||
|  |             } ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         URLClassLoader loader = new URLClassLoader( jarFiles.toArray( new URL[ 0 ] ) ); | ||||||
|  |  | ||||||
|  |         return loader; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,12 +1,23 @@ | |||||||
| package net.md_5.bungee.api.plugin; | package net.md_5.bungee.api.plugin; | ||||||
|  |  | ||||||
| import com.google.common.base.Preconditions; | import com.google.common.base.Preconditions; | ||||||
|  | import com.google.common.io.ByteStreams; | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.net.URLClassLoader; | import java.net.URLClassLoader; | ||||||
|  | import java.security.CodeSigner; | ||||||
|  | import java.security.CodeSource; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import java.util.concurrent.CopyOnWriteArraySet; | import java.util.concurrent.CopyOnWriteArraySet; | ||||||
|  | import java.util.jar.JarEntry; | ||||||
|  | import java.util.jar.JarFile; | ||||||
|  | import java.util.jar.Manifest; | ||||||
|  | import lombok.ToString; | ||||||
| import net.md_5.bungee.api.ProxyServer; | import net.md_5.bungee.api.ProxyServer; | ||||||
|  |  | ||||||
|  | @ToString(of = "desc") | ||||||
| final class PluginClassloader extends URLClassLoader | final class PluginClassloader extends URLClassLoader | ||||||
| { | { | ||||||
|  |  | ||||||
| @@ -14,6 +25,10 @@ final class PluginClassloader extends URLClassLoader | |||||||
|     // |     // | ||||||
|     private final ProxyServer proxy; |     private final ProxyServer proxy; | ||||||
|     private final PluginDescription desc; |     private final PluginDescription desc; | ||||||
|  |     private final JarFile jar; | ||||||
|  |     private final Manifest manifest; | ||||||
|  |     private final URL url; | ||||||
|  |     private final ClassLoader libraryLoader; | ||||||
|     // |     // | ||||||
|     private Plugin plugin; |     private Plugin plugin; | ||||||
|  |  | ||||||
| @@ -22,11 +37,18 @@ final class PluginClassloader extends URLClassLoader | |||||||
|         ClassLoader.registerAsParallelCapable(); |         ClassLoader.registerAsParallelCapable(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public PluginClassloader(ProxyServer proxy, PluginDescription desc, URL[] urls) |     public PluginClassloader(ProxyServer proxy, PluginDescription desc, File file, ClassLoader libraryLoader) throws IOException | ||||||
|     { |     { | ||||||
|         super( urls ); |         super( new URL[] | ||||||
|  |         { | ||||||
|  |             file.toURI().toURL() | ||||||
|  |         } ); | ||||||
|         this.proxy = proxy; |         this.proxy = proxy; | ||||||
|         this.desc = desc; |         this.desc = desc; | ||||||
|  |         this.jar = new JarFile( file ); | ||||||
|  |         this.manifest = jar.getManifest(); | ||||||
|  |         this.url = file.toURI().toURL(); | ||||||
|  |         this.libraryLoader = libraryLoader; | ||||||
|  |  | ||||||
|         allLoaders.add( this ); |         allLoaders.add( this ); | ||||||
|     } |     } | ||||||
| @@ -34,17 +56,34 @@ final class PluginClassloader extends URLClassLoader | |||||||
|     @Override |     @Override | ||||||
|     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException |     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException | ||||||
|     { |     { | ||||||
|         return loadClass0( name, resolve, true ); |         return loadClass0( name, resolve, true, true ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private Class<?> loadClass0(String name, boolean resolve, boolean checkOther) throws ClassNotFoundException |     private Class<?> loadClass0(String name, boolean resolve, boolean checkOther, boolean checkLibraries) throws ClassNotFoundException | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             return super.loadClass( name, resolve ); |             Class<?> result = super.loadClass( name, resolve ); | ||||||
|  |  | ||||||
|  |             // SPIGOT-6749: Library classes will appear in the above, but we don't want to return them to other plugins | ||||||
|  |             if ( checkOther || result.getClassLoader() == this ) | ||||||
|  |             { | ||||||
|  |                 return result; | ||||||
|  |             } | ||||||
|         } catch ( ClassNotFoundException ex ) |         } catch ( ClassNotFoundException ex ) | ||||||
|         { |         { | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if ( checkLibraries && libraryLoader != null ) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 return libraryLoader.loadClass( name ); | ||||||
|  |             } catch ( ClassNotFoundException ex ) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if ( checkOther ) |         if ( checkOther ) | ||||||
|         { |         { | ||||||
|             for ( PluginClassloader loader : allLoaders ) |             for ( PluginClassloader loader : allLoaders ) | ||||||
| @@ -53,16 +92,81 @@ final class PluginClassloader extends URLClassLoader | |||||||
|                 { |                 { | ||||||
|                     try |                     try | ||||||
|                     { |                     { | ||||||
|                         return loader.loadClass0( name, resolve, false ); |                         return loader.loadClass0( name, resolve, false, proxy.getPluginManager().isTransitiveDepend( desc, loader.desc ) ); | ||||||
|                     } catch ( ClassNotFoundException ex ) |                     } catch ( ClassNotFoundException ex ) | ||||||
|                     { |                     { | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         throw new ClassNotFoundException( name ); |         throw new ClassNotFoundException( name ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected Class<?> findClass(String name) throws ClassNotFoundException | ||||||
|  |     { | ||||||
|  |         String path = name.replace( '.', '/' ).concat( ".class" ); | ||||||
|  |         JarEntry entry = jar.getJarEntry( path ); | ||||||
|  |  | ||||||
|  |         if ( entry != null ) | ||||||
|  |         { | ||||||
|  |             byte[] classBytes; | ||||||
|  |  | ||||||
|  |             try ( InputStream is = jar.getInputStream( entry ) ) | ||||||
|  |             { | ||||||
|  |                 classBytes = ByteStreams.toByteArray( is ); | ||||||
|  |             } catch ( IOException ex ) | ||||||
|  |             { | ||||||
|  |                 throw new ClassNotFoundException( name, ex ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             int dot = name.lastIndexOf( '.' ); | ||||||
|  |             if ( dot != -1 ) | ||||||
|  |             { | ||||||
|  |                 String pkgName = name.substring( 0, dot ); | ||||||
|  |                 if ( getPackage( pkgName ) == null ) | ||||||
|  |                 { | ||||||
|  |                     try | ||||||
|  |                     { | ||||||
|  |                         if ( manifest != null ) | ||||||
|  |                         { | ||||||
|  |                             definePackage( pkgName, manifest, url ); | ||||||
|  |                         } else | ||||||
|  |                         { | ||||||
|  |                             definePackage( pkgName, null, null, null, null, null, null, null ); | ||||||
|  |                         } | ||||||
|  |                     } catch ( IllegalArgumentException ex ) | ||||||
|  |                     { | ||||||
|  |                         if ( getPackage( pkgName ) == null ) | ||||||
|  |                         { | ||||||
|  |                             throw new IllegalStateException( "Cannot find package " + pkgName ); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             CodeSigner[] signers = entry.getCodeSigners(); | ||||||
|  |             CodeSource source = new CodeSource( url, signers ); | ||||||
|  |  | ||||||
|  |             return defineClass( name, classBytes, 0, classBytes.length, source ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return super.findClass( name ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void close() throws IOException | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             super.close(); | ||||||
|  |         } finally | ||||||
|  |         { | ||||||
|  |             jar.close(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void init(Plugin plugin) |     void init(Plugin plugin) | ||||||
|     { |     { | ||||||
|         Preconditions.checkArgument( plugin != null, "plugin" ); |         Preconditions.checkArgument( plugin != null, "plugin" ); | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ package net.md_5.bungee.api.plugin; | |||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
|  | import java.util.LinkedList; | ||||||
|  | import java.util.List; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| @@ -48,4 +50,8 @@ public class PluginDescription | |||||||
|      * Optional description. |      * Optional description. | ||||||
|      */ |      */ | ||||||
|     private String description = null; |     private String description = null; | ||||||
|  |     /** | ||||||
|  |      * Optional libraries. | ||||||
|  |      */ | ||||||
|  |     private List<String> libraries = new LinkedList<>(); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,10 +4,12 @@ import com.google.common.base.Preconditions; | |||||||
| import com.google.common.collect.ArrayListMultimap; | import com.google.common.collect.ArrayListMultimap; | ||||||
| import com.google.common.collect.Multimap; | import com.google.common.collect.Multimap; | ||||||
| import com.google.common.eventbus.Subscribe; | import com.google.common.eventbus.Subscribe; | ||||||
|  | import com.google.common.graph.GraphBuilder; | ||||||
|  | import com.google.common.graph.Graphs; | ||||||
|  | import com.google.common.graph.MutableGraph; | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
| import java.net.URL; |  | ||||||
| import java.net.URLClassLoader; | import java.net.URLClassLoader; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| @@ -31,6 +33,7 @@ import net.md_5.bungee.api.ProxyServer; | |||||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
| import net.md_5.bungee.event.EventBus; | import net.md_5.bungee.event.EventBus; | ||||||
| import net.md_5.bungee.event.EventHandler; | import net.md_5.bungee.event.EventHandler; | ||||||
|  | import org.yaml.snakeyaml.LoaderOptions; | ||||||
| import org.yaml.snakeyaml.Yaml; | import org.yaml.snakeyaml.Yaml; | ||||||
| import org.yaml.snakeyaml.constructor.Constructor; | import org.yaml.snakeyaml.constructor.Constructor; | ||||||
| import org.yaml.snakeyaml.introspector.PropertyUtils; | import org.yaml.snakeyaml.introspector.PropertyUtils; | ||||||
| @@ -49,6 +52,8 @@ public final class PluginManager | |||||||
|     private final Yaml yaml; |     private final Yaml yaml; | ||||||
|     private final EventBus eventBus; |     private final EventBus eventBus; | ||||||
|     private final Map<String, Plugin> plugins = new LinkedHashMap<>(); |     private final Map<String, Plugin> plugins = new LinkedHashMap<>(); | ||||||
|  |     private final MutableGraph<String> dependencyGraph = GraphBuilder.directed().build(); | ||||||
|  |     private final LibraryLoader libraryLoader; | ||||||
|     private final Map<String, Command> commandMap = new HashMap<>(); |     private final Map<String, Command> commandMap = new HashMap<>(); | ||||||
|     private Map<String, PluginDescription> toLoad = new HashMap<>(); |     private Map<String, PluginDescription> toLoad = new HashMap<>(); | ||||||
|     private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create(); |     private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create(); | ||||||
| @@ -60,13 +65,24 @@ public final class PluginManager | |||||||
|         this.proxy = proxy; |         this.proxy = proxy; | ||||||
|  |  | ||||||
|         // Ignore unknown entries in the plugin descriptions |         // Ignore unknown entries in the plugin descriptions | ||||||
|         Constructor yamlConstructor = new Constructor(); |         Constructor yamlConstructor = new Constructor( new LoaderOptions() ); | ||||||
|         PropertyUtils propertyUtils = yamlConstructor.getPropertyUtils(); |         PropertyUtils propertyUtils = yamlConstructor.getPropertyUtils(); | ||||||
|         propertyUtils.setSkipMissingProperties( true ); |         propertyUtils.setSkipMissingProperties( true ); | ||||||
|         yamlConstructor.setPropertyUtils( propertyUtils ); |         yamlConstructor.setPropertyUtils( propertyUtils ); | ||||||
|         yaml = new Yaml( yamlConstructor ); |         yaml = new Yaml( yamlConstructor ); | ||||||
|  |  | ||||||
|         eventBus = new EventBus( proxy.getLogger() ); |         eventBus = new EventBus( proxy.getLogger() ); | ||||||
|  |  | ||||||
|  |         LibraryLoader libraryLoader = null; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             libraryLoader = new LibraryLoader( proxy.getLogger() ); | ||||||
|  |         } catch ( NoClassDefFoundError ex ) | ||||||
|  |         { | ||||||
|  |             // Provided depends were not added back | ||||||
|  |             proxy.getLogger().warning( "Could not initialize LibraryLoader (missing dependencies?)" ); | ||||||
|  |         } | ||||||
|  |         this.libraryLoader = libraryLoader; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -309,6 +325,7 @@ public final class PluginManager | |||||||
|                 status = false; |                 status = false; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             dependencyGraph.putEdge( plugin.getName(), dependName ); | ||||||
|             if ( !status ) |             if ( !status ) | ||||||
|             { |             { | ||||||
|                 break; |                 break; | ||||||
| @@ -320,10 +337,7 @@ public final class PluginManager | |||||||
|         { |         { | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 URLClassLoader loader = new PluginClassloader( proxy, plugin, new URL[] |                 URLClassLoader loader = new PluginClassloader( proxy, plugin, plugin.getFile(), ( libraryLoader != null ) ? libraryLoader.createLoader( plugin ) : null ); | ||||||
|                 { |  | ||||||
|                     plugin.getFile().toURI().toURL() |  | ||||||
|                 } ); |  | ||||||
|                 Class<?> main = loader.loadClass( plugin.getMain() ); |                 Class<?> main = loader.loadClass( plugin.getMain() ); | ||||||
|                 Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance(); |                 Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance(); | ||||||
|  |  | ||||||
| @@ -335,7 +349,7 @@ public final class PluginManager | |||||||
|                 } ); |                 } ); | ||||||
|             } catch ( Throwable t ) |             } catch ( Throwable t ) | ||||||
|             { |             { | ||||||
|                 proxy.getLogger().log( Level.WARNING, "Error enabling plugin " + plugin.getName(), t ); |                 proxy.getLogger().log( Level.WARNING, "Error loading plugin " + plugin.getName(), t ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -463,4 +477,19 @@ public final class PluginManager | |||||||
|     { |     { | ||||||
|         return Collections.unmodifiableCollection( commandMap.entrySet() ); |         return Collections.unmodifiableCollection( commandMap.entrySet() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     boolean isTransitiveDepend(PluginDescription plugin, PluginDescription depend) | ||||||
|  |     { | ||||||
|  |         Preconditions.checkArgument( plugin != null, "plugin" ); | ||||||
|  |         Preconditions.checkArgument( depend != null, "depend" ); | ||||||
|  |  | ||||||
|  |         if ( dependencyGraph.nodes().contains( plugin.getName() ) ) | ||||||
|  |         { | ||||||
|  |             if ( Graphs.reachableNodes( dependencyGraph, plugin.getName() ).contains( depend.getName() ) ) | ||||||
|  |             { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,12 +1,13 @@ | |||||||
| package net.md_5.bungee.api; | package net.md_5.bungee.api; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import java.net.InetSocketAddress; | import java.net.InetSocketAddress; | ||||||
| import java.net.SocketAddress; | import java.net.SocketAddress; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import net.md_5.bungee.api.config.ServerInfo; | import net.md_5.bungee.api.config.ServerInfo; | ||||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
| import net.md_5.bungee.api.event.ServerConnectEvent; | import net.md_5.bungee.api.event.ServerConnectEvent; | ||||||
| import org.junit.Test; | import org.junit.jupiter.api.Test; | ||||||
|  |  | ||||||
| public class ServerConnectRequestTest | public class ServerConnectRequestTest | ||||||
| { | { | ||||||
| @@ -78,15 +79,15 @@ public class ServerConnectRequestTest | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     @Test(expected = NullPointerException.class) |     @Test | ||||||
|     public void testNullTarget() |     public void testNullTarget() | ||||||
|     { |     { | ||||||
|         ServerConnectRequest.builder().target( null ).reason( ServerConnectEvent.Reason.JOIN_PROXY ).build(); |         assertThrows( NullPointerException.class, () -> ServerConnectRequest.builder().target( null ).reason( ServerConnectEvent.Reason.JOIN_PROXY ).build() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test(expected = NullPointerException.class) |     @Test | ||||||
|     public void testNullReason() |     public void testNullReason() | ||||||
|     { |     { | ||||||
|         ServerConnectRequest.builder().target( DUMMY_INFO ).reason( null ).build(); |         assertThrows( NullPointerException.class, () -> ServerConnectRequest.builder().target( DUMMY_INFO ).reason( null ).build() ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,63 +1,38 @@ | |||||||
| package net.md_5.bungee.util; | package net.md_5.bungee.util; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import io.netty.channel.unix.DomainSocketAddress; | import io.netty.channel.unix.DomainSocketAddress; | ||||||
| import java.net.InetSocketAddress; | import java.net.InetSocketAddress; | ||||||
| import java.net.SocketAddress; | import java.net.SocketAddress; | ||||||
| import java.util.Arrays; | import java.util.stream.Stream; | ||||||
| import java.util.Collection; |  | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import net.md_5.bungee.Util; | import net.md_5.bungee.Util; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.params.ParameterizedTest; | ||||||
| import org.junit.Test; | import org.junit.jupiter.params.provider.Arguments; | ||||||
| import org.junit.runner.RunWith; | import org.junit.jupiter.params.provider.MethodSource; | ||||||
| import org.junit.runners.Parameterized; |  | ||||||
| import org.junit.runners.Parameterized.Parameters; |  | ||||||
|  |  | ||||||
| @RequiredArgsConstructor | @RequiredArgsConstructor | ||||||
| @RunWith(Parameterized.class) |  | ||||||
| public class AddressParseTest | public class AddressParseTest | ||||||
| { | { | ||||||
|  |  | ||||||
|     @Parameters |     public static Stream<Arguments> data() | ||||||
|     public static Collection<Object[]> data() |  | ||||||
|     { |     { | ||||||
|         return Arrays.asList( new Object[][] |         return Stream.of( | ||||||
|         { |                 Arguments.of( "127.0.0.1", "127.0.0.1", Util.DEFAULT_PORT ), | ||||||
|             { |                 Arguments.of( "127.0.0.1:1337", "127.0.0.1", 1337 ), | ||||||
|                 "127.0.0.1", "127.0.0.1", Util.DEFAULT_PORT |                 Arguments.of( "[::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT ), | ||||||
|             }, |                 Arguments.of( "[0:0:0:0::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT ), | ||||||
|             { |                 Arguments.of( "[0:0:0:0:0:0:0:1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT ), | ||||||
|                 "127.0.0.1:1337", "127.0.0.1", 1337 |                 Arguments.of( "[::1]:1337", "0:0:0:0:0:0:0:1", 1337 ), | ||||||
|             }, |                 Arguments.of( "[0:0:0:0::1]:1337", "0:0:0:0:0:0:0:1", 1337 ), | ||||||
|             { |                 Arguments.of( "[0:0:0:0:0:0:0:1]:1337", "0:0:0:0:0:0:0:1", 1337 ), | ||||||
|                 "[::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT |                 Arguments.of( "unix:///var/run/bungee.sock", "/var/run/bungee.sock", -1 ) | ||||||
|             }, |         ); | ||||||
|             { |  | ||||||
|                 "[0:0:0:0::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "[0:0:0:0:0:0:0:1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "[::1]:1337", "0:0:0:0:0:0:0:1", 1337 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "[0:0:0:0::1]:1337", "0:0:0:0:0:0:0:1", 1337 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "[0:0:0:0:0:0:0:1]:1337", "0:0:0:0:0:0:0:1", 1337 |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 "unix:///var/run/bungee.sock", "/var/run/bungee.sock", -1 |  | ||||||
|             } |  | ||||||
|         } ); |  | ||||||
|     } |     } | ||||||
|     private final String line; |  | ||||||
|     private final String host; |  | ||||||
|     private final int port; |  | ||||||
|  |  | ||||||
|     @Test |     @ParameterizedTest | ||||||
|     public void test() |     @MethodSource("data") | ||||||
|  |     public void test(String line, String host, int port) | ||||||
|     { |     { | ||||||
|         SocketAddress parsed = Util.getAddr( line ); |         SocketAddress parsed = Util.getAddr( line ); | ||||||
|  |  | ||||||
| @@ -65,14 +40,14 @@ public class AddressParseTest | |||||||
|         { |         { | ||||||
|             InetSocketAddress tcp = (InetSocketAddress) parsed; |             InetSocketAddress tcp = (InetSocketAddress) parsed; | ||||||
|  |  | ||||||
|             Assert.assertEquals( host, tcp.getHostString() ); |             assertEquals( host, tcp.getHostString() ); | ||||||
|             Assert.assertEquals( port, tcp.getPort() ); |             assertEquals( port, tcp.getPort() ); | ||||||
|         } else if ( parsed instanceof DomainSocketAddress ) |         } else if ( parsed instanceof DomainSocketAddress ) | ||||||
|         { |         { | ||||||
|             DomainSocketAddress unix = (DomainSocketAddress) parsed; |             DomainSocketAddress unix = (DomainSocketAddress) parsed; | ||||||
|  |  | ||||||
|             Assert.assertEquals( host, unix.path() ); |             assertEquals( host, unix.path() ); | ||||||
|             Assert.assertEquals( -1, port ); |             assertEquals( -1, port ); | ||||||
|         } else |         } else | ||||||
|         { |         { | ||||||
|             throw new AssertionError( "Unknown socket " + parsed ); |             throw new AssertionError( "Unknown socket " + parsed ); | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package net.md_5.bungee.util; | package net.md_5.bungee.util; | ||||||
|  |  | ||||||
| import org.junit.Assert; | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import org.junit.Test; | import org.junit.jupiter.api.Test; | ||||||
|  |  | ||||||
| public class CaseInsensitiveTest | public class CaseInsensitiveTest | ||||||
| { | { | ||||||
| @@ -13,12 +13,12 @@ public class CaseInsensitiveTest | |||||||
|         CaseInsensitiveMap<Object> map = new CaseInsensitiveMap<>(); |         CaseInsensitiveMap<Object> map = new CaseInsensitiveMap<>(); | ||||||
|  |  | ||||||
|         map.put( "FOO", obj ); |         map.put( "FOO", obj ); | ||||||
|         Assert.assertTrue( map.contains( "foo" ) ); // Assert that contains is case insensitive |         assertTrue( map.contains( "foo" ) ); // Assert that contains is case insensitive | ||||||
|         Assert.assertTrue( map.entrySet().iterator().next().getKey().equals( "FOO" ) ); // Assert that case is preserved |         assertTrue( map.entrySet().iterator().next().getKey().equals( "FOO" ) ); // Assert that case is preserved | ||||||
|  |  | ||||||
|         // Assert that remove is case insensitive |         // Assert that remove is case insensitive | ||||||
|         map.remove( "FoO" ); |         map.remove( "FoO" ); | ||||||
|         Assert.assertFalse( map.contains( "foo" ) ); |         assertFalse( map.contains( "foo" ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -27,8 +27,8 @@ public class CaseInsensitiveTest | |||||||
|         CaseInsensitiveSet set = new CaseInsensitiveSet(); |         CaseInsensitiveSet set = new CaseInsensitiveSet(); | ||||||
|  |  | ||||||
|         set.add( "FOO" ); |         set.add( "FOO" ); | ||||||
|         Assert.assertTrue( set.contains( "foo" ) ); // Assert that contains is case insensitive |         assertTrue( set.contains( "foo" ) ); // Assert that contains is case insensitive | ||||||
|         set.remove( "FoO" ); |         set.remove( "FoO" ); | ||||||
|         Assert.assertFalse( set.contains( "foo" ) ); // Assert that remove is case insensitive |         assertFalse( set.contains( "foo" ) ); // Assert that remove is case insensitive | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| package net.md_5.bungee.util; | package net.md_5.bungee.util; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
| import net.md_5.bungee.Util; | import net.md_5.bungee.Util; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| public class UUIDTest | public class UUIDTest | ||||||
| { | { | ||||||
| @@ -13,7 +13,7 @@ public class UUIDTest | |||||||
|     { |     { | ||||||
|         UUID uuid = UUID.fromString( "af74a02d-19cb-445b-b07f-6866a861f783" ); |         UUID uuid = UUID.fromString( "af74a02d-19cb-445b-b07f-6866a861f783" ); | ||||||
|         UUID uuid1 = Util.getUUID( "af74a02d19cb445bb07f6866a861f783" ); |         UUID uuid1 = Util.getUUID( "af74a02d19cb445bb07f6866a861f783" ); | ||||||
|         Assert.assertEquals( uuid, uuid1 ); |         assertEquals( uuid, uuid1 ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -23,7 +23,7 @@ public class UUIDTest | |||||||
|         { |         { | ||||||
|             UUID expected = UUID.randomUUID(); |             UUID expected = UUID.randomUUID(); | ||||||
|             UUID actual = Util.getUUID( expected.toString().replace( "-", "" ) ); |             UUID actual = Util.getUUID( expected.toString().replace( "-", "" ) ); | ||||||
|             Assert.assertEquals( "Could not parse UUID " + expected, expected, actual ); |             assertEquals( expected, actual, "Could not parse UUID " + expected ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-bootstrap</artifactId> |     <artifactId>bungeecord-bootstrap</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Bootstrap</name> |     <name>BungeeCord-Bootstrap</name> | ||||||
| @@ -41,7 +41,7 @@ | |||||||
|             <plugin> |             <plugin> | ||||||
|                 <groupId>org.apache.maven.plugins</groupId> |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|                 <artifactId>maven-jar-plugin</artifactId> |                 <artifactId>maven-jar-plugin</artifactId> | ||||||
|                 <version>3.2.0</version> |                 <version>3.4.1</version> | ||||||
|                 <configuration> |                 <configuration> | ||||||
|                     <archive> |                     <archive> | ||||||
|                         <manifestEntries> |                         <manifestEntries> | ||||||
| @@ -55,7 +55,7 @@ | |||||||
|             <plugin> |             <plugin> | ||||||
|                 <groupId>org.apache.maven.plugins</groupId> |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|                 <artifactId>maven-shade-plugin</artifactId> |                 <artifactId>maven-shade-plugin</artifactId> | ||||||
|                 <version>3.2.3</version> |                 <version>3.5.3</version> | ||||||
|                 <executions> |                 <executions> | ||||||
|                     <execution> |                     <execution> | ||||||
|                         <phase>package</phase> |                         <phase>package</phase> | ||||||
| @@ -79,4 +79,34 @@ | |||||||
|             </plugin> |             </plugin> | ||||||
|         </plugins> |         </plugins> | ||||||
|     </build> |     </build> | ||||||
|  |  | ||||||
|  |     <profiles> | ||||||
|  |         <profile> | ||||||
|  |             <id>jdk-9-release</id> | ||||||
|  |             <activation> | ||||||
|  |                 <jdk>[9,)</jdk> | ||||||
|  |             </activation> | ||||||
|  |             <properties> | ||||||
|  |                 <maven.compiler.release>6</maven.compiler.release> | ||||||
|  |             </properties> | ||||||
|  |         </profile> | ||||||
|  |         <profile> | ||||||
|  |             <id>jdk-12-release</id> | ||||||
|  |             <activation> | ||||||
|  |                 <jdk>[12,)</jdk> | ||||||
|  |             </activation> | ||||||
|  |             <properties> | ||||||
|  |                 <maven.compiler.release>7</maven.compiler.release> | ||||||
|  |             </properties> | ||||||
|  |         </profile> | ||||||
|  |         <profile> | ||||||
|  |             <id>jdk-20-release</id> | ||||||
|  |             <activation> | ||||||
|  |                 <jdk>[20,)</jdk> | ||||||
|  |             </activation> | ||||||
|  |             <properties> | ||||||
|  |                 <maven.compiler.release>8</maven.compiler.release> | ||||||
|  |             </properties> | ||||||
|  |         </profile> | ||||||
|  |     </profiles> | ||||||
| </project> | </project> | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-chat</artifactId> |     <artifactId>bungeecord-chat</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Chat</name> |     <name>BungeeCord-Chat</name> | ||||||
| @@ -22,7 +22,7 @@ | |||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>com.google.code.gson</groupId> |             <groupId>com.google.code.gson</groupId> | ||||||
|             <artifactId>gson</artifactId> |             <artifactId>gson</artifactId> | ||||||
|             <version>2.8.0</version> |             <version>2.10.1</version> | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|     </dependencies> |     </dependencies> | ||||||
|   | |||||||
| @@ -244,7 +244,7 @@ public final class ChatColor | |||||||
|     public static ChatColor of(String string) |     public static ChatColor of(String string) | ||||||
|     { |     { | ||||||
|         Preconditions.checkArgument( string != null, "string cannot be null" ); |         Preconditions.checkArgument( string != null, "string cannot be null" ); | ||||||
|         if ( string.startsWith( "#" ) && string.length() == 7 ) |         if ( string.length() == 7 && string.charAt( 0 ) == '#' ) | ||||||
|         { |         { | ||||||
|             int rgb; |             int rgb; | ||||||
|             try |             try | ||||||
| @@ -300,7 +300,7 @@ public final class ChatColor | |||||||
|     @Deprecated |     @Deprecated | ||||||
|     public static ChatColor[] values() |     public static ChatColor[] values() | ||||||
|     { |     { | ||||||
|         return BY_CHAR.values().toArray( new ChatColor[ BY_CHAR.values().size() ] ); |         return BY_CHAR.values().toArray( new ChatColor[ 0 ] ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -20,38 +20,10 @@ public abstract class BaseComponent | |||||||
|     BaseComponent parent; |     BaseComponent parent; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * The color of this component and any child components (unless overridden) |      * The component's style. | ||||||
|      */ |      */ | ||||||
|     private ChatColor color; |     @Getter | ||||||
|     /** |     private ComponentStyle style = new ComponentStyle(); | ||||||
|      * The font of this component and any child components (unless overridden) |  | ||||||
|      */ |  | ||||||
|     private String font; |  | ||||||
|     /** |  | ||||||
|      * Whether this component and any child components (unless overridden) is |  | ||||||
|      * bold |  | ||||||
|      */ |  | ||||||
|     private Boolean bold; |  | ||||||
|     /** |  | ||||||
|      * Whether this component and any child components (unless overridden) is |  | ||||||
|      * italic |  | ||||||
|      */ |  | ||||||
|     private Boolean italic; |  | ||||||
|     /** |  | ||||||
|      * Whether this component and any child components (unless overridden) is |  | ||||||
|      * underlined |  | ||||||
|      */ |  | ||||||
|     private Boolean underlined; |  | ||||||
|     /** |  | ||||||
|      * Whether this component and any child components (unless overridden) is |  | ||||||
|      * strikethrough |  | ||||||
|      */ |  | ||||||
|     private Boolean strikethrough; |  | ||||||
|     /** |  | ||||||
|      * Whether this component and any child components (unless overridden) is |  | ||||||
|      * obfuscated |  | ||||||
|      */ |  | ||||||
|     private Boolean obfuscated; |  | ||||||
|     /** |     /** | ||||||
|      * The text to insert into the chat when this component (and child |      * The text to insert into the chat when this component (and child | ||||||
|      * components) are clicked while pressing the shift key |      * components) are clicked while pressing the shift key | ||||||
| @@ -78,6 +50,12 @@ public abstract class BaseComponent | |||||||
|     @Getter |     @Getter | ||||||
|     private HoverEvent hoverEvent; |     private HoverEvent hoverEvent; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Whether this component rejects previous formatting | ||||||
|  |      */ | ||||||
|  |     @Getter | ||||||
|  |     private transient boolean reset; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Default constructor. |      * Default constructor. | ||||||
|      * |      * | ||||||
| @@ -147,31 +125,31 @@ public abstract class BaseComponent | |||||||
|         } |         } | ||||||
|         if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL ) |         if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL ) | ||||||
|         { |         { | ||||||
|             if ( replace || color == null ) |             if ( replace || !style.hasColor() ) | ||||||
|             { |             { | ||||||
|                 setColor( component.getColorRaw() ); |                 setColor( component.getColorRaw() ); | ||||||
|             } |             } | ||||||
|             if ( replace || font == null ) |             if ( replace || !style.hasFont() ) | ||||||
|             { |             { | ||||||
|                 setFont( component.getFontRaw() ); |                 setFont( component.getFontRaw() ); | ||||||
|             } |             } | ||||||
|             if ( replace || bold == null ) |             if ( replace || style.isBoldRaw() == null ) | ||||||
|             { |             { | ||||||
|                 setBold( component.isBoldRaw() ); |                 setBold( component.isBoldRaw() ); | ||||||
|             } |             } | ||||||
|             if ( replace || italic == null ) |             if ( replace || style.isItalicRaw() == null ) | ||||||
|             { |             { | ||||||
|                 setItalic( component.isItalicRaw() ); |                 setItalic( component.isItalicRaw() ); | ||||||
|             } |             } | ||||||
|             if ( replace || underlined == null ) |             if ( replace || style.isUnderlinedRaw() == null ) | ||||||
|             { |             { | ||||||
|                 setUnderlined( component.isUnderlinedRaw() ); |                 setUnderlined( component.isUnderlinedRaw() ); | ||||||
|             } |             } | ||||||
|             if ( replace || strikethrough == null ) |             if ( replace || style.isStrikethroughRaw() == null ) | ||||||
|             { |             { | ||||||
|                 setStrikethrough( component.isStrikethroughRaw() ); |                 setStrikethrough( component.isStrikethroughRaw() ); | ||||||
|             } |             } | ||||||
|             if ( replace || obfuscated == null ) |             if ( replace || style.isObfuscatedRaw() == null ) | ||||||
|             { |             { | ||||||
|                 setObfuscated( component.isObfuscatedRaw() ); |                 setObfuscated( component.isObfuscatedRaw() ); | ||||||
|             } |             } | ||||||
| @@ -260,6 +238,32 @@ public abstract class BaseComponent | |||||||
|         return builder.toString(); |         return builder.toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the {@link ComponentStyle} for this component. | ||||||
|  |      * <p> | ||||||
|  |      * Unlike {@link #applyStyle(ComponentStyle)}, this method will overwrite | ||||||
|  |      * all style values on this component. | ||||||
|  |      * | ||||||
|  |      * @param style the style to set, or null to set all style values to default | ||||||
|  |      */ | ||||||
|  |     public void setStyle(ComponentStyle style) | ||||||
|  |     { | ||||||
|  |         this.style = ( style != null ) ? style.clone() : new ComponentStyle(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set this component's color. | ||||||
|  |      * <p> | ||||||
|  |      * <b>Warning: This should be a color, not formatting code (ie, | ||||||
|  |      * {@link ChatColor#color} should not be null).</b> | ||||||
|  |      * | ||||||
|  |      * @param color the component color, or null to use the default | ||||||
|  |      */ | ||||||
|  |     public void setColor(ChatColor color) | ||||||
|  |     { | ||||||
|  |         this.style.setColor( color ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the color of this component. This uses the parent's color if this |      * Returns the color of this component. This uses the parent's color if this | ||||||
|      * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE} |      * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE} | ||||||
| @@ -269,7 +273,7 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public ChatColor getColor() |     public ChatColor getColor() | ||||||
|     { |     { | ||||||
|         if ( color == null ) |         if ( !style.hasColor() ) | ||||||
|         { |         { | ||||||
|             if ( parent == null ) |             if ( parent == null ) | ||||||
|             { |             { | ||||||
| @@ -277,7 +281,7 @@ public abstract class BaseComponent | |||||||
|             } |             } | ||||||
|             return parent.getColor(); |             return parent.getColor(); | ||||||
|         } |         } | ||||||
|         return color; |         return style.getColor(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -288,7 +292,17 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public ChatColor getColorRaw() |     public ChatColor getColorRaw() | ||||||
|     { |     { | ||||||
|         return color; |         return style.getColor(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set this component's font. | ||||||
|  |      * | ||||||
|  |      * @param font the font to set, or null to use the default | ||||||
|  |      */ | ||||||
|  |     public void setFont(String font) | ||||||
|  |     { | ||||||
|  |         this.style.setFont( font ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -299,7 +313,7 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public String getFont() |     public String getFont() | ||||||
|     { |     { | ||||||
|         if ( font == null ) |         if ( !style.hasFont() ) | ||||||
|         { |         { | ||||||
|             if ( parent == null ) |             if ( parent == null ) | ||||||
|             { |             { | ||||||
| @@ -307,7 +321,7 @@ public abstract class BaseComponent | |||||||
|             } |             } | ||||||
|             return parent.getFont(); |             return parent.getFont(); | ||||||
|         } |         } | ||||||
|         return font; |         return style.getFont(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -318,7 +332,17 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public String getFontRaw() |     public String getFontRaw() | ||||||
|     { |     { | ||||||
|         return font; |         return style.getFont(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set whether or not this component is bold. | ||||||
|  |      * | ||||||
|  |      * @param bold the new bold state, or null to use the default | ||||||
|  |      */ | ||||||
|  |     public void setBold(Boolean bold) | ||||||
|  |     { | ||||||
|  |         this.style.setBold( bold ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -330,11 +354,11 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public boolean isBold() |     public boolean isBold() | ||||||
|     { |     { | ||||||
|         if ( bold == null ) |         if ( style.isBoldRaw() == null ) | ||||||
|         { |         { | ||||||
|             return parent != null && parent.isBold(); |             return parent != null && parent.isBold(); | ||||||
|         } |         } | ||||||
|         return bold; |         return style.isBold(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -345,7 +369,17 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public Boolean isBoldRaw() |     public Boolean isBoldRaw() | ||||||
|     { |     { | ||||||
|         return bold; |         return style.isBoldRaw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set whether or not this component is italic. | ||||||
|  |      * | ||||||
|  |      * @param italic the new italic state, or null to use the default | ||||||
|  |      */ | ||||||
|  |     public void setItalic(Boolean italic) | ||||||
|  |     { | ||||||
|  |         this.style.setItalic( italic ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -357,11 +391,11 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public boolean isItalic() |     public boolean isItalic() | ||||||
|     { |     { | ||||||
|         if ( italic == null ) |         if ( style.isItalicRaw() == null ) | ||||||
|         { |         { | ||||||
|             return parent != null && parent.isItalic(); |             return parent != null && parent.isItalic(); | ||||||
|         } |         } | ||||||
|         return italic; |         return style.isItalic(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -372,7 +406,17 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public Boolean isItalicRaw() |     public Boolean isItalicRaw() | ||||||
|     { |     { | ||||||
|         return italic; |         return style.isItalicRaw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set whether or not this component is underlined. | ||||||
|  |      * | ||||||
|  |      * @param underlined the new underlined state, or null to use the default | ||||||
|  |      */ | ||||||
|  |     public void setUnderlined(Boolean underlined) | ||||||
|  |     { | ||||||
|  |         this.style.setUnderlined( underlined ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -384,11 +428,11 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public boolean isUnderlined() |     public boolean isUnderlined() | ||||||
|     { |     { | ||||||
|         if ( underlined == null ) |         if ( style.isUnderlinedRaw() == null ) | ||||||
|         { |         { | ||||||
|             return parent != null && parent.isUnderlined(); |             return parent != null && parent.isUnderlined(); | ||||||
|         } |         } | ||||||
|         return underlined; |         return style.isUnderlined(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -399,7 +443,18 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public Boolean isUnderlinedRaw() |     public Boolean isUnderlinedRaw() | ||||||
|     { |     { | ||||||
|         return underlined; |         return style.isUnderlinedRaw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set whether or not this component is strikethrough. | ||||||
|  |      * | ||||||
|  |      * @param strikethrough the new strikethrough state, or null to use the | ||||||
|  |      * default | ||||||
|  |      */ | ||||||
|  |     public void setStrikethrough(Boolean strikethrough) | ||||||
|  |     { | ||||||
|  |         this.style.setStrikethrough( strikethrough ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -411,11 +466,11 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public boolean isStrikethrough() |     public boolean isStrikethrough() | ||||||
|     { |     { | ||||||
|         if ( strikethrough == null ) |         if ( style.isStrikethroughRaw() == null ) | ||||||
|         { |         { | ||||||
|             return parent != null && parent.isStrikethrough(); |             return parent != null && parent.isStrikethrough(); | ||||||
|         } |         } | ||||||
|         return strikethrough; |         return style.isStrikethrough(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -426,7 +481,17 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public Boolean isStrikethroughRaw() |     public Boolean isStrikethroughRaw() | ||||||
|     { |     { | ||||||
|         return strikethrough; |         return style.isStrikethroughRaw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set whether or not this component is obfuscated. | ||||||
|  |      * | ||||||
|  |      * @param obfuscated the new obfuscated state, or null to use the default | ||||||
|  |      */ | ||||||
|  |     public void setObfuscated(Boolean obfuscated) | ||||||
|  |     { | ||||||
|  |         this.style.setObfuscated( obfuscated ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -438,11 +503,11 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public boolean isObfuscated() |     public boolean isObfuscated() | ||||||
|     { |     { | ||||||
|         if ( obfuscated == null ) |         if ( style.isObfuscatedRaw() == null ) | ||||||
|         { |         { | ||||||
|             return parent != null && parent.isObfuscated(); |             return parent != null && parent.isObfuscated(); | ||||||
|         } |         } | ||||||
|         return obfuscated; |         return style.isObfuscated(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -453,7 +518,48 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public Boolean isObfuscatedRaw() |     public Boolean isObfuscatedRaw() | ||||||
|     { |     { | ||||||
|         return obfuscated; |         return style.isObfuscatedRaw(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Apply the style from the given {@link ComponentStyle} to this component. | ||||||
|  |      * <p> | ||||||
|  |      * Any style values that have been explicitly set in the style will be | ||||||
|  |      * applied to this component. If a value is not set in the style, it will | ||||||
|  |      * not override the style set in this component. | ||||||
|  |      * | ||||||
|  |      * @param style the style to apply | ||||||
|  |      */ | ||||||
|  |     public void applyStyle(ComponentStyle style) | ||||||
|  |     { | ||||||
|  |         if ( style.hasColor() ) | ||||||
|  |         { | ||||||
|  |             setColor( style.getColor() ); | ||||||
|  |         } | ||||||
|  |         if ( style.hasFont() ) | ||||||
|  |         { | ||||||
|  |             setFont( style.getFont() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isBoldRaw() != null ) | ||||||
|  |         { | ||||||
|  |             setBold( style.isBoldRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isItalicRaw() != null ) | ||||||
|  |         { | ||||||
|  |             setItalic( style.isItalicRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isUnderlinedRaw() != null ) | ||||||
|  |         { | ||||||
|  |             setUnderlined( style.isUnderlinedRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isStrikethroughRaw() != null ) | ||||||
|  |         { | ||||||
|  |             setStrikethrough( style.isStrikethroughRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isObfuscatedRaw() != null ) | ||||||
|  |         { | ||||||
|  |             setObfuscated( style.isObfuscatedRaw() ); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void setExtra(List<BaseComponent> components) |     public void setExtra(List<BaseComponent> components) | ||||||
| @@ -492,6 +598,16 @@ public abstract class BaseComponent | |||||||
|         extra.add( component ); |         extra.add( component ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether the component has any styling applied to it. | ||||||
|  |      * | ||||||
|  |      * @return Whether any styling is applied | ||||||
|  |      */ | ||||||
|  |     public boolean hasStyle() | ||||||
|  |     { | ||||||
|  |         return !style.isEmpty(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns whether the component has any formatting or events applied to it |      * Returns whether the component has any formatting or events applied to it | ||||||
|      * |      * | ||||||
| @@ -499,10 +615,8 @@ public abstract class BaseComponent | |||||||
|      */ |      */ | ||||||
|     public boolean hasFormatting() |     public boolean hasFormatting() | ||||||
|     { |     { | ||||||
|         return color != null || font != null || bold != null |         return hasStyle() || insertion != null | ||||||
|                 || italic != null || underlined != null |                 || hoverEvent != null || clickEvent != null; | ||||||
|                 || strikethrough != null || obfuscated != null |  | ||||||
|                 || insertion != null || hoverEvent != null || clickEvent != null; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ public final class ComponentBuilder | |||||||
|      */ |      */ | ||||||
|     public ComponentBuilder(ComponentBuilder original) |     public ComponentBuilder(ComponentBuilder original) | ||||||
|     { |     { | ||||||
|         this( original.parts.toArray( new BaseComponent[ original.parts.size() ] ) ); |         this( original.parts.toArray( new BaseComponent[ 0 ] ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -161,7 +161,7 @@ public final class ComponentBuilder | |||||||
|             previous = dummy; |             previous = dummy; | ||||||
|             dummy = null; |             dummy = null; | ||||||
|         } |         } | ||||||
|         if ( previous != null ) |         if ( previous != null && !component.isReset() ) | ||||||
|         { |         { | ||||||
|             component.copyFormatting( previous, retention, false ); |             component.copyFormatting( previous, retention, false ); | ||||||
|         } |         } | ||||||
| @@ -204,6 +204,33 @@ public final class ComponentBuilder | |||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Appends the {@link TranslationProvider} object to the builder and makes | ||||||
|  |      * the last element the current target for formatting. The components will | ||||||
|  |      * have all the formatting from previous part. | ||||||
|  |      * | ||||||
|  |      * @param translatable the translatable object to append | ||||||
|  |      * @return this ComponentBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentBuilder append(TranslationProvider translatable) | ||||||
|  |     { | ||||||
|  |         return append( translatable, FormatRetention.ALL ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Appends the {@link TranslationProvider} object to the builder and makes | ||||||
|  |      * the last element the current target for formatting. You can specify the | ||||||
|  |      * amount of formatting retained from previous part. | ||||||
|  |      * | ||||||
|  |      * @param translatable the translatable object to append | ||||||
|  |      * @param retention the formatting to retain | ||||||
|  |      * @return this ComponentBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentBuilder append(TranslationProvider translatable, FormatRetention retention) | ||||||
|  |     { | ||||||
|  |         return append( translatable.asTranslatableComponent(), retention ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Appends the text to the builder and makes it the current target for |      * Appends the text to the builder and makes it the current target for | ||||||
|      * formatting. The text will have all the formatting from previous part. |      * formatting. The text will have all the formatting from previous part. | ||||||
| @@ -396,6 +423,18 @@ public final class ComponentBuilder | |||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Applies the provided {@link ComponentStyle} to the current part. | ||||||
|  |      * | ||||||
|  |      * @param style the style to apply | ||||||
|  |      * @return this ComponentBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentBuilder style(ComponentStyle style) | ||||||
|  |     { | ||||||
|  |         getCurrentComponent().applyStyle( style ); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Sets the insertion text for the current part. |      * Sets the insertion text for the current part. | ||||||
|      * |      * | ||||||
| @@ -454,9 +493,32 @@ public final class ComponentBuilder | |||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the component built by this builder. If this builder is empty, an | ||||||
|  |      * empty text component will be returned. | ||||||
|  |      * | ||||||
|  |      * @return the component | ||||||
|  |      */ | ||||||
|  |     public BaseComponent build() | ||||||
|  |     { | ||||||
|  |         TextComponent base = new TextComponent(); | ||||||
|  |         if ( !parts.isEmpty() ) | ||||||
|  |         { | ||||||
|  |             List<BaseComponent> cloned = new ArrayList<>( parts ); | ||||||
|  |             cloned.replaceAll( BaseComponent::duplicate ); | ||||||
|  |             base.setExtra( cloned ); | ||||||
|  |         } | ||||||
|  |         return base; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the components needed to display the message created by this |      * Returns the components needed to display the message created by this | ||||||
|      * builder.git |      * builder.git | ||||||
|  |      * <p> | ||||||
|  |      * <strong>NOTE:</strong> {@link #build()} is preferred as it will | ||||||
|  |      * consolidate all components into a single BaseComponent with extra | ||||||
|  |      * contents as opposed to an array of components which is non-standard and | ||||||
|  |      * may result in unexpected behavior. | ||||||
|      * |      * | ||||||
|      * @return the created components |      * @return the created components | ||||||
|      */ |      */ | ||||||
|   | |||||||
							
								
								
									
										237
									
								
								chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								chat/src/main/java/net/md_5/bungee/api/chat/ComponentStyle.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,237 @@ | |||||||
|  | package net.md_5.bungee.api.chat; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.EqualsAndHashCode; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  | import lombok.Setter; | ||||||
|  | import net.md_5.bungee.api.ChatColor; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Represents a style that may be applied to a {@link BaseComponent}. | ||||||
|  |  */ | ||||||
|  | @Setter | ||||||
|  | @AllArgsConstructor | ||||||
|  | @NoArgsConstructor | ||||||
|  | @EqualsAndHashCode | ||||||
|  | public final class ComponentStyle implements Cloneable | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The color of this style. | ||||||
|  |      * <p> | ||||||
|  |      * <b>Warning: This should be a color, not formatting code (ie, | ||||||
|  |      * {@link ChatColor#color} should not be null).</b> | ||||||
|  |      */ | ||||||
|  |     private ChatColor color; | ||||||
|  |     /** | ||||||
|  |      * The font of this style. | ||||||
|  |      */ | ||||||
|  |     private String font; | ||||||
|  |     /** | ||||||
|  |      * Whether this style is bold. | ||||||
|  |      */ | ||||||
|  |     private Boolean bold; | ||||||
|  |     /** | ||||||
|  |      * Whether this style is italic. | ||||||
|  |      */ | ||||||
|  |     private Boolean italic; | ||||||
|  |     /** | ||||||
|  |      * Whether this style is underlined. | ||||||
|  |      */ | ||||||
|  |     private Boolean underlined; | ||||||
|  |     /** | ||||||
|  |      * Whether this style is strikethrough. | ||||||
|  |      */ | ||||||
|  |     private Boolean strikethrough; | ||||||
|  |     /** | ||||||
|  |      * Whether this style is obfuscated. | ||||||
|  |      */ | ||||||
|  |     private Boolean obfuscated; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the color of this style. May return null. | ||||||
|  |      * | ||||||
|  |      * @return the color of this style, or null if default color | ||||||
|  |      */ | ||||||
|  |     public ChatColor getColor() | ||||||
|  |     { | ||||||
|  |         return color; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether or not this style has a color set. | ||||||
|  |      * | ||||||
|  |      * @return whether a color is set | ||||||
|  |      */ | ||||||
|  |     public boolean hasColor() | ||||||
|  |     { | ||||||
|  |         return ( color != null ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns the font of this style. May return null. | ||||||
|  |      * | ||||||
|  |      * @return the font of this style, or null if default font | ||||||
|  |      */ | ||||||
|  |     public String getFont() | ||||||
|  |     { | ||||||
|  |         return font; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether or not this style has a font set. | ||||||
|  |      * | ||||||
|  |      * @return whether a font is set | ||||||
|  |      */ | ||||||
|  |     public boolean hasFont() | ||||||
|  |     { | ||||||
|  |         return ( font != null ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is bold. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is bold | ||||||
|  |      */ | ||||||
|  |     public boolean isBold() | ||||||
|  |     { | ||||||
|  |         return ( bold != null ) && bold.booleanValue(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is bold. May return null. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is bold, or null if not set | ||||||
|  |      */ | ||||||
|  |     public Boolean isBoldRaw() | ||||||
|  |     { | ||||||
|  |         return bold; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is italic. May return null. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is italic | ||||||
|  |      */ | ||||||
|  |     public boolean isItalic() | ||||||
|  |     { | ||||||
|  |         return ( italic != null ) && italic.booleanValue(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is italic. May return null. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is italic, or null if not set | ||||||
|  |      */ | ||||||
|  |     public Boolean isItalicRaw() | ||||||
|  |     { | ||||||
|  |         return italic; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is underlined. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is underlined | ||||||
|  |      */ | ||||||
|  |     public boolean isUnderlined() | ||||||
|  |     { | ||||||
|  |         return ( underlined != null ) && underlined.booleanValue(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is underlined. May return null. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is underlined, or null if not set | ||||||
|  |      */ | ||||||
|  |     public Boolean isUnderlinedRaw() | ||||||
|  |     { | ||||||
|  |         return underlined; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is strikethrough | ||||||
|  |      * | ||||||
|  |      * @return whether the style is strikethrough | ||||||
|  |      */ | ||||||
|  |     public boolean isStrikethrough() | ||||||
|  |     { | ||||||
|  |         return ( strikethrough != null ) && strikethrough.booleanValue(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is strikethrough. May return null. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is strikethrough, or null if not set | ||||||
|  |      */ | ||||||
|  |     public Boolean isStrikethroughRaw() | ||||||
|  |     { | ||||||
|  |         return strikethrough; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is obfuscated. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is obfuscated | ||||||
|  |      */ | ||||||
|  |     public boolean isObfuscated() | ||||||
|  |     { | ||||||
|  |         return ( obfuscated != null ) && obfuscated.booleanValue(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style is obfuscated. May return null. | ||||||
|  |      * | ||||||
|  |      * @return whether the style is obfuscated, or null if not set | ||||||
|  |      */ | ||||||
|  |     public Boolean isObfuscatedRaw() | ||||||
|  |     { | ||||||
|  |         return obfuscated; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns whether this style has no formatting explicitly set. | ||||||
|  |      * | ||||||
|  |      * @return true if no value is set, false if at least one is set | ||||||
|  |      */ | ||||||
|  |     public boolean isEmpty() | ||||||
|  |     { | ||||||
|  |         return color == null && font == null && bold == null | ||||||
|  |                 && italic == null && underlined == null | ||||||
|  |                 && strikethrough == null && obfuscated == null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ComponentStyle clone() | ||||||
|  |     { | ||||||
|  |         return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get a new {@link ComponentStyleBuilder}. | ||||||
|  |      * | ||||||
|  |      * @return the builder | ||||||
|  |      */ | ||||||
|  |     public static ComponentStyleBuilder builder() | ||||||
|  |     { | ||||||
|  |         return new ComponentStyleBuilder(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get a new {@link ComponentStyleBuilder} with values initialized to the | ||||||
|  |      * style values of the supplied {@link ComponentStyle}. | ||||||
|  |      * | ||||||
|  |      * @param other the component style whose values to copy into the builder | ||||||
|  |      * @return the builder | ||||||
|  |      */ | ||||||
|  |     public static ComponentStyleBuilder builder(ComponentStyle other) | ||||||
|  |     { | ||||||
|  |         return new ComponentStyleBuilder() | ||||||
|  |                 .color( other.color ) | ||||||
|  |                 .font( other.font ) | ||||||
|  |                 .bold( other.bold ) | ||||||
|  |                 .italic( other.italic ) | ||||||
|  |                 .underlined( other.underlined ) | ||||||
|  |                 .strikethrough( other.strikethrough ) | ||||||
|  |                 .obfuscated( other.obfuscated ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,126 @@ | |||||||
|  | package net.md_5.bungee.api.chat; | ||||||
|  |  | ||||||
|  | import net.md_5.bungee.api.ChatColor; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * <p> | ||||||
|  |  * ComponentStyleBuilder simplifies creating component styles by allowing the | ||||||
|  |  * use of a chainable builder. | ||||||
|  |  * </p> | ||||||
|  |  * <pre> | ||||||
|  |  * ComponentStyle style = ComponentStyle.builder() | ||||||
|  |  *     .color(ChatColor.RED) | ||||||
|  |  *     .font("custom:font") | ||||||
|  |  *     .bold(true).italic(true).create(); | ||||||
|  |  * | ||||||
|  |  * BaseComponent component = new ComponentBuilder("Hello world").style(style).create(); | ||||||
|  |  * // Or it can be used directly on a component | ||||||
|  |  * TextComponent text = new TextComponent("Hello world"); | ||||||
|  |  * text.applyStyle(style); | ||||||
|  |  * </pre> | ||||||
|  |  * | ||||||
|  |  * @see ComponentStyle#builder() | ||||||
|  |  * @see ComponentStyle#builder(ComponentStyle) | ||||||
|  |  */ | ||||||
|  | public final class ComponentStyleBuilder | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private ChatColor color; | ||||||
|  |     private String font; | ||||||
|  |     private Boolean bold, italic, underlined, strikethrough, obfuscated; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style color. | ||||||
|  |      * | ||||||
|  |      * @param color the color to set, or null to use the default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder color(ChatColor color) | ||||||
|  |     { | ||||||
|  |         this.color = color; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style font. | ||||||
|  |      * | ||||||
|  |      * @param font the font key to set, or null to use the default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder font(String font) | ||||||
|  |     { | ||||||
|  |         this.font = font; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style's bold property. | ||||||
|  |      * | ||||||
|  |      * @param bold the bold value to set, or null to use the default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder bold(Boolean bold) | ||||||
|  |     { | ||||||
|  |         this.bold = bold; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style's italic property. | ||||||
|  |      * | ||||||
|  |      * @param italic the italic value to set, or null to use the default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder italic(Boolean italic) | ||||||
|  |     { | ||||||
|  |         this.italic = italic; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style's underlined property. | ||||||
|  |      * | ||||||
|  |      * @param underlined the underlined value to set, or null to use the default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder underlined(Boolean underlined) | ||||||
|  |     { | ||||||
|  |         this.underlined = underlined; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style's strikethrough property. | ||||||
|  |      * | ||||||
|  |      * @param strikethrough the strikethrough value to set, or null to use the | ||||||
|  |      * default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder strikethrough(Boolean strikethrough) | ||||||
|  |     { | ||||||
|  |         this.strikethrough = strikethrough; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Set the style's obfuscated property. | ||||||
|  |      * | ||||||
|  |      * @param obfuscated the obfuscated value to set, or null to use the default | ||||||
|  |      * @return this ComponentStyleBuilder for chaining | ||||||
|  |      */ | ||||||
|  |     public ComponentStyleBuilder obfuscated(Boolean obfuscated) | ||||||
|  |     { | ||||||
|  |         this.obfuscated = obfuscated; | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Build the {@link ComponentStyle} using the values set in this builder. | ||||||
|  |      * | ||||||
|  |      * @return the created ComponentStyle | ||||||
|  |      */ | ||||||
|  |     public ComponentStyle build() | ||||||
|  |     { | ||||||
|  |         return new ComponentStyle( color, font, bold, italic, underlined, strikethrough, obfuscated ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -43,7 +43,7 @@ public final class ItemTag | |||||||
|         private final int level; |         private final int level; | ||||||
|         private final int id; |         private final int id; | ||||||
|     } |     } | ||||||
|     */ |      */ | ||||||
|  |  | ||||||
|     private ItemTag(String nbt) |     private ItemTag(String nbt) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -33,6 +33,13 @@ public final class SelectorComponent extends BaseComponent | |||||||
|      */ |      */ | ||||||
|     private String selector; |     private String selector; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * The separator of multiple selected entities. | ||||||
|  |      * <br> | ||||||
|  |      * The default is {@code {"color": "gray", "text": ", "}}. | ||||||
|  |      */ | ||||||
|  |     private BaseComponent separator; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Creates a selector component from the original to clone it. |      * Creates a selector component from the original to clone it. | ||||||
|      * |      * | ||||||
| @@ -42,6 +49,17 @@ public final class SelectorComponent extends BaseComponent | |||||||
|     { |     { | ||||||
|         super( original ); |         super( original ); | ||||||
|         setSelector( original.getSelector() ); |         setSelector( original.getSelector() ); | ||||||
|  |         setSeparator( original.getSeparator() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Creates a selector component from the selector | ||||||
|  |      * | ||||||
|  |      * @param selector the selector as a String | ||||||
|  |      */ | ||||||
|  |     public SelectorComponent(String selector) | ||||||
|  |     { | ||||||
|  |         setSelector( selector ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package net.md_5.bungee.api.chat; | |||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
|  | import java.util.function.Consumer; | ||||||
| import java.util.regex.Matcher; | import java.util.regex.Matcher; | ||||||
| import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| @@ -27,6 +28,41 @@ public final class TextComponent extends BaseComponent | |||||||
|      * @param message the text to convert |      * @param message the text to convert | ||||||
|      * @return the components needed to print the message to the client |      * @return the components needed to print the message to the client | ||||||
|      */ |      */ | ||||||
|  |     public static BaseComponent fromLegacy(String message) | ||||||
|  |     { | ||||||
|  |         return fromLegacy( message, ChatColor.WHITE ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Converts the old formatting system that used | ||||||
|  |      * {@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} into the new json based | ||||||
|  |      * system. | ||||||
|  |      * | ||||||
|  |      * @param message the text to convert | ||||||
|  |      * @param defaultColor color to use when no formatting is to be applied | ||||||
|  |      * (i.e. after ChatColor.RESET). | ||||||
|  |      * @return the components needed to print the message to the client | ||||||
|  |      */ | ||||||
|  |     public static BaseComponent fromLegacy(String message, ChatColor defaultColor) | ||||||
|  |     { | ||||||
|  |         ComponentBuilder componentBuilder = new ComponentBuilder(); | ||||||
|  |         populateComponentStructure( message, defaultColor, componentBuilder::append ); | ||||||
|  |         return componentBuilder.build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Converts the old formatting system that used | ||||||
|  |      * {@link net.md_5.bungee.api.ChatColor#COLOR_CHAR} into the new json based | ||||||
|  |      * system. | ||||||
|  |      * | ||||||
|  |      * @param message the text to convert | ||||||
|  |      * @return the components needed to print the message to the client | ||||||
|  |      * @deprecated {@link #fromLegacy(String)} is preferred as it will | ||||||
|  |      * consolidate all components into a single BaseComponent with extra | ||||||
|  |      * contents as opposed to an array of components which is non-standard and | ||||||
|  |      * may result in unexpected behavior. | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|     public static BaseComponent[] fromLegacyText(String message) |     public static BaseComponent[] fromLegacyText(String message) | ||||||
|     { |     { | ||||||
|         return fromLegacyText( message, ChatColor.WHITE ); |         return fromLegacyText( message, ChatColor.WHITE ); | ||||||
| @@ -41,10 +77,21 @@ public final class TextComponent extends BaseComponent | |||||||
|      * @param defaultColor color to use when no formatting is to be applied |      * @param defaultColor color to use when no formatting is to be applied | ||||||
|      * (i.e. after ChatColor.RESET). |      * (i.e. after ChatColor.RESET). | ||||||
|      * @return the components needed to print the message to the client |      * @return the components needed to print the message to the client | ||||||
|  |      * @deprecated {@link #fromLegacy(String, ChatColor)} is preferred as it | ||||||
|  |      * will consolidate all components into a single BaseComponent with extra | ||||||
|  |      * contents as opposed to an array of components which is non-standard and | ||||||
|  |      * may result in unexpected behavior. | ||||||
|      */ |      */ | ||||||
|  |     @Deprecated | ||||||
|     public static BaseComponent[] fromLegacyText(String message, ChatColor defaultColor) |     public static BaseComponent[] fromLegacyText(String message, ChatColor defaultColor) | ||||||
|     { |     { | ||||||
|         ArrayList<BaseComponent> components = new ArrayList<BaseComponent>(); |         ArrayList<BaseComponent> components = new ArrayList<>(); | ||||||
|  |         populateComponentStructure( message, defaultColor, components::add ); | ||||||
|  |         return components.toArray( new BaseComponent[ 0 ] ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void populateComponentStructure(String message, ChatColor defaultColor, Consumer<BaseComponent> appender) | ||||||
|  |     { | ||||||
|         StringBuilder builder = new StringBuilder(); |         StringBuilder builder = new StringBuilder(); | ||||||
|         TextComponent component = new TextComponent(); |         TextComponent component = new TextComponent(); | ||||||
|         Matcher matcher = url.matcher( message ); |         Matcher matcher = url.matcher( message ); | ||||||
| @@ -94,7 +141,7 @@ public final class TextComponent extends BaseComponent | |||||||
|                     component = new TextComponent( old ); |                     component = new TextComponent( old ); | ||||||
|                     old.setText( builder.toString() ); |                     old.setText( builder.toString() ); | ||||||
|                     builder = new StringBuilder(); |                     builder = new StringBuilder(); | ||||||
|                     components.add( old ); |                     appender.accept( old ); | ||||||
|                 } |                 } | ||||||
|                 if ( format == ChatColor.BOLD ) |                 if ( format == ChatColor.BOLD ) | ||||||
|                 { |                 { | ||||||
| @@ -111,15 +158,15 @@ public final class TextComponent extends BaseComponent | |||||||
|                 } else if ( format == ChatColor.MAGIC ) |                 } else if ( format == ChatColor.MAGIC ) | ||||||
|                 { |                 { | ||||||
|                     component.setObfuscated( true ); |                     component.setObfuscated( true ); | ||||||
|                 } else if ( format == ChatColor.RESET ) |  | ||||||
|                 { |  | ||||||
|                     format = defaultColor; |  | ||||||
|                     component = new TextComponent(); |  | ||||||
|                     component.setColor( format ); |  | ||||||
|                 } else |                 } else | ||||||
|                 { |                 { | ||||||
|  |                     if ( format == ChatColor.RESET ) | ||||||
|  |                     { | ||||||
|  |                         format = defaultColor; | ||||||
|  |                     } | ||||||
|                     component = new TextComponent(); |                     component = new TextComponent(); | ||||||
|                     component.setColor( format ); |                     component.setColor( format ); | ||||||
|  |                     component.setReset( true ); | ||||||
|                 } |                 } | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| @@ -137,7 +184,7 @@ public final class TextComponent extends BaseComponent | |||||||
|                     component = new TextComponent( old ); |                     component = new TextComponent( old ); | ||||||
|                     old.setText( builder.toString() ); |                     old.setText( builder.toString() ); | ||||||
|                     builder = new StringBuilder(); |                     builder = new StringBuilder(); | ||||||
|                     components.add( old ); |                     appender.accept( old ); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 TextComponent old = component; |                 TextComponent old = component; | ||||||
| @@ -146,7 +193,7 @@ public final class TextComponent extends BaseComponent | |||||||
|                 component.setText( urlString ); |                 component.setText( urlString ); | ||||||
|                 component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL, |                 component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL, | ||||||
|                         urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) ); |                         urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) ); | ||||||
|                 components.add( component ); |                 appender.accept( component ); | ||||||
|                 i += pos - i - 1; |                 i += pos - i - 1; | ||||||
|                 component = old; |                 component = old; | ||||||
|                 continue; |                 continue; | ||||||
| @@ -155,9 +202,29 @@ public final class TextComponent extends BaseComponent | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         component.setText( builder.toString() ); |         component.setText( builder.toString() ); | ||||||
|         components.add( component ); |         appender.accept( component ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         return components.toArray( new BaseComponent[ components.size() ] ); |     /** | ||||||
|  |      * Internal compatibility method to transform an array of components to a | ||||||
|  |      * single component. | ||||||
|  |      * | ||||||
|  |      * @param components array | ||||||
|  |      * @return single component | ||||||
|  |      */ | ||||||
|  |     public static BaseComponent fromArray(BaseComponent... components) | ||||||
|  |     { | ||||||
|  |         if ( components == null ) | ||||||
|  |         { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if ( components.length == 1 ) | ||||||
|  |         { | ||||||
|  |             return components[0]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return new TextComponent( components ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -230,6 +297,6 @@ public final class TextComponent extends BaseComponent | |||||||
|     @Override |     @Override | ||||||
|     public String toString() |     public String toString() | ||||||
|     { |     { | ||||||
|         return String.format( "TextComponent{text=%s, %s}", text, super.toString() ); |         return "TextComponent{text=" + text + ", " + super.toString() + '}'; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ import net.md_5.bungee.chat.TranslationRegistry; | |||||||
| public final class TranslatableComponent extends BaseComponent | public final class TranslatableComponent extends BaseComponent | ||||||
| { | { | ||||||
|  |  | ||||||
|     private final Pattern format = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" ); |     private static final Pattern FORMAT = Pattern.compile( "%(?:(\\d+)\\$)?([A-Za-z%]|$)" ); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * The key into the Minecraft locale files to use for the translation. The |      * The key into the Minecraft locale files to use for the translation. The | ||||||
| @@ -30,6 +30,10 @@ public final class TranslatableComponent extends BaseComponent | |||||||
|      * The components to substitute into the translation |      * The components to substitute into the translation | ||||||
|      */ |      */ | ||||||
|     private List<BaseComponent> with; |     private List<BaseComponent> with; | ||||||
|  |     /** | ||||||
|  |      * The fallback, if the translation is not found | ||||||
|  |      */ | ||||||
|  |     private String fallback; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Creates a translatable component from the original to clone it. |      * Creates a translatable component from the original to clone it. | ||||||
| @@ -40,10 +44,11 @@ public final class TranslatableComponent extends BaseComponent | |||||||
|     { |     { | ||||||
|         super( original ); |         super( original ); | ||||||
|         setTranslate( original.getTranslate() ); |         setTranslate( original.getTranslate() ); | ||||||
|  |         setFallback( original.getFallback() ); | ||||||
|  |  | ||||||
|         if ( original.getWith() != null ) |         if ( original.getWith() != null ) | ||||||
|         { |         { | ||||||
|             List<BaseComponent> temp = new ArrayList<BaseComponent>(); |             List<BaseComponent> temp = new ArrayList<>(); | ||||||
|             for ( BaseComponent baseComponent : original.getWith() ) |             for ( BaseComponent baseComponent : original.getWith() ) | ||||||
|             { |             { | ||||||
|                 temp.add( baseComponent.duplicate() ); |                 temp.add( baseComponent.duplicate() ); | ||||||
| @@ -82,6 +87,21 @@ public final class TranslatableComponent extends BaseComponent | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Creates a translatable component with the passed substitutions | ||||||
|  |      * | ||||||
|  |      * @param translatable the translatable object | ||||||
|  |      * @param with the {@link java.lang.String}s and | ||||||
|  |      * {@link net.md_5.bungee.api.chat.BaseComponent}s to use into the | ||||||
|  |      * translation | ||||||
|  |      * @see #translate | ||||||
|  |      * @see #setWith(java.util.List) | ||||||
|  |      */ | ||||||
|  |     public TranslatableComponent(TranslationProvider translatable, Object... with) | ||||||
|  |     { | ||||||
|  |         this( translatable.getTranslationKey(), with ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Creates a duplicate of this TranslatableComponent. |      * Creates a duplicate of this TranslatableComponent. | ||||||
|      * |      * | ||||||
| @@ -153,7 +173,12 @@ public final class TranslatableComponent extends BaseComponent | |||||||
|     { |     { | ||||||
|         String trans = TranslationRegistry.INSTANCE.translate( translate ); |         String trans = TranslationRegistry.INSTANCE.translate( translate ); | ||||||
|  |  | ||||||
|         Matcher matcher = format.matcher( trans ); |         if ( trans.equals( translate ) && fallback != null ) | ||||||
|  |         { | ||||||
|  |             trans = fallback; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Matcher matcher = FORMAT.matcher( trans ); | ||||||
|         int position = 0; |         int position = 0; | ||||||
|         int i = 0; |         int i = 0; | ||||||
|         while ( matcher.find( position ) ) |         while ( matcher.find( position ) ) | ||||||
|   | |||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | package net.md_5.bungee.api.chat; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * An object capable of being translated by the client in a | ||||||
|  |  * {@link TranslatableComponent}. | ||||||
|  |  */ | ||||||
|  | public interface TranslationProvider | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get the translation key. | ||||||
|  |      * | ||||||
|  |      * @return the translation key | ||||||
|  |      */ | ||||||
|  |     String getTranslationKey(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get this translatable object as a {@link TranslatableComponent}. | ||||||
|  |      * | ||||||
|  |      * @return the translatable component | ||||||
|  |      */ | ||||||
|  |     default TranslatableComponent asTranslatableComponent() | ||||||
|  |     { | ||||||
|  |         return asTranslatableComponent( (Object[]) null ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Get this translatable object as a {@link TranslatableComponent}. | ||||||
|  |      * | ||||||
|  |      * @param with the {@link String Strings} and | ||||||
|  |      * {@link BaseComponent BaseComponents} to use in the translation | ||||||
|  |      * @return the translatable component | ||||||
|  |      */ | ||||||
|  |     default TranslatableComponent asTranslatableComponent(Object... with) | ||||||
|  |     { | ||||||
|  |         return new TranslatableComponent( this, with ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -8,6 +8,7 @@ import com.google.gson.JsonParseException; | |||||||
| import com.google.gson.JsonSerializationContext; | import com.google.gson.JsonSerializationContext; | ||||||
| import com.google.gson.JsonSerializer; | import com.google.gson.JsonSerializer; | ||||||
| import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||||
|  | import java.util.UUID; | ||||||
| import net.md_5.bungee.api.chat.BaseComponent; | import net.md_5.bungee.api.chat.BaseComponent; | ||||||
|  |  | ||||||
| public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity> | public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity> | ||||||
| @@ -18,9 +19,19 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize | |||||||
|     { |     { | ||||||
|         JsonObject value = element.getAsJsonObject(); |         JsonObject value = element.getAsJsonObject(); | ||||||
|  |  | ||||||
|  |         String idString; | ||||||
|  |         JsonElement id = value.get( "id" ); | ||||||
|  |         if ( id.isJsonArray() ) | ||||||
|  |         { | ||||||
|  |             idString = parseUUID( context.deserialize( id, int[].class ) ).toString(); | ||||||
|  |         } else | ||||||
|  |         { | ||||||
|  |             idString = id.getAsString(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         return new Entity( |         return new Entity( | ||||||
|                 ( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null, |                 ( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null, | ||||||
|                 value.get( "id" ).getAsString(), |                 idString, | ||||||
|                 ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null |                 ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| @@ -37,4 +48,9 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize | |||||||
|         } |         } | ||||||
|         return object; |         return object; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private static UUID parseUUID(int[] array) | ||||||
|  |     { | ||||||
|  |         return new UUID( (long) array[0] << 32 | (long) array[1] & 0XFFFFFFFFL, (long) array[2] << 32 | (long) array[3] & 0XFFFFFFFFL ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,6 +23,15 @@ public class Text extends Content | |||||||
|         this.value = value; |         this.value = value; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public Text(BaseComponent value) | ||||||
|  |     { | ||||||
|  |         // For legacy serialization reasons, this has to be an array of components | ||||||
|  |         this( new BaseComponent[] | ||||||
|  |         { | ||||||
|  |             value | ||||||
|  |         } ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public Text(String value) |     public Text(String value) | ||||||
|     { |     { | ||||||
|         this.value = value; |         this.value = value; | ||||||
|   | |||||||
| @@ -4,16 +4,15 @@ import com.google.common.base.Preconditions; | |||||||
| import com.google.gson.JsonDeserializationContext; | import com.google.gson.JsonDeserializationContext; | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import com.google.gson.JsonParseException; |  | ||||||
| import com.google.gson.JsonSerializationContext; | import com.google.gson.JsonSerializationContext; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.IdentityHashMap; | import java.util.IdentityHashMap; | ||||||
| import java.util.Locale; | import java.util.Locale; | ||||||
| import net.md_5.bungee.api.ChatColor; |  | ||||||
| import net.md_5.bungee.api.chat.BaseComponent; | import net.md_5.bungee.api.chat.BaseComponent; | ||||||
| import net.md_5.bungee.api.chat.ClickEvent; | import net.md_5.bungee.api.chat.ClickEvent; | ||||||
|  | import net.md_5.bungee.api.chat.ComponentStyle; | ||||||
| import net.md_5.bungee.api.chat.HoverEvent; | import net.md_5.bungee.api.chat.HoverEvent; | ||||||
| import net.md_5.bungee.api.chat.hover.content.Content; | import net.md_5.bungee.api.chat.hover.content.Content; | ||||||
|  |  | ||||||
| @@ -22,82 +21,50 @@ public class BaseComponentSerializer | |||||||
|  |  | ||||||
|     protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) |     protected void deserialize(JsonObject object, BaseComponent component, JsonDeserializationContext context) | ||||||
|     { |     { | ||||||
|         if ( object.has( "color" ) ) |         component.applyStyle( context.deserialize( object, ComponentStyle.class ) ); | ||||||
|  |  | ||||||
|  |         JsonElement insertion = object.get( "insertion" ); | ||||||
|  |         if ( insertion != null ) | ||||||
|         { |         { | ||||||
|             component.setColor( ChatColor.of( object.get( "color" ).getAsString() ) ); |             component.setInsertion( insertion.getAsString() ); | ||||||
|         } |  | ||||||
|         if ( object.has( "font" ) ) |  | ||||||
|         { |  | ||||||
|             component.setFont( object.get( "font" ).getAsString() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "bold" ) ) |  | ||||||
|         { |  | ||||||
|             component.setBold( object.get( "bold" ).getAsBoolean() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "italic" ) ) |  | ||||||
|         { |  | ||||||
|             component.setItalic( object.get( "italic" ).getAsBoolean() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "underlined" ) ) |  | ||||||
|         { |  | ||||||
|             component.setUnderlined( object.get( "underlined" ).getAsBoolean() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "strikethrough" ) ) |  | ||||||
|         { |  | ||||||
|             component.setStrikethrough( object.get( "strikethrough" ).getAsBoolean() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "obfuscated" ) ) |  | ||||||
|         { |  | ||||||
|             component.setObfuscated( object.get( "obfuscated" ).getAsBoolean() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "insertion" ) ) |  | ||||||
|         { |  | ||||||
|             component.setInsertion( object.get( "insertion" ).getAsString() ); |  | ||||||
|         } |  | ||||||
|         if ( object.has( "extra" ) ) |  | ||||||
|         { |  | ||||||
|             component.setExtra( Arrays.asList( context.<BaseComponent[]>deserialize( object.get( "extra" ), BaseComponent[].class ) ) ); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         //Events |         //Events | ||||||
|         if ( object.has( "clickEvent" ) ) |         JsonObject clickEvent = object.getAsJsonObject( "clickEvent" ); | ||||||
|  |         if ( clickEvent != null ) | ||||||
|         { |         { | ||||||
|             JsonObject event = object.getAsJsonObject( "clickEvent" ); |  | ||||||
|             component.setClickEvent( new ClickEvent( |             component.setClickEvent( new ClickEvent( | ||||||
|                     ClickEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ), |                     ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ), | ||||||
|                     ( event.has( "value" ) ) ? event.get( "value" ).getAsString() : "" ) ); |                     ( clickEvent.has( "value" ) ) ? clickEvent.get( "value" ).getAsString() : "" ) ); | ||||||
|         } |         } | ||||||
|         if ( object.has( "hoverEvent" ) ) |         JsonObject hoverEventJson = object.getAsJsonObject( "hoverEvent" ); | ||||||
|  |         if ( hoverEventJson != null ) | ||||||
|         { |         { | ||||||
|             JsonObject event = object.getAsJsonObject( "hoverEvent" ); |  | ||||||
|             HoverEvent hoverEvent = null; |             HoverEvent hoverEvent = null; | ||||||
|             HoverEvent.Action action = HoverEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ); |             HoverEvent.Action action = HoverEvent.Action.valueOf( hoverEventJson.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ); | ||||||
|  |  | ||||||
|             for ( String type : Arrays.asList( "value", "contents" ) ) |             JsonElement value = hoverEventJson.get( "value" ); | ||||||
|  |             if ( value != null ) | ||||||
|             { |             { | ||||||
|                 if ( !event.has( type ) ) |  | ||||||
|                 { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 JsonElement contents = event.get( type ); |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|  |  | ||||||
|                     // Plugins previously had support to pass BaseComponent[] into any action. |                 // Plugins previously had support to pass BaseComponent[] into any action. | ||||||
|                     // If the GSON is possible to be parsed as BaseComponent, attempt to parse as so. |                 // If the GSON is possible to be parsed as BaseComponent, attempt to parse as so. | ||||||
|                     BaseComponent[] components; |                 BaseComponent[] components; | ||||||
|                     if ( contents.isJsonArray() ) |                 if ( value.isJsonArray() ) | ||||||
|  |                 { | ||||||
|  |                     components = context.deserialize( value, BaseComponent[].class ); | ||||||
|  |                 } else | ||||||
|  |                 { | ||||||
|  |                     components = new BaseComponent[] | ||||||
|                     { |                     { | ||||||
|                         components = context.deserialize( contents, BaseComponent[].class ); |                         context.deserialize( value, BaseComponent.class ) | ||||||
|                     } else |                     }; | ||||||
|                     { |                 } | ||||||
|                         components = new BaseComponent[] |                 hoverEvent = new HoverEvent( action, components ); | ||||||
|                         { |             } else | ||||||
|                                 context.deserialize( contents, BaseComponent.class ) |             { | ||||||
|                         }; |                 JsonElement contents = hoverEventJson.get( "contents" ); | ||||||
|                     } |                 if ( contents != null ) | ||||||
|                     hoverEvent = new HoverEvent( action, components ); |  | ||||||
|                 } catch ( JsonParseException ex ) |  | ||||||
|                 { |                 { | ||||||
|                     Content[] list; |                     Content[] list; | ||||||
|                     if ( contents.isJsonArray() ) |                     if ( contents.isJsonArray() ) | ||||||
| @@ -112,15 +79,19 @@ public class BaseComponentSerializer | |||||||
|                     } |                     } | ||||||
|                     hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) ); |                     hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) ); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // stop the loop as soon as either one is found |  | ||||||
|                 break; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ( hoverEvent != null ) |             if ( hoverEvent != null ) | ||||||
|             { |             { | ||||||
|                 component.setHoverEvent( hoverEvent ); |                 component.setHoverEvent( hoverEvent ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         JsonElement extra = object.get( "extra" ); | ||||||
|  |         if ( extra != null ) | ||||||
|  |         { | ||||||
|  |             component.setExtra( Arrays.asList( context.deserialize( extra, BaseComponent[].class ) ) ); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) |     protected void serialize(JsonObject object, BaseComponent component, JsonSerializationContext context) | ||||||
| @@ -135,44 +106,14 @@ public class BaseComponentSerializer | |||||||
|         { |         { | ||||||
|             Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); |             Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); | ||||||
|             ComponentSerializer.serializedComponents.get().add( component ); |             ComponentSerializer.serializedComponents.get().add( component ); | ||||||
|             if ( component.getColorRaw() != null ) |  | ||||||
|             { |             ComponentStyleSerializer.serializeTo( component.getStyle(), object ); | ||||||
|                 object.addProperty( "color", component.getColorRaw().getName() ); |  | ||||||
|             } |  | ||||||
|             if ( component.getFontRaw() != null ) |  | ||||||
|             { |  | ||||||
|                 object.addProperty( "font", component.getFontRaw() ); |  | ||||||
|             } |  | ||||||
|             if ( component.isBoldRaw() != null ) |  | ||||||
|             { |  | ||||||
|                 object.addProperty( "bold", component.isBoldRaw() ); |  | ||||||
|             } |  | ||||||
|             if ( component.isItalicRaw() != null ) |  | ||||||
|             { |  | ||||||
|                 object.addProperty( "italic", component.isItalicRaw() ); |  | ||||||
|             } |  | ||||||
|             if ( component.isUnderlinedRaw() != null ) |  | ||||||
|             { |  | ||||||
|                 object.addProperty( "underlined", component.isUnderlinedRaw() ); |  | ||||||
|             } |  | ||||||
|             if ( component.isStrikethroughRaw() != null ) |  | ||||||
|             { |  | ||||||
|                 object.addProperty( "strikethrough", component.isStrikethroughRaw() ); |  | ||||||
|             } |  | ||||||
|             if ( component.isObfuscatedRaw() != null ) |  | ||||||
|             { |  | ||||||
|                 object.addProperty( "obfuscated", component.isObfuscatedRaw() ); |  | ||||||
|             } |  | ||||||
|             if ( component.getInsertion() != null ) |             if ( component.getInsertion() != null ) | ||||||
|             { |             { | ||||||
|                 object.addProperty( "insertion", component.getInsertion() ); |                 object.addProperty( "insertion", component.getInsertion() ); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if ( component.getExtra() != null ) |  | ||||||
|             { |  | ||||||
|                 object.add( "extra", context.serialize( component.getExtra() ) ); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             //Events |             //Events | ||||||
|             if ( component.getClickEvent() != null ) |             if ( component.getClickEvent() != null ) | ||||||
|             { |             { | ||||||
| @@ -195,6 +136,11 @@ public class BaseComponentSerializer | |||||||
|                 } |                 } | ||||||
|                 object.add( "hoverEvent", hoverEvent ); |                 object.add( "hoverEvent", hoverEvent ); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             if ( component.getExtra() != null ) | ||||||
|  |             { | ||||||
|  |                 object.add( "extra", context.serialize( component.getExtra() ) ); | ||||||
|  |             } | ||||||
|         } finally |         } finally | ||||||
|         { |         { | ||||||
|             ComponentSerializer.serializedComponents.get().remove( component ); |             ComponentSerializer.serializedComponents.get().remove( component ); | ||||||
|   | |||||||
| @@ -2,15 +2,18 @@ package net.md_5.bungee.chat; | |||||||
|  |  | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||||
|  | import com.google.gson.JsonArray; | ||||||
| import com.google.gson.JsonDeserializationContext; | import com.google.gson.JsonDeserializationContext; | ||||||
| import com.google.gson.JsonDeserializer; | import com.google.gson.JsonDeserializer; | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import com.google.gson.JsonParseException; | import com.google.gson.JsonParseException; | ||||||
| import com.google.gson.JsonParser; | import com.google.gson.JsonParser; | ||||||
|  | import com.google.gson.JsonPrimitive; | ||||||
| import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import net.md_5.bungee.api.chat.BaseComponent; | import net.md_5.bungee.api.chat.BaseComponent; | ||||||
|  | import net.md_5.bungee.api.chat.ComponentStyle; | ||||||
| import net.md_5.bungee.api.chat.ItemTag; | import net.md_5.bungee.api.chat.ItemTag; | ||||||
| import net.md_5.bungee.api.chat.KeybindComponent; | import net.md_5.bungee.api.chat.KeybindComponent; | ||||||
| import net.md_5.bungee.api.chat.ScoreComponent; | import net.md_5.bungee.api.chat.ScoreComponent; | ||||||
| @@ -27,7 +30,6 @@ import net.md_5.bungee.api.chat.hover.content.TextSerializer; | |||||||
| public class ComponentSerializer implements JsonDeserializer<BaseComponent> | public class ComponentSerializer implements JsonDeserializer<BaseComponent> | ||||||
| { | { | ||||||
|  |  | ||||||
|     private static final JsonParser JSON_PARSER = new JsonParser(); |  | ||||||
|     private static final Gson gson = new GsonBuilder(). |     private static final Gson gson = new GsonBuilder(). | ||||||
|             registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). |             registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). | ||||||
|             registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). |             registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). | ||||||
| @@ -35,6 +37,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent> | |||||||
|             registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ). |             registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ). | ||||||
|             registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ). |             registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ). | ||||||
|             registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ). |             registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ). | ||||||
|  |             registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ). | ||||||
|             registerTypeAdapter( Entity.class, new EntitySerializer() ). |             registerTypeAdapter( Entity.class, new EntitySerializer() ). | ||||||
|             registerTypeAdapter( Text.class, new TextSerializer() ). |             registerTypeAdapter( Text.class, new TextSerializer() ). | ||||||
|             registerTypeAdapter( Item.class, new ItemSerializer() ). |             registerTypeAdapter( Item.class, new ItemSerializer() ). | ||||||
| @@ -43,9 +46,26 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent> | |||||||
|  |  | ||||||
|     public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>(); |     public static final ThreadLocal<Set<BaseComponent>> serializedComponents = new ThreadLocal<Set<BaseComponent>>(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Parse a JSON-compliant String as an array of base components. The input | ||||||
|  |      * can be one of either an array of components, or a single component | ||||||
|  |      * object. If the input is an array, each component will be parsed | ||||||
|  |      * individually and returned in the order that they were parsed. If the | ||||||
|  |      * input is a single component object, a single-valued array with the | ||||||
|  |      * component will be returned. | ||||||
|  |      * <p> | ||||||
|  |      * <strong>NOTE:</strong> {@link #deserialize(String)} is preferred as it | ||||||
|  |      * will parse only one component as opposed to an array of components which | ||||||
|  |      * is non- standard behavior. This method is still appropriate for parsing | ||||||
|  |      * multiple components at once, although such use case is rarely (if at all) | ||||||
|  |      * exhibited in vanilla Minecraft. | ||||||
|  |      * | ||||||
|  |      * @param json the component json to parse | ||||||
|  |      * @return an array of all parsed components | ||||||
|  |      */ | ||||||
|     public static BaseComponent[] parse(String json) |     public static BaseComponent[] parse(String json) | ||||||
|     { |     { | ||||||
|         JsonElement jsonElement = JSON_PARSER.parse( json ); |         JsonElement jsonElement = JsonParser.parseString( json ); | ||||||
|  |  | ||||||
|         if ( jsonElement.isJsonArray() ) |         if ( jsonElement.isJsonArray() ) | ||||||
|         { |         { | ||||||
| @@ -59,6 +79,85 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent> | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Deserialize a JSON-compliant String as a single component. | ||||||
|  |      * | ||||||
|  |      * @param json the component json to parse | ||||||
|  |      * @return the deserialized component | ||||||
|  |      * @throws IllegalArgumentException if anything other than a valid JSON | ||||||
|  |      * component string is passed as input | ||||||
|  |      */ | ||||||
|  |     public static BaseComponent deserialize(String json) | ||||||
|  |     { | ||||||
|  |         JsonElement jsonElement = JsonParser.parseString( json ); | ||||||
|  |  | ||||||
|  |         return deserialize( jsonElement ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Deserialize a JSON element as a single component. | ||||||
|  |      * | ||||||
|  |      * @param jsonElement the component json to parse | ||||||
|  |      * @return the deserialized component | ||||||
|  |      * @throws IllegalArgumentException if anything other than a valid JSON | ||||||
|  |      * component is passed as input | ||||||
|  |      */ | ||||||
|  |     public static BaseComponent deserialize(JsonElement jsonElement) | ||||||
|  |     { | ||||||
|  |         if ( jsonElement instanceof JsonPrimitive ) | ||||||
|  |         { | ||||||
|  |             JsonPrimitive primitive = (JsonPrimitive) jsonElement; | ||||||
|  |             if ( primitive.isString() ) | ||||||
|  |             { | ||||||
|  |                 return new TextComponent( primitive.getAsString() ); | ||||||
|  |             } | ||||||
|  |         } else if ( jsonElement instanceof JsonArray ) | ||||||
|  |         { | ||||||
|  |             BaseComponent[] array = gson.fromJson( jsonElement, BaseComponent[].class ); | ||||||
|  |             return TextComponent.fromArray( array ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return gson.fromJson( jsonElement, BaseComponent.class ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Deserialize a JSON-compliant String as a component style. | ||||||
|  |      * | ||||||
|  |      * @param json the component style json to parse | ||||||
|  |      * @return the deserialized component style | ||||||
|  |      * @throws IllegalArgumentException if anything other than a valid JSON | ||||||
|  |      * component style string is passed as input | ||||||
|  |      */ | ||||||
|  |     public static ComponentStyle deserializeStyle(String json) | ||||||
|  |     { | ||||||
|  |         JsonElement jsonElement = JsonParser.parseString( json ); | ||||||
|  |  | ||||||
|  |         return deserializeStyle( jsonElement ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Deserialize a JSON element as a component style. | ||||||
|  |      * | ||||||
|  |      * @param jsonElement the component style json to parse | ||||||
|  |      * @return the deserialized component style | ||||||
|  |      * @throws IllegalArgumentException if anything other than a valid JSON | ||||||
|  |      * component style is passed as input | ||||||
|  |      */ | ||||||
|  |     public static ComponentStyle deserializeStyle(JsonElement jsonElement) | ||||||
|  |     { | ||||||
|  |         return gson.fromJson( jsonElement, ComponentStyle.class ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static JsonElement toJson(BaseComponent component) | ||||||
|  |     { | ||||||
|  |         return gson.toJsonTree( component ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static JsonElement toJson(ComponentStyle style) | ||||||
|  |     { | ||||||
|  |         return gson.toJsonTree( style ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public static String toString(Object object) |     public static String toString(Object object) | ||||||
|     { |     { | ||||||
|         return gson.toJson( object ); |         return gson.toJson( object ); | ||||||
| @@ -80,6 +179,11 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent> | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public static String toString(ComponentStyle style) | ||||||
|  |     { | ||||||
|  |         return gson.toJson( style ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException |     public BaseComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -0,0 +1,120 @@ | |||||||
|  | package net.md_5.bungee.chat; | ||||||
|  |  | ||||||
|  | import com.google.gson.JsonDeserializationContext; | ||||||
|  | import com.google.gson.JsonDeserializer; | ||||||
|  | import com.google.gson.JsonElement; | ||||||
|  | import com.google.gson.JsonObject; | ||||||
|  | import com.google.gson.JsonParseException; | ||||||
|  | import com.google.gson.JsonPrimitive; | ||||||
|  | import com.google.gson.JsonSerializationContext; | ||||||
|  | import com.google.gson.JsonSerializer; | ||||||
|  | import java.lang.reflect.Type; | ||||||
|  | import java.util.Map; | ||||||
|  | import net.md_5.bungee.api.ChatColor; | ||||||
|  | import net.md_5.bungee.api.chat.ComponentStyle; | ||||||
|  | import net.md_5.bungee.api.chat.ComponentStyleBuilder; | ||||||
|  |  | ||||||
|  | public class ComponentStyleSerializer implements JsonSerializer<ComponentStyle>, JsonDeserializer<ComponentStyle> | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private static boolean getAsBoolean(JsonElement el) | ||||||
|  |     { | ||||||
|  |         if ( el.isJsonPrimitive() ) | ||||||
|  |         { | ||||||
|  |             JsonPrimitive primitive = (JsonPrimitive) el; | ||||||
|  |  | ||||||
|  |             if ( primitive.isBoolean() ) | ||||||
|  |             { | ||||||
|  |                 return primitive.getAsBoolean(); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if ( primitive.isNumber() ) | ||||||
|  |             { | ||||||
|  |                 Number number = primitive.getAsNumber(); | ||||||
|  |                 if ( number instanceof Byte ) | ||||||
|  |                 { | ||||||
|  |                     return number.byteValue() != 0; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     static void serializeTo(ComponentStyle style, JsonObject object) | ||||||
|  |     { | ||||||
|  |         if ( style.isBoldRaw() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "bold", style.isBoldRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isItalicRaw() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "italic", style.isItalicRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isUnderlinedRaw() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "underlined", style.isUnderlinedRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isStrikethroughRaw() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "strikethrough", style.isStrikethroughRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.isObfuscatedRaw() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "obfuscated", style.isObfuscatedRaw() ); | ||||||
|  |         } | ||||||
|  |         if ( style.hasColor() && style.getColor().getColor() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "color", style.getColor().getName() ); | ||||||
|  |         } | ||||||
|  |         if ( style.hasFont() ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "font", style.getFont() ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public ComponentStyle deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException | ||||||
|  |     { | ||||||
|  |         ComponentStyleBuilder builder = ComponentStyle.builder(); | ||||||
|  |         JsonObject object = json.getAsJsonObject(); | ||||||
|  |         for ( Map.Entry<String, JsonElement> entry : object.entrySet() ) | ||||||
|  |         { | ||||||
|  |             String name = entry.getKey(); | ||||||
|  |             JsonElement value = entry.getValue(); | ||||||
|  |             switch ( name ) | ||||||
|  |             { | ||||||
|  |                 case "bold": | ||||||
|  |                     builder.bold( getAsBoolean( value ) ); | ||||||
|  |                     break; | ||||||
|  |                 case "italic": | ||||||
|  |                     builder.italic( getAsBoolean( value ) ); | ||||||
|  |                     break; | ||||||
|  |                 case "underlined": | ||||||
|  |                     builder.underlined( getAsBoolean( value ) ); | ||||||
|  |                     break; | ||||||
|  |                 case "strikethrough": | ||||||
|  |                     builder.strikethrough( getAsBoolean( value ) ); | ||||||
|  |                     break; | ||||||
|  |                 case "obfuscated": | ||||||
|  |                     builder.obfuscated( getAsBoolean( value ) ); | ||||||
|  |                     break; | ||||||
|  |                 case "color": | ||||||
|  |                     builder.color( ChatColor.of( value.getAsString() ) ); | ||||||
|  |                     break; | ||||||
|  |                 case "font": | ||||||
|  |                     builder.font( value.getAsString() ); | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return builder.build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public JsonElement serialize(ComponentStyle src, Type typeOfSrc, JsonSerializationContext context) | ||||||
|  |     { | ||||||
|  |         JsonObject object = new JsonObject(); | ||||||
|  |         serializeTo( src, object ); | ||||||
|  |         return object; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -17,13 +17,14 @@ public class KeybindComponentSerializer extends BaseComponentSerializer implemen | |||||||
|     public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException |     public KeybindComponent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException | ||||||
|     { |     { | ||||||
|         JsonObject object = json.getAsJsonObject(); |         JsonObject object = json.getAsJsonObject(); | ||||||
|         if ( !object.has( "keybind" ) ) |         JsonElement keybind = object.get( "keybind" ); | ||||||
|  |         if ( keybind == null ) | ||||||
|         { |         { | ||||||
|             throw new JsonParseException( "Could not parse JSON: missing 'keybind' property" ); |             throw new JsonParseException( "Could not parse JSON: missing 'keybind' property" ); | ||||||
|         } |         } | ||||||
|         KeybindComponent component = new KeybindComponent(); |         KeybindComponent component = new KeybindComponent(); | ||||||
|         deserialize( object, component, context ); |         deserialize( object, component, context ); | ||||||
|         component.setKeybind( object.get( "keybind" ).getAsString() ); |         component.setKeybind( keybind.getAsString() ); | ||||||
|         return component; |         return component; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,22 +17,29 @@ public class ScoreComponentSerializer extends BaseComponentSerializer implements | |||||||
|     public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException |     public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException | ||||||
|     { |     { | ||||||
|         JsonObject json = element.getAsJsonObject(); |         JsonObject json = element.getAsJsonObject(); | ||||||
|         if ( !json.has( "score" ) ) |         JsonObject score = json.getAsJsonObject( "score" ); | ||||||
|  |         if ( score == null ) | ||||||
|         { |         { | ||||||
|             throw new JsonParseException( "Could not parse JSON: missing 'score' property" ); |             throw new JsonParseException( "Could not parse JSON: missing 'score' property" ); | ||||||
|         } |         } | ||||||
|         JsonObject score = json.get( "score" ).getAsJsonObject(); |         JsonElement nameJson = score.get( "name" ); | ||||||
|         if ( !score.has( "name" ) || !score.has( "objective" ) ) |         if ( nameJson == null ) | ||||||
|  |         { | ||||||
|  |             throw new JsonParseException( "A score component needs at least a name (and an objective)" ); | ||||||
|  |         } | ||||||
|  |         JsonElement objectiveJson = score.get( "objective" ); | ||||||
|  |         if ( objectiveJson == null ) | ||||||
|         { |         { | ||||||
|             throw new JsonParseException( "A score component needs at least a name and an objective" ); |             throw new JsonParseException( "A score component needs at least a name and an objective" ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         String name = score.get( "name" ).getAsString(); |         String name = nameJson.getAsString(); | ||||||
|         String objective = score.get( "objective" ).getAsString(); |         String objective = objectiveJson.getAsString(); | ||||||
|         ScoreComponent component = new ScoreComponent( name, objective ); |         ScoreComponent component = new ScoreComponent( name, objective ); | ||||||
|         if ( score.has( "value" ) && !score.get( "value" ).getAsString().isEmpty() ) |         JsonElement value = score.get( "value" ); | ||||||
|  |         if ( value != null && !value.getAsString().isEmpty() ) | ||||||
|         { |         { | ||||||
|             component.setValue( score.get( "value" ).getAsString() ); |             component.setValue( value.getAsString() ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         deserialize( json, component, context ); |         deserialize( json, component, context ); | ||||||
|   | |||||||
| @@ -17,11 +17,19 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme | |||||||
|     public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException |     public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException | ||||||
|     { |     { | ||||||
|         JsonObject object = element.getAsJsonObject(); |         JsonObject object = element.getAsJsonObject(); | ||||||
|         if ( !object.has( "selector" ) ) |         JsonElement selector = object.get( "selector" ); | ||||||
|  |         if ( selector == null ) | ||||||
|         { |         { | ||||||
|             throw new JsonParseException( "Could not parse JSON: missing 'selector' property" ); |             throw new JsonParseException( "Could not parse JSON: missing 'selector' property" ); | ||||||
|         } |         } | ||||||
|         SelectorComponent component = new SelectorComponent( object.get( "selector" ).getAsString() ); |         SelectorComponent component = new SelectorComponent( selector.getAsString() ); | ||||||
|  |  | ||||||
|  |         JsonElement separator = object.get( "separator" ); | ||||||
|  |         if ( separator != null ) | ||||||
|  |         { | ||||||
|  |             component.setSeparator( ComponentSerializer.deserialize( separator.getAsString() ) ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         deserialize( object, component, context ); |         deserialize( object, component, context ); | ||||||
|         return component; |         return component; | ||||||
|     } |     } | ||||||
| @@ -32,6 +40,11 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme | |||||||
|         JsonObject object = new JsonObject(); |         JsonObject object = new JsonObject(); | ||||||
|         serialize( object, component, context ); |         serialize( object, component, context ); | ||||||
|         object.addProperty( "selector", component.getSelector() ); |         object.addProperty( "selector", component.getSelector() ); | ||||||
|  |  | ||||||
|  |         if ( component.getSeparator() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "separator", ComponentSerializer.toString( component.getSeparator() ) ); | ||||||
|  |         } | ||||||
|         return object; |         return object; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,8 +8,6 @@ import com.google.gson.JsonParseException; | |||||||
| import com.google.gson.JsonSerializationContext; | import com.google.gson.JsonSerializationContext; | ||||||
| import com.google.gson.JsonSerializer; | import com.google.gson.JsonSerializer; | ||||||
| import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||||
| import java.util.List; |  | ||||||
| import net.md_5.bungee.api.chat.BaseComponent; |  | ||||||
| import net.md_5.bungee.api.chat.TextComponent; | import net.md_5.bungee.api.chat.TextComponent; | ||||||
|  |  | ||||||
| public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent> | public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent> | ||||||
| @@ -20,11 +18,11 @@ public class TextComponentSerializer extends BaseComponentSerializer implements | |||||||
|     { |     { | ||||||
|         TextComponent component = new TextComponent(); |         TextComponent component = new TextComponent(); | ||||||
|         JsonObject object = json.getAsJsonObject(); |         JsonObject object = json.getAsJsonObject(); | ||||||
|         if ( !object.has( "text" ) ) |         JsonElement text = object.get( "text" ); | ||||||
|  |         if ( text != null ) | ||||||
|         { |         { | ||||||
|             throw new JsonParseException( "Could not parse JSON: missing 'text' property" ); |             component.setText( text.getAsString() ); | ||||||
|         } |         } | ||||||
|         component.setText( object.get( "text" ).getAsString() ); |  | ||||||
|         deserialize( object, component, context ); |         deserialize( object, component, context ); | ||||||
|         return component; |         return component; | ||||||
|     } |     } | ||||||
| @@ -32,13 +30,9 @@ public class TextComponentSerializer extends BaseComponentSerializer implements | |||||||
|     @Override |     @Override | ||||||
|     public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) |     public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) | ||||||
|     { |     { | ||||||
|         List<BaseComponent> extra = src.getExtra(); |  | ||||||
|         JsonObject object = new JsonObject(); |         JsonObject object = new JsonObject(); | ||||||
|  |         serialize( object, src, context ); | ||||||
|         object.addProperty( "text", src.getText() ); |         object.addProperty( "text", src.getText() ); | ||||||
|         if ( src.hasFormatting() || ( extra != null && !extra.isEmpty() ) ) |  | ||||||
|         { |  | ||||||
|             serialize( object, src, context ); |  | ||||||
|         } |  | ||||||
|         return object; |         return object; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -21,14 +21,21 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp | |||||||
|         TranslatableComponent component = new TranslatableComponent(); |         TranslatableComponent component = new TranslatableComponent(); | ||||||
|         JsonObject object = json.getAsJsonObject(); |         JsonObject object = json.getAsJsonObject(); | ||||||
|         deserialize( object, component, context ); |         deserialize( object, component, context ); | ||||||
|         if ( !object.has( "translate" ) ) |         JsonElement translate = object.get( "translate" ); | ||||||
|  |         if ( translate == null ) | ||||||
|         { |         { | ||||||
|             throw new JsonParseException( "Could not parse JSON: missing 'translate' property" ); |             throw new JsonParseException( "Could not parse JSON: missing 'translate' property" ); | ||||||
|         } |         } | ||||||
|         component.setTranslate( object.get( "translate" ).getAsString() ); |         component.setTranslate( translate.getAsString() ); | ||||||
|         if ( object.has( "with" ) ) |         JsonElement with = object.get( "with" ); | ||||||
|  |         if ( with != null ) | ||||||
|         { |         { | ||||||
|             component.setWith( Arrays.asList( context.<BaseComponent[]>deserialize( object.get( "with" ), BaseComponent[].class ) ) ); |             component.setWith( Arrays.asList( context.deserialize( with, BaseComponent[].class ) ) ); | ||||||
|  |         } | ||||||
|  |         JsonElement fallback = object.get( "fallback" ); | ||||||
|  |         if ( fallback != null ) | ||||||
|  |         { | ||||||
|  |             component.setFallback( fallback.getAsString() ); | ||||||
|         } |         } | ||||||
|         return component; |         return component; | ||||||
|     } |     } | ||||||
| @@ -43,6 +50,10 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp | |||||||
|         { |         { | ||||||
|             object.add( "with", context.serialize( src.getWith() ) ); |             object.add( "with", context.serialize( src.getWith() ) ); | ||||||
|         } |         } | ||||||
|  |         if ( src.getFallback() != null ) | ||||||
|  |         { | ||||||
|  |             object.addProperty( "fallback", src.getFallback() ); | ||||||
|  |         } | ||||||
|         return object; |         return object; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| package net.md_5.bungee.chat; | package net.md_5.bungee.chat; | ||||||
|  |  | ||||||
| import com.google.common.base.Charsets; |  | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.LinkedList; | import java.util.LinkedList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -102,7 +102,7 @@ public final class TranslationRegistry | |||||||
|  |  | ||||||
|         public JsonProvider(String resourcePath) throws IOException |         public JsonProvider(String resourcePath) throws IOException | ||||||
|         { |         { | ||||||
|             try ( InputStreamReader rd = new InputStreamReader( JsonProvider.class.getResourceAsStream( resourcePath ), Charsets.UTF_8 ) ) |             try ( InputStreamReader rd = new InputStreamReader( JsonProvider.class.getResourceAsStream( resourcePath ), StandardCharsets.UTF_8 ) ) | ||||||
|             { |             { | ||||||
|                 JsonObject obj = new Gson().fromJson( rd, JsonObject.class ); |                 JsonObject obj = new Gson().fromJson( rd, JsonObject.class ); | ||||||
|                 for ( Map.Entry<String, JsonElement> entries : obj.entrySet() ) |                 for ( Map.Entry<String, JsonElement> entries : obj.entrySet() ) | ||||||
|   | |||||||
| @@ -1,12 +1,17 @@ | |||||||
| package net.md_5.bungee.api.chat; | package net.md_5.bungee.api.chat; | ||||||
|  |  | ||||||
|  | import static net.md_5.bungee.api.ChatColor.*; | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import java.awt.Color; | import java.awt.Color; | ||||||
| import net.md_5.bungee.api.ChatColor; | import java.util.function.BiFunction; | ||||||
| import net.md_5.bungee.api.chat.hover.content.Item; | import java.util.function.Consumer; | ||||||
|  | import java.util.function.Function; | ||||||
|  | import java.util.function.ObjIntConsumer; | ||||||
|  | import java.util.function.Supplier; | ||||||
|  | import net.md_5.bungee.api.chat.hover.content.Entity; | ||||||
| import net.md_5.bungee.api.chat.hover.content.Text; | import net.md_5.bungee.api.chat.hover.content.Text; | ||||||
| import net.md_5.bungee.chat.ComponentSerializer; | import net.md_5.bungee.chat.ComponentSerializer; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| public class ComponentsTest | public class ComponentsTest | ||||||
| { | { | ||||||
| @@ -15,13 +20,27 @@ public class ComponentsTest | |||||||
|     { |     { | ||||||
|         String json = ComponentSerializer.toString( components ); |         String json = ComponentSerializer.toString( components ); | ||||||
|         BaseComponent[] parsed = ComponentSerializer.parse( json ); |         BaseComponent[] parsed = ComponentSerializer.parse( json ); | ||||||
|         Assert.assertEquals( TextComponent.toLegacyText( parsed ), TextComponent.toLegacyText( components ) ); |         assertEquals( BaseComponent.toLegacyText( parsed ), BaseComponent.toLegacyText( components ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void testDissembleReassemble(String json) |     public static void testDissembleReassemble(BaseComponent component) | ||||||
|     { |     { | ||||||
|  |         String json = ComponentSerializer.toString( component ); | ||||||
|         BaseComponent[] parsed = ComponentSerializer.parse( json ); |         BaseComponent[] parsed = ComponentSerializer.parse( json ); | ||||||
|         Assert.assertEquals( json, ComponentSerializer.toString( parsed ) ); |         assertEquals( BaseComponent.toLegacyText( parsed ), BaseComponent.toLegacyText( component ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static void testAssembleDissemble(String json, boolean modern) | ||||||
|  |     { | ||||||
|  |         if ( modern ) | ||||||
|  |         { | ||||||
|  |             BaseComponent deserialized = ComponentSerializer.deserialize( json ); | ||||||
|  |             assertEquals( json, ComponentSerializer.toString( deserialized ) ); | ||||||
|  |         } else | ||||||
|  |         { | ||||||
|  |             BaseComponent[] parsed = ComponentSerializer.parse( json ); | ||||||
|  |             assertEquals( json, ComponentSerializer.toString( parsed ) ); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -41,16 +60,20 @@ public class ComponentsTest | |||||||
|         { |         { | ||||||
|             textComponent |             textComponent | ||||||
|         } ); |         } ); | ||||||
|         json = "{\"text\":\"Test\",\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]}}"; |         testDissembleReassemble( textComponent ); | ||||||
|         testDissembleReassemble( json ); |         json = "{\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]},\"text\":\"Test\"}"; | ||||||
|  |         testAssembleDissemble( json, false ); | ||||||
|  |         testAssembleDissemble( json, true ); | ||||||
|         ////////// |         ////////// | ||||||
|         String hoverVal = "{\"text\":\"{id:\\\"minecraft:dirt\\\",Count:1b}\"}"; |         String hoverVal = "{\"text\":\"{id:\\\"minecraft:dirt\\\",Count:1b}\"}"; | ||||||
|         json = "{\"extra\":[{\"text\":\"[\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"]\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[" + hoverVal + "]},\"text\":\"\"}"; |         json = "{\"extra\":[{\"text\":\"[\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"]\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[" + hoverVal + "]},\"text\":\"\"}"; | ||||||
|         components = ComponentSerializer.parse( json ); |         components = ComponentSerializer.parse( json ); | ||||||
|         Text contentText = ( (Text) components[0].getHoverEvent().getContents().get( 0 ) ); |         Text contentText = ( (Text) components[0].getHoverEvent().getContents().get( 0 ) ); | ||||||
|         Assert.assertEquals( hoverVal, ComponentSerializer.toString( (BaseComponent[]) contentText.getValue() ) ); |         assertEquals( hoverVal, ComponentSerializer.toString( (BaseComponent[]) contentText.getValue() ) ); | ||||||
|         testDissembleReassemble( components ); |         testDissembleReassemble( components ); | ||||||
|         ////////// |         ////////// | ||||||
|  |         // TODO: now ambiguous since "text" to distinguish Text from Item is not required | ||||||
|  |         /* | ||||||
|         TextComponent component1 = new TextComponent( "HoverableText" ); |         TextComponent component1 = new TextComponent( "HoverableText" ); | ||||||
|         String nbt = "{display:{Name:{text:Hello},Lore:[{text:Line_1},{text:Line_2}]},ench:[{id:49,lvl:5}],Unbreakable:1}}"; |         String nbt = "{display:{Name:{text:Hello},Lore:[{text:Line_1},{text:Line_2}]},ench:[{id:49,lvl:5}],Unbreakable:1}}"; | ||||||
|         Item contentItem = new Item( "minecraft:wood", 1, ItemTag.ofNbt( nbt ) ); |         Item contentItem = new Item( "minecraft:wood", 1, ItemTag.ofNbt( nbt ) ); | ||||||
| @@ -59,25 +82,53 @@ public class ComponentsTest | |||||||
|         json = ComponentSerializer.toString( component1 ); |         json = ComponentSerializer.toString( component1 ); | ||||||
|         components = ComponentSerializer.parse( json ); |         components = ComponentSerializer.parse( json ); | ||||||
|         Item parsedContentItem = ( (Item) components[0].getHoverEvent().getContents().get( 0 ) ); |         Item parsedContentItem = ( (Item) components[0].getHoverEvent().getContents().get( 0 ) ); | ||||||
|         Assert.assertEquals( contentItem, parsedContentItem ); |         assertEquals( contentItem, parsedContentItem ); | ||||||
|         Assert.assertEquals( contentItem.getCount(), parsedContentItem.getCount() ); |         assertEquals( contentItem.getCount(), parsedContentItem.getCount() ); | ||||||
|         Assert.assertEquals( contentItem.getId(), parsedContentItem.getId() ); |         assertEquals( contentItem.getId(), parsedContentItem.getId() ); | ||||||
|         Assert.assertEquals( nbt, parsedContentItem.getTag().getNbt() ); |         assertEquals( nbt, parsedContentItem.getTag().getNbt() ); | ||||||
|  |          */ | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testEmptyComponentBuilder() |     public void testArrayUUIDParse() | ||||||
|  |     { | ||||||
|  |         BaseComponent[] uuidComponent = ComponentSerializer.parse( "{\"translate\":\"multiplayer.player.joined\",\"with\":[{\"text\":\"Rexcantor64\",\"hoverEvent\":{\"contents\":{\"type\":\"minecraft:player\",\"id\":[1328556382,-2138814985,-1895806765,-1039963041],\"name\":\"Rexcantor64\"},\"action\":\"show_entity\"},\"insertion\":\"Rexcantor64\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/tell Rexcantor64 \"}}],\"color\":\"yellow\"}" ); | ||||||
|  |         assertEquals( "4f30295e-8084-45f7-8f00-48d3c2036c5f", ( (Entity) ( (TranslatableComponent) uuidComponent[0] ).getWith().get( 0 ).getHoverEvent().getContents().get( 0 ) ).getId() ); | ||||||
|  |         testDissembleReassemble( uuidComponent ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testEmptyComponentBuilderCreate() | ||||||
|  |     { | ||||||
|  |         testEmptyComponentBuilder( | ||||||
|  |                 ComponentBuilder::create, | ||||||
|  |                 (components) -> assertEquals( components.length, 0 ), | ||||||
|  |                 (components, size) -> assertEquals( size, components.length ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testEmptyComponentBuilderBuild() | ||||||
|  |     { | ||||||
|  |         testEmptyComponentBuilder( | ||||||
|  |                 ComponentBuilder::build, | ||||||
|  |                 (component) -> assertNull( component.getExtra() ), | ||||||
|  |                 (component, size) -> assertEquals( component.getExtra().size(), size ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static <T> void testEmptyComponentBuilder(Function<ComponentBuilder, T> componentBuilder, Consumer<T> emptyAssertion, ObjIntConsumer<T> sizedAssertion) | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder(); |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|  |  | ||||||
|         BaseComponent[] parts = builder.create(); |         T component = componentBuilder.apply( builder ); | ||||||
|         Assert.assertEquals( parts.length, 0 ); |         emptyAssertion.accept( component ); | ||||||
|  |  | ||||||
|         for ( int i = 0; i < 3; i++ ) |         for ( int i = 0; i < 3; i++ ) | ||||||
|         { |         { | ||||||
|             builder.append( "part:" + i ); |             builder.append( "part:" + i ); | ||||||
|             parts = builder.create(); |             component = componentBuilder.apply( builder ); | ||||||
|             Assert.assertEquals( parts.length, i + 1 ); |             sizedAssertion.accept( component, i + 1 ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -85,23 +136,34 @@ public class ComponentsTest | |||||||
|     public void testDummyRetaining() |     public void testDummyRetaining() | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder(); |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|         Assert.assertNotNull( builder.getCurrentComponent() ); |         assertNotNull( builder.getCurrentComponent() ); | ||||||
|         builder.color( ChatColor.GREEN ); |         builder.color( GREEN ); | ||||||
|         builder.append( "test ", ComponentBuilder.FormatRetention.ALL ); |         builder.append( "test ", ComponentBuilder.FormatRetention.ALL ); | ||||||
|         Assert.assertEquals( builder.getCurrentComponent().getColor(), ChatColor.GREEN ); |         assertEquals( builder.getCurrentComponent().getColor(), GREEN ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test(expected = IndexOutOfBoundsException.class) |     @Test | ||||||
|     public void testComponentGettingExceptions() |     public void testComponentGettingExceptions() | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder(); |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|         builder.getComponent( -1 ); |         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( -1 ) ); | ||||||
|         builder.getComponent( 0 ); |         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 0 ) ); | ||||||
|         builder.getComponent( 1 ); |         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) ); | ||||||
|         BaseComponent component = new TextComponent( "Hello" ); |         BaseComponent component = new TextComponent( "Hello" ); | ||||||
|         builder.append( component ); |         builder.append( component ); | ||||||
|         Assert.assertEquals( builder.getComponent( 0 ), component ); |         assertEquals( builder.getComponent( 0 ), component ); | ||||||
|         builder.getComponent( 1 ); |         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testFormatNotColor() | ||||||
|  |     { | ||||||
|  |         BaseComponent[] component = new ComponentBuilder().color( BOLD ).append( "Test" ).create(); | ||||||
|  |  | ||||||
|  |         String json = ComponentSerializer.toString( component ); | ||||||
|  |         BaseComponent[] parsed = ComponentSerializer.parse( json ); | ||||||
|  |  | ||||||
|  |         assertNull( parsed[0].getColorRaw(), "Format should not be preserved as color" ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -110,33 +172,33 @@ public class ComponentsTest | |||||||
|         ComponentBuilder builder = new ComponentBuilder(); |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|         TextComponent apple = new TextComponent( "apple" ); |         TextComponent apple = new TextComponent( "apple" ); | ||||||
|         builder.append( apple ); |         builder.append( apple ); | ||||||
|         Assert.assertEquals( builder.getCurrentComponent(), apple ); |         assertEquals( builder.getCurrentComponent(), apple ); | ||||||
|         Assert.assertEquals( builder.getComponent( 0 ), apple ); |         assertEquals( builder.getComponent( 0 ), apple ); | ||||||
|  |  | ||||||
|         TextComponent mango = new TextComponent( "mango" ); |         TextComponent mango = new TextComponent( "mango" ); | ||||||
|         TextComponent orange = new TextComponent( "orange" ); |         TextComponent orange = new TextComponent( "orange" ); | ||||||
|         builder.append( mango ); |         builder.append( mango ); | ||||||
|         builder.append( orange ); |         builder.append( orange ); | ||||||
|         builder.removeComponent( 1 ); // Removing mango |         builder.removeComponent( 1 ); // Removing mango | ||||||
|         Assert.assertEquals( builder.getComponent( 0 ), apple ); |         assertEquals( builder.getComponent( 0 ), apple ); | ||||||
|         Assert.assertEquals( builder.getComponent( 1 ), orange ); |         assertEquals( builder.getComponent( 1 ), orange ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testToLegacyFromLegacy() |     public void testToLegacyFromLegacy() | ||||||
|     { |     { | ||||||
|         String text = "§a§lHello §f§kworld§7!"; |         String text = "" + GREEN + BOLD + "Hello " + WHITE + MAGIC + "world" + GRAY + "!"; | ||||||
|         Assert.assertEquals( text, TextComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) ); |         assertEquals( text, BaseComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test(expected = IndexOutOfBoundsException.class) |     @Test | ||||||
|     public void testComponentBuilderCursorInvalidPos() |     public void testComponentBuilderCursorInvalidPos() | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder(); |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|         builder.append( new TextComponent( "Apple, " ) ); |         builder.append( new TextComponent( "Apple, " ) ); | ||||||
|         builder.append( new TextComponent( "Orange, " ) ); |         builder.append( new TextComponent( "Orange, " ) ); | ||||||
|         builder.setCursor( -1 ); |         assertThrows( IndexOutOfBoundsException.class, () -> builder.setCursor( -1 ) ); | ||||||
|         builder.setCursor( 2 ); |         assertThrows( IndexOutOfBoundsException.class, () -> builder.setCursor( 2 ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -144,33 +206,33 @@ public class ComponentsTest | |||||||
|     { |     { | ||||||
|         TextComponent t1, t2, t3; |         TextComponent t1, t2, t3; | ||||||
|         ComponentBuilder builder = new ComponentBuilder(); |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|         Assert.assertEquals( builder.getCursor(), -1 ); |         assertEquals( builder.getCursor(), -1 ); | ||||||
|         builder.append( t1 = new TextComponent( "Apple, " ) ); |         builder.append( t1 = new TextComponent( "Apple, " ) ); | ||||||
|         Assert.assertEquals( builder.getCursor(), 0 ); |         assertEquals( builder.getCursor(), 0 ); | ||||||
|         builder.append( t2 = new TextComponent( "Orange, " ) ); |         builder.append( t2 = new TextComponent( "Orange, " ) ); | ||||||
|         builder.append( t3 = new TextComponent( "Mango, " ) ); |         builder.append( t3 = new TextComponent( "Mango, " ) ); | ||||||
|         Assert.assertEquals( builder.getCursor(), 2 ); |         assertEquals( builder.getCursor(), 2 ); | ||||||
|  |  | ||||||
|         builder.setCursor( 0 ); |         builder.setCursor( 0 ); | ||||||
|         Assert.assertEquals( builder.getCurrentComponent(), t1 ); |         assertEquals( builder.getCurrentComponent(), t1 ); | ||||||
|  |  | ||||||
|         // Test that appending new components updates the position to the new list size |         // Test that appending new components updates the position to the new list size | ||||||
|         // after having previously set it to 0 (first component) |         // after having previously set it to 0 (first component) | ||||||
|         builder.append( new TextComponent( "and Grapefruit" ) ); |         builder.append( new TextComponent( "and Grapefruit" ) ); | ||||||
|         Assert.assertEquals( builder.getCursor(), 3 ); |         assertEquals( builder.getCursor(), 3 ); | ||||||
|  |  | ||||||
|         builder.setCursor( 0 ); |         builder.setCursor( 0 ); | ||||||
|         builder.resetCursor(); |         builder.resetCursor(); | ||||||
|         Assert.assertEquals( builder.getCursor(), 3 ); |         assertEquals( builder.getCursor(), 3 ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testLegacyComponentBuilderAppend() |     public void testLegacyComponentBuilderAppend() | ||||||
|     { |     { | ||||||
|         String text = "§a§lHello §r§kworld§7!"; |         String text = "" + GREEN + BOLD + "Hello " + RESET + MAGIC + "world" + GRAY + "!"; | ||||||
|         BaseComponent[] components = TextComponent.fromLegacyText( text ); |         BaseComponent[] components = TextComponent.fromLegacyText( text ); | ||||||
|         BaseComponent[] builderComponents = new ComponentBuilder().append( components ).create(); |         BaseComponent[] builderComponents = new ComponentBuilder().append( components ).create(); | ||||||
|         Assert.assertArrayEquals( components, builderComponents ); |         assertArrayEquals( components, builderComponents ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* |     /* | ||||||
| @@ -192,7 +254,7 @@ public class ComponentsTest | |||||||
|         component.setHoverEvent( event ); |         component.setHoverEvent( event ); | ||||||
|         String serialised = ComponentSerializer.toString( component ); |         String serialised = ComponentSerializer.toString( component ); | ||||||
|         BaseComponent[] deserialised = ComponentSerializer.parse( serialised ); |         BaseComponent[] deserialised = ComponentSerializer.parse( serialised ); | ||||||
|         Assert.assertEquals( TextComponent.toLegacyText( deserialised ), TextComponent.toLegacyText( component ) ); |         assertEquals( TextComponent.toLegacyText( deserialised ), TextComponent.toLegacyText( component ) ); | ||||||
|     } |     } | ||||||
|      */ |      */ | ||||||
|  |  | ||||||
| @@ -207,13 +269,13 @@ public class ComponentsTest | |||||||
|         ); |         ); | ||||||
|         TextComponent component = new TextComponent( "test" ); |         TextComponent component = new TextComponent( "test" ); | ||||||
|         component.setHoverEvent( hoverEvent ); |         component.setHoverEvent( hoverEvent ); | ||||||
|         Assert.assertEquals( component.getHoverEvent().getContents().size(), 1 ); |         assertEquals( component.getHoverEvent().getContents().size(), 1 ); | ||||||
|         Assert.assertTrue( component.getHoverEvent().getContents().get( 0 ) instanceof Text ); |         assertTrue( component.getHoverEvent().getContents().get( 0 ) instanceof Text ); | ||||||
|         Assert.assertEquals( ( (Text) component.getHoverEvent().getContents().get( 0 ) ).getValue(), advancement ); |         assertEquals( ( (Text) component.getHoverEvent().getContents().get( 0 ) ).getValue(), advancement ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testHoverEventContents() |     public void testHoverEventContentsCreate() | ||||||
|     { |     { | ||||||
|         // First do the text using the newer contents system |         // First do the text using the newer contents system | ||||||
|         HoverEvent hoverEvent = new HoverEvent( |         HoverEvent hoverEvent = new HoverEvent( | ||||||
| @@ -222,21 +284,53 @@ public class ComponentsTest | |||||||
|                 new Text( new ComponentBuilder( "Second" ).create() ) |                 new Text( new ComponentBuilder( "Second" ).create() ) | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         TextComponent component = new TextComponent( "Sample text" ); |         this.testHoverEventContents( | ||||||
|         component.setHoverEvent( hoverEvent ); |                 hoverEvent, | ||||||
|         Assert.assertEquals( hoverEvent.getContents().size(), 2 ); |                 ComponentSerializer::parse, | ||||||
|         Assert.assertFalse( hoverEvent.isLegacy() ); |                 (components) -> components[0].getHoverEvent(), | ||||||
|         String serialized = ComponentSerializer.toString( component ); |                 ComponentsTest::testDissembleReassemble // BaseComponent | ||||||
|         BaseComponent[] deserialized = ComponentSerializer.parse( serialized ); |         ); | ||||||
|         Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); |  | ||||||
|  |  | ||||||
|         // check the test still works with the value method |         // check the test still works with the value method | ||||||
|         hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() ); |         hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() ); | ||||||
|         Assert.assertEquals( hoverEvent.getContents().size(), 1 ); |         TextComponent component = new TextComponent( "Sample text" ); | ||||||
|         Assert.assertTrue( hoverEvent.isLegacy() ); |         component.setHoverEvent( hoverEvent ); | ||||||
|         serialized = ComponentSerializer.toString( component ); |  | ||||||
|         deserialized = ComponentSerializer.parse( serialized ); |         assertEquals( hoverEvent.getContents().size(), 1 ); | ||||||
|         Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); |         assertTrue( hoverEvent.isLegacy() ); | ||||||
|  |         String serialized = ComponentSerializer.toString( component ); | ||||||
|  |         BaseComponent[] deserialized = ComponentSerializer.parse( serialized ); | ||||||
|  |         assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testHoverEventContentsBuild() | ||||||
|  |     { | ||||||
|  |         // First do the text using the newer contents system | ||||||
|  |         HoverEvent hoverEvent = new HoverEvent( | ||||||
|  |                 HoverEvent.Action.SHOW_TEXT, | ||||||
|  |                 new Text( new ComponentBuilder( "First" ).build() ), | ||||||
|  |                 new Text( new ComponentBuilder( "Second" ).build() ) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         this.testHoverEventContents( | ||||||
|  |                 hoverEvent, | ||||||
|  |                 ComponentSerializer::deserialize, | ||||||
|  |                 BaseComponent::getHoverEvent, | ||||||
|  |                 ComponentsTest::testDissembleReassemble // BaseComponent | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private <T> void testHoverEventContents(HoverEvent hoverEvent, Function<String, T> deserializer, Function<T, HoverEvent> hoverEventGetter, Consumer<T> dissembleReassembleTest) | ||||||
|  |     { | ||||||
|  |         TextComponent component = new TextComponent( "Sample text" ); | ||||||
|  |         component.setHoverEvent( hoverEvent ); | ||||||
|  |         assertEquals( hoverEvent.getContents().size(), 2 ); | ||||||
|  |         assertFalse( hoverEvent.isLegacy() ); | ||||||
|  |  | ||||||
|  |         String serialized = ComponentSerializer.toString( component ); | ||||||
|  |         T deserialized = deserializer.apply( serialized ); | ||||||
|  |         assertEquals( component.getHoverEvent(), hoverEventGetter.apply( deserialized ) ); | ||||||
|  |  | ||||||
|         // Test single content: |         // Test single content: | ||||||
|         String json = "{\"italic\":true,\"color\":\"gray\",\"translate\":\"chat.type.admin\",\"with\":[{\"text\":\"@\"}" |         String json = "{\"italic\":true,\"color\":\"gray\",\"translate\":\"chat.type.admin\",\"with\":[{\"text\":\"@\"}" | ||||||
| @@ -248,37 +342,76 @@ public class ComponentsTest | |||||||
|                 + "\"/tell Name \"},\"hoverEvent\":{\"action\":\"show_entity\",\"contents\":" |                 + "\"/tell Name \"},\"hoverEvent\":{\"action\":\"show_entity\",\"contents\":" | ||||||
|                 + "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":" |                 + "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":" | ||||||
|                 + "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}"; |                 + "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}"; | ||||||
|         testDissembleReassemble( ComponentSerializer.parse( json ) ); |         dissembleReassembleTest.accept( deserializer.apply( json ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testFormatRetentionCopyFormatting() |     public void testFormatRetentionCopyFormattingCreate() | ||||||
|  |     { | ||||||
|  |         testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testFormatRetentionCopyFormattingBuild() | ||||||
|  |     { | ||||||
|  |         testFormatRetentionCopyFormatting( () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Test" ).build() ) ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void testFormatRetentionCopyFormatting(Supplier<HoverEvent> hoverEventSupplier) | ||||||
|     { |     { | ||||||
|         TextComponent first = new TextComponent( "Hello" ); |         TextComponent first = new TextComponent( "Hello" ); | ||||||
|         first.setBold( true ); |         first.setBold( true ); | ||||||
|         first.setColor( ChatColor.RED ); |         first.setColor( RED ); | ||||||
|         first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) ); |         first.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "test" ) ); | ||||||
|         first.setHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Test" ).create() ) ); |         first.setHoverEvent( hoverEventSupplier.get() ); | ||||||
|  |  | ||||||
|         TextComponent second = new TextComponent( " world" ); |         TextComponent second = new TextComponent( " world" ); | ||||||
|         second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true ); |         second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true ); | ||||||
|         Assert.assertEquals( first.isBold(), second.isBold() ); |         assertEquals( first.isBold(), second.isBold() ); | ||||||
|         Assert.assertEquals( first.getColor(), second.getColor() ); |         assertEquals( first.getColor(), second.getColor() ); | ||||||
|         Assert.assertEquals( first.getClickEvent(), second.getClickEvent() ); |         assertEquals( first.getClickEvent(), second.getClickEvent() ); | ||||||
|         Assert.assertEquals( first.getHoverEvent(), second.getHoverEvent() ); |         assertEquals( first.getHoverEvent(), second.getHoverEvent() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilderClone() |     public void testBuilderCloneCreate() | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.RED ).append( "world" ).color( ChatColor.DARK_RED ); |         testBuilderClone( (builder) -> BaseComponent.toLegacyText( builder.create() ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testBuilderCloneBuild() | ||||||
|  |     { | ||||||
|  |         testBuilderClone( (builder) -> BaseComponent.toLegacyText( builder.build() ) ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void testBuilderClone(Function<ComponentBuilder, String> legacyTextFunction) | ||||||
|  |     { | ||||||
|  |         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( RED ).append( "world" ).color( DARK_RED ); | ||||||
|         ComponentBuilder cloned = new ComponentBuilder( builder ); |         ComponentBuilder cloned = new ComponentBuilder( builder ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( TextComponent.toLegacyText( builder.create() ), TextComponent.toLegacyText( cloned.create() ) ); |         assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilderAppendMixedComponents() |     public void testBuilderAppendCreateMixedComponents() | ||||||
|  |     { | ||||||
|  |         testBuilderAppendMixedComponents( | ||||||
|  |                 ComponentBuilder::create, | ||||||
|  |                 (components, index) -> components[index] | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testBuilderAppendBuildMixedComponents() | ||||||
|  |     { | ||||||
|  |         testBuilderAppendMixedComponents( | ||||||
|  |                 ComponentBuilder::build, | ||||||
|  |                 (component, index) -> component.getExtra().get( index ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static <T> void testBuilderAppendMixedComponents(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder( "Hello " ); |         ComponentBuilder builder = new ComponentBuilder( "Hello " ); | ||||||
|         TextComponent textComponent = new TextComponent( "world " ); |         TextComponent textComponent = new TextComponent( "world " ); | ||||||
| @@ -291,11 +424,11 @@ public class ComponentsTest | |||||||
|         } ); |         } ); | ||||||
|         ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" ); |         ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" ); | ||||||
|         builder.append( scoreComponent ); // non array based BaseComponent append |         builder.append( scoreComponent ); // non array based BaseComponent append | ||||||
|         BaseComponent[] components = builder.create(); |         T component = componentBuilder.apply( builder ); | ||||||
|         Assert.assertEquals( "Hello ", components[0].toPlainText() ); |         assertEquals( "Hello ", extraGetter.apply( component, 0 ).toPlainText() ); | ||||||
|         Assert.assertEquals( textComponent.toPlainText(), components[1].toPlainText() ); |         assertEquals( textComponent.toPlainText(), extraGetter.apply( component, 1 ).toPlainText() ); | ||||||
|         Assert.assertEquals( translatableComponent.toPlainText(), components[2].toPlainText() ); |         assertEquals( translatableComponent.toPlainText(), extraGetter.apply( component, 2 ).toPlainText() ); | ||||||
|         Assert.assertEquals( scoreComponent.toPlainText(), components[3].toPlainText() ); |         assertEquals( scoreComponent.toPlainText(), extraGetter.apply( component, 3 ).toPlainText() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -305,171 +438,261 @@ public class ComponentsTest | |||||||
|         String text = ComponentSerializer.toString( component ); |         String text = ComponentSerializer.toString( component ); | ||||||
|         BaseComponent[] reparsed = ComponentSerializer.parse( text ); |         BaseComponent[] reparsed = ComponentSerializer.parse( text ); | ||||||
|  |  | ||||||
|         Assert.assertArrayEquals( component, reparsed ); |         assertArrayEquals( component, reparsed ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilderAppend() |     public void testStyle() | ||||||
|  |     { | ||||||
|  |         ComponentStyle style = ComponentSerializer.deserializeStyle( "{\"color\":\"red\",\"font\":\"minecraft:example\",\"bold\":true,\"italic\":false,\"obfuscated\":true}" ); | ||||||
|  |         String text = ComponentSerializer.toString( style ); | ||||||
|  |         ComponentStyle reparsed = ComponentSerializer.deserializeStyle( text ); | ||||||
|  |  | ||||||
|  |         assertEquals( style, reparsed ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testBuilderAppendCreate() | ||||||
|  |     { | ||||||
|  |         testBuilderAppend( | ||||||
|  |                 () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ), | ||||||
|  |                 ComponentBuilder::create, | ||||||
|  |                 (components, index) -> components[index], | ||||||
|  |                 BaseComponent::toPlainText, | ||||||
|  |                 YELLOW + "Hello " + GREEN + "world!", | ||||||
|  |                 BaseComponent::toLegacyText | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testBuilderAppendBuild() | ||||||
|  |     { | ||||||
|  |         testBuilderAppend( | ||||||
|  |                 () -> new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "Hello world" ).build() ) ), | ||||||
|  |                 ComponentBuilder::build, | ||||||
|  |                 (component, index) -> component.getExtra().get( index ), | ||||||
|  |                 (component) -> BaseComponent.toPlainText( component ), | ||||||
|  |                 // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component | ||||||
|  |                 WHITE.toString() + YELLOW + "Hello " + GREEN + "world!", | ||||||
|  |                 (component) -> BaseComponent.toLegacyText( component ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static <T> void testBuilderAppend(Supplier<HoverEvent> hoverEventSupplier, Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter, Function<T, String> toPlainTextFunction, String expectedLegacyText, Function<T, String> toLegacyTextFunction) | ||||||
|     { |     { | ||||||
|         ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " ); |         ClickEvent clickEvent = new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/help " ); | ||||||
|         HoverEvent hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Hello world" ).create() ); |         HoverEvent hoverEvent = hoverEventSupplier.get(); | ||||||
|  |  | ||||||
|         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW ); |         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW ); | ||||||
|         builder.append( new ComponentBuilder( "world!" ).color( ChatColor.GREEN ).event( hoverEvent ).event( clickEvent ).create() ); |         builder.append( new ComponentBuilder( "world!" ).color( GREEN ).event( hoverEvent ).event( clickEvent ).create() ); // Intentionally using create() to append multiple individual components | ||||||
|  |  | ||||||
|         BaseComponent[] components = builder.create(); |         T component = componentBuilder.apply( builder ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( components[1].getHoverEvent(), hoverEvent ); |         assertEquals( extraGetter.apply( component, 1 ).getHoverEvent(), hoverEvent ); | ||||||
|         Assert.assertEquals( components[1].getClickEvent(), clickEvent ); |         assertEquals( extraGetter.apply( component, 1 ).getClickEvent(), clickEvent ); | ||||||
|         Assert.assertEquals( "Hello world!", BaseComponent.toPlainText( components ) ); |         assertEquals( "Hello world!", toPlainTextFunction.apply( component ) ); | ||||||
|         Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) ); |         assertEquals( expectedLegacyText, toLegacyTextFunction.apply( component ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilderAppendLegacy() |     public void testBuilderAppendLegacyCreate() | ||||||
|     { |     { | ||||||
|         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW ); |         testBuilderAppendLegacy( | ||||||
|         builder.appendLegacy( "§aworld!" ); |                 ComponentBuilder::create, | ||||||
|  |                 BaseComponent::toPlainText, | ||||||
|  |                 YELLOW + "Hello " + GREEN + "world!", | ||||||
|  |                 BaseComponent::toLegacyText | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         BaseComponent[] components = builder.create(); |     @Test | ||||||
|  |     public void testBuilderAppendLegacyBuild() | ||||||
|  |     { | ||||||
|  |         testBuilderAppendLegacy( | ||||||
|  |                 ComponentBuilder::build, | ||||||
|  |                 (component) -> BaseComponent.toPlainText( component ), | ||||||
|  |                 // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component | ||||||
|  |                 WHITE.toString() + YELLOW + "Hello " + GREEN + "world!", | ||||||
|  |                 (component) -> BaseComponent.toLegacyText( component ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Hello world!", BaseComponent.toPlainText( components ) ); |     private static <T> void testBuilderAppendLegacy(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction) | ||||||
|         Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) ); |     { | ||||||
|  |         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW ); | ||||||
|  |         builder.appendLegacy( GREEN + "world!" ); | ||||||
|  |  | ||||||
|  |         T component = componentBuilder.apply( builder ); | ||||||
|  |  | ||||||
|  |         assertEquals( "Hello world!", toPlainTextFunction.apply( component ) ); | ||||||
|  |         assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBasicComponent() |     public void testBasicComponent() | ||||||
|     { |     { | ||||||
|         TextComponent textComponent = new TextComponent( "Hello world" ); |         TextComponent textComponent = new TextComponent( "Hello world" ); | ||||||
|         textComponent.setColor( ChatColor.RED ); |         textComponent.setColor( RED ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Hello world", textComponent.toPlainText() ); |         assertEquals( "Hello world", textComponent.toPlainText() ); | ||||||
|         Assert.assertEquals( ChatColor.RED + "Hello world", textComponent.toLegacyText() ); |         assertEquals( RED + "Hello world", textComponent.toLegacyText() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testLegacyConverter() |     public void testLegacyConverter() | ||||||
|     { |     { | ||||||
|         BaseComponent[] test1 = TextComponent.fromLegacyText( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold" ); |         BaseComponent[] test1 = TextComponent.fromLegacyText( AQUA + "Aqua " + RED + BOLD + "RedBold" ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Aqua RedBold", BaseComponent.toPlainText( test1 ) ); |         assertEquals( "Aqua RedBold", BaseComponent.toPlainText( test1 ) ); | ||||||
|         Assert.assertEquals( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) ); |         assertEquals( AQUA + "Aqua " + RED + BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) ); | ||||||
|  |  | ||||||
|         BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + ChatColor.GREEN + "google.com/test" ); |         BaseComponent[] test2 = TextComponent.fromLegacyText( "Text http://spigotmc.org " + GREEN + "google.com/test" ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) ); |         assertEquals( "Text http://spigotmc.org google.com/test", BaseComponent.toPlainText( test2 ) ); | ||||||
|         //The extra ChatColor instances are sometimes inserted when not needed but it doesn't change the result |         //The extra ChatColor instances are sometimes inserted when not needed but it doesn't change the result | ||||||
|         Assert.assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE |         assertEquals( WHITE + "Text " + WHITE + "http://spigotmc.org" + WHITE | ||||||
|                 + " " + ChatColor.GREEN + "google.com/test" + ChatColor.GREEN, BaseComponent.toLegacyText( test2 ) ); |                 + " " + GREEN + "google.com/test" + GREEN, BaseComponent.toLegacyText( test2 ) ); | ||||||
|  |  | ||||||
|         ClickEvent url1 = test2[1].getClickEvent(); |         ClickEvent url1 = test2[1].getClickEvent(); | ||||||
|         Assert.assertNotNull( url1 ); |         assertNotNull( url1 ); | ||||||
|         Assert.assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL ); |         assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL ); | ||||||
|         Assert.assertEquals( "http://spigotmc.org", url1.getValue() ); |         assertEquals( "http://spigotmc.org", url1.getValue() ); | ||||||
|  |  | ||||||
|         ClickEvent url2 = test2[3].getClickEvent(); |         ClickEvent url2 = test2[3].getClickEvent(); | ||||||
|         Assert.assertNotNull( url2 ); |         assertNotNull( url2 ); | ||||||
|         Assert.assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL ); |         assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL ); | ||||||
|         Assert.assertEquals( "http://google.com/test", url2.getValue() ); |         assertEquals( "http://google.com/test", url2.getValue() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testTranslateComponent() |     public void testBuilderCreate() | ||||||
|     { |     { | ||||||
|         TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" ); |         testBuilder( | ||||||
|         item.setColor( ChatColor.AQUA ); |                 ComponentBuilder::create, | ||||||
|         TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success", |                 BaseComponent::toPlainText, | ||||||
|                 item, "5", |                 RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!", | ||||||
|                 "thinkofdeath" ); |                 BaseComponent::toLegacyText | ||||||
|  |         ); | ||||||
|         Assert.assertEquals( "Given Golden Sword * 5 to thinkofdeath", translatableComponent.toPlainText() ); |  | ||||||
|         Assert.assertEquals( ChatColor.WHITE + "Given " + ChatColor.AQUA + "Golden Sword" + ChatColor.WHITE |  | ||||||
|                 + " * " + ChatColor.WHITE + "5" + ChatColor.WHITE + " to " + ChatColor.WHITE + "thinkofdeath", |  | ||||||
|                 translatableComponent.toLegacyText() ); |  | ||||||
|  |  | ||||||
|         TranslatableComponent positional = new TranslatableComponent( "book.pageIndicator", "5", "50" ); |  | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Page 5 of 50", positional.toPlainText() ); |  | ||||||
|         Assert.assertEquals( ChatColor.WHITE + "Page " + ChatColor.WHITE + "5" + ChatColor.WHITE + " of " + ChatColor.WHITE + "50", positional.toLegacyText() ); |  | ||||||
|  |  | ||||||
|         TranslatableComponent one_four_two = new TranslatableComponent( "filled_map.buried_treasure" ); |  | ||||||
|         Assert.assertEquals( "Buried Treasure Map", one_four_two.toPlainText() ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilder() |     public void testBuilderBuild() | ||||||
|     { |     { | ||||||
|         BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ). |         testBuilder( | ||||||
|                 append( "World" ).bold( true ).color( ChatColor.BLUE ). |                 ComponentBuilder::build, | ||||||
|                 append( "!" ).color( ChatColor.YELLOW ).create(); |                 (component) -> BaseComponent.toPlainText( component ), | ||||||
|  |                 // An extra format code is appended to the beginning because there is an empty TextComponent at the start of every component | ||||||
|  |                 WHITE.toString() + RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!", | ||||||
|  |                 (component) -> BaseComponent.toLegacyText( component ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Hello World!", BaseComponent.toPlainText( components ) ); |     private static <T> void testBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction) | ||||||
|         Assert.assertEquals( ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD |     { | ||||||
|                 + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", BaseComponent.toLegacyText( components ) ); |         T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ). | ||||||
|  |                 append( "World" ).bold( true ).color( BLUE ). | ||||||
|  |                 append( "!" ).color( YELLOW ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( "Hello World!", toPlainTextFunction.apply( component ) ); | ||||||
|  |         assertEquals( expectedLegacyString, toLegacyTextFunction.apply( component ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilderReset() |     public void testBuilderCreateReset() | ||||||
|     { |     { | ||||||
|         BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) |         testBuilderReset( | ||||||
|                 .append( "World" ).reset().create(); |                 ComponentBuilder::create, | ||||||
|  |                 (components, index) -> components[index] | ||||||
|         Assert.assertEquals( components[0].getColor(), ChatColor.RED ); |         ); | ||||||
|         Assert.assertEquals( components[1].getColor(), ChatColor.WHITE ); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testBuilderFormatRetention() |     public void testBuilderBuildReset() | ||||||
|     { |     { | ||||||
|         BaseComponent[] noneRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) |         testBuilderReset( | ||||||
|                 .append( "World", ComponentBuilder.FormatRetention.NONE ).create(); |                 ComponentBuilder::build, | ||||||
|  |                 (component, index) -> component.getExtra().get( index ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         Assert.assertEquals( noneRetention[0].getColor(), ChatColor.RED ); |     private static <T> void testBuilderReset(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) | ||||||
|         Assert.assertEquals( noneRetention[1].getColor(), ChatColor.WHITE ); |     { | ||||||
|  |         T component = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) | ||||||
|  |                 .append( "World" ).reset() ); | ||||||
|  |  | ||||||
|         HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "test" ).create() ); |         assertEquals( RED, extraGetter.apply( component, 0 ).getColor() ); | ||||||
|  |         assertEquals( WHITE, extraGetter.apply( component, 1 ).getColor() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         BaseComponent[] formattingRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) |     @Test | ||||||
|                 .event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ).create(); |     public void testBuilderCreateFormatRetention() | ||||||
|  |     { | ||||||
|  |         testBuilderFormatRetention( | ||||||
|  |                 ComponentBuilder::create, | ||||||
|  |                 (components, index) -> components[index] | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         Assert.assertEquals( formattingRetention[0].getColor(), ChatColor.RED ); |     @Test | ||||||
|         Assert.assertEquals( formattingRetention[0].getHoverEvent(), testEvent ); |     public void testBuilderBuildFormatRetention() | ||||||
|         Assert.assertEquals( formattingRetention[1].getColor(), ChatColor.RED ); |     { | ||||||
|         Assert.assertNull( formattingRetention[1].getHoverEvent() ); |         testBuilderFormatRetention( | ||||||
|  |                 ComponentBuilder::build, | ||||||
|  |                 (component, index) -> component.getExtra().get( index ) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static <T> void testBuilderFormatRetention(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) | ||||||
|  |     { | ||||||
|  |         T noneRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) | ||||||
|  |                 .append( "World", ComponentBuilder.FormatRetention.NONE ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( RED, extraGetter.apply( noneRetention, 0 ).getColor() ); | ||||||
|  |         assertEquals( WHITE, extraGetter.apply( noneRetention, 1 ).getColor() ); | ||||||
|  |  | ||||||
|  |         HoverEvent testEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new Text( new ComponentBuilder( "test" ).build() ) ); | ||||||
|  |  | ||||||
|  |         T formattingRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) | ||||||
|  |                 .event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ) ); | ||||||
|  |  | ||||||
|  |         assertEquals( RED, extraGetter.apply( formattingRetention, 0 ).getColor() ); | ||||||
|  |         assertEquals( testEvent, extraGetter.apply( formattingRetention, 0 ).getHoverEvent() ); | ||||||
|  |         assertEquals( RED, extraGetter.apply( formattingRetention, 1 ).getColor() ); | ||||||
|  |         assertNull( extraGetter.apply( formattingRetention, 1 ).getHoverEvent() ); | ||||||
|  |  | ||||||
|         ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" ); |         ClickEvent testClickEvent = new ClickEvent( ClickEvent.Action.OPEN_URL, "http://www.example.com" ); | ||||||
|  |  | ||||||
|         BaseComponent[] eventRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) |         T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) | ||||||
|                 .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ).create(); |                 .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( eventRetention[0].getColor(), ChatColor.RED ); |         assertEquals( RED, extraGetter.apply( eventRetention, 0 ).getColor() ); | ||||||
|         Assert.assertEquals( eventRetention[0].getHoverEvent(), testEvent ); |         assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() ); | ||||||
|         Assert.assertEquals( eventRetention[0].getClickEvent(), testClickEvent ); |         assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() ); | ||||||
|         Assert.assertEquals( eventRetention[1].getColor(), ChatColor.WHITE ); |         assertEquals( WHITE, extraGetter.apply( eventRetention, 1 ).getColor() ); | ||||||
|         Assert.assertEquals( eventRetention[1].getHoverEvent(), testEvent ); |         assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() ); | ||||||
|         Assert.assertEquals( eventRetention[1].getClickEvent(), testClickEvent ); |         assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test(expected = IllegalArgumentException.class) |     @Test | ||||||
|     public void testLoopSimple() |     public void testLoopSimple() | ||||||
|     { |     { | ||||||
|         TextComponent component = new TextComponent( "Testing" ); |         TextComponent component = new TextComponent( "Testing" ); | ||||||
|         component.addExtra( component ); |         component.addExtra( component ); | ||||||
|         ComponentSerializer.toString( component ); |         assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( component ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test(expected = IllegalArgumentException.class) |     @Test | ||||||
|     public void testLoopComplex() |     public void testLoopComplex() | ||||||
|     { |     { | ||||||
|         TextComponent a = new TextComponent( "A" ); |         TextComponent a = new TextComponent( "A" ); | ||||||
|         TextComponent b = new TextComponent( "B" ); |         TextComponent b = new TextComponent( "B" ); | ||||||
|         b.setColor( ChatColor.AQUA ); |         b.setColor( AQUA ); | ||||||
|         TextComponent c = new TextComponent( "C" ); |         TextComponent c = new TextComponent( "C" ); | ||||||
|         c.setColor( ChatColor.RED ); |         c.setColor( RED ); | ||||||
|         a.addExtra( b ); |         a.addExtra( b ); | ||||||
|         b.addExtra( c ); |         b.addExtra( c ); | ||||||
|         c.addExtra( a ); |         c.addExtra( a ); | ||||||
|         ComponentSerializer.toString( a ); |         assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( a ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -477,25 +700,25 @@ public class ComponentsTest | |||||||
|     { |     { | ||||||
|         TextComponent a = new TextComponent( "A" ); |         TextComponent a = new TextComponent( "A" ); | ||||||
|         TextComponent b = new TextComponent( "B" ); |         TextComponent b = new TextComponent( "B" ); | ||||||
|         b.setColor( ChatColor.AQUA ); |         b.setColor( AQUA ); | ||||||
|         a.addExtra( b ); |         a.addExtra( b ); | ||||||
|         a.addExtra( b ); |         a.addExtra( b ); | ||||||
|         ComponentSerializer.toString( a ); |         ComponentSerializer.toString( a ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test(expected = IllegalArgumentException.class) |     @Test | ||||||
|     public void testRepeatedError() |     public void testRepeatedError() | ||||||
|     { |     { | ||||||
|         TextComponent a = new TextComponent( "A" ); |         TextComponent a = new TextComponent( "A" ); | ||||||
|         TextComponent b = new TextComponent( "B" ); |         TextComponent b = new TextComponent( "B" ); | ||||||
|         b.setColor( ChatColor.AQUA ); |         b.setColor( AQUA ); | ||||||
|         TextComponent c = new TextComponent( "C" ); |         TextComponent c = new TextComponent( "C" ); | ||||||
|         c.setColor( ChatColor.RED ); |         c.setColor( RED ); | ||||||
|         a.addExtra( b ); |         a.addExtra( b ); | ||||||
|         a.addExtra( c ); |         a.addExtra( c ); | ||||||
|         c.addExtra( a ); |         c.addExtra( a ); | ||||||
|         a.addExtra( b ); |         a.addExtra( b ); | ||||||
|         ComponentSerializer.toString( a ); |         assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( a ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -506,35 +729,35 @@ public class ComponentsTest | |||||||
|         // collect all invalid color codes (e.g. §z, §g, ...) |         // collect all invalid color codes (e.g. §z, §g, ...) | ||||||
|         for ( char alphChar : "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray() ) |         for ( char alphChar : "0123456789abcdefghijklmnopqrstuvwxyz".toCharArray() ) | ||||||
|         { |         { | ||||||
|             if ( ChatColor.ALL_CODES.indexOf( alphChar ) == -1 ) |             if ( ALL_CODES.indexOf( alphChar ) == -1 ) | ||||||
|             { |             { | ||||||
|                 allInvalidColorCodes.append( ChatColor.COLOR_CHAR ); |                 allInvalidColorCodes.append( COLOR_CHAR ); | ||||||
|                 allInvalidColorCodes.append( alphChar ); |                 allInvalidColorCodes.append( alphChar ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // last char is a single '§' |         // last char is a single '§' | ||||||
|         allInvalidColorCodes.append( ChatColor.COLOR_CHAR ); |         allInvalidColorCodes.append( COLOR_CHAR ); | ||||||
|  |  | ||||||
|         String invalidColorCodesLegacyText = fromAndToLegacyText( allInvalidColorCodes.toString() ); |         String invalidColorCodesLegacyText = fromAndToLegacyText( allInvalidColorCodes.toString() ); | ||||||
|         String emptyLegacyText = fromAndToLegacyText( "" ); |         String emptyLegacyText = fromAndToLegacyText( "" ); | ||||||
|  |  | ||||||
|         // all invalid color codes and the trailing '§' should be ignored |         // all invalid color codes and the trailing '§' should be ignored | ||||||
|         Assert.assertEquals( emptyLegacyText, invalidColorCodesLegacyText ); |         assertEquals( emptyLegacyText, invalidColorCodesLegacyText ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testFormattingOnlyTextConversion() |     public void testFormattingOnlyTextConversion() | ||||||
|     { |     { | ||||||
|         String text = "§a"; |         String text = "" + GREEN; | ||||||
|  |  | ||||||
|         BaseComponent[] converted = TextComponent.fromLegacyText( text ); |         BaseComponent[] converted = TextComponent.fromLegacyText( text ); | ||||||
|         Assert.assertEquals( ChatColor.GREEN, converted[0].getColor() ); |         assertEquals( GREEN, converted[0].getColor() ); | ||||||
|  |  | ||||||
|         String roundtripLegacyText = BaseComponent.toLegacyText( converted ); |         String roundtripLegacyText = BaseComponent.toLegacyText( converted ); | ||||||
|  |  | ||||||
|         // color code should not be lost during conversion |         // color code should not be lost during conversion | ||||||
|         Assert.assertEquals( text, roundtripLegacyText ); |         assertEquals( text, roundtripLegacyText ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -546,7 +769,7 @@ public class ComponentsTest | |||||||
|         TextComponent second = new TextComponent( "Hello, " ); |         TextComponent second = new TextComponent( "Hello, " ); | ||||||
|         second.addExtra( new TextComponent( "World!" ) ); |         second.addExtra( new TextComponent( "World!" ) ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( first, second ); |         assertEquals( first, second ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -558,21 +781,90 @@ public class ComponentsTest | |||||||
|         TextComponent second = new TextComponent( "Hello, " ); |         TextComponent second = new TextComponent( "Hello, " ); | ||||||
|         second.addExtra( new TextComponent( "World!" ) ); |         second.addExtra( new TextComponent( "World!" ) ); | ||||||
|  |  | ||||||
|         Assert.assertNotEquals( first, second ); |         assertNotEquals( first, second ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testLegacyHack() |     public void testLegacyHack() | ||||||
|     { |     { | ||||||
|         BaseComponent[] hexColored = new ComponentBuilder().color( ChatColor.of( Color.GRAY ) ).append( "Test" ).create(); |         BaseComponent[] hexColored = new ComponentBuilder().color( of( Color.GRAY ) ).append( "Test" ).create(); | ||||||
|         String legacy = TextComponent.toLegacyText( hexColored ); |         String legacy = BaseComponent.toLegacyText( hexColored ); | ||||||
|  |  | ||||||
|         BaseComponent[] reColored = TextComponent.fromLegacyText( legacy ); |         BaseComponent[] reColored = TextComponent.fromLegacyText( legacy ); | ||||||
|  |  | ||||||
|         Assert.assertArrayEquals( hexColored, reColored ); |         assertArrayEquals( hexColored, reColored ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private String fromAndToLegacyText(String legacyText) |     @Test | ||||||
|  |     public void testLegacyResetInBuilderCreate() | ||||||
|  |     { | ||||||
|  |         testLegacyResetInBuilder( | ||||||
|  |                 ComponentBuilder::create, | ||||||
|  |                 ComponentSerializer::toString | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testLegacyResetInBuilderBuild() | ||||||
|  |     { | ||||||
|  |         testLegacyResetInBuilder( | ||||||
|  |                 ComponentBuilder::build, | ||||||
|  |                 ComponentSerializer::toString | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testHasFormatting() | ||||||
|  |     { | ||||||
|  |         BaseComponent component = new TextComponent(); | ||||||
|  |         assertFalse( component.hasFormatting() ); | ||||||
|  |  | ||||||
|  |         component.setBold( true ); | ||||||
|  |         assertTrue( component.hasFormatting() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testStyleIsEmpty() | ||||||
|  |     { | ||||||
|  |         ComponentStyle style = ComponentStyle.builder().build(); | ||||||
|  |         assertTrue( style.isEmpty() ); | ||||||
|  |  | ||||||
|  |         style = ComponentStyle.builder() | ||||||
|  |                 .bold( true ) | ||||||
|  |                 .build(); | ||||||
|  |         assertFalse( style.isEmpty() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* | ||||||
|  |      * In legacy chat, colors and reset both reset all formatting. | ||||||
|  |      * Make sure it works in combination with ComponentBuilder. | ||||||
|  |      */ | ||||||
|  |     private static <T> void testLegacyResetInBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> componentSerializer) | ||||||
|  |     { | ||||||
|  |         ComponentBuilder builder = new ComponentBuilder(); | ||||||
|  |         BaseComponent[] a = TextComponent.fromLegacyText( "" + DARK_RED + UNDERLINE + "44444" + RESET + "dd" + GOLD + BOLD + "6666" ); | ||||||
|  |  | ||||||
|  |         String expected = "{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"},{\"color\":" | ||||||
|  |                 + "\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"}],\"text\":\"\"}"; | ||||||
|  |         assertEquals( expected, ComponentSerializer.toString( a ) ); | ||||||
|  |  | ||||||
|  |         builder.append( a ); | ||||||
|  |  | ||||||
|  |         String test1 = componentSerializer.apply( componentBuilder.apply( builder ) ); | ||||||
|  |         assertEquals( expected, test1 ); | ||||||
|  |  | ||||||
|  |         BaseComponent[] b = TextComponent.fromLegacyText( RESET + "rrrr" ); | ||||||
|  |         builder.append( b ); | ||||||
|  |  | ||||||
|  |         String test2 = componentSerializer.apply( componentBuilder.apply( builder ) ); | ||||||
|  |         assertEquals( | ||||||
|  |                 "{\"extra\":[{\"underlined\":true,\"color\":\"dark_red\",\"text\":\"44444\"}," | ||||||
|  |                 + "{\"color\":\"white\",\"text\":\"dd\"},{\"bold\":true,\"color\":\"gold\",\"text\":\"6666\"}," | ||||||
|  |                 + "{\"color\":\"white\",\"text\":\"rrrr\"}],\"text\":\"\"}", | ||||||
|  |                 test2 ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static String fromAndToLegacyText(String legacyText) | ||||||
|     { |     { | ||||||
|         return BaseComponent.toLegacyText( TextComponent.fromLegacyText( legacyText ) ); |         return BaseComponent.toLegacyText( TextComponent.fromLegacyText( legacyText ) ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| package net.md_5.bungee.api.chat; | package net.md_5.bungee.api.chat; | ||||||
|  |  | ||||||
|  | import static net.md_5.bungee.api.ChatColor.*; | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import net.md_5.bungee.chat.ComponentSerializer; | import net.md_5.bungee.chat.ComponentSerializer; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| public class TranslatableComponentTest | public class TranslatableComponentTest | ||||||
| { | { | ||||||
| @@ -11,8 +12,8 @@ public class TranslatableComponentTest | |||||||
|     public void testMissingPlaceholdersAdded() |     public void testMissingPlaceholdersAdded() | ||||||
|     { |     { | ||||||
|         TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholders: %s", 2, "aoeu" ); |         TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholders: %s", 2, "aoeu" ); | ||||||
|         Assert.assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() ); |         assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() ); | ||||||
|         Assert.assertEquals( "§fTest string with §f2§f placeholders: §faoeu", testComponent.toLegacyText() ); |         assertEquals( WHITE + "Test string with " + WHITE + "2" + WHITE + " placeholders: " + WHITE + "aoeu", testComponent.toLegacyText() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -22,7 +23,29 @@ public class TranslatableComponentTest | |||||||
|         String jsonString = ComponentSerializer.toString( testComponent ); |         String jsonString = ComponentSerializer.toString( testComponent ); | ||||||
|         BaseComponent[] baseComponents = ComponentSerializer.parse( jsonString ); |         BaseComponent[] baseComponents = ComponentSerializer.parse( jsonString ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( "Test string with a placeholder", TextComponent.toPlainText( baseComponents ) ); |         assertEquals( "Test string with a placeholder", BaseComponent.toPlainText( baseComponents ) ); | ||||||
|         Assert.assertEquals( "§fTest string with §fa§f placeholder", TextComponent.toLegacyText( baseComponents ) ); |         assertEquals( WHITE + "Test string with " + WHITE + "a" + WHITE + " placeholder", BaseComponent.toLegacyText( baseComponents ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testTranslateComponent() | ||||||
|  |     { | ||||||
|  |         TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" ); | ||||||
|  |         item.setColor( AQUA ); | ||||||
|  |         TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success", | ||||||
|  |                 item, "5", "thinkofdeath" ); | ||||||
|  |  | ||||||
|  |         assertEquals( "Given Golden Sword * 5 to thinkofdeath", translatableComponent.toPlainText() ); | ||||||
|  |         assertEquals( WHITE + "Given " + AQUA + "Golden Sword" + WHITE + " * " + WHITE + "5" + WHITE + " to " + WHITE + "thinkofdeath", | ||||||
|  |                 translatableComponent.toLegacyText() ); | ||||||
|  |  | ||||||
|  |         TranslatableComponent positional = new TranslatableComponent( "book.pageIndicator", "5", "50" ); | ||||||
|  |  | ||||||
|  |         assertEquals( "Page 5 of 50", positional.toPlainText() ); | ||||||
|  |         assertEquals( WHITE + "Page " + WHITE + "5" + WHITE + " of " + WHITE + "50", positional.toLegacyText() ); | ||||||
|  |  | ||||||
|  |         TranslatableComponent one_four_two = new TranslatableComponent( "filled_map.buried_treasure" ); | ||||||
|  |         assertEquals( "Buried Treasure Map", one_four_two.toPlainText() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -33,9 +33,9 @@ | |||||||
|  |  | ||||||
|         <!-- See http://checkstyle.sourceforge.net/config_filters.html --> |         <!-- See http://checkstyle.sourceforge.net/config_filters.html --> | ||||||
|         <module name="SuppressionCommentFilter"/> |         <module name="SuppressionCommentFilter"/> | ||||||
|  |         <module name="SuppressWarningsHolder"/> | ||||||
|  |  | ||||||
|         <!-- See http://checkstyle.sourceforge.net/config_imports.html --> |         <!-- See http://checkstyle.sourceforge.net/config_imports.html --> | ||||||
|         <module name="AvoidStarImport"/> |  | ||||||
|         <module name="ImportOrder"> |         <module name="ImportOrder"> | ||||||
|             <property name="option" value="above"/> |             <property name="option" value="above"/> | ||||||
|             <property name="ordered" value="true"/> |             <property name="ordered" value="true"/> | ||||||
| @@ -54,11 +54,11 @@ | |||||||
|         <module name="OperatorWrap"/> |         <module name="OperatorWrap"/> | ||||||
|         <module name="ParenPad"> |         <module name="ParenPad"> | ||||||
|             <property name="option" value="nospace"/> |             <property name="option" value="nospace"/> | ||||||
|             <property name="tokens" value="ANNOTATION, CTOR_DEF, METHOD_DEF"/> |             <property name="tokens" value="ANNOTATION, CTOR_DEF, METHOD_DEF, LAMBDA"/> | ||||||
|         </module> |         </module> | ||||||
|         <module name="ParenPad"> |         <module name="ParenPad"> | ||||||
|             <property name="option" value="space"/> |             <property name="option" value="space"/> | ||||||
|             <property name="tokens" value="ANNOTATION_FIELD_DEF, CTOR_CALL, DOT, ENUM_CONSTANT_DEF, EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA"/> |             <property name="tokens" value="ANNOTATION_FIELD_DEF, CTOR_CALL, DOT, ENUM_CONSTANT_DEF, EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, RECORD_DEF"/> | ||||||
|         </module> |         </module> | ||||||
|         <module name="SingleSpaceSeparator"/> |         <module name="SingleSpaceSeparator"/> | ||||||
|         <module name="TypecastParenPad"/> |         <module name="TypecastParenPad"/> | ||||||
| @@ -84,4 +84,6 @@ | |||||||
|         <module name="Indentation"/> |         <module name="Indentation"/> | ||||||
|         <module name="UpperEll"/> |         <module name="UpperEll"/> | ||||||
|     </module> |     </module> | ||||||
|  |  | ||||||
|  |     <module name="SuppressWarningsFilter"/> | ||||||
| </module> | </module> | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-config</artifactId> |     <artifactId>bungeecord-config</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Config</name> |     <name>BungeeCord-Config</name> | ||||||
| @@ -22,14 +22,14 @@ | |||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>com.google.code.gson</groupId> |             <groupId>com.google.code.gson</groupId> | ||||||
|             <artifactId>gson</artifactId> |             <artifactId>gson</artifactId> | ||||||
|             <version>2.8.0</version> |             <version>2.10.1</version> | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|             <optional>true</optional> |             <optional>true</optional> | ||||||
|         </dependency> |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>org.yaml</groupId> |             <groupId>org.yaml</groupId> | ||||||
|             <artifactId>snakeyaml</artifactId> |             <artifactId>snakeyaml</artifactId> | ||||||
|             <version>1.26</version> |             <version>2.2</version> | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|             <optional>true</optional> |             <optional>true</optional> | ||||||
|         </dependency> |         </dependency> | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| package net.md_5.bungee.config; | package net.md_5.bungee.config; | ||||||
|  |  | ||||||
| import com.google.common.base.Charsets; |  | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| @@ -16,6 +15,7 @@ import java.io.OutputStreamWriter; | |||||||
| import java.io.Reader; | import java.io.Reader; | ||||||
| import java.io.Writer; | import java.io.Writer; | ||||||
| import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
| @@ -37,7 +37,7 @@ public class JsonConfiguration extends ConfigurationProvider | |||||||
|     @Override |     @Override | ||||||
|     public void save(Configuration config, File file) throws IOException |     public void save(Configuration config, File file) throws IOException | ||||||
|     { |     { | ||||||
|         try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), Charsets.UTF_8 ) ) |         try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), StandardCharsets.UTF_8 ) ) | ||||||
|         { |         { | ||||||
|             save( config, writer ); |             save( config, writer ); | ||||||
|         } |         } | ||||||
| @@ -91,7 +91,7 @@ public class JsonConfiguration extends ConfigurationProvider | |||||||
|     @Override |     @Override | ||||||
|     public Configuration load(InputStream is, Configuration defaults) |     public Configuration load(InputStream is, Configuration defaults) | ||||||
|     { |     { | ||||||
|         return load( new InputStreamReader( is, Charsets.UTF_8 ), defaults ); |         return load( new InputStreamReader( is, StandardCharsets.UTF_8 ), defaults ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| package net.md_5.bungee.config; | package net.md_5.bungee.config; | ||||||
|  |  | ||||||
| import com.google.common.base.Charsets; |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FileInputStream; | import java.io.FileInputStream; | ||||||
| import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||||
| @@ -9,11 +8,13 @@ import java.io.InputStream; | |||||||
| import java.io.OutputStreamWriter; | import java.io.OutputStreamWriter; | ||||||
| import java.io.Reader; | import java.io.Reader; | ||||||
| import java.io.Writer; | import java.io.Writer; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import lombok.AccessLevel; | import lombok.AccessLevel; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| import org.yaml.snakeyaml.DumperOptions; | import org.yaml.snakeyaml.DumperOptions; | ||||||
|  | import org.yaml.snakeyaml.LoaderOptions; | ||||||
| import org.yaml.snakeyaml.Yaml; | import org.yaml.snakeyaml.Yaml; | ||||||
| import org.yaml.snakeyaml.constructor.Constructor; | import org.yaml.snakeyaml.constructor.Constructor; | ||||||
| import org.yaml.snakeyaml.nodes.Node; | import org.yaml.snakeyaml.nodes.Node; | ||||||
| @@ -29,7 +30,10 @@ public class YamlConfiguration extends ConfigurationProvider | |||||||
|         @Override |         @Override | ||||||
|         protected Yaml initialValue() |         protected Yaml initialValue() | ||||||
|         { |         { | ||||||
|             Representer representer = new Representer() |             DumperOptions options = new DumperOptions(); | ||||||
|  |             options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK ); | ||||||
|  |  | ||||||
|  |             Representer representer = new Representer( options ) | ||||||
|             { |             { | ||||||
|                 { |                 { | ||||||
|                     representers.put( Configuration.class, new Represent() |                     representers.put( Configuration.class, new Represent() | ||||||
| @@ -43,17 +47,14 @@ public class YamlConfiguration extends ConfigurationProvider | |||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             DumperOptions options = new DumperOptions(); |             return new Yaml( new Constructor( new LoaderOptions() ), representer, options ); | ||||||
|             options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK ); |  | ||||||
|  |  | ||||||
|             return new Yaml( new Constructor(), representer, options ); |  | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void save(Configuration config, File file) throws IOException |     public void save(Configuration config, File file) throws IOException | ||||||
|     { |     { | ||||||
|         try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), Charsets.UTF_8 ) ) |         try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), StandardCharsets.UTF_8 ) ) | ||||||
|         { |         { | ||||||
|             save( config, writer ); |             save( config, writer ); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,148 +1,138 @@ | |||||||
| package net.md_5.bungee.config; | package net.md_5.bungee.config; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import java.io.StringReader; | import java.io.StringReader; | ||||||
| import java.io.StringWriter; | import java.io.StringWriter; | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  | import java.util.stream.Stream; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.params.ParameterizedTest; | ||||||
| import org.junit.Test; | import org.junit.jupiter.params.provider.Arguments; | ||||||
| import org.junit.runner.RunWith; | import org.junit.jupiter.params.provider.MethodSource; | ||||||
| import org.junit.runners.Parameterized; |  | ||||||
| import org.junit.runners.Parameterized.Parameters; |  | ||||||
|  |  | ||||||
| @RequiredArgsConstructor | @RequiredArgsConstructor | ||||||
| @RunWith(Parameterized.class) |  | ||||||
| public class CompoundConfigurationTest | public class CompoundConfigurationTest | ||||||
| { | { | ||||||
|  |  | ||||||
|     @Parameters(name = "{0}") |     public static Stream<Arguments> data() | ||||||
|     public static Iterable<Object[]> data() |  | ||||||
|     { |     { | ||||||
|         // CHECKSTYLE:OFF |         return Stream.of( | ||||||
|         return Arrays.asList( new Object[][] |                 Arguments.of( | ||||||
|         { |                         // provider | ||||||
|             { |                         YamlConfiguration.class, | ||||||
|                 // provider |                         // testDocument | ||||||
|                 YamlConfiguration.class, |                         "" | ||||||
|                 // testDocument |                         + "receipt:     Oz-Ware Purchase Invoice\n" | ||||||
|                 "" |                         + "date:        2012-08-06\n" | ||||||
|                 + "receipt:     Oz-Ware Purchase Invoice\n" |                         + "customer:\n" | ||||||
|                 + "date:        2012-08-06\n" |                         + "    given:   Dorothy\n" | ||||||
|                 + "customer:\n" |                         + "    family:  Gale\n" | ||||||
|                 + "    given:   Dorothy\n" |                         + "\n" | ||||||
|                 + "    family:  Gale\n" |                         + "items:\n" | ||||||
|                 + "\n" |                         + "    - part_no:   A4786\n" | ||||||
|                 + "items:\n" |                         + "      descrip:   Water Bucket (Filled)\n" | ||||||
|                 + "    - part_no:   A4786\n" |                         + "      price:     1.47\n" | ||||||
|                 + "      descrip:   Water Bucket (Filled)\n" |                         + "      quantity:  4\n" | ||||||
|                 + "      price:     1.47\n" |                         + "\n" | ||||||
|                 + "      quantity:  4\n" |                         + "    - part_no:   E1628\n" | ||||||
|                 + "\n" |                         + "      descrip:   High Heeled \"Ruby\" Slippers\n" | ||||||
|                 + "    - part_no:   E1628\n" |                         + "      size:      8\n" | ||||||
|                 + "      descrip:   High Heeled \"Ruby\" Slippers\n" |                         + "      price:     100.27\n" | ||||||
|                 + "      size:      8\n" |                         + "      quantity:  1\n" | ||||||
|                 + "      price:     100.27\n" |                         + "\n" | ||||||
|                 + "      quantity:  1\n" |                         + "bill-to:  &id001\n" | ||||||
|                 + "\n" |                         + "    street: |\n" | ||||||
|                 + "bill-to:  &id001\n" |                         + "            123 Tornado Alley\n" | ||||||
|                 + "    street: |\n" |                         + "            Suite 16\n" | ||||||
|                 + "            123 Tornado Alley\n" |                         + "    city:   East Centerville\n" | ||||||
|                 + "            Suite 16\n" |                         + "    state:  KS\n" | ||||||
|                 + "    city:   East Centerville\n" |                         + "\n" | ||||||
|                 + "    state:  KS\n" |                         + "ship-to:  *id001\n" | ||||||
|                 + "\n" |                         + "\n" | ||||||
|                 + "ship-to:  *id001\n" |                         + "specialDelivery:  >\n" | ||||||
|                 + "\n" |                         + "    Follow the Yellow Brick\n" | ||||||
|                 + "specialDelivery:  >\n" |                         + "    Road to the Emerald City.\n" | ||||||
|                 + "    Follow the Yellow Brick\n" |                         + "    Pay no attention to the\n" | ||||||
|                 + "    Road to the Emerald City.\n" |                         + "    man behind the curtain.", | ||||||
|                 + "    Pay no attention to the\n" |                         // numberTest | ||||||
|                 + "    man behind the curtain.", |                         "" | ||||||
|                 // numberTest |                         + "someKey:\n" | ||||||
|                 "" |                         + "    1: 1\n" | ||||||
|                 + "someKey:\n" |                         + "    2: 2\n" | ||||||
|                 + "    1: 1\n" |                         + "    3: 3\n" | ||||||
|                 + "    2: 2\n" |                         + "    4: 4", | ||||||
|                 + "    3: 3\n" |                         // nullTest | ||||||
|                 + "    4: 4", |                         "" | ||||||
|                 // nullTest |                         + "null:\n" | ||||||
|                 "" |                         + "    null: object\n" | ||||||
|                 + "null:\n" |                         + "    object: null\n" | ||||||
|                 + "    null: object\n" |                 ), | ||||||
|                 + "    object: null\n" |                 Arguments.of( | ||||||
|             }, |                         // provider | ||||||
|             { |                         JsonConfiguration.class, | ||||||
|                 // provider |                         // testDocument | ||||||
|                 JsonConfiguration.class, |                         "" | ||||||
|                 // testDocument |                         + "{\n" | ||||||
|                 "" |                         + "  \"customer\": {\n" | ||||||
|                 + "{\n" |                         + "    \"given\": \"Dorothy\", \n" | ||||||
|                 + "  \"customer\": {\n" |                         + "    \"family\": \"Gale\"\n" | ||||||
|                 + "    \"given\": \"Dorothy\", \n" |                         + "  }, \n" | ||||||
|                 + "    \"family\": \"Gale\"\n" |                         + "  \"ship-to\": {\n" | ||||||
|                 + "  }, \n" |                         + "    \"city\": \"East Centerville\", \n" | ||||||
|                 + "  \"ship-to\": {\n" |                         + "    \"state\": \"KS\", \n" | ||||||
|                 + "    \"city\": \"East Centerville\", \n" |                         + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" | ||||||
|                 + "    \"state\": \"KS\", \n" |                         + "  }, \n" | ||||||
|                 + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" |                         + "  \"bill-to\": {\n" | ||||||
|                 + "  }, \n" |                         + "    \"city\": \"East Centerville\", \n" | ||||||
|                 + "  \"bill-to\": {\n" |                         + "    \"state\": \"KS\", \n" | ||||||
|                 + "    \"city\": \"East Centerville\", \n" |                         + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" | ||||||
|                 + "    \"state\": \"KS\", \n" |                         + "  }, \n" | ||||||
|                 + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" |                         + "  \"date\": \"2012-08-06\", \n" | ||||||
|                 + "  }, \n" |                         + "  \"items\": [\n" | ||||||
|                 + "  \"date\": \"2012-08-06\", \n" |                         + "    {\n" | ||||||
|                 + "  \"items\": [\n" |                         + "      \"part_no\": \"A4786\", \n" | ||||||
|                 + "    {\n" |                         + "      \"price\": 1.47, \n" | ||||||
|                 + "      \"part_no\": \"A4786\", \n" |                         + "      \"descrip\": \"Water Bucket (Filled)\", \n" | ||||||
|                 + "      \"price\": 1.47, \n" |                         + "      \"quantity\": 4\n" | ||||||
|                 + "      \"descrip\": \"Water Bucket (Filled)\", \n" |                         + "    }, \n" | ||||||
|                 + "      \"quantity\": 4\n" |                         + "    {\n" | ||||||
|                 + "    }, \n" |                         + "      \"part_no\": \"E1628\", \n" | ||||||
|                 + "    {\n" |                         + "      \"descrip\": \"High Heeled \\\"Ruby\\\" Slippers\", \n" | ||||||
|                 + "      \"part_no\": \"E1628\", \n" |                         + "      \"price\": 100.27, \n" | ||||||
|                 + "      \"descrip\": \"High Heeled \\\"Ruby\\\" Slippers\", \n" |                         + "      \"quantity\": 1, \n" | ||||||
|                 + "      \"price\": 100.27, \n" |                         + "      \"size\": 8\n" | ||||||
|                 + "      \"quantity\": 1, \n" |                         + "    }\n" | ||||||
|                 + "      \"size\": 8\n" |                         + "  ], \n" | ||||||
|                 + "    }\n" |                         + "  \"receipt\": \"Oz-Ware Purchase Invoice\", \n" | ||||||
|                 + "  ], \n" |                         + "  \"specialDelivery\": \"Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the curtain.\"\n" | ||||||
|                 + "  \"receipt\": \"Oz-Ware Purchase Invoice\", \n" |                         + "}", | ||||||
|                 + "  \"specialDelivery\": \"Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the curtain.\"\n" |                         // numberTest | ||||||
|                 + "}", |                         "" | ||||||
|                 // numberTest |                         + "{\n" | ||||||
|                 "" |                         + "  \"someKey\": {\n" | ||||||
|                 + "{\n" |                         + "    \"1\": 1, \n" | ||||||
|                 + "  \"someKey\": {\n" |                         + "    \"2\": 2, \n" | ||||||
|                 + "    \"1\": 1, \n" |                         + "    \"3\": 3, \n" | ||||||
|                 + "    \"2\": 2, \n" |                         + "    \"4\": 4\n" | ||||||
|                 + "    \"3\": 3, \n" |                         + "  }\n" | ||||||
|                 + "    \"4\": 4\n" |                         + "}", | ||||||
|                 + "  }\n" |                         // nullTest | ||||||
|                 + "}", |                         "" | ||||||
|                 // nullTest |                         + "{\n" | ||||||
|                 "" |                         + "  \"null\": {\n" | ||||||
|                 + "{\n" |                         + "    \"null\": \"object\", \n" | ||||||
|                 + "  \"null\": {\n" |                         + "    \"object\": null\n" | ||||||
|                 + "    \"null\": \"object\", \n" |                         + "  }\n" | ||||||
|                 + "    \"object\": null\n" |                         + "}" | ||||||
|                 + "  }\n" |                 ) | ||||||
|                 + "}" |         ); | ||||||
|             } |  | ||||||
|         } ); |  | ||||||
|         // CHECKSTYLE:ON |  | ||||||
|     } |     } | ||||||
|     // |  | ||||||
|     private final Class<? extends ConfigurationProvider> provider; |  | ||||||
|     private final String testDocument; |  | ||||||
|     private final String numberTest; |  | ||||||
|     private final String nullTest; |  | ||||||
|  |  | ||||||
|     @Test |     @ParameterizedTest | ||||||
|     public void testConfig() throws Exception |     @MethodSource("data") | ||||||
|  |     public void testConfig(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) throws Exception | ||||||
|     { |     { | ||||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( testDocument ); |         Configuration conf = ConfigurationProvider.getProvider( provider ).load( testDocument ); | ||||||
|         testSection( conf ); |         testSection( conf ); | ||||||
| @@ -151,7 +141,7 @@ public class CompoundConfigurationTest | |||||||
|         ConfigurationProvider.getProvider( provider ).save( conf, sw ); |         ConfigurationProvider.getProvider( provider ).save( conf, sw ); | ||||||
|  |  | ||||||
|         // Check nulls were saved, see #1094 |         // Check nulls were saved, see #1094 | ||||||
|         Assert.assertFalse( "Config contains null", sw.toString().contains( "null" ) ); |         assertFalse( sw.toString().contains( "null" ), "Config contains null" ); | ||||||
|  |  | ||||||
|         conf = ConfigurationProvider.getProvider( provider ).load( new StringReader( sw.toString() ) ); |         conf = ConfigurationProvider.getProvider( provider ).load( new StringReader( sw.toString() ) ); | ||||||
|         conf.set( "receipt", "Oz-Ware Purchase Invoice" ); // Add it back |         conf.set( "receipt", "Oz-Ware Purchase Invoice" ); // Add it back | ||||||
| @@ -160,37 +150,38 @@ public class CompoundConfigurationTest | |||||||
|  |  | ||||||
|     private void testSection(Configuration conf) |     private void testSection(Configuration conf) | ||||||
|     { |     { | ||||||
|         Assert.assertEquals( "receipt", "Oz-Ware Purchase Invoice", conf.getString( "receipt" ) ); |         assertEquals( "Oz-Ware Purchase Invoice", conf.getString( "receipt" ), "receipt" ); | ||||||
|         // Assert.assertEquals( "date", "2012-08-06", conf.get( "date" ).toString() ); |         // assertEquals( "2012-08-06", conf.get( "date" ).toString(), "date" ); | ||||||
|  |  | ||||||
|         Configuration customer = conf.getSection( "customer" ); |         Configuration customer = conf.getSection( "customer" ); | ||||||
|         Assert.assertEquals( "customer.given", "Dorothy", customer.getString( "given" ) ); |         assertEquals( "Dorothy", customer.getString( "given" ), "customer.given" ); | ||||||
|         Assert.assertEquals( "customer.given", "Dorothy", conf.getString( "customer.given" ) ); |         assertEquals( "Dorothy", conf.getString( "customer.given" ), "customer.given" ); | ||||||
|  |  | ||||||
|         List items = conf.getList( "items" ); |         List items = conf.getList( "items" ); | ||||||
|         Map item = (Map) items.get( 0 ); |         Map item = (Map) items.get( 0 ); | ||||||
|         Assert.assertEquals( "items[0].part_no", "A4786", item.get( "part_no" ) ); |         assertEquals( "A4786", item.get( "part_no" ), "items[0].part_no" ); | ||||||
|  |  | ||||||
|         conf.set( "receipt", null ); |         conf.set( "receipt", null ); | ||||||
|         Assert.assertEquals( null, conf.get( "receipt" ) ); |         assertEquals( null, conf.get( "receipt" ) ); | ||||||
|         Assert.assertEquals( "foo", conf.get( "receipt", "foo" ) ); |         assertEquals( "foo", conf.get( "receipt", "foo" ) ); | ||||||
|  |  | ||||||
|         Configuration newSection = conf.getSection( "new.section" ); |         Configuration newSection = conf.getSection( "new.section" ); | ||||||
|         newSection.set( "value", "foo" ); |         newSection.set( "value", "foo" ); | ||||||
|         Assert.assertEquals( "foo", conf.get( "new.section.value" ) ); |         assertEquals( "foo", conf.get( "new.section.value" ) ); | ||||||
|  |  | ||||||
|         conf.set( "other.new.section", "bar" ); |         conf.set( "other.new.section", "bar" ); | ||||||
|         Assert.assertEquals( "bar", conf.get( "other.new.section" ) ); |         assertEquals( "bar", conf.get( "other.new.section" ) ); | ||||||
|  |  | ||||||
|         Assert.assertTrue( conf.contains( "customer.given" ) ); |         assertTrue( conf.contains( "customer.given" ) ); | ||||||
|         Assert.assertTrue( customer.contains( "given" ) ); |         assertTrue( customer.contains( "given" ) ); | ||||||
|  |  | ||||||
|         Assert.assertFalse( conf.contains( "customer.foo" ) ); |         assertFalse( conf.contains( "customer.foo" ) ); | ||||||
|         Assert.assertFalse( customer.contains( "foo" ) ); |         assertFalse( customer.contains( "foo" ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @ParameterizedTest | ||||||
|     public void testNumberedKeys() |     @MethodSource("data") | ||||||
|  |     public void testNumberedKeys(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) | ||||||
|     { |     { | ||||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( numberTest ); |         Configuration conf = ConfigurationProvider.getProvider( provider ).load( numberTest ); | ||||||
|  |  | ||||||
| @@ -201,29 +192,31 @@ public class CompoundConfigurationTest | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @ParameterizedTest | ||||||
|     public void testNull() |     @MethodSource("data") | ||||||
|  |     public void testNull(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) | ||||||
|     { |     { | ||||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( nullTest ); |         Configuration conf = ConfigurationProvider.getProvider( provider ).load( nullTest ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( "object", conf.get( "null.null" ) ); |         assertEquals( "object", conf.get( "null.null" ) ); | ||||||
|         Assert.assertEquals( "object", conf.getSection( "null" ).get( "null" ) ); |         assertEquals( "object", conf.getSection( "null" ).get( "null" ) ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( null, conf.get( "null.object" ) ); |         assertEquals( null, conf.get( "null.object" ) ); | ||||||
|         Assert.assertEquals( "", conf.getString( "null.object" ) ); |         assertEquals( "", conf.getString( "null.object" ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @ParameterizedTest | ||||||
|     public void testMapAddition() |     @MethodSource("data") | ||||||
|  |     public void testMapAddition(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) | ||||||
|     { |     { | ||||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( testDocument ); |         Configuration conf = ConfigurationProvider.getProvider( provider ).load( testDocument ); | ||||||
|  |  | ||||||
|         conf.set( "addition", Collections.singletonMap( "foo", "bar" ) ); |         conf.set( "addition", Collections.singletonMap( "foo", "bar" ) ); | ||||||
|  |  | ||||||
|         // Order matters |         // Order matters | ||||||
|         Assert.assertEquals( "bar", conf.getSection( "addition" ).getString( "foo" ) ); |         assertEquals( "bar", conf.getSection( "addition" ).getString( "foo" ) ); | ||||||
|         Assert.assertEquals( "bar", conf.getString( "addition.foo" ) ); |         assertEquals( "bar", conf.getString( "addition.foo" ) ); | ||||||
|  |  | ||||||
|         Assert.assertTrue( conf.get( "addition" ) instanceof Configuration ); |         assertTrue( conf.get( "addition" ) instanceof Configuration ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| package net.md_5.bungee.config; | package net.md_5.bungee.config; | ||||||
|  |  | ||||||
| import org.junit.Assert; | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import org.junit.Test; | import org.junit.jupiter.api.Test; | ||||||
|  |  | ||||||
| public class DefaultConfigurationTest | public class DefaultConfigurationTest | ||||||
| { | { | ||||||
| @@ -16,8 +16,8 @@ public class DefaultConfigurationTest | |||||||
|  |  | ||||||
|         Configuration actualConfig = new Configuration( defaultConfig ); |         Configuration actualConfig = new Configuration( defaultConfig ); | ||||||
|  |  | ||||||
|         Assert.assertEquals( 10, actualConfig.getInt( "setting" ) ); |         assertEquals( 10, actualConfig.getInt( "setting" ) ); | ||||||
|         Assert.assertEquals( 11, actualConfig.getInt( "nested.setting" ) ); |         assertEquals( 11, actualConfig.getInt( "nested.setting" ) ); | ||||||
|         Assert.assertEquals( 12, actualConfig.getInt( "double.nested.setting" ) ); |         assertEquals( 12, actualConfig.getInt( "double.nested.setting" ) ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-event</artifactId> |     <artifactId>bungeecord-event</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Event</name> |     <name>BungeeCord-Event</name> | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package net.md_5.bungee.event; | package net.md_5.bungee.event; | ||||||
|  |  | ||||||
|  | import com.google.common.collect.ImmutableSet; | ||||||
| import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
| import java.text.MessageFormat; | import java.text.MessageFormat; | ||||||
| @@ -41,6 +42,8 @@ public class EventBus | |||||||
|         { |         { | ||||||
|             for ( EventHandlerMethod method : handlers ) |             for ( EventHandlerMethod method : handlers ) | ||||||
|             { |             { | ||||||
|  |                 long start = System.nanoTime(); | ||||||
|  |  | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     method.invoke( event ); |                     method.invoke( event ); | ||||||
| @@ -54,6 +57,15 @@ public class EventBus | |||||||
|                 { |                 { | ||||||
|                     logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() ); |                     logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() ); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 long elapsed = System.nanoTime() - start; | ||||||
|  |                 if ( elapsed > 50000000 ) | ||||||
|  |                 { | ||||||
|  |                     logger.log( Level.WARNING, "Plugin listener {0} took {1}ms to process event {2}!", new Object[] | ||||||
|  |                     { | ||||||
|  |                         method.getListener().getClass().getName(), elapsed / 1000000, event | ||||||
|  |                     } ); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -61,7 +73,8 @@ public class EventBus | |||||||
|     private Map<Class<?>, Map<Byte, Set<Method>>> findHandlers(Object listener) |     private Map<Class<?>, Map<Byte, Set<Method>>> findHandlers(Object listener) | ||||||
|     { |     { | ||||||
|         Map<Class<?>, Map<Byte, Set<Method>>> handler = new HashMap<>(); |         Map<Class<?>, Map<Byte, Set<Method>>> handler = new HashMap<>(); | ||||||
|         for ( Method m : listener.getClass().getDeclaredMethods() ) |         Set<Method> methods = ImmutableSet.<Method>builder().add( listener.getClass().getMethods() ).add( listener.getClass().getDeclaredMethods() ).build(); | ||||||
|  |         for ( final Method m : methods ) | ||||||
|         { |         { | ||||||
|             EventHandler annotation = m.getAnnotation( EventHandler.class ); |             EventHandler annotation = m.getAnnotation( EventHandler.class ); | ||||||
|             if ( annotation != null ) |             if ( annotation != null ) | ||||||
| @@ -75,18 +88,8 @@ public class EventBus | |||||||
|                     } ); |                     } ); | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|                 Map<Byte, Set<Method>> prioritiesMap = handler.get( params[0] ); |                 Map<Byte, Set<Method>> prioritiesMap = handler.computeIfAbsent( params[0], k -> new HashMap<>() ); | ||||||
|                 if ( prioritiesMap == null ) |                 Set<Method> priority = prioritiesMap.computeIfAbsent( annotation.priority(), k -> new HashSet<>() ); | ||||||
|                 { |  | ||||||
|                     prioritiesMap = new HashMap<>(); |  | ||||||
|                     handler.put( params[0], prioritiesMap ); |  | ||||||
|                 } |  | ||||||
|                 Set<Method> priority = prioritiesMap.get( annotation.priority() ); |  | ||||||
|                 if ( priority == null ) |  | ||||||
|                 { |  | ||||||
|                     priority = new HashSet<>(); |  | ||||||
|                     prioritiesMap.put( annotation.priority(), priority ); |  | ||||||
|                 } |  | ||||||
|                 priority.add( m ); |                 priority.add( m ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -101,22 +104,11 @@ public class EventBus | |||||||
|         { |         { | ||||||
|             for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() ) |             for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() ) | ||||||
|             { |             { | ||||||
|                 Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() ); |                 Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.computeIfAbsent( e.getKey(), k -> new HashMap<>() ); | ||||||
|                 if ( prioritiesMap == null ) |  | ||||||
|                 { |  | ||||||
|                     prioritiesMap = new HashMap<>(); |  | ||||||
|                     byListenerAndPriority.put( e.getKey(), prioritiesMap ); |  | ||||||
|                 } |  | ||||||
|                 for ( Map.Entry<Byte, Set<Method>> entry : e.getValue().entrySet() ) |                 for ( Map.Entry<Byte, Set<Method>> entry : e.getValue().entrySet() ) | ||||||
|                 { |                 { | ||||||
|                     Map<Object, Method[]> currentPriorityMap = prioritiesMap.get( entry.getKey() ); |                     Map<Object, Method[]> currentPriorityMap = prioritiesMap.computeIfAbsent( entry.getKey(), k -> new HashMap<>() ); | ||||||
|                     if ( currentPriorityMap == null ) |                     currentPriorityMap.put( listener, entry.getValue().toArray( new Method[ 0 ] ) ); | ||||||
|                     { |  | ||||||
|                         currentPriorityMap = new HashMap<>(); |  | ||||||
|                         prioritiesMap.put( entry.getKey(), currentPriorityMap ); |  | ||||||
|                     } |  | ||||||
|                     Method[] baked = new Method[ entry.getValue().size() ]; |  | ||||||
|                     currentPriorityMap.put( listener, entry.getValue().toArray( baked ) ); |  | ||||||
|                 } |                 } | ||||||
|                 bakeHandlers( e.getKey() ); |                 bakeHandlers( e.getKey() ); | ||||||
|             } |             } | ||||||
| @@ -194,7 +186,7 @@ public class EventBus | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } while ( value++ < Byte.MAX_VALUE ); |             } while ( value++ < Byte.MAX_VALUE ); | ||||||
|             byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) ); |             byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ 0 ] ) ); | ||||||
|         } else |         } else | ||||||
|         { |         { | ||||||
|             byEventBaked.remove( eventClass ); |             byEventBaked.remove( eventClass ); | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| package net.md_5.bungee.event; | package net.md_5.bungee.event; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import java.util.concurrent.CountDownLatch; | import java.util.concurrent.CountDownLatch; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| public class EventBusTest | public class EventBusTest | ||||||
| { | { | ||||||
| @@ -15,14 +15,14 @@ public class EventBusTest | |||||||
|     { |     { | ||||||
|         bus.register( this ); |         bus.register( this ); | ||||||
|         bus.post( new FirstEvent() ); |         bus.post( new FirstEvent() ); | ||||||
|         Assert.assertEquals( 0, latch.getCount() ); |         assertEquals( 0, latch.getCount() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @EventHandler |     @EventHandler | ||||||
|     public void firstListener(FirstEvent event) |     public void firstListener(FirstEvent event) | ||||||
|     { |     { | ||||||
|         bus.post( new SecondEvent() ); |         bus.post( new SecondEvent() ); | ||||||
|         Assert.assertEquals( 1, latch.getCount() ); |         assertEquals( 1, latch.getCount() ); | ||||||
|         latch.countDown(); |         latch.countDown(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| package net.md_5.bungee.event; | package net.md_5.bungee.event; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import java.util.concurrent.CountDownLatch; | import java.util.concurrent.CountDownLatch; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| public class EventPriorityTest | public class EventPriorityTest | ||||||
| { | { | ||||||
| @@ -16,41 +16,41 @@ public class EventPriorityTest | |||||||
|         bus.register( this ); |         bus.register( this ); | ||||||
|         bus.register( new EventPriorityListenerPartner() ); |         bus.register( new EventPriorityListenerPartner() ); | ||||||
|         bus.post( new PriorityTestEvent() ); |         bus.post( new PriorityTestEvent() ); | ||||||
|         Assert.assertEquals( 0, latch.getCount() ); |         assertEquals( 0, latch.getCount() ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @EventHandler(priority = Byte.MIN_VALUE) |     @EventHandler(priority = Byte.MIN_VALUE) | ||||||
|     public void onMinPriority(PriorityTestEvent event) |     public void onMinPriority(PriorityTestEvent event) | ||||||
|     { |     { | ||||||
|         Assert.assertEquals( 7, latch.getCount() ); |         assertEquals( 7, latch.getCount() ); | ||||||
|         latch.countDown(); |         latch.countDown(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @EventHandler(priority = EventPriority.LOWEST) |     @EventHandler(priority = EventPriority.LOWEST) | ||||||
|     public void onLowestPriority(PriorityTestEvent event) |     public void onLowestPriority(PriorityTestEvent event) | ||||||
|     { |     { | ||||||
|         Assert.assertEquals( 6, latch.getCount() ); |         assertEquals( 6, latch.getCount() ); | ||||||
|         latch.countDown(); |         latch.countDown(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @EventHandler |     @EventHandler | ||||||
|     public void onNormalPriority(PriorityTestEvent event) |     public void onNormalPriority(PriorityTestEvent event) | ||||||
|     { |     { | ||||||
|         Assert.assertEquals( 4, latch.getCount() ); |         assertEquals( 4, latch.getCount() ); | ||||||
|         latch.countDown(); |         latch.countDown(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @EventHandler(priority = EventPriority.HIGHEST) |     @EventHandler(priority = EventPriority.HIGHEST) | ||||||
|     public void onHighestPriority(PriorityTestEvent event) |     public void onHighestPriority(PriorityTestEvent event) | ||||||
|     { |     { | ||||||
|         Assert.assertEquals( 2, latch.getCount() ); |         assertEquals( 2, latch.getCount() ); | ||||||
|         latch.countDown(); |         latch.countDown(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @EventHandler(priority = Byte.MAX_VALUE) |     @EventHandler(priority = Byte.MAX_VALUE) | ||||||
|     public void onMaxPriority(PriorityTestEvent event) |     public void onMaxPriority(PriorityTestEvent event) | ||||||
|     { |     { | ||||||
|         Assert.assertEquals( 1, latch.getCount() ); |         assertEquals( 1, latch.getCount() ); | ||||||
|         latch.countDown(); |         latch.countDown(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -64,14 +64,14 @@ public class EventPriorityTest | |||||||
|         @EventHandler(priority = EventPriority.HIGH) |         @EventHandler(priority = EventPriority.HIGH) | ||||||
|         public void onHighPriority(PriorityTestEvent event) |         public void onHighPriority(PriorityTestEvent event) | ||||||
|         { |         { | ||||||
|             Assert.assertEquals( 3, latch.getCount() ); |             assertEquals( 3, latch.getCount() ); | ||||||
|             latch.countDown(); |             latch.countDown(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         @EventHandler(priority = EventPriority.LOW) |         @EventHandler(priority = EventPriority.LOW) | ||||||
|         public void onLowPriority(PriorityTestEvent event) |         public void onLowPriority(PriorityTestEvent event) | ||||||
|         { |         { | ||||||
|             Assert.assertEquals( 5, latch.getCount() ); |             assertEquals( 5, latch.getCount() ); | ||||||
|             latch.countDown(); |             latch.countDown(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								event/src/test/java/net/md_5/bungee/event/SubclassTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								event/src/test/java/net/md_5/bungee/event/SubclassTest.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | package net.md_5.bungee.event; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
|  | import java.util.concurrent.CountDownLatch; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  |  | ||||||
|  | public class SubclassTest extends EventBusTest | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private final CountDownLatch latch = new CountDownLatch( 1 ); | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Override | ||||||
|  |     public void testNestedEvents() | ||||||
|  |     { | ||||||
|  |         super.testNestedEvents(); | ||||||
|  |         assertEquals( 0, latch.getCount() ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @EventHandler | ||||||
|  |     protected void extraListener(FirstEvent event) | ||||||
|  |     { | ||||||
|  |         assertEquals( 1, latch.getCount() ); | ||||||
|  |         latch.countDown(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| package net.md_5.bungee.event; | package net.md_5.bungee.event; | ||||||
|  |  | ||||||
| import org.junit.Assert; | import static org.junit.jupiter.api.Assertions.fail; | ||||||
| import org.junit.Test; | import org.junit.jupiter.api.Test; | ||||||
|  |  | ||||||
| public class UnregisteringListenerTest | public class UnregisteringListenerTest | ||||||
| { | { | ||||||
| @@ -19,7 +19,7 @@ public class UnregisteringListenerTest | |||||||
|     @EventHandler |     @EventHandler | ||||||
|     public void onEvent(TestEvent evt) |     public void onEvent(TestEvent evt) | ||||||
|     { |     { | ||||||
|         Assert.fail( "Event listener wasn't unregistered" ); |         fail( "Event listener wasn't unregistered" ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static class TestEvent |     public static class TestEvent | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-log</artifactId> |     <artifactId>bungeecord-log</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Log</name> |     <name>BungeeCord-Log</name> | ||||||
|   | |||||||
| @@ -13,19 +13,23 @@ public class BungeeLogger extends Logger | |||||||
|  |  | ||||||
|     private final LogDispatcher dispatcher = new LogDispatcher( this ); |     private final LogDispatcher dispatcher = new LogDispatcher( this ); | ||||||
|  |  | ||||||
|  |     // CHECKSTYLE:OFF | ||||||
|     @SuppressWarnings( |     @SuppressWarnings( | ||||||
|             { |             { | ||||||
|                 "CallToPrintStackTrace", "CallToThreadStartDuringObjectConstruction" |                 "CallToPrintStackTrace", "CallToThreadStartDuringObjectConstruction" | ||||||
|             }) |             }) | ||||||
|  |     // CHECKSTYLE:ON | ||||||
|     @SuppressFBWarnings("SC_START_IN_CTOR") |     @SuppressFBWarnings("SC_START_IN_CTOR") | ||||||
|     public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader) |     public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader) | ||||||
|     { |     { | ||||||
|         super( loggerName, null ); |         super( loggerName, null ); | ||||||
|         setLevel( Level.ALL ); |         setLevel( Level.ALL ); | ||||||
|  |         setUseParentHandlers( false ); | ||||||
|  |  | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             FileHandler fileHandler = new FileHandler( filePattern, 1 << 24, 8, true ); |             FileHandler fileHandler = new FileHandler( filePattern, 1 << 24, 8, true ); | ||||||
|  |             fileHandler.setLevel( Level.parse( System.getProperty( "net.md_5.bungee.file-log-level", "INFO" ) ) ); | ||||||
|             fileHandler.setFormatter( new ConciseFormatter( false ) ); |             fileHandler.setFormatter( new ConciseFormatter( false ) ); | ||||||
|             addHandler( fileHandler ); |             addHandler( fileHandler ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | package net.md_5.bungee.log; | ||||||
|  |  | ||||||
|  | import java.util.logging.Handler; | ||||||
|  | import java.util.logging.LogRecord; | ||||||
|  | import java.util.logging.Logger; | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | public class LoggingForwardHandler extends Handler | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private final Logger logger; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void publish(LogRecord record) | ||||||
|  |     { | ||||||
|  |         logger.log( record ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void flush() | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void close() throws SecurityException | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| package net.md_5.bungee.log; | package net.md_5.bungee.log; | ||||||
|  |  | ||||||
| import com.google.common.base.Charsets; |  | ||||||
| import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.logging.Level; | import java.util.logging.Level; | ||||||
| import java.util.logging.Logger; | import java.util.logging.Logger; | ||||||
| import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||||
| @@ -19,7 +19,7 @@ public class LoggingOutputStream extends ByteArrayOutputStream | |||||||
|     @Override |     @Override | ||||||
|     public void flush() throws IOException |     public void flush() throws IOException | ||||||
|     { |     { | ||||||
|         String contents = toString( Charsets.UTF_8.name() ); |         String contents = toString( StandardCharsets.UTF_8.name() ); | ||||||
|         super.reset(); |         super.reset(); | ||||||
|         if ( !contents.isEmpty() && !contents.equals( separator ) ) |         if ( !contents.isEmpty() && !contents.equals( separator ) ) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-module</artifactId> |         <artifactId>bungeecord-module</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module-cmd-alert</artifactId> |     <artifactId>bungeecord-module-cmd-alert</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>cmd_alert</name> |     <name>cmd_alert</name> | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package net.md_5.bungee.module.cmd.alert; | package net.md_5.bungee.module.cmd.alert; | ||||||
|  |  | ||||||
|  | import java.util.Locale; | ||||||
| import net.md_5.bungee.api.ChatColor; | import net.md_5.bungee.api.ChatColor; | ||||||
| import net.md_5.bungee.api.CommandSender; | import net.md_5.bungee.api.CommandSender; | ||||||
| import net.md_5.bungee.api.ProxyServer; | import net.md_5.bungee.api.ProxyServer; | ||||||
| @@ -23,10 +24,10 @@ public class CommandAlert extends Command | |||||||
|         } else |         } else | ||||||
|         { |         { | ||||||
|             StringBuilder builder = new StringBuilder(); |             StringBuilder builder = new StringBuilder(); | ||||||
|             if ( args[0].startsWith( "&h" ) ) |             if ( args[0].toLowerCase( Locale.ROOT ).startsWith( "&h" ) ) | ||||||
|             { |             { | ||||||
|                 // Remove &h |                 // Remove &h | ||||||
|                 args[0] = args[0].substring( 2, args[0].length() ); |                 args[0] = args[0].substring( 2 ); | ||||||
|             } else |             } else | ||||||
|             { |             { | ||||||
|                 builder.append( ProxyServer.getInstance().getTranslation( "alert" ) ); |                 builder.append( ProxyServer.getInstance().getTranslation( "alert" ) ); | ||||||
| @@ -40,7 +41,7 @@ public class CommandAlert extends Command | |||||||
|  |  | ||||||
|             String message = builder.substring( 0, builder.length() - 1 ); |             String message = builder.substring( 0, builder.length() - 1 ); | ||||||
|  |  | ||||||
|             ProxyServer.getInstance().broadcast( TextComponent.fromLegacyText( message ) ); |             ProxyServer.getInstance().broadcast( TextComponent.fromLegacy( message ) ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-module</artifactId> |         <artifactId>bungeecord-module</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module-cmd-find</artifactId> |     <artifactId>bungeecord-module-cmd-find</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>cmd_find</name> |     <name>cmd_find</name> | ||||||
|   | |||||||
| @@ -1,7 +1,12 @@ | |||||||
| package net.md_5.bungee.module.cmd.find; | package net.md_5.bungee.module.cmd.find; | ||||||
|  |  | ||||||
|  | import java.util.Collections; | ||||||
| import net.md_5.bungee.api.CommandSender; | import net.md_5.bungee.api.CommandSender; | ||||||
| import net.md_5.bungee.api.ProxyServer; | import net.md_5.bungee.api.ProxyServer; | ||||||
|  | import net.md_5.bungee.api.chat.ClickEvent; | ||||||
|  | import net.md_5.bungee.api.chat.ComponentBuilder; | ||||||
|  | import net.md_5.bungee.api.chat.HoverEvent; | ||||||
|  | import net.md_5.bungee.api.config.ServerInfo; | ||||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
| import net.md_5.bungee.command.PlayerCommand; | import net.md_5.bungee.command.PlayerCommand; | ||||||
|  |  | ||||||
| @@ -27,8 +32,27 @@ public class CommandFind extends PlayerCommand | |||||||
|                 sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_not_online" ) ); |                 sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_not_online" ) ); | ||||||
|             } else |             } else | ||||||
|             { |             { | ||||||
|                 sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_online_at", player.getName(), player.getServer().getInfo().getName() ) ); |                 boolean moduleLoaded = ProxyServer.getInstance().getPluginManager().getPlugin( "cmd_server" ) != null; | ||||||
|  |                 ServerInfo server = player.getServer().getInfo(); | ||||||
|  |                 ComponentBuilder componentBuilder = new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "user_online_at", player.getName(), server.getName() ) ); | ||||||
|  |  | ||||||
|  |                 if ( moduleLoaded && server.canAccess( sender ) ) | ||||||
|  |                 { | ||||||
|  |                     componentBuilder.event( new HoverEvent( | ||||||
|  |                             HoverEvent.Action.SHOW_TEXT, | ||||||
|  |                             new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() ) | ||||||
|  |                     ); | ||||||
|  |                     componentBuilder.event( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) ); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 sender.sendMessage( componentBuilder.create() ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Iterable<String> onTabComplete(CommandSender sender, String[] args) | ||||||
|  |     { | ||||||
|  |         return args.length == 1 ? super.onTabComplete( sender, args ) : Collections.emptyList(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								module/cmd-kick/nb-configuration.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								module/cmd-kick/nb-configuration.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project-shared-configuration> | ||||||
|  |     <!-- | ||||||
|  |     This file contains additional configuration written by modules in the NetBeans IDE. | ||||||
|  |     The configuration is intended to be shared among all the users of project and | ||||||
|  |     therefore it is assumed to be part of version control checkout. | ||||||
|  |     Without this configuration present, some functionality in the IDE may be limited or fail altogether. | ||||||
|  |     --> | ||||||
|  |     <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1"> | ||||||
|  |         <!-- | ||||||
|  |         Properties that influence various parts of the IDE, especially code formatting and the like.  | ||||||
|  |         You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up. | ||||||
|  |         That way multiple projects can share the same settings (useful for formatting rules for example). | ||||||
|  |         Any value defined here will override the pom.xml file value but is only applicable to the current project. | ||||||
|  |         --> | ||||||
|  |         <org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement>NEW_LINE</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinMethodCallParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSwitchParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinCatchParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinTryParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinSynchronizedParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinArrayInitBrackets> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinWhileParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinIfParens> | ||||||
|  |         <org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceWithinForParens> | ||||||
|  |     </properties> | ||||||
|  | </project-shared-configuration> | ||||||
							
								
								
									
										20
									
								
								module/cmd-kick/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								module/cmd-kick/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  |  | ||||||
|  | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|  |          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||||
|  |     <modelVersion>4.0.0</modelVersion> | ||||||
|  |  | ||||||
|  |     <parent> | ||||||
|  |         <groupId>net.md-5</groupId> | ||||||
|  |         <artifactId>bungeecord-module</artifactId> | ||||||
|  |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|  |         <relativePath>../pom.xml</relativePath> | ||||||
|  |     </parent> | ||||||
|  |  | ||||||
|  |     <groupId>net.md-5</groupId> | ||||||
|  |     <artifactId>bungeecord-module-cmd-kick</artifactId> | ||||||
|  |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|  |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|  |     <name>cmd_kick</name> | ||||||
|  |     <description>Provides the gkick command</description> | ||||||
|  | </project> | ||||||
| @@ -0,0 +1,72 @@ | |||||||
|  | package net.md_5.bungee.module.cmd.kick; | ||||||
|  |  | ||||||
|  | import com.google.common.base.Joiner; | ||||||
|  | import com.google.common.collect.ImmutableSet; | ||||||
|  | import java.util.HashSet; | ||||||
|  | import java.util.Locale; | ||||||
|  | import java.util.Set; | ||||||
|  | import net.md_5.bungee.api.ChatColor; | ||||||
|  | import net.md_5.bungee.api.CommandSender; | ||||||
|  | import net.md_5.bungee.api.ProxyServer; | ||||||
|  | import net.md_5.bungee.api.chat.TextComponent; | ||||||
|  | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
|  | import net.md_5.bungee.api.plugin.Command; | ||||||
|  | import net.md_5.bungee.api.plugin.TabExecutor; | ||||||
|  |  | ||||||
|  | public class CommandKick extends Command implements TabExecutor | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     public CommandKick() | ||||||
|  |     { | ||||||
|  |         super( "gkick", "bungeecord.command.kick" ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void execute(CommandSender sender, String[] args) | ||||||
|  |     { | ||||||
|  |         if ( args.length == 0 ) | ||||||
|  |         { | ||||||
|  |             sender.sendMessage( ProxyServer.getInstance().getTranslation( "username_needed" ) ); | ||||||
|  |         } else | ||||||
|  |         { | ||||||
|  |             ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] ); | ||||||
|  |  | ||||||
|  |             if ( player == null ) | ||||||
|  |             { | ||||||
|  |                 sender.sendMessage( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "user_not_online" ) ) ); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if ( args.length == 1 ) | ||||||
|  |             { | ||||||
|  |                 player.disconnect( TextComponent.fromLegacy( ProxyServer.getInstance().getTranslation( "kick_message" ) ) ); | ||||||
|  |             } else | ||||||
|  |             { | ||||||
|  |                 String[] reason = new String[ args.length - 1 ]; | ||||||
|  |                 System.arraycopy( args, 1, reason, 0, reason.length ); | ||||||
|  |                 player.disconnect( TextComponent.fromLegacy( ChatColor.translateAlternateColorCodes( '&', Joiner.on( ' ' ).join( reason ) ) ) ); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Iterable<String> onTabComplete(CommandSender sender, String[] args) | ||||||
|  |     { | ||||||
|  |         if ( args.length == 1 ) | ||||||
|  |         { | ||||||
|  |             Set<String> matches = new HashSet<>(); | ||||||
|  |             String search = args[0].toLowerCase( Locale.ROOT ); | ||||||
|  |             for ( ProxiedPlayer player : ProxyServer.getInstance().getPlayers() ) | ||||||
|  |             { | ||||||
|  |                 if ( player.getName().toLowerCase( Locale.ROOT ).startsWith( search ) ) | ||||||
|  |                 { | ||||||
|  |                     matches.add( player.getName() ); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return matches; | ||||||
|  |         } else | ||||||
|  |         { | ||||||
|  |             return ImmutableSet.of(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package net.md_5.bungee.module.cmd.kick; | ||||||
|  |  | ||||||
|  | import net.md_5.bungee.api.plugin.Plugin; | ||||||
|  |  | ||||||
|  | public class PluginKick extends Plugin | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onEnable() | ||||||
|  |     { | ||||||
|  |         getProxy().getPluginManager().registerCommand( this, new CommandKick() ); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								module/cmd-kick/src/main/resources/plugin.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								module/cmd-kick/src/main/resources/plugin.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | name: ${project.name} | ||||||
|  | main: net.md_5.bungee.module.cmd.kick.PluginKick | ||||||
|  | version: ${describe} | ||||||
|  | description: ${project.description} | ||||||
|  | author: ${module.author} | ||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-module</artifactId> |         <artifactId>bungeecord-module</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module-cmd-list</artifactId> |     <artifactId>bungeecord-module-cmd-list</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>cmd_list</name> |     <name>cmd_list</name> | ||||||
|   | |||||||
| @@ -1,20 +1,26 @@ | |||||||
| package net.md_5.bungee.module.cmd.list; | package net.md_5.bungee.module.cmd.list; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import net.md_5.bungee.Util; |  | ||||||
| import net.md_5.bungee.api.ChatColor; | import net.md_5.bungee.api.ChatColor; | ||||||
| import net.md_5.bungee.api.CommandSender; | import net.md_5.bungee.api.CommandSender; | ||||||
| import net.md_5.bungee.api.ProxyServer; | import net.md_5.bungee.api.ProxyServer; | ||||||
|  | import net.md_5.bungee.api.chat.BaseComponent; | ||||||
|  | import net.md_5.bungee.api.chat.ClickEvent; | ||||||
|  | import net.md_5.bungee.api.chat.ComponentBuilder; | ||||||
|  | import net.md_5.bungee.api.chat.HoverEvent; | ||||||
|  | import net.md_5.bungee.api.chat.hover.content.Text; | ||||||
| import net.md_5.bungee.api.config.ServerInfo; | import net.md_5.bungee.api.config.ServerInfo; | ||||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||||
| import net.md_5.bungee.api.plugin.Command; | import net.md_5.bungee.api.plugin.Command; | ||||||
|  | import net.md_5.bungee.api.plugin.TabExecutor; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Command to list all players connected to the proxy. |  * Command to list all players connected to the proxy. | ||||||
|  */ |  */ | ||||||
| public class CommandList extends Command | public class CommandList extends Command implements TabExecutor | ||||||
| { | { | ||||||
|  |  | ||||||
|     public CommandList() |     public CommandList() | ||||||
| @@ -25,6 +31,9 @@ public class CommandList extends Command | |||||||
|     @Override |     @Override | ||||||
|     public void execute(CommandSender sender, String[] args) |     public void execute(CommandSender sender, String[] args) | ||||||
|     { |     { | ||||||
|  |         boolean hideEmptyServers = ( args.length == 0 ) || !args[0].equalsIgnoreCase( "all" ); | ||||||
|  |         boolean moduleLoaded = ProxyServer.getInstance().getPluginManager().getPlugin( "cmd_server" ) != null; | ||||||
|  |  | ||||||
|         for ( ServerInfo server : ProxyServer.getInstance().getServers().values() ) |         for ( ServerInfo server : ProxyServer.getInstance().getServers().values() ) | ||||||
|         { |         { | ||||||
|             if ( !server.canAccess( sender ) ) |             if ( !server.canAccess( sender ) ) | ||||||
| @@ -32,16 +41,39 @@ public class CommandList extends Command | |||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             Collection<ProxiedPlayer> serverPlayers = server.getPlayers(); | ||||||
|  |             if ( hideEmptyServers && serverPlayers.isEmpty() ) | ||||||
|  |             { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             List<String> players = new ArrayList<>(); |             List<String> players = new ArrayList<>(); | ||||||
|             for ( ProxiedPlayer player : server.getPlayers() ) |             for ( ProxiedPlayer player : serverPlayers ) | ||||||
|             { |             { | ||||||
|                 players.add( player.getDisplayName() ); |                 players.add( player.getDisplayName() ); | ||||||
|             } |             } | ||||||
|             Collections.sort( players, String.CASE_INSENSITIVE_ORDER ); |             Collections.sort( players, String.CASE_INSENSITIVE_ORDER ); | ||||||
|  |  | ||||||
|             sender.sendMessage( ProxyServer.getInstance().getTranslation( "command_list", server.getName(), server.getPlayers().size(), Util.format( players, ChatColor.RESET + ", " ) ) ); |             BaseComponent baseComponent = new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "command_list", server.getName(), players.size(), String.join( ChatColor.RESET + ", ", players ) ) ).build(); | ||||||
|  |  | ||||||
|  |             if ( moduleLoaded ) | ||||||
|  |             { | ||||||
|  |                 baseComponent.setHoverEvent( new HoverEvent( | ||||||
|  |                         HoverEvent.Action.SHOW_TEXT, | ||||||
|  |                         new Text( new ComponentBuilder().appendLegacy( ProxyServer.getInstance().getTranslation( "click_to_connect" ) ).create() ) ) | ||||||
|  |                 ); | ||||||
|  |                 baseComponent.setClickEvent( new ClickEvent( ClickEvent.Action.RUN_COMMAND, "/server " + server.getName() ) ); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             sender.sendMessage( baseComponent ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players", ProxyServer.getInstance().getOnlineCount() ) ); |         sender.sendMessage( ProxyServer.getInstance().getTranslation( "total_players", ProxyServer.getInstance().getOnlineCount() ) ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Iterable<String> onTabComplete(CommandSender sender, String[] args) | ||||||
|  |     { | ||||||
|  |         return ( args.length > 1 ) ? Collections.emptyList() : Collections.singletonList( "all" ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-module</artifactId> |         <artifactId>bungeecord-module</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module-cmd-send</artifactId> |     <artifactId>bungeecord-module-cmd-send</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>cmd_send</name> |     <name>cmd_send</name> | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ public class CommandSend extends Command implements TabExecutor | |||||||
|             this.sender = sender; |             this.sender = sender; | ||||||
|             for ( ServerConnectRequest.Result result : ServerConnectRequest.Result.values() ) |             for ( ServerConnectRequest.Result result : ServerConnectRequest.Result.values() ) | ||||||
|             { |             { | ||||||
|                 results.put( result, new ArrayList<String>() ); |                 results.put( result, Collections.synchronizedList( new ArrayList<>() ) ); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-module</artifactId> |         <artifactId>bungeecord-module</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module-cmd-server</artifactId> |     <artifactId>bungeecord-module-cmd-server</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>cmd_server</name> |     <name>cmd_server</name> | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module</artifactId> |     <artifactId>bungeecord-module</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>pom</packaging> |     <packaging>pom</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord Modules</name> |     <name>BungeeCord Modules</name> | ||||||
| @@ -21,6 +21,7 @@ | |||||||
|     <modules> |     <modules> | ||||||
|         <module>cmd-alert</module> |         <module>cmd-alert</module> | ||||||
|         <module>cmd-find</module> |         <module>cmd-find</module> | ||||||
|  |         <module>cmd-kick</module> | ||||||
|         <module>cmd-list</module> |         <module>cmd-list</module> | ||||||
|         <module>cmd-send</module> |         <module>cmd-send</module> | ||||||
|         <module>cmd-server</module> |         <module>cmd-server</module> | ||||||
|   | |||||||
| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-module</artifactId> |         <artifactId>bungeecord-module</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-module-reconnect-yaml</artifactId> |     <artifactId>bungeecord-module-reconnect-yaml</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>reconnect_yaml</name> |     <name>reconnect_yaml</name> | ||||||
|   | |||||||
| @@ -1,6 +1,14 @@ | |||||||
| #!/bin/sh | #!/bin/sh | ||||||
|  |  | ||||||
|  | set -eu | ||||||
|  |  | ||||||
|  | echo "Compiling mbedtls" | ||||||
|  | (cd mbedtls && make no_test) | ||||||
|  |  | ||||||
|  | echo "Compiling zlib" | ||||||
|  | (cd zlib && CFLAGS=-fPIC ./configure --static && make) | ||||||
|  |  | ||||||
| CXX="g++ -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" | CXX="g++ -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" | ||||||
|  |  | ||||||
| $CXX src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so -lcrypto | $CXX -Imbedtls/include src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so mbedtls/library/libmbedcrypto.a | ||||||
| $CXX src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so -lz | $CXX -Izlib src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so zlib/libz.a | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								native/mbedtls
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								native/mbedtls
									
									
									
									
									
										Submodule
									
								
							 Submodule native/mbedtls added at 2ca6c285a0
									
								
							| @@ -6,13 +6,13 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-native</artifactId> |     <artifactId>bungeecord-native</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Native</name> |     <name>BungeeCord-Native</name> | ||||||
| @@ -22,7 +22,6 @@ | |||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>io.netty</groupId> |             <groupId>io.netty</groupId> | ||||||
|             <artifactId>netty-transport</artifactId> |             <artifactId>netty-transport</artifactId> | ||||||
|             <version>${netty.version}</version> |  | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|     </dependencies> |     </dependencies> | ||||||
|   | |||||||
| @@ -1,6 +1,3 @@ | |||||||
| // Support for CentOS 6 |  | ||||||
| __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); |  | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
| #include <zlib.h> | #include <zlib.h> | ||||||
| #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" | #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,18 +6,19 @@ import java.io.FileOutputStream; | |||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.io.InputStream; | import java.io.InputStream; | ||||||
| import java.io.OutputStream; | import java.io.OutputStream; | ||||||
|  | import java.util.function.Supplier; | ||||||
| import net.md_5.bungee.jni.cipher.BungeeCipher; | import net.md_5.bungee.jni.cipher.BungeeCipher; | ||||||
|  |  | ||||||
| public final class NativeCode<T> | public final class NativeCode<T> | ||||||
| { | { | ||||||
|  |  | ||||||
|     private final String name; |     private final String name; | ||||||
|     private final Class<? extends T> javaImpl; |     private final Supplier<? extends T> javaImpl; | ||||||
|     private final Class<? extends T> nativeImpl; |     private final Supplier<? extends T> nativeImpl; | ||||||
|     // |     // | ||||||
|     private boolean loaded; |     private boolean loaded; | ||||||
|  |  | ||||||
|     public NativeCode(String name, Class<? extends T> javaImpl, Class<? extends T> nativeImpl) |     public NativeCode(String name, Supplier<? extends T> javaImpl, Supplier<? extends T> nativeImpl) | ||||||
|     { |     { | ||||||
|         this.name = name; |         this.name = name; | ||||||
|         this.javaImpl = javaImpl; |         this.javaImpl = javaImpl; | ||||||
| @@ -26,13 +27,7 @@ public final class NativeCode<T> | |||||||
|  |  | ||||||
|     public T newInstance() |     public T newInstance() | ||||||
|     { |     { | ||||||
|         try |         return ( loaded ) ? nativeImpl.get() : javaImpl.get(); | ||||||
|         { |  | ||||||
|             return ( loaded ) ? nativeImpl.getDeclaredConstructor().newInstance() : javaImpl.getDeclaredConstructor().newInstance(); |  | ||||||
|         } catch ( ReflectiveOperationException ex ) |  | ||||||
|         { |  | ||||||
|             throw new RuntimeException( "Error getting instance", ex ); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public boolean load() |     public boolean load() | ||||||
|   | |||||||
| @@ -25,9 +25,15 @@ public class JavaCipher implements BungeeCipher | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public JavaCipher() throws GeneralSecurityException |     public JavaCipher() | ||||||
|     { |     { | ||||||
|         this.cipher = Cipher.getInstance( "AES/CFB8/NoPadding" ); |         try | ||||||
|  |         { | ||||||
|  |             this.cipher = Cipher.getInstance( "AES/CFB8/NoPadding" ); | ||||||
|  |         } catch ( GeneralSecurityException ex ) | ||||||
|  |         { | ||||||
|  |             throw new RuntimeException( ex ); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,5 +1,6 @@ | |||||||
| package net.md_5.bungee; | package net.md_5.bungee; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import io.netty.buffer.ByteBuf; | import io.netty.buffer.ByteBuf; | ||||||
| import io.netty.buffer.Unpooled; | import io.netty.buffer.Unpooled; | ||||||
| import java.util.Random; | import java.util.Random; | ||||||
| @@ -9,12 +10,11 @@ import net.md_5.bungee.jni.NativeCode; | |||||||
| import net.md_5.bungee.jni.cipher.BungeeCipher; | import net.md_5.bungee.jni.cipher.BungeeCipher; | ||||||
| import net.md_5.bungee.jni.cipher.JavaCipher; | import net.md_5.bungee.jni.cipher.JavaCipher; | ||||||
| import net.md_5.bungee.jni.cipher.NativeCipher; | import net.md_5.bungee.jni.cipher.NativeCipher; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.MethodOrderer; | ||||||
| import org.junit.FixMethodOrder; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; | import org.junit.jupiter.api.TestMethodOrder; | ||||||
| import org.junit.runners.MethodSorters; |  | ||||||
|  |  | ||||||
| @FixMethodOrder(MethodSorters.NAME_ASCENDING) | @TestMethodOrder(MethodOrderer.MethodName.class) | ||||||
| public class NativeCipherTest | public class NativeCipherTest | ||||||
| { | { | ||||||
|  |  | ||||||
| @@ -26,7 +26,7 @@ public class NativeCipherTest | |||||||
|     private final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" ); |     private final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" ); | ||||||
|     private static final int BENCHMARK_COUNT = 4096; |     private static final int BENCHMARK_COUNT = 4096; | ||||||
|     // |     // | ||||||
|     private static final NativeCode<BungeeCipher> factory = new NativeCode<>( "native-cipher", JavaCipher.class, NativeCipher.class ); |     private static final NativeCode<BungeeCipher> factory = new NativeCode<>( "native-cipher", JavaCipher::new, NativeCipher::new ); | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testNative() throws Exception |     public void testNative() throws Exception | ||||||
| @@ -34,7 +34,7 @@ public class NativeCipherTest | |||||||
|         if ( NativeCode.isSupported() ) |         if ( NativeCode.isSupported() ) | ||||||
|         { |         { | ||||||
|             boolean loaded = factory.load(); |             boolean loaded = factory.load(); | ||||||
|             Assert.assertTrue( "Native cipher failed to load!", loaded ); |             assertTrue( loaded, "Native cipher failed to load!" ); | ||||||
|  |  | ||||||
|             NativeCipher cipher = new NativeCipher(); |             NativeCipher cipher = new NativeCipher(); | ||||||
|             System.out.println( "Testing native cipher..." ); |             System.out.println( "Testing native cipher..." ); | ||||||
| @@ -48,7 +48,7 @@ public class NativeCipherTest | |||||||
|         if ( NativeCode.isSupported() ) |         if ( NativeCode.isSupported() ) | ||||||
|         { |         { | ||||||
|             boolean loaded = factory.load(); |             boolean loaded = factory.load(); | ||||||
|             Assert.assertTrue( "Native cipher failed to load!", loaded ); |             assertTrue( loaded, "Native cipher failed to load!" ); | ||||||
|  |  | ||||||
|             NativeCipher cipher = new NativeCipher(); |             NativeCipher cipher = new NativeCipher(); | ||||||
|  |  | ||||||
| @@ -98,7 +98,7 @@ public class NativeCipherTest | |||||||
|         // Encrypt |         // Encrypt | ||||||
|         cipher.init( true, secret ); |         cipher.init( true, secret ); | ||||||
|         cipher.cipher( nativePlain, out ); |         cipher.cipher( nativePlain, out ); | ||||||
|         Assert.assertEquals( nativeCiphered, out ); |         assertEquals( nativeCiphered, out ); | ||||||
|  |  | ||||||
|         out.clear(); |         out.clear(); | ||||||
|  |  | ||||||
| @@ -106,7 +106,7 @@ public class NativeCipherTest | |||||||
|         cipher.init( false, secret ); |         cipher.init( false, secret ); | ||||||
|         cipher.cipher( nativeCiphered, out ); |         cipher.cipher( nativeCiphered, out ); | ||||||
|         nativePlain.resetReaderIndex(); |         nativePlain.resetReaderIndex(); | ||||||
|         Assert.assertEquals( nativePlain, out ); |         assertEquals( nativePlain, out ); | ||||||
|  |  | ||||||
|         System.out.println( "This cipher works correctly!" ); |         System.out.println( "This cipher works correctly!" ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package net.md_5.bungee; | package net.md_5.bungee; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import io.netty.buffer.ByteBuf; | import io.netty.buffer.ByteBuf; | ||||||
| import io.netty.buffer.Unpooled; | import io.netty.buffer.Unpooled; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| @@ -9,20 +10,19 @@ import net.md_5.bungee.jni.NativeCode; | |||||||
| import net.md_5.bungee.jni.zlib.BungeeZlib; | import net.md_5.bungee.jni.zlib.BungeeZlib; | ||||||
| import net.md_5.bungee.jni.zlib.JavaZlib; | import net.md_5.bungee.jni.zlib.JavaZlib; | ||||||
| import net.md_5.bungee.jni.zlib.NativeZlib; | import net.md_5.bungee.jni.zlib.NativeZlib; | ||||||
| import org.junit.Assert; | import org.junit.jupiter.api.Test; | ||||||
| import org.junit.Test; |  | ||||||
|  |  | ||||||
| public class NativeZlibTest | public class NativeZlibTest | ||||||
| { | { | ||||||
|  |  | ||||||
|     private final NativeCode<BungeeZlib> factory = new NativeCode<>( "native-compress", JavaZlib.class, NativeZlib.class ); |     private final NativeCode<BungeeZlib> factory = new NativeCode<>( "native-compress", JavaZlib::new, NativeZlib::new ); | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void doTest() throws DataFormatException |     public void doTest() throws DataFormatException | ||||||
|     { |     { | ||||||
|         if ( NativeCode.isSupported() ) |         if ( NativeCode.isSupported() ) | ||||||
|         { |         { | ||||||
|             Assert.assertTrue( "Native code failed to load!", factory.load() ); |             assertTrue( factory.load(), "Native code failed to load!" ); | ||||||
|             test( factory.newInstance() ); |             test( factory.newInstance() ); | ||||||
|         } |         } | ||||||
|         test( new JavaZlib() ); |         test( new JavaZlib() ); | ||||||
| @@ -64,6 +64,6 @@ public class NativeZlibTest | |||||||
|         long elapsed = System.currentTimeMillis() - start; |         long elapsed = System.currentTimeMillis() - start; | ||||||
|         System.out.println( "Took: " + elapsed + "ms" ); |         System.out.println( "Took: " + elapsed + "ms" ); | ||||||
|  |  | ||||||
|         Assert.assertTrue( "Results do not match", Arrays.equals( dataBuf, check ) ); |         assertTrue( Arrays.equals( dataBuf, check ), "Results do not match" ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								native/zlib
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								native/zlib
									
									
									
									
									
										Submodule
									
								
							 Submodule native/zlib added at 92530568d2
									
								
							
							
								
								
									
										140
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -5,7 +5,7 @@ | |||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-parent</artifactId> |     <artifactId>bungeecord-parent</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>pom</packaging> |     <packaging>pom</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Parent</name> |     <name>BungeeCord-Parent</name> | ||||||
| @@ -41,6 +41,7 @@ | |||||||
|         <module>protocol</module> |         <module>protocol</module> | ||||||
|         <module>proxy</module> |         <module>proxy</module> | ||||||
|         <module>query</module> |         <module>query</module> | ||||||
|  |         <module>slf4j</module> | ||||||
|         <module>native</module> |         <module>native</module> | ||||||
|     </modules> |     </modules> | ||||||
|  |  | ||||||
| @@ -71,23 +72,34 @@ | |||||||
|  |  | ||||||
|     <properties> |     <properties> | ||||||
|         <build.number>unknown</build.number> |         <build.number>unknown</build.number> | ||||||
|         <netty.version>4.1.53.Final</netty.version> |         <lombok.version>1.18.32</lombok.version> | ||||||
|         <maven.compiler.source>1.8</maven.compiler.source> |         <maven.compiler.source>1.8</maven.compiler.source> | ||||||
|         <maven.compiler.target>1.8</maven.compiler.target> |         <maven.compiler.target>1.8</maven.compiler.target> | ||||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||||
|     </properties> |     </properties> | ||||||
|  |  | ||||||
|  |     <dependencyManagement> | ||||||
|  |         <dependencies> | ||||||
|  |             <dependency> | ||||||
|  |                 <groupId>io.netty</groupId> | ||||||
|  |                 <artifactId>netty-bom</artifactId> | ||||||
|  |                 <version>4.1.109.Final</version> | ||||||
|  |                 <type>pom</type> | ||||||
|  |                 <scope>import</scope> | ||||||
|  |             </dependency> | ||||||
|  |         </dependencies> | ||||||
|  |     </dependencyManagement> | ||||||
|     <dependencies> |     <dependencies> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>junit</groupId> |             <groupId>org.junit.jupiter</groupId> | ||||||
|             <artifactId>junit</artifactId> |             <artifactId>junit-jupiter</artifactId> | ||||||
|             <version>4.13.1</version> |             <version>5.10.2</version> | ||||||
|             <scope>test</scope> |             <scope>test</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>com.google.guava</groupId> |             <groupId>com.google.guava</groupId> | ||||||
|             <artifactId>guava</artifactId> |             <artifactId>guava</artifactId> | ||||||
|             <version>21.0</version> |             <version>32.1.2-jre</version> | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
| @@ -96,20 +108,35 @@ | |||||||
|             <version>3.0.1</version> |             <version>3.0.1</version> | ||||||
|             <scope>provided</scope> |             <scope>provided</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.jetbrains</groupId> | ||||||
|  |             <artifactId>annotations-java5</artifactId> | ||||||
|  |             <version>24.1.0</version> | ||||||
|  |             <scope>provided</scope> | ||||||
|  |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>org.projectlombok</groupId> |             <groupId>org.projectlombok</groupId> | ||||||
|             <artifactId>lombok</artifactId> |             <artifactId>lombok</artifactId> | ||||||
|             <version>1.18.10</version> |             <version>${lombok.version}</version> | ||||||
|             <scope>provided</scope> |             <scope>provided</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|     </dependencies> |     </dependencies> | ||||||
|  |  | ||||||
|     <build> |     <build> | ||||||
|  |         <pluginManagement> | ||||||
|  |             <plugins> | ||||||
|  |                 <plugin> | ||||||
|  |                     <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |                     <artifactId>maven-javadoc-plugin</artifactId> | ||||||
|  |                     <version>3.6.3</version> | ||||||
|  |                 </plugin> | ||||||
|  |             </plugins> | ||||||
|  |         </pluginManagement> | ||||||
|         <plugins> |         <plugins> | ||||||
|             <plugin> |             <plugin> | ||||||
|                 <groupId>net.md-5</groupId> |                 <groupId>net.md-5</groupId> | ||||||
|                 <artifactId>scriptus</artifactId> |                 <artifactId>scriptus</artifactId> | ||||||
|                 <version>0.4.1</version> |                 <version>0.5.0</version> | ||||||
|                 <configuration> |                 <configuration> | ||||||
|                     <format>git:${project.name}:${project.version}:%s:${build.number}</format> |                     <format>git:${project.name}:${project.version}:%s:${build.number}</format> | ||||||
|                 </configuration> |                 </configuration> | ||||||
| @@ -122,10 +149,15 @@ | |||||||
|                     </execution> |                     </execution> | ||||||
|                 </executions> |                 </executions> | ||||||
|             </plugin> |             </plugin> | ||||||
|  |             <plugin> | ||||||
|  |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |                 <artifactId>maven-compiler-plugin</artifactId> | ||||||
|  |                 <version>3.13.0</version> | ||||||
|  |             </plugin> | ||||||
|             <plugin> |             <plugin> | ||||||
|                 <groupId>org.apache.maven.plugins</groupId> |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|                 <artifactId>maven-checkstyle-plugin</artifactId> |                 <artifactId>maven-checkstyle-plugin</artifactId> | ||||||
|                 <version>3.1.1</version> |                 <version>3.3.1</version> | ||||||
|                 <executions> |                 <executions> | ||||||
|                     <execution> |                     <execution> | ||||||
|                         <phase>process-classes</phase> |                         <phase>process-classes</phase> | ||||||
| @@ -143,14 +175,14 @@ | |||||||
|                     <dependency> |                     <dependency> | ||||||
|                         <groupId>com.puppycrawl.tools</groupId> |                         <groupId>com.puppycrawl.tools</groupId> | ||||||
|                         <artifactId>checkstyle</artifactId> |                         <artifactId>checkstyle</artifactId> | ||||||
|                         <version>8.36.2</version> |                         <version>8.45.1</version> | ||||||
|                     </dependency> |                     </dependency> | ||||||
|                 </dependencies> |                 </dependencies> | ||||||
|             </plugin> |             </plugin> | ||||||
|             <plugin> |             <plugin> | ||||||
|                 <groupId>org.codehaus.mojo</groupId> |                 <groupId>org.codehaus.mojo</groupId> | ||||||
|                 <artifactId>animal-sniffer-maven-plugin</artifactId> |                 <artifactId>animal-sniffer-maven-plugin</artifactId> | ||||||
|                 <version>1.19</version> |                 <version>1.23</version> | ||||||
|                 <executions> |                 <executions> | ||||||
|                     <execution> |                     <execution> | ||||||
|                         <phase>process-classes</phase> |                         <phase>process-classes</phase> | ||||||
| @@ -167,10 +199,82 @@ | |||||||
|                     </signature> |                     </signature> | ||||||
|                 </configuration> |                 </configuration> | ||||||
|             </plugin> |             </plugin> | ||||||
|  |             <plugin> | ||||||
|  |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |                 <artifactId>maven-enforcer-plugin</artifactId> | ||||||
|  |                 <version>3.4.1</version> | ||||||
|  |                 <executions> | ||||||
|  |                     <execution> | ||||||
|  |                         <id>enforce</id> | ||||||
|  |                         <configuration> | ||||||
|  |                             <rules> | ||||||
|  |                                 <dependencyConvergence> | ||||||
|  |                                     <excludes> | ||||||
|  |                                         <!-- org.apache.maven:maven-resolver-provider is inconsistent --> | ||||||
|  |                                         <exclude>org.apache.commons:commons-lang3</exclude> | ||||||
|  |                                         <!-- org.apache.maven:maven-resolver-transport-http is inconsistent --> | ||||||
|  |                                         <exclude>org.apache.httpcomponents:httpcore</exclude> | ||||||
|  |                                     </excludes> | ||||||
|  |                                 </dependencyConvergence> | ||||||
|  |                             </rules> | ||||||
|  |                         </configuration> | ||||||
|  |                         <goals> | ||||||
|  |                             <!--<goal>enforce</goal>--> <!-- Disabled until maven-resolver is upgraded again. --> | ||||||
|  |                         </goals> | ||||||
|  |                     </execution> | ||||||
|  |                 </executions> | ||||||
|  |             </plugin> | ||||||
|         </plugins> |         </plugins> | ||||||
|     </build> |     </build> | ||||||
|  |  | ||||||
|     <profiles> |     <profiles> | ||||||
|  |         <profile> | ||||||
|  |             <id>jdk-9-release</id> | ||||||
|  |             <activation> | ||||||
|  |                 <jdk>[9,)</jdk> | ||||||
|  |             </activation> | ||||||
|  |             <properties> | ||||||
|  |                 <maven.compiler.release>8</maven.compiler.release> | ||||||
|  |             </properties> | ||||||
|  |         </profile> | ||||||
|  |         <profile> | ||||||
|  |             <id>jdk-9-javadoc</id> | ||||||
|  |             <activation> | ||||||
|  |                 <jdk>[9,)</jdk> | ||||||
|  |             </activation> | ||||||
|  |             <build> | ||||||
|  |                 <pluginManagement> | ||||||
|  |                     <plugins> | ||||||
|  |                         <plugin> | ||||||
|  |                             <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |                             <artifactId>maven-javadoc-plugin</artifactId> | ||||||
|  |                             <configuration> | ||||||
|  |                                 <additionalOptions>-html5</additionalOptions> | ||||||
|  |                             </configuration> | ||||||
|  |                         </plugin> | ||||||
|  |                     </plugins> | ||||||
|  |                 </pluginManagement> | ||||||
|  |             </build> | ||||||
|  |         </profile> | ||||||
|  |         <profile> | ||||||
|  |             <id>jdk-15-javadoc</id> | ||||||
|  |             <activation> | ||||||
|  |                 <jdk>[15,)</jdk> | ||||||
|  |             </activation> | ||||||
|  |             <build> | ||||||
|  |                 <pluginManagement> | ||||||
|  |                     <plugins> | ||||||
|  |                         <plugin> | ||||||
|  |                             <groupId>org.apache.maven.plugins</groupId> | ||||||
|  |                             <artifactId>maven-javadoc-plugin</artifactId> | ||||||
|  |                             <configuration> | ||||||
|  |                                 <doclint>none</doclint> | ||||||
|  |                             </configuration> | ||||||
|  |                         </plugin> | ||||||
|  |                     </plugins> | ||||||
|  |                 </pluginManagement> | ||||||
|  |             </build> | ||||||
|  |         </profile> | ||||||
|         <profile> |         <profile> | ||||||
|             <id>dist</id> |             <id>dist</id> | ||||||
|             <build> |             <build> | ||||||
| @@ -178,7 +282,7 @@ | |||||||
|                     <plugin> |                     <plugin> | ||||||
|                         <groupId>org.apache.maven.plugins</groupId> |                         <groupId>org.apache.maven.plugins</groupId> | ||||||
|                         <artifactId>maven-source-plugin</artifactId> |                         <artifactId>maven-source-plugin</artifactId> | ||||||
|                         <version>3.2.1</version> |                         <version>3.3.1</version> | ||||||
|                         <executions> |                         <executions> | ||||||
|                             <execution> |                             <execution> | ||||||
|                                 <phase>package</phase> |                                 <phase>package</phase> | ||||||
| @@ -191,7 +295,7 @@ | |||||||
|                     <plugin> |                     <plugin> | ||||||
|                         <groupId>org.projectlombok</groupId> |                         <groupId>org.projectlombok</groupId> | ||||||
|                         <artifactId>lombok-maven-plugin</artifactId> |                         <artifactId>lombok-maven-plugin</artifactId> | ||||||
|                         <version>1.18.10.0</version> |                         <version>1.18.20.0</version> | ||||||
|                         <executions> |                         <executions> | ||||||
|                             <execution> |                             <execution> | ||||||
|                                 <phase>package</phase> |                                 <phase>package</phase> | ||||||
| @@ -205,11 +309,17 @@ | |||||||
|                             <outputDirectory>${project.build.directory}/delombok</outputDirectory> |                             <outputDirectory>${project.build.directory}/delombok</outputDirectory> | ||||||
|                             <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory> |                             <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory> | ||||||
|                         </configuration> |                         </configuration> | ||||||
|  |                         <dependencies> | ||||||
|  |                             <dependency> | ||||||
|  |                                 <groupId>org.projectlombok</groupId> | ||||||
|  |                                 <artifactId>lombok</artifactId> | ||||||
|  |                                 <version>${lombok.version}</version> | ||||||
|  |                             </dependency> | ||||||
|  |                         </dependencies> | ||||||
|                     </plugin> |                     </plugin> | ||||||
|                     <plugin> |                     <plugin> | ||||||
|                         <groupId>org.apache.maven.plugins</groupId> |                         <groupId>org.apache.maven.plugins</groupId> | ||||||
|                         <artifactId>maven-javadoc-plugin</artifactId> |                         <artifactId>maven-javadoc-plugin</artifactId> | ||||||
|                         <version>3.2.0</version> |  | ||||||
|                         <executions> |                         <executions> | ||||||
|                             <!-- Execute Javadoc once normally to catch any warnings --> |                             <!-- Execute Javadoc once normally to catch any warnings --> | ||||||
|                             <execution> |                             <execution> | ||||||
| @@ -247,7 +357,7 @@ | |||||||
|                     <plugin> |                     <plugin> | ||||||
|                         <groupId>org.apache.maven.plugins</groupId> |                         <groupId>org.apache.maven.plugins</groupId> | ||||||
|                         <artifactId>maven-gpg-plugin</artifactId> |                         <artifactId>maven-gpg-plugin</artifactId> | ||||||
|                         <version>1.6</version> |                         <version>3.2.4</version> | ||||||
|                         <executions> |                         <executions> | ||||||
|                             <execution> |                             <execution> | ||||||
|                                 <phase>verify</phase> |                                 <phase>verify</phase> | ||||||
|   | |||||||
| @@ -6,38 +6,32 @@ | |||||||
|     <parent> |     <parent> | ||||||
|         <groupId>net.md-5</groupId> |         <groupId>net.md-5</groupId> | ||||||
|         <artifactId>bungeecord-parent</artifactId> |         <artifactId>bungeecord-parent</artifactId> | ||||||
|         <version>1.16-R0.4-SNAPSHOT</version> |         <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|         <relativePath>../pom.xml</relativePath> |         <relativePath>../pom.xml</relativePath> | ||||||
|     </parent> |     </parent> | ||||||
|  |  | ||||||
|     <groupId>net.md-5</groupId> |     <groupId>net.md-5</groupId> | ||||||
|     <artifactId>bungeecord-protocol</artifactId> |     <artifactId>bungeecord-protocol</artifactId> | ||||||
|     <version>1.16-R0.4-SNAPSHOT</version> |     <version>1.20-R0.3-SNAPSHOT</version> | ||||||
|     <packaging>jar</packaging> |     <packaging>jar</packaging> | ||||||
|  |  | ||||||
|     <name>BungeeCord-Protocol</name> |     <name>BungeeCord-Protocol</name> | ||||||
|     <description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description> |     <description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description> | ||||||
|  |  | ||||||
|     <!-- We really shouldn't depend on external repositories, but at least this is the Central staging one --> |     <!-- We really shouldn't depend on external repositories --> | ||||||
|     <repositories> |     <repositories> | ||||||
|         <repository> |         <repository> | ||||||
|             <id>sonatype-nexus-snapshots</id> |             <id>minecraft-libraries</id> | ||||||
|             <name>Sonatype Nexus Snapshots</name> |             <name>Minecraft Libraries</name> | ||||||
|             <url>https://oss.sonatype.org/content/repositories/snapshots</url> |             <url>https://libraries.minecraft.net/</url> | ||||||
|             <releases> |  | ||||||
|                 <enabled>false</enabled> |  | ||||||
|             </releases> |  | ||||||
|             <snapshots> |  | ||||||
|                 <enabled>true</enabled> |  | ||||||
|             </snapshots> |  | ||||||
|         </repository> |         </repository> | ||||||
|     </repositories> |     </repositories> | ||||||
|  |  | ||||||
|     <dependencies> |     <dependencies> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>net.md-5</groupId> |             <groupId>com.mojang</groupId> | ||||||
|             <artifactId>brigadier</artifactId> |             <artifactId>brigadier</artifactId> | ||||||
|             <version>1.0.16-SNAPSHOT</version> |             <version>1.2.9</version> | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
| @@ -49,7 +43,6 @@ | |||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>io.netty</groupId> |             <groupId>io.netty</groupId> | ||||||
|             <artifactId>netty-codec</artifactId> |             <artifactId>netty-codec</artifactId> | ||||||
|             <version>${netty.version}</version> |  | ||||||
|             <scope>compile</scope> |             <scope>compile</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
|   | |||||||
| @@ -2,12 +2,18 @@ package net.md_5.bungee.protocol; | |||||||
|  |  | ||||||
| import net.md_5.bungee.protocol.packet.BossBar; | import net.md_5.bungee.protocol.packet.BossBar; | ||||||
| import net.md_5.bungee.protocol.packet.Chat; | import net.md_5.bungee.protocol.packet.Chat; | ||||||
|  | import net.md_5.bungee.protocol.packet.ClearTitles; | ||||||
|  | import net.md_5.bungee.protocol.packet.ClientChat; | ||||||
|  | import net.md_5.bungee.protocol.packet.ClientCommand; | ||||||
| import net.md_5.bungee.protocol.packet.ClientSettings; | import net.md_5.bungee.protocol.packet.ClientSettings; | ||||||
| import net.md_5.bungee.protocol.packet.ClientStatus; | import net.md_5.bungee.protocol.packet.ClientStatus; | ||||||
| import net.md_5.bungee.protocol.packet.Commands; | import net.md_5.bungee.protocol.packet.Commands; | ||||||
|  | import net.md_5.bungee.protocol.packet.CookieRequest; | ||||||
|  | import net.md_5.bungee.protocol.packet.CookieResponse; | ||||||
| import net.md_5.bungee.protocol.packet.EncryptionRequest; | import net.md_5.bungee.protocol.packet.EncryptionRequest; | ||||||
| import net.md_5.bungee.protocol.packet.EncryptionResponse; | import net.md_5.bungee.protocol.packet.EncryptionResponse; | ||||||
| import net.md_5.bungee.protocol.packet.EntityStatus; | import net.md_5.bungee.protocol.packet.EntityStatus; | ||||||
|  | import net.md_5.bungee.protocol.packet.FinishConfiguration; | ||||||
| import net.md_5.bungee.protocol.packet.GameState; | import net.md_5.bungee.protocol.packet.GameState; | ||||||
| import net.md_5.bungee.protocol.packet.Handshake; | import net.md_5.bungee.protocol.packet.Handshake; | ||||||
| import net.md_5.bungee.protocol.packet.KeepAlive; | import net.md_5.bungee.protocol.packet.KeepAlive; | ||||||
| @@ -15,6 +21,7 @@ import net.md_5.bungee.protocol.packet.Kick; | |||||||
| import net.md_5.bungee.protocol.packet.LegacyHandshake; | import net.md_5.bungee.protocol.packet.LegacyHandshake; | ||||||
| import net.md_5.bungee.protocol.packet.LegacyPing; | import net.md_5.bungee.protocol.packet.LegacyPing; | ||||||
| import net.md_5.bungee.protocol.packet.Login; | import net.md_5.bungee.protocol.packet.Login; | ||||||
|  | import net.md_5.bungee.protocol.packet.LoginAcknowledged; | ||||||
| import net.md_5.bungee.protocol.packet.LoginPayloadRequest; | import net.md_5.bungee.protocol.packet.LoginPayloadRequest; | ||||||
| import net.md_5.bungee.protocol.packet.LoginPayloadResponse; | import net.md_5.bungee.protocol.packet.LoginPayloadResponse; | ||||||
| import net.md_5.bungee.protocol.packet.LoginRequest; | import net.md_5.bungee.protocol.packet.LoginRequest; | ||||||
| @@ -22,18 +29,29 @@ import net.md_5.bungee.protocol.packet.LoginSuccess; | |||||||
| import net.md_5.bungee.protocol.packet.PingPacket; | import net.md_5.bungee.protocol.packet.PingPacket; | ||||||
| import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; | import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; | ||||||
| import net.md_5.bungee.protocol.packet.PlayerListItem; | import net.md_5.bungee.protocol.packet.PlayerListItem; | ||||||
|  | import net.md_5.bungee.protocol.packet.PlayerListItemRemove; | ||||||
|  | import net.md_5.bungee.protocol.packet.PlayerListItemUpdate; | ||||||
| import net.md_5.bungee.protocol.packet.PluginMessage; | import net.md_5.bungee.protocol.packet.PluginMessage; | ||||||
| import net.md_5.bungee.protocol.packet.Respawn; | import net.md_5.bungee.protocol.packet.Respawn; | ||||||
| import net.md_5.bungee.protocol.packet.ScoreboardDisplay; | import net.md_5.bungee.protocol.packet.ScoreboardDisplay; | ||||||
| import net.md_5.bungee.protocol.packet.ScoreboardObjective; | import net.md_5.bungee.protocol.packet.ScoreboardObjective; | ||||||
| import net.md_5.bungee.protocol.packet.ScoreboardScore; | import net.md_5.bungee.protocol.packet.ScoreboardScore; | ||||||
|  | import net.md_5.bungee.protocol.packet.ScoreboardScoreReset; | ||||||
|  | import net.md_5.bungee.protocol.packet.ServerData; | ||||||
| import net.md_5.bungee.protocol.packet.SetCompression; | import net.md_5.bungee.protocol.packet.SetCompression; | ||||||
|  | import net.md_5.bungee.protocol.packet.StartConfiguration; | ||||||
| import net.md_5.bungee.protocol.packet.StatusRequest; | import net.md_5.bungee.protocol.packet.StatusRequest; | ||||||
| import net.md_5.bungee.protocol.packet.StatusResponse; | import net.md_5.bungee.protocol.packet.StatusResponse; | ||||||
|  | import net.md_5.bungee.protocol.packet.StoreCookie; | ||||||
|  | import net.md_5.bungee.protocol.packet.Subtitle; | ||||||
|  | import net.md_5.bungee.protocol.packet.SystemChat; | ||||||
| import net.md_5.bungee.protocol.packet.TabCompleteRequest; | import net.md_5.bungee.protocol.packet.TabCompleteRequest; | ||||||
| import net.md_5.bungee.protocol.packet.TabCompleteResponse; | import net.md_5.bungee.protocol.packet.TabCompleteResponse; | ||||||
| import net.md_5.bungee.protocol.packet.Team; | import net.md_5.bungee.protocol.packet.Team; | ||||||
| import net.md_5.bungee.protocol.packet.Title; | import net.md_5.bungee.protocol.packet.Title; | ||||||
|  | import net.md_5.bungee.protocol.packet.TitleTimes; | ||||||
|  | import net.md_5.bungee.protocol.packet.Transfer; | ||||||
|  | import net.md_5.bungee.protocol.packet.UnsignedClientCommand; | ||||||
| import net.md_5.bungee.protocol.packet.ViewDistance; | import net.md_5.bungee.protocol.packet.ViewDistance; | ||||||
|  |  | ||||||
| public abstract class AbstractPacketHandler | public abstract class AbstractPacketHandler | ||||||
| @@ -75,6 +93,22 @@ public abstract class AbstractPacketHandler | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void handle(ClientChat chat) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(SystemChat chat) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(ClientCommand command) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(UnsignedClientCommand command) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void handle(Respawn respawn) throws Exception |     public void handle(Respawn respawn) throws Exception | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| @@ -95,6 +129,14 @@ public abstract class AbstractPacketHandler | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void handle(PlayerListItemRemove playerListItem) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(PlayerListItemUpdate playerListItem) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void handle(PlayerListHeaderFooter playerListHeaderFooter) throws Exception |     public void handle(PlayerListHeaderFooter playerListHeaderFooter) throws Exception | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| @@ -111,6 +153,10 @@ public abstract class AbstractPacketHandler | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void handle(ScoreboardScoreReset scoreboardScoreReset) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void handle(EncryptionRequest encryptionRequest) throws Exception |     public void handle(EncryptionRequest encryptionRequest) throws Exception | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| @@ -127,6 +173,18 @@ public abstract class AbstractPacketHandler | |||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void handle(Subtitle title) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(TitleTimes title) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(ClearTitles title) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public void handle(PluginMessage pluginMessage) throws Exception |     public void handle(PluginMessage pluginMessage) throws Exception | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| @@ -178,4 +236,36 @@ public abstract class AbstractPacketHandler | |||||||
|     public void handle(GameState gameState) throws Exception |     public void handle(GameState gameState) throws Exception | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void handle(ServerData serverData) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(LoginAcknowledged loginAcknowledged) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(StartConfiguration startConfiguration) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(FinishConfiguration finishConfiguration) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(Transfer transfer) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(StoreCookie storeCookie) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(CookieRequest cookieRequest) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void handle(CookieResponse cookieResponse) throws Exception | ||||||
|  |     { | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user