Compare commits
	
		
			309 Commits
		
	
	
		
			patch-brig
			...
			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: | ||||
|   build: | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ubuntu-22.04 | ||||
|  | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         java: [8, 11] | ||||
|         java: [8, 11, 17, 21] | ||||
|  | ||||
|     name: Java ${{ matrix.java }} | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/setup-java@v1 | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/setup-java@v4 | ||||
|       with: | ||||
|         distribution: zulu | ||||
|         java-version: ${{ matrix.java }} | ||||
|     - run: java -version && mvn --version | ||||
|     - 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). | ||||
|  | ||||
| (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> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-api</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-API</name> | ||||
| @@ -46,13 +46,33 @@ | ||||
|         <dependency> | ||||
|             <groupId>io.netty</groupId> | ||||
|             <artifactId>netty-transport-native-unix-common</artifactId> | ||||
|             <version>${netty.version}</version> | ||||
|             <scope>compile</scope> | ||||
|         </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> | ||||
|             <groupId>org.yaml</groupId> | ||||
|             <artifactId>snakeyaml</artifactId> | ||||
|             <version>1.26</version> | ||||
|             <version>2.2</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import java.net.InetSocketAddress; | ||||
| import java.net.SocketAddress; | ||||
| import java.net.URI; | ||||
| import java.net.URISyntaxException; | ||||
| import java.util.Locale; | ||||
| import java.util.UUID; | ||||
|  | ||||
| /** | ||||
| @@ -68,6 +69,17 @@ public class Util | ||||
|         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 | ||||
|      * debugging. | ||||
| @@ -76,11 +88,24 @@ public class Util | ||||
|      * @return a string representing information about the {@link Throwable} | ||||
|      */ | ||||
|     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 | ||||
|         StackTraceElement[] trace = t.getStackTrace(); | ||||
|         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) | ||||
| @@ -88,6 +113,16 @@ public class Util | ||||
|         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) | ||||
|     { | ||||
|         return Joiner.on( separators ).join( objects ); | ||||
|   | ||||
| @@ -28,18 +28,13 @@ public abstract class AbstractReconnectHandler implements ReconnectHandler | ||||
|  | ||||
|     public static ServerInfo getForcedHost(PendingConnection con) | ||||
|     { | ||||
|         if ( con.getVirtualHost() == null ) | ||||
|         { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         String forced = con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() ); | ||||
|         String forced = ( con.getVirtualHost() == null ) ? null : con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() ); | ||||
|  | ||||
|         if ( forced == null && con.getListener().isForceDefault() ) | ||||
|         { | ||||
|             forced = con.getListener().getDefaultServer(); | ||||
|         } | ||||
|         return ProxyServer.getInstance().getServerInfo( forced ); | ||||
|         return ( forced == null ) ? null : ProxyServer.getInstance().getServerInfo( forced ); | ||||
|     } | ||||
|  | ||||
|     protected abstract ServerInfo getStoredServer(ProxiedPlayer player); | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| package net.md_5.bungee.api; | ||||
|  | ||||
| import com.google.common.base.Preconditions; | ||||
| import com.google.common.io.BaseEncoding; | ||||
| import com.google.gson.TypeAdapter; | ||||
| import com.google.gson.internal.bind.TypeAdapters; | ||||
| import com.google.gson.stream.JsonReader; | ||||
| import com.google.gson.stream.JsonToken; | ||||
| import com.google.gson.stream.JsonWriter; | ||||
| import java.awt.image.BufferedImage; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| @@ -26,13 +27,26 @@ public class Favicon | ||||
|         @Override | ||||
|         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 | ||||
|         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 ); | ||||
|         } | ||||
|     }; | ||||
| @@ -59,6 +73,7 @@ public class Favicon | ||||
|      */ | ||||
|     public static Favicon create(BufferedImage image) | ||||
|     { | ||||
|         Preconditions.checkArgument( image != null, "image is null" ); | ||||
|         // check size | ||||
|         if ( image.getWidth() != 64 || image.getHeight() != 64 ) | ||||
|         { | ||||
|   | ||||
| @@ -105,13 +105,13 @@ public class ServerPing | ||||
|     @Deprecated | ||||
|     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 | ||||
|     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 | ||||
| @@ -139,7 +139,7 @@ public class ServerPing | ||||
|     @Deprecated | ||||
|     public void setDescription(String description) | ||||
|     { | ||||
|         this.description = new TextComponent( TextComponent.fromLegacyText( description ) ); | ||||
|         this.description = TextComponent.fromLegacy( description ); | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|   | ||||
| @@ -2,7 +2,9 @@ package net.md_5.bungee.api.connection; | ||||
|  | ||||
| import java.net.InetSocketAddress; | ||||
| import java.util.UUID; | ||||
| import java.util.concurrent.CompletableFuture; | ||||
| import net.md_5.bungee.api.config.ListenerInfo; | ||||
| import org.jetbrains.annotations.ApiStatus; | ||||
|  | ||||
| /** | ||||
|  * 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. | ||||
|      */ | ||||
|     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.Map; | ||||
| import java.util.UUID; | ||||
| import java.util.concurrent.CompletableFuture; | ||||
| import net.md_5.bungee.api.Callback; | ||||
| import net.md_5.bungee.api.ChatMessageType; | ||||
| 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.event.ServerConnectEvent; | ||||
| 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. | ||||
|  */ | ||||
| public interface ProxiedPlayer extends Connection, CommandSender | ||||
| @@ -57,8 +59,7 @@ public interface ProxiedPlayer extends Connection, CommandSender | ||||
|     String getDisplayName(); | ||||
|  | ||||
|     /** | ||||
|      * Sets this players display name to be used as their nametag and tab list | ||||
|      * name. | ||||
|      * Sets this player's display name to be used by proxy commands and plugins. | ||||
|      * | ||||
|      * @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. | ||||
|      * | ||||
|      * @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(); | ||||
|  | ||||
|     /** | ||||
|      * 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,157 +0,0 @@ | ||||
| package net.md_5.bungee.api.event; | ||||
|  | ||||
| import com.mojang.brigadier.arguments.ArgumentType; | ||||
| import com.mojang.brigadier.arguments.IntegerArgumentType; | ||||
| import com.mojang.brigadier.arguments.StringArgumentType; | ||||
| import com.mojang.brigadier.builder.ArgumentBuilder; | ||||
| import com.mojang.brigadier.builder.RequiredArgumentBuilder; | ||||
| import com.mojang.brigadier.suggestion.SuggestionProvider; | ||||
| import com.mojang.brigadier.tree.CommandNode; | ||||
| import com.mojang.brigadier.tree.RootCommandNode; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.Setter; | ||||
| import lombok.ToString; | ||||
| import net.md_5.bungee.api.CommandSender; | ||||
| import net.md_5.bungee.api.connection.Connection; | ||||
| import net.md_5.bungee.api.plugin.Command; | ||||
| import net.md_5.bungee.api.plugin.Plugin; | ||||
| import net.md_5.bungee.api.plugin.PluginManager; | ||||
| import net.md_5.bungee.api.plugin.TabExecutor; | ||||
|  | ||||
| /** | ||||
|  * Event called when a downstream server (on 1.13+) sends the command structure | ||||
|  * to a player, but before BungeeCord adds the dummy command nodes of | ||||
|  * registered commands. | ||||
|  * <p> | ||||
|  * BungeeCord will not overwrite the modifications made by the listeners. | ||||
|  * | ||||
|  * <h2>Usage example</h2> | ||||
|  * Here is a usage example of this event, to declare a command structure. | ||||
|  * This illustrates the commands /server and /send of Bungee. | ||||
|  * <pre> | ||||
|  * event.getRoot().addChild( LiteralArgumentBuilder.<CommandSender>literal( "server" ) | ||||
|  *         .requires( sender -> sender.hasPermission( "bungeecord.command.server" ) ) | ||||
|  *         .executes( a -> 0 ) | ||||
|  *         .then( RequiredArgumentBuilder.argument( "serverName", StringArgumentType.greedyString() ) | ||||
|  *                 .suggests( SuggestionRegistry.ASK_SERVER ) | ||||
|  *         ) | ||||
|  *         .build() | ||||
|  * ); | ||||
|  * event.getRoot().addChild( LiteralArgumentBuilder.<CommandSender>literal( "send" ) | ||||
|  *         .requires( sender -> sender.hasPermission( "bungeecord.command.send" ) ) | ||||
|  *         .then( RequiredArgumentBuilder.argument( "playerName", StringArgumentType.word() ) | ||||
|  *                 .suggests( SuggestionRegistry.ASK_SERVER ) | ||||
|  *                 .then( RequiredArgumentBuilder.argument( "serverName", StringArgumentType.greedyString() ) | ||||
|  *                         .suggests( SuggestionRegistry.ASK_SERVER ) | ||||
|  *                 ) | ||||
|  *         ) | ||||
|  *         .build() | ||||
|  * ); | ||||
|  * </pre> | ||||
|  * | ||||
|  * <h2>Flag a {@link CommandNode} as executable or not</h2> | ||||
|  * The implementation of a {@link com.mojang.brigadier.Command Command} used in | ||||
|  * {@link ArgumentBuilder#executes(com.mojang.brigadier.Command)} will never be | ||||
|  * executed. This will only tell to the client if the current node is | ||||
|  * executable or not. | ||||
|  * <ul> | ||||
|  *     <li> | ||||
|  *         {@code builder.executes(null)} (default) to mark the node as not | ||||
|  *         executable. | ||||
|  *     </li> | ||||
|  *     <li> | ||||
|  *         {@code builder.executes(a -> 0)}, or any non null argument, to mark | ||||
|  *         the node as executable (the child arguments are displayed as | ||||
|  *         optional). | ||||
|  *     </li> | ||||
|  * </ul> | ||||
|  * | ||||
|  * <h2>{@link CommandNode}’s suggestions management</h2> | ||||
|  * The implementation of a SuggestionProvider used in | ||||
|  * {@link RequiredArgumentBuilder#suggests(SuggestionProvider)} will never be | ||||
|  * executed. This will only tell to the client how to deal with the | ||||
|  * auto-completion of the argument. | ||||
|  * <ul> | ||||
|  *     <li> | ||||
|  *         {@code builder.suggests(null)} (default) to disable auto-completion | ||||
|  *         for this argument. | ||||
|  *     </li> | ||||
|  *     <li> | ||||
|  *         {@code builder.suggests(SuggestionRegistry.ALL_RECIPES)} to suggest | ||||
|  *         Minecraft’s recipes. | ||||
|  *     </li> | ||||
|  *     <li> | ||||
|  *         {@code builder.suggests(SuggestionRegistry.AVAILABLE_SOUNDS)} to | ||||
|  *         suggest Minecraft’s default sound identifiers. | ||||
|  *     </li> | ||||
|  *     <li> | ||||
|  *         {@code builder.suggests(SuggestionRegistry.SUMMONABLE_ENTITIES)} to | ||||
|  *         suggest Minecraft’s default summonable entities identifiers. | ||||
|  *     </li> | ||||
|  *     <li> | ||||
|  *         {@code builder.suggests(SuggestionRegistry.ASK_SERVER)}, or any | ||||
|  *         other non null argument, to make the Minecraft client ask | ||||
|  *         auto-completion to the server. Any specified implementation of | ||||
|  *         {@link SuggestionProvider} will never be executed. | ||||
|  *     </li> | ||||
|  * </ul> | ||||
|  * | ||||
|  * <h2>Argument types</h2> | ||||
|  * When building a new argument command node using | ||||
|  * {@link RequiredArgumentBuilder#argument(String, ArgumentType)}, you have to | ||||
|  * specify an {@link ArgumentType}. You can use all subclasses of | ||||
|  * {@link ArgumentType} provided with brigadier (for instance, | ||||
|  * {@link StringArgumentType} or {@link IntegerArgumentType}), or call any | ||||
|  * {@code ArgumentRegistry.minecraft*()} methods to use a {@code minecraft:*} | ||||
|  * argument type. | ||||
|  * | ||||
|  * <h2>Limitations with brigadier API</h2> | ||||
|  * This event is only used for the client to show command syntax, suggest | ||||
|  * sub-commands and color the arguments in the chat box. The command execution | ||||
|  * needs to be implemented using {@link PluginManager#registerCommand(Plugin, | ||||
|  * Command)} and the server-side tab-completion using {@link TabCompleteEvent} | ||||
|  * or {@link TabExecutor}. | ||||
|  */ | ||||
| @Data | ||||
| @ToString(callSuper = true) | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| public class CommandsDeclareEvent extends TargetedEvent | ||||
| { | ||||
|     /** | ||||
|      * Wether or not the command tree is modified by this event. | ||||
|      * | ||||
|      * If this value is set to true, BungeeCord will ensure that the | ||||
|      * modifications made in the command tree, will be sent to the player. | ||||
|      * If this is false, the modifications may not be taken into account. | ||||
|      * | ||||
|      * When calling {@link #getRoot()}, this value is automatically set | ||||
|      * to true. | ||||
|      */ | ||||
|     @Setter(value = AccessLevel.NONE) | ||||
|     private boolean modified = false; | ||||
|  | ||||
|     /** | ||||
|      * The root command node of the command structure that will be send to the | ||||
|      * player. | ||||
|      */ | ||||
|     private final RootCommandNode<CommandSender> root; | ||||
|  | ||||
|     public CommandsDeclareEvent(Connection sender, Connection receiver, RootCommandNode<CommandSender> root) | ||||
|     { | ||||
|         super( sender, receiver ); | ||||
|         this.root = root; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The root command node of the command structure that will be send to the | ||||
|      * player. | ||||
|      * @return The root command node | ||||
|      */ | ||||
|     public RootCommandNode<CommandSender> getRoot() | ||||
|     { | ||||
|         modified = true; | ||||
|         return root; | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,7 @@ | ||||
| package net.md_5.bungee.api.event; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.Setter; | ||||
| import lombok.ToString; | ||||
| import net.md_5.bungee.api.Callback; | ||||
| 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. | ||||
|      */ | ||||
|     @Setter(AccessLevel.NONE) | ||||
|     private BaseComponent[] cancelReasonComponents; | ||||
|     private BaseComponent reason; | ||||
|     /** | ||||
|      * Connection attempting to login. | ||||
|      */ | ||||
| @@ -42,28 +39,44 @@ public class LoginEvent extends AsyncEvent<LoginEvent> implements Cancellable | ||||
|  | ||||
|     /** | ||||
|      * @return reason to be displayed | ||||
|      * @deprecated Use component methods instead. | ||||
|      * @deprecated use component methods instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public String getCancelReason() | ||||
|     { | ||||
|         return BaseComponent.toLegacyText( getCancelReasonComponents() ); | ||||
|         return TextComponent.toLegacyText( getReason() ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param cancelReason reason to be displayed | ||||
|      * @deprecated Use | ||||
|      * {@link #setCancelReason(net.md_5.bungee.api.chat.BaseComponent...)} | ||||
|      * instead. | ||||
|      * @deprecated use component methods instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     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) | ||||
|     { | ||||
|         this.cancelReasonComponents = cancelReason; | ||||
|         setReason( TextComponent.fromArray( cancelReason ) ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,9 @@ package net.md_5.bungee.api.event; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| 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.plugin.Event; | ||||
|  | ||||
| /** | ||||
|  * 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 | ||||
| @ToString(callSuper = false) | ||||
| @EqualsAndHashCode(callSuper = false) | ||||
| public class PostLoginEvent extends Event | ||||
| public class PostLoginEvent extends AsyncEvent<PostLoginEvent> | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * The player involved with this event. | ||||
|      */ | ||||
|     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; | ||||
|  | ||||
| import lombok.AccessLevel; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.Setter; | ||||
| import lombok.ToString; | ||||
| import net.md_5.bungee.api.Callback; | ||||
| 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. | ||||
|      */ | ||||
|     @Setter(AccessLevel.NONE) | ||||
|     private BaseComponent[] cancelReasonComponents; | ||||
|     private BaseComponent reason; | ||||
|     /** | ||||
|      * Connection attempting to login. | ||||
|      */ | ||||
| @@ -47,28 +44,44 @@ public class PreLoginEvent extends AsyncEvent<PreLoginEvent> implements Cancella | ||||
|  | ||||
|     /** | ||||
|      * @return reason to be displayed | ||||
|      * @deprecated Use component methods instead. | ||||
|      * @deprecated use component methods instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public String getCancelReason() | ||||
|     { | ||||
|         return BaseComponent.toLegacyText( getCancelReasonComponents() ); | ||||
|         return BaseComponent.toLegacyText( getReason() ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param cancelReason reason to be displayed | ||||
|      * @deprecated Use | ||||
|      * {@link #setCancelReason(net.md_5.bungee.api.chat.BaseComponent...)} | ||||
|      * instead. | ||||
|      * @deprecated Use component methods instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     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) | ||||
|     { | ||||
|         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; | ||||
|  | ||||
| /** | ||||
|  * 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 | ||||
| @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.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 | ||||
| @AllArgsConstructor | ||||
| @ToString(callSuper = false) | ||||
|   | ||||
| @@ -35,7 +35,7 @@ public class ServerKickEvent extends Event implements Cancellable | ||||
|     /** | ||||
|      * Kick reason. | ||||
|      */ | ||||
|     private BaseComponent[] kickReasonComponent; | ||||
|     private BaseComponent reason; | ||||
|     /** | ||||
|      * 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 ); | ||||
|     } | ||||
|  | ||||
|     @Deprecated | ||||
|     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.kickedFrom = kickedFrom; | ||||
|         this.kickReasonComponent = kickReasonComponent; | ||||
|         this.reason = reason; | ||||
|         this.cancelServer = cancelServer; | ||||
|         this.state = state; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return the kick reason | ||||
|      * @deprecated use component methods instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     public String getKickReason() | ||||
|     { | ||||
|         return BaseComponent.toLegacyText( kickReasonComponent ); | ||||
|         return BaseComponent.toLegacyText( getReason() ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param reason the kick reason | ||||
|      * @deprecated use component methods instead | ||||
|      */ | ||||
|     @Deprecated | ||||
|     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; | ||||
|  | ||||
| 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.URLClassLoader; | ||||
| import java.security.CodeSigner; | ||||
| import java.security.CodeSource; | ||||
| import java.util.Set; | ||||
| 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; | ||||
|  | ||||
| @ToString(of = "desc") | ||||
| final class PluginClassloader extends URLClassLoader | ||||
| { | ||||
|  | ||||
| @@ -14,6 +25,10 @@ final class PluginClassloader extends URLClassLoader | ||||
|     // | ||||
|     private final ProxyServer proxy; | ||||
|     private final PluginDescription desc; | ||||
|     private final JarFile jar; | ||||
|     private final Manifest manifest; | ||||
|     private final URL url; | ||||
|     private final ClassLoader libraryLoader; | ||||
|     // | ||||
|     private Plugin plugin; | ||||
|  | ||||
| @@ -22,11 +37,18 @@ final class PluginClassloader extends URLClassLoader | ||||
|         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.desc = desc; | ||||
|         this.jar = new JarFile( file ); | ||||
|         this.manifest = jar.getManifest(); | ||||
|         this.url = file.toURI().toURL(); | ||||
|         this.libraryLoader = libraryLoader; | ||||
|  | ||||
|         allLoaders.add( this ); | ||||
|     } | ||||
| @@ -34,17 +56,34 @@ final class PluginClassloader extends URLClassLoader | ||||
|     @Override | ||||
|     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 | ||||
|         { | ||||
|             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 ) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         if ( checkLibraries && libraryLoader != null ) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 return libraryLoader.loadClass( name ); | ||||
|             } catch ( ClassNotFoundException ex ) | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ( checkOther ) | ||||
|         { | ||||
|             for ( PluginClassloader loader : allLoaders ) | ||||
| @@ -53,16 +92,81 @@ final class PluginClassloader extends URLClassLoader | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         return loader.loadClass0( name, resolve, false ); | ||||
|                         return loader.loadClass0( name, resolve, false, proxy.getPluginManager().isTransitiveDepend( desc, loader.desc ) ); | ||||
|                     } catch ( ClassNotFoundException ex ) | ||||
|                     { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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) | ||||
|     { | ||||
|         Preconditions.checkArgument( plugin != null, "plugin" ); | ||||
|   | ||||
| @@ -2,6 +2,8 @@ package net.md_5.bungee.api.plugin; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.util.HashSet; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Data; | ||||
| @@ -48,4 +50,8 @@ public class PluginDescription | ||||
|      * Optional description. | ||||
|      */ | ||||
|     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.Multimap; | ||||
| 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.InputStream; | ||||
| import java.lang.reflect.Method; | ||||
| import java.net.URL; | ||||
| import java.net.URLClassLoader; | ||||
| import java.util.Arrays; | ||||
| 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.event.EventBus; | ||||
| import net.md_5.bungee.event.EventHandler; | ||||
| import org.yaml.snakeyaml.LoaderOptions; | ||||
| import org.yaml.snakeyaml.Yaml; | ||||
| import org.yaml.snakeyaml.constructor.Constructor; | ||||
| import org.yaml.snakeyaml.introspector.PropertyUtils; | ||||
| @@ -49,6 +52,8 @@ public final class PluginManager | ||||
|     private final Yaml yaml; | ||||
|     private final EventBus eventBus; | ||||
|     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 Map<String, PluginDescription> toLoad = new HashMap<>(); | ||||
|     private final Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create(); | ||||
| @@ -60,13 +65,24 @@ public final class PluginManager | ||||
|         this.proxy = proxy; | ||||
|  | ||||
|         // Ignore unknown entries in the plugin descriptions | ||||
|         Constructor yamlConstructor = new Constructor(); | ||||
|         Constructor yamlConstructor = new Constructor( new LoaderOptions() ); | ||||
|         PropertyUtils propertyUtils = yamlConstructor.getPropertyUtils(); | ||||
|         propertyUtils.setSkipMissingProperties( true ); | ||||
|         yamlConstructor.setPropertyUtils( propertyUtils ); | ||||
|         yaml = new Yaml( yamlConstructor ); | ||||
|  | ||||
|         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; | ||||
|             } | ||||
|  | ||||
|             dependencyGraph.putEdge( plugin.getName(), dependName ); | ||||
|             if ( !status ) | ||||
|             { | ||||
|                 break; | ||||
| @@ -320,10 +337,7 @@ public final class PluginManager | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 URLClassLoader loader = new PluginClassloader( proxy, plugin, new URL[] | ||||
|                 { | ||||
|                     plugin.getFile().toURI().toURL() | ||||
|                 } ); | ||||
|                 URLClassLoader loader = new PluginClassloader( proxy, plugin, plugin.getFile(), ( libraryLoader != null ) ? libraryLoader.createLoader( plugin ) : null ); | ||||
|                 Class<?> main = loader.loadClass( plugin.getMain() ); | ||||
|                 Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance(); | ||||
|  | ||||
| @@ -335,7 +349,7 @@ public final class PluginManager | ||||
|                 } ); | ||||
|             } 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() ); | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.net.SocketAddress; | ||||
| import java.util.Collection; | ||||
| import net.md_5.bungee.api.config.ServerInfo; | ||||
| import net.md_5.bungee.api.connection.ProxiedPlayer; | ||||
| import net.md_5.bungee.api.event.ServerConnectEvent; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class ServerConnectRequestTest | ||||
| { | ||||
| @@ -78,15 +79,15 @@ public class ServerConnectRequestTest | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     @Test(expected = NullPointerException.class) | ||||
|     @Test | ||||
|     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() | ||||
|     { | ||||
|         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; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import io.netty.channel.unix.DomainSocketAddress; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.net.SocketAddress; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collection; | ||||
| import java.util.stream.Stream; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import net.md_5.bungee.Util; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.junit.runners.Parameterized; | ||||
| import org.junit.runners.Parameterized.Parameters; | ||||
| import org.junit.jupiter.params.ParameterizedTest; | ||||
| import org.junit.jupiter.params.provider.Arguments; | ||||
| import org.junit.jupiter.params.provider.MethodSource; | ||||
|  | ||||
| @RequiredArgsConstructor | ||||
| @RunWith(Parameterized.class) | ||||
| public class AddressParseTest | ||||
| { | ||||
|  | ||||
|     @Parameters | ||||
|     public static Collection<Object[]> data() | ||||
|     public static Stream<Arguments> data() | ||||
|     { | ||||
|         return Arrays.asList( new Object[][] | ||||
|         { | ||||
|             { | ||||
|                 "127.0.0.1", "127.0.0.1", Util.DEFAULT_PORT | ||||
|             }, | ||||
|             { | ||||
|                 "127.0.0.1:1337", "127.0.0.1", 1337 | ||||
|             }, | ||||
|             { | ||||
|                 "[::1]", "0:0:0:0:0:0:0:1", Util.DEFAULT_PORT | ||||
|             }, | ||||
|             { | ||||
|                 "[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 | ||||
|             } | ||||
|         } ); | ||||
|         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 ), | ||||
|                 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 ), | ||||
|                 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 ), | ||||
|                 Arguments.of( "unix:///var/run/bungee.sock", "/var/run/bungee.sock", -1 ) | ||||
|         ); | ||||
|     } | ||||
|     private final String line; | ||||
|     private final String host; | ||||
|     private final int port; | ||||
|  | ||||
|     @Test | ||||
|     public void test() | ||||
|     @ParameterizedTest | ||||
|     @MethodSource("data") | ||||
|     public void test(String line, String host, int port) | ||||
|     { | ||||
|         SocketAddress parsed = Util.getAddr( line ); | ||||
|  | ||||
| @@ -65,14 +40,14 @@ public class AddressParseTest | ||||
|         { | ||||
|             InetSocketAddress tcp = (InetSocketAddress) parsed; | ||||
|  | ||||
|             Assert.assertEquals( host, tcp.getHostString() ); | ||||
|             Assert.assertEquals( port, tcp.getPort() ); | ||||
|             assertEquals( host, tcp.getHostString() ); | ||||
|             assertEquals( port, tcp.getPort() ); | ||||
|         } else if ( parsed instanceof DomainSocketAddress ) | ||||
|         { | ||||
|             DomainSocketAddress unix = (DomainSocketAddress) parsed; | ||||
|  | ||||
|             Assert.assertEquals( host, unix.path() ); | ||||
|             Assert.assertEquals( -1, port ); | ||||
|             assertEquals( host, unix.path() ); | ||||
|             assertEquals( -1, port ); | ||||
|         } else | ||||
|         { | ||||
|             throw new AssertionError( "Unknown socket " + parsed ); | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| package net.md_5.bungee.util; | ||||
|  | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class CaseInsensitiveTest | ||||
| { | ||||
| @@ -13,12 +13,12 @@ public class CaseInsensitiveTest | ||||
|         CaseInsensitiveMap<Object> map = new CaseInsensitiveMap<>(); | ||||
|  | ||||
|         map.put( "FOO", obj ); | ||||
|         Assert.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.contains( "foo" ) ); // Assert that contains is case insensitive | ||||
|         assertTrue( map.entrySet().iterator().next().getKey().equals( "FOO" ) ); // Assert that case is preserved | ||||
|  | ||||
|         // Assert that remove is case insensitive | ||||
|         map.remove( "FoO" ); | ||||
|         Assert.assertFalse( map.contains( "foo" ) ); | ||||
|         assertFalse( map.contains( "foo" ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -27,8 +27,8 @@ public class CaseInsensitiveTest | ||||
|         CaseInsensitiveSet set = new CaseInsensitiveSet(); | ||||
|  | ||||
|         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" ); | ||||
|         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; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import java.util.UUID; | ||||
| import net.md_5.bungee.Util; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class UUIDTest | ||||
| { | ||||
| @@ -13,7 +13,7 @@ public class UUIDTest | ||||
|     { | ||||
|         UUID uuid = UUID.fromString( "af74a02d-19cb-445b-b07f-6866a861f783" ); | ||||
|         UUID uuid1 = Util.getUUID( "af74a02d19cb445bb07f6866a861f783" ); | ||||
|         Assert.assertEquals( uuid, uuid1 ); | ||||
|         assertEquals( uuid, uuid1 ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -23,7 +23,7 @@ public class UUIDTest | ||||
|         { | ||||
|             UUID expected = UUID.randomUUID(); | ||||
|             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> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-bootstrap</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Bootstrap</name> | ||||
| @@ -41,7 +41,7 @@ | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-jar-plugin</artifactId> | ||||
|                 <version>3.2.0</version> | ||||
|                 <version>3.4.1</version> | ||||
|                 <configuration> | ||||
|                     <archive> | ||||
|                         <manifestEntries> | ||||
| @@ -55,7 +55,7 @@ | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-shade-plugin</artifactId> | ||||
|                 <version>3.2.3</version> | ||||
|                 <version>3.5.3</version> | ||||
|                 <executions> | ||||
|                     <execution> | ||||
|                         <phase>package</phase> | ||||
| @@ -79,4 +79,34 @@ | ||||
|             </plugin> | ||||
|         </plugins> | ||||
|     </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> | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-chat</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Chat</name> | ||||
| @@ -22,7 +22,7 @@ | ||||
|         <dependency> | ||||
|             <groupId>com.google.code.gson</groupId> | ||||
|             <artifactId>gson</artifactId> | ||||
|             <version>2.8.0</version> | ||||
|             <version>2.10.1</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|   | ||||
| @@ -244,7 +244,7 @@ public final class ChatColor | ||||
|     public static ChatColor of(String string) | ||||
|     { | ||||
|         Preconditions.checkArgument( string != null, "string cannot be null" ); | ||||
|         if ( string.startsWith( "#" ) && string.length() == 7 ) | ||||
|         if ( string.length() == 7 && string.charAt( 0 ) == '#' ) | ||||
|         { | ||||
|             int rgb; | ||||
|             try | ||||
| @@ -300,7 +300,7 @@ public final class ChatColor | ||||
|     @Deprecated | ||||
|     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; | ||||
|  | ||||
|     /** | ||||
|      * The color of this component and any child components (unless overridden) | ||||
|      * The component's style. | ||||
|      */ | ||||
|     private ChatColor color; | ||||
|     /** | ||||
|      * 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; | ||||
|     @Getter | ||||
|     private ComponentStyle style = new ComponentStyle(); | ||||
|     /** | ||||
|      * The text to insert into the chat when this component (and child | ||||
|      * components) are clicked while pressing the shift key | ||||
| @@ -78,6 +50,12 @@ public abstract class BaseComponent | ||||
|     @Getter | ||||
|     private HoverEvent hoverEvent; | ||||
|  | ||||
|     /** | ||||
|      * Whether this component rejects previous formatting | ||||
|      */ | ||||
|     @Getter | ||||
|     private transient boolean reset; | ||||
|  | ||||
|     /** | ||||
|      * Default constructor. | ||||
|      * | ||||
| @@ -147,31 +125,31 @@ public abstract class BaseComponent | ||||
|         } | ||||
|         if ( retention == FormatRetention.FORMATTING || retention == FormatRetention.ALL ) | ||||
|         { | ||||
|             if ( replace || color == null ) | ||||
|             if ( replace || !style.hasColor() ) | ||||
|             { | ||||
|                 setColor( component.getColorRaw() ); | ||||
|             } | ||||
|             if ( replace || font == null ) | ||||
|             if ( replace || !style.hasFont() ) | ||||
|             { | ||||
|                 setFont( component.getFontRaw() ); | ||||
|             } | ||||
|             if ( replace || bold == null ) | ||||
|             if ( replace || style.isBoldRaw() == null ) | ||||
|             { | ||||
|                 setBold( component.isBoldRaw() ); | ||||
|             } | ||||
|             if ( replace || italic == null ) | ||||
|             if ( replace || style.isItalicRaw() == null ) | ||||
|             { | ||||
|                 setItalic( component.isItalicRaw() ); | ||||
|             } | ||||
|             if ( replace || underlined == null ) | ||||
|             if ( replace || style.isUnderlinedRaw() == null ) | ||||
|             { | ||||
|                 setUnderlined( component.isUnderlinedRaw() ); | ||||
|             } | ||||
|             if ( replace || strikethrough == null ) | ||||
|             if ( replace || style.isStrikethroughRaw() == null ) | ||||
|             { | ||||
|                 setStrikethrough( component.isStrikethroughRaw() ); | ||||
|             } | ||||
|             if ( replace || obfuscated == null ) | ||||
|             if ( replace || style.isObfuscatedRaw() == null ) | ||||
|             { | ||||
|                 setObfuscated( component.isObfuscatedRaw() ); | ||||
|             } | ||||
| @@ -260,6 +238,32 @@ public abstract class BaseComponent | ||||
|         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 | ||||
|      * component doesn't have one. {@link net.md_5.bungee.api.ChatColor#WHITE} | ||||
| @@ -269,7 +273,7 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     public ChatColor getColor() | ||||
|     { | ||||
|         if ( color == null ) | ||||
|         if ( !style.hasColor() ) | ||||
|         { | ||||
|             if ( parent == null ) | ||||
|             { | ||||
| @@ -277,7 +281,7 @@ public abstract class BaseComponent | ||||
|             } | ||||
|             return parent.getColor(); | ||||
|         } | ||||
|         return color; | ||||
|         return style.getColor(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -288,7 +292,17 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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() | ||||
|     { | ||||
|         if ( font == null ) | ||||
|         if ( !style.hasFont() ) | ||||
|         { | ||||
|             if ( parent == null ) | ||||
|             { | ||||
| @@ -307,7 +321,7 @@ public abstract class BaseComponent | ||||
|             } | ||||
|             return parent.getFont(); | ||||
|         } | ||||
|         return font; | ||||
|         return style.getFont(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -318,7 +332,17 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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() | ||||
|     { | ||||
|         if ( bold == null ) | ||||
|         if ( style.isBoldRaw() == null ) | ||||
|         { | ||||
|             return parent != null && parent.isBold(); | ||||
|         } | ||||
|         return bold; | ||||
|         return style.isBold(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -345,7 +369,17 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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() | ||||
|     { | ||||
|         if ( italic == null ) | ||||
|         if ( style.isItalicRaw() == null ) | ||||
|         { | ||||
|             return parent != null && parent.isItalic(); | ||||
|         } | ||||
|         return italic; | ||||
|         return style.isItalic(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -372,7 +406,17 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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() | ||||
|     { | ||||
|         if ( underlined == null ) | ||||
|         if ( style.isUnderlinedRaw() == null ) | ||||
|         { | ||||
|             return parent != null && parent.isUnderlined(); | ||||
|         } | ||||
|         return underlined; | ||||
|         return style.isUnderlined(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -399,7 +443,18 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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() | ||||
|     { | ||||
|         if ( strikethrough == null ) | ||||
|         if ( style.isStrikethroughRaw() == null ) | ||||
|         { | ||||
|             return parent != null && parent.isStrikethrough(); | ||||
|         } | ||||
|         return strikethrough; | ||||
|         return style.isStrikethrough(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -426,7 +481,17 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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() | ||||
|     { | ||||
|         if ( obfuscated == null ) | ||||
|         if ( style.isObfuscatedRaw() == null ) | ||||
|         { | ||||
|             return parent != null && parent.isObfuscated(); | ||||
|         } | ||||
|         return obfuscated; | ||||
|         return style.isObfuscated(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -453,7 +518,48 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     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) | ||||
| @@ -492,6 +598,16 @@ public abstract class BaseComponent | ||||
|         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 | ||||
|      * | ||||
| @@ -499,10 +615,8 @@ public abstract class BaseComponent | ||||
|      */ | ||||
|     public boolean hasFormatting() | ||||
|     { | ||||
|         return color != null || font != null || bold != null | ||||
|                 || italic != null || underlined != null | ||||
|                 || strikethrough != null || obfuscated != null | ||||
|                 || insertion != null || hoverEvent != null || clickEvent != null; | ||||
|         return hasStyle() || insertion != null | ||||
|                 || hoverEvent != null || clickEvent != null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -57,7 +57,7 @@ public final class ComponentBuilder | ||||
|      */ | ||||
|     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; | ||||
|             dummy = null; | ||||
|         } | ||||
|         if ( previous != null ) | ||||
|         if ( previous != null && !component.isReset() ) | ||||
|         { | ||||
|             component.copyFormatting( previous, retention, false ); | ||||
|         } | ||||
| @@ -204,6 +204,33 @@ public final class ComponentBuilder | ||||
|         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 | ||||
|      * formatting. The text will have all the formatting from previous part. | ||||
| @@ -396,6 +423,18 @@ public final class ComponentBuilder | ||||
|         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. | ||||
|      * | ||||
| @@ -454,9 +493,32 @@ public final class ComponentBuilder | ||||
|         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 | ||||
|      * 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 | ||||
|      */ | ||||
|   | ||||
							
								
								
									
										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 id; | ||||
|     } | ||||
|     */ | ||||
|      */ | ||||
|  | ||||
|     private ItemTag(String nbt) | ||||
|     { | ||||
|   | ||||
| @@ -33,6 +33,13 @@ public final class SelectorComponent extends BaseComponent | ||||
|      */ | ||||
|     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. | ||||
|      * | ||||
| @@ -42,6 +49,17 @@ public final class SelectorComponent extends BaseComponent | ||||
|     { | ||||
|         super( original ); | ||||
|         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 | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package net.md_5.bungee.api.chat; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
| import lombok.AllArgsConstructor; | ||||
| @@ -27,6 +28,41 @@ public final class TextComponent extends BaseComponent | ||||
|      * @param message the text to convert | ||||
|      * @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) | ||||
|     { | ||||
|         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 | ||||
|      * (i.e. after ChatColor.RESET). | ||||
|      * @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) | ||||
|     { | ||||
|         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(); | ||||
|         TextComponent component = new TextComponent(); | ||||
|         Matcher matcher = url.matcher( message ); | ||||
| @@ -94,7 +141,7 @@ public final class TextComponent extends BaseComponent | ||||
|                     component = new TextComponent( old ); | ||||
|                     old.setText( builder.toString() ); | ||||
|                     builder = new StringBuilder(); | ||||
|                     components.add( old ); | ||||
|                     appender.accept( old ); | ||||
|                 } | ||||
|                 if ( format == ChatColor.BOLD ) | ||||
|                 { | ||||
| @@ -111,15 +158,15 @@ public final class TextComponent extends BaseComponent | ||||
|                 } else if ( format == ChatColor.MAGIC ) | ||||
|                 { | ||||
|                     component.setObfuscated( true ); | ||||
|                 } else if ( format == ChatColor.RESET ) | ||||
|                 { | ||||
|                     format = defaultColor; | ||||
|                     component = new TextComponent(); | ||||
|                     component.setColor( format ); | ||||
|                 } else | ||||
|                 { | ||||
|                     if ( format == ChatColor.RESET ) | ||||
|                     { | ||||
|                         format = defaultColor; | ||||
|                     } | ||||
|                     component = new TextComponent(); | ||||
|                     component.setColor( format ); | ||||
|                     component.setReset( true ); | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
| @@ -137,7 +184,7 @@ public final class TextComponent extends BaseComponent | ||||
|                     component = new TextComponent( old ); | ||||
|                     old.setText( builder.toString() ); | ||||
|                     builder = new StringBuilder(); | ||||
|                     components.add( old ); | ||||
|                     appender.accept( old ); | ||||
|                 } | ||||
|  | ||||
|                 TextComponent old = component; | ||||
| @@ -146,7 +193,7 @@ public final class TextComponent extends BaseComponent | ||||
|                 component.setText( urlString ); | ||||
|                 component.setClickEvent( new ClickEvent( ClickEvent.Action.OPEN_URL, | ||||
|                         urlString.startsWith( "http" ) ? urlString : "http://" + urlString ) ); | ||||
|                 components.add( component ); | ||||
|                 appender.accept( component ); | ||||
|                 i += pos - i - 1; | ||||
|                 component = old; | ||||
|                 continue; | ||||
| @@ -155,9 +202,29 @@ public final class TextComponent extends BaseComponent | ||||
|         } | ||||
|  | ||||
|         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 | ||||
|     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 | ||||
| { | ||||
|  | ||||
|     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 | ||||
| @@ -30,6 +30,10 @@ public final class TranslatableComponent extends BaseComponent | ||||
|      * The components to substitute into the translation | ||||
|      */ | ||||
|     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. | ||||
| @@ -40,10 +44,11 @@ public final class TranslatableComponent extends BaseComponent | ||||
|     { | ||||
|         super( original ); | ||||
|         setTranslate( original.getTranslate() ); | ||||
|         setFallback( original.getFallback() ); | ||||
|  | ||||
|         if ( original.getWith() != null ) | ||||
|         { | ||||
|             List<BaseComponent> temp = new ArrayList<BaseComponent>(); | ||||
|             List<BaseComponent> temp = new ArrayList<>(); | ||||
|             for ( BaseComponent baseComponent : original.getWith() ) | ||||
|             { | ||||
|                 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. | ||||
|      * | ||||
| @@ -153,7 +173,12 @@ public final class TranslatableComponent extends BaseComponent | ||||
|     { | ||||
|         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 i = 0; | ||||
|         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.JsonSerializer; | ||||
| import java.lang.reflect.Type; | ||||
| import java.util.UUID; | ||||
| import net.md_5.bungee.api.chat.BaseComponent; | ||||
|  | ||||
| public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserializer<Entity> | ||||
| @@ -18,9 +19,19 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize | ||||
|     { | ||||
|         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( | ||||
|                 ( value.has( "type" ) ) ? value.get( "type" ).getAsString() : null, | ||||
|                 value.get( "id" ).getAsString(), | ||||
|                 idString, | ||||
|                 ( value.has( "name" ) ) ? context.deserialize( value.get( "name" ), BaseComponent.class ) : null | ||||
|         ); | ||||
|     } | ||||
| @@ -37,4 +48,9 @@ public class EntitySerializer implements JsonSerializer<Entity>, JsonDeserialize | ||||
|         } | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     public Text(BaseComponent value) | ||||
|     { | ||||
|         // For legacy serialization reasons, this has to be an array of components | ||||
|         this( new BaseComponent[] | ||||
|         { | ||||
|             value | ||||
|         } ); | ||||
|     } | ||||
|  | ||||
|     public Text(String value) | ||||
|     { | ||||
|         this.value = value; | ||||
|   | ||||
| @@ -4,16 +4,15 @@ import com.google.common.base.Preconditions; | ||||
| import com.google.gson.JsonDeserializationContext; | ||||
| import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | ||||
| import com.google.gson.JsonParseException; | ||||
| import com.google.gson.JsonSerializationContext; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.IdentityHashMap; | ||||
| 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.ClickEvent; | ||||
| import net.md_5.bungee.api.chat.ComponentStyle; | ||||
| import net.md_5.bungee.api.chat.HoverEvent; | ||||
| 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) | ||||
|     { | ||||
|         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() ) ); | ||||
|         } | ||||
|         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 ) ) ); | ||||
|             component.setInsertion( insertion.getAsString() ); | ||||
|         } | ||||
|  | ||||
|         //Events | ||||
|         if ( object.has( "clickEvent" ) ) | ||||
|         JsonObject clickEvent = object.getAsJsonObject( "clickEvent" ); | ||||
|         if ( clickEvent != null ) | ||||
|         { | ||||
|             JsonObject event = object.getAsJsonObject( "clickEvent" ); | ||||
|             component.setClickEvent( new ClickEvent( | ||||
|                     ClickEvent.Action.valueOf( event.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ), | ||||
|                     ( event.has( "value" ) ) ? event.get( "value" ).getAsString() : "" ) ); | ||||
|                     ClickEvent.Action.valueOf( clickEvent.get( "action" ).getAsString().toUpperCase( Locale.ROOT ) ), | ||||
|                     ( 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.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. | ||||
|                     // If the GSON is possible to be parsed as BaseComponent, attempt to parse as so. | ||||
|                     BaseComponent[] components; | ||||
|                     if ( contents.isJsonArray() ) | ||||
|                 // 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. | ||||
|                 BaseComponent[] components; | ||||
|                 if ( value.isJsonArray() ) | ||||
|                 { | ||||
|                     components = context.deserialize( value, BaseComponent[].class ); | ||||
|                 } else | ||||
|                 { | ||||
|                     components = new BaseComponent[] | ||||
|                     { | ||||
|                         components = context.deserialize( contents, BaseComponent[].class ); | ||||
|                     } else | ||||
|                     { | ||||
|                         components = new BaseComponent[] | ||||
|                         { | ||||
|                                 context.deserialize( contents, BaseComponent.class ) | ||||
|                         }; | ||||
|                     } | ||||
|                     hoverEvent = new HoverEvent( action, components ); | ||||
|                 } catch ( JsonParseException ex ) | ||||
|                         context.deserialize( value, BaseComponent.class ) | ||||
|                     }; | ||||
|                 } | ||||
|                 hoverEvent = new HoverEvent( action, components ); | ||||
|             } else | ||||
|             { | ||||
|                 JsonElement contents = hoverEventJson.get( "contents" ); | ||||
|                 if ( contents != null ) | ||||
|                 { | ||||
|                     Content[] list; | ||||
|                     if ( contents.isJsonArray() ) | ||||
| @@ -112,15 +79,19 @@ public class BaseComponentSerializer | ||||
|                     } | ||||
|                     hoverEvent = new HoverEvent( action, new ArrayList<>( Arrays.asList( list ) ) ); | ||||
|                 } | ||||
|  | ||||
|                 // stop the loop as soon as either one is found | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if ( hoverEvent != null ) | ||||
|             { | ||||
|                 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) | ||||
| @@ -135,44 +106,14 @@ public class BaseComponentSerializer | ||||
|         { | ||||
|             Preconditions.checkArgument( !ComponentSerializer.serializedComponents.get().contains( component ), "Component loop" ); | ||||
|             ComponentSerializer.serializedComponents.get().add( component ); | ||||
|             if ( component.getColorRaw() != null ) | ||||
|             { | ||||
|                 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() ); | ||||
|             } | ||||
|  | ||||
|             ComponentStyleSerializer.serializeTo( component.getStyle(), object ); | ||||
|  | ||||
|             if ( component.getInsertion() != null ) | ||||
|             { | ||||
|                 object.addProperty( "insertion", component.getInsertion() ); | ||||
|             } | ||||
|  | ||||
|             if ( component.getExtra() != null ) | ||||
|             { | ||||
|                 object.add( "extra", context.serialize( component.getExtra() ) ); | ||||
|             } | ||||
|  | ||||
|             //Events | ||||
|             if ( component.getClickEvent() != null ) | ||||
|             { | ||||
| @@ -195,6 +136,11 @@ public class BaseComponentSerializer | ||||
|                 } | ||||
|                 object.add( "hoverEvent", hoverEvent ); | ||||
|             } | ||||
|  | ||||
|             if ( component.getExtra() != null ) | ||||
|             { | ||||
|                 object.add( "extra", context.serialize( component.getExtra() ) ); | ||||
|             } | ||||
|         } finally | ||||
|         { | ||||
|             ComponentSerializer.serializedComponents.get().remove( component ); | ||||
|   | ||||
| @@ -2,15 +2,18 @@ package net.md_5.bungee.chat; | ||||
|  | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.GsonBuilder; | ||||
| import com.google.gson.JsonArray; | ||||
| 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.JsonParser; | ||||
| import com.google.gson.JsonPrimitive; | ||||
| import java.lang.reflect.Type; | ||||
| import java.util.Set; | ||||
| 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.KeybindComponent; | ||||
| 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> | ||||
| { | ||||
|  | ||||
|     private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
|     private static final Gson gson = new GsonBuilder(). | ||||
|             registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ). | ||||
|             registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ). | ||||
| @@ -35,6 +37,7 @@ public class ComponentSerializer implements JsonDeserializer<BaseComponent> | ||||
|             registerTypeAdapter( KeybindComponent.class, new KeybindComponentSerializer() ). | ||||
|             registerTypeAdapter( ScoreComponent.class, new ScoreComponentSerializer() ). | ||||
|             registerTypeAdapter( SelectorComponent.class, new SelectorComponentSerializer() ). | ||||
|             registerTypeAdapter( ComponentStyle.class, new ComponentStyleSerializer() ). | ||||
|             registerTypeAdapter( Entity.class, new EntitySerializer() ). | ||||
|             registerTypeAdapter( Text.class, new TextSerializer() ). | ||||
|             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>>(); | ||||
|  | ||||
|     /** | ||||
|      * 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) | ||||
|     { | ||||
|         JsonElement jsonElement = JSON_PARSER.parse( json ); | ||||
|         JsonElement jsonElement = JsonParser.parseString( json ); | ||||
|  | ||||
|         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) | ||||
|     { | ||||
|         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 | ||||
|     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 | ||||
|     { | ||||
|         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" ); | ||||
|         } | ||||
|         KeybindComponent component = new KeybindComponent(); | ||||
|         deserialize( object, component, context ); | ||||
|         component.setKeybind( object.get( "keybind" ).getAsString() ); | ||||
|         component.setKeybind( keybind.getAsString() ); | ||||
|         return component; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -17,22 +17,29 @@ public class ScoreComponentSerializer extends BaseComponentSerializer implements | ||||
|     public ScoreComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException | ||||
|     { | ||||
|         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" ); | ||||
|         } | ||||
|         JsonObject score = json.get( "score" ).getAsJsonObject(); | ||||
|         if ( !score.has( "name" ) || !score.has( "objective" ) ) | ||||
|         JsonElement nameJson = score.get( "name" ); | ||||
|         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" ); | ||||
|         } | ||||
|  | ||||
|         String name = score.get( "name" ).getAsString(); | ||||
|         String objective = score.get( "objective" ).getAsString(); | ||||
|         String name = nameJson.getAsString(); | ||||
|         String objective = objectiveJson.getAsString(); | ||||
|         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 ); | ||||
|   | ||||
| @@ -17,11 +17,19 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme | ||||
|     public SelectorComponent deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException | ||||
|     { | ||||
|         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" ); | ||||
|         } | ||||
|         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 ); | ||||
|         return component; | ||||
|     } | ||||
| @@ -32,6 +40,11 @@ public class SelectorComponentSerializer extends BaseComponentSerializer impleme | ||||
|         JsonObject object = new JsonObject(); | ||||
|         serialize( object, component, context ); | ||||
|         object.addProperty( "selector", component.getSelector() ); | ||||
|  | ||||
|         if ( component.getSeparator() != null ) | ||||
|         { | ||||
|             object.addProperty( "separator", ComponentSerializer.toString( component.getSeparator() ) ); | ||||
|         } | ||||
|         return object; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,8 +8,6 @@ import com.google.gson.JsonParseException; | ||||
| import com.google.gson.JsonSerializationContext; | ||||
| import com.google.gson.JsonSerializer; | ||||
| 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; | ||||
|  | ||||
| public class TextComponentSerializer extends BaseComponentSerializer implements JsonSerializer<TextComponent>, JsonDeserializer<TextComponent> | ||||
| @@ -20,11 +18,11 @@ public class TextComponentSerializer extends BaseComponentSerializer implements | ||||
|     { | ||||
|         TextComponent component = new TextComponent(); | ||||
|         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 ); | ||||
|         return component; | ||||
|     } | ||||
| @@ -32,13 +30,9 @@ public class TextComponentSerializer extends BaseComponentSerializer implements | ||||
|     @Override | ||||
|     public JsonElement serialize(TextComponent src, Type typeOfSrc, JsonSerializationContext context) | ||||
|     { | ||||
|         List<BaseComponent> extra = src.getExtra(); | ||||
|         JsonObject object = new JsonObject(); | ||||
|         serialize( object, src, context ); | ||||
|         object.addProperty( "text", src.getText() ); | ||||
|         if ( src.hasFormatting() || ( extra != null && !extra.isEmpty() ) ) | ||||
|         { | ||||
|             serialize( object, src, context ); | ||||
|         } | ||||
|         return object; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -21,14 +21,21 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp | ||||
|         TranslatableComponent component = new TranslatableComponent(); | ||||
|         JsonObject object = json.getAsJsonObject(); | ||||
|         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" ); | ||||
|         } | ||||
|         component.setTranslate( object.get( "translate" ).getAsString() ); | ||||
|         if ( object.has( "with" ) ) | ||||
|         component.setTranslate( translate.getAsString() ); | ||||
|         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; | ||||
|     } | ||||
| @@ -43,6 +50,10 @@ public class TranslatableComponentSerializer extends BaseComponentSerializer imp | ||||
|         { | ||||
|             object.add( "with", context.serialize( src.getWith() ) ); | ||||
|         } | ||||
|         if ( src.getFallback() != null ) | ||||
|         { | ||||
|             object.addProperty( "fallback", src.getFallback() ); | ||||
|         } | ||||
|         return object; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| package net.md_5.bungee.chat; | ||||
|  | ||||
| import com.google.common.base.Charsets; | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.JsonElement; | ||||
| import com.google.gson.JsonObject; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStreamReader; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.HashMap; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| @@ -102,7 +102,7 @@ public final class TranslationRegistry | ||||
|  | ||||
|         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 ); | ||||
|                 for ( Map.Entry<String, JsonElement> entries : obj.entrySet() ) | ||||
|   | ||||
| @@ -1,12 +1,17 @@ | ||||
| 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 net.md_5.bungee.api.ChatColor; | ||||
| import net.md_5.bungee.api.chat.hover.content.Item; | ||||
| import java.util.function.BiFunction; | ||||
| 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.chat.ComponentSerializer; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class ComponentsTest | ||||
| { | ||||
| @@ -15,13 +20,27 @@ public class ComponentsTest | ||||
|     { | ||||
|         String json = ComponentSerializer.toString( components ); | ||||
|         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 ); | ||||
|         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 | ||||
| @@ -41,16 +60,20 @@ public class ComponentsTest | ||||
|         { | ||||
|             textComponent | ||||
|         } ); | ||||
|         json = "{\"text\":\"Test\",\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:netherrack\\\",Count:47b}\"}]}}"; | ||||
|         testDissembleReassemble( json ); | ||||
|         testDissembleReassemble( textComponent ); | ||||
|         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}\"}"; | ||||
|         json = "{\"extra\":[{\"text\":\"[\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"]\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[" + hoverVal + "]},\"text\":\"\"}"; | ||||
|         components = ComponentSerializer.parse( json ); | ||||
|         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 ); | ||||
|         ////////// | ||||
|         // TODO: now ambiguous since "text" to distinguish Text from Item is not required | ||||
|         /* | ||||
|         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}}"; | ||||
|         Item contentItem = new Item( "minecraft:wood", 1, ItemTag.ofNbt( nbt ) ); | ||||
| @@ -59,25 +82,53 @@ public class ComponentsTest | ||||
|         json = ComponentSerializer.toString( component1 ); | ||||
|         components = ComponentSerializer.parse( json ); | ||||
|         Item parsedContentItem = ( (Item) components[0].getHoverEvent().getContents().get( 0 ) ); | ||||
|         Assert.assertEquals( contentItem, parsedContentItem ); | ||||
|         Assert.assertEquals( contentItem.getCount(), parsedContentItem.getCount() ); | ||||
|         Assert.assertEquals( contentItem.getId(), parsedContentItem.getId() ); | ||||
|         Assert.assertEquals( nbt, parsedContentItem.getTag().getNbt() ); | ||||
|         assertEquals( contentItem, parsedContentItem ); | ||||
|         assertEquals( contentItem.getCount(), parsedContentItem.getCount() ); | ||||
|         assertEquals( contentItem.getId(), parsedContentItem.getId() ); | ||||
|         assertEquals( nbt, parsedContentItem.getTag().getNbt() ); | ||||
|          */ | ||||
|     } | ||||
|  | ||||
|     @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(); | ||||
|  | ||||
|         BaseComponent[] parts = builder.create(); | ||||
|         Assert.assertEquals( parts.length, 0 ); | ||||
|         T component = componentBuilder.apply( builder ); | ||||
|         emptyAssertion.accept( component ); | ||||
|  | ||||
|         for ( int i = 0; i < 3; i++ ) | ||||
|         { | ||||
|             builder.append( "part:" + i ); | ||||
|             parts = builder.create(); | ||||
|             Assert.assertEquals( parts.length, i + 1 ); | ||||
|             component = componentBuilder.apply( builder ); | ||||
|             sizedAssertion.accept( component, i + 1 ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -85,23 +136,34 @@ public class ComponentsTest | ||||
|     public void testDummyRetaining() | ||||
|     { | ||||
|         ComponentBuilder builder = new ComponentBuilder(); | ||||
|         Assert.assertNotNull( builder.getCurrentComponent() ); | ||||
|         builder.color( ChatColor.GREEN ); | ||||
|         assertNotNull( builder.getCurrentComponent() ); | ||||
|         builder.color( GREEN ); | ||||
|         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() | ||||
|     { | ||||
|         ComponentBuilder builder = new ComponentBuilder(); | ||||
|         builder.getComponent( -1 ); | ||||
|         builder.getComponent( 0 ); | ||||
|         builder.getComponent( 1 ); | ||||
|         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( -1 ) ); | ||||
|         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 0 ) ); | ||||
|         assertThrows( IndexOutOfBoundsException.class, () -> builder.getComponent( 1 ) ); | ||||
|         BaseComponent component = new TextComponent( "Hello" ); | ||||
|         builder.append( component ); | ||||
|         Assert.assertEquals( builder.getComponent( 0 ), component ); | ||||
|         builder.getComponent( 1 ); | ||||
|         assertEquals( builder.getComponent( 0 ), component ); | ||||
|         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 | ||||
| @@ -110,33 +172,33 @@ public class ComponentsTest | ||||
|         ComponentBuilder builder = new ComponentBuilder(); | ||||
|         TextComponent apple = new TextComponent( "apple" ); | ||||
|         builder.append( apple ); | ||||
|         Assert.assertEquals( builder.getCurrentComponent(), apple ); | ||||
|         Assert.assertEquals( builder.getComponent( 0 ), apple ); | ||||
|         assertEquals( builder.getCurrentComponent(), apple ); | ||||
|         assertEquals( builder.getComponent( 0 ), apple ); | ||||
|  | ||||
|         TextComponent mango = new TextComponent( "mango" ); | ||||
|         TextComponent orange = new TextComponent( "orange" ); | ||||
|         builder.append( mango ); | ||||
|         builder.append( orange ); | ||||
|         builder.removeComponent( 1 ); // Removing mango | ||||
|         Assert.assertEquals( builder.getComponent( 0 ), apple ); | ||||
|         Assert.assertEquals( builder.getComponent( 1 ), orange ); | ||||
|         assertEquals( builder.getComponent( 0 ), apple ); | ||||
|         assertEquals( builder.getComponent( 1 ), orange ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testToLegacyFromLegacy() | ||||
|     { | ||||
|         String text = "§a§lHello §f§kworld§7!"; | ||||
|         Assert.assertEquals( text, TextComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) ); | ||||
|         String text = "" + GREEN + BOLD + "Hello " + WHITE + MAGIC + "world" + GRAY + "!"; | ||||
|         assertEquals( text, BaseComponent.toLegacyText( TextComponent.fromLegacyText( text ) ) ); | ||||
|     } | ||||
|  | ||||
|     @Test(expected = IndexOutOfBoundsException.class) | ||||
|     @Test | ||||
|     public void testComponentBuilderCursorInvalidPos() | ||||
|     { | ||||
|         ComponentBuilder builder = new ComponentBuilder(); | ||||
|         builder.append( new TextComponent( "Apple, " ) ); | ||||
|         builder.append( new TextComponent( "Orange, " ) ); | ||||
|         builder.setCursor( -1 ); | ||||
|         builder.setCursor( 2 ); | ||||
|         assertThrows( IndexOutOfBoundsException.class, () -> builder.setCursor( -1 ) ); | ||||
|         assertThrows( IndexOutOfBoundsException.class, () -> builder.setCursor( 2 ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -144,33 +206,33 @@ public class ComponentsTest | ||||
|     { | ||||
|         TextComponent t1, t2, t3; | ||||
|         ComponentBuilder builder = new ComponentBuilder(); | ||||
|         Assert.assertEquals( builder.getCursor(), -1 ); | ||||
|         assertEquals( builder.getCursor(), -1 ); | ||||
|         builder.append( t1 = new TextComponent( "Apple, " ) ); | ||||
|         Assert.assertEquals( builder.getCursor(), 0 ); | ||||
|         assertEquals( builder.getCursor(), 0 ); | ||||
|         builder.append( t2 = new TextComponent( "Orange, " ) ); | ||||
|         builder.append( t3 = new TextComponent( "Mango, " ) ); | ||||
|         Assert.assertEquals( builder.getCursor(), 2 ); | ||||
|         assertEquals( builder.getCursor(), 2 ); | ||||
|  | ||||
|         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 | ||||
|         // after having previously set it to 0 (first component) | ||||
|         builder.append( new TextComponent( "and Grapefruit" ) ); | ||||
|         Assert.assertEquals( builder.getCursor(), 3 ); | ||||
|         assertEquals( builder.getCursor(), 3 ); | ||||
|  | ||||
|         builder.setCursor( 0 ); | ||||
|         builder.resetCursor(); | ||||
|         Assert.assertEquals( builder.getCursor(), 3 ); | ||||
|         assertEquals( builder.getCursor(), 3 ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     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[] builderComponents = new ComponentBuilder().append( components ).create(); | ||||
|         Assert.assertArrayEquals( components, builderComponents ); | ||||
|         assertArrayEquals( components, builderComponents ); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
| @@ -192,7 +254,7 @@ public class ComponentsTest | ||||
|         component.setHoverEvent( event ); | ||||
|         String serialised = ComponentSerializer.toString( component ); | ||||
|         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" ); | ||||
|         component.setHoverEvent( hoverEvent ); | ||||
|         Assert.assertEquals( component.getHoverEvent().getContents().size(), 1 ); | ||||
|         Assert.assertTrue( component.getHoverEvent().getContents().get( 0 ) instanceof Text ); | ||||
|         Assert.assertEquals( ( (Text) component.getHoverEvent().getContents().get( 0 ) ).getValue(), advancement ); | ||||
|         assertEquals( component.getHoverEvent().getContents().size(), 1 ); | ||||
|         assertTrue( component.getHoverEvent().getContents().get( 0 ) instanceof Text ); | ||||
|         assertEquals( ( (Text) component.getHoverEvent().getContents().get( 0 ) ).getValue(), advancement ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testHoverEventContents() | ||||
|     public void testHoverEventContentsCreate() | ||||
|     { | ||||
|         // First do the text using the newer contents system | ||||
|         HoverEvent hoverEvent = new HoverEvent( | ||||
| @@ -222,21 +284,53 @@ public class ComponentsTest | ||||
|                 new Text( new ComponentBuilder( "Second" ).create() ) | ||||
|         ); | ||||
|  | ||||
|         TextComponent component = new TextComponent( "Sample text" ); | ||||
|         component.setHoverEvent( hoverEvent ); | ||||
|         Assert.assertEquals( hoverEvent.getContents().size(), 2 ); | ||||
|         Assert.assertFalse( hoverEvent.isLegacy() ); | ||||
|         String serialized = ComponentSerializer.toString( component ); | ||||
|         BaseComponent[] deserialized = ComponentSerializer.parse( serialized ); | ||||
|         Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); | ||||
|         this.testHoverEventContents( | ||||
|                 hoverEvent, | ||||
|                 ComponentSerializer::parse, | ||||
|                 (components) -> components[0].getHoverEvent(), | ||||
|                 ComponentsTest::testDissembleReassemble // BaseComponent | ||||
|         ); | ||||
|  | ||||
|         // check the test still works with the value method | ||||
|         hoverEvent = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder( "Sample text" ).create() ); | ||||
|         Assert.assertEquals( hoverEvent.getContents().size(), 1 ); | ||||
|         Assert.assertTrue( hoverEvent.isLegacy() ); | ||||
|         serialized = ComponentSerializer.toString( component ); | ||||
|         deserialized = ComponentSerializer.parse( serialized ); | ||||
|         Assert.assertEquals( component.getHoverEvent(), deserialized[0].getHoverEvent() ); | ||||
|         TextComponent component = new TextComponent( "Sample text" ); | ||||
|         component.setHoverEvent( hoverEvent ); | ||||
|  | ||||
|         assertEquals( hoverEvent.getContents().size(), 1 ); | ||||
|         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: | ||||
|         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\":" | ||||
|                 + "{\"type\":\"minecraft:player\",\"id\":\"00000000-0000-0000-0000-00000000000000\",\"name\":" | ||||
|                 + "{\"text\":\"Name\"}}},\"text\":\"Name\"}]}]}"; | ||||
|         testDissembleReassemble( ComponentSerializer.parse( json ) ); | ||||
|         dissembleReassembleTest.accept( deserializer.apply( json ) ); | ||||
|     } | ||||
|  | ||||
|     @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" ); | ||||
|         first.setBold( true ); | ||||
|         first.setColor( ChatColor.RED ); | ||||
|         first.setColor( RED ); | ||||
|         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" ); | ||||
|         second.copyFormatting( first, ComponentBuilder.FormatRetention.ALL, true ); | ||||
|         Assert.assertEquals( first.isBold(), second.isBold() ); | ||||
|         Assert.assertEquals( first.getColor(), second.getColor() ); | ||||
|         Assert.assertEquals( first.getClickEvent(), second.getClickEvent() ); | ||||
|         Assert.assertEquals( first.getHoverEvent(), second.getHoverEvent() ); | ||||
|         assertEquals( first.isBold(), second.isBold() ); | ||||
|         assertEquals( first.getColor(), second.getColor() ); | ||||
|         assertEquals( first.getClickEvent(), second.getClickEvent() ); | ||||
|         assertEquals( first.getHoverEvent(), second.getHoverEvent() ); | ||||
|     } | ||||
|  | ||||
|     @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 ); | ||||
|  | ||||
|         Assert.assertEquals( TextComponent.toLegacyText( builder.create() ), TextComponent.toLegacyText( cloned.create() ) ); | ||||
|         assertEquals( legacyTextFunction.apply( builder ), legacyTextFunction.apply( cloned ) ); | ||||
|     } | ||||
|  | ||||
|     @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 " ); | ||||
|         TextComponent textComponent = new TextComponent( "world " ); | ||||
| @@ -291,11 +424,11 @@ public class ComponentsTest | ||||
|         } ); | ||||
|         ScoreComponent scoreComponent = new ScoreComponent( "myscore", "myobjective" ); | ||||
|         builder.append( scoreComponent ); // non array based BaseComponent append | ||||
|         BaseComponent[] components = builder.create(); | ||||
|         Assert.assertEquals( "Hello ", components[0].toPlainText() ); | ||||
|         Assert.assertEquals( textComponent.toPlainText(), components[1].toPlainText() ); | ||||
|         Assert.assertEquals( translatableComponent.toPlainText(), components[2].toPlainText() ); | ||||
|         Assert.assertEquals( scoreComponent.toPlainText(), components[3].toPlainText() ); | ||||
|         T component = componentBuilder.apply( builder ); | ||||
|         assertEquals( "Hello ", extraGetter.apply( component, 0 ).toPlainText() ); | ||||
|         assertEquals( textComponent.toPlainText(), extraGetter.apply( component, 1 ).toPlainText() ); | ||||
|         assertEquals( translatableComponent.toPlainText(), extraGetter.apply( component, 2 ).toPlainText() ); | ||||
|         assertEquals( scoreComponent.toPlainText(), extraGetter.apply( component, 3 ).toPlainText() ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -305,171 +438,261 @@ public class ComponentsTest | ||||
|         String text = ComponentSerializer.toString( component ); | ||||
|         BaseComponent[] reparsed = ComponentSerializer.parse( text ); | ||||
|  | ||||
|         Assert.assertArrayEquals( component, reparsed ); | ||||
|         assertArrayEquals( component, reparsed ); | ||||
|     } | ||||
|  | ||||
|     @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 " ); | ||||
|         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 ); | ||||
|         builder.append( new ComponentBuilder( "world!" ).color( ChatColor.GREEN ).event( hoverEvent ).event( clickEvent ).create() ); | ||||
|         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( YELLOW ); | ||||
|         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 ); | ||||
|         Assert.assertEquals( components[1].getClickEvent(), clickEvent ); | ||||
|         Assert.assertEquals( "Hello world!", BaseComponent.toPlainText( components ) ); | ||||
|         Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) ); | ||||
|         assertEquals( extraGetter.apply( component, 1 ).getHoverEvent(), hoverEvent ); | ||||
|         assertEquals( extraGetter.apply( component, 1 ).getClickEvent(), clickEvent ); | ||||
|         assertEquals( "Hello world!", toPlainTextFunction.apply( component ) ); | ||||
|         assertEquals( expectedLegacyText, toLegacyTextFunction.apply( component ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuilderAppendLegacy() | ||||
|     public void testBuilderAppendLegacyCreate() | ||||
|     { | ||||
|         ComponentBuilder builder = new ComponentBuilder( "Hello " ).color( ChatColor.YELLOW ); | ||||
|         builder.appendLegacy( "§aworld!" ); | ||||
|         testBuilderAppendLegacy( | ||||
|                 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 ) ); | ||||
|         Assert.assertEquals( ChatColor.YELLOW + "Hello " + ChatColor.GREEN + "world!", BaseComponent.toLegacyText( components ) ); | ||||
|     private static <T> void testBuilderAppendLegacy(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction) | ||||
|     { | ||||
|         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 | ||||
|     public void testBasicComponent() | ||||
|     { | ||||
|         TextComponent textComponent = new TextComponent( "Hello world" ); | ||||
|         textComponent.setColor( ChatColor.RED ); | ||||
|         textComponent.setColor( RED ); | ||||
|  | ||||
|         Assert.assertEquals( "Hello world", textComponent.toPlainText() ); | ||||
|         Assert.assertEquals( ChatColor.RED + "Hello world", textComponent.toLegacyText() ); | ||||
|         assertEquals( "Hello world", textComponent.toPlainText() ); | ||||
|         assertEquals( RED + "Hello world", textComponent.toLegacyText() ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     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 ) ); | ||||
|         Assert.assertEquals( ChatColor.AQUA + "Aqua " + ChatColor.RED + ChatColor.BOLD + "RedBold", BaseComponent.toLegacyText( test1 ) ); | ||||
|         assertEquals( "Aqua RedBold", BaseComponent.toPlainText( 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 | ||||
|         Assert.assertEquals( ChatColor.WHITE + "Text " + ChatColor.WHITE + "http://spigotmc.org" + ChatColor.WHITE | ||||
|                 + " " + ChatColor.GREEN + "google.com/test" + ChatColor.GREEN, BaseComponent.toLegacyText( test2 ) ); | ||||
|         assertEquals( WHITE + "Text " + WHITE + "http://spigotmc.org" + WHITE | ||||
|                 + " " + GREEN + "google.com/test" + GREEN, BaseComponent.toLegacyText( test2 ) ); | ||||
|  | ||||
|         ClickEvent url1 = test2[1].getClickEvent(); | ||||
|         Assert.assertNotNull( url1 ); | ||||
|         Assert.assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL ); | ||||
|         Assert.assertEquals( "http://spigotmc.org", url1.getValue() ); | ||||
|         assertNotNull( url1 ); | ||||
|         assertTrue( url1.getAction() == ClickEvent.Action.OPEN_URL ); | ||||
|         assertEquals( "http://spigotmc.org", url1.getValue() ); | ||||
|  | ||||
|         ClickEvent url2 = test2[3].getClickEvent(); | ||||
|         Assert.assertNotNull( url2 ); | ||||
|         Assert.assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL ); | ||||
|         Assert.assertEquals( "http://google.com/test", url2.getValue() ); | ||||
|         assertNotNull( url2 ); | ||||
|         assertTrue( url2.getAction() == ClickEvent.Action.OPEN_URL ); | ||||
|         assertEquals( "http://google.com/test", url2.getValue() ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testTranslateComponent() | ||||
|     public void testBuilderCreate() | ||||
|     { | ||||
|         TranslatableComponent item = new TranslatableComponent( "item.swordGold.name" ); | ||||
|         item.setColor( ChatColor.AQUA ); | ||||
|         TranslatableComponent translatableComponent = new TranslatableComponent( "commands.give.success", | ||||
|                 item, "5", | ||||
|                 "thinkofdeath" ); | ||||
|  | ||||
|         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() ); | ||||
|         testBuilder( | ||||
|                 ComponentBuilder::create, | ||||
|                 BaseComponent::toPlainText, | ||||
|                 RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!", | ||||
|                 BaseComponent::toLegacyText | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuilder() | ||||
|     public void testBuilderBuild() | ||||
|     { | ||||
|         BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ). | ||||
|                 append( "World" ).bold( true ).color( ChatColor.BLUE ). | ||||
|                 append( "!" ).color( ChatColor.YELLOW ).create(); | ||||
|         testBuilder( | ||||
|                 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() + RED + "Hello " + BLUE + BOLD + "World" + YELLOW + BOLD + "!", | ||||
|                 (component) -> BaseComponent.toLegacyText( component ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|         Assert.assertEquals( "Hello World!", BaseComponent.toPlainText( components ) ); | ||||
|         Assert.assertEquals( ChatColor.RED + "Hello " + ChatColor.BLUE + ChatColor.BOLD | ||||
|                 + "World" + ChatColor.YELLOW + ChatColor.BOLD + "!", BaseComponent.toLegacyText( components ) ); | ||||
|     private static <T> void testBuilder(Function<ComponentBuilder, T> componentBuilder, Function<T, String> toPlainTextFunction, String expectedLegacyString, Function<T, String> toLegacyTextFunction) | ||||
|     { | ||||
|         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 | ||||
|     public void testBuilderReset() | ||||
|     public void testBuilderCreateReset() | ||||
|     { | ||||
|         BaseComponent[] components = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) | ||||
|                 .append( "World" ).reset().create(); | ||||
|  | ||||
|         Assert.assertEquals( components[0].getColor(), ChatColor.RED ); | ||||
|         Assert.assertEquals( components[1].getColor(), ChatColor.WHITE ); | ||||
|         testBuilderReset( | ||||
|                 ComponentBuilder::create, | ||||
|                 (components, index) -> components[index] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testBuilderFormatRetention() | ||||
|     public void testBuilderBuildReset() | ||||
|     { | ||||
|         BaseComponent[] noneRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) | ||||
|                 .append( "World", ComponentBuilder.FormatRetention.NONE ).create(); | ||||
|         testBuilderReset( | ||||
|                 ComponentBuilder::build, | ||||
|                 (component, index) -> component.getExtra().get( index ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|         Assert.assertEquals( noneRetention[0].getColor(), ChatColor.RED ); | ||||
|         Assert.assertEquals( noneRetention[1].getColor(), ChatColor.WHITE ); | ||||
|     private static <T> void testBuilderReset(Function<ComponentBuilder, T> componentBuilder, BiFunction<T, Integer, BaseComponent> extraGetter) | ||||
|     { | ||||
|         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 ) | ||||
|                 .event( testEvent ).append( "World", ComponentBuilder.FormatRetention.FORMATTING ).create(); | ||||
|     @Test | ||||
|     public void testBuilderCreateFormatRetention() | ||||
|     { | ||||
|         testBuilderFormatRetention( | ||||
|                 ComponentBuilder::create, | ||||
|                 (components, index) -> components[index] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|         Assert.assertEquals( formattingRetention[0].getColor(), ChatColor.RED ); | ||||
|         Assert.assertEquals( formattingRetention[0].getHoverEvent(), testEvent ); | ||||
|         Assert.assertEquals( formattingRetention[1].getColor(), ChatColor.RED ); | ||||
|         Assert.assertNull( formattingRetention[1].getHoverEvent() ); | ||||
|     @Test | ||||
|     public void testBuilderBuildFormatRetention() | ||||
|     { | ||||
|         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" ); | ||||
|  | ||||
|         BaseComponent[] eventRetention = new ComponentBuilder( "Hello " ).color( ChatColor.RED ) | ||||
|                 .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ).create(); | ||||
|         T eventRetention = componentBuilder.apply( new ComponentBuilder( "Hello " ).color( RED ) | ||||
|                 .event( testEvent ).event( testClickEvent ).append( "World", ComponentBuilder.FormatRetention.EVENTS ) ); | ||||
|  | ||||
|         Assert.assertEquals( eventRetention[0].getColor(), ChatColor.RED ); | ||||
|         Assert.assertEquals( eventRetention[0].getHoverEvent(), testEvent ); | ||||
|         Assert.assertEquals( eventRetention[0].getClickEvent(), testClickEvent ); | ||||
|         Assert.assertEquals( eventRetention[1].getColor(), ChatColor.WHITE ); | ||||
|         Assert.assertEquals( eventRetention[1].getHoverEvent(), testEvent ); | ||||
|         Assert.assertEquals( eventRetention[1].getClickEvent(), testClickEvent ); | ||||
|         assertEquals( RED, extraGetter.apply( eventRetention, 0 ).getColor() ); | ||||
|         assertEquals( testEvent, extraGetter.apply( eventRetention, 0 ).getHoverEvent() ); | ||||
|         assertEquals( testClickEvent, extraGetter.apply( eventRetention, 0 ).getClickEvent() ); | ||||
|         assertEquals( WHITE, extraGetter.apply( eventRetention, 1 ).getColor() ); | ||||
|         assertEquals( testEvent, extraGetter.apply( eventRetention, 1 ).getHoverEvent() ); | ||||
|         assertEquals( testClickEvent, extraGetter.apply( eventRetention, 1 ).getClickEvent() ); | ||||
|     } | ||||
|  | ||||
|     @Test(expected = IllegalArgumentException.class) | ||||
|     @Test | ||||
|     public void testLoopSimple() | ||||
|     { | ||||
|         TextComponent component = new TextComponent( "Testing" ); | ||||
|         component.addExtra( component ); | ||||
|         ComponentSerializer.toString( component ); | ||||
|         assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( component ) ); | ||||
|     } | ||||
|  | ||||
|     @Test(expected = IllegalArgumentException.class) | ||||
|     @Test | ||||
|     public void testLoopComplex() | ||||
|     { | ||||
|         TextComponent a = new TextComponent( "A" ); | ||||
|         TextComponent b = new TextComponent( "B" ); | ||||
|         b.setColor( ChatColor.AQUA ); | ||||
|         b.setColor( AQUA ); | ||||
|         TextComponent c = new TextComponent( "C" ); | ||||
|         c.setColor( ChatColor.RED ); | ||||
|         c.setColor( RED ); | ||||
|         a.addExtra( b ); | ||||
|         b.addExtra( c ); | ||||
|         c.addExtra( a ); | ||||
|         ComponentSerializer.toString( a ); | ||||
|         assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( a ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -477,25 +700,25 @@ public class ComponentsTest | ||||
|     { | ||||
|         TextComponent a = new TextComponent( "A" ); | ||||
|         TextComponent b = new TextComponent( "B" ); | ||||
|         b.setColor( ChatColor.AQUA ); | ||||
|         b.setColor( AQUA ); | ||||
|         a.addExtra( b ); | ||||
|         a.addExtra( b ); | ||||
|         ComponentSerializer.toString( a ); | ||||
|     } | ||||
|  | ||||
|     @Test(expected = IllegalArgumentException.class) | ||||
|     @Test | ||||
|     public void testRepeatedError() | ||||
|     { | ||||
|         TextComponent a = new TextComponent( "A" ); | ||||
|         TextComponent b = new TextComponent( "B" ); | ||||
|         b.setColor( ChatColor.AQUA ); | ||||
|         b.setColor( AQUA ); | ||||
|         TextComponent c = new TextComponent( "C" ); | ||||
|         c.setColor( ChatColor.RED ); | ||||
|         c.setColor( RED ); | ||||
|         a.addExtra( b ); | ||||
|         a.addExtra( c ); | ||||
|         c.addExtra( a ); | ||||
|         a.addExtra( b ); | ||||
|         ComponentSerializer.toString( a ); | ||||
|         assertThrows( IllegalArgumentException.class, () -> ComponentSerializer.toString( a ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -506,35 +729,35 @@ public class ComponentsTest | ||||
|         // collect all invalid color codes (e.g. §z, §g, ...) | ||||
|         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 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // last char is a single '§' | ||||
|         allInvalidColorCodes.append( ChatColor.COLOR_CHAR ); | ||||
|         allInvalidColorCodes.append( COLOR_CHAR ); | ||||
|  | ||||
|         String invalidColorCodesLegacyText = fromAndToLegacyText( allInvalidColorCodes.toString() ); | ||||
|         String emptyLegacyText = fromAndToLegacyText( "" ); | ||||
|  | ||||
|         // all invalid color codes and the trailing '§' should be ignored | ||||
|         Assert.assertEquals( emptyLegacyText, invalidColorCodesLegacyText ); | ||||
|         assertEquals( emptyLegacyText, invalidColorCodesLegacyText ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testFormattingOnlyTextConversion() | ||||
|     { | ||||
|         String text = "§a"; | ||||
|         String text = "" + GREEN; | ||||
|  | ||||
|         BaseComponent[] converted = TextComponent.fromLegacyText( text ); | ||||
|         Assert.assertEquals( ChatColor.GREEN, converted[0].getColor() ); | ||||
|         assertEquals( GREEN, converted[0].getColor() ); | ||||
|  | ||||
|         String roundtripLegacyText = BaseComponent.toLegacyText( converted ); | ||||
|  | ||||
|         // color code should not be lost during conversion | ||||
|         Assert.assertEquals( text, roundtripLegacyText ); | ||||
|         assertEquals( text, roundtripLegacyText ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -546,7 +769,7 @@ public class ComponentsTest | ||||
|         TextComponent second = new TextComponent( "Hello, " ); | ||||
|         second.addExtra( new TextComponent( "World!" ) ); | ||||
|  | ||||
|         Assert.assertEquals( first, second ); | ||||
|         assertEquals( first, second ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -558,21 +781,90 @@ public class ComponentsTest | ||||
|         TextComponent second = new TextComponent( "Hello, " ); | ||||
|         second.addExtra( new TextComponent( "World!" ) ); | ||||
|  | ||||
|         Assert.assertNotEquals( first, second ); | ||||
|         assertNotEquals( first, second ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testLegacyHack() | ||||
|     { | ||||
|         BaseComponent[] hexColored = new ComponentBuilder().color( ChatColor.of( Color.GRAY ) ).append( "Test" ).create(); | ||||
|         String legacy = TextComponent.toLegacyText( hexColored ); | ||||
|         BaseComponent[] hexColored = new ComponentBuilder().color( of( Color.GRAY ) ).append( "Test" ).create(); | ||||
|         String legacy = BaseComponent.toLegacyText( hexColored ); | ||||
|  | ||||
|         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 ) ); | ||||
|     } | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| 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 org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class TranslatableComponentTest | ||||
| { | ||||
| @@ -11,8 +12,8 @@ public class TranslatableComponentTest | ||||
|     public void testMissingPlaceholdersAdded() | ||||
|     { | ||||
|         TranslatableComponent testComponent = new TranslatableComponent( "Test string with %s placeholders: %s", 2, "aoeu" ); | ||||
|         Assert.assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() ); | ||||
|         Assert.assertEquals( "§fTest string with §f2§f placeholders: §faoeu", testComponent.toLegacyText() ); | ||||
|         assertEquals( "Test string with 2 placeholders: aoeu", testComponent.toPlainText() ); | ||||
|         assertEquals( WHITE + "Test string with " + WHITE + "2" + WHITE + " placeholders: " + WHITE + "aoeu", testComponent.toLegacyText() ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| @@ -22,7 +23,29 @@ public class TranslatableComponentTest | ||||
|         String jsonString = ComponentSerializer.toString( testComponent ); | ||||
|         BaseComponent[] baseComponents = ComponentSerializer.parse( jsonString ); | ||||
|  | ||||
|         Assert.assertEquals( "Test string with a placeholder", TextComponent.toPlainText( baseComponents ) ); | ||||
|         Assert.assertEquals( "§fTest string with §fa§f placeholder", TextComponent.toLegacyText( baseComponents ) ); | ||||
|         assertEquals( "Test string with a placeholder", BaseComponent.toPlainText( 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 --> | ||||
|         <module name="SuppressionCommentFilter"/> | ||||
|         <module name="SuppressWarningsHolder"/> | ||||
|  | ||||
|         <!-- See http://checkstyle.sourceforge.net/config_imports.html --> | ||||
|         <module name="AvoidStarImport"/> | ||||
|         <module name="ImportOrder"> | ||||
|             <property name="option" value="above"/> | ||||
|             <property name="ordered" value="true"/> | ||||
| @@ -54,11 +54,11 @@ | ||||
|         <module name="OperatorWrap"/> | ||||
|         <module name="ParenPad"> | ||||
|             <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 name="ParenPad"> | ||||
|             <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 name="SingleSpaceSeparator"/> | ||||
|         <module name="TypecastParenPad"/> | ||||
| @@ -84,4 +84,6 @@ | ||||
|         <module name="Indentation"/> | ||||
|         <module name="UpperEll"/> | ||||
|     </module> | ||||
|  | ||||
|     <module name="SuppressWarningsFilter"/> | ||||
| </module> | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-config</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Config</name> | ||||
| @@ -22,14 +22,14 @@ | ||||
|         <dependency> | ||||
|             <groupId>com.google.code.gson</groupId> | ||||
|             <artifactId>gson</artifactId> | ||||
|             <version>2.8.0</version> | ||||
|             <version>2.10.1</version> | ||||
|             <scope>compile</scope> | ||||
|             <optional>true</optional> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.yaml</groupId> | ||||
|             <artifactId>snakeyaml</artifactId> | ||||
|             <version>1.26</version> | ||||
|             <version>2.2</version> | ||||
|             <scope>compile</scope> | ||||
|             <optional>true</optional> | ||||
|         </dependency> | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package net.md_5.bungee.config; | ||||
|  | ||||
| import com.google.common.base.Charsets; | ||||
| import com.google.gson.Gson; | ||||
| import com.google.gson.GsonBuilder; | ||||
| import com.google.gson.JsonElement; | ||||
| @@ -16,6 +15,7 @@ import java.io.OutputStreamWriter; | ||||
| import java.io.Reader; | ||||
| import java.io.Writer; | ||||
| import java.lang.reflect.Type; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.Map; | ||||
| import lombok.AccessLevel; | ||||
| @@ -37,7 +37,7 @@ public class JsonConfiguration extends ConfigurationProvider | ||||
|     @Override | ||||
|     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 ); | ||||
|         } | ||||
| @@ -91,7 +91,7 @@ public class JsonConfiguration extends ConfigurationProvider | ||||
|     @Override | ||||
|     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 | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package net.md_5.bungee.config; | ||||
|  | ||||
| import com.google.common.base.Charsets; | ||||
| import java.io.File; | ||||
| import java.io.FileInputStream; | ||||
| import java.io.FileOutputStream; | ||||
| @@ -9,11 +8,13 @@ import java.io.InputStream; | ||||
| import java.io.OutputStreamWriter; | ||||
| import java.io.Reader; | ||||
| import java.io.Writer; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.LinkedHashMap; | ||||
| import java.util.Map; | ||||
| import lombok.AccessLevel; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.yaml.snakeyaml.DumperOptions; | ||||
| import org.yaml.snakeyaml.LoaderOptions; | ||||
| import org.yaml.snakeyaml.Yaml; | ||||
| import org.yaml.snakeyaml.constructor.Constructor; | ||||
| import org.yaml.snakeyaml.nodes.Node; | ||||
| @@ -29,7 +30,10 @@ public class YamlConfiguration extends ConfigurationProvider | ||||
|         @Override | ||||
|         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() | ||||
| @@ -43,17 +47,14 @@ public class YamlConfiguration extends ConfigurationProvider | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             DumperOptions options = new DumperOptions(); | ||||
|             options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK ); | ||||
|  | ||||
|             return new Yaml( new Constructor(), representer, options ); | ||||
|             return new Yaml( new Constructor( new LoaderOptions() ), representer, options ); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     @Override | ||||
|     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 ); | ||||
|         } | ||||
|   | ||||
| @@ -1,148 +1,138 @@ | ||||
| package net.md_5.bungee.config; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import java.io.StringReader; | ||||
| import java.io.StringWriter; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.stream.Stream; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.junit.runners.Parameterized; | ||||
| import org.junit.runners.Parameterized.Parameters; | ||||
| import org.junit.jupiter.params.ParameterizedTest; | ||||
| import org.junit.jupiter.params.provider.Arguments; | ||||
| import org.junit.jupiter.params.provider.MethodSource; | ||||
|  | ||||
| @RequiredArgsConstructor | ||||
| @RunWith(Parameterized.class) | ||||
| public class CompoundConfigurationTest | ||||
| { | ||||
|  | ||||
|     @Parameters(name = "{0}") | ||||
|     public static Iterable<Object[]> data() | ||||
|     public static Stream<Arguments> data() | ||||
|     { | ||||
|         // CHECKSTYLE:OFF | ||||
|         return Arrays.asList( new Object[][] | ||||
|         { | ||||
|             { | ||||
|                 // provider | ||||
|                 YamlConfiguration.class, | ||||
|                 // testDocument | ||||
|                 "" | ||||
|                 + "receipt:     Oz-Ware Purchase Invoice\n" | ||||
|                 + "date:        2012-08-06\n" | ||||
|                 + "customer:\n" | ||||
|                 + "    given:   Dorothy\n" | ||||
|                 + "    family:  Gale\n" | ||||
|                 + "\n" | ||||
|                 + "items:\n" | ||||
|                 + "    - part_no:   A4786\n" | ||||
|                 + "      descrip:   Water Bucket (Filled)\n" | ||||
|                 + "      price:     1.47\n" | ||||
|                 + "      quantity:  4\n" | ||||
|                 + "\n" | ||||
|                 + "    - part_no:   E1628\n" | ||||
|                 + "      descrip:   High Heeled \"Ruby\" Slippers\n" | ||||
|                 + "      size:      8\n" | ||||
|                 + "      price:     100.27\n" | ||||
|                 + "      quantity:  1\n" | ||||
|                 + "\n" | ||||
|                 + "bill-to:  &id001\n" | ||||
|                 + "    street: |\n" | ||||
|                 + "            123 Tornado Alley\n" | ||||
|                 + "            Suite 16\n" | ||||
|                 + "    city:   East Centerville\n" | ||||
|                 + "    state:  KS\n" | ||||
|                 + "\n" | ||||
|                 + "ship-to:  *id001\n" | ||||
|                 + "\n" | ||||
|                 + "specialDelivery:  >\n" | ||||
|                 + "    Follow the Yellow Brick\n" | ||||
|                 + "    Road to the Emerald City.\n" | ||||
|                 + "    Pay no attention to the\n" | ||||
|                 + "    man behind the curtain.", | ||||
|                 // numberTest | ||||
|                 "" | ||||
|                 + "someKey:\n" | ||||
|                 + "    1: 1\n" | ||||
|                 + "    2: 2\n" | ||||
|                 + "    3: 3\n" | ||||
|                 + "    4: 4", | ||||
|                 // nullTest | ||||
|                 "" | ||||
|                 + "null:\n" | ||||
|                 + "    null: object\n" | ||||
|                 + "    object: null\n" | ||||
|             }, | ||||
|             { | ||||
|                 // provider | ||||
|                 JsonConfiguration.class, | ||||
|                 // testDocument | ||||
|                 "" | ||||
|                 + "{\n" | ||||
|                 + "  \"customer\": {\n" | ||||
|                 + "    \"given\": \"Dorothy\", \n" | ||||
|                 + "    \"family\": \"Gale\"\n" | ||||
|                 + "  }, \n" | ||||
|                 + "  \"ship-to\": {\n" | ||||
|                 + "    \"city\": \"East Centerville\", \n" | ||||
|                 + "    \"state\": \"KS\", \n" | ||||
|                 + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" | ||||
|                 + "  }, \n" | ||||
|                 + "  \"bill-to\": {\n" | ||||
|                 + "    \"city\": \"East Centerville\", \n" | ||||
|                 + "    \"state\": \"KS\", \n" | ||||
|                 + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" | ||||
|                 + "  }, \n" | ||||
|                 + "  \"date\": \"2012-08-06\", \n" | ||||
|                 + "  \"items\": [\n" | ||||
|                 + "    {\n" | ||||
|                 + "      \"part_no\": \"A4786\", \n" | ||||
|                 + "      \"price\": 1.47, \n" | ||||
|                 + "      \"descrip\": \"Water Bucket (Filled)\", \n" | ||||
|                 + "      \"quantity\": 4\n" | ||||
|                 + "    }, \n" | ||||
|                 + "    {\n" | ||||
|                 + "      \"part_no\": \"E1628\", \n" | ||||
|                 + "      \"descrip\": \"High Heeled \\\"Ruby\\\" Slippers\", \n" | ||||
|                 + "      \"price\": 100.27, \n" | ||||
|                 + "      \"quantity\": 1, \n" | ||||
|                 + "      \"size\": 8\n" | ||||
|                 + "    }\n" | ||||
|                 + "  ], \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 | ||||
|                 "" | ||||
|                 + "{\n" | ||||
|                 + "  \"someKey\": {\n" | ||||
|                 + "    \"1\": 1, \n" | ||||
|                 + "    \"2\": 2, \n" | ||||
|                 + "    \"3\": 3, \n" | ||||
|                 + "    \"4\": 4\n" | ||||
|                 + "  }\n" | ||||
|                 + "}", | ||||
|                 // nullTest | ||||
|                 "" | ||||
|                 + "{\n" | ||||
|                 + "  \"null\": {\n" | ||||
|                 + "    \"null\": \"object\", \n" | ||||
|                 + "    \"object\": null\n" | ||||
|                 + "  }\n" | ||||
|                 + "}" | ||||
|             } | ||||
|         } ); | ||||
|         // CHECKSTYLE:ON | ||||
|         return Stream.of( | ||||
|                 Arguments.of( | ||||
|                         // provider | ||||
|                         YamlConfiguration.class, | ||||
|                         // testDocument | ||||
|                         "" | ||||
|                         + "receipt:     Oz-Ware Purchase Invoice\n" | ||||
|                         + "date:        2012-08-06\n" | ||||
|                         + "customer:\n" | ||||
|                         + "    given:   Dorothy\n" | ||||
|                         + "    family:  Gale\n" | ||||
|                         + "\n" | ||||
|                         + "items:\n" | ||||
|                         + "    - part_no:   A4786\n" | ||||
|                         + "      descrip:   Water Bucket (Filled)\n" | ||||
|                         + "      price:     1.47\n" | ||||
|                         + "      quantity:  4\n" | ||||
|                         + "\n" | ||||
|                         + "    - part_no:   E1628\n" | ||||
|                         + "      descrip:   High Heeled \"Ruby\" Slippers\n" | ||||
|                         + "      size:      8\n" | ||||
|                         + "      price:     100.27\n" | ||||
|                         + "      quantity:  1\n" | ||||
|                         + "\n" | ||||
|                         + "bill-to:  &id001\n" | ||||
|                         + "    street: |\n" | ||||
|                         + "            123 Tornado Alley\n" | ||||
|                         + "            Suite 16\n" | ||||
|                         + "    city:   East Centerville\n" | ||||
|                         + "    state:  KS\n" | ||||
|                         + "\n" | ||||
|                         + "ship-to:  *id001\n" | ||||
|                         + "\n" | ||||
|                         + "specialDelivery:  >\n" | ||||
|                         + "    Follow the Yellow Brick\n" | ||||
|                         + "    Road to the Emerald City.\n" | ||||
|                         + "    Pay no attention to the\n" | ||||
|                         + "    man behind the curtain.", | ||||
|                         // numberTest | ||||
|                         "" | ||||
|                         + "someKey:\n" | ||||
|                         + "    1: 1\n" | ||||
|                         + "    2: 2\n" | ||||
|                         + "    3: 3\n" | ||||
|                         + "    4: 4", | ||||
|                         // nullTest | ||||
|                         "" | ||||
|                         + "null:\n" | ||||
|                         + "    null: object\n" | ||||
|                         + "    object: null\n" | ||||
|                 ), | ||||
|                 Arguments.of( | ||||
|                         // provider | ||||
|                         JsonConfiguration.class, | ||||
|                         // testDocument | ||||
|                         "" | ||||
|                         + "{\n" | ||||
|                         + "  \"customer\": {\n" | ||||
|                         + "    \"given\": \"Dorothy\", \n" | ||||
|                         + "    \"family\": \"Gale\"\n" | ||||
|                         + "  }, \n" | ||||
|                         + "  \"ship-to\": {\n" | ||||
|                         + "    \"city\": \"East Centerville\", \n" | ||||
|                         + "    \"state\": \"KS\", \n" | ||||
|                         + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" | ||||
|                         + "  }, \n" | ||||
|                         + "  \"bill-to\": {\n" | ||||
|                         + "    \"city\": \"East Centerville\", \n" | ||||
|                         + "    \"state\": \"KS\", \n" | ||||
|                         + "    \"street\": \"123 Tornado Alley\\nSuite 16\\n\"\n" | ||||
|                         + "  }, \n" | ||||
|                         + "  \"date\": \"2012-08-06\", \n" | ||||
|                         + "  \"items\": [\n" | ||||
|                         + "    {\n" | ||||
|                         + "      \"part_no\": \"A4786\", \n" | ||||
|                         + "      \"price\": 1.47, \n" | ||||
|                         + "      \"descrip\": \"Water Bucket (Filled)\", \n" | ||||
|                         + "      \"quantity\": 4\n" | ||||
|                         + "    }, \n" | ||||
|                         + "    {\n" | ||||
|                         + "      \"part_no\": \"E1628\", \n" | ||||
|                         + "      \"descrip\": \"High Heeled \\\"Ruby\\\" Slippers\", \n" | ||||
|                         + "      \"price\": 100.27, \n" | ||||
|                         + "      \"quantity\": 1, \n" | ||||
|                         + "      \"size\": 8\n" | ||||
|                         + "    }\n" | ||||
|                         + "  ], \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 | ||||
|                         "" | ||||
|                         + "{\n" | ||||
|                         + "  \"someKey\": {\n" | ||||
|                         + "    \"1\": 1, \n" | ||||
|                         + "    \"2\": 2, \n" | ||||
|                         + "    \"3\": 3, \n" | ||||
|                         + "    \"4\": 4\n" | ||||
|                         + "  }\n" | ||||
|                         + "}", | ||||
|                         // nullTest | ||||
|                         "" | ||||
|                         + "{\n" | ||||
|                         + "  \"null\": {\n" | ||||
|                         + "    \"null\": \"object\", \n" | ||||
|                         + "    \"object\": null\n" | ||||
|                         + "  }\n" | ||||
|                         + "}" | ||||
|                 ) | ||||
|         ); | ||||
|     } | ||||
|     // | ||||
|     private final Class<? extends ConfigurationProvider> provider; | ||||
|     private final String testDocument; | ||||
|     private final String numberTest; | ||||
|     private final String nullTest; | ||||
|  | ||||
|     @Test | ||||
|     public void testConfig() throws Exception | ||||
|     @ParameterizedTest | ||||
|     @MethodSource("data") | ||||
|     public void testConfig(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) throws Exception | ||||
|     { | ||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( testDocument ); | ||||
|         testSection( conf ); | ||||
| @@ -151,7 +141,7 @@ public class CompoundConfigurationTest | ||||
|         ConfigurationProvider.getProvider( provider ).save( conf, sw ); | ||||
|  | ||||
|         // 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.set( "receipt", "Oz-Ware Purchase Invoice" ); // Add it back | ||||
| @@ -160,37 +150,38 @@ public class CompoundConfigurationTest | ||||
|  | ||||
|     private void testSection(Configuration conf) | ||||
|     { | ||||
|         Assert.assertEquals( "receipt", "Oz-Ware Purchase Invoice", conf.getString( "receipt" ) ); | ||||
|         // Assert.assertEquals( "date", "2012-08-06", conf.get( "date" ).toString() ); | ||||
|         assertEquals( "Oz-Ware Purchase Invoice", conf.getString( "receipt" ), "receipt" ); | ||||
|         // assertEquals( "2012-08-06", conf.get( "date" ).toString(), "date" ); | ||||
|  | ||||
|         Configuration customer = conf.getSection( "customer" ); | ||||
|         Assert.assertEquals( "customer.given", "Dorothy", customer.getString( "given" ) ); | ||||
|         Assert.assertEquals( "customer.given", "Dorothy", conf.getString( "customer.given" ) ); | ||||
|         assertEquals( "Dorothy", customer.getString( "given" ), "customer.given" ); | ||||
|         assertEquals( "Dorothy", conf.getString( "customer.given" ), "customer.given" ); | ||||
|  | ||||
|         List items = conf.getList( "items" ); | ||||
|         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 ); | ||||
|         Assert.assertEquals( null, conf.get( "receipt" ) ); | ||||
|         Assert.assertEquals( "foo", conf.get( "receipt", "foo" ) ); | ||||
|         assertEquals( null, conf.get( "receipt" ) ); | ||||
|         assertEquals( "foo", conf.get( "receipt", "foo" ) ); | ||||
|  | ||||
|         Configuration newSection = conf.getSection( "new.section" ); | ||||
|         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" ); | ||||
|         Assert.assertEquals( "bar", conf.get( "other.new.section" ) ); | ||||
|         assertEquals( "bar", conf.get( "other.new.section" ) ); | ||||
|  | ||||
|         Assert.assertTrue( conf.contains( "customer.given" ) ); | ||||
|         Assert.assertTrue( customer.contains( "given" ) ); | ||||
|         assertTrue( conf.contains( "customer.given" ) ); | ||||
|         assertTrue( customer.contains( "given" ) ); | ||||
|  | ||||
|         Assert.assertFalse( conf.contains( "customer.foo" ) ); | ||||
|         Assert.assertFalse( customer.contains( "foo" ) ); | ||||
|         assertFalse( conf.contains( "customer.foo" ) ); | ||||
|         assertFalse( customer.contains( "foo" ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testNumberedKeys() | ||||
|     @ParameterizedTest | ||||
|     @MethodSource("data") | ||||
|     public void testNumberedKeys(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) | ||||
|     { | ||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( numberTest ); | ||||
|  | ||||
| @@ -201,29 +192,31 @@ public class CompoundConfigurationTest | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testNull() | ||||
|     @ParameterizedTest | ||||
|     @MethodSource("data") | ||||
|     public void testNull(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) | ||||
|     { | ||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( nullTest ); | ||||
|  | ||||
|         Assert.assertEquals( "object", conf.get( "null.null" ) ); | ||||
|         Assert.assertEquals( "object", conf.getSection( "null" ).get( "null" ) ); | ||||
|         assertEquals( "object", conf.get( "null.null" ) ); | ||||
|         assertEquals( "object", conf.getSection( "null" ).get( "null" ) ); | ||||
|  | ||||
|         Assert.assertEquals( null, conf.get( "null.object" ) ); | ||||
|         Assert.assertEquals( "", conf.getString( "null.object" ) ); | ||||
|         assertEquals( null, conf.get( "null.object" ) ); | ||||
|         assertEquals( "", conf.getString( "null.object" ) ); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testMapAddition() | ||||
|     @ParameterizedTest | ||||
|     @MethodSource("data") | ||||
|     public void testMapAddition(Class<? extends ConfigurationProvider> provider, String testDocument, String numberTest, String nullTest) | ||||
|     { | ||||
|         Configuration conf = ConfigurationProvider.getProvider( provider ).load( testDocument ); | ||||
|  | ||||
|         conf.set( "addition", Collections.singletonMap( "foo", "bar" ) ); | ||||
|  | ||||
|         // Order matters | ||||
|         Assert.assertEquals( "bar", conf.getSection( "addition" ).getString( "foo" ) ); | ||||
|         Assert.assertEquals( "bar", conf.getString( "addition.foo" ) ); | ||||
|         assertEquals( "bar", conf.getSection( "addition" ).getString( "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; | ||||
|  | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class DefaultConfigurationTest | ||||
| { | ||||
| @@ -16,8 +16,8 @@ public class DefaultConfigurationTest | ||||
|  | ||||
|         Configuration actualConfig = new Configuration( defaultConfig ); | ||||
|  | ||||
|         Assert.assertEquals( 10, actualConfig.getInt( "setting" ) ); | ||||
|         Assert.assertEquals( 11, actualConfig.getInt( "nested.setting" ) ); | ||||
|         Assert.assertEquals( 12, actualConfig.getInt( "double.nested.setting" ) ); | ||||
|         assertEquals( 10, actualConfig.getInt( "setting" ) ); | ||||
|         assertEquals( 11, actualConfig.getInt( "nested.setting" ) ); | ||||
|         assertEquals( 12, actualConfig.getInt( "double.nested.setting" ) ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-event</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Event</name> | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package net.md_5.bungee.event; | ||||
|  | ||||
| import com.google.common.collect.ImmutableSet; | ||||
| import java.lang.reflect.InvocationTargetException; | ||||
| import java.lang.reflect.Method; | ||||
| import java.text.MessageFormat; | ||||
| @@ -41,6 +42,8 @@ public class EventBus | ||||
|         { | ||||
|             for ( EventHandlerMethod method : handlers ) | ||||
|             { | ||||
|                 long start = System.nanoTime(); | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     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() ); | ||||
|                 } | ||||
|  | ||||
|                 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) | ||||
|     { | ||||
|         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 ); | ||||
|             if ( annotation != null ) | ||||
| @@ -75,18 +88,8 @@ public class EventBus | ||||
|                     } ); | ||||
|                     continue; | ||||
|                 } | ||||
|                 Map<Byte, Set<Method>> prioritiesMap = handler.get( params[0] ); | ||||
|                 if ( prioritiesMap == null ) | ||||
|                 { | ||||
|                     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 ); | ||||
|                 } | ||||
|                 Map<Byte, Set<Method>> prioritiesMap = handler.computeIfAbsent( params[0], k -> new HashMap<>() ); | ||||
|                 Set<Method> priority = prioritiesMap.computeIfAbsent( annotation.priority(), k -> new HashSet<>() ); | ||||
|                 priority.add( m ); | ||||
|             } | ||||
|         } | ||||
| @@ -101,22 +104,11 @@ public class EventBus | ||||
|         { | ||||
|             for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() ) | ||||
|             { | ||||
|                 Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() ); | ||||
|                 if ( prioritiesMap == null ) | ||||
|                 { | ||||
|                     prioritiesMap = new HashMap<>(); | ||||
|                     byListenerAndPriority.put( e.getKey(), prioritiesMap ); | ||||
|                 } | ||||
|                 Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.computeIfAbsent( e.getKey(), k -> new HashMap<>() ); | ||||
|                 for ( Map.Entry<Byte, Set<Method>> entry : e.getValue().entrySet() ) | ||||
|                 { | ||||
|                     Map<Object, Method[]> currentPriorityMap = prioritiesMap.get( entry.getKey() ); | ||||
|                     if ( currentPriorityMap == null ) | ||||
|                     { | ||||
|                         currentPriorityMap = new HashMap<>(); | ||||
|                         prioritiesMap.put( entry.getKey(), currentPriorityMap ); | ||||
|                     } | ||||
|                     Method[] baked = new Method[ entry.getValue().size() ]; | ||||
|                     currentPriorityMap.put( listener, entry.getValue().toArray( baked ) ); | ||||
|                     Map<Object, Method[]> currentPriorityMap = prioritiesMap.computeIfAbsent( entry.getKey(), k -> new HashMap<>() ); | ||||
|                     currentPriorityMap.put( listener, entry.getValue().toArray( new Method[ 0 ] ) ); | ||||
|                 } | ||||
|                 bakeHandlers( e.getKey() ); | ||||
|             } | ||||
| @@ -194,7 +186,7 @@ public class EventBus | ||||
|                     } | ||||
|                 } | ||||
|             } while ( value++ < Byte.MAX_VALUE ); | ||||
|             byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) ); | ||||
|             byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ 0 ] ) ); | ||||
|         } else | ||||
|         { | ||||
|             byEventBaked.remove( eventClass ); | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| package net.md_5.bungee.event; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import java.util.concurrent.CountDownLatch; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class EventBusTest | ||||
| { | ||||
| @@ -15,14 +15,14 @@ public class EventBusTest | ||||
|     { | ||||
|         bus.register( this ); | ||||
|         bus.post( new FirstEvent() ); | ||||
|         Assert.assertEquals( 0, latch.getCount() ); | ||||
|         assertEquals( 0, latch.getCount() ); | ||||
|     } | ||||
|  | ||||
|     @EventHandler | ||||
|     public void firstListener(FirstEvent event) | ||||
|     { | ||||
|         bus.post( new SecondEvent() ); | ||||
|         Assert.assertEquals( 1, latch.getCount() ); | ||||
|         assertEquals( 1, latch.getCount() ); | ||||
|         latch.countDown(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| package net.md_5.bungee.event; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import java.util.concurrent.CountDownLatch; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class EventPriorityTest | ||||
| { | ||||
| @@ -16,41 +16,41 @@ public class EventPriorityTest | ||||
|         bus.register( this ); | ||||
|         bus.register( new EventPriorityListenerPartner() ); | ||||
|         bus.post( new PriorityTestEvent() ); | ||||
|         Assert.assertEquals( 0, latch.getCount() ); | ||||
|         assertEquals( 0, latch.getCount() ); | ||||
|     } | ||||
|  | ||||
|     @EventHandler(priority = Byte.MIN_VALUE) | ||||
|     public void onMinPriority(PriorityTestEvent event) | ||||
|     { | ||||
|         Assert.assertEquals( 7, latch.getCount() ); | ||||
|         assertEquals( 7, latch.getCount() ); | ||||
|         latch.countDown(); | ||||
|     } | ||||
|  | ||||
|     @EventHandler(priority = EventPriority.LOWEST) | ||||
|     public void onLowestPriority(PriorityTestEvent event) | ||||
|     { | ||||
|         Assert.assertEquals( 6, latch.getCount() ); | ||||
|         assertEquals( 6, latch.getCount() ); | ||||
|         latch.countDown(); | ||||
|     } | ||||
|  | ||||
|     @EventHandler | ||||
|     public void onNormalPriority(PriorityTestEvent event) | ||||
|     { | ||||
|         Assert.assertEquals( 4, latch.getCount() ); | ||||
|         assertEquals( 4, latch.getCount() ); | ||||
|         latch.countDown(); | ||||
|     } | ||||
|  | ||||
|     @EventHandler(priority = EventPriority.HIGHEST) | ||||
|     public void onHighestPriority(PriorityTestEvent event) | ||||
|     { | ||||
|         Assert.assertEquals( 2, latch.getCount() ); | ||||
|         assertEquals( 2, latch.getCount() ); | ||||
|         latch.countDown(); | ||||
|     } | ||||
|  | ||||
|     @EventHandler(priority = Byte.MAX_VALUE) | ||||
|     public void onMaxPriority(PriorityTestEvent event) | ||||
|     { | ||||
|         Assert.assertEquals( 1, latch.getCount() ); | ||||
|         assertEquals( 1, latch.getCount() ); | ||||
|         latch.countDown(); | ||||
|     } | ||||
|  | ||||
| @@ -64,14 +64,14 @@ public class EventPriorityTest | ||||
|         @EventHandler(priority = EventPriority.HIGH) | ||||
|         public void onHighPriority(PriorityTestEvent event) | ||||
|         { | ||||
|             Assert.assertEquals( 3, latch.getCount() ); | ||||
|             assertEquals( 3, latch.getCount() ); | ||||
|             latch.countDown(); | ||||
|         } | ||||
|  | ||||
|         @EventHandler(priority = EventPriority.LOW) | ||||
|         public void onLowPriority(PriorityTestEvent event) | ||||
|         { | ||||
|             Assert.assertEquals( 5, latch.getCount() ); | ||||
|             assertEquals( 5, latch.getCount() ); | ||||
|             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; | ||||
|  | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import static org.junit.jupiter.api.Assertions.fail; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| public class UnregisteringListenerTest | ||||
| { | ||||
| @@ -19,7 +19,7 @@ public class UnregisteringListenerTest | ||||
|     @EventHandler | ||||
|     public void onEvent(TestEvent evt) | ||||
|     { | ||||
|         Assert.fail( "Event listener wasn't unregistered" ); | ||||
|         fail( "Event listener wasn't unregistered" ); | ||||
|     } | ||||
|  | ||||
|     public static class TestEvent | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-log</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Log</name> | ||||
|   | ||||
| @@ -13,19 +13,23 @@ public class BungeeLogger extends Logger | ||||
|  | ||||
|     private final LogDispatcher dispatcher = new LogDispatcher( this ); | ||||
|  | ||||
|     // CHECKSTYLE:OFF | ||||
|     @SuppressWarnings( | ||||
|             { | ||||
|                 "CallToPrintStackTrace", "CallToThreadStartDuringObjectConstruction" | ||||
|             }) | ||||
|     // CHECKSTYLE:ON | ||||
|     @SuppressFBWarnings("SC_START_IN_CTOR") | ||||
|     public BungeeLogger(String loggerName, String filePattern, ConsoleReader reader) | ||||
|     { | ||||
|         super( loggerName, null ); | ||||
|         setLevel( Level.ALL ); | ||||
|         setUseParentHandlers( false ); | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             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 ) ); | ||||
|             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; | ||||
|  | ||||
| import com.google.common.base.Charsets; | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.IOException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.logging.Level; | ||||
| import java.util.logging.Logger; | ||||
| import lombok.RequiredArgsConstructor; | ||||
| @@ -19,7 +19,7 @@ public class LoggingOutputStream extends ByteArrayOutputStream | ||||
|     @Override | ||||
|     public void flush() throws IOException | ||||
|     { | ||||
|         String contents = toString( Charsets.UTF_8.name() ); | ||||
|         String contents = toString( StandardCharsets.UTF_8.name() ); | ||||
|         super.reset(); | ||||
|         if ( !contents.isEmpty() && !contents.equals( separator ) ) | ||||
|         { | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-module</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module-cmd-alert</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>cmd_alert</name> | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| 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.CommandSender; | ||||
| import net.md_5.bungee.api.ProxyServer; | ||||
| @@ -23,10 +24,10 @@ public class CommandAlert extends Command | ||||
|         } else | ||||
|         { | ||||
|             StringBuilder builder = new StringBuilder(); | ||||
|             if ( args[0].startsWith( "&h" ) ) | ||||
|             if ( args[0].toLowerCase( Locale.ROOT ).startsWith( "&h" ) ) | ||||
|             { | ||||
|                 // Remove &h | ||||
|                 args[0] = args[0].substring( 2, args[0].length() ); | ||||
|                 args[0] = args[0].substring( 2 ); | ||||
|             } else | ||||
|             { | ||||
|                 builder.append( ProxyServer.getInstance().getTranslation( "alert" ) ); | ||||
| @@ -40,7 +41,7 @@ public class CommandAlert extends Command | ||||
|  | ||||
|             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> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-module</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module-cmd-find</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>cmd_find</name> | ||||
|   | ||||
| @@ -1,7 +1,12 @@ | ||||
| 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.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.command.PlayerCommand; | ||||
|  | ||||
| @@ -27,8 +32,27 @@ public class CommandFind extends PlayerCommand | ||||
|                 sender.sendMessage( ProxyServer.getInstance().getTranslation( "user_not_online" ) ); | ||||
|             } 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> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-module</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module-cmd-list</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>cmd_list</name> | ||||
|   | ||||
| @@ -1,20 +1,26 @@ | ||||
| package net.md_5.bungee.module.cmd.list; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import net.md_5.bungee.Util; | ||||
| 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.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.connection.ProxiedPlayer; | ||||
| 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. | ||||
|  */ | ||||
| public class CommandList extends Command | ||||
| public class CommandList extends Command implements TabExecutor | ||||
| { | ||||
|  | ||||
|     public CommandList() | ||||
| @@ -25,6 +31,9 @@ public class CommandList extends Command | ||||
|     @Override | ||||
|     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() ) | ||||
|         { | ||||
|             if ( !server.canAccess( sender ) ) | ||||
| @@ -32,16 +41,39 @@ public class CommandList extends Command | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             Collection<ProxiedPlayer> serverPlayers = server.getPlayers(); | ||||
|             if ( hideEmptyServers && serverPlayers.isEmpty() ) | ||||
|             { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             List<String> players = new ArrayList<>(); | ||||
|             for ( ProxiedPlayer player : server.getPlayers() ) | ||||
|             for ( ProxiedPlayer player : serverPlayers ) | ||||
|             { | ||||
|                 players.add( player.getDisplayName() ); | ||||
|             } | ||||
|             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() ) ); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Iterable<String> onTabComplete(CommandSender sender, String[] args) | ||||
|     { | ||||
|         return ( args.length > 1 ) ? Collections.emptyList() : Collections.singletonList( "all" ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-module</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module-cmd-send</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>cmd_send</name> | ||||
|   | ||||
| @@ -38,7 +38,7 @@ public class CommandSend extends Command implements TabExecutor | ||||
|             this.sender = sender; | ||||
|             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> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-module</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module-cmd-server</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>cmd_server</name> | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>pom</packaging> | ||||
|  | ||||
|     <name>BungeeCord Modules</name> | ||||
| @@ -21,6 +21,7 @@ | ||||
|     <modules> | ||||
|         <module>cmd-alert</module> | ||||
|         <module>cmd-find</module> | ||||
|         <module>cmd-kick</module> | ||||
|         <module>cmd-list</module> | ||||
|         <module>cmd-send</module> | ||||
|         <module>cmd-server</module> | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-module</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-module-reconnect-yaml</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>reconnect_yaml</name> | ||||
|   | ||||
| @@ -1,6 +1,14 @@ | ||||
| #!/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 src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so -lcrypto | ||||
| $CXX src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so -lz | ||||
| $CXX -Imbedtls/include src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so mbedtls/library/libmbedcrypto.a | ||||
| $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> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-native</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Native</name> | ||||
| @@ -22,7 +22,6 @@ | ||||
|         <dependency> | ||||
|             <groupId>io.netty</groupId> | ||||
|             <artifactId>netty-transport</artifactId> | ||||
|             <version>${netty.version}</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|   | ||||
| @@ -1,6 +1,3 @@ | ||||
| // Support for CentOS 6 | ||||
| __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <zlib.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.InputStream; | ||||
| import java.io.OutputStream; | ||||
| import java.util.function.Supplier; | ||||
| import net.md_5.bungee.jni.cipher.BungeeCipher; | ||||
|  | ||||
| public final class NativeCode<T> | ||||
| { | ||||
|  | ||||
|     private final String name; | ||||
|     private final Class<? extends T> javaImpl; | ||||
|     private final Class<? extends T> nativeImpl; | ||||
|     private final Supplier<? extends T> javaImpl; | ||||
|     private final Supplier<? extends T> nativeImpl; | ||||
|     // | ||||
|     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.javaImpl = javaImpl; | ||||
| @@ -26,13 +27,7 @@ public final class NativeCode<T> | ||||
|  | ||||
|     public T newInstance() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             return ( loaded ) ? nativeImpl.getDeclaredConstructor().newInstance() : javaImpl.getDeclaredConstructor().newInstance(); | ||||
|         } catch ( ReflectiveOperationException ex ) | ||||
|         { | ||||
|             throw new RuntimeException( "Error getting instance", ex ); | ||||
|         } | ||||
|         return ( loaded ) ? nativeImpl.get() : javaImpl.get(); | ||||
|     } | ||||
|  | ||||
|     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 | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,5 +1,6 @@ | ||||
| package net.md_5.bungee; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import io.netty.buffer.Unpooled; | ||||
| 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.JavaCipher; | ||||
| import net.md_5.bungee.jni.cipher.NativeCipher; | ||||
| import org.junit.Assert; | ||||
| import org.junit.FixMethodOrder; | ||||
| import org.junit.Test; | ||||
| import org.junit.runners.MethodSorters; | ||||
| import org.junit.jupiter.api.MethodOrderer; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.TestMethodOrder; | ||||
|  | ||||
| @FixMethodOrder(MethodSorters.NAME_ASCENDING) | ||||
| @TestMethodOrder(MethodOrderer.MethodName.class) | ||||
| public class NativeCipherTest | ||||
| { | ||||
|  | ||||
| @@ -26,7 +26,7 @@ public class NativeCipherTest | ||||
|     private final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" ); | ||||
|     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 | ||||
|     public void testNative() throws Exception | ||||
| @@ -34,7 +34,7 @@ public class NativeCipherTest | ||||
|         if ( NativeCode.isSupported() ) | ||||
|         { | ||||
|             boolean loaded = factory.load(); | ||||
|             Assert.assertTrue( "Native cipher failed to load!", loaded ); | ||||
|             assertTrue( loaded, "Native cipher failed to load!" ); | ||||
|  | ||||
|             NativeCipher cipher = new NativeCipher(); | ||||
|             System.out.println( "Testing native cipher..." ); | ||||
| @@ -48,7 +48,7 @@ public class NativeCipherTest | ||||
|         if ( NativeCode.isSupported() ) | ||||
|         { | ||||
|             boolean loaded = factory.load(); | ||||
|             Assert.assertTrue( "Native cipher failed to load!", loaded ); | ||||
|             assertTrue( loaded, "Native cipher failed to load!" ); | ||||
|  | ||||
|             NativeCipher cipher = new NativeCipher(); | ||||
|  | ||||
| @@ -98,7 +98,7 @@ public class NativeCipherTest | ||||
|         // Encrypt | ||||
|         cipher.init( true, secret ); | ||||
|         cipher.cipher( nativePlain, out ); | ||||
|         Assert.assertEquals( nativeCiphered, out ); | ||||
|         assertEquals( nativeCiphered, out ); | ||||
|  | ||||
|         out.clear(); | ||||
|  | ||||
| @@ -106,7 +106,7 @@ public class NativeCipherTest | ||||
|         cipher.init( false, secret ); | ||||
|         cipher.cipher( nativeCiphered, out ); | ||||
|         nativePlain.resetReaderIndex(); | ||||
|         Assert.assertEquals( nativePlain, out ); | ||||
|         assertEquals( nativePlain, out ); | ||||
|  | ||||
|         System.out.println( "This cipher works correctly!" ); | ||||
|     } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package net.md_5.bungee; | ||||
|  | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import io.netty.buffer.ByteBuf; | ||||
| import io.netty.buffer.Unpooled; | ||||
| 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.JavaZlib; | ||||
| import net.md_5.bungee.jni.zlib.NativeZlib; | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.jupiter.api.Test; | ||||
|  | ||||
| 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 | ||||
|     public void doTest() throws DataFormatException | ||||
|     { | ||||
|         if ( NativeCode.isSupported() ) | ||||
|         { | ||||
|             Assert.assertTrue( "Native code failed to load!", factory.load() ); | ||||
|             assertTrue( factory.load(), "Native code failed to load!" ); | ||||
|             test( factory.newInstance() ); | ||||
|         } | ||||
|         test( new JavaZlib() ); | ||||
| @@ -64,6 +64,6 @@ public class NativeZlibTest | ||||
|         long elapsed = System.currentTimeMillis() - start; | ||||
|         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> | ||||
|     <artifactId>bungeecord-parent</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>pom</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Parent</name> | ||||
| @@ -41,6 +41,7 @@ | ||||
|         <module>protocol</module> | ||||
|         <module>proxy</module> | ||||
|         <module>query</module> | ||||
|         <module>slf4j</module> | ||||
|         <module>native</module> | ||||
|     </modules> | ||||
|  | ||||
| @@ -71,23 +72,34 @@ | ||||
|  | ||||
|     <properties> | ||||
|         <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.target>1.8</maven.compiler.target> | ||||
|         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||||
|     </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> | ||||
|         <dependency> | ||||
|             <groupId>junit</groupId> | ||||
|             <artifactId>junit</artifactId> | ||||
|             <version>4.13.1</version> | ||||
|             <groupId>org.junit.jupiter</groupId> | ||||
|             <artifactId>junit-jupiter</artifactId> | ||||
|             <version>5.10.2</version> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.google.guava</groupId> | ||||
|             <artifactId>guava</artifactId> | ||||
|             <version>21.0</version> | ||||
|             <version>32.1.2-jre</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
| @@ -96,20 +108,35 @@ | ||||
|             <version>3.0.1</version> | ||||
|             <scope>provided</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.jetbrains</groupId> | ||||
|             <artifactId>annotations-java5</artifactId> | ||||
|             <version>24.1.0</version> | ||||
|             <scope>provided</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.projectlombok</groupId> | ||||
|             <artifactId>lombok</artifactId> | ||||
|             <version>1.18.10</version> | ||||
|             <version>${lombok.version}</version> | ||||
|             <scope>provided</scope> | ||||
|         </dependency> | ||||
|     </dependencies> | ||||
|  | ||||
|     <build> | ||||
|         <pluginManagement> | ||||
|             <plugins> | ||||
|                 <plugin> | ||||
|                     <groupId>org.apache.maven.plugins</groupId> | ||||
|                     <artifactId>maven-javadoc-plugin</artifactId> | ||||
|                     <version>3.6.3</version> | ||||
|                 </plugin> | ||||
|             </plugins> | ||||
|         </pluginManagement> | ||||
|         <plugins> | ||||
|             <plugin> | ||||
|                 <groupId>net.md-5</groupId> | ||||
|                 <artifactId>scriptus</artifactId> | ||||
|                 <version>0.4.1</version> | ||||
|                 <version>0.5.0</version> | ||||
|                 <configuration> | ||||
|                     <format>git:${project.name}:${project.version}:%s:${build.number}</format> | ||||
|                 </configuration> | ||||
| @@ -122,10 +149,15 @@ | ||||
|                     </execution> | ||||
|                 </executions> | ||||
|             </plugin> | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-compiler-plugin</artifactId> | ||||
|                 <version>3.13.0</version> | ||||
|             </plugin> | ||||
|             <plugin> | ||||
|                 <groupId>org.apache.maven.plugins</groupId> | ||||
|                 <artifactId>maven-checkstyle-plugin</artifactId> | ||||
|                 <version>3.1.1</version> | ||||
|                 <version>3.3.1</version> | ||||
|                 <executions> | ||||
|                     <execution> | ||||
|                         <phase>process-classes</phase> | ||||
| @@ -143,14 +175,14 @@ | ||||
|                     <dependency> | ||||
|                         <groupId>com.puppycrawl.tools</groupId> | ||||
|                         <artifactId>checkstyle</artifactId> | ||||
|                         <version>8.36.2</version> | ||||
|                         <version>8.45.1</version> | ||||
|                     </dependency> | ||||
|                 </dependencies> | ||||
|             </plugin> | ||||
|             <plugin> | ||||
|                 <groupId>org.codehaus.mojo</groupId> | ||||
|                 <artifactId>animal-sniffer-maven-plugin</artifactId> | ||||
|                 <version>1.19</version> | ||||
|                 <version>1.23</version> | ||||
|                 <executions> | ||||
|                     <execution> | ||||
|                         <phase>process-classes</phase> | ||||
| @@ -167,10 +199,82 @@ | ||||
|                     </signature> | ||||
|                 </configuration> | ||||
|             </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> | ||||
|     </build> | ||||
|  | ||||
|     <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> | ||||
|             <id>dist</id> | ||||
|             <build> | ||||
| @@ -178,7 +282,7 @@ | ||||
|                     <plugin> | ||||
|                         <groupId>org.apache.maven.plugins</groupId> | ||||
|                         <artifactId>maven-source-plugin</artifactId> | ||||
|                         <version>3.2.1</version> | ||||
|                         <version>3.3.1</version> | ||||
|                         <executions> | ||||
|                             <execution> | ||||
|                                 <phase>package</phase> | ||||
| @@ -191,7 +295,7 @@ | ||||
|                     <plugin> | ||||
|                         <groupId>org.projectlombok</groupId> | ||||
|                         <artifactId>lombok-maven-plugin</artifactId> | ||||
|                         <version>1.18.10.0</version> | ||||
|                         <version>1.18.20.0</version> | ||||
|                         <executions> | ||||
|                             <execution> | ||||
|                                 <phase>package</phase> | ||||
| @@ -205,11 +309,17 @@ | ||||
|                             <outputDirectory>${project.build.directory}/delombok</outputDirectory> | ||||
|                             <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory> | ||||
|                         </configuration> | ||||
|                         <dependencies> | ||||
|                             <dependency> | ||||
|                                 <groupId>org.projectlombok</groupId> | ||||
|                                 <artifactId>lombok</artifactId> | ||||
|                                 <version>${lombok.version}</version> | ||||
|                             </dependency> | ||||
|                         </dependencies> | ||||
|                     </plugin> | ||||
|                     <plugin> | ||||
|                         <groupId>org.apache.maven.plugins</groupId> | ||||
|                         <artifactId>maven-javadoc-plugin</artifactId> | ||||
|                         <version>3.2.0</version> | ||||
|                         <executions> | ||||
|                             <!-- Execute Javadoc once normally to catch any warnings --> | ||||
|                             <execution> | ||||
| @@ -247,7 +357,7 @@ | ||||
|                     <plugin> | ||||
|                         <groupId>org.apache.maven.plugins</groupId> | ||||
|                         <artifactId>maven-gpg-plugin</artifactId> | ||||
|                         <version>1.6</version> | ||||
|                         <version>3.2.4</version> | ||||
|                         <executions> | ||||
|                             <execution> | ||||
|                                 <phase>verify</phase> | ||||
|   | ||||
| @@ -6,38 +6,32 @@ | ||||
|     <parent> | ||||
|         <groupId>net.md-5</groupId> | ||||
|         <artifactId>bungeecord-parent</artifactId> | ||||
|         <version>1.16-R0.4-SNAPSHOT</version> | ||||
|         <version>1.20-R0.3-SNAPSHOT</version> | ||||
|         <relativePath>../pom.xml</relativePath> | ||||
|     </parent> | ||||
|  | ||||
|     <groupId>net.md-5</groupId> | ||||
|     <artifactId>bungeecord-protocol</artifactId> | ||||
|     <version>1.16-R0.4-SNAPSHOT</version> | ||||
|     <version>1.20-R0.3-SNAPSHOT</version> | ||||
|     <packaging>jar</packaging> | ||||
|  | ||||
|     <name>BungeeCord-Protocol</name> | ||||
|     <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> | ||||
|         <repository> | ||||
|             <id>sonatype-nexus-snapshots</id> | ||||
|             <name>Sonatype Nexus Snapshots</name> | ||||
|             <url>https://oss.sonatype.org/content/repositories/snapshots</url> | ||||
|             <releases> | ||||
|                 <enabled>false</enabled> | ||||
|             </releases> | ||||
|             <snapshots> | ||||
|                 <enabled>true</enabled> | ||||
|             </snapshots> | ||||
|             <id>minecraft-libraries</id> | ||||
|             <name>Minecraft Libraries</name> | ||||
|             <url>https://libraries.minecraft.net/</url> | ||||
|         </repository> | ||||
|     </repositories> | ||||
|  | ||||
|     <dependencies> | ||||
|         <dependency> | ||||
|             <groupId>net.md-5</groupId> | ||||
|             <groupId>com.mojang</groupId> | ||||
|             <artifactId>brigadier</artifactId> | ||||
|             <version>1.0.16-SNAPSHOT</version> | ||||
|             <version>1.2.9</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
| @@ -49,7 +43,6 @@ | ||||
|         <dependency> | ||||
|             <groupId>io.netty</groupId> | ||||
|             <artifactId>netty-codec</artifactId> | ||||
|             <version>${netty.version}</version> | ||||
|             <scope>compile</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user