Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87e9a6defd | ||
|
|
3d896ff828 | ||
|
|
bf93240888 | ||
|
|
0b0e30c3c9 | ||
|
|
84d3816bd1 | ||
|
|
04ff606947 | ||
|
|
1cb8e6e238 | ||
|
|
4a2226170d | ||
|
|
1469124cfc | ||
|
|
0826095e73 | ||
|
|
1bd698adde | ||
|
|
1058d269ff | ||
|
|
bacd87b78e | ||
|
|
5befbe8780 | ||
|
|
e159b2adae | ||
|
|
52f7418853 | ||
|
|
76061f7ccd | ||
|
|
7d39e21eae | ||
|
|
fee3723474 | ||
|
|
2f0e4c6888 | ||
|
|
b04cb6d3c5 | ||
|
|
a9228a698a | ||
|
|
27102495d8 | ||
|
|
d1bf96ab33 | ||
|
|
3360dbb468 | ||
|
|
d224e7584e | ||
|
|
6373540076 | ||
|
|
5f78aca997 | ||
|
|
145d944d33 | ||
|
|
ac91940e3b | ||
|
|
ef8452be73 | ||
|
|
47e0810697 | ||
|
|
1f30f225c7 | ||
|
|
fcbfd60ddb | ||
|
|
46f9033713 | ||
|
|
7e65018a0e | ||
|
|
9ef3485968 | ||
|
|
257f8f1145 | ||
|
|
8d65cbb0e5 | ||
|
|
20d221e3ca | ||
|
|
94072acf10 | ||
|
|
0422cc2597 | ||
|
|
58f61e645b | ||
|
|
5f416bd9e5 | ||
|
|
64528c913c | ||
|
|
357e7c6a49 | ||
|
|
81dd202053 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Unwanted behavior that does not match configuration.
|
||||
labels: ["Bug"]
|
||||
description: Plugin does not work as described.
|
||||
labels: ["Type: Bug"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
@@ -45,7 +45,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any other info you want to provide, such as screenshots.
|
||||
description: Anything else you want to provide that will help in resolving the issue faster, such as screenshots or videos.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Error
|
||||
description: Error message with a stack trace in console or error file.
|
||||
labels: ["Error"]
|
||||
labels: ["Type: Error"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Wiki change
|
||||
description: Change to text on a wiki page or adding a new section.
|
||||
labels: ["Wiki"]
|
||||
labels: ["Type: Wiki change"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
If this plugin has helped you in any way, and you would like to return something back
|
||||
to make the plugin even better, there is a lot of ways to contribute:
|
||||
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
|
||||
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
|
||||
is in quality of the report (typically providing correct steps to reproduce that actually work).
|
||||
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
|
||||
This way you will help make the actual releases more stable / help get stable releases sooner.
|
||||
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
|
||||
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
|
||||
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
|
||||
@@ -0,0 +1,174 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
@@ -31,13 +31,4 @@ allowing us to know who bought it and who didn't. It also serves as a much faste
|
||||
You will be provided with a much faster and higher quality service.**
|
||||
|
||||
# Contributing
|
||||
If this plugin has helped you in any way, and you would like to return something back
|
||||
to make the plugin even better, there is a lot of ways to contribute:
|
||||
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
|
||||
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
|
||||
is in quality of the report (typically providing correct steps to reproduce that actually work).
|
||||
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
|
||||
This way you will help make the actual releases more stable / help get stable releases sooner.
|
||||
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
|
||||
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
|
||||
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
|
||||
See [CONTRIBUTING.md](https://github.com/NEZNAMY/TAB/blob/master/CONTRIBUTING.md)
|
||||
@@ -11,7 +11,7 @@ public interface ArmorStand {
|
||||
*
|
||||
* @return true if static, false if dynamic
|
||||
*/
|
||||
boolean hasStaticOffset();
|
||||
boolean isStaticOffset();
|
||||
|
||||
/**
|
||||
* Changes offset of the armor stand
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Enum class representing all possibly used protocol versions
|
||||
*/
|
||||
@@ -70,13 +73,16 @@ public enum ProtocolVersion {
|
||||
public static final ProtocolVersion[] VALUES = values();
|
||||
|
||||
/** Version's network id found at https://wiki.vg/Protocol_version_numbers */
|
||||
private final int networkId;
|
||||
@Getter private final int networkId;
|
||||
|
||||
/** Version's minor version, such as 16 for 1.16.x */
|
||||
private int minorVersion;
|
||||
/** Version's minor version, such as 16 for 1.16.x. Allowing override to
|
||||
* set minor version of UNKNOWN_SERVER_VERSION value to value from package to fix compatibility
|
||||
* with server forks that set bukkit version field value to "Unknown".
|
||||
*/
|
||||
@Getter @Setter private int minorVersion;
|
||||
|
||||
/** Version's friendly name displayed in %player-version% placeholder */
|
||||
private final String friendlyName;
|
||||
@Getter private final String friendlyName;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given network id
|
||||
@@ -102,45 +108,6 @@ public enum ProtocolVersion {
|
||||
this.friendlyName = friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version's network id
|
||||
*
|
||||
* @return version's network id
|
||||
*/
|
||||
public int getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user-friendly name of the version (such as 1.16.2 instead of V1_16_2)
|
||||
*
|
||||
* @return user-friendly name of the version
|
||||
*/
|
||||
public String getFriendlyName() {
|
||||
return friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minor version of this release, such as 8 for 1.8.x or 13 for 1.13.x
|
||||
*
|
||||
* @return version's minor version
|
||||
*/
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides minor version of the version instance. This is used internally to
|
||||
* set minor version of UNKNOWN_SERVER_VERSION value to value from package to fix compatibility
|
||||
* with server forks that set bukkit version field value to "Unknown".
|
||||
*
|
||||
* @param minorVersion
|
||||
* minor version to change value to
|
||||
*/
|
||||
public void setMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum constant of entered version or UNKNOWN_SERVER_VERSION if unknown version
|
||||
*
|
||||
|
||||
@@ -244,6 +244,7 @@ public class TabConstants {
|
||||
public static final String UNLIMITED_NAME_TAGS = "NameTagX";
|
||||
public static final String UNLIMITED_NAME_TAGS_PACKET_LISTENER = "nametagx-packet";
|
||||
public static final String UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER = "nametagx-vehicle";
|
||||
public static final String UNLIMITED_NAME_TAGS_GAMEMODE_LISTENER = "nametagx-gamemode";
|
||||
|
||||
//Bukkit only
|
||||
public static final String PER_WORLD_PLAYER_LIST = "PerWorldPlayerList";
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.api;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
@@ -14,10 +15,10 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
public abstract class TabFeature {
|
||||
|
||||
/** Feature's name displayed in /tab cpu */
|
||||
private final String featureName;
|
||||
@Getter private final String featureName;
|
||||
|
||||
/** Feature's function name displayed in place of refreshing in /tab cpu */
|
||||
private final String refreshDisplayName;
|
||||
@Getter private final String refreshDisplayName;
|
||||
|
||||
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
|
||||
protected String[] disabledServers = new String[0];
|
||||
@@ -340,24 +341,6 @@ public abstract class TabFeature {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the feature displayed in /tab cpu.
|
||||
*
|
||||
* @return name of the feature displayed in /tab cpu
|
||||
*/
|
||||
public String getFeatureName() {
|
||||
return featureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text to display in /tab cpu of this feature refreshing visuals
|
||||
*
|
||||
* @return Text to display as refreshing of this feature
|
||||
*/
|
||||
public String getRefreshDisplayName() {
|
||||
return refreshDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if method with specified name is overridden
|
||||
* in the implementation, {@code false} if not
|
||||
|
||||
@@ -303,18 +303,4 @@ public interface TabPlayer {
|
||||
* @return player's game profile name as seen by other players
|
||||
*/
|
||||
String getNickname();
|
||||
|
||||
/**
|
||||
* Returns player's chat message signing key. Returned object is a direct
|
||||
* object from the server, which is different per platform.
|
||||
*
|
||||
* @return Player's direct chat message signing key
|
||||
*/
|
||||
Object getProfilePublicKey();
|
||||
|
||||
/**
|
||||
* Return player's chat session ID
|
||||
* @return player's chat session ID
|
||||
*/
|
||||
UUID getChatSessionId();
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package me.neznamy.tab.api.bossbar;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* An enum representing all available BossBar styles.
|
||||
* Calling ordinal() will return style's network ID.
|
||||
* Style names are equal to NMS names.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum BarStyle {
|
||||
|
||||
PROGRESS("SOLID"),
|
||||
@@ -14,23 +18,5 @@ public enum BarStyle {
|
||||
NOTCHED_20("SEGMENTED_20");
|
||||
|
||||
/** Style's name used in Bukkit API */
|
||||
private final String bukkitName;
|
||||
|
||||
/**
|
||||
* Initializes enum constant with given bukkit name
|
||||
*
|
||||
* @param bukkitName
|
||||
* Name of color to be returned in {@code getBukkitName()}
|
||||
*/
|
||||
BarStyle(String bukkitName){
|
||||
this.bukkitName = bukkitName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of this style in Bukkit API
|
||||
* @return name of this style in Bukkit API
|
||||
*/
|
||||
public String getBukkitName() {
|
||||
return bukkitName;
|
||||
}
|
||||
@Getter private final String bukkitName;
|
||||
}
|
||||
@@ -1,52 +1,19 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Class for click event action in chat component
|
||||
*/
|
||||
@Data
|
||||
public class ChatClickable {
|
||||
|
||||
/** Click action */
|
||||
private final EnumClickAction action;
|
||||
@NonNull private final EnumClickAction action;
|
||||
|
||||
/** Click value */
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given action and value.
|
||||
*
|
||||
* @param action
|
||||
* click event action
|
||||
* @param value
|
||||
* click event value
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code action} is null or {@code value} is null
|
||||
*/
|
||||
public ChatClickable(EnumClickAction action, String value) {
|
||||
Preconditions.checkNotNull(action, "hover action");
|
||||
Preconditions.checkNotNull(value, "hover value");
|
||||
this.action = action;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns click action defined in constructor
|
||||
*
|
||||
* @return click action
|
||||
*/
|
||||
public EnumClickAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns click action defined in constructor
|
||||
*
|
||||
* @return click action
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
@NonNull private final String value;
|
||||
|
||||
/**
|
||||
* Enum for all possible click actions
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ChatComponentEntity extends IChatBaseComponent {
|
||||
|
||||
@@ -12,9 +12,7 @@ public class ChatComponentEntity extends IChatBaseComponent {
|
||||
private final String VALUE_1_12;
|
||||
private final String VALUE_1_8;
|
||||
|
||||
public ChatComponentEntity(String type, UUID id, String name) {
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(id, "id");
|
||||
public ChatComponentEntity(@NonNull String type, @NonNull UUID id, String name) {
|
||||
this.CONTENTS = String.format("{\"type\":\"%s\",\"id\":\"%s\",\"name\":{\"text\":\"%s\"}}", type, id, name);
|
||||
this.VALUE_1_13 = String.format("{type:\"%s\",id:\"%s\",name:\"{\\\"text\\\":\\\"%s\\\"}\"}", type, id, name);
|
||||
this.VALUE_1_12 = String.format("{type:\"%s\",id:\"%s\",name:\"%s\"}", type, id, name);
|
||||
|
||||
@@ -1,70 +1,32 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Class for hover event action in chat component
|
||||
*/
|
||||
@Data
|
||||
public class ChatHoverable {
|
||||
|
||||
/** Hover action */
|
||||
private final EnumHoverAction action;
|
||||
@NonNull private final EnumHoverAction action;
|
||||
|
||||
/** Hover value */
|
||||
private final IChatBaseComponent value;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given action and value.
|
||||
*
|
||||
* @param action
|
||||
* hover event action
|
||||
* @param value
|
||||
* hover event value
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code action} is null or {@code value} is null
|
||||
*/
|
||||
public ChatHoverable(EnumHoverAction action, IChatBaseComponent value) {
|
||||
Preconditions.checkNotNull(action, "hover action");
|
||||
Preconditions.checkNotNull(value, "hover value");
|
||||
this.action = action;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hover action defined in constructor
|
||||
*
|
||||
* @return hover action
|
||||
*/
|
||||
public EnumHoverAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hover value defined in constructor
|
||||
*
|
||||
* @return hover value
|
||||
*/
|
||||
public IChatBaseComponent getValue() {
|
||||
return value;
|
||||
}
|
||||
@NonNull private final IChatBaseComponent value;
|
||||
|
||||
/**
|
||||
* Enum for all possible hover actions
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum EnumHoverAction {
|
||||
|
||||
SHOW_TEXT("contents"),
|
||||
SHOW_ITEM("value"),
|
||||
SHOW_ENTITY("contents");
|
||||
|
||||
private final String preferredKey;
|
||||
|
||||
EnumHoverAction(String preferredKey) {
|
||||
this.preferredKey = preferredKey;
|
||||
}
|
||||
|
||||
public String getPreferredKey() {
|
||||
return preferredKey;
|
||||
}
|
||||
@Getter private final String preferredKey;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,31 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
|
||||
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data @NoArgsConstructor
|
||||
public class ChatModifier {
|
||||
|
||||
private TextColor color;
|
||||
|
||||
private Boolean bold;
|
||||
private Boolean italic;
|
||||
private Boolean underlined;
|
||||
private Boolean strikethrough;
|
||||
private Boolean obfuscated;
|
||||
|
||||
private ChatClickable clickEvent;
|
||||
private ChatHoverable hoverEvent;
|
||||
|
||||
private String font;
|
||||
|
||||
private ProtocolVersion targetVersion;
|
||||
|
||||
public ChatModifier() {
|
||||
}
|
||||
|
||||
public ChatModifier(ChatModifier modifier) {
|
||||
Preconditions.checkNotNull(modifier, "modifier");
|
||||
public ChatModifier(@NonNull ChatModifier modifier) {
|
||||
this.color = modifier.color == null ? null : new TextColor(modifier.color);
|
||||
this.bold = modifier.bold;
|
||||
this.italic = modifier.italic;
|
||||
@@ -44,53 +38,6 @@ public class ChatModifier {
|
||||
this.targetVersion = modifier.targetVersion;
|
||||
}
|
||||
|
||||
public TextColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(TextColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Boolean getBold() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
public Boolean getItalic() {
|
||||
return italic;
|
||||
}
|
||||
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
|
||||
public Boolean getUnderlined() {
|
||||
return underlined;
|
||||
}
|
||||
|
||||
public void setUnderlined(Boolean underlined) {
|
||||
this.underlined = underlined;
|
||||
}
|
||||
|
||||
public Boolean getStrikethrough() {
|
||||
return strikethrough;
|
||||
}
|
||||
|
||||
public void setStrikethrough(Boolean strikethrough) {
|
||||
this.strikethrough = strikethrough;
|
||||
}
|
||||
|
||||
public Boolean getObfuscated() {
|
||||
return obfuscated;
|
||||
}
|
||||
|
||||
public void setObfuscated(Boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bold is defined and set to true, false otherwise
|
||||
@@ -137,30 +84,13 @@ public class ChatModifier {
|
||||
return Boolean.TRUE.equals(obfuscated);
|
||||
}
|
||||
|
||||
public ChatClickable getClickEvent() {
|
||||
return clickEvent;
|
||||
}
|
||||
|
||||
public void setClickEvent(ChatClickable clickEvent) {
|
||||
this.clickEvent = clickEvent;
|
||||
}
|
||||
|
||||
public ChatHoverable getHoverEvent() {
|
||||
return hoverEvent;
|
||||
}
|
||||
|
||||
public void setHoverEvent(ChatHoverable hoverEvent) {
|
||||
this.hoverEvent = hoverEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to OPEN_URL and url to given value
|
||||
*
|
||||
* @param url
|
||||
* url to open
|
||||
*/
|
||||
public void onClickOpenUrl(String url) {
|
||||
Preconditions.checkNotNull(url, "url");
|
||||
public void onClickOpenUrl(@NonNull String url) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.OPEN_URL, url);
|
||||
}
|
||||
|
||||
@@ -170,8 +100,7 @@ public class ChatModifier {
|
||||
* @param command
|
||||
* command to perform, might be without / to send a chat message
|
||||
*/
|
||||
public void onClickRunCommand(String command) {
|
||||
Preconditions.checkNotNull(command, "command");
|
||||
public void onClickRunCommand(@NonNull String command) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.RUN_COMMAND, command);
|
||||
}
|
||||
|
||||
@@ -181,8 +110,7 @@ public class ChatModifier {
|
||||
* @param command
|
||||
* command to suggest
|
||||
*/
|
||||
public void onClickSuggestCommand(String command) {
|
||||
Preconditions.checkNotNull(command, "command");
|
||||
public void onClickSuggestCommand(@NonNull String command) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.SUGGEST_COMMAND, command);
|
||||
}
|
||||
|
||||
@@ -203,14 +131,11 @@ public class ChatModifier {
|
||||
* @param text
|
||||
* text to copy to clipboard on click
|
||||
*/
|
||||
public void onClickCopyToClipBoard(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public void onClickCopyToClipBoard(@NonNull String text) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.COPY_TO_CLIPBOARD, text);
|
||||
}
|
||||
|
||||
public void onClick(EnumClickAction action, String value) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
public void onClick(@NonNull EnumClickAction action, @NonNull String value) {
|
||||
clickEvent = new ChatClickable(action, value);
|
||||
}
|
||||
|
||||
@@ -220,8 +145,7 @@ public class ChatModifier {
|
||||
* @param text
|
||||
* text to show
|
||||
*/
|
||||
public void onHoverShowText(IChatBaseComponent text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public void onHoverShowText(@NonNull IChatBaseComponent text) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_TEXT, text);
|
||||
}
|
||||
|
||||
@@ -231,8 +155,7 @@ public class ChatModifier {
|
||||
* @param serializedItem
|
||||
* item to show
|
||||
*/
|
||||
public void onHoverShowItem(String serializedItem) {
|
||||
Preconditions.checkNotNull(serializedItem, "item");
|
||||
public void onHoverShowItem(@NonNull String serializedItem) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ITEM, new IChatBaseComponent(serializedItem));
|
||||
}
|
||||
|
||||
@@ -246,26 +169,14 @@ public class ChatModifier {
|
||||
* @param name
|
||||
* entity custom name
|
||||
*/
|
||||
public void onHoverShowEntity(String type, UUID id, String name) {
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(id, "id");
|
||||
public void onHoverShowEntity(@NonNull String type, @NonNull UUID id, String name) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
|
||||
}
|
||||
|
||||
public void onHover(EnumHoverAction action, IChatBaseComponent value) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
public void onHover(@NonNull EnumHoverAction action, @NonNull IChatBaseComponent value) {
|
||||
hoverEvent = new ChatHoverable(action, value);
|
||||
}
|
||||
|
||||
public String getFont() {
|
||||
return font;
|
||||
}
|
||||
|
||||
public void setFont(String font) {
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONObject serialize() {
|
||||
JSONObject json = new JSONObject();
|
||||
@@ -305,12 +216,4 @@ public class ChatModifier {
|
||||
if (isObfuscated()) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void setTargetVersion(ProtocolVersion targetVersion) {
|
||||
this.targetVersion = targetVersion;
|
||||
}
|
||||
|
||||
public ProtocolVersion getTargetVersion() {
|
||||
return targetVersion;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
@@ -23,10 +25,11 @@ import java.util.List;
|
||||
* will act like a normal chat component.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@RequiredArgsConstructor
|
||||
public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
|
||||
/** The original serialized component string */
|
||||
private final String json;
|
||||
@NonNull private final String json;
|
||||
|
||||
/**
|
||||
* Flag tracking whether this component was fully deserialized
|
||||
@@ -41,17 +44,6 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
*/
|
||||
private boolean modified;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param json
|
||||
* serialized component as string
|
||||
*/
|
||||
public DeserializedChatComponent(String json) {
|
||||
Preconditions.checkNotNull(json, "json");
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (modified) return super.toString();
|
||||
@@ -77,7 +69,7 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatModifier getModifier() {
|
||||
public @NotNull ChatModifier getModifier() {
|
||||
if (!deserialized) deserialize();
|
||||
return super.getModifier();
|
||||
}
|
||||
@@ -90,14 +82,14 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
public void addExtra(@NotNull IChatBaseComponent child) {
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
super.addExtra(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
public void setModifier(@NotNull ChatModifier modifier) {
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
super.setModifier(modifier);
|
||||
@@ -162,9 +154,7 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
* name of key
|
||||
* @return value from json object or null if not present
|
||||
*/
|
||||
private static Boolean getBoolean(JSONObject jsonObject, String key) {
|
||||
Preconditions.checkNotNull(jsonObject, "json object");
|
||||
Preconditions.checkNotNull(key, "key");
|
||||
private static Boolean getBoolean(@NonNull JSONObject jsonObject, @NonNull String key) {
|
||||
String value = String.valueOf(jsonObject.getOrDefault(key, null));
|
||||
return "null".equals(value) ? null : Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
|
||||
/**
|
||||
@@ -40,22 +41,22 @@ public enum EnumChatFormat {
|
||||
public static final String COLOR_STRING = String.valueOf(COLOR_CHAR);
|
||||
|
||||
/** Character representing the color or magic code */
|
||||
private final char character;
|
||||
@Getter private final char character;
|
||||
|
||||
/** Red value of this constant, 0 for magic codes */
|
||||
private final short red;
|
||||
@Getter private final short red;
|
||||
|
||||
/** Green value of this constant, 0 for magic codes */
|
||||
private final short green;
|
||||
@Getter private final short green;
|
||||
|
||||
/** Blue value of this constant, 0 for magic codes */
|
||||
private final short blue;
|
||||
@Getter private final short blue;
|
||||
|
||||
/** Color as 6-digit hex code, null for magic codes */
|
||||
private final String hexCode;
|
||||
@Getter private final String hexCode;
|
||||
|
||||
/** Color symbol followed by constant's character */
|
||||
private final String chatFormat;
|
||||
@Getter private final String format;
|
||||
|
||||
/**
|
||||
* Constructs new color instance with given character and hex code
|
||||
@@ -67,7 +68,7 @@ public enum EnumChatFormat {
|
||||
*/
|
||||
EnumChatFormat(char character, String hexCode) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
this.format = String.valueOf(COLOR_CHAR) + character;
|
||||
this.hexCode = hexCode;
|
||||
int hexColor = Integer.parseInt(hexCode, 16);
|
||||
red = (short) ((hexColor >> 16) & 0xFF);
|
||||
@@ -83,40 +84,13 @@ public enum EnumChatFormat {
|
||||
*/
|
||||
EnumChatFormat(char character) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
this.format = String.valueOf(COLOR_CHAR) + character;
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
hexCode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns red value of this color code
|
||||
*
|
||||
* @return red value
|
||||
*/
|
||||
public short getRed() {
|
||||
return red;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns green value of this color code
|
||||
*
|
||||
* @return green value
|
||||
*/
|
||||
public short getGreen() {
|
||||
return green;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns blue value of this color code
|
||||
*
|
||||
* @return blue value
|
||||
*/
|
||||
public short getBlue() {
|
||||
return blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value based on provided character or null if character is not valid
|
||||
*
|
||||
@@ -152,33 +126,6 @@ public enum EnumChatFormat {
|
||||
return EnumChatFormat.WHITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns color char followed by color's character
|
||||
*
|
||||
* @return color char followed by color's character
|
||||
*/
|
||||
public String getFormat() {
|
||||
return chatFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns character representing this color
|
||||
*
|
||||
* @return character representing this color
|
||||
*/
|
||||
public char getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hex code of this format, null if this is a magic code
|
||||
*
|
||||
* @return hex code of this format
|
||||
*/
|
||||
public String getHexCode() {
|
||||
return hexCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value with exact red, green and blue values or null if no match was found
|
||||
*
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.api.util.ComponentCache;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.*;
|
||||
@@ -12,39 +16,37 @@ import java.util.*;
|
||||
* A class representing the n.m.s.IChatBaseComponent class to make work with it much easier
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoArgsConstructor
|
||||
public class IChatBaseComponent {
|
||||
|
||||
/**
|
||||
* Component cache maps to avoid large memory allocations as well as
|
||||
* higher CPU usage when using animations which send the same text on repeat.
|
||||
*/
|
||||
private static final Map<String, IChatBaseComponent> componentCache = new HashMap<>();
|
||||
private static final Map<IChatBaseComponent, String> serializeCacheModern = new HashMap<>();
|
||||
private static final Map<IChatBaseComponent, String> serializeCacheLegacy = new HashMap<>();
|
||||
private static final ComponentCache<String, IChatBaseComponent> stringCache = new ComponentCache<>(10000, (text, clientVersion) -> {
|
||||
return text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x") || text.contains("<") ?
|
||||
IChatBaseComponent.fromColoredText(text) : //contains RGB colors
|
||||
new IChatBaseComponent(text); //no RGB
|
||||
});
|
||||
private static final ComponentCache<IChatBaseComponent, String> serializeCache = new ComponentCache<>(10000,
|
||||
(component, clientVersion) -> component.toString());
|
||||
|
||||
/** Text of the component */
|
||||
private String text;
|
||||
@Getter @Setter private String text;
|
||||
|
||||
/** Chat modifier containing color, magic codes, hover and click event */
|
||||
private ChatModifier modifier = new ChatModifier();
|
||||
@Getter @Setter @NonNull private ChatModifier modifier = new ChatModifier();
|
||||
|
||||
/** Extra components used in "extra" field */
|
||||
private List<IChatBaseComponent> extra;
|
||||
|
||||
/**
|
||||
* Constructs a new empty component
|
||||
*/
|
||||
public IChatBaseComponent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new component which is a clone of provided component
|
||||
*
|
||||
* @param component
|
||||
* component to clone
|
||||
*/
|
||||
public IChatBaseComponent(IChatBaseComponent component) {
|
||||
Preconditions.checkNotNull(component, "component");
|
||||
public IChatBaseComponent(@NonNull IChatBaseComponent component) {
|
||||
this.text = component.text;
|
||||
this.modifier = new ChatModifier(component.modifier);
|
||||
for (IChatBaseComponent child : component.getExtra()) {
|
||||
@@ -93,53 +95,11 @@ public class IChatBaseComponent {
|
||||
* @param child
|
||||
* component to append
|
||||
*/
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
Preconditions.checkNotNull(child, "extra");
|
||||
public void addExtra(@NonNull IChatBaseComponent child) {
|
||||
if (extra == null) extra = new ArrayList<>();
|
||||
extra.add(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text of this component
|
||||
*
|
||||
* @return text of this component
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns chat modifier of this component
|
||||
*
|
||||
* @return chat modifier of this component
|
||||
*/
|
||||
public ChatModifier getModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets modifier to provided value
|
||||
*
|
||||
* @param modifier
|
||||
* modifier to set value to
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code modifier} is null
|
||||
*/
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
Preconditions.checkNotNull(modifier, "modifier");
|
||||
this.modifier = modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes text of this component
|
||||
*
|
||||
* @param text
|
||||
* text to show
|
||||
*/
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes string and returns created component. If provided string is null, returns null.
|
||||
*
|
||||
@@ -207,19 +167,7 @@ public class IChatBaseComponent {
|
||||
for (IChatBaseComponent child : getExtra()) {
|
||||
child.modifier.setTargetVersion(clientVersion);
|
||||
}
|
||||
String string;
|
||||
if (clientVersion.getMinorVersion() >= 16) {
|
||||
if (serializeCacheModern.containsKey(this)) return serializeCacheModern.get(this);
|
||||
string = toString();
|
||||
if (serializeCacheModern.size() > 10000) serializeCacheModern.clear();
|
||||
serializeCacheModern.put(this, string);
|
||||
} else {
|
||||
if (serializeCacheLegacy.containsKey(this)) return serializeCacheLegacy.get(this);
|
||||
string = toString();
|
||||
if (serializeCacheLegacy.size() > 10000) serializeCacheLegacy.clear();
|
||||
serializeCacheLegacy.put(this, string);
|
||||
}
|
||||
return string;
|
||||
return serializeCache.get(this, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,8 +177,7 @@ public class IChatBaseComponent {
|
||||
* text to convert
|
||||
* @return organized component from colored text
|
||||
*/
|
||||
public static IChatBaseComponent fromColoredText(String originalText){
|
||||
Preconditions.checkNotNull(originalText, "text");
|
||||
public static IChatBaseComponent fromColoredText(@NonNull String originalText){
|
||||
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText));
|
||||
List<IChatBaseComponent> components = new ArrayList<>();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -421,18 +368,6 @@ public class IChatBaseComponent {
|
||||
* @return The most performance-optimized component based on text
|
||||
*/
|
||||
public static IChatBaseComponent optimizedComponent(String text){
|
||||
if (text == null) return null;
|
||||
if (componentCache.containsKey(text)) return componentCache.get(text);
|
||||
IChatBaseComponent component;
|
||||
if (text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x") || text.contains("<")){
|
||||
//contains RGB colors
|
||||
component = IChatBaseComponent.fromColoredText(text);
|
||||
} else {
|
||||
//no RGB
|
||||
component = new IChatBaseComponent(text);
|
||||
}
|
||||
if (componentCache.size() > 10000) componentCache.clear();
|
||||
componentCache.put(text, component);
|
||||
return component;
|
||||
return stringCache.get(text, null);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
@@ -24,7 +26,7 @@ public class TextColor {
|
||||
* Boolean value whether the legacy color was forced with constructor or should be
|
||||
* automatically assigned as closest color.
|
||||
* This value is used in gradients when converting text for legacy players. */
|
||||
private boolean legacyColorForced;
|
||||
@Getter private boolean legacyColorForced;
|
||||
|
||||
/**
|
||||
* Constructs new instance as a clone of the provided color.
|
||||
@@ -34,8 +36,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if color is {@code null}
|
||||
*/
|
||||
public TextColor(TextColor color) {
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
public TextColor(@NonNull TextColor color) {
|
||||
rgb = color.rgb;
|
||||
legacyColor = color.legacyColor;
|
||||
hexCode = color.hexCode;
|
||||
@@ -50,8 +51,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if hexCode is {@code null}
|
||||
*/
|
||||
public TextColor(String hexCode) {
|
||||
Preconditions.checkNotNull(hexCode, "hex code");
|
||||
public TextColor(@NonNull String hexCode) {
|
||||
this.hexCode = hexCode;
|
||||
}
|
||||
|
||||
@@ -65,9 +65,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code hexCode} is {@code null} or {@code legacyColor} is {@code null}
|
||||
*/
|
||||
public TextColor(String hexCode, EnumChatFormat legacyColor) {
|
||||
Preconditions.checkNotNull(hexCode, "hex code");
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color");
|
||||
public TextColor(@NonNull String hexCode, EnumChatFormat legacyColor) {
|
||||
this.hexCode = hexCode;
|
||||
this.legacyColorForced = true;
|
||||
this.legacyColor = legacyColor;
|
||||
@@ -81,8 +79,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code legacyColor} is {@code null}
|
||||
*/
|
||||
public TextColor(EnumChatFormat legacyColor) {
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color");
|
||||
public TextColor(@NonNull EnumChatFormat legacyColor) {
|
||||
this.rgb = (legacyColor.getRed() << 16) + (legacyColor.getGreen() << 8) + legacyColor.getBlue();
|
||||
this.hexCode = legacyColor.getHexCode();
|
||||
}
|
||||
@@ -195,15 +192,6 @@ public class TextColor {
|
||||
return "#" + getHexCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if legacy color was forced with a constructor, false if not
|
||||
*
|
||||
* @return true if forced, false if not
|
||||
*/
|
||||
public boolean isLegacyColorForced() {
|
||||
return legacyColorForced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the string as it appears in chat component and turns it into the color object.
|
||||
* If the entered string is null, returns null.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -10,21 +13,12 @@ import java.util.List;
|
||||
* slows down the performance, as well as makes code more complicated
|
||||
* and open to problems.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class WrappedChatComponent extends IChatBaseComponent {
|
||||
|
||||
/** Original NMS component */
|
||||
private final Object originalComponent;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param originalComponent
|
||||
* Original NMS component
|
||||
*/
|
||||
public WrappedChatComponent(Object originalComponent) {
|
||||
this.originalComponent = originalComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original NMS component
|
||||
* @return the original NMS component
|
||||
@@ -54,7 +48,7 @@ public class WrappedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatModifier getModifier() {
|
||||
public @NotNull ChatModifier getModifier() {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@@ -64,12 +58,12 @@ public class WrappedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
public void addExtra(@NonNull IChatBaseComponent child) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
public void setModifier(@NotNull ChatModifier modifier) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package me.neznamy.tab.api.chat.rgb;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
import me.neznamy.tab.api.chat.rgb.format.*;
|
||||
@@ -12,7 +9,12 @@ import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.CommonGradient;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.GradientPattern;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.KyoriGradient;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A helper class to reformat all RGB formats into the default #RRGGBB and apply gradients
|
||||
@@ -20,7 +22,7 @@ import me.neznamy.tab.api.util.Preconditions;
|
||||
public class RGBUtils {
|
||||
|
||||
/** Instance of the class */
|
||||
private static final RGBUtils instance = new RGBUtils();
|
||||
@Getter private static final RGBUtils instance = new RGBUtils();
|
||||
|
||||
/** Registered RGB formatters */
|
||||
private final RGBFormatter[] formats;
|
||||
@@ -39,10 +41,9 @@ public class RGBUtils {
|
||||
*/
|
||||
public RGBUtils() {
|
||||
List<RGBFormatter> list = new ArrayList<>();
|
||||
try {
|
||||
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
|
||||
if (ReflectionUtils.classExists("net.kyori.adventure.text.minimessage.MiniMessage")) {
|
||||
list.add(new MiniMessageFormat());
|
||||
} catch (ClassNotFoundException ignored) {}
|
||||
}
|
||||
list.add(new BukkitFormat());
|
||||
list.add(new CMIFormat());
|
||||
list.add(new UnnamedFormat1());
|
||||
@@ -65,15 +66,6 @@ public class RGBUtils {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of this class
|
||||
*
|
||||
* @return instance
|
||||
*/
|
||||
public static RGBUtils getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all RGB formats and gradients to text and returns it.
|
||||
*
|
||||
@@ -81,8 +73,7 @@ public class RGBUtils {
|
||||
* original text
|
||||
* @return text where everything is converted to #RRGGBB
|
||||
*/
|
||||
public String applyFormats(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public String applyFormats(@NonNull String text) {
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, false);
|
||||
@@ -101,8 +92,7 @@ public class RGBUtils {
|
||||
* original text
|
||||
* @return text where all gradients with static text are converted to #RRGGBB
|
||||
*/
|
||||
public String applyCleanGradients(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public String applyCleanGradients(@NonNull String text) {
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, true);
|
||||
@@ -183,8 +173,7 @@ public class RGBUtils {
|
||||
* string to check
|
||||
* @return {@code true} if valid, {@code false} if not
|
||||
*/
|
||||
public boolean isHexCode(String string) {
|
||||
Preconditions.checkNotNull(string, "string");
|
||||
public boolean isHexCode(@NonNull String string) {
|
||||
if (string.length() != 6) return false;
|
||||
for (int i=0; i<6; i++) {
|
||||
char c = string.charAt(i);
|
||||
|
||||
@@ -3,9 +3,11 @@ package me.neznamy.tab.api.chat.rgb.gradient;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class CommonGradient implements GradientPattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
@@ -15,18 +17,7 @@ public class CommonGradient implements GradientPattern {
|
||||
private final int startColorStart;
|
||||
private final int messageStart;
|
||||
private final int endColorStartSub;
|
||||
|
||||
public CommonGradient(Pattern pattern, Pattern legacyPattern, String containCheck, int legacyCharPosition,
|
||||
int startColorStart, int messageStart, int endColorStartSub) {
|
||||
this.pattern = pattern;
|
||||
this.legacyPattern = legacyPattern;
|
||||
this.containCheck = containCheck;
|
||||
this.legacyCharPosition = legacyCharPosition;
|
||||
this.startColorStart = startColorStart;
|
||||
this.messageStart = messageStart;
|
||||
this.endColorStartSub = endColorStartSub;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String applyPattern(String text, boolean ignorePlaceholders) {
|
||||
if (!text.contains(containCheck)) return text;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package me.neznamy.tab.api.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -23,10 +25,10 @@ public abstract class ConfigurationFile {
|
||||
protected List<String> header;
|
||||
|
||||
/** Configuration file content */
|
||||
protected Map<String, Object> values;
|
||||
@Getter @Setter protected Map<String, Object> values;
|
||||
|
||||
/** File to use */
|
||||
protected final File file;
|
||||
@Getter protected final File file;
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
@@ -43,8 +45,7 @@ public abstract class ConfigurationFile {
|
||||
* @throws IOException
|
||||
* if I/O operation with the file unexpectedly fails
|
||||
*/
|
||||
protected ConfigurationFile(InputStream source, File destination) throws IOException {
|
||||
Preconditions.checkNotNull(destination, "destination");
|
||||
protected ConfigurationFile(InputStream source, @NonNull File destination) throws IOException {
|
||||
this.file = destination;
|
||||
if (file.getParentFile() != null && !file.getParentFile().exists()) Files.createDirectories(file.getParentFile().toPath());
|
||||
if (!file.exists()) {
|
||||
@@ -68,19 +69,6 @@ public abstract class ConfigurationFile {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root value map
|
||||
*
|
||||
* @return the root value map
|
||||
*/
|
||||
public Map<String, Object> getValues(){
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets config option with specified path. If the option is not present and
|
||||
* {@code defaultValue} is not {@code null}, value is inserted, {@link #save()}
|
||||
@@ -408,8 +396,4 @@ public abstract class ConfigurationFile {
|
||||
Files.write(file.toPath(), content);
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.util.BiFunctionWithException;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class for packet building, methods are overridden in
|
||||
@@ -48,9 +48,7 @@ public class PacketBuilder {
|
||||
* @throws ReflectiveOperationException
|
||||
* if reflection fails
|
||||
*/
|
||||
public Object build(TabPacket packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
Preconditions.checkNotNull(packet, "packet");
|
||||
Preconditions.checkNotNull(clientVersion, "clientVersion");
|
||||
public Object build(@NonNull TabPacket packet, @NonNull ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return buildMap.get(packet.getClass()).apply(packet, clientVersion);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/** UUID of the BossBar */
|
||||
private final UUID id;
|
||||
|
||||
/** Action of this packet */
|
||||
private final Action action;
|
||||
private Action action;
|
||||
|
||||
/** BossBar title */
|
||||
private String name;
|
||||
@@ -53,11 +55,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, String name, float pct, BarColor color, BarStyle overlay) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(name, "title");
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
Preconditions.checkNotNull(overlay, "style");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, @NonNull String name, float pct, @NonNull BarColor color, @NonNull BarStyle overlay) {
|
||||
this.action = Action.ADD;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -68,28 +66,26 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#REMOVE} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
public PacketPlayOutBoss(@NonNull UUID id) {
|
||||
this.action = Action.REMOVE;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PCT action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_PCT} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param pct
|
||||
* BossBar progress
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, float pct) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, float pct) {
|
||||
this.action = Action.UPDATE_PCT;
|
||||
this.id = id;
|
||||
this.pct = pct;
|
||||
@@ -97,16 +93,14 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_NAME action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_NAME} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param name
|
||||
* BossBar title
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, String name) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(name, "title");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, @NonNull String name) {
|
||||
this.action = Action.UPDATE_NAME;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -114,7 +108,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_STYLE} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
@@ -123,10 +117,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
Preconditions.checkNotNull(overlay, "style");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, @NonNull BarColor color, @NonNull BarStyle overlay) {
|
||||
this.action = Action.UPDATE_STYLE;
|
||||
this.id = id;
|
||||
this.color = color;
|
||||
@@ -135,7 +126,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PROPERTIES action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_PROPERTIES} action
|
||||
*
|
||||
* @param darkenScreen
|
||||
* Darken screen flag
|
||||
@@ -144,8 +135,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
* @param createWorldFog
|
||||
* Create fog flag
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
|
||||
this.action = Action.UPDATE_PROPERTIES;
|
||||
this.id = id;
|
||||
this.darkenScreen = darkenScreen;
|
||||
@@ -169,123 +159,6 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutBoss{id=%s,operation=%s,name=%s,pct=%s,color=%s,overlay=%s,darkenScreen=%s,playMusic=%s,createWorldFog=%s}",
|
||||
id, action, name, pct, color, overlay, darkenScreen, playMusic, createWorldFog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #color}
|
||||
*
|
||||
* @return color
|
||||
*/
|
||||
public BarColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #overlay}
|
||||
*
|
||||
* @return style
|
||||
*/
|
||||
public BarStyle getOverlay() {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #id}
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #pct}
|
||||
*
|
||||
* @return progress
|
||||
*/
|
||||
public float getPct() {
|
||||
return pct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #darkenScreen}
|
||||
*
|
||||
* @return darkenScreen
|
||||
*/
|
||||
public boolean isDarkenScreen() {
|
||||
return darkenScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #createWorldFog}
|
||||
*
|
||||
* @return createWorldFog
|
||||
*/
|
||||
public boolean isCreateWorldFog() {
|
||||
return createWorldFog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playMusic}
|
||||
*
|
||||
* @return playMusic
|
||||
*/
|
||||
public boolean isPlayMusic() {
|
||||
return playMusic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #darkenScreen} to specified value
|
||||
*
|
||||
* @param darkenScreen
|
||||
* Darken screen flag
|
||||
*/
|
||||
public void setDarkenScreen(boolean darkenScreen) {
|
||||
this.darkenScreen = darkenScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #createWorldFog} to specified value
|
||||
*
|
||||
* @param createWorldFog
|
||||
* Create fog flag
|
||||
*/
|
||||
public void setCreateWorldFog(boolean createWorldFog) {
|
||||
this.createWorldFog = createWorldFog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #playMusic} to specified value
|
||||
*
|
||||
* @param playMusic
|
||||
* Play music flag
|
||||
*/
|
||||
public void setPlayMusic(boolean playMusic) {
|
||||
this.playMusic = playMusic;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing all valid boss packet actions.
|
||||
* Calling ordinal() will return action's network ID.
|
||||
|
||||
@@ -1,56 +1,20 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutChat implements TabPacket {
|
||||
|
||||
/** Message to be sent */
|
||||
private final IChatBaseComponent message;
|
||||
@NonNull private final IChatBaseComponent message;
|
||||
|
||||
/** Message position */
|
||||
private final ChatMessageType type;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param message
|
||||
* Chat message to be sent
|
||||
* @param type
|
||||
* Message position
|
||||
*/
|
||||
public PacketPlayOutChat(IChatBaseComponent message, ChatMessageType type) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
Preconditions.checkNotNull(type, "message type");
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutChat{message=%s,type=%s}", message, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #message}
|
||||
*
|
||||
* @return message
|
||||
*/
|
||||
public IChatBaseComponent getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #type}
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public ChatMessageType getType() {
|
||||
return type;
|
||||
}
|
||||
@NonNull private final ChatMessageType type;
|
||||
|
||||
/**
|
||||
* An enum representing positions of a chat message
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
|
||||
/** Packet actions */
|
||||
@@ -39,9 +42,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
public PacketPlayOutPlayerInfo(@NonNull EnumPlayerInfoAction action, @NonNull List<PlayerInfoData> entries) {
|
||||
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
|
||||
actions = EnumSet.of(EnumPlayerInfoAction.ADD_PLAYER,
|
||||
EnumPlayerInfoAction.INITIALIZE_CHAT,
|
||||
@@ -63,62 +64,32 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, PlayerInfoData... entries) {
|
||||
Preconditions.checkNotNull(actions, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
public PacketPlayOutPlayerInfo(@NonNull EnumSet<EnumPlayerInfoAction> actions, @NonNull PlayerInfoData... entries) {
|
||||
this.actions = actions;
|
||||
this.entries = Arrays.asList(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param actions
|
||||
* Packet actions
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, List<PlayerInfoData> entries) {
|
||||
Preconditions.checkNotNull(actions, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.actions = actions;
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerInfo{actions=%s,entries=%s}", actions, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #actions}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public EnumSet<EnumPlayerInfoAction> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entries}
|
||||
*
|
||||
* @return affected entries
|
||||
*/
|
||||
public List<PlayerInfoData> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass representing player list entry
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public static class PlayerInfoData {
|
||||
|
||||
/** Latency */
|
||||
private int latency;
|
||||
/** Real name of affected player */
|
||||
private String name;
|
||||
|
||||
/** Player UUID */
|
||||
@NonNull private UUID uniqueId;
|
||||
|
||||
/** Player's skin, null for empty skin */
|
||||
private Skin skin;
|
||||
|
||||
/** I have no idea what this is */
|
||||
private boolean listed;
|
||||
|
||||
/** Latency */
|
||||
private int latency;
|
||||
|
||||
/** GameMode */
|
||||
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
|
||||
|
||||
@@ -128,53 +99,12 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
*/
|
||||
private IChatBaseComponent displayName;
|
||||
|
||||
/** Real name of affected player */
|
||||
private String name;
|
||||
|
||||
/** Player UUID */
|
||||
private UUID uniqueId;
|
||||
|
||||
/** Player's skin, null for empty skin */
|
||||
private Skin skin;
|
||||
|
||||
/** Chat session ID */
|
||||
private UUID chatSessionId;
|
||||
|
||||
/** Player's chat signing key */
|
||||
private Object profilePublicKey;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction#ADD_PLAYER} action
|
||||
*
|
||||
* @param name
|
||||
* Player's name
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
* @param skin
|
||||
* Player's platform-specific skin object
|
||||
* @param latency
|
||||
* Player's ping
|
||||
* @param gameMode
|
||||
* Player's GameMode
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
* @param profilePublicKey
|
||||
* Player's chat signing key
|
||||
*/
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, boolean listed, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, UUID chatSessionId, Object profilePublicKey) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
this.name = name;
|
||||
this.uniqueId = uniqueId;
|
||||
this.skin = skin;
|
||||
this.listed = listed;
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
this.chatSessionId = chatSessionId;
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction#UPDATE_GAME_MODE} action
|
||||
@@ -184,8 +114,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param gameMode
|
||||
* Player's GameMode
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, EnumGamemode gameMode) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
public PlayerInfoData(@NonNull UUID uniqueId, EnumGamemode gameMode) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
@@ -199,8 +128,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param latency
|
||||
* Player's ping
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, int latency) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
public PlayerInfoData(@NonNull UUID uniqueId, int latency) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.latency = latency;
|
||||
}
|
||||
@@ -214,8 +142,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, IChatBaseComponent displayName) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
public PlayerInfoData(@NonNull UUID uniqueId, IChatBaseComponent displayName) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
@@ -227,186 +154,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId) {
|
||||
public PlayerInfoData(@NonNull UUID uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s,profilePublicKey=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin, profilePublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #latency}
|
||||
*
|
||||
* @return latency
|
||||
*/
|
||||
public int getLatency() {
|
||||
return latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #latency} to specified value
|
||||
*
|
||||
* @param latency
|
||||
* Latency to use
|
||||
*/
|
||||
public void setLatency(int latency) {
|
||||
this.latency = latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #listed}
|
||||
*
|
||||
* @return listed
|
||||
*/
|
||||
public boolean isListed() {
|
||||
return listed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #listed} to specified value
|
||||
*
|
||||
* @param listed
|
||||
* Listed flag
|
||||
*/
|
||||
public void setListed(boolean listed) {
|
||||
this.listed = listed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #displayName}
|
||||
*
|
||||
* @return displayName
|
||||
*/
|
||||
public IChatBaseComponent getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #displayName} to specified value
|
||||
*
|
||||
* @param displayName
|
||||
* Display name to use
|
||||
*/
|
||||
public void setDisplayName(IChatBaseComponent displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #uniqueId}
|
||||
*
|
||||
* @return uniqueId
|
||||
*/
|
||||
public UUID getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #uniqueId} to specified value
|
||||
*
|
||||
* @param uniqueId
|
||||
* UUID to use
|
||||
*/
|
||||
public void setUniqueId(UUID uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #name} to specified value
|
||||
*
|
||||
* @param name
|
||||
* name to use
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #gameMode}
|
||||
*
|
||||
* @return gameMode
|
||||
*/
|
||||
public EnumGamemode getGameMode() {
|
||||
return gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #gameMode} to specified value
|
||||
*
|
||||
* @param gameMode
|
||||
* GameMode to use
|
||||
*/
|
||||
public void setGameMode(EnumGamemode gameMode) {
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #skin}
|
||||
*
|
||||
* @return skin
|
||||
*/
|
||||
public Skin getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #skin} to specified value
|
||||
*
|
||||
* @param skin
|
||||
* Skin to use
|
||||
*/
|
||||
public void setSkin(Skin skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #chatSessionId}
|
||||
*
|
||||
* @return chatSessionId
|
||||
*/
|
||||
public UUID getChatSessionId() {
|
||||
return chatSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #chatSessionId} to specified value
|
||||
*
|
||||
* @param chatSessionId
|
||||
* chatSessionId to use
|
||||
*/
|
||||
public void setChatSessionId(UUID chatSessionId) {
|
||||
this.chatSessionId = chatSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #profilePublicKey}
|
||||
*
|
||||
* @return profilePublicKey
|
||||
*/
|
||||
public Object getProfilePublicKey() {
|
||||
return profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #profilePublicKey} to specified value
|
||||
*
|
||||
* @param profilePublicKey
|
||||
* profilePublicKey to use
|
||||
*/
|
||||
public void setProfilePublicKey(Object profilePublicKey) {
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-44
@@ -1,18 +1,21 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
|
||||
|
||||
/** TabList header */
|
||||
private final IChatBaseComponent header;
|
||||
@NonNull private final IChatBaseComponent header;
|
||||
|
||||
/** TabList footer */
|
||||
private final IChatBaseComponent footer;
|
||||
@NonNull private final IChatBaseComponent footer;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. They are converted to {@link IChatBaseComponent}
|
||||
@@ -23,48 +26,8 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
|
||||
* @param footer
|
||||
* TabList footer
|
||||
*/
|
||||
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
|
||||
Preconditions.checkNotNull(header, "header");
|
||||
Preconditions.checkNotNull(footer, "footer");
|
||||
public PacketPlayOutPlayerListHeaderFooter(@NonNull String header, @NonNull String footer) {
|
||||
this.header = IChatBaseComponent.optimizedComponent(header);
|
||||
this.footer = IChatBaseComponent.optimizedComponent(footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param header
|
||||
* TabList header
|
||||
* @param footer
|
||||
* TabList footer
|
||||
*/
|
||||
public PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent header, IChatBaseComponent footer) {
|
||||
Preconditions.checkNotNull(header, "header");
|
||||
Preconditions.checkNotNull(footer, "footer");
|
||||
this.header = header;
|
||||
this.footer = footer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerListHeaderFooter{header=%s,footer=%s}", header, footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #header}
|
||||
*
|
||||
* @return header
|
||||
*/
|
||||
public IChatBaseComponent getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #footer}
|
||||
*
|
||||
* @return footer
|
||||
*/
|
||||
public IChatBaseComponent getFooter() {
|
||||
return footer;
|
||||
}
|
||||
}
|
||||
+4
-42
@@ -1,10 +1,12 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
|
||||
|
||||
/**
|
||||
@@ -16,45 +18,5 @@ public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
|
||||
private final int slot;
|
||||
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param slot
|
||||
* Display slot
|
||||
* @param objectiveName
|
||||
* Internal name of the objective
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardDisplayObjective(int slot, String objectiveName) {
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
this.slot = slot;
|
||||
this.objectiveName = objectiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardDisplayObjective{slot=%s,objectiveName=%s}", slot, objectiveName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #slot}
|
||||
*
|
||||
* @return slot
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
@NonNull private final String objectiveName;
|
||||
}
|
||||
+13
-77
@@ -1,21 +1,16 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
/** Display name */
|
||||
private final String displayName;
|
||||
|
||||
/** Display type, only takes effect in PlayerList */
|
||||
private final EnumScoreboardHealthDisplay renderType;
|
||||
|
||||
/**
|
||||
* Packet action.
|
||||
* 0 = register,
|
||||
@@ -24,30 +19,14 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
*/
|
||||
private final int action;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param action
|
||||
* Packet action (0 = add, 1 = remove, 2 = update title)
|
||||
* @param objectiveName
|
||||
* objective name, up to 16 characters long
|
||||
* @param displayName
|
||||
* title
|
||||
* @param renderType
|
||||
* display type
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective(int action, String objectiveName, String displayName, EnumScoreboardHealthDisplay renderType) {
|
||||
Preconditions.checkRange(action, 0, 2, "action");
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
Preconditions.checkNotNull(renderType, "render type");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = displayName;
|
||||
this.renderType = renderType;
|
||||
this.action = action;
|
||||
}
|
||||
/** Up to 16 characters long objective name */
|
||||
@NonNull private final String objectiveName;
|
||||
|
||||
/** Display name */
|
||||
private final String displayName;
|
||||
|
||||
/** Display type, only takes effect in PlayerList */
|
||||
private final EnumScoreboardHealthDisplay renderType;
|
||||
|
||||
/**
|
||||
* Constructs new packet with given objective name and 1 (unregister) action.
|
||||
@@ -57,8 +36,7 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective(String objectiveName) {
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
public PacketPlayOutScoreboardObjective(@NonNull String objectiveName) {
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = ""; //avoiding NPE on <1.7
|
||||
@@ -66,48 +44,6 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
this.renderType = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardObjective{objectiveName=%s,displayName=%s,renderType=%s,method=%s}",
|
||||
objectiveName, displayName, renderType, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #displayName}
|
||||
*
|
||||
* @return displayName
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #renderType}
|
||||
*
|
||||
* @return renderType
|
||||
*/
|
||||
public EnumScoreboardHealthDisplay getRenderType() {
|
||||
return renderType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing available display types.
|
||||
* They only take effect in PlayerList position.
|
||||
|
||||
@@ -1,91 +1,26 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutScoreboardScore implements TabPacket {
|
||||
|
||||
/** Packet action */
|
||||
private final Action action;
|
||||
@NonNull private final Action action;
|
||||
|
||||
/** Objective name */
|
||||
private final String objectiveName;
|
||||
@NonNull private final String objectiveName;
|
||||
|
||||
/** Affected player */
|
||||
private final String player;
|
||||
@NonNull private final String player;
|
||||
|
||||
/** Player's score */
|
||||
private final int score;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param action
|
||||
* Packet action
|
||||
* @param objectiveName
|
||||
* Objective name
|
||||
* @param player
|
||||
* Affected player
|
||||
* @param score
|
||||
* Player's score
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardScore(Action action, String objectiveName, String player, int score) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
Preconditions.checkNotNull(player, "player");
|
||||
this.action = action;
|
||||
this.objectiveName = objectiveName;
|
||||
this.player = player;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardScore{action=%s,objectiveName=%s,player=%s,score=%s}",
|
||||
action, objectiveName, player, score);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objective name
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #player}
|
||||
*
|
||||
* @return player
|
||||
*/
|
||||
public String getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #score}
|
||||
*
|
||||
* @return score
|
||||
*/
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing action types
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
|
||||
/**
|
||||
@@ -18,7 +21,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* Players with teams are below them, players of the same team are
|
||||
* sorted alphabetically by usernames.
|
||||
*/
|
||||
private final String name;
|
||||
@NonNull private final String name;
|
||||
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
private String playerPrefix;
|
||||
@@ -49,7 +52,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* Player name length is limited to 16 characters for <1.8, 40 characters since 1.8.
|
||||
* Entity UUID option was added in 1.8.
|
||||
*/
|
||||
private Collection<String> players = Collections.emptyList();
|
||||
@NonNull private Collection<String> players = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Packet action.
|
||||
@@ -80,8 +83,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
private PacketPlayOutScoreboardTeam(int action, String name) {
|
||||
Preconditions.checkNotNull(name, "team name");
|
||||
private PacketPlayOutScoreboardTeam(int action, @NonNull String name) {
|
||||
Preconditions.checkMaxLength(name, 16, "team name");
|
||||
this.action = action;
|
||||
this.name = name;
|
||||
@@ -107,13 +109,9 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, Collection<String> players, int options) {
|
||||
public PacketPlayOutScoreboardTeam(String team, @NonNull String prefix, @NonNull String suffix, String visibility,
|
||||
String collision, @NonNull Collection<String> players, int options) {
|
||||
this(0, team);
|
||||
Preconditions.checkNotNull(prefix, "prefix");
|
||||
Preconditions.checkNotNull(suffix, "suffix");
|
||||
Preconditions.checkNotNull(visibility, "visibility");
|
||||
Preconditions.checkNotNull(collision, "collision");
|
||||
Preconditions.checkNotNull(players, "players");
|
||||
this.playerPrefix = prefix;
|
||||
this.playerSuffix = suffix;
|
||||
this.nameTagVisibility = visibility;
|
||||
@@ -152,13 +150,9 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, int options) {
|
||||
public PacketPlayOutScoreboardTeam(String team, @NonNull String prefix, @NonNull String suffix,
|
||||
String visibility, String collision, int options) {
|
||||
this(2, team);
|
||||
Preconditions.checkNotNull(prefix, "prefix");
|
||||
Preconditions.checkNotNull(suffix, "suffix");
|
||||
Preconditions.checkNotNull(visibility, "visibility");
|
||||
Preconditions.checkNotNull(collision, "collision");
|
||||
Preconditions.checkNotNull(players, "players");
|
||||
this.playerPrefix = prefix;
|
||||
this.playerSuffix = suffix;
|
||||
this.nameTagVisibility = visibility;
|
||||
@@ -179,107 +173,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, Collection<String> players, boolean add) {
|
||||
public PacketPlayOutScoreboardTeam(String team, @NonNull Collection<String> players, boolean add) {
|
||||
this(add ? 3 : 4, team);
|
||||
Preconditions.checkNotNull(players, "players");
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardTeam{name=%s,playerPrefix=%s,playerSuffix=%s,nameTagVisibility=%s,"
|
||||
+ "collisionRule=%s,color=%s,players=%s,method=%s,options=%s}",
|
||||
name, playerPrefix, playerSuffix, nameTagVisibility, collisionRule, color, players, action, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return team name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playerPrefix}
|
||||
*
|
||||
* @return playerPrefix
|
||||
*/
|
||||
public String getPlayerPrefix() {
|
||||
return playerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playerSuffix}
|
||||
*
|
||||
* @return playerSuffix
|
||||
*/
|
||||
public String getPlayerSuffix() {
|
||||
return playerSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #nameTagVisibility}
|
||||
*
|
||||
* @return nameTagVisibility
|
||||
*/
|
||||
public String getNameTagVisibility() {
|
||||
return nameTagVisibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #collisionRule}
|
||||
*
|
||||
* @return collisionRule
|
||||
*/
|
||||
public String getCollisionRule() {
|
||||
return collisionRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #color} to specified color
|
||||
*
|
||||
* @param color
|
||||
* color to use
|
||||
*/
|
||||
public void setColor(EnumChatFormat color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #color}
|
||||
*
|
||||
* @return color
|
||||
*/
|
||||
public EnumChatFormat getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #players}
|
||||
*
|
||||
* @return players
|
||||
*/
|
||||
public Collection<String> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #options}
|
||||
*
|
||||
* @return team options
|
||||
*/
|
||||
public int getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,18 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Class representing a minecraft skin as a value - signature pair.
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class Skin {
|
||||
|
||||
/** Skin value */
|
||||
private final String value;
|
||||
@NonNull private final String value;
|
||||
|
||||
/** Skin signature, can be null */
|
||||
private final String signature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param value
|
||||
* skin value
|
||||
* @param signature
|
||||
* skin signature
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code value} is {@code null}
|
||||
*/
|
||||
public Skin(String value, String signature) {
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
this.value = value;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns skin value
|
||||
*
|
||||
* @return skin value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns skin signature
|
||||
*
|
||||
* @return skin signature
|
||||
*/
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user-friendly representation of this object
|
||||
*
|
||||
* @return user-friendly representation of this object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Skin{value=%s,signature=%s}", value, signature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ComponentCache<K, V> {
|
||||
|
||||
private final int cacheSize;
|
||||
private final BiFunctionWithException<K, ProtocolVersion, V> function;
|
||||
private final Map<K, V> cacheModern = new HashMap<>();
|
||||
private final Map<K, V> cacheLegacy = new HashMap<>();
|
||||
|
||||
public V get(K key, ProtocolVersion clientVersion) {
|
||||
try {
|
||||
if (key == null) return null;
|
||||
Map<K, V> cache = clientVersion == null || clientVersion.getMinorVersion() >= 16 ? cacheModern : cacheLegacy;
|
||||
if (cache.containsKey(key)) return cache.get(key);
|
||||
V value = function.apply(key, clientVersion);
|
||||
if (cache.size() > cacheSize) cache.clear();
|
||||
cache.put(key, value);
|
||||
return value;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
|
||||
/**
|
||||
@@ -12,20 +13,6 @@ public class Preconditions {
|
||||
*/
|
||||
private Preconditions() {}
|
||||
|
||||
/**
|
||||
* Checks object and throws {@code IllegalArgumentException} if it is {@code null}.
|
||||
*
|
||||
* @param obj
|
||||
* Object to check
|
||||
* @param name
|
||||
* Name of the variable to use in exception message
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code obj} is {@code null}
|
||||
*/
|
||||
public static void checkNotNull(Object obj, String name) {
|
||||
if (obj == null) throw new IllegalArgumentException(name + " cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks length of String and throws {@code IllegalArgumentException} if it is
|
||||
* longer than limit.
|
||||
@@ -39,8 +26,7 @@ public class Preconditions {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code string} is {@code null} or longer than {@code maxLength} characters
|
||||
*/
|
||||
public static void checkMaxLength(String string, int maxLength, String name) {
|
||||
checkNotNull(string, name);
|
||||
public static void checkMaxLength(@NonNull String string, int maxLength, String name) {
|
||||
if (string.length() > maxLength) throw new IllegalArgumentException(name + " is longer than " + maxLength + " characters (" + string.length() + ")");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
/**
|
||||
* Utility class storing methods working with reflection
|
||||
*/
|
||||
public class ReflectionUtils {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if class with given full name exists,
|
||||
* {@code false} if not.
|
||||
*
|
||||
* @param path
|
||||
* Full class path and name
|
||||
* @return {@code true} if exists, {@code false} if not
|
||||
*/
|
||||
public static boolean classExists(String path) {
|
||||
try {
|
||||
Class.forName(path);
|
||||
return true;
|
||||
} catch (ClassNotFoundException | NullPointerException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.plugin.indra)
|
||||
implementation(libs.plugin.shadow)
|
||||
implementation(libs.plugin.lombok)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
id("net.kyori.indra")
|
||||
}
|
||||
|
||||
indra {
|
||||
javaVersions {
|
||||
target(8)
|
||||
}
|
||||
github("NEZNAMY", "TAB") {
|
||||
ci(true)
|
||||
}
|
||||
`java-library`
|
||||
id("io.freefair.lombok")
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -27,4 +19,15 @@ tasks {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet")
|
||||
}
|
||||
compileJava {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
options.release.set(8)
|
||||
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,25 @@
|
||||
plugins {
|
||||
id("tab.base-conventions")
|
||||
id("net.kyori.indra.publishing")
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
indra {
|
||||
publishReleasesTo("krypton", "https://repo.kryptonmc.org/releases")
|
||||
publishSnapshotsTo("krypton", "https://repo.kryptonmc.org/snapshots")
|
||||
configurePublications {
|
||||
artifactId = "tab-$artifactId"
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "krypton"
|
||||
url = uri("https://repo.kryptonmc.org/releases")
|
||||
credentials(PasswordCredentials::class)
|
||||
}
|
||||
}
|
||||
publications.create<MavenPublication>("mavenJava") {
|
||||
groupId = rootProject.group as String
|
||||
artifactId = "tab-${project.name}"
|
||||
version = rootProject.version as String
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -11,8 +11,9 @@ allprojects {
|
||||
val platforms = setOf(
|
||||
projects.bukkit,
|
||||
projects.bungeecord,
|
||||
// projects.krypton,
|
||||
projects.velocity
|
||||
projects.krypton,
|
||||
projects.velocity,
|
||||
//projects.sponge
|
||||
).map { it.dependencyProject }
|
||||
|
||||
val special = setOf(
|
||||
|
||||
@@ -6,7 +6,4 @@ dependencies {
|
||||
compileOnly(libs.vault)
|
||||
compileOnly(libs.via)
|
||||
compileOnly(libs.authlib)
|
||||
compileOnly(libs.essentials) {
|
||||
exclude("org.spigotmc", "spigot-api")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
@@ -12,20 +13,11 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
/**
|
||||
* The core for bukkit forwarding events into all enabled features
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitEventListener implements Listener {
|
||||
|
||||
/** Platform instance */
|
||||
private final BukkitPlatform platform;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param platform
|
||||
* Platform instance
|
||||
*/
|
||||
public BukkitEventListener(BukkitPlatform platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerQuitEvent to remove player data and forward the event to features
|
||||
|
||||
@@ -12,6 +12,7 @@ import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
|
||||
import me.neznamy.tab.api.util.ComponentCache;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
@@ -35,10 +36,14 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
private final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
|
||||
|
||||
/** Component cache for better performance (1.16+ players) */
|
||||
private final Map<IChatBaseComponent, Object> componentCacheModern = new HashMap<>();
|
||||
|
||||
private final ComponentCache<IChatBaseComponent, Object> componentCacheModern = new ComponentCache<>(10000,
|
||||
(component, clientVersion) -> nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion)));
|
||||
|
||||
/** Component cache for better performance (1.15- players) */
|
||||
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
|
||||
private final ComponentCache<IChatBaseComponent, Object> componentCacheLegacy = new ComponentCache<>(10000,
|
||||
(component, clientVersion) -> nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion)));
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
@@ -490,14 +495,14 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
if (packet.getAction() == Action.UPDATE_PCT || packet.getAction() == Action.ADD) {
|
||||
float health = 300*packet.getPct();
|
||||
if (health == 0) health = 1;
|
||||
w.helper().setHealth(health);
|
||||
w.getHelper().setHealth(health);
|
||||
}
|
||||
if (packet.getAction() == Action.UPDATE_NAME || packet.getAction() == Action.ADD) {
|
||||
w.helper().setCustomName(packet.getName(), clientVersion);
|
||||
w.getHelper().setCustomName(packet.getName(), clientVersion);
|
||||
}
|
||||
if (packet.getAction() == Action.ADD) {
|
||||
w.helper().setEntityFlags((byte) 32);
|
||||
w.helper().setWitherInvulnerableTime(880); // Magic number
|
||||
w.getHelper().setEntityFlags((byte) 32);
|
||||
w.getHelper().setWitherInvulnerableTime(880); // Magic number
|
||||
return build(new PacketPlayOutSpawnEntityLiving(entityId, new UUID(0, 0), EntityType.WITHER, new Location(null, 0,0,0), w));
|
||||
} else {
|
||||
return build(new PacketPlayOutEntityMetadata(entityId, w));
|
||||
@@ -530,14 +535,8 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
if (component == null) return null;
|
||||
if (component instanceof WrappedChatComponent) return ((WrappedChatComponent) component).get();
|
||||
Map<IChatBaseComponent, Object> cache = clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy;
|
||||
if (cache.containsKey(component)) return cache.get(component);
|
||||
if (cache.size() > 10000) cache.clear();
|
||||
Object chat = nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion));
|
||||
cache.put(component, chat);
|
||||
return chat;
|
||||
return (clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy).get(component, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.function.Function;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
@@ -39,21 +40,12 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
/** Injected player */
|
||||
private final TabPlayer player;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player
|
||||
*
|
||||
* @param player
|
||||
* player to inject
|
||||
*/
|
||||
public BukkitChannelDuplexHandler(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(@NotNull ChannelHandlerContext context, @NotNull Object packet) {
|
||||
try {
|
||||
@@ -72,7 +64,7 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
|
||||
return;
|
||||
}
|
||||
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
long time = System.nanoTime();
|
||||
modifyPlayers(packet);
|
||||
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
|
||||
+24
-49
@@ -1,29 +1,21 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import net.ess3.api.events.NickChangeEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Bukkit registry to register bukkit-only and universal placeholders
|
||||
@@ -33,9 +25,6 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
/** Number formatter for 2 decimal places */
|
||||
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
|
||||
|
||||
/** JavaPlugin reference for registering events */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
/** Vault Chat hook */
|
||||
private Chat chat;
|
||||
|
||||
@@ -55,13 +44,9 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
private Method purpurIsAfk;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter and loads hooks
|
||||
*
|
||||
* @param plugin
|
||||
* reference to the plugin's main class
|
||||
* Constructs new instance and loads hooks
|
||||
*/
|
||||
public BukkitPlaceholderRegistry(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
public BukkitPlaceholderRegistry() {
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
|
||||
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
@@ -100,28 +85,18 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
}
|
||||
Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
|
||||
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
|
||||
if (essentials != null) {
|
||||
try {
|
||||
Object user = essentials.getClass().getMethod("getUser", UUID.class).invoke(essentials, p.getUniqueId());
|
||||
if ((boolean) user.getClass().getMethod("isAfk").invoke(user)) return true;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get AFK status of " + p.getName() + " using Essentials", e);
|
||||
}
|
||||
}
|
||||
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
|
||||
});
|
||||
if (essentials != null) {
|
||||
PlayerPlaceholder nick = manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, p -> {
|
||||
String nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
|
||||
return nickname == null ? p.getName() : nickname;
|
||||
});
|
||||
Listener nickListener = new Listener() {
|
||||
@EventHandler
|
||||
public void onNickChange(NickChangeEvent e) {
|
||||
String name = e.getValue() == null ? e.getController().getName() : e.getValue();
|
||||
TabPlayer player = TAB.getInstance().getPlayer(e.getController().getUUID());
|
||||
if (player == null) return;
|
||||
nick.updateValue(player, name);
|
||||
}
|
||||
};
|
||||
nick.enableTriggerMode(() -> Bukkit.getPluginManager().registerEvents(nickListener, plugin),
|
||||
() -> HandlerList.unregisterAll(nickListener));
|
||||
} else {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, TabPlayer::getName);
|
||||
}
|
||||
// Removed placeholder, keeping the implementation to avoid placeholder breaking for users on update
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, TabPlayer::getName);
|
||||
|
||||
if (chat != null) {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> chat.getPlayerPrefix((Player) p.getPlayer()));
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PetFix;
|
||||
import me.neznamy.tab.platforms.bukkit.features.WitherBossBar;
|
||||
import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.BukkitNameTagX;
|
||||
import me.neznamy.tab.platforms.bukkit.permission.Vault;
|
||||
import me.neznamy.tab.shared.Platform;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.backend.BackendPlatform;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
import me.neznamy.tab.shared.permission.None;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
@@ -28,20 +33,21 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Implementation of Platform interface for Bukkit platform
|
||||
*/
|
||||
public class BukkitPlatform extends Platform {
|
||||
public class BukkitPlatform extends BackendPlatform {
|
||||
|
||||
/** Plugin instance for registering tasks and events */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
/** Variables checking presence of other plugins to hook into */
|
||||
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
|
||||
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
|
||||
@Getter @Setter private boolean libsDisguisesEnabled = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
|
||||
private Plugin viaVersion;
|
||||
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
|
||||
|
||||
@@ -54,6 +60,15 @@ public class BukkitPlatform extends Platform {
|
||||
public BukkitPlatform(JavaPlugin plugin) {
|
||||
super(new BukkitPacketBuilder());
|
||||
this.plugin = plugin;
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
|
||||
if (ReflectionUtils.classExists("com.viaversion.viaversion.api.Via")) {
|
||||
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
|
||||
} else {
|
||||
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,50 +84,8 @@ public class BukkitPlatform extends Platform {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFeatures() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
|
||||
try {
|
||||
Class.forName("com.viaversion.viaversion.api.Via");
|
||||
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
|
||||
} catch (ClassNotFoundException e) {
|
||||
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
|
||||
}
|
||||
}
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().isPipelineInjection())
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
|
||||
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new BukkitNameTagX(plugin));
|
||||
} else {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
|
||||
}
|
||||
}
|
||||
tab.loadUniversalFeatures();
|
||||
if (tab.getConfiguration().getConfig().getBoolean("bossbar.enabled", false)) {
|
||||
if (tab.getServerVersion().getMinorVersion() < 9) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new WitherBossBar(plugin));
|
||||
} else {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new BossBarManagerImpl());
|
||||
}
|
||||
}
|
||||
if (tab.getServerVersion().getMinorVersion() >= 9 && tab.getConfiguration().getConfig().getBoolean("fix-pet-names.enabled", false))
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PET_FIX, new PetFix());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false))
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, new PerWorldPlayerList(plugin));
|
||||
if (placeholderAPI && tab.getConfiguration().getConfig().getBoolean("placeholders.register-tab-expansion", true)) {
|
||||
BukkitTabExpansion expansion = new BukkitTabExpansion();
|
||||
expansion.register();
|
||||
TAB.getInstance().getPlaceholderManager().setTabExpansion(expansion);
|
||||
}
|
||||
for (Player p : getOnlinePlayers()) {
|
||||
tab.addPlayer(new BukkitTabPlayer(p, getProtocolVersion(p)));
|
||||
}
|
||||
public BossBarManagerImpl getLegacyBossBar() {
|
||||
return new WitherBossBar(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -121,6 +94,48 @@ public class BukkitPlatform extends Platform {
|
||||
return pl == null ? null : pl.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlayers() {
|
||||
for (Player p : getOnlinePlayers()) {
|
||||
TAB.getInstance().addPlayer(new BukkitTabPlayer(p, getProtocolVersion(p)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerPlaceholders() {
|
||||
new BukkitPlaceholderRegistry().registerPlaceholders(TAB.getInstance().getPlaceholderManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PipelineInjector getPipelineInjector() {
|
||||
return new BukkitPipelineInjector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameTag getUnlimitedNametags() {
|
||||
return new BukkitNameTagX(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabExpansion getTabExpansion() {
|
||||
if (placeholderAPI) {
|
||||
BukkitTabExpansion expansion = new BukkitTabExpansion();
|
||||
expansion.register();
|
||||
return expansion;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature getPetFix() {
|
||||
return new PetFix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TabFeature getPerWorldPlayerlist() {
|
||||
return new PerWorldPlayerList(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns online players from Bukkit API
|
||||
*
|
||||
@@ -181,27 +196,6 @@ public class BukkitPlatform extends Platform {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns status of LibsDisguises plugin presence
|
||||
*
|
||||
* @return {@code true} if plugin is enabled, {@code false} if not
|
||||
*/
|
||||
public boolean isLibsDisguisesEnabled() {
|
||||
return libsDisguises;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets LibsDisguises presence status to provided value. This is used
|
||||
* to disable LibsDisguises hook in case the plugin is not correctly loaded
|
||||
* for any reason to avoid error spam in the hook.
|
||||
*
|
||||
* @param enabled
|
||||
* New status of LibsDisguises presence
|
||||
*/
|
||||
public void setLibsDisguisesEnabled(boolean enabled) {
|
||||
libsDisguises = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets protocol version of requested player and returns it.
|
||||
*
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarFlag;
|
||||
@@ -16,18 +25,10 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* TabPlayer implementation for Bukkit platform
|
||||
@@ -65,8 +66,7 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
Preconditions.checkNotNull(permission, "permission");
|
||||
public boolean hasPermission(@NonNull String permission) {
|
||||
long time = System.nanoTime();
|
||||
boolean value = getPlayer().hasPermission(permission);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
|
||||
@@ -285,40 +285,6 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProfilePublicKey() {
|
||||
if (NMSStorage.getInstance().getMinorVersion() < 19) return null;
|
||||
try {
|
||||
if (NMSStorage.getInstance().RemoteChatSession != null) {
|
||||
//1.19.3+
|
||||
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
|
||||
if (session == null) return null;
|
||||
Object key = NMSStorage.getInstance().RemoteChatSession_getProfilePublicKey.invoke(session);
|
||||
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
|
||||
} else {
|
||||
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
|
||||
if (key == null) return null;
|
||||
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getChatSessionId() {
|
||||
if (NMSStorage.getInstance().RemoteChatSession != null) return null;
|
||||
try {
|
||||
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
|
||||
if (session == null) return null;
|
||||
return (UUID) NMSStorage.getInstance().RemoteChatSession_getSessionId.invoke(session);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get chat session of " + getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel() {
|
||||
try {
|
||||
|
||||
+54
-269
@@ -1,65 +1,33 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Pose;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A class representing an armor stand attached to a player
|
||||
*/
|
||||
public class BukkitArmorStand implements ArmorStand {
|
||||
|
||||
/** Entity id counter to pick unique entity ID for each armor stand */
|
||||
private static int idCounter = 2000000000;
|
||||
|
||||
/** NameTag feature */
|
||||
private final BukkitNameTagX manager = (BukkitNameTagX) TabAPI.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
|
||||
|
||||
/** Armor stand manager which this armor stand belongs to */
|
||||
private final BukkitArmorStandManager asm;
|
||||
|
||||
/** Owner of the armor stand */
|
||||
private final TabPlayer owner;
|
||||
public class BukkitArmorStand extends BackendArmorStand {
|
||||
|
||||
/** Owner as a Bukkit player */
|
||||
private final Player player;
|
||||
|
||||
/** Offset in blocks, 0 for original height */
|
||||
private double yOffset;
|
||||
|
||||
/** If offset is static, or dynamic based on other armor stands */
|
||||
private final boolean staticOffset;
|
||||
|
||||
/** Entity ID of this armor stand */
|
||||
private final int entityId = idCounter++;
|
||||
|
||||
/** Unique ID of this armor stand */
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
|
||||
/** Sneaking flag of armor stands */
|
||||
private boolean sneaking;
|
||||
|
||||
/** Armor stand visibility */
|
||||
private boolean visible;
|
||||
|
||||
/** Refresh property dedicated to this armor stand */
|
||||
private final Property property;
|
||||
|
||||
/** Entity destroy packet */
|
||||
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
protected TabPacket destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
@@ -75,47 +43,12 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
* @param staticOffset
|
||||
* {@code true} if offset is static, {@code false} if not
|
||||
*/
|
||||
public BukkitArmorStand(BukkitArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
this.asm = asm;
|
||||
this.owner = owner;
|
||||
this.staticOffset = staticOffset;
|
||||
public BukkitArmorStand(BackendNameTagX feature, BackendArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
super(feature, asm, owner, propertyName, yOffset, staticOffset);
|
||||
player = (Player) owner.getPlayer();
|
||||
this.yOffset = yOffset;
|
||||
this.property = owner.getProperty(propertyName);
|
||||
visible = getVisibility();
|
||||
sneaking = player.isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
visible = getVisibility();
|
||||
updateMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStaticOffset() {
|
||||
return staticOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getOffset() {
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(double offset) {
|
||||
if (yOffset == offset) return;
|
||||
yOffset = offset;
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_OFFSET_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(TabPlayer viewer) {
|
||||
for (TabPacket packet : getSpawnPackets(viewer)) {
|
||||
@@ -123,95 +56,11 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(TabPlayer viewer) {
|
||||
if (!asm.isNearby(viewer) && viewer != owner) {
|
||||
asm.spawn(viewer);
|
||||
} else {
|
||||
viewer.sendCustomPacket(getTeleportPacket(viewer), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sneak(boolean sneaking) {
|
||||
if (this.sneaking == sneaking) return; //idk
|
||||
this.sneaking = sneaking;
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !manager.isArmorStandsAlwaysVisible()) {
|
||||
//1.14.x client sided bug, de-spawning completely
|
||||
if (sneaking) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SNEAK);
|
||||
} else {
|
||||
spawn(viewer);
|
||||
}
|
||||
} else {
|
||||
//respawning so there's no animation and it's instant
|
||||
respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateVisibility(boolean force) {
|
||||
boolean visibility = getVisibility();
|
||||
if (visible != visibility || force) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns teleport packet for specified viewer
|
||||
*
|
||||
* @param viewer
|
||||
* player to get location for
|
||||
* @return teleport packet
|
||||
*/
|
||||
public PacketPlayOutEntityTeleport getTeleportPacket(TabPlayer viewer) {
|
||||
return new PacketPlayOutEntityTeleport(entityId, getArmorStandLocationFor(viewer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand's metadata
|
||||
*/
|
||||
public void updateMetadata() {
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general visibility rule for everyone with limited info
|
||||
*
|
||||
* @return {@code true} if armor stand should be visible, {@code false} if not
|
||||
*/
|
||||
public boolean getVisibility() {
|
||||
if (manager.isArmorStandsAlwaysVisible()) return true;
|
||||
if (owner.isDisguised() || manager.getVehicleManager().isOnBoat(owner)) return false;
|
||||
return !owner.hasInvisibilityPotion() && owner.getGamemode() != 3 && !manager.hasHiddenNametag(owner) && property.get().length() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns location where armor stand should be at time of calling.
|
||||
* This takes into account everything that affects height, including
|
||||
@@ -222,54 +71,43 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
* @return Location where armor stand should be for specified viewer
|
||||
*/
|
||||
public Location getLocation(TabPlayer viewer) {
|
||||
double x = player.getLocation().getX();
|
||||
double y = getY() + yOffset;
|
||||
double z = player.getLocation().getZ();
|
||||
if (!player.isSleeping()) {
|
||||
if (sneaking) {
|
||||
if (viewer.getVersion().getMinorVersion() >= 15) {
|
||||
y += 1.37;
|
||||
} else if (viewer.getVersion().getMinorVersion() >= 9) {
|
||||
y += 1.52;
|
||||
} else {
|
||||
y += 1.7;
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 1.8 : 1.84; // Normal
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 0.2 : 0.26; // Sleeping
|
||||
}
|
||||
return new Location(null,x,y,z);
|
||||
}
|
||||
Location loc = player.getLocation();
|
||||
double y = loc.getY();
|
||||
|
||||
/**
|
||||
* Returns Y where player is based on player's vehicle due to bukkit API bug
|
||||
*
|
||||
* @return correct player's Y
|
||||
*/
|
||||
protected double getY() {
|
||||
//1.14+ server sided bug
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
if (vehicle.getType().toString().contains("HORSE")) { //covering all 3 horse types
|
||||
return vehicle.getLocation().getY() + 0.85;
|
||||
y = vehicle.getLocation().getY() + 0.85;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("DONKEY")) { //1.11+
|
||||
return vehicle.getLocation().getY() + 0.525;
|
||||
y = vehicle.getLocation().getY() + 0.525;
|
||||
}
|
||||
if (vehicle.getType() == EntityType.PIG) {
|
||||
return vehicle.getLocation().getY() + 0.325;
|
||||
y = vehicle.getLocation().getY() + 0.325;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("STRIDER")) { //1.16+
|
||||
return vehicle.getLocation().getY() + 1.15;
|
||||
y = vehicle.getLocation().getY() + 1.15;
|
||||
}
|
||||
} else {
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
y = loc.getY()-1.22;
|
||||
}
|
||||
}
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
return player.getLocation().getY();
|
||||
y += getYAdd(player.isSleeping(), sneaking, viewer);
|
||||
return new Location(null, loc.getX(), y, loc.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetadata(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTeleportPacket(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityTeleport(entityId, getLocation(viewer)),
|
||||
TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,6 +119,21 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
return TabAPI.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
|
||||
}
|
||||
|
||||
private TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = calculateVisibility();
|
||||
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getLocation(viewer), null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
};
|
||||
} else {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getLocation(viewer), dataWatcher),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates data watcher with specified display name for viewer
|
||||
*
|
||||
@@ -292,79 +145,11 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
*/
|
||||
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
|
||||
DataWatcher datawatcher = new DataWatcher();
|
||||
|
||||
byte flag = 32; //invisible
|
||||
if (sneaking) flag += (byte)2;
|
||||
datawatcher.helper().setEntityFlags(flag);
|
||||
datawatcher.helper().setCustomName(displayName, viewer.getVersion());
|
||||
|
||||
boolean visibility;
|
||||
if (isNameVisiblyEmpty(displayName) || !((Player) viewer.getPlayer()).canSee(player) ||
|
||||
manager.hasHiddenNametag(owner, viewer) || manager.hasHiddenNameTagVisibilityView(viewer)) {
|
||||
visibility = false;
|
||||
} else {
|
||||
visibility = visible;
|
||||
}
|
||||
datawatcher.helper().setCustomNameVisible(visibility);
|
||||
|
||||
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x()) datawatcher.helper().setArmorStandFlags((byte)16);
|
||||
datawatcher.getHelper().setEntityFlags((byte) (sneaking ? 34 : 32));
|
||||
datawatcher.getHelper().setCustomName(displayName, viewer.getVersion());
|
||||
datawatcher.getHelper().setCustomNameVisible(!shouldBeInvisibleFor(viewer, displayName) && visible);
|
||||
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x())
|
||||
datawatcher.getHelper().setArmorStandFlags((byte)16);
|
||||
return datawatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if display name is in fact empty,
|
||||
* for example only containing color codes, {@code false} if not.
|
||||
*
|
||||
* @param displayName
|
||||
* string to check
|
||||
* @return {@code true} if it's empty, {@code false} if not
|
||||
*/
|
||||
private boolean isNameVisiblyEmpty(String displayName) {
|
||||
if (displayName.length() == 0) return true;
|
||||
if (!displayName.startsWith(EnumChatFormat.COLOR_STRING) && !displayName.startsWith("&") && !displayName.startsWith("#")) return false;
|
||||
String text = IChatBaseComponent.fromColoredText(displayName).toRawText();
|
||||
if (text.contains(" ")) text = text.replace(" ", "");
|
||||
return text.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of packets to send to make armor stand spawn with metadata
|
||||
*
|
||||
* @param viewer
|
||||
* viewer to apply relational placeholders for
|
||||
* @return List of packets that spawn the armor stand
|
||||
*/
|
||||
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = getVisibility();
|
||||
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
};
|
||||
} else {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), dataWatcher),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns location where armor stand should be for specified viewer
|
||||
*
|
||||
* @param viewer
|
||||
* player to get location for
|
||||
* @return location of armor stand
|
||||
*/
|
||||
public Location getArmorStandLocationFor(TabPlayer viewer) {
|
||||
return viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x() ? getLocation(viewer).add(0,-2,0) : getLocation(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
// 1.8.0 will not see entity that respawned in the same tick
|
||||
// creating new delayed task every time someone sneaks can be abused and cause OOM
|
||||
spawn(viewer);
|
||||
}
|
||||
}
|
||||
+103
-139
@@ -3,24 +3,32 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The core class for unlimited NameTag mode on Bukkit
|
||||
*/
|
||||
public class BukkitNameTagX extends NameTagX {
|
||||
public class BukkitNameTagX extends BackendNameTagX {
|
||||
|
||||
/** Reference to NMS storage for quick access */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
/** Bukkit event listener */
|
||||
private final EventListener eventListener = new EventListener(this);
|
||||
|
||||
/** Vehicle manager reference */
|
||||
private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter, loads config options, registers events
|
||||
* and registers sub-features.
|
||||
@@ -29,160 +37,116 @@ public class BukkitNameTagX extends NameTagX {
|
||||
* plugin instance
|
||||
*/
|
||||
public BukkitNameTagX(JavaPlugin plugin) {
|
||||
super(BukkitArmorStandManager::new);
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, all);
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
if (sender.getVersion().getMinorVersion() == 8 && nms.PacketPlayInUseEntity.isInstance(packet)) {
|
||||
int entityId = nms.PacketPlayInUseEntity_ENTITY.getInt(packet);
|
||||
TabPlayer attacked = null;
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (all.isLoaded() && getArmorStandManager(all).hasArmorStandWithID(entityId)) {
|
||||
attacked = all;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked != sender) {
|
||||
nms.setField(packet, nms.PacketPlayInUseEntity_ENTITY, getEntityId(attacked));
|
||||
}
|
||||
}
|
||||
startVisibilityRefreshTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts task checking for player visibility to hide armor stands of invisible players.
|
||||
*/
|
||||
private void startVisibilityRefreshTask() {
|
||||
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
|
||||
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(p)) continue;
|
||||
getArmorStandManager(p).updateVisibility(false);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
HandlerList.unregisterAll(eventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
super.onJoin(connectedPlayer);
|
||||
if (isPlayerDisabled(connectedPlayer)) return;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, connectedPlayer);
|
||||
spawnArmorStands(connectedPlayer, viewer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnBoat(TabPlayer player) {
|
||||
return vehicleManager != null && vehicleManager.isOnBoat(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns armor stands of target player to viewer if all requirements are met.
|
||||
* These include players being in the same world, distance being less than 48 blocks
|
||||
* and target player being visible to viewer.
|
||||
*
|
||||
* @param viewer
|
||||
* Player viewing armor stands
|
||||
* @param target
|
||||
* Target player with armor stands
|
||||
*/
|
||||
private void spawnArmorStands(TabPlayer viewer, TabPlayer target) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
if (target == viewer || isPlayerDisabled(target)) return;
|
||||
if (((Player) viewer.getPlayer()).getWorld() != ((Player) target.getPlayer()).getWorld()) return;
|
||||
if (getDistance(viewer, target) <= 48 && ((Player)viewer.getPlayer()).canSee((Player)target.getPlayer()) && !target.isVanished())
|
||||
getArmorStandManager(target).spawn(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNameTagPreview(TabPlayer player, boolean status) {
|
||||
if (status) {
|
||||
getArmorStandManager(player).spawn(player);
|
||||
} else {
|
||||
getArmorStandManager(player).destroy(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeArmorStands(TabPlayer player) {
|
||||
if (isPlayerDisabled(player)) return;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseArmorStands(TabPlayer player) {
|
||||
getArmorStandManager(player).destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNameTagVisibilityView(TabPlayer player) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).updateVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).unregisterPlayer(disconnectedPlayer);
|
||||
}
|
||||
armorStandManagerMap.get(disconnectedPlayer).destroy();
|
||||
armorStandManagerMap.remove(disconnectedPlayer); // WeakHashMap doesn't clear this due to value referencing the key
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
super.onWorldChange(p, from, to);
|
||||
if (isUnlimitedDisabled(p.getServer(), to)) {
|
||||
getDisabledUnlimitedPlayers().add(p);
|
||||
updateTeamData(p);
|
||||
} else if (getDisabledUnlimitedPlayers().remove(p)) {
|
||||
updateTeamData(p);
|
||||
}
|
||||
if (isPreviewingNametag(p)) {
|
||||
getArmorStandManager(p).spawn(p);
|
||||
}
|
||||
//for some reason this is needed for some users
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getWorld().equals(from)) {
|
||||
getArmorStandManager(p).destroy(viewer);
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return;
|
||||
if (!receiver.isLoaded() || isDisabledPlayer(receiver) || getDisabledUnlimitedPlayers().contains(receiver)) return;
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
|
||||
packetListener.onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityTeleport.isInstance(packet)) {
|
||||
packetListener.onEntityMove(receiver, nms.PacketPlayOutEntityTeleport_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
|
||||
packetListener.onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityDestroy.isInstance(packet)) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
Object entities = nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet);
|
||||
if (entities instanceof List) {
|
||||
packetListener.onEntityDestroy(receiver, (List<Integer>) entities);
|
||||
} else {
|
||||
//1.17.0
|
||||
packetListener.onEntityDestroy(receiver, (int) entities);
|
||||
}
|
||||
} else {
|
||||
packetListener.onEntityDestroy(receiver, (int[]) nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns flat distance between two players ignoring Y value
|
||||
*
|
||||
* @param player1
|
||||
* first player
|
||||
* @param player2
|
||||
* second player
|
||||
* @return flat distance in blocks
|
||||
*/
|
||||
private double getDistance(TabPlayer player1, TabPlayer player2) {
|
||||
@Override
|
||||
public double getDistance(TabPlayer player1, TabPlayer player2) {
|
||||
Location loc1 = ((Player) player1.getPlayer()).getLocation();
|
||||
Location loc2 = ((Player) player2.getPlayer()).getLocation();
|
||||
return Math.sqrt(Math.pow(loc1.getX()-loc2.getX(), 2) + Math.pow(loc1.getZ()-loc2.getZ(), 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitArmorStandManager getArmorStandManager(TabPlayer player) {
|
||||
return (BukkitArmorStandManager) armorStandManagerMap.get(player);
|
||||
public boolean areInSameWorld(TabPlayer player1, TabPlayer player2) {
|
||||
return ((Player) player1.getPlayer()).getWorld() == ((Player) player1.getPlayer()).getWorld();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #vehicleManager}
|
||||
* @return {@link #vehicleManager}
|
||||
*/
|
||||
public VehicleRefresher getVehicleManager() {
|
||||
return vehicleManager;
|
||||
@Override
|
||||
public boolean canSee(TabPlayer viewer, TabPlayer target) {
|
||||
return ((Player)viewer.getPlayer()).canSee((Player)target.getPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener() {
|
||||
HandlerList.unregisterAll(eventListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public List<Integer> getPassengers(Object entity) {
|
||||
Entity vehicle = (Entity) entity;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 11) {
|
||||
return vehicle.getPassengers().stream().map(Entity::getEntityId).collect(Collectors.toList());
|
||||
} else {
|
||||
if (vehicle.getPassenger() != null) {
|
||||
return Collections.singletonList(vehicle.getPassenger().getEntityId());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerVehiclePlaceholder() {
|
||||
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> {
|
||||
Entity v = ((Player)p.getPlayer()).getVehicle();
|
||||
//There's a bug in Bukkit 1.19.3 throwing NPE on .toString(), use default toString implementation
|
||||
return v == null ? "" : v.getClass().getName() + "@" + Integer.toHexString(v.hashCode());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVehicle(TabPlayer player) {
|
||||
return ((Player)player.getPlayer()).getVehicle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId(Object entity) {
|
||||
return ((Entity)entity).getEntityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntityType(Object entity) {
|
||||
return ((Entity)entity).getType().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendArmorStand createArmorStand(BackendArmorStandManager feature, TabPlayer owner, String lineName, double yOffset, boolean staticOffset) {
|
||||
return new BukkitArmorStand(this, feature, owner, lineName, yOffset, staticOffset);
|
||||
}
|
||||
}
|
||||
+2
-10
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -13,21 +14,12 @@ import org.bukkit.event.player.PlayerToggleSneakEvent;
|
||||
* Class listening to Bukkit events which the feature requires and are
|
||||
* sufficient, without packets being requires.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class EventListener implements Listener {
|
||||
|
||||
/** Reference to the main feature */
|
||||
private final BukkitNameTagX feature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param feature
|
||||
* NameTag feature handler
|
||||
*/
|
||||
public EventListener(BukkitNameTagX feature) {
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sneak event listener to de-spawn and spawn armor stands to skip animation
|
||||
*
|
||||
|
||||
+2
-15
@@ -1,12 +1,12 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.Data;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityDestroy minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
|
||||
/** Destroyed entities */
|
||||
@@ -21,17 +21,4 @@ public class PacketPlayOutEntityDestroy implements TabPacket {
|
||||
public PacketPlayOutEntityDestroy(int... entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutEntityDestroy{entities=%s}", Arrays.toString(entities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entities}
|
||||
* @return {@link #entities}
|
||||
*/
|
||||
public int[] getEntities() {
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
+4
-37
@@ -1,52 +1,19 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityMetadata minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's data that changed */
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param entityId
|
||||
* Entity's id
|
||||
* @param dataWatcher
|
||||
* Entity's data that changed
|
||||
*/
|
||||
public PacketPlayOutEntityMetadata(int entityId, DataWatcher dataWatcher) {
|
||||
Preconditions.checkNotNull(dataWatcher, "data watcher");
|
||||
this.entityId = entityId;
|
||||
this.dataWatcher = dataWatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutEntityMetadata{entityId=%s,dataWatcher=%s}", entityId, dataWatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityId}
|
||||
* @return {@link #entityId}
|
||||
*/
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #dataWatcher}
|
||||
* @return {@link #dataWatcher}
|
||||
*/
|
||||
public DataWatcher getDataWatcher() {
|
||||
return dataWatcher;
|
||||
}
|
||||
@NonNull private final DataWatcher dataWatcher;
|
||||
}
|
||||
+5
-39
@@ -1,53 +1,19 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import org.bukkit.Location;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityTeleport minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's new location */
|
||||
private final Location location;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param entityId
|
||||
* Entity's id
|
||||
* @param location
|
||||
* Entity's spawn location
|
||||
*/
|
||||
public PacketPlayOutEntityTeleport(int entityId, Location location) {
|
||||
Preconditions.checkNotNull(location, "location");
|
||||
this.entityId = entityId;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutEntityTeleport{entityId=%s,location=%s}", entityId, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityId}
|
||||
* @return {@link #entityId}
|
||||
*/
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #location}
|
||||
* @return {@link #location}
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
@NonNull private final Location location;
|
||||
}
|
||||
+9
-79
@@ -1,102 +1,32 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutSpawnEntityLiving minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
/** Entity's UUID (for 1.9+ servers) */
|
||||
private final UUID uuid;
|
||||
@NonNull private final UUID uniqueId;
|
||||
|
||||
/** Type of entity */
|
||||
private final EntityType entityType;
|
||||
@NonNull private final EntityType entityType;
|
||||
|
||||
/** Entity's spawn location */
|
||||
private final Location location;
|
||||
@NonNull private final Location location;
|
||||
|
||||
/** Entity's data for 1.14- servers */
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param entityId
|
||||
* Entity's id
|
||||
* @param uuid
|
||||
* Entity's UUID (for 1.9+ servers)
|
||||
* @param entityType
|
||||
* Type of entity
|
||||
* @param location
|
||||
* Entity's spawn location
|
||||
* @param dataWatcher
|
||||
* Entity's data for 1.14- servers
|
||||
*/
|
||||
public PacketPlayOutSpawnEntityLiving(int entityId, UUID uuid, EntityType entityType, Location location, DataWatcher dataWatcher) {
|
||||
Preconditions.checkNotNull(uuid, "uuid");
|
||||
Preconditions.checkNotNull(entityType, "entityType");
|
||||
Preconditions.checkNotNull(location, "location");
|
||||
this.entityId = entityId;
|
||||
this.uuid = uuid;
|
||||
this.entityType = entityType;
|
||||
this.location = location;
|
||||
this.dataWatcher = dataWatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutSpawnEntityLiving{entityId=%s,uuid=%s,entityType=%s,location=%s,dataWatcher=%s}",
|
||||
entityId, uuid, entityType, location, dataWatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityId}
|
||||
* @return {@link #entityId}
|
||||
*/
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #uuid}
|
||||
* @return {@link #uuid}
|
||||
*/
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entityType}
|
||||
* @return {@link #entityType}
|
||||
*/
|
||||
public EntityType getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #location}
|
||||
* @return {@link #location}
|
||||
*/
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #dataWatcher}
|
||||
* @return {@link #dataWatcher}
|
||||
*/
|
||||
public DataWatcher getDataWatcher() {
|
||||
return dataWatcher;
|
||||
}
|
||||
}
|
||||
+9
-23
@@ -1,22 +1,25 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcher class to make work with it much easier
|
||||
*/
|
||||
@ToString
|
||||
public class DataWatcher {
|
||||
|
||||
/** Watched data */
|
||||
private final Map<Integer, DataWatcherItem> dataValues = new HashMap<>();
|
||||
|
||||
/** Helper for easier data write */
|
||||
private final DataWatcherHelper helper = new DataWatcherHelper(this);
|
||||
@Getter private final DataWatcherHelper helper = new DataWatcherHelper(this);
|
||||
|
||||
/**
|
||||
* Sets value into data values
|
||||
@@ -26,9 +29,7 @@ public class DataWatcher {
|
||||
* @param value
|
||||
* value
|
||||
*/
|
||||
public void setValue(DataWatcherObject type, Object value){
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
public void setValue(@NonNull DataWatcherObject type, @NonNull Object value){
|
||||
dataValues.put(type.getPosition(), new DataWatcherItem(type, value));
|
||||
}
|
||||
|
||||
@@ -53,15 +54,6 @@ public class DataWatcher {
|
||||
return dataValues.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns helper created by this instance
|
||||
*
|
||||
* @return data write helper
|
||||
*/
|
||||
public DataWatcherHelper helper() {
|
||||
return helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the class into an instance of NMS.DataWatcher
|
||||
*
|
||||
@@ -100,8 +92,7 @@ public class DataWatcher {
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static DataWatcher fromNMS(Object nmsWatcher) throws ReflectiveOperationException {
|
||||
Preconditions.checkNotNull(nmsWatcher, "nmsWatcher");
|
||||
public static DataWatcher fromNMS(@NonNull Object nmsWatcher) throws ReflectiveOperationException {
|
||||
DataWatcher watcher = new DataWatcher();
|
||||
List<Object> items = (List<Object>) nmsWatcher.getClass().getMethod("c").invoke(nmsWatcher);
|
||||
if (items != null) {
|
||||
@@ -112,9 +103,4 @@ public class DataWatcher {
|
||||
}
|
||||
return watcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataWatcher{values=%s}", dataValues);
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -65,7 +65,7 @@ public class DataWatcherHelper {
|
||||
* flags to write
|
||||
*/
|
||||
public void setEntityFlags(byte flags) {
|
||||
data.setValue(new DataWatcherObject(0, registry.getByte()), flags);
|
||||
data.setValue(new DataWatcherObject(0, registry.getTypeByte()), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,12 +79,12 @@ public class DataWatcherHelper {
|
||||
public void setCustomName(String customName, ProtocolVersion clientVersion) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) {
|
||||
try {
|
||||
data.setValue(new DataWatcherObject(2, registry.getOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
|
||||
data.setValue(new DataWatcherObject(2, registry.getTypeOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to create component", e);
|
||||
}
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8){
|
||||
data.setValue(new DataWatcherObject(2, registry.getString()), customName);
|
||||
data.setValue(new DataWatcherObject(2, registry.getTypeString()), customName);
|
||||
} else {
|
||||
//name length is limited to 64 characters on <1.8
|
||||
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
|
||||
@@ -104,7 +104,7 @@ public class DataWatcherHelper {
|
||||
*/
|
||||
public void setCustomNameVisible(boolean visible) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
data.setValue(new DataWatcherObject(3, registry.getBoolean()), visible);
|
||||
data.setValue(new DataWatcherObject(3, registry.getTypeBoolean()), visible);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(3, null), (byte)(visible?1:0));
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class DataWatcherHelper {
|
||||
*/
|
||||
public void setHealth(float health) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 6) {
|
||||
data.setValue(new DataWatcherObject(6, registry.getFloat()), health);
|
||||
data.setValue(new DataWatcherObject(6, registry.getTypeFloat()), health);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(16, null), (int)health);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class DataWatcherHelper {
|
||||
* flags to write
|
||||
*/
|
||||
public void setArmorStandFlags(byte flags) {
|
||||
data.setValue(new DataWatcherObject(armorStandFlagsPosition, registry.getByte()), flags);
|
||||
data.setValue(new DataWatcherObject(armorStandFlagsPosition, registry.getTypeByte()), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-38
@@ -1,12 +1,13 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.Data;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
/**
|
||||
* Class representing NMS Data Watcher Item
|
||||
*/
|
||||
@Data
|
||||
public class DataWatcherItem {
|
||||
|
||||
/** Value type */
|
||||
@@ -14,22 +15,7 @@ public class DataWatcherItem {
|
||||
|
||||
/** Data value */
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* Constructs new instance of the object with given parameters
|
||||
*
|
||||
* @param type
|
||||
* value type
|
||||
* @param value
|
||||
* value
|
||||
*/
|
||||
public DataWatcherItem(DataWatcherObject type, Object value){
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns and instance of this class from given NMS item
|
||||
*
|
||||
@@ -51,25 +37,4 @@ public class DataWatcherItem {
|
||||
}
|
||||
return new DataWatcherItem(object, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #type}
|
||||
* @return {@link #type}
|
||||
*/
|
||||
public DataWatcherObject getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #value}
|
||||
* @return {@link #value}
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataWatcherItem{type=%s,value=%s}", type, value);
|
||||
}
|
||||
}
|
||||
+4
-34
@@ -1,8 +1,12 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcherObject class to make work with it much easier
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class DataWatcherObject {
|
||||
|
||||
/** Object's position in DataWatcher */
|
||||
@@ -10,38 +14,4 @@ public class DataWatcherObject {
|
||||
|
||||
/** Value encoder for 1.9+ */
|
||||
private final Object serializer;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class with given parameters
|
||||
*
|
||||
* @param position
|
||||
* position in DataWatcher
|
||||
* @param serializer
|
||||
* Serializer for value in this slot
|
||||
*/
|
||||
public DataWatcherObject(int position, Object serializer){
|
||||
this.position = position;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #position}
|
||||
* @return {@link #position}
|
||||
*/
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #serializer}
|
||||
* @return {@link #serializer}
|
||||
*/
|
||||
public Object getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("DataWatcherObject{position=%d,serializer=%s}", position, serializer);
|
||||
}
|
||||
}
|
||||
+19
-58
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -10,19 +11,19 @@ import java.util.Map;
|
||||
public class DataWatcherRegistry {
|
||||
|
||||
/** Byte encoder */
|
||||
private Object registryByte;
|
||||
@Getter private Object typeByte;
|
||||
|
||||
/** Float encoder */
|
||||
private Object registryFloat;
|
||||
@Getter private Object typeFloat;
|
||||
|
||||
/** String encoder */
|
||||
private Object registryString;
|
||||
@Getter private Object typeString;
|
||||
|
||||
/** Encoder for optional component */
|
||||
private Object registryOptionalIChatBaseComponent;
|
||||
@Getter private Object typeOptionalComponent;
|
||||
|
||||
/** Boolean encoder */
|
||||
private Object registryBoolean;
|
||||
@Getter private Object typeBoolean;
|
||||
|
||||
/**
|
||||
* Initializes required NMS classes and fields
|
||||
@@ -32,68 +33,28 @@ public class DataWatcherRegistry {
|
||||
Map<String, Object> fields = nms.getStaticFields(nms.DataWatcherRegistry);
|
||||
if (fields.containsKey("a")) {
|
||||
// Bukkit mapping
|
||||
registryByte = fields.get("a");
|
||||
registryFloat = fields.get("c");
|
||||
registryString = fields.get("d");
|
||||
typeByte = fields.get("a");
|
||||
typeFloat = fields.get("c");
|
||||
typeString = fields.get("d");
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
if (nms.is1_19_3Plus()) {
|
||||
registryOptionalIChatBaseComponent = fields.get("g");
|
||||
registryBoolean = fields.get("j");
|
||||
typeOptionalComponent = fields.get("g");
|
||||
typeBoolean = fields.get("j");
|
||||
} else {
|
||||
registryOptionalIChatBaseComponent = fields.get("f");
|
||||
registryBoolean = fields.get("i");
|
||||
typeOptionalComponent = fields.get("f");
|
||||
typeBoolean = fields.get("i");
|
||||
}
|
||||
} else {
|
||||
registryBoolean = fields.get("h");
|
||||
typeBoolean = fields.get("h");
|
||||
}
|
||||
} else {
|
||||
// Mojang mapping
|
||||
registryByte = fields.get("BYTE");
|
||||
registryFloat = fields.get("FLOAT");
|
||||
registryString = fields.get("STRING");
|
||||
registryOptionalIChatBaseComponent = fields.get("OPTIONAL_COMPONENT");
|
||||
registryBoolean = fields.get("BOOLEAN");
|
||||
typeByte = fields.get("BYTE");
|
||||
typeFloat = fields.get("FLOAT");
|
||||
typeString = fields.get("STRING");
|
||||
typeOptionalComponent = fields.get("OPTIONAL_COMPONENT");
|
||||
typeBoolean = fields.get("BOOLEAN");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryByte}
|
||||
* @return {@link #registryByte}
|
||||
*/
|
||||
public Object getByte() {
|
||||
return registryByte;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryFloat}
|
||||
* @return {@link #registryFloat}
|
||||
*/
|
||||
public Object getFloat() {
|
||||
return registryFloat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryString}
|
||||
* @return {@link #registryString}
|
||||
*/
|
||||
public Object getString() {
|
||||
return registryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryOptionalIChatBaseComponent}
|
||||
* @return {@link #registryOptionalIChatBaseComponent}
|
||||
*/
|
||||
public Object getOptionalComponent() {
|
||||
return registryOptionalIChatBaseComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #registryBoolean}
|
||||
* @return {@link #registryBoolean}
|
||||
*/
|
||||
public Object getBoolean() {
|
||||
return registryBoolean;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ package me.neznamy.tab.platforms.bukkit.nms.storage;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
@@ -16,16 +19,16 @@ import java.util.*;
|
||||
public abstract class NMSStorage {
|
||||
|
||||
/** Instance of this class */
|
||||
private static NMSStorage instance;
|
||||
@Getter @Setter private static NMSStorage instance;
|
||||
|
||||
/** Server's NMS/CraftBukkit package */
|
||||
protected final String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
|
||||
/** Server's minor version */
|
||||
protected final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
@Getter protected final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
|
||||
/** Flag determining whether the server version is at least 1.19.3 or not */
|
||||
private boolean is1_19_3Plus;
|
||||
@Getter private final boolean is1_19_3Plus = ReflectionUtils.classExists("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
|
||||
/** Basic universal values */
|
||||
protected Class<?> Packet;
|
||||
@@ -42,8 +45,6 @@ public abstract class NMSStorage {
|
||||
public Field PLAYER_CONNECTION;
|
||||
public Field NETWORK_MANAGER;
|
||||
public Field CHANNEL;
|
||||
public Field EntityHuman_ProfilePublicKey;
|
||||
public Field EntityPlayer_RemoteChatSession;
|
||||
public Method getHandle;
|
||||
public Method sendPacket;
|
||||
public Method getProfile;
|
||||
@@ -71,7 +72,7 @@ public abstract class NMSStorage {
|
||||
public Field DataWatcherObject_SLOT;
|
||||
public Field DataWatcherObject_SERIALIZER;
|
||||
public Method DataWatcher_REGISTER;
|
||||
private final DataWatcherRegistry registry;
|
||||
@Getter private final DataWatcherRegistry dataWatcherRegistry;
|
||||
//1.19.3+
|
||||
protected Class<?> DataWatcher$DataValue;
|
||||
public Field DataWatcher$DataValue_POSITION;
|
||||
@@ -150,17 +151,13 @@ public abstract class NMSStorage {
|
||||
public Method PlayerInfoData_getGamemode;
|
||||
public Method PlayerInfoData_getDisplayName;
|
||||
public Method PlayerInfoData_getProfilePublicKeyRecord;
|
||||
public Method ProfilePublicKey_getRecord;
|
||||
//1.19.3+
|
||||
public Class<?> ClientboundPlayerInfoRemovePacket;
|
||||
public Class<?> RemoteChatSession;
|
||||
public Class<?> RemoteChatSession$Data;
|
||||
public Constructor<?> newClientboundPlayerInfoRemovePacket;
|
||||
public Constructor<?> newRemoteChatSession;
|
||||
public Constructor<?> newRemoteChatSession$Data;
|
||||
public Method ClientboundPlayerInfoRemovePacket_getEntries;
|
||||
public Method RemoteChatSession_getSessionId;
|
||||
public Method RemoteChatSession_getProfilePublicKey;
|
||||
public Method RemoteChatSession$Data_getSessionId;
|
||||
public Method RemoteChatSession$Data_getProfilePublicKey;
|
||||
|
||||
@@ -226,10 +223,6 @@ public abstract class NMSStorage {
|
||||
* If some field, constructor or method was not found
|
||||
*/
|
||||
public NMSStorage() throws ReflectiveOperationException {
|
||||
try {
|
||||
Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
is1_19_3Plus = true;
|
||||
} catch (ClassNotFoundException | NullPointerException ignored) {}
|
||||
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
|
||||
loadClasses();
|
||||
if (minorVersion >= 7) {
|
||||
@@ -245,7 +238,7 @@ public abstract class NMSStorage {
|
||||
PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
|
||||
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
|
||||
sendPacket = getMethods(PlayerConnection, void.class, Packet).get(0);
|
||||
registry = new DataWatcherRegistry(this);
|
||||
dataWatcherRegistry = new DataWatcherRegistry(this);
|
||||
dataWatcher();
|
||||
entityTeleport();
|
||||
spawnEntityLiving();
|
||||
@@ -258,25 +251,6 @@ public abstract class NMSStorage {
|
||||
loadNamedFieldsAndMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new instance
|
||||
*
|
||||
* @param instance
|
||||
* new instance
|
||||
*/
|
||||
public static void setInstance(NMSStorage instance) {
|
||||
NMSStorage.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance
|
||||
*
|
||||
* @return instance
|
||||
*/
|
||||
public static NMSStorage getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads PacketPlayOutChat's methods, fields and constructors
|
||||
*
|
||||
@@ -319,14 +293,12 @@ public abstract class NMSStorage {
|
||||
RemoteChatSession$Data_getSessionId = getMethods(RemoteChatSession$Data, UUID.class).get(0);
|
||||
RemoteChatSession$Data_getProfilePublicKey = getMethods(RemoteChatSession$Data, ProfilePublicKey$a).get(0);
|
||||
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, RemoteChatSession$Data).get(0);
|
||||
EntityPlayer_RemoteChatSession = getFields(EntityPlayer, RemoteChatSession).get(0);
|
||||
PlayerInfoData_isListed = getMethods(PlayerInfoData, boolean.class).get(0);
|
||||
} else {
|
||||
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
|
||||
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
|
||||
if (minorVersion >= 19) {
|
||||
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
|
||||
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
|
||||
}
|
||||
}
|
||||
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
|
||||
@@ -338,9 +310,6 @@ public abstract class NMSStorage {
|
||||
}
|
||||
PlayerInfoData_getGamemode = getMethods(PlayerInfoData, EnumGamemode).get(0);
|
||||
PlayerInfoData_getDisplayName = getMethods(PlayerInfoData, IChatBaseComponent).get(0);
|
||||
if (minorVersion >= 19) {
|
||||
ProfilePublicKey_getRecord = getMethods(ProfilePublicKey, ProfilePublicKey$a).get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,14 +655,6 @@ public abstract class NMSStorage {
|
||||
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server's minor version
|
||||
* @return server's minor version
|
||||
*/
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value of a field in object
|
||||
*
|
||||
@@ -710,14 +671,6 @@ public abstract class NMSStorage {
|
||||
field.set(obj, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data watcher registry
|
||||
* @return data watcher registry
|
||||
*/
|
||||
public DataWatcherRegistry getDataWatcherRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes object accessible and returns it for chaining.
|
||||
*
|
||||
@@ -751,8 +704,4 @@ public abstract class NMSStorage {
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public boolean is1_19_3Plus() {
|
||||
return is1_19_3Plus;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,5 @@ dependencies {
|
||||
implementation(projects.shared)
|
||||
implementation(libs.bstats.bungeecord)
|
||||
compileOnly(libs.bungeecord)
|
||||
compileOnly(libs.premiumVanish)
|
||||
compileOnly(libs.redisBungee)
|
||||
}
|
||||
+2
-10
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
@@ -13,21 +14,12 @@ import net.md_5.bungee.event.EventHandler;
|
||||
/**
|
||||
* The core for BungeeCord forwarding events into all enabled features
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BungeeEventListener implements Listener {
|
||||
|
||||
/** Reference to BungeeCord platform implementation */
|
||||
private final BungeePlatform platform;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param platform
|
||||
* BungeeCord implementation of platform
|
||||
*/
|
||||
public BungeeEventListener(BungeePlatform platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect event listener to forward the event to all features
|
||||
*
|
||||
|
||||
+53
-80
@@ -44,36 +44,11 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) {
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
|
||||
PlayerListItemRemove remove = new PlayerListItemRemove();
|
||||
remove.setUuids(packet.getEntries().stream().map(PlayerInfoData::getUniqueId).toArray(UUID[]::new));
|
||||
return remove;
|
||||
}
|
||||
List<Item> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
Item item = new Item();
|
||||
if (data.getDisplayName() != null) item.setDisplayName(data.getDisplayName().toString(clientVersion));
|
||||
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
|
||||
item.setListed(data.isListed());
|
||||
item.setPing(data.getLatency());
|
||||
if (data.getSkin() != null) {
|
||||
item.setProperties(new Property[]{new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature())});
|
||||
} else {
|
||||
item.setProperties(new Property[0]);
|
||||
}
|
||||
item.setUsername(data.getName());
|
||||
item.setUuid(data.getUniqueId());
|
||||
item.setChatSessionId(data.getChatSessionId());
|
||||
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
|
||||
items.add(item);
|
||||
}
|
||||
PlayerListItemUpdate bungeePacket = new PlayerListItemUpdate();
|
||||
PlayerListItemUpdate.Action[] array = packet.getActions().stream().map(action -> PlayerListItemUpdate.Action.valueOf(
|
||||
action.toString().replace("GAME_MODE", "GAMEMODE"))).toArray(PlayerListItemUpdate.Action[]::new);
|
||||
bungeePacket.setActions(EnumSet.of(array[0], array));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId() &&
|
||||
packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
|
||||
PlayerListItemRemove remove = new PlayerListItemRemove();
|
||||
remove.setUuids(packet.getEntries().stream().map(PlayerInfoData::getUniqueId).toArray(UUID[]::new));
|
||||
return remove;
|
||||
}
|
||||
List<Item> items = new ArrayList<>();
|
||||
for (PlayerInfoData data : packet.getEntries()) {
|
||||
@@ -87,7 +62,8 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
} else if (clientVersion.getMinorVersion() < 8) {
|
||||
item.setDisplayName(String.valueOf(data.getName())); //avoiding NPE, 1.7 client requires this, 1.8 added a leading boolean
|
||||
}
|
||||
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
|
||||
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal() - 1);
|
||||
item.setListed(data.isListed());
|
||||
item.setPing(data.getLatency());
|
||||
if (data.getSkin() != null) {
|
||||
item.setProperties(new Property[]{new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature())});
|
||||
@@ -96,15 +72,25 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
}
|
||||
item.setUsername(data.getName());
|
||||
item.setUuid(data.getUniqueId());
|
||||
item.setChatSessionId(data.getChatSessionId());
|
||||
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
|
||||
items.add(item);
|
||||
}
|
||||
PlayerListItem bungeePacket = new PlayerListItem();
|
||||
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
|
||||
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
|
||||
bungeePacket.setAction(PlayerListItem.Action.valueOf(action.toString().replace("GAME_MODE", "GAMEMODE")));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
PlayerListItemUpdate bungeePacket = new PlayerListItemUpdate();
|
||||
PlayerListItemUpdate.Action[] array = packet.getActions().stream().map(action -> PlayerListItemUpdate.Action.valueOf(
|
||||
action.toString().replace("GAME_MODE", "GAMEMODE"))).toArray(PlayerListItemUpdate.Action[]::new);
|
||||
bungeePacket.setActions(EnumSet.of(array[0], array));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
} else {
|
||||
PlayerListItem bungeePacket = new PlayerListItem();
|
||||
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
|
||||
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
|
||||
bungeePacket.setAction(PlayerListItem.Action.valueOf(action.toString().replace("GAME_MODE", "GAMEMODE")));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,50 +125,37 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
|
||||
@Override
|
||||
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion) {
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
if (bungeePacket instanceof PlayerListItemRemove) {
|
||||
return new PacketPlayOutPlayerInfo(
|
||||
EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
Arrays.stream(((PlayerListItemRemove) bungeePacket).getUuids()).map(PlayerInfoData::new).collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
PlayerListItemUpdate item = (PlayerListItemUpdate) bungeePacket;
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : item.getItems()) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
|
||||
listData.add(new PlayerInfoData(
|
||||
i.getUsername(),
|
||||
i.getUuid(),
|
||||
skin,
|
||||
Boolean.TRUE.equals(i.getListed()),
|
||||
i.getPing() == null ? 0 : i.getPing(),
|
||||
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
|
||||
IChatBaseComponent.deserialize(i.getDisplayName()),
|
||||
i.getChatSessionId(),
|
||||
i.getPublicKey()));
|
||||
}
|
||||
EnumPlayerInfoAction[] array = item.getActions().stream().map(action ->
|
||||
EnumPlayerInfoAction.valueOf(action.toString().replace("GAMEMODE", "GAME_MODE"))).toArray(EnumPlayerInfoAction[]::new);
|
||||
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
|
||||
return new PacketPlayOutPlayerInfo(actions, listData);
|
||||
} else {
|
||||
PlayerListItem item = (PlayerListItem) bungeePacket;
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : item.getItems()) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
|
||||
listData.add(new PlayerInfoData(
|
||||
i.getUsername(),
|
||||
i.getUuid(),
|
||||
skin,
|
||||
true,
|
||||
i.getPing() == null ? 0 : i.getPing(),
|
||||
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
|
||||
IChatBaseComponent.deserialize(i.getDisplayName()),
|
||||
null,
|
||||
i.getPublicKey()));
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
|
||||
if (bungeePacket instanceof PlayerListItemRemove) {
|
||||
return new PacketPlayOutPlayerInfo(
|
||||
EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
Arrays.stream(((PlayerListItemRemove) bungeePacket).getUuids()).map(PlayerInfoData::new).collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
Item[] items = bungeePacket instanceof PlayerListItemUpdate ? ((PlayerListItemUpdate) bungeePacket).getItems() : ((PlayerListItem) bungeePacket).getItems();
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : items) {
|
||||
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
|
||||
listData.add(new PlayerInfoData(
|
||||
i.getUsername(),
|
||||
i.getUuid(),
|
||||
skin,
|
||||
Boolean.TRUE.equals(i.getListed()),
|
||||
i.getPing() == null ? 0 : i.getPing(),
|
||||
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
|
||||
IChatBaseComponent.deserialize(i.getDisplayName()),
|
||||
i.getChatSessionId(),
|
||||
i.getPublicKey()));
|
||||
}
|
||||
if (bungeePacket instanceof PlayerListItemUpdate) {
|
||||
EnumPlayerInfoAction[] array = ((PlayerListItemUpdate) bungeePacket).getActions().stream().map(this::toCustomAction).toArray(EnumPlayerInfoAction[]::new);
|
||||
return new PacketPlayOutPlayerInfo(EnumSet.of(array[0], array), listData);
|
||||
} else {
|
||||
return new PacketPlayOutPlayerInfo(toCustomAction(((PlayerListItem) bungeePacket).getAction()), listData);
|
||||
}
|
||||
}
|
||||
|
||||
private EnumPlayerInfoAction toCustomAction(Object action) {
|
||||
return EnumPlayerInfoAction.valueOf(action.toString().replace("GAMEMODE", "GAME_MODE"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-12
@@ -5,6 +5,7 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
@@ -49,21 +50,12 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BungeeChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
/** Injected player */
|
||||
protected final TabPlayer player;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player
|
||||
*
|
||||
* @param player
|
||||
* player to inject
|
||||
*/
|
||||
public BungeeChannelDuplexHandler(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
|
||||
try {
|
||||
@@ -124,9 +116,9 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) {
|
||||
for (RedisPlayer p : redis.getRedisPlayers().values()) {
|
||||
if (col.contains(p.getNickName()) && !packet.getName().equals(p.getTeamName())) {
|
||||
if (col.contains(p.getNickname()) && !packet.getName().equals(p.getTeamName())) {
|
||||
logTeamOverride(packet.getName(), p.getName(), p.getTeamName());
|
||||
col.remove(p.getNickName());
|
||||
col.remove(p.getNickname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import me.neznamy.tab.shared.proxy.ProxyPlatform;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* BungeeCord implementation of Platform
|
||||
@@ -21,22 +24,20 @@ public class BungeePlatform extends ProxyPlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFeatures() {
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().isPipelineInjection())
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BungeePipelineInjector());
|
||||
tab.getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((ProxiedPlayer) p.getPlayer()).getDisplayName());
|
||||
super.loadFeatures();
|
||||
public @Nullable PipelineInjector getPipelineInjector() {
|
||||
return new BungeePipelineInjector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RedisSupport getRedisSupport() {
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.REDIS_BUNGEE) != null) {
|
||||
if (RedisBungeeAPI.getRedisBungeeApi() != null) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, new RedisBungeeSupport());
|
||||
return new RedisBungeeSupport();
|
||||
} else {
|
||||
TAB.getInstance().getErrorManager().criticalError("RedisBungee plugin was detected, but it returned null API instance. Disabling hook.", null);
|
||||
}
|
||||
}
|
||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||
tab.addPlayer(new BungeeTabPlayer(p));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,4 +45,11 @@ public class BungeePlatform extends ProxyPlatform {
|
||||
Plugin pl = ProxyServer.getInstance().getPluginManager().getPlugin(plugin);
|
||||
return pl == null ? null : pl.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlayers() {
|
||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||
TAB.getInstance().addPlayer(new BungeeTabPlayer(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
@@ -61,8 +60,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission0(String permission) {
|
||||
Preconditions.checkNotNull(permission, "permission");
|
||||
public boolean hasPermission0(@NonNull String permission) {
|
||||
long time = System.nanoTime();
|
||||
boolean value = getPlayer().hasPermission(permission);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
|
||||
@@ -102,8 +100,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
* packet class
|
||||
* @return packet ID
|
||||
*/
|
||||
public int getPacketId(Class<? extends DefinedPacket> clazz) {
|
||||
Preconditions.checkNotNull(clazz, "class");
|
||||
public int getPacketId(@NonNull Class<? extends DefinedPacket> clazz) {
|
||||
try {
|
||||
return (int) getId.invoke(directionData, clazz, getPlayer().getPendingConnection().getVersion());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -120,7 +117,8 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
@Override
|
||||
public boolean isVanished() {
|
||||
try {
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.PREMIUM_VANISH) != null && BungeeVanishAPI.isInvisible(getPlayer())) return true;
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.PREMIUM_VANISH) != null &&
|
||||
(boolean) Class.forName("de.myzelyam.api.vanish.BungeeVanishAPI").getMethod("isInvisible", ProxiedPlayer.class).invoke(null, getPlayer())) return true;
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("PremiumVanish v" + TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.PREMIUM_VANISH) +
|
||||
" generated an error when retrieving vanish status of " + getName(), e);
|
||||
@@ -145,12 +143,11 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
|
||||
@Override
|
||||
public UUID getChatSessionId() {
|
||||
throw new UnsupportedOperationException("Not implemented yet");
|
||||
return ((InitialHandler)getPlayer().getPendingConnection()).getLoginRequest().getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
if (getPlayer().getServer() == null) return;
|
||||
getPlayer().getServer().sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
|
||||
TAB.getInstance().getCPUManager().packetSent("Plugin Message (" + new String(message) + ")");
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
@@ -25,7 +26,7 @@ public class Main extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if (!isVersionSupported()) {
|
||||
if (!ReflectionUtils.classExists("net.md_5.bungee.protocol.packet.PlayerListItemUpdate")) {
|
||||
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1671 and up (or an equivalent fork) to work."));
|
||||
return;
|
||||
}
|
||||
@@ -40,20 +41,6 @@ public class Main extends Plugin {
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.GLOBAL_PLAYER_LIST_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for compatibility and returns {@code true} if version is supported, {@code false} if not
|
||||
*
|
||||
* @return {@code true} if version is compatible, {@code false} if not
|
||||
*/
|
||||
private boolean isVersionSupported() {
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.protocol.packet.PlayerListItemUpdate");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
|
||||
projectVersion=3.2.3-SNAPSHOT
|
||||
projectVersion=3.2.5
|
||||
|
||||
# Gradle properties
|
||||
org.gradle.daemon=true
|
||||
|
||||
@@ -21,8 +21,9 @@ event = "3.0.0"
|
||||
# Platforms
|
||||
bukkit = "1.16.5-R0.1-SNAPSHOT"
|
||||
bungeecord = "1.19-R0.1-SNAPSHOT"
|
||||
krypton = "0.59.2"
|
||||
krypton = "98fe09edf8" # Krypton uses commit-based versioning
|
||||
velocity = "3.1.2-SNAPSHOT"
|
||||
sponge = "8.1.0"
|
||||
|
||||
# Platform extras
|
||||
placeholderapi = "2.11.2"
|
||||
@@ -30,13 +31,11 @@ vault = "1.7"
|
||||
via = "4.5.1"
|
||||
authlib = "1.5.25"
|
||||
bstats = "3.0.0"
|
||||
essentials = "2.19.7"
|
||||
premiumVanish = "2.7.11-2"
|
||||
redisBungee = "0.10.1"
|
||||
|
||||
# Plugins
|
||||
indra = "2.1.1"
|
||||
shadow = "7.1.2"
|
||||
lombok = "6.6.1"
|
||||
|
||||
[libraries]
|
||||
|
||||
@@ -65,14 +64,13 @@ krypton-api = { group = "org.kryptonmc", name = "krypton-api", version.ref = "kr
|
||||
krypton-server = { group = "org.kryptonmc", name = "krypton-server", version.ref = "krypton" }
|
||||
krypton-annotationProcessor = { group = "org.kryptonmc", name = "krypton-annotation-processor", version.ref = "krypton" }
|
||||
velocity = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }
|
||||
sponge = { group = "org.spongepowered", name = "spongeapi", version.ref = "sponge" }
|
||||
|
||||
# Platform extras
|
||||
placeholderapi = { group = "me.clip", name = "placeholderapi", version.ref = "placeholderapi" }
|
||||
vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" }
|
||||
via = { group = "com.viaversion", name = "viaversion-api", version.ref = "via" }
|
||||
authlib = { group = "com.mojang", name = "authlib", version.ref = "authlib" }
|
||||
essentials = { group = "net.essentialsx", name = "EssentialsX", version.ref = "essentials" }
|
||||
premiumVanish = { group = "com.github.MyzelYam", name = "PremiumVanishAPI", version.ref = "premiumVanish" }
|
||||
redisBungee = { group = "com.github.limework.redisbungee", name = "RedisBungee-Bungee", version.ref = "redisBungee" }
|
||||
|
||||
# Metrics
|
||||
@@ -81,5 +79,5 @@ bstats-bungeecord = { group = "org.bstats", name = "bstats-bungeecord", version.
|
||||
bstats-velocity = { group = "org.bstats", name = "bstats-velocity", version.ref = "bstats" }
|
||||
|
||||
# Plugins
|
||||
plugin-indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
|
||||
plugin-shadow = { group = "gradle.plugin.com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
|
||||
plugin-lombok = { group = "io.freefair.gradle", name = "lombok-plugin", version.ref = "lombok"}
|
||||
@@ -7,8 +7,9 @@ plugins {
|
||||
val platforms = setOf(
|
||||
rootProject.projects.bukkit,
|
||||
rootProject.projects.bungeecord,
|
||||
// rootProject.projects.krypton,
|
||||
rootProject.projects.velocity
|
||||
rootProject.projects.krypton,
|
||||
rootProject.projects.velocity,
|
||||
//rootProject.projects.sponge
|
||||
).map { it.dependencyProject }
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.6.0"
|
||||
kotlin("kapt") version "1.6.0"
|
||||
kotlin("jvm") version "1.8.0"
|
||||
kotlin("kapt") version "1.8.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -8,15 +10,19 @@ dependencies {
|
||||
compileOnly(libs.krypton.api)
|
||||
compileOnly(libs.krypton.server) {
|
||||
exclude("ca.spottedleaf", "dataconverter")
|
||||
exclude("org.kryptonmc", "nbt-common")
|
||||
exclude("org.kryptonmc", "nbt-extra-kotlin")
|
||||
exclude("org.kryptonmc", "serialization-core")
|
||||
exclude("org.kryptonmc", "serialization-gson")
|
||||
exclude("org.kryptonmc", "serialization-nbt")
|
||||
}
|
||||
compileOnly(libs.kotlin.stdlib)
|
||||
compileOnly(libs.via)
|
||||
kapt(libs.krypton.annotationProcessor)
|
||||
}
|
||||
|
||||
tasks.compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
freeCompilerArgs = listOf("-Xjvm-default=enable")
|
||||
tasks {
|
||||
compileKotlin {
|
||||
compilerOptions.jvmTarget.set(JvmTarget.JVM_17)
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,35 @@
|
||||
package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.api.event.Listener
|
||||
import org.kryptonmc.api.event.ListenerPriority
|
||||
import org.kryptonmc.api.event.command.CommandExecuteEvent
|
||||
import org.kryptonmc.api.event.command.CommandResult
|
||||
import org.kryptonmc.api.event.player.JoinEvent
|
||||
import org.kryptonmc.api.event.player.QuitEvent
|
||||
|
||||
class KryptonEventListener(private val plugin: Main) {
|
||||
|
||||
@Listener(ListenerPriority.NONE)
|
||||
@Listener
|
||||
fun onJoin(event: JoinEvent) {
|
||||
if (TAB.getInstance().isDisabled) return
|
||||
TAB.getInstance().cpuManager.runTask() {
|
||||
TAB.getInstance().featureManager.onJoin(KryptonTabPlayer(event.player, plugin.protocolVersion(event.player)))
|
||||
TAB.getInstance().cpuManager.runTask {
|
||||
TAB.getInstance().featureManager.onJoin(KryptonTabPlayer(event.player, plugin.getProtocolVersion(event.player)))
|
||||
}
|
||||
}
|
||||
|
||||
@Listener(ListenerPriority.NONE)
|
||||
@Listener
|
||||
fun onQuit(event: QuitEvent) {
|
||||
if (TAB.getInstance().isDisabled) return
|
||||
TAB.getInstance().cpuManager.runTask() {
|
||||
TAB.getInstance().cpuManager.runTask {
|
||||
TAB.getInstance().featureManager.onQuit(TAB.getInstance().getPlayer(event.player.uuid))
|
||||
}
|
||||
}
|
||||
|
||||
@Listener(ListenerPriority.NONE)
|
||||
@Listener
|
||||
fun onCommand(event: CommandExecuteEvent) {
|
||||
if (TAB.getInstance().isDisabled) return
|
||||
if (TAB.getInstance().featureManager.onCommand(TAB.getInstance().getPlayer(event.sender.uuid), event.command)) {
|
||||
event.result = CommandResult.denied()
|
||||
val player = event.sender
|
||||
if (player !is Player) return
|
||||
|
||||
if (TAB.getInstance().featureManager.onCommand(TAB.getInstance().getPlayer(player.uuid), event.command)) {
|
||||
event.deny()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,32 @@ import me.neznamy.tab.api.ProtocolVersion
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent
|
||||
import me.neznamy.tab.api.protocol.*
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
|
||||
import org.kryptonmc.api.auth.GameProfile
|
||||
import org.kryptonmc.api.auth.ProfileProperty
|
||||
import org.kryptonmc.api.world.GameMode
|
||||
import org.kryptonmc.krypton.adventure.KryptonAdventure
|
||||
import org.kryptonmc.krypton.entity.player.PlayerPublicKey
|
||||
import org.kryptonmc.krypton.network.chat.RemoteChatSession
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutDisplayObjective
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutObjective
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfo
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutTeam
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoRemove
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoUpdate
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutUpdateObjectives
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutUpdateScore
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutUpdateTeams
|
||||
import java.util.EnumSet
|
||||
|
||||
// All build functions that just return the packet parameter will be passed through to be handled in KryptonTabPlayer.
|
||||
object KryptonPacketBuilder : PacketBuilder() {
|
||||
|
||||
private val GAME_MODES = GameMode.values()
|
||||
|
||||
@JvmStatic
|
||||
fun toComponent(text: String?, clientVersion: ProtocolVersion): Component {
|
||||
if (text == null || text.isEmpty()) return Component.empty()
|
||||
if (text.isNullOrEmpty()) return Component.empty()
|
||||
return GsonComponentSerializer.gson().deserialize(IChatBaseComponent.optimizedComponent(text).toString(clientVersion))
|
||||
}
|
||||
|
||||
@@ -28,84 +38,108 @@ object KryptonPacketBuilder : PacketBuilder() {
|
||||
override fun build(packet: PacketPlayOutChat, clientVersion: ProtocolVersion?): Any = packet
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun build(packet: PacketPlayOutPlayerInfo, clientVersion: ProtocolVersion): Any = PacketOutPlayerInfo(
|
||||
PacketOutPlayerInfo.Action.valueOf(packet.action.name),
|
||||
packet.entries.map {
|
||||
val displayName = it.displayName?.toString(clientVersion)
|
||||
val skin = if (it.skin != null) listOf(ProfileProperty.of("textures", it.skin!!.value, it.skin!!.signature)) else emptyList()
|
||||
PacketOutPlayerInfo.PlayerData(
|
||||
it.uniqueId,
|
||||
it.name ?: "",
|
||||
skin,
|
||||
GameMode.fromId((it.gameMode?.ordinal ?: 0) - 1) ?: GameMode.SURVIVAL,
|
||||
it.latency,
|
||||
if (displayName != null) GsonComponentSerializer.gson().deserialize(displayName) else Component.empty()
|
||||
override fun build(packet: PacketPlayOutPlayerInfo, clientVersion: ProtocolVersion): Any {
|
||||
if (packet.actions.contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
|
||||
return PacketOutPlayerInfoRemove(packet.entries.map { it.uniqueId })
|
||||
}
|
||||
|
||||
val actions = EnumSet.noneOf(PacketOutPlayerInfoUpdate.Action::class.java)
|
||||
packet.actions.forEach { PacketOutPlayerInfoUpdate.Action.fromId(it.ordinal) }
|
||||
|
||||
val entries = packet.entries.map { data ->
|
||||
val displayName = data.displayName?.toString(clientVersion)
|
||||
val skin = if (data.skin != null) listOf(ProfileProperty.of("textures", data.skin!!.value, data.skin!!.signature)) else emptyList()
|
||||
|
||||
val sessionId = data.chatSessionId
|
||||
val publicKey = data.profilePublicKey as? PlayerPublicKey.Data
|
||||
val session = if (sessionId != null && publicKey != null) RemoteChatSession.Data(sessionId, publicKey) else null
|
||||
|
||||
PacketOutPlayerInfoUpdate.Entry(
|
||||
data.uniqueId,
|
||||
GameProfile.of(data.name ?: "", data.uniqueId, skin),
|
||||
data.isListed,
|
||||
data.latency,
|
||||
GAME_MODES[data.gameMode.ordinal - 1],
|
||||
displayName?.let { GsonComponentSerializer.gson().deserialize(it) },
|
||||
session
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
return PacketOutPlayerInfoUpdate(actions, entries)
|
||||
}
|
||||
|
||||
override fun build(packet: PacketPlayOutPlayerListHeaderFooter, clientVersion: ProtocolVersion?): Any = packet
|
||||
|
||||
override fun build(packet: PacketPlayOutScoreboardDisplayObjective, clientVersion: ProtocolVersion?): Any = PacketOutDisplayObjective(
|
||||
packet.slot,
|
||||
packet.objectiveName
|
||||
)
|
||||
override fun build(packet: PacketPlayOutScoreboardDisplayObjective, clientVersion: ProtocolVersion?): Any {
|
||||
return PacketOutDisplayObjective(packet.slot, packet.objectiveName)
|
||||
}
|
||||
|
||||
override fun build(packet: PacketPlayOutScoreboardObjective, clientVersion: ProtocolVersion): Any = PacketOutObjective(
|
||||
PacketOutObjective.Action.fromId(packet.method)!!,
|
||||
packet.objectiveName,
|
||||
toComponent(packet.displayName, clientVersion),
|
||||
packet.renderType?.ordinal ?: -1
|
||||
)
|
||||
override fun build(packet: PacketPlayOutScoreboardObjective, clientVersion: ProtocolVersion): Any {
|
||||
return PacketOutUpdateObjectives(
|
||||
packet.objectiveName,
|
||||
packet.action,
|
||||
toComponent(packet.displayName, clientVersion),
|
||||
packet.renderType?.ordinal ?: -1
|
||||
)
|
||||
}
|
||||
|
||||
override fun build(packet: PacketPlayOutScoreboardScore, clientVersion: ProtocolVersion?): Any = PacketOutUpdateScore(
|
||||
PacketOutUpdateScore.Action.fromId(packet.action.ordinal)!!,
|
||||
Component.text(packet.player),
|
||||
packet.objectiveName,
|
||||
packet.score
|
||||
)
|
||||
override fun build(packet: PacketPlayOutScoreboardScore, clientVersion: ProtocolVersion?): Any {
|
||||
return PacketOutUpdateScore(packet.player, packet.action.ordinal, packet.objectiveName, packet.score)
|
||||
}
|
||||
|
||||
override fun build(packet: PacketPlayOutScoreboardTeam, clientVersion: ProtocolVersion): Any {
|
||||
val action = PacketOutTeam.Action.fromId(packet.method)!!
|
||||
val action = PacketOutUpdateTeams.Action.fromId(packet.action)!!
|
||||
val players = packet.players.map(Component::text)
|
||||
return PacketOutUpdateTeams(packet.name, action, createParameters(packet, clientVersion), players)
|
||||
}
|
||||
|
||||
private fun createParameters(packet: PacketPlayOutScoreboardTeam, clientVersion: ProtocolVersion): PacketOutUpdateTeams.Parameters? {
|
||||
if (packet.action != 0 && packet.action != 2) return null
|
||||
var prefix = packet.playerPrefix
|
||||
var suffix = packet.playerSuffix
|
||||
if (clientVersion.minorVersion < 13) {
|
||||
prefix = cutTo(prefix, 16)
|
||||
suffix = cutTo(suffix, 16)
|
||||
}
|
||||
return PacketOutTeam(
|
||||
action,
|
||||
packet.name,
|
||||
return PacketOutUpdateTeams.Parameters(
|
||||
Component.text(packet.name),
|
||||
packet.color?.ordinal ?: EnumChatFormat.lastColorsOf(packet.playerPrefix).ordinal,
|
||||
packet.options,
|
||||
packet.nameTagVisibility,
|
||||
packet.collisionRule,
|
||||
KryptonAdventure.getColorFromId(packet.color?.ordinal ?: EnumChatFormat.lastColorsOf(packet.playerPrefix).ordinal),
|
||||
toComponent(prefix, clientVersion),
|
||||
toComponent(suffix, clientVersion),
|
||||
packet.options and 1 > 0,
|
||||
packet.options and 2 > 0,
|
||||
packet.nameTagVisibility ?: "",
|
||||
packet.collisionRule ?: "",
|
||||
players,
|
||||
if (action == PacketOutTeam.Action.ADD_MEMBERS || action == PacketOutTeam.Action.REMOVE_MEMBERS) players else emptySet()
|
||||
toComponent(suffix, clientVersion)
|
||||
)
|
||||
}
|
||||
|
||||
override fun readPlayerInfo(packet: Any?, clientVersion: ProtocolVersion?): PacketPlayOutPlayerInfo? {
|
||||
if (packet !is PacketOutPlayerInfo) return null
|
||||
val action = PacketPlayOutPlayerInfo.EnumPlayerInfoAction.valueOf(packet.action.name)
|
||||
val listData = packet.players.map { data ->
|
||||
val serializedListName = if (data.displayName != null) GsonComponentSerializer.gson().serialize(data.displayName!!) else null
|
||||
val textures = data.properties.firstOrNull { it.name == "textures" } ?: return null
|
||||
PacketPlayOutPlayerInfo.PlayerInfoData(
|
||||
data.name,
|
||||
data.uuid,
|
||||
if (packet is PacketOutPlayerInfoRemove) {
|
||||
return PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, packet.profileIds.map { PlayerInfoData(it) })
|
||||
}
|
||||
if (packet !is PacketOutPlayerInfoUpdate) return null
|
||||
|
||||
val actions = EnumSet.noneOf(EnumPlayerInfoAction::class.java)
|
||||
packet.actions.forEach { actions.add(EnumPlayerInfoAction.valueOf(it.name)) }
|
||||
|
||||
val entries = packet.entries.map { entry ->
|
||||
val displayName = entry.displayName
|
||||
val serializedListName = if (displayName != null) GsonComponentSerializer.gson().serialize(displayName) else null
|
||||
val textures = entry.profile.properties.firstOrNull { it.name == "textures" } ?: return null
|
||||
|
||||
PlayerInfoData(
|
||||
entry.profile.name,
|
||||
entry.profile.uuid,
|
||||
Skin(textures.value, textures.signature),
|
||||
data.latency,
|
||||
PacketPlayOutPlayerInfo.EnumGamemode.VALUES[data.gameMode.ordinal + 1],
|
||||
if (serializedListName != null) IChatBaseComponent.deserialize(serializedListName) else null
|
||||
entry.listed,
|
||||
entry.latency,
|
||||
PacketPlayOutPlayerInfo.EnumGamemode.VALUES[entry.gameMode.ordinal + 1],
|
||||
if (serializedListName != null) IChatBaseComponent.deserialize(serializedListName) else null,
|
||||
entry.chatSession?.sessionId,
|
||||
entry.chatSession?.publicKey
|
||||
)
|
||||
}
|
||||
return PacketPlayOutPlayerInfo(action, listData)
|
||||
|
||||
return PacketPlayOutPlayerInfo(actions, entries)
|
||||
}
|
||||
|
||||
override fun readObjective(packet: Any?): PacketPlayOutScoreboardObjective? = null
|
||||
|
||||
@@ -6,14 +6,13 @@ import io.netty.channel.ChannelPromise
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.features.PipelineInjector
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfo
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoRemove
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutPlayerInfoUpdate
|
||||
import java.util.function.Function
|
||||
|
||||
class KryptonPipelineInjector : PipelineInjector("handler") {
|
||||
|
||||
init {
|
||||
channelFunction = Function(::KryptonChannelDuplexHandler)
|
||||
}
|
||||
override fun getChannelFunction(): Function<TabPlayer, ChannelDuplexHandler> = Function(::KryptonChannelDuplexHandler)
|
||||
|
||||
inner class KryptonChannelDuplexHandler(private val player: TabPlayer) : ChannelDuplexHandler() {
|
||||
|
||||
@@ -28,7 +27,7 @@ class KryptonPipelineInjector : PipelineInjector("handler") {
|
||||
|
||||
override fun write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise) {
|
||||
try {
|
||||
if (msg is PacketOutPlayerInfo) {
|
||||
if (msg is PacketOutPlayerInfoUpdate || msg is PacketOutPlayerInfoRemove) {
|
||||
super.write(ctx, TAB.getInstance().featureManager.onPacketPlayOutPlayerInfo(player, msg), promise)
|
||||
return
|
||||
}
|
||||
|
||||
+36
-39
@@ -1,8 +1,5 @@
|
||||
package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import me.lucko.spark.api.statistic.StatisticWindow.CpuUsage
|
||||
import me.lucko.spark.api.statistic.StatisticWindow.MillisPerTick
|
||||
import me.lucko.spark.api.statistic.StatisticWindow.TicksPerSecond
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager
|
||||
import me.neznamy.tab.shared.placeholders.PlaceholderRegistry
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
@@ -17,42 +14,42 @@ class KryptonPlaceholderRegistry(private val plugin: Main) : PlaceholderRegistry
|
||||
manager.registerPlayerPlaceholder("%displayname%", 500) { (it.player as Player).displayName }
|
||||
manager.registerPlayerPlaceholder("%health%", 100) { ceil((it.player as Player).health) }
|
||||
|
||||
// TPS
|
||||
val tps = plugin.server.spark.tps()!!
|
||||
manager.registerServerPlaceholder("%tps_5s%", 1000) { format(tps.poll(TicksPerSecond.SECONDS_5)) }
|
||||
manager.registerServerPlaceholder("%tps_10s%", 1000) { format(tps.poll(TicksPerSecond.SECONDS_10)) }
|
||||
manager.registerServerPlaceholder("%tps_1m%", 1000) { format(tps.poll(TicksPerSecond.MINUTES_1)) }
|
||||
manager.registerServerPlaceholder("%tps_5m%", 1000) { format(tps.poll(TicksPerSecond.MINUTES_5)) }
|
||||
manager.registerServerPlaceholder("%tps_15m%", 1000) { format(tps.poll(TicksPerSecond.MINUTES_15)) }
|
||||
|
||||
// MSPT
|
||||
val mspt = plugin.server.spark.mspt()!!
|
||||
manager.registerServerPlaceholder("%mspt_min_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).min()) }
|
||||
manager.registerServerPlaceholder("%mspt_max_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).max()) }
|
||||
manager.registerServerPlaceholder("%mspt_mean_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).mean()) }
|
||||
manager.registerServerPlaceholder("%mspt_median_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).median()) }
|
||||
manager.registerServerPlaceholder("%mspt_percentile95_10s%", 1000) {
|
||||
formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).percentile95th())
|
||||
}
|
||||
manager.registerServerPlaceholder("%mspt_min_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).min()) }
|
||||
manager.registerServerPlaceholder("%mspt_max_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).max()) }
|
||||
manager.registerServerPlaceholder("%mspt_mean_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).mean()) }
|
||||
manager.registerServerPlaceholder("%mspt_median_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).median()) }
|
||||
manager.registerServerPlaceholder("%mspt_percentile95_1m%", 1000) {
|
||||
formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).percentile95th())
|
||||
}
|
||||
|
||||
// CPU process
|
||||
val process = plugin.server.spark.cpuProcess()
|
||||
manager.registerServerPlaceholder("%cpu_process_10s%", 1000) { format(process.poll(CpuUsage.SECONDS_10)) }
|
||||
manager.registerServerPlaceholder("%cpu_process_1m%", 1000) { format(process.poll(CpuUsage.MINUTES_1)) }
|
||||
manager.registerServerPlaceholder("%cpu_process_15m%", 1000) { format(process.poll(CpuUsage.MINUTES_15)) }
|
||||
|
||||
// CPU system
|
||||
val system = plugin.server.spark.cpuSystem()
|
||||
manager.registerServerPlaceholder("%cpu_system_10s%", 1000) { format(system.poll(CpuUsage.SECONDS_10)) }
|
||||
manager.registerServerPlaceholder("%cpu_system_1m%", 1000) { format(system.poll(CpuUsage.MINUTES_1)) }
|
||||
manager.registerServerPlaceholder("%cpu_system_15m%", 1000) { format(system.poll(CpuUsage.MINUTES_15)) }
|
||||
// // TPS
|
||||
// val tps = plugin.server.spark.tps()!!
|
||||
// manager.registerServerPlaceholder("%tps_5s%", 1000) { format(tps.poll(TicksPerSecond.SECONDS_5)) }
|
||||
// manager.registerServerPlaceholder("%tps_10s%", 1000) { format(tps.poll(TicksPerSecond.SECONDS_10)) }
|
||||
// manager.registerServerPlaceholder("%tps_1m%", 1000) { format(tps.poll(TicksPerSecond.MINUTES_1)) }
|
||||
// manager.registerServerPlaceholder("%tps_5m%", 1000) { format(tps.poll(TicksPerSecond.MINUTES_5)) }
|
||||
// manager.registerServerPlaceholder("%tps_15m%", 1000) { format(tps.poll(TicksPerSecond.MINUTES_15)) }
|
||||
//
|
||||
// // MSPT
|
||||
// val mspt = plugin.server.spark.mspt()!!
|
||||
// manager.registerServerPlaceholder("%mspt_min_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).min()) }
|
||||
// manager.registerServerPlaceholder("%mspt_max_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).max()) }
|
||||
// manager.registerServerPlaceholder("%mspt_mean_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).mean()) }
|
||||
// manager.registerServerPlaceholder("%mspt_median_10s%", 1000) { formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).median()) }
|
||||
// manager.registerServerPlaceholder("%mspt_percentile95_10s%", 1000) {
|
||||
// formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).percentile95th())
|
||||
// }
|
||||
// manager.registerServerPlaceholder("%mspt_min_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).min()) }
|
||||
// manager.registerServerPlaceholder("%mspt_max_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).max()) }
|
||||
// manager.registerServerPlaceholder("%mspt_mean_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).mean()) }
|
||||
// manager.registerServerPlaceholder("%mspt_median_1m%", 1000) { formatMSPT(mspt.poll(MillisPerTick.MINUTES_1).median()) }
|
||||
// manager.registerServerPlaceholder("%mspt_percentile95_1m%", 1000) {
|
||||
// formatMSPT(mspt.poll(MillisPerTick.SECONDS_10).percentile95th())
|
||||
// }
|
||||
//
|
||||
// // CPU process
|
||||
// val process = plugin.server.spark.cpuProcess()
|
||||
// manager.registerServerPlaceholder("%cpu_process_10s%", 1000) { format(process.poll(CpuUsage.SECONDS_10)) }
|
||||
// manager.registerServerPlaceholder("%cpu_process_1m%", 1000) { format(process.poll(CpuUsage.MINUTES_1)) }
|
||||
// manager.registerServerPlaceholder("%cpu_process_15m%", 1000) { format(process.poll(CpuUsage.MINUTES_15)) }
|
||||
//
|
||||
// // CPU system
|
||||
// val system = plugin.server.spark.cpuSystem()
|
||||
// manager.registerServerPlaceholder("%cpu_system_10s%", 1000) { format(system.poll(CpuUsage.SECONDS_10)) }
|
||||
// manager.registerServerPlaceholder("%cpu_system_1m%", 1000) { format(system.poll(CpuUsage.MINUTES_1)) }
|
||||
// manager.registerServerPlaceholder("%cpu_system_15m%", 1000) { format(system.poll(CpuUsage.MINUTES_15)) }
|
||||
}
|
||||
|
||||
private fun format(value: Double): String = TWO_DECIMAL_PLACES.format(value)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.api.protocol.PacketBuilder
|
||||
import me.neznamy.tab.platforms.krypton.features.PerWorldPlayerList
|
||||
import me.neznamy.tab.platforms.krypton.features.unlimitedtags.NameTagX
|
||||
import me.neznamy.tab.shared.Platform
|
||||
import me.neznamy.tab.api.TabFeature
|
||||
import me.neznamy.tab.platforms.krypton.features.unlimitedtags.KryptonNameTagX
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.TabConstants
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl
|
||||
import me.neznamy.tab.shared.backend.BackendPlatform
|
||||
import me.neznamy.tab.shared.features.PipelineInjector
|
||||
import me.neznamy.tab.shared.features.TabExpansion
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag
|
||||
import me.neznamy.tab.shared.permission.LuckPerms
|
||||
import me.neznamy.tab.shared.permission.None
|
||||
@@ -15,41 +13,16 @@ import me.neznamy.tab.shared.permission.PermissionPlugin
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||
import java.io.File
|
||||
|
||||
class KryptonPlatform(
|
||||
private val plugin: Main,
|
||||
private val folder: File
|
||||
) : Platform {
|
||||
class KryptonPlatform(private val plugin: Main) : BackendPlatform(KryptonPacketBuilder) {
|
||||
|
||||
private val server = plugin.server
|
||||
|
||||
override fun detectPermissionPlugin(): PermissionPlugin {
|
||||
if (isPluginEnabled("luckperms")) return LuckPerms(server.pluginManager.plugin("luckperms")!!.description.version)
|
||||
if (server.pluginManager.isLoaded("luckperms")) return LuckPerms(getPluginVersion("luckperms"))
|
||||
return None()
|
||||
}
|
||||
|
||||
override fun loadFeatures() {
|
||||
if (TAB.getInstance().configuration.isPipelineInjection) {
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.PIPELINE_INJECTION, KryptonPipelineInjector())
|
||||
}
|
||||
|
||||
// Placeholders
|
||||
KryptonPlaceholderRegistry(plugin).registerPlaceholders(TAB.getInstance().placeholderManager)
|
||||
UniversalPlaceholderRegistry().registerPlaceholders(TAB.getInstance().placeholderManager)
|
||||
|
||||
// Load features
|
||||
loadNametagFeature()
|
||||
TAB.getInstance().loadUniversalFeatures()
|
||||
if (TAB.getInstance().config.getBoolean("bossbar.enabled", false)) {
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.BOSS_BAR, BossBarManagerImpl())
|
||||
}
|
||||
if (TAB.getInstance().config.getBoolean("per-world-playerlist.enabled", false)) {
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, PerWorldPlayerList(plugin))
|
||||
}
|
||||
server.players.forEach { TAB.getInstance().addPlayer(KryptonTabPlayer(it, plugin.protocolVersion(it))) }
|
||||
}
|
||||
|
||||
override fun sendConsoleMessage(message: String, translateColors: Boolean) {
|
||||
val component = if (translateColors) LegacyComponentSerializer.legacyAmpersand().deserialize(message) else Component.text(message)
|
||||
server.console.sendMessage(component)
|
||||
@@ -65,42 +38,44 @@ class KryptonPlatform(
|
||||
val serverIntervals = manager.serverPlaceholderRefreshIntervals
|
||||
val playerIntervals = manager.playerPlaceholderRefreshIntervals
|
||||
if (serverIntervals.containsKey(identifier)) {
|
||||
manager.registerServerPlaceholder(identifier, serverIntervals[identifier]!!) { identifier }
|
||||
manager.registerServerPlaceholder(identifier, serverIntervals.get(identifier)!!) { identifier }
|
||||
return
|
||||
}
|
||||
if (playerIntervals.containsKey(identifier)) {
|
||||
manager.registerPlayerPlaceholder(identifier, playerIntervals[identifier]!!) { identifier }
|
||||
manager.registerPlayerPlaceholder(identifier, playerIntervals.get(identifier)!!) { identifier }
|
||||
return
|
||||
}
|
||||
manager.registerPlayerPlaceholder(identifier, manager.defaultRefresh) { identifier }
|
||||
}
|
||||
|
||||
override fun getServerVersion(): String = "${server.platform.name} ${server.platform.version}"
|
||||
override fun getPluginVersion(plugin: String): String? = server.pluginManager.getPlugin(plugin)?.description?.version
|
||||
|
||||
override fun getDataFolder(): File = folder
|
||||
|
||||
override fun callLoadEvent() {
|
||||
// do nothing here, this will be removed
|
||||
override fun loadPlayers() {
|
||||
server.players.forEach { TAB.getInstance().addPlayer(KryptonTabPlayer(it, plugin.getProtocolVersion(it))) }
|
||||
}
|
||||
|
||||
override fun callLoadEvent(player: TabPlayer) {
|
||||
// do nothing here, this will be removed
|
||||
override fun registerPlaceholders() {
|
||||
KryptonPlaceholderRegistry(plugin).registerPlaceholders(TAB.getInstance().placeholderManager)
|
||||
UniversalPlaceholderRegistry().registerPlaceholders(TAB.getInstance().placeholderManager)
|
||||
}
|
||||
|
||||
override fun getMaxPlayers(): Int = server.maxPlayers
|
||||
override fun getPipelineInjector(): PipelineInjector {
|
||||
return KryptonPipelineInjector()
|
||||
}
|
||||
|
||||
override fun getPacketBuilder(): PacketBuilder = KryptonPacketBuilder
|
||||
override fun getUnlimitedNametags(): NameTag {
|
||||
return KryptonNameTagX(plugin)
|
||||
}
|
||||
|
||||
override fun isProxy(): Boolean = false
|
||||
override fun getTabExpansion(): TabExpansion? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun isPluginEnabled(plugin: String): Boolean = server.pluginManager.isLoaded(plugin)
|
||||
override fun getPetFix(): TabFeature? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun loadNametagFeature() {
|
||||
if (!TAB.getInstance().config.getBoolean("scoreboard-teams.enabled", true)) return
|
||||
if (TAB.getInstance().config.getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)) {
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, NameTagX(plugin))
|
||||
} else {
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, NameTag())
|
||||
}
|
||||
override fun getPerWorldPlayerlist(): TabFeature? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,45 @@
|
||||
package me.neznamy.tab.platforms.krypton
|
||||
|
||||
import io.netty.channel.Channel
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutChat
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutChat.ChatMessageType
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerListHeaderFooter
|
||||
import me.neznamy.tab.api.protocol.Skin
|
||||
import me.neznamy.tab.shared.ITabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import net.kyori.adventure.audience.MessageType
|
||||
import net.kyori.adventure.bossbar.BossBar
|
||||
import net.kyori.adventure.bossbar.BossBar.Color
|
||||
import net.kyori.adventure.bossbar.BossBar.Flag
|
||||
import net.kyori.adventure.bossbar.BossBar.Overlay
|
||||
import net.kyori.adventure.chat.ChatType
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.krypton.entity.player.KryptonPlayer
|
||||
import org.kryptonmc.krypton.network.NettyConnection
|
||||
import org.kryptonmc.krypton.packet.Packet
|
||||
import java.util.UUID
|
||||
|
||||
class KryptonTabPlayer(
|
||||
delegate: Player,
|
||||
protocolVersion: Int
|
||||
) : ITabPlayer(delegate, delegate.uuid, delegate.profile.name, "N/A", delegate.world.name, protocolVersion) {
|
||||
) : ITabPlayer(delegate, delegate.uuid, delegate.profile.name, "N/A", delegate.world.name, protocolVersion, true) {
|
||||
|
||||
private val delegate = delegate as KryptonPlayer
|
||||
private val bossBars = mutableMapOf<UUID, BossBar>()
|
||||
|
||||
init {
|
||||
channel = this.delegate.session.channel
|
||||
val connection = this.delegate.connection
|
||||
channel = NettyConnection::class.java.getDeclaredField("channel").apply { isAccessible = true }.get(connection) as Channel
|
||||
}
|
||||
|
||||
override fun hasPermission(permission: String): Boolean = delegate.hasPermission(permission)
|
||||
|
||||
override fun getPing(): Int {
|
||||
val latency = delegate.session.latency
|
||||
val latency = delegate.connection.latency()
|
||||
if (latency > 10000 || latency < 0) return -1
|
||||
return latency
|
||||
}
|
||||
@@ -44,7 +48,7 @@ class KryptonTabPlayer(
|
||||
if (packet == null) return
|
||||
try {
|
||||
if (packet is Packet) {
|
||||
delegate.session.send(packet)
|
||||
delegate.connection.send(packet)
|
||||
return
|
||||
}
|
||||
when (packet) {
|
||||
@@ -71,12 +75,12 @@ class KryptonTabPlayer(
|
||||
|
||||
override fun isOnline(): Boolean = delegate.isOnline
|
||||
|
||||
override fun isVanished(): Boolean = delegate.isVanished
|
||||
override fun isVanished(): Boolean = false
|
||||
|
||||
override fun getGamemode(): Int = delegate.gameMode.ordinal
|
||||
|
||||
private fun handle(packet: PacketPlayOutBoss) {
|
||||
when (packet.operation) {
|
||||
when (packet.action) {
|
||||
Action.ADD -> {
|
||||
if (bossBars.containsKey(packet.id)) return
|
||||
val bar = BossBar.bossBar(
|
||||
@@ -88,22 +92,22 @@ class KryptonTabPlayer(
|
||||
if (packet.isCreateWorldFog) bar.addFlag(Flag.CREATE_WORLD_FOG)
|
||||
if (packet.isDarkenScreen) bar.addFlag(Flag.DARKEN_SCREEN)
|
||||
if (packet.isPlayMusic) bar.addFlag(Flag.PLAY_BOSS_MUSIC)
|
||||
bossBars[packet.id] = bar
|
||||
bossBars.put(packet.id, bar)
|
||||
delegate.showBossBar(bar)
|
||||
}
|
||||
Action.REMOVE -> {
|
||||
delegate.hideBossBar(bossBars[packet.id] ?: return)
|
||||
delegate.hideBossBar(bossBars.get(packet.id) ?: return)
|
||||
bossBars.remove(packet.id)
|
||||
}
|
||||
Action.UPDATE_PCT -> bossBars[packet.id]?.progress(packet.pct)
|
||||
Action.UPDATE_NAME -> bossBars[packet.id]?.name(LegacyComponentSerializer.legacySection().deserialize(packet.name))
|
||||
Action.UPDATE_PCT -> bossBars.get(packet.id)?.progress(packet.pct)
|
||||
Action.UPDATE_NAME -> bossBars.get(packet.id)?.name(LegacyComponentSerializer.legacySection().deserialize(packet.name))
|
||||
Action.UPDATE_STYLE -> {
|
||||
val bar = bossBars[packet.id] ?: return
|
||||
val bar = bossBars.get(packet.id) ?: return
|
||||
bar.overlay(Overlay.valueOf(packet.overlay.toString()))
|
||||
bar.color(Color.valueOf(packet.color.toString()))
|
||||
}
|
||||
Action.UPDATE_PROPERTIES -> {
|
||||
val bar = bossBars[packet.id] ?: return
|
||||
val bar = bossBars.get(packet.id) ?: return
|
||||
processFlag(bar, packet.isCreateWorldFog, Flag.CREATE_WORLD_FOG)
|
||||
processFlag(bar, packet.isDarkenScreen, Flag.DARKEN_SCREEN)
|
||||
processFlag(bar, packet.isPlayMusic, Flag.PLAY_BOSS_MUSIC)
|
||||
@@ -114,12 +118,11 @@ class KryptonTabPlayer(
|
||||
|
||||
private fun handle(packet: PacketPlayOutChat) {
|
||||
val message = GsonComponentSerializer.gson().deserialize(packet.message.toString(version))
|
||||
if (packet.type === PacketPlayOutChat.ChatMessageType.GAME_INFO) {
|
||||
// Adventure recommends that we send action bars for GAME_INFO, which is why GAME_INFO isn't in MessageType
|
||||
delegate.sendActionBar(message)
|
||||
return
|
||||
when (packet.type) {
|
||||
ChatMessageType.CHAT -> delegate.sendMessage(message, ChatType.CHAT.bind(getPlayer().displayName))
|
||||
ChatMessageType.SYSTEM -> delegate.sendMessage(message)
|
||||
ChatMessageType.GAME_INFO -> delegate.sendActionBar(message)
|
||||
}
|
||||
delegate.sendMessage(message, MessageType.valueOf(packet.type.name))
|
||||
}
|
||||
|
||||
private fun handle(packet: PacketPlayOutPlayerListHeaderFooter) {
|
||||
|
||||
@@ -3,16 +3,19 @@ package me.neznamy.tab.platforms.krypton
|
||||
import com.google.inject.Inject
|
||||
import com.viaversion.viaversion.api.Via
|
||||
import me.neznamy.tab.api.ProtocolVersion
|
||||
import me.neznamy.tab.api.TabConstants
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.TabConstants
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import org.kryptonmc.api.Server
|
||||
import org.kryptonmc.api.command.CommandMeta
|
||||
import org.kryptonmc.api.command.Sender
|
||||
import org.kryptonmc.api.command.SimpleCommand
|
||||
import org.kryptonmc.api.command.meta.SimpleCommandMeta
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.api.event.Event
|
||||
import org.kryptonmc.api.event.EventFilter
|
||||
import org.kryptonmc.api.event.EventNode
|
||||
import org.kryptonmc.api.event.Listener
|
||||
import org.kryptonmc.api.event.server.ServerStartEvent
|
||||
import org.kryptonmc.api.event.server.ServerStopEvent
|
||||
@@ -34,20 +37,34 @@ import java.nio.file.Path
|
||||
)
|
||||
class Main @Inject constructor(
|
||||
val server: Server,
|
||||
private val pluginEventNode: EventNode<Event>,
|
||||
@DataFolder val folder: Path
|
||||
) {
|
||||
|
||||
val eventNode: EventNode<Event> = EventNode.filteredForEvent("tab_events", EventFilter.ALL) { !TAB.getInstance().isPluginDisabled }
|
||||
|
||||
@Listener
|
||||
fun onStart(event: ServerStartEvent) {
|
||||
TAB.setInstance(TAB(KryptonPlatform(this, folder.toFile()), ProtocolVersion.fromNetworkId(server.platform.protocolVersion)))
|
||||
if (TAB.getInstance().serverVersion == ProtocolVersion.UNKNOWN) {
|
||||
pluginEventNode.addChild(eventNode)
|
||||
|
||||
val tab = TAB(
|
||||
KryptonPlatform(this),
|
||||
ProtocolVersion.fromNetworkId(server.platform.protocolVersion),
|
||||
server.platform.version,
|
||||
folder.toFile(),
|
||||
null
|
||||
)
|
||||
TAB.setInstance(tab)
|
||||
|
||||
if (TAB.getInstance().serverVersion == ProtocolVersion.UNKNOWN_SERVER_VERSION) {
|
||||
server.console.sendMessage(Component.text(
|
||||
"[TAB] Unknown server version: ${server.platform.version}! Plugin may not work correctly",
|
||||
NamedTextColor.RED
|
||||
))
|
||||
}
|
||||
server.eventManager.register(this, KryptonEventListener(this))
|
||||
server.commandManager.register(KryptonTABCommand(), SimpleCommandMeta.builder("tab").build())
|
||||
|
||||
eventNode.registerListeners(KryptonEventListener(this))
|
||||
server.commandManager.register(KryptonTABCommand(), CommandMeta.builder("tab").build())
|
||||
TAB.getInstance().load()
|
||||
}
|
||||
|
||||
@@ -56,12 +73,12 @@ class Main @Inject constructor(
|
||||
TAB.getInstance()?.unload()
|
||||
}
|
||||
|
||||
fun protocolVersion(player: Player): Int {
|
||||
if (server.pluginManager.isLoaded("viaversion")) return viaProtocolVersion(player)
|
||||
fun getProtocolVersion(player: Player): Int {
|
||||
if (server.pluginManager.isLoaded("viaversion")) return getViaProtocolVersion(player)
|
||||
return TAB.getInstance().serverVersion.networkId
|
||||
}
|
||||
|
||||
private fun viaProtocolVersion(player: Player, retryLevel: Int = 0): Int {
|
||||
private fun getViaProtocolVersion(player: Player, retryLevel: Int = 0): Int {
|
||||
try {
|
||||
if (retryLevel == 10) {
|
||||
TAB.getInstance().debug("Failed to get protocol version of ${player.profile.name} after 10 retries")
|
||||
@@ -70,7 +87,7 @@ class Main @Inject constructor(
|
||||
val version = Via.getAPI().getPlayerVersion(player)
|
||||
if (version == -1) {
|
||||
Thread.sleep(5)
|
||||
return viaProtocolVersion(player, retryLevel + 1)
|
||||
return getViaProtocolVersion(player, retryLevel + 1)
|
||||
}
|
||||
TAB.getInstance().debug("ViaVersion returned protocol version $version for ${player.profile.name}")
|
||||
return version
|
||||
@@ -79,7 +96,7 @@ class Main @Inject constructor(
|
||||
return -1
|
||||
} catch (exception: Throwable) {
|
||||
TAB.getInstance().errorManager.printError("Failed to get protocol version of ${player.profile.name} using ViaVersion " +
|
||||
"v${server.pluginManager.plugin("viaversion")?.description?.version}")
|
||||
"v${server.pluginManager.getPlugin("viaversion")?.description?.version}")
|
||||
return TAB.getInstance().serverVersion.networkId
|
||||
}
|
||||
}
|
||||
@@ -87,7 +104,7 @@ class Main @Inject constructor(
|
||||
class KryptonTABCommand : SimpleCommand {
|
||||
|
||||
override fun execute(sender: Sender, args: Array<String>) {
|
||||
if (TAB.getInstance().isDisabled) {
|
||||
if (TAB.getInstance().isPluginDisabled) {
|
||||
val canReload = sender.hasPermission("tab.reload")
|
||||
val isAdmin = sender.hasPermission("tab.admin")
|
||||
TAB.getInstance().disabledCommand.execute(args, canReload, isAdmin).forEach { sender.sendMessage(Component.text(it)) }
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package me.neznamy.tab.platforms.krypton.features
|
||||
|
||||
import me.neznamy.tab.api.TabFeature
|
||||
import me.neznamy.tab.platforms.krypton.Main
|
||||
import me.neznamy.tab.shared.TabConstants
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.api.event.Listener
|
||||
import org.kryptonmc.api.event.player.JoinEvent
|
||||
|
||||
// TODO: Also check player on world change when Krypton supports world changing
|
||||
class PerWorldPlayerList(private val plugin: Main) : TabFeature("Per world playerlist", null) {
|
||||
|
||||
private val allowBypass = TAB.getInstance().configuration.config.getBoolean(
|
||||
"per-world-playerlist.allow-bypass-permission",
|
||||
false
|
||||
)
|
||||
private val ignoredWorlds = TAB.getInstance().configuration.config.getStringList(
|
||||
"per-world-playerlist.ignore-effect-in-worlds",
|
||||
listOf("ignoredworld", "build")
|
||||
)
|
||||
private val sharedWorlds = TAB.getInstance().configuration.config
|
||||
.getConfigurationSection<String, List<String>>("per-world-playerlist.shared-playerlist-world-groups")
|
||||
|
||||
init {
|
||||
TAB.getInstance().debug("Loaded PerWorldPlayerList feature with parameters allowBypass=$allowBypass, ignoredWorlds=$ignoredWorlds, " +
|
||||
"sharedWorlds=$sharedWorlds")
|
||||
plugin.server.eventManager.register(plugin, this)
|
||||
}
|
||||
|
||||
@Listener
|
||||
fun onJoin(event: JoinEvent) {
|
||||
val time = System.nanoTime()
|
||||
checkPlayer(event.player)
|
||||
TAB.getInstance().cpuManager.addTime(featureName, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime() - time)
|
||||
}
|
||||
|
||||
override fun load() {
|
||||
plugin.server.scheduler.run(plugin) { plugin.server.players.forEach(::checkPlayer) }
|
||||
}
|
||||
|
||||
override fun unload() {
|
||||
plugin.server.players.forEach { plugin.server.players.forEach(it::show) }
|
||||
plugin.server.eventManager.unregisterListener(plugin, this)
|
||||
}
|
||||
|
||||
private fun checkPlayer(player: Player) {
|
||||
plugin.server.players.forEach {
|
||||
if (it === player) return@forEach
|
||||
if (!shouldSee(player, it) && player.canSee(it)) player.hide(it)
|
||||
if (shouldSee(player, it) && !player.canSee(it)) player.show(it)
|
||||
if (!shouldSee(it, player) && it.canSee(player)) it.hide(player)
|
||||
if (shouldSee(it, player) && !it.canSee(player)) it.show(player)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSee(viewer: Player, target: Player): Boolean {
|
||||
if (target === viewer) return true
|
||||
if ((allowBypass && viewer.hasPermission(TabConstants.Permission.PER_WORLD_PLAYERLIST_BYPASS)) || ignoredWorlds.contains(viewer.world.name)) {
|
||||
return true
|
||||
}
|
||||
var viewerWorldGroup = "${viewer.world.name}-default"
|
||||
var targetWorldGroup = "${target.world.name}-default"
|
||||
sharedWorlds.forEach { (key, value) ->
|
||||
if (value == null) return@forEach
|
||||
if (value.contains(viewer.world.name)) viewerWorldGroup = key
|
||||
if (value.contains(target.world.name)) targetWorldGroup = key
|
||||
}
|
||||
return viewerWorldGroup == targetWorldGroup
|
||||
}
|
||||
}
|
||||
+8
-9
@@ -1,20 +1,19 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabConstants
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.TabConstants.CpuUsageCategory
|
||||
import org.kryptonmc.api.event.Listener
|
||||
import org.kryptonmc.api.event.ListenerPriority
|
||||
import org.kryptonmc.api.event.player.PerformActionEvent
|
||||
|
||||
class EventListener(private val feature: NameTagX) {
|
||||
class EventListener(private val feature: KryptonNameTagX) {
|
||||
|
||||
@Listener(ListenerPriority.MAXIMUM)
|
||||
fun onAction(event: PerformActionEvent) {
|
||||
if (event.action != PerformActionEvent.Action.START_SNEAKING && event.action != PerformActionEvent.Action.STOP_SNEAKING) return
|
||||
@Listener
|
||||
fun onSneak(event: PerformActionEvent) {
|
||||
val player = TAB.getInstance().getPlayer(event.player.uuid)
|
||||
if (player == null || !player.isLoaded || feature.isPlayerDisabled(player)) return
|
||||
TAB.getInstance().cpuManager.runMeasuredTask(feature, CpuUsageCategory.PLAYER_SNEAK) {
|
||||
player.armorStandManager?.sneak(event.action == PerformActionEvent.Action.START_SNEAKING)
|
||||
if (player == null || feature.isPlayerDisabled(player)) return
|
||||
|
||||
TAB.getInstance().threadManager.runMeasuredTask(feature, TabConstants.CpuUsageCategory.PLAYER_SNEAK) {
|
||||
feature.getArmorStandManager(player)!!.sneak(event.action == PerformActionEvent.Action.START_SNEAKING)
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
-209
@@ -1,246 +1,77 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.ArmorStand
|
||||
import me.neznamy.tab.api.Property
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent
|
||||
import me.neznamy.tab.platforms.krypton.KryptonPacketBuilder
|
||||
import me.neznamy.tab.shared.TabConstants
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import org.kryptonmc.api.entity.EntityTypes
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.api.registry.Registries
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager
|
||||
import org.kryptonmc.krypton.entity.KryptonEntityTypes
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataHolder
|
||||
import org.kryptonmc.krypton.entity.metadata.MetadataKeys
|
||||
import org.kryptonmc.krypton.entity.player.KryptonPlayer
|
||||
import org.kryptonmc.krypton.packet.Packet
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutDestroyEntities
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutEntityTeleport
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutMetadata
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSpawnLivingEntity
|
||||
import org.spongepowered.math.vector.Vector2f
|
||||
import org.spongepowered.math.vector.Vector3d
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutRemoveEntities
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSetEntityMetadata
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSpawnEntity
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutTeleportEntity
|
||||
|
||||
class KryptonArmorStand(
|
||||
private val owner: TabPlayer,
|
||||
private val property: Property,
|
||||
private var yOffset: Double,
|
||||
private val staticOffset: Boolean
|
||||
) : ArmorStand {
|
||||
manager: BackendArmorStandManager,
|
||||
nameTagX: KryptonNameTagX,
|
||||
owner: TabPlayer,
|
||||
propertyName: String,
|
||||
yOffset: Double,
|
||||
staticOffset: Boolean
|
||||
) : BackendArmorStand(nameTagX, manager, owner, propertyName, yOffset, staticOffset) {
|
||||
|
||||
private val manager = TAB.getInstance().featureManager.getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS) as NameTagX
|
||||
private val player = owner.player as Player
|
||||
private val entityId = ID_COUNTER.incrementAndGet()
|
||||
private val uuid = UUID.randomUUID()
|
||||
private var sneaking = false
|
||||
private var visible = calculateVisibility()
|
||||
private val destroyPacket = PacketOutDestroyEntities(entityId)
|
||||
private val location: Vector3d
|
||||
get() {
|
||||
val x = player.location.x()
|
||||
var y = calculateY() + yOffset + 2
|
||||
val z = player.location.z()
|
||||
y -= if (player.sleepingPosition != null) 1.76 else if (sneaking) 0.45 else 0.18
|
||||
return Vector3d(x, y, z)
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
visible = calculateVisibility()
|
||||
updateMetadata()
|
||||
}
|
||||
|
||||
override fun getProperty(): Property = property
|
||||
|
||||
override fun hasStaticOffset(): Boolean = staticOffset
|
||||
|
||||
override fun getOffset(): Double = yOffset
|
||||
|
||||
override fun setOffset(offset: Double) {
|
||||
if (yOffset == offset) return
|
||||
yOffset = offset
|
||||
owner.armorStandManager.nearbyPlayers.forEach {
|
||||
it.sendPacket(getTeleportPacket(it), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_OFFSET_CHANGE)
|
||||
}
|
||||
}
|
||||
private val player = owner.player as KryptonPlayer
|
||||
private val destroyPacket = PacketOutRemoveEntities(intArrayOf(entityId))
|
||||
|
||||
override fun spawn(viewer: TabPlayer) {
|
||||
getSpawnPackets(viewer).forEach { viewer.sendPacket(it, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SPAWN) }
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
TAB.getInstance().onlinePlayers.forEach { it.sendPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN) }
|
||||
getSpawnPackets(viewer).forEach { viewer.sendPacket(it) }
|
||||
}
|
||||
|
||||
override fun destroy(viewer: TabPlayer) {
|
||||
viewer.sendPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN)
|
||||
viewer.sendPacket(destroyPacket)
|
||||
}
|
||||
|
||||
override fun teleport() {
|
||||
owner.armorStandManager.nearbyPlayers.forEach {
|
||||
it.sendPacket(getTeleportPacket(it), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT)
|
||||
}
|
||||
private fun getArmorStandYFor(viewer: TabPlayer): Double {
|
||||
var y = player.position.y
|
||||
if (player.isSwimming || player.isGliding) y -= 1.22
|
||||
y += getYAdd(false, sneaking, viewer)
|
||||
return y
|
||||
}
|
||||
|
||||
override fun teleport(viewer: TabPlayer) {
|
||||
if (!owner.armorStandManager.isNearby(viewer) && viewer !== owner) {
|
||||
owner.armorStandManager.spawn(viewer)
|
||||
return
|
||||
}
|
||||
viewer.sendPacket(getTeleportPacket(viewer), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT)
|
||||
override fun updateMetadata(viewer: TabPlayer) {
|
||||
viewer.sendPacket(PacketOutSetEntityMetadata(entityId, createMetadata(property.getFormat(viewer), viewer).collectAll()))
|
||||
}
|
||||
|
||||
override fun sneak(sneaking: Boolean) {
|
||||
if (this.sneaking == sneaking) return // idk
|
||||
this.sneaking = sneaking
|
||||
owner.armorStandManager.nearbyPlayers.forEach {
|
||||
if (it.version.minorVersion == 14 && !ALWAYS_VISIBLE) {
|
||||
// 1.14.x client sided bug, despawning completely
|
||||
if (sneaking) {
|
||||
it.sendPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SNEAK)
|
||||
} else {
|
||||
spawn(it)
|
||||
}
|
||||
} else {
|
||||
// respawning so there's no animation and it's instant
|
||||
respawn(it)
|
||||
}
|
||||
}
|
||||
override fun sendTeleportPacket(viewer: TabPlayer) {
|
||||
viewer.sendPacket(PacketOutTeleportEntity(entityId, player.position.x, getArmorStandYFor(viewer), player.position.z, 0F, 0F, false))
|
||||
}
|
||||
|
||||
override fun updateVisibility(force: Boolean) {
|
||||
val visibility = calculateVisibility()
|
||||
if (visible != visibility || force) {
|
||||
visible = visibility
|
||||
updateMetadata()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEntityId(): Int = entityId
|
||||
|
||||
private fun updateMetadata() {
|
||||
owner.armorStandManager.nearbyPlayers.forEach {
|
||||
val packet = PacketOutMetadata(entityId, createMetadata(property.getFormat(it), it).all)
|
||||
it.sendPacket(packet, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTeleportPacket(viewer: TabPlayer): PacketOutEntityTeleport = PacketOutEntityTeleport(
|
||||
entityId,
|
||||
armorStandLocationFor(viewer),
|
||||
Vector2f.ZERO,
|
||||
false
|
||||
)
|
||||
|
||||
// Using a sequence here makes sense because we only need the iteration behaviour, which is exactly what sequences are for.
|
||||
// For those reading this from Java, a Sequence is kind of Kotlin's equivalent to Java's Stream.
|
||||
private fun getSpawnPackets(viewer: TabPlayer): Sequence<Packet> {
|
||||
private fun getSpawnPackets(viewer: TabPlayer): Array<Packet> {
|
||||
visible = calculateVisibility()
|
||||
val data = createMetadata(property.getFormat(viewer), viewer)
|
||||
val location = armorStandLocationFor(viewer)
|
||||
return sequenceOf(
|
||||
PacketOutSpawnLivingEntity(
|
||||
entityId,
|
||||
uuid,
|
||||
TYPE,
|
||||
location.x(),
|
||||
location.y(),
|
||||
location.z(),
|
||||
0F,
|
||||
0F,
|
||||
0F,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
),
|
||||
PacketOutMetadata(entityId, data.all)
|
||||
return arrayOf(
|
||||
PacketOutSpawnEntity(entityId, uuid, KryptonEntityTypes.ARMOR_STAND, player.position.x, getArmorStandYFor(viewer),
|
||||
player.position.z, 0F, 0F, 0F, 0, 0, 0, 0),
|
||||
PacketOutSetEntityMetadata(entityId, data.collectAll())
|
||||
)
|
||||
}
|
||||
|
||||
private fun createMetadata(displayName: String, viewer: TabPlayer): MetadataHolder {
|
||||
val viewerPlayer = viewer.player as KryptonPlayer
|
||||
val holder = MetadataHolder(viewerPlayer).apply {
|
||||
add(MetadataKeys.FLAGS)
|
||||
add(MetadataKeys.CUSTOM_NAME)
|
||||
add(MetadataKeys.CUSTOM_NAME_VISIBILITY)
|
||||
define(MetadataKeys.Entity.FLAGS, 0)
|
||||
define(MetadataKeys.Entity.CUSTOM_NAME, null)
|
||||
define(MetadataKeys.Entity.CUSTOM_NAME_VISIBILITY, false)
|
||||
}
|
||||
|
||||
var flags = 32 // invisible
|
||||
if (sneaking) flags += 2
|
||||
holder[MetadataKeys.FLAGS] = flags.toByte()
|
||||
holder[MetadataKeys.CUSTOM_NAME] = KryptonPacketBuilder.toComponent(displayName, viewer.version)
|
||||
|
||||
if (isNameVisibilityEmpty(displayName) || !viewerPlayer.canSee(player) || manager.hasHiddenNametag(owner, viewer)) {
|
||||
holder[MetadataKeys.CUSTOM_NAME_VISIBILITY] = false
|
||||
} else {
|
||||
holder[MetadataKeys.CUSTOM_NAME_VISIBILITY] = visible
|
||||
}
|
||||
|
||||
if (viewer.version.minorVersion > 8 || manager.markerFor18x) {
|
||||
holder.add(MetadataKeys.ARMOR_STAND.FLAGS)
|
||||
holder[MetadataKeys.ARMOR_STAND.FLAGS] = 16.toByte()
|
||||
holder.set(MetadataKeys.Entity.FLAGS, (if (sneaking) 34 else 32).toByte())
|
||||
holder.set(MetadataKeys.Entity.CUSTOM_NAME, KryptonPacketBuilder.toComponent(displayName, viewer.version))
|
||||
holder.set(MetadataKeys.Entity.CUSTOM_NAME_VISIBILITY, !shouldBeInvisibleFor(viewer, displayName) && visible)
|
||||
if (viewer.version.minorVersion > 8 || manager.isMarkerFor18x) {
|
||||
holder.define(MetadataKeys.ArmorStand.FLAGS, 16.toByte())
|
||||
}
|
||||
return holder
|
||||
}
|
||||
|
||||
private fun calculateVisibility(): Boolean {
|
||||
if (owner.isDisguised || manager.vehicleManager.isOnBoat(owner)) return false
|
||||
if (ALWAYS_VISIBLE) return true
|
||||
return !owner.hasInvisibilityPotion() && owner.gamemode != 3 && !manager.hasHiddenNametag(owner) && property.get().isNotEmpty()
|
||||
}
|
||||
|
||||
private fun calculateY(): Double {
|
||||
// 1.14+ server sided bug
|
||||
val vehicle = player.vehicle
|
||||
if (vehicle != null) {
|
||||
val key = vehicle.type.key().asString()
|
||||
when {
|
||||
key.contains("horse") -> return vehicle.location.y() + 0.85
|
||||
key.contains("donkey") -> return vehicle.location.y() + 0.525
|
||||
vehicle.type === EntityTypes.PIG -> return vehicle.location.y() + 0.325
|
||||
key.contains("strider") -> vehicle.location.y() + 1.15
|
||||
}
|
||||
}
|
||||
if (player.isSwimming || player.isGliding) return player.location.y() - 1.22
|
||||
return player.location.y()
|
||||
}
|
||||
|
||||
private fun armorStandLocationFor(viewer: TabPlayer): Vector3d {
|
||||
if (viewer.version.minorVersion == 8 && !manager.markerFor18x) return location.add(0.0, -2.0, 0.0)
|
||||
return location
|
||||
}
|
||||
|
||||
private fun isNameVisibilityEmpty(displayName: String): Boolean {
|
||||
if (displayName.isEmpty()) return true
|
||||
if (!displayName.startsWith(EnumChatFormat.COLOR_CHAR) && !displayName.startsWith('&') && !displayName.startsWith('#')) return false
|
||||
var text = IChatBaseComponent.fromColoredText(displayName).toRawText()
|
||||
if (text.contains(' ')) text = text.replace(" ", "")
|
||||
return text.isEmpty()
|
||||
}
|
||||
|
||||
override fun respawn(viewer: TabPlayer) {
|
||||
viewer.sendPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN)
|
||||
val spawn = Runnable { spawn(viewer) }
|
||||
if (viewer.version.minorVersion == 8) {
|
||||
// 1.8.8 client sided bug
|
||||
TAB.getInstance().cpuManager.runTaskLater(
|
||||
50,
|
||||
manager,
|
||||
TabConstants.CpuUsageCategory.V1_8_0_BUG_COMPENSATION,
|
||||
spawn
|
||||
)
|
||||
} else {
|
||||
spawn.run()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val ALWAYS_VISIBLE = TAB.getInstance().configuration.isArmorStandsAlwaysVisible
|
||||
// entity ID counter to pick (hopefully) unique entity IDs
|
||||
private val ID_COUNTER = AtomicInteger(2000000000)
|
||||
private val TYPE = Registries.ENTITY_TYPE.idOf(EntityTypes.ARMOR_STAND)
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabConstants
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.platforms.krypton.Main
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX
|
||||
import org.kryptonmc.api.entity.Entity
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.api.event.EventFilter
|
||||
import org.kryptonmc.api.event.EventNode
|
||||
import org.kryptonmc.api.event.player.PerformActionEvent
|
||||
import org.kryptonmc.krypton.packet.EntityPacket
|
||||
import org.kryptonmc.krypton.packet.MovementPacket
|
||||
import org.kryptonmc.krypton.packet.`in`.play.PacketInInteract
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutRemoveEntities
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSpawnPlayer
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class KryptonNameTagX(private val plugin: Main) : BackendNameTagX() {
|
||||
|
||||
private val eventListener = EventListener(this)
|
||||
|
||||
init {
|
||||
val eventNode = EventNode.filteredForEvent("tab_nametagx", EventFilter.ALL) { event ->
|
||||
if (event !is PerformActionEvent) return@filteredForEvent false
|
||||
event.action == PerformActionEvent.Action.START_SNEAKING || event.action == PerformActionEvent.Action.STOP_SNEAKING
|
||||
}
|
||||
plugin.eventNode.addChild(eventNode)
|
||||
eventNode.registerListeners(eventListener)
|
||||
}
|
||||
|
||||
override fun onPacketReceive(sender: TabPlayer, packet: Any): Boolean {
|
||||
if (sender.version.minorVersion == 8 && packet is PacketInInteract) {
|
||||
val entityId = packet.entityId
|
||||
var attacked: TabPlayer? = null
|
||||
for (player in TAB.getInstance().onlinePlayers) {
|
||||
if (player.isLoaded && getArmorStandManager(player)!!.hasArmorStandWithID(entityId)) {
|
||||
attacked = player
|
||||
break
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked !== sender) updateEntityId(packet, entityId)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPacketSend(receiver: TabPlayer, packet: Any) {
|
||||
if (receiver.version.minorVersion < 8) return
|
||||
if (!receiver.isLoaded || isPlayerDisabled(receiver) || disabledUnlimitedPlayers.contains(receiver)) return
|
||||
when (packet) {
|
||||
is MovementPacket -> {
|
||||
if (packet !is EntityPacket) return
|
||||
packetListener.onEntityMove(receiver, (packet as EntityPacket).entityId)
|
||||
}
|
||||
is PacketOutSpawnPlayer -> packetListener.onEntitySpawn(receiver, packet.entityId)
|
||||
is PacketOutRemoveEntities -> packet.ids.forEach { packetListener.onEntityDestroy(receiver, it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun isOnBoat(player: TabPlayer): Boolean = vehicleManager.isOnBoat(player)
|
||||
|
||||
override fun getDistance(player1: TabPlayer, player2: TabPlayer): Double {
|
||||
val pos1 = (player1.player as Player).position
|
||||
val pos2 = (player2.player as Player).position
|
||||
return sqrt((pos1.x - pos2.x) * (pos1.x - pos2.x) + (pos1.z - pos2.z) * (pos1.z - pos2.z))
|
||||
}
|
||||
|
||||
override fun areInSameWorld(player1: TabPlayer, player2: TabPlayer): Boolean {
|
||||
return (player1.player as Player).world != (player2.player as Player).world
|
||||
}
|
||||
|
||||
override fun canSee(viewer: TabPlayer?, target: TabPlayer?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun unregisterListener() {
|
||||
plugin.eventNode.unregisterListeners(eventListener)
|
||||
}
|
||||
|
||||
override fun getPassengers(vehicle: Any): List<Int> {
|
||||
return (vehicle as Entity).passengers.map { it.id }
|
||||
}
|
||||
|
||||
override fun registerVehiclePlaceholder() {
|
||||
TAB.getInstance().placeholderManager.registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100) {
|
||||
(it.player as Player).vehicle.toString()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getVehicle(player: TabPlayer): Any? {
|
||||
return (player.player as Player).vehicle
|
||||
}
|
||||
|
||||
override fun getEntityId(entity: Any): Int {
|
||||
return (entity as Entity).id
|
||||
}
|
||||
|
||||
override fun getEntityType(entity: Any): String {
|
||||
return (entity as Entity).type.toString()
|
||||
}
|
||||
|
||||
override fun createArmorStand(asm: BackendArmorStandManager, owner: TabPlayer, lineName: String, yOffset: Double, staticOffset: Boolean): BackendArmorStand {
|
||||
return KryptonArmorStand(asm, this, owner, lineName, yOffset, staticOffset)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val INTERACT_ENTITY_ID_FIELD = PacketInInteract::class.java.getDeclaredField("entityId").apply { isAccessible = true }
|
||||
|
||||
@JvmStatic
|
||||
private fun updateEntityId(packet: PacketInInteract, id: Int) {
|
||||
INTERACT_ENTITY_ID_FIELD.setInt(packet, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabFeature
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
|
||||
class LocationRefresher(private val feature: NameTagX) : TabFeature(feature.featureName, "Processing passengers / preview") {
|
||||
|
||||
init {
|
||||
TAB.getInstance().placeholderManager.registerPlayerPlaceholder("%location0%", 50) {
|
||||
if (!feature.vehicleManager.isInVehicle(it) && !it.isPreviewingNametag) return@registerPlayerPlaceholder null
|
||||
val location = (it.player as Player).location
|
||||
location.x() + location.y() + location.z()
|
||||
}
|
||||
addUsedPlaceholders(listOf("%location0%"))
|
||||
}
|
||||
|
||||
override fun refresh(refreshed: TabPlayer, force: Boolean) {
|
||||
if (feature.vehicleManager.isInVehicle(refreshed)) feature.vehicleManager.processPassengers(refreshed.player as Player)
|
||||
if (refreshed.isPreviewingNametag) refreshed.armorStandManager.teleport(refreshed)
|
||||
}
|
||||
}
|
||||
-323
@@ -1,323 +0,0 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.ArmorStandManager
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.api.team.UnlimitedNametagManager
|
||||
import me.neznamy.tab.platforms.krypton.Main
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.TabConstants
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.krypton.entity.player.KryptonPlayer
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class NameTagX(plugin: Main) : NameTag(), UnlimitedNametagManager {
|
||||
|
||||
// config options
|
||||
val markerFor18x = TAB.getInstance().configuration.config.getBoolean(
|
||||
"scoreboard-teams.unlimited-nametag-mode.use-marker-tag-for-1-8-x-clients",
|
||||
false
|
||||
)
|
||||
val disableOnBoats = TAB.getInstance().configuration.config.getBoolean(
|
||||
"scoreboard-teams.unlimited-nametag-mode.disable-on-boats",
|
||||
true
|
||||
)
|
||||
private val spaceBetweenLines = TAB.getInstance().configuration.config.getDouble(
|
||||
"scoreboard-teams.unlimited-nametag-mode.space-between-lines",
|
||||
0.22
|
||||
)
|
||||
private val disabledUnlimitedWorlds: List<String> = TAB.getInstance().configuration.config.getStringList(
|
||||
"scoreboard-teams.unlimited-nametag-mode.disable-in-worlds",
|
||||
emptyList()
|
||||
)
|
||||
private val dynamicLines = TAB.getInstance().configuration.config.getStringList(
|
||||
"scoreboard-teams.unlimited-nametag-mode.dynamic-lines",
|
||||
listOf(TabConstants.Property.ABOVENAME, TabConstants.Property.NAMETAG, TabConstants.Property.BELOWNAME, "another")
|
||||
)
|
||||
private val staticLines = TAB.getInstance().configuration.config
|
||||
.getConfigurationSection<String, Any>("scoreboard-teams.unlimited-nametag-mode.static-lines")
|
||||
|
||||
// player data by entity ID, used for better performance
|
||||
val entityIdMap = ConcurrentHashMap<Int, TabPlayer>()
|
||||
|
||||
private val playersInDisabledUnlimitedWorlds = mutableSetOf<TabPlayer>()
|
||||
private val disableUnlimitedWorldsArray = disabledUnlimitedWorlds.toTypedArray()
|
||||
private val unlimitedWorldWhitelistMode = disabledUnlimitedWorlds.contains("WHITELIST")
|
||||
|
||||
private val playersDisabledWithAPI = mutableListOf<TabPlayer>()
|
||||
|
||||
val vehicleManager = VehicleRefresher(this)
|
||||
private val eventListener = EventListener(this)
|
||||
|
||||
init {
|
||||
dynamicLines.reverse()
|
||||
plugin.server.eventManager.register(plugin, eventListener)
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, PacketListener(this))
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager)
|
||||
TAB.getInstance().featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_LOCATION_REFRESHER, LocationRefresher(this))
|
||||
TAB.getInstance().debug("Loaded Unlimited nametag featured with parameters markerFor18x=$markerFor18x, disableOnBoats=$disableOnBoats, " +
|
||||
"spaceBetweenLines=$spaceBetweenLines, disabledUnlimitedWorlds=$disabledUnlimitedWorlds")
|
||||
}
|
||||
|
||||
fun isPlayerDisabled(player: TabPlayer): Boolean = isDisabledPlayer(player) || playersInDisabledUnlimitedWorlds.contains(player)
|
||||
|
||||
override fun load() {
|
||||
TAB.getInstance().onlinePlayers.forEach { all ->
|
||||
entityIdMap[(all.player as KryptonPlayer).id] = all
|
||||
updateProperties(all)
|
||||
loadArmorStands(all)
|
||||
if (isDisabled(all.world)) playersInDisabledUnlimitedWorlds.add(all)
|
||||
if (isPlayerDisabled(all)) return@forEach
|
||||
vehicleManager.loadPassengers(all)
|
||||
TAB.getInstance().onlinePlayers.forEach { spawnArmorStands(all, it, false) }
|
||||
}
|
||||
super.load()
|
||||
startVisibilityRefreshTask()
|
||||
}
|
||||
|
||||
override fun unload() {
|
||||
super.unload()
|
||||
TAB.getInstance().onlinePlayers.forEach { it.armorStandManager?.destroy() }
|
||||
entityIdMap.clear()
|
||||
}
|
||||
|
||||
override fun onJoin(connectedPlayer: TabPlayer) {
|
||||
if (isDisabled(connectedPlayer.world) && !playersInDisabledUnlimitedWorlds.contains(connectedPlayer)) {
|
||||
playersInDisabledUnlimitedWorlds.add(connectedPlayer)
|
||||
}
|
||||
super.onJoin(connectedPlayer)
|
||||
entityIdMap[(connectedPlayer.player as KryptonPlayer).id] = connectedPlayer
|
||||
loadArmorStands(connectedPlayer)
|
||||
if (isPlayerDisabled(connectedPlayer)) return
|
||||
vehicleManager.loadPassengers(connectedPlayer)
|
||||
TAB.getInstance().onlinePlayers.forEach { spawnArmorStands(connectedPlayer, it, true) }
|
||||
}
|
||||
|
||||
override fun onQuit(disconnectedPlayer: TabPlayer) {
|
||||
super.onQuit(disconnectedPlayer)
|
||||
TAB.getInstance().onlinePlayers.forEach { it.armorStandManager?.unregisterPlayer(disconnectedPlayer) }
|
||||
entityIdMap.remove((disconnectedPlayer.player as KryptonPlayer).id)
|
||||
playersInDisabledUnlimitedWorlds.remove(disconnectedPlayer)
|
||||
playersDisabledWithAPI.remove(disconnectedPlayer)
|
||||
if (disconnectedPlayer.armorStandManager != null) { // player was not loaded yet
|
||||
disconnectedPlayer.armorStandManager.destroy()
|
||||
TAB.getInstance().cpuManager.runTaskLater(
|
||||
500,
|
||||
this,
|
||||
TabConstants.CpuUsageCategory.PLAYER_QUIT,
|
||||
disconnectedPlayer.armorStandManager::destroy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh(refreshed: TabPlayer, force: Boolean) {
|
||||
super.refresh(refreshed, force)
|
||||
if (isPlayerDisabled(refreshed)) return
|
||||
if (force) {
|
||||
refreshed.armorStandManager.destroy()
|
||||
loadArmorStands(refreshed)
|
||||
vehicleManager.loadPassengers(refreshed)
|
||||
TAB.getInstance().onlinePlayers.forEach {
|
||||
if (it === refreshed) return@forEach
|
||||
if (it.world == refreshed.world) refreshed.armorStandManager.spawn(it)
|
||||
}
|
||||
return
|
||||
}
|
||||
var fix = false
|
||||
refreshed.armorStandManager.armorStands.forEach {
|
||||
if (it.property.update()) {
|
||||
it.refresh()
|
||||
fix = true
|
||||
}
|
||||
if (fix) fixArmorStandHeights(refreshed)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateProperties(player: TabPlayer): Boolean {
|
||||
super.updateProperties(player)
|
||||
player.loadPropertyFromConfig(this, TabConstants.Property.CUSTOMTAGNAME, player.name)
|
||||
rebuildNametagLine(player)
|
||||
dynamicLines.forEach { if (it != TabConstants.Property.NAMETAG) player.loadPropertyFromConfig(this, it) }
|
||||
staticLines.keys.forEach { if (it != TabConstants.Property.NAMETAG) player.loadPropertyFromConfig(this, it) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getFeatureName(): String = "Unlimited Nametags"
|
||||
|
||||
override fun getTeamVisibility(player: TabPlayer, viewer: TabPlayer): Boolean {
|
||||
if (player.hasInvisibilityPotion()) return false // 1.8.x client sided bug
|
||||
return vehicleManager.isOnBoat(player) || isPlayerDisabled(player)
|
||||
}
|
||||
|
||||
override fun pauseTeamHandling(player: TabPlayer) {
|
||||
if (teamHandlingPaused.contains(player)) return
|
||||
if (!isDisabledPlayer(player)) unregisterTeam(player)
|
||||
teamHandlingPaused.add(player) // adding after, so unregisterTeam method runs
|
||||
player.armorStandManager.destroy()
|
||||
}
|
||||
|
||||
override fun resumeTeamHandling(player: TabPlayer) {
|
||||
if (!teamHandlingPaused.contains(player)) return
|
||||
teamHandlingPaused.remove(player) // removing before, so registerTeam method runs
|
||||
if (!isDisabledPlayer(player)) registerTeam(player)
|
||||
if (!isPlayerDisabled(player)) TAB.getInstance().onlinePlayers.forEach { spawnArmorStands(player, it, false) }
|
||||
}
|
||||
|
||||
override fun disableArmorStands(player: TabPlayer) {
|
||||
if (playersDisabledWithAPI.contains(player)) return
|
||||
playersDisabledWithAPI.add(player)
|
||||
player.armorStandManager.destroy()
|
||||
updateTeamData(player)
|
||||
}
|
||||
|
||||
override fun enableArmorStands(player: TabPlayer) {
|
||||
if (!playersDisabledWithAPI.contains(player)) return
|
||||
playersDisabledWithAPI.remove(player)
|
||||
if (!isPlayerDisabled(player)) TAB.getInstance().onlinePlayers.forEach { spawnArmorStands(player, it, false) }
|
||||
updateTeamData(player)
|
||||
}
|
||||
|
||||
override fun hasDisabledArmorStands(player: TabPlayer): Boolean = playersDisabledWithAPI.contains(player)
|
||||
|
||||
override fun setPrefix(player: TabPlayer, prefix: String) {
|
||||
player.getProperty(TabConstants.Property.TAGPREFIX).temporaryValue = prefix
|
||||
rebuildNametagLine(player)
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun setSuffix(player: TabPlayer, suffix: String) {
|
||||
player.getProperty(TabConstants.Property.TAGSUFFIX).temporaryValue = suffix
|
||||
rebuildNametagLine(player)
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun resetPrefix(player: TabPlayer) {
|
||||
player.getProperty(TabConstants.Property.TAGPREFIX).temporaryValue = null
|
||||
rebuildNametagLine(player)
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun resetSuffix(player: TabPlayer) {
|
||||
player.getProperty(TabConstants.Property.TAGSUFFIX).temporaryValue = null
|
||||
rebuildNametagLine(player)
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun setName(player: TabPlayer, customname: String) {
|
||||
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).temporaryValue = customname
|
||||
rebuildNametagLine(player)
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun setLine(player: TabPlayer, line: String, value: String) {
|
||||
player.getProperty(line).temporaryValue = value
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun resetName(player: TabPlayer) {
|
||||
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).temporaryValue = null
|
||||
rebuildNametagLine(player)
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun resetLine(player: TabPlayer, line: String) {
|
||||
player.getProperty(line).temporaryValue = null
|
||||
player.forceRefresh()
|
||||
}
|
||||
|
||||
override fun getCustomName(player: TabPlayer): String = player.getProperty(TabConstants.Property.CUSTOMTAGNAME).temporaryValue
|
||||
|
||||
override fun getCustomLineValue(player: TabPlayer, line: String): String = player.getProperty(line).temporaryValue
|
||||
|
||||
override fun getOriginalName(player: TabPlayer): String = player.getProperty(TabConstants.Property.CUSTOMTAGNAME).originalRawValue
|
||||
|
||||
override fun getOriginalLineValue(player: TabPlayer, line: String): String = player.getProperty(line).originalRawValue
|
||||
|
||||
override fun getDefinedLines(): List<String> = dynamicLines.plus(staticLines.keys)
|
||||
|
||||
private fun isDisabled(world: String): Boolean {
|
||||
var contains = contains(disableUnlimitedWorldsArray, world)
|
||||
if (unlimitedWorldWhitelistMode) contains = !contains
|
||||
return contains
|
||||
}
|
||||
|
||||
private fun startVisibilityRefreshTask() {
|
||||
TAB.getInstance().cpuManager.startRepeatingMeasuredTask(
|
||||
500,
|
||||
this,
|
||||
TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY
|
||||
) {
|
||||
TAB.getInstance().onlinePlayers.forEach {
|
||||
if (!it.isLoaded || isPlayerDisabled(it)) return@forEach
|
||||
it.armorStandManager.updateVisibility(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun spawnArmorStands(owner: TabPlayer, viewer: TabPlayer, sendMutually: Boolean) {
|
||||
if (owner === viewer) return // not displaying own armor stands
|
||||
val ownerPlayer = owner.player as Player
|
||||
val viewerPlayer = viewer.player as Player
|
||||
if (viewerPlayer.world !== ownerPlayer.world) return // in different worlds
|
||||
if (isPlayerDisabled(owner)) return
|
||||
if (owner.distanceTo(viewer) <= 48) {
|
||||
if (viewerPlayer.canSee(ownerPlayer) && !owner.isVanished) {
|
||||
owner.armorStandManager.spawn(viewer)
|
||||
}
|
||||
if (sendMutually && viewer.armorStandManager != null && ownerPlayer.canSee(viewerPlayer) && !viewer.isVanished) {
|
||||
viewer.armorStandManager.spawn(owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadArmorStands(player: TabPlayer) {
|
||||
// player.armorStandManager = ArmorStandManager()
|
||||
rebuildNametagLine(player)
|
||||
var height = 0.0
|
||||
dynamicLines.forEach {
|
||||
val property = player.getProperty(it)
|
||||
if (property.currentRawValue.isEmpty()) return@forEach
|
||||
player.armorStandManager.addArmorStand(it, KryptonArmorStand(player, property, height, false))
|
||||
height += spaceBetweenLines
|
||||
}
|
||||
staticLines.forEach { (key, value) ->
|
||||
val property = player.getProperty(key)
|
||||
if (property.currentRawValue.isEmpty()) return@forEach
|
||||
player.armorStandManager.addArmorStand(key, KryptonArmorStand(player, property, value.toString().toDouble(), true))
|
||||
}
|
||||
fixArmorStandHeights(player)
|
||||
}
|
||||
|
||||
private fun fixArmorStandHeights(player: TabPlayer) {
|
||||
var currentY = -spaceBetweenLines
|
||||
player.armorStandManager.armorStands.forEach {
|
||||
if (it.hasStaticOffset()) return@forEach
|
||||
if (it.property.get().isNotEmpty()) {
|
||||
currentY += spaceBetweenLines
|
||||
it.offset = currentY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rebuildNametagLine(player: TabPlayer) {
|
||||
player.setProperty(
|
||||
this,
|
||||
TabConstants.Property.NAMETAG,
|
||||
player.getProperty(TabConstants.Property.TAGPREFIX).currentRawValue +
|
||||
player.getProperty(TabConstants.Property.CUSTOMTAGNAME).currentRawValue +
|
||||
player.getProperty(TabConstants.Property.TAGSUFFIX).currentRawValue
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
private fun TabPlayer.distanceTo(other: TabPlayer): Double {
|
||||
val first = (player as Player).location
|
||||
val second = (other.player as Player).location
|
||||
return sqrt((first.x() - second.x()).pow(2) + (first.z() - second.z()).pow(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabFeature
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import me.neznamy.tab.shared.TabConstants.CpuUsageCategory
|
||||
import org.kryptonmc.krypton.entity.KryptonEntity
|
||||
import org.kryptonmc.krypton.packet.EntityPacket
|
||||
import org.kryptonmc.krypton.packet.MovementPacket
|
||||
import org.kryptonmc.krypton.packet.`in`.play.PacketInInteract
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutDestroyEntities
|
||||
import org.kryptonmc.krypton.packet.out.play.PacketOutSpawnPlayer
|
||||
|
||||
class PacketListener(private val nameTagX: NameTagX) : TabFeature(nameTagX.featureName, null) {
|
||||
|
||||
override fun onPacketReceive(sender: TabPlayer, packet: Any): Boolean {
|
||||
if (sender.version.minorVersion == 8 && packet is PacketInInteract) {
|
||||
val entityId = packet.entityId
|
||||
var attacked: TabPlayer? = null
|
||||
for (player in TAB.getInstance().onlinePlayers) {
|
||||
if (player.isLoaded && player.armorStandManager.hasArmorStandWithID(entityId)) {
|
||||
attacked = player
|
||||
break
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked !== sender) packet.updateEntityId(entityId)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPacketSend(receiver: TabPlayer, packet: Any) {
|
||||
if (receiver.version.minorVersion < 8) return
|
||||
if (!receiver.isLoaded || nameTagX.isPlayerDisabled(receiver)) return
|
||||
when (packet) {
|
||||
is MovementPacket -> {
|
||||
if (packet !is EntityPacket) return
|
||||
onEntityMove(receiver, (packet as EntityPacket).entityId)
|
||||
}
|
||||
is PacketOutSpawnPlayer -> onEntitySpawn(receiver, packet.entityId)
|
||||
is PacketOutDestroyEntities -> packet.ids.forEach { onEntityDestroy(receiver, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onEntityMove(receiver: TabPlayer, entityId: Int) {
|
||||
val player = nameTagX.entityIdMap[entityId]
|
||||
if (player != null) {
|
||||
TAB.getInstance().cpuManager.runMeasuredTask(nameTagX, CpuUsageCategory.PACKET_ENTITY_MOVE) {
|
||||
player.armorStandManager.teleport(receiver)
|
||||
}
|
||||
return
|
||||
}
|
||||
nameTagX.vehicleManager.vehicles(entityId).forEach {
|
||||
val passenger = nameTagX.entityIdMap[(it as KryptonEntity).id]
|
||||
if (passenger != null && passenger.armorStandManager != null) {
|
||||
TAB.getInstance().cpuManager.runMeasuredTask(nameTagX, CpuUsageCategory.PACKET_ENTITY_MOVE) {
|
||||
passenger.armorStandManager.teleport(receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onEntitySpawn(receiver: TabPlayer, entityId: Int) {
|
||||
val spawnedPlayer = nameTagX.entityIdMap[entityId]
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded) {
|
||||
TAB.getInstance().cpuManager.runMeasuredTask(nameTagX, CpuUsageCategory.PACKET_ENTITY_SPAWN) {
|
||||
spawnedPlayer.armorStandManager.spawn(receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onEntityDestroy(receiver: TabPlayer, entity: Int) {
|
||||
val deSpawnedPlayer = nameTagX.entityIdMap[entity]
|
||||
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded) {
|
||||
TAB.getInstance().cpuManager.runMeasuredTask(nameTagX, CpuUsageCategory.PACKET_ENTITY_DESTROY) {
|
||||
deSpawnedPlayer.armorStandManager.destroy(receiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val INTERACT_ENTITY_ID_FIELD = PacketInInteract::class.java.getDeclaredField("entityId").apply { isAccessible = true }
|
||||
|
||||
private fun PacketInInteract.updateEntityId(id: Int) {
|
||||
INTERACT_ENTITY_ID_FIELD.setInt(this, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
package me.neznamy.tab.platforms.krypton.features.unlimitedtags
|
||||
|
||||
import me.neznamy.tab.api.TabFeature
|
||||
import me.neznamy.tab.api.TabPlayer
|
||||
import me.neznamy.tab.shared.TAB
|
||||
import org.kryptonmc.api.entity.Entity
|
||||
import org.kryptonmc.api.entity.EntityTypes
|
||||
import org.kryptonmc.api.entity.player.Player
|
||||
import org.kryptonmc.krypton.entity.KryptonEntity
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class VehicleRefresher(private val feature: NameTagX) : TabFeature(feature.featureName, "Refreshing vehicles") {
|
||||
|
||||
private val playersInVehicle = ConcurrentHashMap<TabPlayer, KryptonEntity>()
|
||||
private val vehicles = ConcurrentHashMap<Int, List<Entity>>()
|
||||
private val playersOnBoats = mutableListOf<TabPlayer>()
|
||||
|
||||
init {
|
||||
addUsedPlaceholders(listOf("%vehicle%"))
|
||||
TAB.getInstance().placeholderManager.registerPlayerPlaceholder("%vehicle%", 100) { (it.player as Player).vehicle.toString() }
|
||||
}
|
||||
|
||||
fun isInVehicle(player: TabPlayer): Boolean = playersInVehicle.containsKey(player)
|
||||
|
||||
fun isOnBoat(player: TabPlayer): Boolean = playersOnBoats.contains(player)
|
||||
|
||||
fun vehicles(id: Int): List<Entity> = vehicles[id] ?: emptyList()
|
||||
|
||||
fun loadPassengers(player: TabPlayer) {
|
||||
if ((player.player as Player).vehicle == null) return
|
||||
val vehicle = (player.player as Player).vehicle
|
||||
vehicles[(vehicle as KryptonEntity).id] = vehicle.passengers
|
||||
}
|
||||
|
||||
fun processPassengers(vehicle: Entity) {
|
||||
vehicle.passengers.forEach {
|
||||
if (it is Player) TAB.getInstance().getPlayer(it.uuid).armorStandManager.teleport()
|
||||
processPassengers(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh(refreshed: TabPlayer, force: Boolean) {
|
||||
if (feature.isPlayerDisabled(refreshed)) return
|
||||
val vehicle = (refreshed.player as Player).vehicle
|
||||
if (playersInVehicle.containsKey(refreshed) && vehicle == null) {
|
||||
// vehicle exit
|
||||
vehicles.remove((playersInVehicle[refreshed] as KryptonEntity).id)
|
||||
refreshed.armorStandManager.teleport()
|
||||
playersInVehicle.remove(refreshed)
|
||||
if (feature.disableOnBoats && playersOnBoats.contains(refreshed)) {
|
||||
playersOnBoats.remove(refreshed)
|
||||
feature.updateTeamData(refreshed)
|
||||
}
|
||||
}
|
||||
if (!playersInVehicle.containsKey(refreshed) && vehicle != null) {
|
||||
// vehicle enter
|
||||
vehicles[(vehicle as KryptonEntity).id] = vehicle.passengers
|
||||
refreshed.armorStandManager.respawn() // making teleport instant instead of showing teleport animation
|
||||
playersInVehicle[refreshed] = vehicle
|
||||
if (feature.disableOnBoats && vehicle.type === EntityTypes.BOAT) {
|
||||
playersOnBoats.add(refreshed)
|
||||
feature.updateTeamData(refreshed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQuit(disconnectedPlayer: TabPlayer) {
|
||||
if (playersInVehicle.containsKey(disconnectedPlayer)) vehicles.remove(playersInVehicle[disconnectedPlayer]!!.id)
|
||||
playersInVehicle.remove(disconnectedPlayer)
|
||||
playersOnBoats.remove(disconnectedPlayer)
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -4,7 +4,6 @@ enableFeaturePreview("VERSION_CATALOGS")
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral() // Netty, SnakeYaml, json-simple, Gson, slf4j, Guava, Kyori event, bStats, AuthLib
|
||||
maven("https://jitpack.io") // PremiumVanish
|
||||
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // PlaceholderAPI
|
||||
maven("https://repo.viaversion.com/") // ViaVersion
|
||||
maven("https://repo.essentialsx.net/releases/") // Essentials
|
||||
@@ -15,13 +14,14 @@ dependencyResolutionManagement {
|
||||
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
|
||||
maven("https://repo.purpurmc.org/snapshots") // Purpur, Vault
|
||||
maven("https://repo.papermc.io/repository/maven-public/") // Velocity
|
||||
maven("https://repo.spongepowered.org/repository/maven-public/") // Sponge
|
||||
}
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
}
|
||||
|
||||
pluginManagement {
|
||||
includeBuild("build-logic")
|
||||
repositories {
|
||||
maven("https://repo.spongepowered.org/repository/maven-public/")
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
}
|
||||
@@ -31,8 +31,9 @@ rootProject.name = "TAB"
|
||||
|
||||
include(":api")
|
||||
include(":shared")
|
||||
//include(":krypton")
|
||||
include(":krypton")
|
||||
include(":velocity")
|
||||
include(":bukkit")
|
||||
include(":bungeecord")
|
||||
//include(":sponge")
|
||||
include(":jar")
|
||||
@@ -3,6 +3,7 @@ package me.neznamy.tab.shared;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -34,7 +35,7 @@ public class DynamicText implements Property {
|
||||
private String rawValue;
|
||||
|
||||
/** Raw value assigned via API, null if not set */
|
||||
private String temporaryValue;
|
||||
@Getter private String temporaryValue;
|
||||
|
||||
/**
|
||||
* Raw value using %s for each placeholder ready to be inserted
|
||||
@@ -168,12 +169,7 @@ public class DynamicText implements Property {
|
||||
public String getCurrentRawValue() {
|
||||
return temporaryValue != null ? temporaryValue : rawValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemporaryValue() {
|
||||
return temporaryValue;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getOriginalRawValue() {
|
||||
return rawValue;
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.nio.file.Files;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
@@ -26,7 +27,7 @@ public class ErrorManager {
|
||||
private final File errorLog = new File(TAB.getInstance().getDataFolder(), "errors.log");
|
||||
|
||||
/** anti-override.log file when some plugin or server itself attempts to override the plugin */
|
||||
private final File antiOverrideLog = new File(TAB.getInstance().getDataFolder(), "anti-override.log");
|
||||
@Getter private final File antiOverrideLog = new File(TAB.getInstance().getDataFolder(), "anti-override.log");
|
||||
|
||||
/** placeholder-errors.log file for errors thrown by placeholders */
|
||||
private final File placeholderErrorLog = new File(TAB.getInstance().getDataFolder(), "placeholder-errors.log");
|
||||
@@ -294,13 +295,4 @@ public class ErrorManager {
|
||||
public void missingAttribute(String objectType, Object objectName, String attribute) {
|
||||
startupWarn(objectType + " \"&e" + objectName + "&c\" is missing \"&e" + attribute + "&c\" attribute!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns anti-override.log file
|
||||
*
|
||||
* @return anti-override.log file
|
||||
*/
|
||||
public File getAntiOverrideLog() {
|
||||
return antiOverrideLog;
|
||||
}
|
||||
}
|
||||
@@ -119,11 +119,15 @@ public class FeatureManagerImpl implements FeatureManager {
|
||||
long millis = System.currentTimeMillis();
|
||||
TAB.getInstance().addPlayer(connectedPlayer);
|
||||
for (TabFeature f : values) {
|
||||
if (!f.overridesMethod("onJoin")) continue;
|
||||
long time = System.nanoTime();
|
||||
f.onJoin(connectedPlayer);
|
||||
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
|
||||
try {
|
||||
if (!f.overridesMethod("onJoin")) continue;
|
||||
long time = System.nanoTime();
|
||||
f.onJoin(connectedPlayer);
|
||||
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
|
||||
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
|
||||
} catch (Throwable e) {
|
||||
TAB.getInstance().getErrorManager().criticalError("Feature " + f.getClass().getSimpleName() + " failed to process player " + connectedPlayer.getName(), e);
|
||||
}
|
||||
}
|
||||
((ITabPlayer)connectedPlayer).markAsLoaded(true);
|
||||
TAB.getInstance().debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -14,10 +15,10 @@ import java.util.List;
|
||||
public class GroupManager extends TabFeature {
|
||||
|
||||
/** Detected permission plugin to take groups from */
|
||||
private final PermissionPlugin plugin;
|
||||
@Getter private final PermissionPlugin plugin;
|
||||
|
||||
/** If enabled, groups are assigned via permissions instead of permission plugin */
|
||||
private final boolean groupsByPermissions = TAB.getInstance().getConfiguration().getConfig().getBoolean("assign-groups-by-permissions", false);
|
||||
@Getter private final boolean groupsByPermissions = TAB.getInstance().getConfiguration().getConfig().getBoolean("assign-groups-by-permissions", false);
|
||||
|
||||
/** List of group permissions to iterate through if {@link #groupsByPermissions} is {@code true} */
|
||||
private final List<String> primaryGroupFindingList = TAB.getInstance().getConfiguration().getConfig().getStringList("primary-group-finding-list", Arrays.asList("Owner", "Admin", "Helper", "default"));
|
||||
@@ -84,23 +85,4 @@ public class GroupManager extends TabFeature {
|
||||
}
|
||||
return TabConstants.NO_GROUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if assigning by permissions is configured,
|
||||
* {@code false} if not.
|
||||
*
|
||||
* @return {@code true} if assigning by permissions, {@code false} if not
|
||||
*/
|
||||
public boolean isGroupsByPermissions() {
|
||||
return groupsByPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns detected permission plugin
|
||||
*
|
||||
* @return detected permission plugin
|
||||
*/
|
||||
public PermissionPlugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
|
||||
import me.neznamy.tab.api.protocol.*;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutChat.ChatMessageType;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.shared.event.impl.PlayerLoadEventImpl;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Abstract class storing common variables and functions for player,
|
||||
* which are not specific to any feature.
|
||||
@@ -24,10 +26,10 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
protected final Object player;
|
||||
|
||||
/** Player's real name */
|
||||
private final String name;
|
||||
@Getter private final String name;
|
||||
|
||||
/** Player's unique ID */
|
||||
private final UUID uniqueId;
|
||||
@Getter private final UUID uniqueId;
|
||||
|
||||
/** Player's tablist UUID */
|
||||
private final UUID tabListId;
|
||||
@@ -36,10 +38,10 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
* World the player is currently in, {@code "N/A"} if TAB is
|
||||
* installed on proxy and bukkit bridge is not installed
|
||||
*/
|
||||
private String world;
|
||||
@Getter @Setter private String world;
|
||||
|
||||
/** Server the player is currently in, {@code "N/A"} if TAB is installed on Bukkit */
|
||||
private String server;
|
||||
@Getter @Setter private String server;
|
||||
|
||||
/** Player's permission group defined in permission plugin or with permission nodes */
|
||||
private String permissionGroup;
|
||||
@@ -54,16 +56,16 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
private final Map<String, Property> properties = new HashMap<>();
|
||||
|
||||
/** Player's game version */
|
||||
protected final ProtocolVersion version;
|
||||
@Getter protected final ProtocolVersion version;
|
||||
|
||||
/** Player's network channel */
|
||||
protected Channel channel;
|
||||
@Getter @Setter protected Channel channel;
|
||||
|
||||
/**
|
||||
* Player's load status, {@code true} when player is fully loaded,
|
||||
* {@code false} if not yet
|
||||
*/
|
||||
private boolean onJoinFinished;
|
||||
@Getter private boolean loaded;
|
||||
|
||||
/** Scoreboard teams player has registered */
|
||||
private final Set<String> registeredTeams = new HashSet<>();
|
||||
@@ -72,7 +74,7 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
private final Set<String> registeredObjectives = new HashSet<>();
|
||||
|
||||
/** Player's name as seen in GameProfile, can be altered by nick plugins */
|
||||
private String nickname;
|
||||
@Getter @Setter private String nickname;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
@@ -147,7 +149,7 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
* {@code true} if this is a player join, {@code false} if reload
|
||||
*/
|
||||
public void markAsLoaded(boolean join) {
|
||||
onJoinFinished = true;
|
||||
loaded = true;
|
||||
if (TAB.getInstance().getEventBus() != null) TAB.getInstance().getEventBus().fire(new PlayerLoadEventImpl(this, join));
|
||||
}
|
||||
|
||||
@@ -157,34 +159,13 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
* @param permissionGroup
|
||||
* New permission group
|
||||
*/
|
||||
public void setGroup(String permissionGroup) {
|
||||
Preconditions.checkNotNull(permissionGroup, "permissionGroup");
|
||||
public void setGroup(@NonNull String permissionGroup) {
|
||||
if (this.permissionGroup.equals(permissionGroup)) return;
|
||||
this.permissionGroup = permissionGroup;
|
||||
((PlayerPlaceholder)TAB.getInstance().getPlaceholderManager().getPlaceholder(TabConstants.Placeholder.GROUP)).updateValue(this, permissionGroup);
|
||||
forceRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets player's world to given value
|
||||
*
|
||||
* @param name
|
||||
* Name of the new world
|
||||
*/
|
||||
public void setWorld(String name) {
|
||||
world = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets player's server to given value
|
||||
*
|
||||
* @param name
|
||||
* Name of the new server
|
||||
*/
|
||||
public void setServer(String name) {
|
||||
server = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears maps of registered teams and objectives when Login packet is sent
|
||||
*/
|
||||
@@ -227,16 +208,6 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
sendCustomPacket(new PacketPlayOutChat(message, ChatMessageType.SYSTEM));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getTablistUUID() {
|
||||
return tabListId;
|
||||
@@ -244,25 +215,10 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
|
||||
@Override
|
||||
public void forceRefresh() {
|
||||
if (!onJoinFinished) return;
|
||||
if (!loaded) return;
|
||||
TAB.getInstance().getFeatureManager().refresh(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolVersion getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendCustomPacket(TabPacket packet) {
|
||||
if (packet == null) return;
|
||||
@@ -347,16 +303,6 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
return temporaryGroup != null ? temporaryGroup : permissionGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return onJoinFinished;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadPropertyFromConfig(TabFeature feature, String property) {
|
||||
return loadPropertyFromConfig(feature, property, "");
|
||||
@@ -388,17 +334,4 @@ public abstract class ITabPlayer implements TabPlayer {
|
||||
if (sorting == null) return null;
|
||||
return sorting.getShortTeamName(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
}
|
||||
|
||||
public void setNickname(String nickname) {
|
||||
this.nickname = nickname;
|
||||
}
|
||||
|
||||
public void setChannel(Channel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,34 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.FeatureManager;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.protocol.PacketBuilder;
|
||||
import me.neznamy.tab.shared.config.Configs;
|
||||
import me.neznamy.tab.shared.features.*;
|
||||
import me.neznamy.tab.shared.features.alignedplayerlist.AlignedPlayerList;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.layout.LayoutManager;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import me.neznamy.tab.shared.permission.PermissionPlugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* An interface with methods that are called in universal code,
|
||||
* but require platform-specific API calls
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public abstract class Platform {
|
||||
|
||||
/** Platform's packet builder implementation */
|
||||
private final PacketBuilder packetBuilder;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param packetBuilder
|
||||
* Platform's packet builder
|
||||
*/
|
||||
protected Platform(PacketBuilder packetBuilder) {
|
||||
this.packetBuilder = packetBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns platform-specific packet builder implementation
|
||||
*
|
||||
* @return platform-specific packet builder
|
||||
*/
|
||||
public PacketBuilder getPacketBuilder(){
|
||||
return packetBuilder;
|
||||
}
|
||||
@Getter private final PacketBuilder packetBuilder;
|
||||
|
||||
public void sendConsoleMessage(String message, boolean translateColors) {
|
||||
Object logger = TAB.getInstance().getLogger();
|
||||
@@ -50,9 +47,101 @@ public abstract class Platform {
|
||||
public abstract PermissionPlugin detectPermissionPlugin();
|
||||
|
||||
/**
|
||||
* Loads platform-specific features
|
||||
* Loads features
|
||||
*/
|
||||
public abstract void loadFeatures();
|
||||
public void loadFeatures() {
|
||||
Configs configuration = TAB.getInstance().getConfiguration();
|
||||
FeatureManager featureManager = TAB.getInstance().getFeatureManager();
|
||||
|
||||
if (configuration.isPipelineInjection() && TAB.getInstance().getServerVersion().getMinorVersion() >= 8) {
|
||||
PipelineInjector inj = getPipelineInjector();
|
||||
if (inj != null) featureManager.registerFeature(TabConstants.Feature.PIPELINE_INJECTION, inj);
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.enabled", true)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
if (configuration.getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)
|
||||
&& TAB.getInstance().getServerVersion().getMinorVersion() >= 8) {
|
||||
featureManager.registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, getUnlimitedNametags());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("bossbar.enabled", false)) {
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR, new BossBarManagerImpl());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.BOSS_BAR, getLegacyBossBar());
|
||||
}
|
||||
}
|
||||
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 9 &&
|
||||
configuration.getConfig().getBoolean("fix-pet-names.enabled", false)) {
|
||||
TabFeature petFix = getPetFix();
|
||||
if (petFix != null) featureManager.registerFeature(TabConstants.Feature.PET_FIX, getPetFix());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("header-footer.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.HEADER_FOOTER, new HeaderFooter());
|
||||
|
||||
if (configuration.isRemoveGhostPlayers())
|
||||
featureManager.registerFeature(TabConstants.Feature.GHOST_PLAYER_FIX, new GhostPlayerFix());
|
||||
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 8 && configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true)) {
|
||||
if (configuration.getConfig().getBoolean("tablist-name-formatting.align-tabsuffix-on-the-right", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new AlignedPlayerList());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new PlayerList());
|
||||
}
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("ping-spoof.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.PING_SPOOF, new PingSpoof());
|
||||
|
||||
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber());
|
||||
|
||||
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
|
||||
|
||||
if (configuration.getConfig().getBoolean("belowname-objective.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName());
|
||||
|
||||
if (configuration.getConfig().getBoolean("scoreboard.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
|
||||
|
||||
if (TAB.getInstance().getServerVersion().getMinorVersion() >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
|
||||
if (TAB.getInstance().getTeamManager() == null) {
|
||||
//sorting is disabled, but layout needs team names
|
||||
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
}
|
||||
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("placeholders.register-tab-expansion", false)) {
|
||||
TAB.getInstance().getPlaceholderManager().setTabExpansion(getTabExpansion());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("global-playerlist.enabled", false)) {
|
||||
TabFeature global = getGlobalPlayerlist();
|
||||
if (global != null) featureManager.registerFeature(TabConstants.Feature.GLOBAL_PLAYER_LIST, getGlobalPlayerlist());
|
||||
}
|
||||
|
||||
if (configuration.getConfig().getBoolean("per-world-playerlist.enabled", false)) {
|
||||
TabFeature pwp = getPerWorldPlayerlist();
|
||||
if (pwp != null) featureManager.registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, pwp);
|
||||
}
|
||||
|
||||
RedisSupport redis = getRedisSupport();
|
||||
if (redis != null) TAB.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, redis);
|
||||
|
||||
featureManager.registerFeature(TabConstants.Feature.NICK_COMPATIBILITY, new NickCompatibility());
|
||||
}
|
||||
|
||||
public BossBarManagerImpl getLegacyBossBar() {
|
||||
return new BossBarManagerImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link me.neznamy.tab.api.placeholder.Placeholder}
|
||||
@@ -72,4 +161,29 @@ public abstract class Platform {
|
||||
* @return Version string if plugin is installed, {@code null} if not
|
||||
*/
|
||||
public abstract String getPluginVersion(String plugin);
|
||||
|
||||
/**
|
||||
* Creates instance for all online players and adds them to the plugin
|
||||
*/
|
||||
public abstract void loadPlayers();
|
||||
|
||||
/**
|
||||
* Registers all placeholders, including universal and platform-specific ones
|
||||
*/
|
||||
public abstract void registerPlaceholders();
|
||||
|
||||
public abstract @Nullable PipelineInjector getPipelineInjector();
|
||||
|
||||
public abstract NameTag getUnlimitedNametags();
|
||||
|
||||
public abstract TabExpansion getTabExpansion();
|
||||
|
||||
public abstract @Nullable TabFeature getPetFix();
|
||||
|
||||
public abstract @Nullable TabFeature getGlobalPlayerlist();
|
||||
|
||||
public abstract @Nullable RedisSupport getRedisSupport();
|
||||
|
||||
public abstract @Nullable TabFeature getPerWorldPlayerlist();
|
||||
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
package me.neznamy.tab.shared;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.bossbar.BossBarManager;
|
||||
import me.neznamy.tab.api.config.ConfigurationFile;
|
||||
import me.neznamy.tab.api.scoreboard.ScoreboardManager;
|
||||
import me.neznamy.tab.api.team.TeamManager;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import me.neznamy.tab.shared.command.DisabledCommand;
|
||||
import me.neznamy.tab.shared.command.TabCommand;
|
||||
import me.neznamy.tab.shared.config.Configs;
|
||||
import me.neznamy.tab.shared.event.EventBusImpl;
|
||||
import me.neznamy.tab.shared.event.impl.TabLoadEventImpl;
|
||||
import me.neznamy.tab.shared.features.*;
|
||||
import me.neznamy.tab.shared.features.alignedplayerlist.AlignedPlayerList;
|
||||
import me.neznamy.tab.shared.features.layout.LayoutManager;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
|
||||
import me.neznamy.tab.shared.features.sorting.Sorting;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
import java.io.File;
|
||||
@@ -29,7 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class TAB extends TabAPI {
|
||||
|
||||
/** Instance of this class */
|
||||
private static TAB instance;
|
||||
@Getter @Setter private static TAB instance;
|
||||
|
||||
/** Player data storage */
|
||||
private final Map<UUID, TabPlayer> data = new ConcurrentHashMap<>();
|
||||
@@ -38,16 +37,16 @@ public class TAB extends TabAPI {
|
||||
private final Map<UUID, TabPlayer> playersByTabListId = new ConcurrentHashMap<>();
|
||||
|
||||
/** Online player array to avoid memory allocation when iterating */
|
||||
private TabPlayer[] players = new TabPlayer[0];
|
||||
@Getter private TabPlayer[] onlinePlayers = new TabPlayer[0];
|
||||
|
||||
/** Instance of plugin's main command */
|
||||
private TabCommand command;
|
||||
@Getter private TabCommand command;
|
||||
|
||||
/** Command executor to use when the plugin is disabled due to an error */
|
||||
private final DisabledCommand disabledCommand = new DisabledCommand();
|
||||
@Getter private final DisabledCommand disabledCommand = new DisabledCommand();
|
||||
|
||||
/** Implementation of platform the plugin is installed on for platform-specific calls */
|
||||
private final Platform platform;
|
||||
@Getter private final Platform platform;
|
||||
|
||||
/**
|
||||
* CPU manager for thread and task management as well as
|
||||
@@ -60,44 +59,44 @@ public class TAB extends TabAPI {
|
||||
* Platform-independent event executor allowing other plugins
|
||||
* to listen to universal platform-independent event objects
|
||||
*/
|
||||
private EventBusImpl eventBus;
|
||||
@Getter private EventBusImpl eventBus;
|
||||
|
||||
/**
|
||||
* Error manager for printing any and all errors that may
|
||||
* occur in any part of the code including hooks into other plugins
|
||||
* into files instead of flooding the already flooded console.
|
||||
*/
|
||||
private ErrorManager errorManager;
|
||||
@Getter private ErrorManager errorManager;
|
||||
|
||||
/** Feature manager forwarding events into all loaded features */
|
||||
private FeatureManagerImpl featureManager;
|
||||
@Getter private FeatureManagerImpl featureManager;
|
||||
|
||||
/** Plugin's configuration files and values storage */
|
||||
private Configs configuration;
|
||||
@Getter private Configs configuration;
|
||||
|
||||
/**
|
||||
* Boolean tracking whether this plugin is enabled or not,
|
||||
* which is due to either internal error on load or yaml syntax error
|
||||
*/
|
||||
private boolean disabled;
|
||||
@Getter private boolean pluginDisabled;
|
||||
|
||||
/** Minecraft version the server is running on, always using the latest on proxies */
|
||||
private final ProtocolVersion serverVersion;
|
||||
@Getter private final ProtocolVersion serverVersion;
|
||||
|
||||
/** Boolean checking floodgate plugin presence for hook */
|
||||
private boolean floodgate;
|
||||
@Getter private final boolean floodgateInstalled = ReflectionUtils.classExists("org.geysermc.floodgate.api.FloodgateApi");
|
||||
|
||||
/** Version string defined by the server */
|
||||
private final String serverVersionString;
|
||||
@Getter private final String serverVersionString;
|
||||
|
||||
/** TAB's data folder */
|
||||
private final File dataFolder;
|
||||
@Getter private final File dataFolder;
|
||||
|
||||
/** Plugin's console logger provided by platform */
|
||||
private final Object logger;
|
||||
@Getter private final Object logger;
|
||||
|
||||
/** File with YAML syntax error, which prevented plugin from loading */
|
||||
private String brokenFile;
|
||||
@Getter @Setter private String brokenFile;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and sets this
|
||||
@@ -114,12 +113,6 @@ public class TAB extends TabAPI {
|
||||
this.serverVersionString = serverVersionString;
|
||||
this.dataFolder = dataFolder;
|
||||
this.logger = logger;
|
||||
try {
|
||||
Class.forName("org.geysermc.floodgate.api.FloodgateApi");
|
||||
floodgate = true;
|
||||
} catch (ClassNotFoundException | IllegalStateException e) {
|
||||
//plugin not installed
|
||||
}
|
||||
try {
|
||||
eventBus = new EventBusImpl();
|
||||
} catch (NoSuchMethodError e) {
|
||||
@@ -154,13 +147,15 @@ public class TAB extends TabAPI {
|
||||
featureManager = new FeatureManagerImpl();
|
||||
featureManager.registerFeature(TabConstants.Feature.PLACEHOLDER_MANAGER, new PlaceholderManagerImpl());
|
||||
featureManager.registerFeature(TabConstants.Feature.GROUP_MANAGER, new GroupManager(platform.detectPermissionPlugin()));
|
||||
platform.registerPlaceholders();
|
||||
platform.loadFeatures();
|
||||
platform.loadPlayers();
|
||||
command = new TabCommand();
|
||||
featureManager.load();
|
||||
for (TabPlayer p : players) ((ITabPlayer)p).markAsLoaded(false);
|
||||
for (TabPlayer p : onlinePlayers) ((ITabPlayer)p).markAsLoaded(false);
|
||||
cpu.enable();
|
||||
if (eventBus != null) eventBus.fire(TabLoadEventImpl.getInstance());
|
||||
disabled = false;
|
||||
pluginDisabled = false;
|
||||
sendConsoleMessage("&aEnabled in " + (System.currentTimeMillis()-time) + "ms", true);
|
||||
return configuration.getMessages().getReloadSuccess();
|
||||
} catch (YAMLException e) {
|
||||
@@ -180,7 +175,7 @@ public class TAB extends TabAPI {
|
||||
* and cancels all tasks.
|
||||
*/
|
||||
public void unload() {
|
||||
if (disabled) return;
|
||||
if (pluginDisabled) return;
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
if (configuration.getMysql() != null) configuration.getMysql().closeConnection();
|
||||
@@ -196,48 +191,13 @@ public class TAB extends TabAPI {
|
||||
* Clears online player maps and arrays and cancels all tasks
|
||||
*/
|
||||
private void kill() {
|
||||
disabled = true;
|
||||
pluginDisabled = true;
|
||||
data.clear();
|
||||
playersByTabListId.clear();
|
||||
players = new TabPlayer[0];
|
||||
onlinePlayers = new TabPlayer[0];
|
||||
cpu.cancelAllTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads universal features present on all platforms with the same configuration
|
||||
*/
|
||||
public void loadUniversalFeatures() {
|
||||
if (configuration.getConfig().getBoolean("header-footer.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.HEADER_FOOTER, new HeaderFooter());
|
||||
if (configuration.isRemoveGhostPlayers())
|
||||
featureManager.registerFeature(TabConstants.Feature.GHOST_PLAYER_FIX, new GhostPlayerFix());
|
||||
if (serverVersion.getMinorVersion() >= 8 && configuration.getConfig().getBoolean("tablist-name-formatting.enabled", true)) {
|
||||
if (configuration.getConfig().getBoolean("tablist-name-formatting.align-tabsuffix-on-the-right", false)) {
|
||||
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new AlignedPlayerList());
|
||||
} else {
|
||||
featureManager.registerFeature(TabConstants.Feature.PLAYER_LIST, new PlayerList());
|
||||
}
|
||||
}
|
||||
if (configuration.getConfig().getBoolean("ping-spoof.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.PING_SPOOF, new PingSpoof());
|
||||
if (configuration.getConfig().getBoolean("yellow-number-in-tablist.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.YELLOW_NUMBER, new YellowNumber());
|
||||
if (configuration.getConfig().getBoolean("prevent-spectator-effect.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SPECTATOR_FIX, new SpectatorFix());
|
||||
if (configuration.getConfig().getBoolean("belowname-objective.enabled", true))
|
||||
featureManager.registerFeature(TabConstants.Feature.BELOW_NAME, new BelowName());
|
||||
if (configuration.getConfig().getBoolean("scoreboard.enabled", false))
|
||||
featureManager.registerFeature(TabConstants.Feature.SCOREBOARD, new ScoreboardManagerImpl());
|
||||
if (serverVersion.getMinorVersion() >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
|
||||
if (getTeamManager() == null) {
|
||||
//sorting is disabled, but layout needs team names
|
||||
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
|
||||
}
|
||||
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
|
||||
}
|
||||
featureManager.registerFeature(TabConstants.Feature.NICK_COMPATIBILITY, new NickCompatibility());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds specified player to online players
|
||||
*
|
||||
@@ -247,7 +207,7 @@ public class TAB extends TabAPI {
|
||||
public void addPlayer(TabPlayer player) {
|
||||
data.put(player.getUniqueId(), player);
|
||||
playersByTabListId.put(player.getTablistUUID(), player);
|
||||
players = data.values().toArray(new TabPlayer[0]);
|
||||
onlinePlayers = data.values().toArray(new TabPlayer[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,35 +219,7 @@ public class TAB extends TabAPI {
|
||||
public void removePlayer(TabPlayer player) {
|
||||
data.remove(player.getUniqueId());
|
||||
playersByTabListId.remove(player.getTablistUUID());
|
||||
players = data.values().toArray(new TabPlayer[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of this class
|
||||
*
|
||||
* @return instance of this class
|
||||
*/
|
||||
public static TAB getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes instance of this class to new value
|
||||
*
|
||||
* @param instance
|
||||
* Instance to set variable to
|
||||
*/
|
||||
public static void setInstance(TAB instance) {
|
||||
TAB.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if floodgate plugin is installed, {@code false} if not
|
||||
*
|
||||
* @return {@code true} if floodgate plugin is installed, {@code false} if not
|
||||
*/
|
||||
public boolean isFloodgateInstalled() {
|
||||
return floodgate;
|
||||
onlinePlayers = data.values().toArray(new TabPlayer[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,15 +231,6 @@ public class TAB extends TabAPI {
|
||||
return (GroupManager) featureManager.getFeature(TabConstants.Feature.GROUP_MANAGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #platform}
|
||||
*
|
||||
* @return {@link #platform}
|
||||
*/
|
||||
public Platform getPlatform() {
|
||||
return platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #cpu}
|
||||
*
|
||||
@@ -317,79 +240,6 @@ public class TAB extends TabAPI {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #errorManager}
|
||||
*
|
||||
* @return {@link #errorManager}
|
||||
*/
|
||||
public ErrorManager getErrorManager() {
|
||||
return errorManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #configuration}
|
||||
*
|
||||
* @return {@link #configuration}
|
||||
*/
|
||||
public Configs getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #command}
|
||||
*
|
||||
* @return {@link #command}
|
||||
*/
|
||||
public TabCommand getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #disabledCommand}
|
||||
*
|
||||
* @return {@link #disabledCommand}
|
||||
*/
|
||||
public DisabledCommand getDisabledCommand() {
|
||||
return disabledCommand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #serverVersionString}
|
||||
*
|
||||
* @return {@link #serverVersionString}
|
||||
*/
|
||||
public String getServerVersionString() {
|
||||
return serverVersionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #dataFolder}
|
||||
*
|
||||
* @return {@link #dataFolder}
|
||||
*/
|
||||
public File getDataFolder() {
|
||||
return dataFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #logger}
|
||||
*
|
||||
* @return {@link #logger}
|
||||
*/
|
||||
public Object getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventBusImpl getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolVersion getServerVersion() {
|
||||
return serverVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBarManager getBossBarManager() {
|
||||
return (BossBarManager) featureManager.getFeature(TabConstants.Feature.BOSS_BAR);
|
||||
@@ -464,33 +314,13 @@ public class TAB extends TabAPI {
|
||||
errorManager.printError(message, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBrokenFile(String file) {
|
||||
this.brokenFile = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TablistFormatManager getTablistFormatManager() {
|
||||
return (TablistFormatManager) featureManager.getFeature(TabConstants.Feature.PLAYER_LIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabPlayer[] getOnlinePlayers(){
|
||||
return players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureManagerImpl getFeatureManager() {
|
||||
return featureManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (configuration != null && configuration.isDebugMode()) sendConsoleMessage("&9[DEBUG] " + message, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginDisabled() {
|
||||
return disabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.neznamy.tab.shared.backend;
|
||||
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.protocol.PacketBuilder;
|
||||
import me.neznamy.tab.shared.Platform;
|
||||
import me.neznamy.tab.shared.features.redis.RedisSupport;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class BackendPlatform extends Platform {
|
||||
|
||||
public BackendPlatform(PacketBuilder packetBuilder) {
|
||||
super(packetBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TabFeature getGlobalPlayerlist() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RedisSupport getRedisSupport() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.Property;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class BackendArmorStand implements ArmorStand {
|
||||
|
||||
/** Entity id counter to pick unique entity ID for each armor stand */
|
||||
private static int idCounter = 2000000000;
|
||||
|
||||
/** NameTag feature */
|
||||
protected final BackendNameTagX manager;
|
||||
|
||||
/** Armor stand manager which this armor stand belongs to */
|
||||
protected final BackendArmorStandManager asm;
|
||||
|
||||
/** Owner of the armor stand */
|
||||
protected final TabPlayer owner;
|
||||
|
||||
/** Offset in blocks, 0 for original height */
|
||||
@Getter protected double offset;
|
||||
|
||||
/** If offset is static, or dynamic based on other armor stands */
|
||||
@Getter private final boolean staticOffset;
|
||||
|
||||
/** Entity ID of this armor stand */
|
||||
@Getter protected final int entityId = idCounter++;
|
||||
|
||||
/** Unique ID of this armor stand */
|
||||
protected final UUID uuid = UUID.randomUUID();
|
||||
|
||||
/** Sneaking flag of armor stands */
|
||||
protected boolean sneaking;
|
||||
|
||||
/** Armor stand visibility to use in metadata */
|
||||
protected boolean visible;
|
||||
|
||||
/** Visibility flag to compare against for previous value, including invisibility potion compensation */
|
||||
private boolean visibleWPotion;
|
||||
|
||||
/** Refresh property dedicated to this armor stand */
|
||||
@Getter protected final Property property;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param asm
|
||||
* Armor stand manager which this armor stand belongs to
|
||||
* @param owner
|
||||
* Owner of the armor stand
|
||||
* @param propertyName
|
||||
* Name of refresh property to use
|
||||
* @param yOffset
|
||||
* Offset in blocks
|
||||
* @param staticOffset
|
||||
* {@code true} if offset is static, {@code false} if not
|
||||
*/
|
||||
public BackendArmorStand(BackendNameTagX feature, BackendArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
this.manager = feature;
|
||||
this.asm = asm;
|
||||
this.owner = owner;
|
||||
this.staticOffset = staticOffset;
|
||||
this.offset = yOffset;
|
||||
this.property = owner.getProperty(propertyName);
|
||||
visible = calculateVisibility();
|
||||
visibleWPotion = visible && !owner.hasInvisibilityPotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(double offset) {
|
||||
if (this.offset == offset) return;
|
||||
this.offset = offset;
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
sendTeleportPacket(all);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
visible = calculateVisibility();
|
||||
visibleWPotion = visible && !owner.hasInvisibilityPotion();
|
||||
updateMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateVisibility(boolean force) {
|
||||
boolean visibility = calculateVisibility() && !owner.hasInvisibilityPotion(); //trigger packet send but don't save, so it can be reverted if viewer is spectator
|
||||
if (visibleWPotion != visibility || force) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sneak(boolean sneaking) {
|
||||
if (this.sneaking == sneaking) return; //idk
|
||||
this.sneaking = sneaking;
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !manager.isArmorStandsAlwaysVisible()) {
|
||||
//1.14.x client sided bug, de-spawning completely
|
||||
if (sneaking) {
|
||||
destroy(viewer);
|
||||
} else {
|
||||
spawn(viewer);
|
||||
}
|
||||
} else {
|
||||
//respawning so there's no animation and it's instant
|
||||
respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers())
|
||||
destroy(all);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
sendTeleportPacket(all);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(TabPlayer viewer) {
|
||||
if (!asm.isNearby(viewer) && viewer != owner) {
|
||||
asm.spawn(viewer);
|
||||
} else {
|
||||
sendTeleportPacket(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general visibility rule for everyone with limited info
|
||||
*
|
||||
* @return {@code true} if armor stand should be visible, {@code false} if not
|
||||
*/
|
||||
public boolean calculateVisibility() {
|
||||
if (manager.isArmorStandsAlwaysVisible()) return true;
|
||||
if (owner.isDisguised() || manager.isOnBoat(owner)) return false;
|
||||
return owner.getGamemode() != 3 && !manager.hasHiddenNametag(owner) && property.get().length() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if display name is in fact empty,
|
||||
* for example only containing color codes, {@code false} if not.
|
||||
*
|
||||
* @param displayName
|
||||
* string to check
|
||||
* @return {@code true} if it's empty, {@code false} if not
|
||||
*/
|
||||
protected boolean isNameVisiblyEmpty(String displayName) {
|
||||
if (displayName.length() == 0) return true;
|
||||
if (!displayName.startsWith(EnumChatFormat.COLOR_STRING) && !displayName.startsWith("&") && !displayName.startsWith("#")) return false;
|
||||
String text = IChatBaseComponent.fromColoredText(displayName).toRawText();
|
||||
if (text.contains(" ")) text = text.replace(" ", "");
|
||||
return text.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Y to add based on player's pose, armor stand offset and viewer
|
||||
*
|
||||
* @param sleeping
|
||||
* Whether player is sleeping or not
|
||||
* @param sneaking
|
||||
* Whether player is sneakinig or not
|
||||
* @param viewer
|
||||
* Viewer of the armor stand
|
||||
* @return Y to add to player's location
|
||||
*/
|
||||
public double getYAdd(boolean sleeping, boolean sneaking, TabPlayer viewer) {
|
||||
double y = getOffset();
|
||||
if (!sleeping) {
|
||||
if (sneaking) {
|
||||
if (viewer.getVersion().getMinorVersion() >= 15) {
|
||||
y += 1.37;
|
||||
} else if (viewer.getVersion().getMinorVersion() >= 9) {
|
||||
y += 1.52;
|
||||
} else {
|
||||
y += 1.7;
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 1.8 : 1.84; // Normal
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 0.2 : 0.26; // Sleeping
|
||||
}
|
||||
if (viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x()) y -= 2;
|
||||
return y;
|
||||
}
|
||||
|
||||
public void respawn(TabPlayer viewer) {
|
||||
destroy(viewer);
|
||||
// 1.8.0 will not see entity that respawned in the same tick
|
||||
// creating new delayed task every time someone sneaks can be abused and cause OOM
|
||||
spawn(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand's metadata for everyone
|
||||
*/
|
||||
public void updateMetadata() {
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
updateMetadata(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldBeInvisibleFor(TabPlayer viewer, String displayName) {
|
||||
return isNameVisiblyEmpty(displayName) || !manager.canSee(viewer, owner) ||
|
||||
manager.hasHiddenNametag(owner, viewer) || manager.hasHiddenNameTagVisibilityView(viewer) ||
|
||||
(owner.hasInvisibilityPotion() && viewer.getGamemode() != 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand's metadata for specified viewer
|
||||
*/
|
||||
public abstract void updateMetadata(TabPlayer viewer);
|
||||
|
||||
public abstract void sendTeleportPacket(TabPlayer viewer);
|
||||
}
|
||||
+28
-33
@@ -1,9 +1,10 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.ArmorStand;
|
||||
import me.neznamy.tab.api.ArmorStandManager;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -11,22 +12,20 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A helper class for easy management of armor stands of a player
|
||||
*/
|
||||
public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
public class BackendArmorStandManager implements ArmorStandManager {
|
||||
|
||||
/** Map of registered armor stands by name */
|
||||
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
|
||||
private final Map<String, BackendArmorStand> armorStands = new LinkedHashMap<>();
|
||||
|
||||
/** Armor stands in an array for speed while iterating */
|
||||
private ArmorStand[] armorStandArray = new ArmorStand[0];
|
||||
private BackendArmorStand[] armorStandArray = new BackendArmorStand[0];
|
||||
|
||||
/** Players in entity tracking range of owner */
|
||||
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
|
||||
private final List<TabPlayer> nearbyPlayerList = new ArrayList<>();
|
||||
|
||||
/** Nearby players in an array for speed while iterating */
|
||||
private TabPlayer[] nearbyPlayerArray = new TabPlayer[0];
|
||||
@Getter
|
||||
private TabPlayer[] nearbyPlayers = new TabPlayer[0];
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads armor stands.
|
||||
@@ -36,17 +35,17 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
* @param owner
|
||||
* Owner of this armor stand manager
|
||||
*/
|
||||
public BukkitArmorStandManager(NameTagX nameTagX, TabPlayer owner) {
|
||||
public BackendArmorStandManager(NameTagX nameTagX, TabPlayer owner) {
|
||||
owner.setProperty(nameTagX, TabConstants.Property.NAMETAG, owner.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue()
|
||||
+ owner.getProperty(TabConstants.Property.TAGSUFFIX).getCurrentRawValue());
|
||||
double height = 0;
|
||||
for (String line : nameTagX.getDynamicLines()) {
|
||||
addArmorStand(line, new BukkitArmorStand(this, owner, line, height, false));
|
||||
addArmorStand(line, ((BackendNameTagX)nameTagX).createArmorStand(this, owner, line, height, false));
|
||||
height += 0.26;
|
||||
}
|
||||
for (Map.Entry<String, Object> line : nameTagX.getStaticLines().entrySet()) {
|
||||
addArmorStand(line.getKey(), new BukkitArmorStand(this, owner, line.getKey(), Double.parseDouble(line.getValue().toString()), true));
|
||||
addArmorStand(line.getKey(), ((BackendNameTagX)nameTagX).createArmorStand(this, owner, line.getKey(), Double.parseDouble(line.getValue().toString()), true));
|
||||
}
|
||||
fixArmorStandHeights();
|
||||
}
|
||||
@@ -68,14 +67,6 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
for (ArmorStand a : armorStandArray) a.teleport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of nearby players
|
||||
* @return array of nearby players
|
||||
*/
|
||||
public TabPlayer[] getNearbyPlayers(){
|
||||
return nearbyPlayerArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if requested player is nearby, {@code false} if not
|
||||
*
|
||||
@@ -84,7 +75,7 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
* @return {@code true} if player nearby, {@code false} if not
|
||||
*/
|
||||
public boolean isNearby(TabPlayer viewer) {
|
||||
return nearbyPlayers.contains(viewer);
|
||||
return nearbyPlayerList.contains(viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +109,7 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
*/
|
||||
public void respawn() {
|
||||
for (ArmorStand a : armorStandArray) {
|
||||
for (TabPlayer viewer : nearbyPlayerArray) {
|
||||
for (TabPlayer viewer : nearbyPlayers) {
|
||||
a.respawn(viewer);
|
||||
}
|
||||
}
|
||||
@@ -131,8 +122,8 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
* player to spawn armor stands for
|
||||
*/
|
||||
public void spawn(TabPlayer viewer) {
|
||||
nearbyPlayers.add(viewer);
|
||||
nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
|
||||
nearbyPlayerList.add(viewer);
|
||||
nearbyPlayers = nearbyPlayerList.toArray(new TabPlayer[0]);
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
for (ArmorStand a : armorStandArray) a.spawn(viewer);
|
||||
}
|
||||
@@ -143,7 +134,7 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
public void fixArmorStandHeights() {
|
||||
double currentY = -0.26;
|
||||
for (ArmorStand as : armorStandArray) {
|
||||
if (as.hasStaticOffset()) continue;
|
||||
if (as.isStaticOffset()) continue;
|
||||
if (as.getProperty().get().length() != 0) {
|
||||
currentY += 0.26;
|
||||
as.setOffset(currentY);
|
||||
@@ -159,10 +150,10 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
* @param as
|
||||
* Armor stand to add
|
||||
*/
|
||||
public void addArmorStand(String name, ArmorStand as) {
|
||||
public void addArmorStand(String name, BackendArmorStand as) {
|
||||
armorStands.put(name, as);
|
||||
armorStandArray = armorStands.values().toArray(new ArmorStand[0]);
|
||||
for (TabPlayer p : nearbyPlayerArray) as.spawn(p);
|
||||
armorStandArray = armorStands.values().toArray(new BackendArmorStand[0]);
|
||||
for (TabPlayer p : nearbyPlayers) as.spawn(p);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,7 +163,7 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
* player to remove
|
||||
*/
|
||||
public void unregisterPlayer(TabPlayer viewer) {
|
||||
if (nearbyPlayers.remove(viewer)) nearbyPlayerArray = nearbyPlayers.toArray(new TabPlayer[0]);
|
||||
if (nearbyPlayerList.remove(viewer)) nearbyPlayers = nearbyPlayerList.toArray(new TabPlayer[0]);
|
||||
}
|
||||
|
||||
public void updateVisibility(boolean force) {
|
||||
@@ -193,8 +184,8 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (ArmorStand a : armorStandArray) a.destroy();
|
||||
nearbyPlayers.clear();
|
||||
nearbyPlayerArray = new TabPlayer[0];
|
||||
nearbyPlayerList.clear();
|
||||
nearbyPlayers = new TabPlayer[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,4 +199,8 @@ public class BukkitArmorStandManager implements ArmorStandManager {
|
||||
}
|
||||
if (fix) fixArmorStandHeights();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMetadata(TabPlayer viewer) {
|
||||
for (BackendArmorStand a : armorStandArray) a.updateMetadata(viewer);
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BackendNameTagX extends NameTagX {
|
||||
|
||||
/** Vehicle manager reference */
|
||||
@Getter private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
|
||||
|
||||
/** Packet Listener reference */
|
||||
protected final PacketListener packetListener = new PacketListener(this);
|
||||
|
||||
public BackendNameTagX() {
|
||||
super(BackendArmorStandManager::new);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, packetListener);
|
||||
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder("%gamemode%", 500, TabPlayer::getGamemode);
|
||||
TabFeature gamemode = new TabFeature("Unlimited NameTags", "Gamemode listener") {
|
||||
{
|
||||
addUsedPlaceholders(Collections.singletonList("%gamemode%"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(TabPlayer viewer, boolean force) {
|
||||
for (TabPlayer target : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(target).updateMetadata(viewer);
|
||||
}
|
||||
}
|
||||
};
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_GAMEMODE_LISTENER, gamemode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts task checking for player visibility to hide armor stands of invisible players.
|
||||
*/
|
||||
private void startVisibilityRefreshTask() {
|
||||
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
|
||||
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(p)) continue;
|
||||
getArmorStandManager(p).updateVisibility(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendArmorStandManager getArmorStandManager(TabPlayer player) {
|
||||
return (BackendArmorStandManager) armorStandManagerMap.get(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, all);
|
||||
}
|
||||
}
|
||||
startVisibilityRefreshTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
unregisterListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
super.onJoin(connectedPlayer);
|
||||
if (isPlayerDisabled(connectedPlayer)) return;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, connectedPlayer);
|
||||
spawnArmorStands(connectedPlayer, viewer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnBoat(TabPlayer player) {
|
||||
return vehicleManager != null && vehicleManager.isOnBoat(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns armor stands of target player to viewer if all requirements are met.
|
||||
* These include players being in the same world, distance being less than 48 blocks
|
||||
* and target player being visible to viewer.
|
||||
*
|
||||
* @param viewer
|
||||
* Player viewing armor stands
|
||||
* @param target
|
||||
* Target player with armor stands
|
||||
*/
|
||||
private void spawnArmorStands(TabPlayer viewer, TabPlayer target) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
if (target == viewer || isPlayerDisabled(target)) return;
|
||||
if (!areInSameWorld(viewer, target)) return;
|
||||
if (getDistance(viewer, target) <= 48 && canSee(viewer, target) && !target.isVanished())
|
||||
getArmorStandManager(target).spawn(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).unregisterPlayer(disconnectedPlayer);
|
||||
}
|
||||
armorStandManagerMap.get(disconnectedPlayer).destroy();
|
||||
armorStandManagerMap.remove(disconnectedPlayer); // WeakHashMap doesn't clear this due to value referencing the key
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeArmorStands(TabPlayer player) {
|
||||
if (isPlayerDisabled(player)) return;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNameTagPreview(TabPlayer player, boolean status) {
|
||||
if (status) {
|
||||
getArmorStandManager(player).spawn(player);
|
||||
} else {
|
||||
getArmorStandManager(player).destroy(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseArmorStands(TabPlayer player) {
|
||||
getArmorStandManager(player).destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNameTagVisibilityView(TabPlayer player) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).updateVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
super.onWorldChange(p, from, to);
|
||||
if (isUnlimitedDisabled(p.getServer(), to)) {
|
||||
getDisabledUnlimitedPlayers().add(p);
|
||||
updateTeamData(p);
|
||||
} else if (getDisabledUnlimitedPlayers().remove(p)) {
|
||||
updateTeamData(p);
|
||||
}
|
||||
if (isPreviewingNametag(p)) {
|
||||
getArmorStandManager(p).spawn(p);
|
||||
}
|
||||
//for some reason this is needed for some users
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getWorld().equals(from)) {
|
||||
getArmorStandManager(p).destroy(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getEntityId(TabPlayer player) {
|
||||
return getEntityId(player.getPlayer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns flat distance between two players ignoring Y value
|
||||
*
|
||||
* @param player1
|
||||
* first player
|
||||
* @param player2
|
||||
* second player
|
||||
* @return flat distance in blocks
|
||||
*/
|
||||
public abstract double getDistance(TabPlayer player1, TabPlayer player2);
|
||||
|
||||
public abstract boolean areInSameWorld(TabPlayer player1, TabPlayer player2);
|
||||
|
||||
public abstract boolean canSee(TabPlayer viewer, TabPlayer target);
|
||||
|
||||
public abstract void unregisterListener();
|
||||
|
||||
public abstract List<Integer> getPassengers(Object vehicle);
|
||||
|
||||
public abstract void registerVehiclePlaceholder();
|
||||
|
||||
public abstract Object getVehicle(TabPlayer player);
|
||||
|
||||
public abstract int getEntityId(Object entity);
|
||||
|
||||
public abstract String getEntityType(Object entity);
|
||||
|
||||
public abstract BackendArmorStand createArmorStand(BackendArmorStandManager feature, TabPlayer owner, String lineName, double yOffset, boolean staticOffset);
|
||||
}
|
||||
+31
-81
@@ -1,20 +1,18 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
package me.neznamy.tab.shared.backend.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* The packet listening part for securing proper functionality of armor stands.
|
||||
* Bukkit events are too unreliable and delayed/ahead which causes de-sync
|
||||
* Events are too unreliable and delayed/ahead which causes de-sync
|
||||
* if trying to listen to move event.
|
||||
* For entering/leaving tracking range there are no events and
|
||||
* periodic / move-triggered distance checks would cause high CPU usage.
|
||||
@@ -22,13 +20,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class PacketListener extends TabFeature {
|
||||
|
||||
/** Reference to the main feature */
|
||||
private final BukkitNameTagX nameTagX;
|
||||
protected final BackendNameTagX nameTagX;
|
||||
|
||||
/** A player map by entity id, used for better performance */
|
||||
private final Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
/** Reference to NMS storage for quick access */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
@@ -36,7 +31,7 @@ public class PacketListener extends TabFeature {
|
||||
* @param nameTagX
|
||||
* main feature
|
||||
*/
|
||||
public PacketListener(BukkitNameTagX nameTagX) {
|
||||
public PacketListener(BackendNameTagX nameTagX) {
|
||||
super(nameTagX.getFeatureName(), null);
|
||||
this.nameTagX = nameTagX;
|
||||
}
|
||||
@@ -44,61 +39,34 @@ public class PacketListener extends TabFeature {
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
entityIdMap.put(((Player) all.getPlayer()).getEntityId(), all);
|
||||
entityIdMap.put(nameTagX.getEntityId(all), all);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
entityIdMap.put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
|
||||
entityIdMap.put(nameTagX.getEntityId(connectedPlayer), connectedPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
entityIdMap.remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
|
||||
entityIdMap.remove(nameTagX.getEntityId(disconnectedPlayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
if (sender.getVersion().getMinorVersion() == 8 && nms.PacketPlayInUseEntity.isInstance(packet)) {
|
||||
int entityId = nms.PacketPlayInUseEntity_ENTITY.getInt(packet);
|
||||
TabPlayer attacked = null;
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (all.isLoaded() && nameTagX.getArmorStandManager(all).hasArmorStandWithID(entityId)) {
|
||||
attacked = all;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked != sender) {
|
||||
nms.setField(packet, nms.PacketPlayInUseEntity_ENTITY, ((Player) attacked.getPlayer()).getEntityId());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return;
|
||||
if (!receiver.isLoaded() || nameTagX.isDisabledPlayer(receiver) || nameTagX.getDisabledUnlimitedPlayers().contains(receiver)) return;
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
|
||||
onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityTeleport.isInstance(packet)) {
|
||||
onEntityMove(receiver, nms.PacketPlayOutEntityTeleport_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
|
||||
onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityDestroy.isInstance(packet)) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
Object entities = nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet);
|
||||
if (entities instanceof List) {
|
||||
onEntityDestroy(receiver, (List<Integer>) entities);
|
||||
} else {
|
||||
//1.17.0
|
||||
onEntityDestroy(receiver, (int) entities);
|
||||
}
|
||||
} else {
|
||||
onEntityDestroy(receiver, (int[]) nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet));
|
||||
}
|
||||
/**
|
||||
* Processes named entity spawn packet and spawns armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entityId
|
||||
* spawned entity
|
||||
*/
|
||||
public void onEntitySpawn(TabPlayer receiver, int entityId) {
|
||||
TabPlayer spawnedPlayer = entityIdMap.get(entityId);
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(spawnedPlayer)) {
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_SPAWN,
|
||||
() -> nameTagX.getArmorStandManager(spawnedPlayer).spawn(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,18 +81,17 @@ public class PacketListener extends TabFeature {
|
||||
* @param entityId
|
||||
* entity that moved
|
||||
*/
|
||||
private void onEntityMove(TabPlayer receiver, int entityId) {
|
||||
public void onEntityMove(TabPlayer receiver, int entityId) {
|
||||
TabPlayer pl = entityIdMap.get(entityId);
|
||||
List<Entity> vehicleList;
|
||||
if (pl != null) {
|
||||
//player moved
|
||||
if (nameTagX.isPlayerDisabled(pl) || !pl.isLoaded()) return;
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE,
|
||||
() -> nameTagX.getArmorStandManager(pl).teleport(receiver));
|
||||
} else if ((vehicleList = nameTagX.getVehicleManager().getVehicles().get(entityId)) != null){
|
||||
} else {
|
||||
//a vehicle carrying something moved
|
||||
for (Entity entity : vehicleList) {
|
||||
TabPlayer passenger = entityIdMap.get(entity.getEntityId());
|
||||
for (Integer entity : nameTagX.getVehicleManager().getVehicles().getOrDefault(entityId, Collections.emptyList())) {
|
||||
TabPlayer passenger = entityIdMap.get(entity);
|
||||
if (passenger != null && nameTagX.getArmorStandManager(passenger) != null) {
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE_PASSENGER,
|
||||
() -> nameTagX.getArmorStandManager(passenger).teleport(receiver));
|
||||
@@ -133,23 +100,6 @@ public class PacketListener extends TabFeature {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes named entity spawn packet and spawns armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entityId
|
||||
* spawned entity
|
||||
*/
|
||||
private void onEntitySpawn(TabPlayer receiver, int entityId) {
|
||||
TabPlayer spawnedPlayer = entityIdMap.get(entityId);
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(spawnedPlayer)) {
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_SPAWN,
|
||||
() -> nameTagX.getArmorStandManager(spawnedPlayer).spawn(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes entity destroy packet and destroys armor stands if
|
||||
* entity ID belongs to an online player.
|
||||
@@ -159,7 +109,7 @@ public class PacketListener extends TabFeature {
|
||||
* @param entities
|
||||
* de-spawned entities
|
||||
*/
|
||||
private void onEntityDestroy(TabPlayer receiver, List<Integer> entities) {
|
||||
public void onEntityDestroy(TabPlayer receiver, List<Integer> entities) {
|
||||
for (int entity : entities) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
@@ -174,7 +124,7 @@ public class PacketListener extends TabFeature {
|
||||
* @param entities
|
||||
* de-spawned entities
|
||||
*/
|
||||
private void onEntityDestroy(TabPlayer receiver, int... entities) {
|
||||
public void onEntityDestroy(TabPlayer receiver, int... entities) {
|
||||
for (int entity : entities) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
@@ -189,12 +139,12 @@ public class PacketListener extends TabFeature {
|
||||
* @param entity
|
||||
* de-spawned entity
|
||||
*/
|
||||
private void onEntityDestroy(TabPlayer receiver, int entity) {
|
||||
public void onEntityDestroy(TabPlayer receiver, int entity) {
|
||||
TabPlayer deSpawnedPlayer = entityIdMap.get(entity);
|
||||
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer)) {
|
||||
BukkitArmorStandManager asm = nameTagX.getArmorStandManager(deSpawnedPlayer);
|
||||
BackendArmorStandManager asm = nameTagX.getArmorStandManager(deSpawnedPlayer);
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY,
|
||||
() -> asm.destroy(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 162 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user