Compare commits
46 Commits
Author | SHA1 | Date |
---|---|---|
Dan Mulloy | 8272ecb33d | |
Dan Mulloy | d13ce24507 | |
Dan Mulloy | b6a7d5f91d | |
Dan Mulloy | f6b3af4221 | |
vytskalt | 616431cc33 | |
Dan Mulloy | 77ada09c11 | |
Maurice Eisenblätter | 389314cc2f | |
Maurice Eisenblätter | 56fb51e94f | |
Dan Mulloy | 76432f9d36 | |
Dan Mulloy | f0468e04d9 | |
Dan Mulloy | 052e7a3dc0 | |
Dan Mulloy | 9ae73d9e20 | |
Diogo Correia | 11f4810884 | |
Dan Mulloy | fe51dea707 | |
Dan Mulloy | 4afbe845ff | |
Dan Mulloy | 0daf389aff | |
Dan Mulloy | 5c19f647fa | |
iiAhmedYT | e07f01ece8 | |
Brokkonaut | 7d4458bb5f | |
Günter Nejar Keller | 41a0c9da41 | |
LOOHP | 720bb83234 | |
Richy | 1325ec6a98 | |
Maurice Eisenblätter | b0c4b7fe45 | |
Maurice Eisenblätter | a2f02652e9 | |
Dan Mulloy | f9d3266777 | |
Maurice Eisenblätter | 70e4812fde | |
Dan Mulloy | d0c7382d7f | |
Dan Mulloy | e1255edb32 | |
Dan Mulloy | 6f057b361b | |
Lukas Alt | 80aa420099 | |
Nick | 564349c914 | |
TrainmasterHD | 0da27515a4 | |
Richy | 2448d8372e | |
Dan Mulloy | 0eca2eebd2 | |
Dan Mulloy | 8ba1dc1284 | |
Pasqual Koschmieder | 80a097953f | |
Maurice Eisenblätter | d4b4f50674 | |
Pasqual Koschmieder | a7aa31adc0 | |
Pasqual Koschmieder | af33a2ab41 | |
Pasqual Koschmieder | 03d7be13d0 | |
Maurice Eisenblätter | f0401acd2f | |
Fanfaryy | 2686c9fec0 | |
Lukas Alt | 8fc5e509ae | |
Manuel P | 9e9b39a37d | |
Tomescu Vlad-Costin | e219103a25 | |
Dan Mulloy | 98fbcc6585 |
|
@ -15,7 +15,7 @@ jobs:
|
|||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '19'
|
||||
java-version: '21'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Run gradle build lifecycle
|
||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '19'
|
||||
java-version: '21'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
|
|
|
@ -53,7 +53,7 @@ You can also add ProtocolLib as a Maven dependency:
|
|||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<version>5.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -67,7 +67,7 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0'
|
||||
compileOnly 'com.comphenix.protocol:ProtocolLib:5.1.0'
|
||||
}
|
||||
```
|
||||
|
||||
|
|
36
build.gradle
36
build.gradle
|
@ -5,7 +5,7 @@ plugins {
|
|||
}
|
||||
|
||||
group = 'com.comphenix.protocol'
|
||||
version = '5.1.0'
|
||||
version = '5.3.0-SNAPSHOT'
|
||||
description = 'Provides access to the Minecraft protocol'
|
||||
|
||||
def isSnapshot = version.endsWith('-SNAPSHOT')
|
||||
|
@ -33,22 +33,22 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'net.bytebuddy:byte-buddy:1.14.3'
|
||||
compileOnly 'org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.spigotmc:spigot:1.20-R0.1-SNAPSHOT'
|
||||
implementation 'net.bytebuddy:byte-buddy:1.14.14'
|
||||
compileOnly 'org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT'
|
||||
compileOnly 'io.netty:netty-all:4.0.23.Final'
|
||||
compileOnly 'net.kyori:adventure-text-serializer-gson:4.13.0'
|
||||
compileOnly 'net.kyori:adventure-text-serializer-gson:4.14.0'
|
||||
compileOnly 'com.googlecode.json-simple:json-simple:1.1.1'
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
|
||||
testImplementation 'org.mockito:mockito-core:4.11.0'
|
||||
testImplementation 'org.mockito:mockito-inline:4.11.0'
|
||||
testImplementation 'io.netty:netty-common:4.1.77.Final'
|
||||
testImplementation 'io.netty:netty-transport:4.1.77.Final'
|
||||
testImplementation 'org.spigotmc:spigot:1.20-R0.1-SNAPSHOT'
|
||||
testImplementation 'net.kyori:adventure-text-serializer-gson:4.13.0'
|
||||
testImplementation 'net.kyori:adventure-text-serializer-plain:4.13.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.0'
|
||||
testImplementation 'org.mockito:mockito-core:5.6.0'
|
||||
testImplementation 'io.netty:netty-common:4.1.97.Final'
|
||||
testImplementation 'io.netty:netty-transport:4.1.97.Final'
|
||||
testImplementation 'org.spigotmc:spigot:1.20.6-R0.1-SNAPSHOT'
|
||||
testImplementation 'net.kyori:adventure-text-serializer-gson:4.14.0'
|
||||
testImplementation 'net.kyori:adventure-text-serializer-plain:4.14.0'
|
||||
}
|
||||
|
||||
java {
|
||||
|
@ -69,6 +69,14 @@ shadowJar {
|
|||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
|
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
@ -83,7 +83,8 @@ done
|
|||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
|
@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
|
@ -201,11 +202,11 @@ fi
|
|||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.File;
|
|||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Path;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
|
@ -35,8 +36,8 @@ import org.bukkit.plugin.PluginDescriptionFile;
|
|||
import com.comphenix.protocol.error.DetailedErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimingReportGenerator;
|
||||
import com.comphenix.protocol.timing.TimingReport;
|
||||
import com.comphenix.protocol.timing.TimingTrackerManager;
|
||||
import com.comphenix.protocol.updater.Updater;
|
||||
import com.comphenix.protocol.updater.Updater.UpdateType;
|
||||
import com.comphenix.protocol.utility.Closer;
|
||||
|
@ -143,15 +144,14 @@ class CommandProtocol extends CommandBase {
|
|||
}
|
||||
|
||||
private void toggleTimings(CommandSender sender, String[] args) {
|
||||
TimedListenerManager manager = TimedListenerManager.getInstance();
|
||||
boolean state = !manager.isTiming(); // toggle
|
||||
boolean isNotTracking = !TimingTrackerManager.isTracking();
|
||||
|
||||
// Parse the boolean parameter
|
||||
if (args.length == 2) {
|
||||
Boolean parsed = parseBoolean(toQueue(args, 2), "start");
|
||||
|
||||
if (parsed != null) {
|
||||
state = parsed;
|
||||
isNotTracking = parsed;
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Specify a state: ON or OFF.");
|
||||
return;
|
||||
|
@ -161,31 +161,25 @@ class CommandProtocol extends CommandBase {
|
|||
return;
|
||||
}
|
||||
|
||||
// Now change the state
|
||||
if (state) {
|
||||
if (manager.startTiming())
|
||||
if (isNotTracking) {
|
||||
if (TimingTrackerManager.startTracking())
|
||||
sender.sendMessage(ChatColor.GOLD + "Started timing packet listeners.");
|
||||
else
|
||||
sender.sendMessage(ChatColor.RED + "Packet timing already started.");
|
||||
} else {
|
||||
if (manager.stopTiming()) {
|
||||
saveTimings(manager);
|
||||
if (TimingTrackerManager.stopTracking()) {
|
||||
saveTimings(TimingTrackerManager.createReportAndReset());
|
||||
sender.sendMessage(ChatColor.GOLD + "Stopped and saved result in plugin folder.");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.RED + "Packet timing already stopped.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveTimings(TimedListenerManager manager) {
|
||||
|
||||
private void saveTimings(TimingReport report) {
|
||||
try {
|
||||
File destination = new File(plugin.getDataFolder(), "Timings - " + System.currentTimeMillis() + ".txt");
|
||||
TimingReportGenerator generator = new TimingReportGenerator();
|
||||
|
||||
// Print to a text file
|
||||
generator.saveTo(destination, manager);
|
||||
manager.clear();
|
||||
|
||||
Path path = plugin.getDataFolder().toPath().resolve("timings_" + System.currentTimeMillis() + ".txt");
|
||||
report.saveTo(path);
|
||||
} catch (IOException e) {
|
||||
reporter.reportMinimal(plugin, "saveTimings()", e);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,14 @@ import java.lang.annotation.ElementType;
|
|||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
|
||||
import com.comphenix.protocol.events.ConnectionSide;
|
||||
|
@ -14,10 +20,10 @@ import com.comphenix.protocol.injector.packet.PacketRegistry;
|
|||
import com.comphenix.protocol.scheduler.UniversalRunnable;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
/**
|
||||
|
@ -29,7 +35,7 @@ import org.bukkit.Bukkit;
|
|||
public class PacketType implements Serializable, Cloneable, Comparable<PacketType> {
|
||||
// Increment whenever the type changes
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
/**
|
||||
* Represents an unknown packet ID.
|
||||
*/
|
||||
|
@ -50,7 +56,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
private static final Sender SENDER = Sender.CLIENT;
|
||||
|
||||
@ForceAsync
|
||||
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, "SetProtocol", "C00Handshake");
|
||||
public static final PacketType SET_PROTOCOL = new PacketType(PROTOCOL, SENDER, 0x00, "ClientIntentionPacket", "SetProtocol", "C00Handshake");
|
||||
|
||||
private static final Client INSTANCE = new Client();
|
||||
|
||||
|
@ -101,117 +107,128 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
public static class Server extends PacketTypeEnum {
|
||||
private static final Sender SENDER = Sender.SERVER;
|
||||
|
||||
public static final PacketType BUNDLE = new PacketType(PROTOCOL, SENDER, 0x00, "Delimiter", "BundleDelimiterPacket");
|
||||
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x01, "SpawnEntity", "SPacketSpawnObject");
|
||||
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
|
||||
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x03, "NamedEntitySpawn", "SPacketSpawnPlayer");
|
||||
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x04, "Animation", "SPacketAnimation");
|
||||
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x05, "Statistic", "SPacketStatistics");
|
||||
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "BlockChangedAck");
|
||||
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x07, "BlockBreakAnimation", "SPacketBlockBreakAnim");
|
||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x08, "TileEntityData", "SPacketUpdateTileEntity");
|
||||
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x09, "BlockAction", "SPacketBlockAction");
|
||||
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0A, "BlockChange", "SPacketBlockChange");
|
||||
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0B, "Boss", "SPacketUpdateBossInfo");
|
||||
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0C, "ServerDifficulty", "SPacketServerDifficulty");
|
||||
public static final PacketType CHUNKS_BIOMES = new PacketType(PROTOCOL, SENDER, 0x0D, "ChunksBiomes", "ClientboundChunksBiomesPacket");
|
||||
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0E, "ClearTitles");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0F, "TabComplete", "SPacketTabComplete");
|
||||
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x10, "Commands");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x11, "CloseWindow", "SPacketCloseWindow");
|
||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x12, "WindowItems", "SPacketWindowItems");
|
||||
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x13, "WindowData", "SPacketWindowProperty");
|
||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x14, "SetSlot", "SPacketSetSlot");
|
||||
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x15, "SetCooldown", "SPacketCooldown");
|
||||
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x16, "CustomChatCompletions");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, "CustomPayload", "SPacketCustomPayload");
|
||||
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x18, "DamageEvent", "ClientboundDamageEventPacket");
|
||||
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x19, "DeleteChat");
|
||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, "KickDisconnect", "SPacketDisconnect");
|
||||
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1B, "DisguisedChat");
|
||||
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, "EntityStatus", "SPacketEntityStatus");
|
||||
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1D, "Explosion", "SPacketExplosion");
|
||||
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1E, "UnloadChunk", "SPacketUnloadChunk");
|
||||
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1F, "GameStateChange", "SPacketChangeGameState");
|
||||
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x20, "OpenWindowHorse");
|
||||
public static final PacketType HURT_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x21, "HurtAnimation", "ClientboundHurtAnimationPacket");
|
||||
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x22, "InitializeBorder");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x23, "KeepAlive", "SPacketKeepAlive");
|
||||
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x24, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
|
||||
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x25, "WorldEvent", "SPacketEffect");
|
||||
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x26, "WorldParticles", "SPacketParticles");
|
||||
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x27, "LightUpdate");
|
||||
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x28, "Login", "SPacketJoinGame");
|
||||
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x29, "Map", "SPacketMaps");
|
||||
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2A, "OpenWindowMerchant");
|
||||
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
|
||||
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2C, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
|
||||
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2D, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "VehicleMove", "SPacketMoveVehicle");
|
||||
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "OpenBook");
|
||||
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x30, "OpenWindow", "SPacketOpenWindow");
|
||||
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x31, "OpenSignEditor", "SPacketSignEditorOpen");
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x32, "Ping");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x33, "AutoRecipe", "SPacketPlaceGhostRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x34, "Abilities", "SPacketPlayerAbilities");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerChat", "Chat", "SPacketChat");
|
||||
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerCombatEnd");
|
||||
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x37, "PlayerCombatEnter");
|
||||
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x38, "PlayerCombatKill");
|
||||
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerInfoRemove");
|
||||
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerInfoUpdate", "PlayerInfo");
|
||||
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x3B, "LookAt", "SPacketPlayerPosLook");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x3C, "Position");
|
||||
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x3D, "Recipes", "SPacketRecipeBook");
|
||||
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3E, "EntityDestroy", "SPacketDestroyEntities");
|
||||
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3F, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
|
||||
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x40, "ResourcePackSend", "SPacketResourcePackSend");
|
||||
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x41, "Respawn", "SPacketRespawn");
|
||||
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x42, "EntityHeadRotation", "SPacketEntityHeadLook");
|
||||
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x43, "MultiBlockChange", "SPacketMultiBlockChange");
|
||||
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x44, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
|
||||
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x45, "ServerData");
|
||||
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x46, "SetActionBarText");
|
||||
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderCenter");
|
||||
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x48, "SetBorderLerpSize");
|
||||
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x49, "SetBorderSize");
|
||||
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x4A, "SetBorderWarningDelay");
|
||||
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4B, "SetBorderWarningDistance");
|
||||
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x4C, "Camera", "SPacketCamera");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x4D, "HeldItemSlot", "SPacketHeldItemChange");
|
||||
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4E, "ViewCentre");
|
||||
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4F, "ViewDistance");
|
||||
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x50, "SpawnPosition", "SPacketSpawnPosition");
|
||||
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x51, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
|
||||
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x52, "EntityMetadata", "SPacketEntityMetadata");
|
||||
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x53, "AttachEntity", "SPacketEntityAttach");
|
||||
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x54, "EntityVelocity", "SPacketEntityVelocity");
|
||||
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x55, "EntityEquipment", "SPacketEntityEquipment");
|
||||
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x56, "Experience", "SPacketSetExperience");
|
||||
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x57, "UpdateHealth", "SPacketUpdateHealth");
|
||||
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x58, "ScoreboardObjective", "SPacketScoreboardObjective");
|
||||
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x59, "Mount", "SPacketSetPassengers");
|
||||
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x5A, "ScoreboardTeam", "SPacketTeams");
|
||||
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x5B, "ScoreboardScore", "SPacketUpdateScore");
|
||||
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x5C, "SetSimulationDistance");
|
||||
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5D, "SetSubtitleText");
|
||||
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5E, "UpdateTime", "SPacketTimeUpdate");
|
||||
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetTitleText");
|
||||
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x60, "SetTitlesAnimation");
|
||||
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x61, "EntitySound", "SPacketSoundEffect");
|
||||
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x62, "NamedSoundEffect");
|
||||
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x63, "StopSound");
|
||||
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x64, "SystemChat");
|
||||
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x65, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
|
||||
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x66, "NBTQuery");
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x67, "Collect", "SPacketCollectItem");
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x68, "EntityTeleport", "SPacketEntityTeleport");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x69, "Advancements", "SPacketAdvancementInfo");
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x6A, "UpdateAttributes", "SPacketEntityProperties");
|
||||
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x6B, "UpdateEnabledFeatures");
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x6C, "EntityEffect", "SPacketEntityEffect");
|
||||
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x6D, "RecipeUpdate");
|
||||
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6E, "Tags");
|
||||
public static final PacketType BUNDLE = new PacketType(PROTOCOL, SENDER, 0x00, "BundleDelimiter", "Delimiter", "BundleDelimiterPacket");
|
||||
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x01, "AddEntity", "SpawnEntity", "SPacketSpawnObject");
|
||||
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x02, "AddExperienceOrb", "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
|
||||
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x03, "Animate", "Animation", "SPacketAnimation");
|
||||
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x04, "AwardStats", "Statistic", "SPacketStatistics");
|
||||
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x05, "BlockChangedAck");
|
||||
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, "BlockDestruction", "BlockBreakAnimation", "SPacketBlockBreakAnim");
|
||||
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "BlockEntityData", "TileEntityData", "SPacketUpdateTileEntity");
|
||||
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockEvent", "BlockAction", "SPacketBlockAction");
|
||||
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x09, "BlockUpdate", "BlockChange", "SPacketBlockChange");
|
||||
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0A, "BossEvent", "Boss", "SPacketUpdateBossInfo");
|
||||
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0B, "ChangeDifficulty", "ServerDifficulty", "SPacketServerDifficulty");
|
||||
public static final PacketType CHUNK_BATCH_FINISHED = new PacketType(PROTOCOL, SENDER, 0x0C, "ChunkBatchFinished");
|
||||
public static final PacketType CHUNK_BATCH_START = new PacketType(PROTOCOL, SENDER, 0x0D, "ChunkBatchStart");
|
||||
public static final PacketType CHUNKS_BIOMES = new PacketType(PROTOCOL, SENDER, 0x0E, "ChunksBiomes", "ClientboundChunksBiomesPacket");
|
||||
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0F, "ClearTitles");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x10, "CommandSuggestions", "TabComplete", "SPacketTabComplete");
|
||||
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, "Commands");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "ContainerClose", "CloseWindow", "SPacketCloseWindow");
|
||||
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x13, "ContainerSetContent", "WindowItems", "SPacketWindowItems");
|
||||
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x14, "ContainerSetData", "WindowData", "SPacketWindowProperty");
|
||||
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x15, "ContainerSetSlot", "SetSlot", "SPacketSetSlot");
|
||||
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x16, "CookieRequest");
|
||||
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x17, "Cooldown", "SetCooldown", "SPacketCooldown");
|
||||
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x18, "CustomChatCompletions");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x19, "CustomPayload", "SPacketCustomPayload");
|
||||
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x1A, "DamageEvent", "ClientboundDamageEventPacket");
|
||||
public static final PacketType DEBUG_SAMPLE = new PacketType(PROTOCOL, SENDER, 0x1B, "DebugSample");
|
||||
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x1C, "DeleteChat");
|
||||
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1D, "Disconnect", "KickDisconnect", "SPacketDisconnect");
|
||||
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1E, "DisguisedChat");
|
||||
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1F, "EntityEvent", "EntityStatus", "SPacketEntityStatus");
|
||||
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x20, "Explode", "Explosion", "SPacketExplosion");
|
||||
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x21, "ForgetLevelChunk", "UnloadChunk", "SPacketUnloadChunk");
|
||||
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x22, "GameEvent", "GameStateChange", "SPacketChangeGameState");
|
||||
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x23, "HorseScreenOpen", "OpenWindowHorse");
|
||||
public static final PacketType HURT_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x24, "HurtAnimation", "ClientboundHurtAnimationPacket");
|
||||
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x25, "InitializeBorder");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x26, "KeepAlive", "SPacketKeepAlive");
|
||||
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x27, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
|
||||
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x28, "LevelEvent", "WorldEvent", "SPacketEffect");
|
||||
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x29, "LevelParticles", "WorldParticles", "SPacketParticles");
|
||||
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x2A, "LightUpdate");
|
||||
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x2B, "Login", "SPacketJoinGame");
|
||||
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x2C, "MapItemData", "Map", "SPacketMaps");
|
||||
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2D, "MerchantOffers", "OpenWindowMerchant");
|
||||
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
|
||||
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
|
||||
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x30, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x31, "MoveVehicle", "VehicleMove", "SPacketMoveVehicle");
|
||||
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x32, "OpenBook");
|
||||
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x33, "OpenScreen", "OpenWindow", "SPacketOpenWindow");
|
||||
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x34, "OpenSignEditor", "SPacketSignEditorOpen");
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x35, "Ping");
|
||||
public static final PacketType PONG_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x36, "PongResponse");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x37, "PlaceGhostRecipe", "AutoRecipe", "SPacketPlaceGhostRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x38, "PlayerAbilities", "Abilities", "SPacketPlayerAbilities");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerChat", "Chat", "SPacketChat");
|
||||
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerCombatEnd");
|
||||
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x3B, "PlayerCombatEnter");
|
||||
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x3C, "PlayerCombatKill");
|
||||
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x3D, "PlayerInfoRemove");
|
||||
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3E, "PlayerInfoUpdate", "PlayerInfo");
|
||||
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x3F, "PlayerLookAt", "LookAt", "SPacketPlayerPosLook");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x40, "PlayerPosition", "Position");
|
||||
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x41, "Recipe", "Recipes", "SPacketRecipeBook");
|
||||
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x42, "RemoveEntities", "EntityDestroy", "SPacketDestroyEntities");
|
||||
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x43, "RemoveMobEffect", "RemoveEntityEffect", "SPacketRemoveEntityEffect");
|
||||
public static final PacketType RESET_SCORE = new PacketType(PROTOCOL, SENDER, 0x44, "ResetScore", "ResetScorePacket");
|
||||
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x45, "ResourcePackPop", "ResourcePackPopPacket");
|
||||
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x46, "ResourcePackPush", "ResourcePackPushPacket");
|
||||
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x47, "Respawn", "SPacketRespawn");
|
||||
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x48, "RotateHead", "EntityHeadRotation", "SPacketEntityHeadLook");
|
||||
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x49, "SectionBlocksUpdate", "MultiBlockChange", "SPacketMultiBlockChange");
|
||||
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x4A, "SelectAdvancementsTab", "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
|
||||
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x4B, "ServerData");
|
||||
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x4C, "SetActionBarText");
|
||||
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x4D, "SetBorderCenter");
|
||||
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x4E, "SetBorderLerpSize");
|
||||
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x4F, "SetBorderSize");
|
||||
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x50, "SetBorderWarningDelay");
|
||||
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x51, "SetBorderWarningDistance");
|
||||
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x52, "SetCamera", "Camera", "SPacketCamera");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x53, "SetCarriedItem", "HeldItemSlot", "SPacketHeldItemChange");
|
||||
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x54, "SetChunkCacheCenter", "ViewCentre");
|
||||
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x55, "SetChunkCacheRadius", "ViewDistance");
|
||||
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x56, "SetDefaultSpawnPosition", "SpawnPosition", "SPacketSpawnPosition");
|
||||
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x57, "SetDisplayObjective", "ScoreboardDisplayObjective", "SPacketDisplayObjective");
|
||||
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x58, "SetEntityData", "EntityMetadata", "SPacketEntityMetadata");
|
||||
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x59, "SetEntityLink", "AttachEntity", "SPacketEntityAttach");
|
||||
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x5A, "SetEntityMotion", "EntityVelocity", "SPacketEntityVelocity");
|
||||
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x5B, "SetEquipment", "EntityEquipment", "SPacketEntityEquipment");
|
||||
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x5C, "SetExperience", "Experience", "SPacketSetExperience");
|
||||
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x5D, "SetHealth", "UpdateHealth", "SPacketUpdateHealth");
|
||||
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x5E, "SetObjective", "ScoreboardObjective", "SPacketScoreboardObjective");
|
||||
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetPassengers", "Mount", "SPacketSetPassengers");
|
||||
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x60, "SetPlayerTeam", "ScoreboardTeam", "SPacketTeams");
|
||||
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x61, "SetScore", "ScoreboardScore", "SPacketUpdateScore");
|
||||
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x62, "SetSimulationDistance");
|
||||
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x63, "SetSubtitleText");
|
||||
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x64, "SetTime", "UpdateTime", "SPacketTimeUpdate");
|
||||
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x65, "SetTitleText");
|
||||
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x66, "SetTitlesAnimation");
|
||||
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x67, "SoundEntity", "EntitySound", "SPacketSoundEffect");
|
||||
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x68, "Sound", "NamedSoundEffect");
|
||||
public static final PacketType START_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x69, "StartConfiguration");
|
||||
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x6A, "StopSound");
|
||||
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x6B, "StoreCookie");
|
||||
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x6C, "SystemChat");
|
||||
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x6D, "TabList", "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
|
||||
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x6E, "TagQuery", "NBTQuery");
|
||||
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x6F, "TakeItemEntity", "Collect", "SPacketCollectItem");
|
||||
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x70, "TeleportEntity", "EntityTeleport", "SPacketEntityTeleport");
|
||||
public static final PacketType TICKING_STATE = new PacketType(PROTOCOL, SENDER, 0x71, "TickingState", "TickingStatePacket");
|
||||
public static final PacketType TICKING_STEP_STATE = new PacketType(PROTOCOL, SENDER, 0x72, "TickingStep", "TickingStepPacket");
|
||||
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x73, "Transfer");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x74, "UpdateAdvancements", "Advancements", "SPacketAdvancementInfo");
|
||||
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x75, "UpdateAttributes", "SPacketEntityProperties");
|
||||
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x76, "UpdateMobEffect", "EntityEffect", "SPacketEntityEffect");
|
||||
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x77, "UpdateRecipes", "RecipeUpdate");
|
||||
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x78, "UpdateTags", "Tags");
|
||||
public static final PacketType PROJECTILE_POWER = new PacketType(PROTOCOL, SENDER, 0x79, "ProjectilePower");
|
||||
|
||||
// ---- Removed in 1.9
|
||||
|
||||
|
@ -346,25 +363,43 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x0C, "ChatPreview");
|
||||
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 240, "ChatPreview");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerChatHeader");
|
||||
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 239, "PlayerChatHeader");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x4E, "SetDisplayChatPreview");
|
||||
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 238, "SetDisplayChatPreview");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x16, "CustomSoundEffect", "SPacketCustomSound");
|
||||
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 237, "CustomSoundEffect", "SPacketCustomSound");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.20.2: moved to configuration phase packets
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 236, "UpdateEnabledFeatures");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.20.2
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 235, "NamedEntitySpawn", "SPacketSpawnPlayer");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 234, "ResourcePackSend", "SPacketResourcePackSend");
|
||||
|
||||
private static final Server INSTANCE = new Server();
|
||||
|
||||
|
@ -386,57 +421,64 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
public static class Client extends PacketTypeEnum {
|
||||
private static final Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "TeleportAccept", "CPacketConfirmTeleport");
|
||||
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "TileNBTQuery");
|
||||
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x02, "DifficultyChange");
|
||||
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "AcceptTeleportation", "TeleportAccept", "CPacketConfirmTeleport");
|
||||
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "BlockEntityTagQuery", "TileNBTQuery");
|
||||
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x02, "ChangeDifficulty", "DifficultyChange");
|
||||
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "ChatAck");
|
||||
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ChatCommand");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x05, "Chat", "CPacketChatMessage");
|
||||
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x06, "ChatSessionUpdate");
|
||||
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x07, "ClientCommand", "CPacketClientStatus");
|
||||
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x08, "Settings", "CPacketClientSettings");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x09, "TabComplete", "CPacketTabComplete");
|
||||
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x0A, "EnchantItem", "CPacketEnchantItem");
|
||||
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0B, "WindowClick", "CPacketClickWindow");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0C, "CloseWindow", "CPacketCloseWindow");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0D, "CustomPayload", "CPacketCustomPayload");
|
||||
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0E, "BEdit");
|
||||
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0F, "EntityNBTQuery");
|
||||
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x10, "UseEntity", "CPacketUseEntity");
|
||||
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x11, "JigsawGenerate");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x12, "KeepAlive", "CPacketKeepAlive");
|
||||
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x13, "DifficultyLock");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
|
||||
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
|
||||
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
|
||||
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x17, "Flying$d");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "VehicleMove", "CPacketVehicleMove");
|
||||
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x19, "BoatMove", "CPacketSteerBoat");
|
||||
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x1A, "PickItem");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1B, "AutoRecipe", "CPacketPlaceRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1C, "Abilities", "CPacketPlayerAbilities");
|
||||
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1D, "BlockDig", "CPacketPlayerDigging");
|
||||
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1E, "EntityAction", "CPacketEntityAction");
|
||||
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1F, "SteerVehicle", "CPacketInput");
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x20, "Pong");
|
||||
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x21, "RecipeSettings");
|
||||
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x22, "RecipeDisplayed", "CPacketRecipeInfo");
|
||||
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x23, "ItemName");
|
||||
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x24, "ResourcePackStatus", "CPacketResourcePackStatus");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x25, "Advancements", "CPacketSeenAdvancements");
|
||||
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x26, "TrSel");
|
||||
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x27, "Beacon");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x28, "HeldItemSlot", "CPacketHeldItemChange");
|
||||
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x29, "SetCommandBlock");
|
||||
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x2A, "SetCommandMinecart");
|
||||
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x2B, "SetCreativeSlot", "CPacketCreativeInventoryAction");
|
||||
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x2C, "SetJigsaw");
|
||||
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x2D, "Struct");
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x2E, "UpdateSign", "CPacketUpdateSign");
|
||||
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x2F, "ArmAnimation", "CPacketAnimation");
|
||||
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x30, "Spectate", "CPacketSpectate");
|
||||
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x31, "UseItem", "CPacketPlayerTryUseItemOnBlock");
|
||||
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x32, "BlockPlace", "CPacketPlayerTryUseItem");
|
||||
public static final PacketType CHAT_COMMAND_SIGNED = new PacketType(PROTOCOL, SENDER, 0x05, "ChatCommandSigned");
|
||||
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x06, "Chat", "CPacketChatMessage");
|
||||
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x07, "ChatSessionUpdate");
|
||||
public static final PacketType CHUNK_BATCH_RECEIVED = new PacketType(PROTOCOL, SENDER, 0x08, "ChunkBatchReceived");
|
||||
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x09, "ClientCommand", "CPacketClientStatus");
|
||||
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x0A, "ClientInformation", "Settings", "CPacketClientSettings");
|
||||
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0B, "CommandSuggestion", "TabComplete", "CPacketTabComplete");
|
||||
public static final PacketType CONFIGURATION_ACK = new PacketType(PROTOCOL, SENDER, 0x0C, "ConfigurationAcknowledged");
|
||||
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x0D, "ContainerButtonClick", "EnchantItem", "CPacketEnchantItem");
|
||||
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0E, "ContainerClick", "WindowClick", "CPacketClickWindow");
|
||||
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0F, "ContainerClose", "CloseWindow", "CPacketCloseWindow");
|
||||
public static final PacketType CONTAINER_SLOT_STATE_CHANGED = new PacketType(PROTOCOL, SENDER, 0x10, "ContainerSlotStateChanged", "ContainerSlotStateChangedPacket");
|
||||
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x11, "CookieResponse");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x12, "CustomPayload", "CPacketCustomPayload");
|
||||
public static final PacketType DEBUG_SAMPLE_SUBSCRIPTION = new PacketType(PROTOCOL, SENDER, 0x13, "DebugSampleSubscription");
|
||||
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x14, "EditBook", "BEdit");
|
||||
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x15, "EntityTagQuery", "EntityNBTQuery");
|
||||
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x16, "Interact", "UseEntity", "CPacketUseEntity");
|
||||
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x17, "JigsawGenerate");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x18, "KeepAlive", "CPacketKeepAlive");
|
||||
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x19, "LockDifficulty", "DifficultyLock");
|
||||
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x1A, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
|
||||
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x1B, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
|
||||
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x1C, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
|
||||
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x1D, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$StatusOnly", "Flying$d");
|
||||
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x1E, "MoveVehicle", "VehicleMove", "CPacketVehicleMove");
|
||||
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x1F, "PaddleBoat", "BoatMove", "CPacketSteerBoat");
|
||||
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x20, "PickItem");
|
||||
public static final PacketType PING_REQUEST = new PacketType(PROTOCOL, SENDER, 0x21, "PingRequest");
|
||||
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x22, "PlaceRecipe", "AutoRecipe", "CPacketPlaceRecipe");
|
||||
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x23, "PlayerAbilities", "Abilities", "CPacketPlayerAbilities");
|
||||
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x24, "PlayerAction", "BlockDig", "CPacketPlayerDigging");
|
||||
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x25, "PlayerCommand", "EntityAction", "CPacketEntityAction");
|
||||
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x26, "PlayerInput", "SteerVehicle", "CPacketInput");
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x27, "Pong");
|
||||
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x28, "RecipeBookChangeSettings", "RecipeSettings");
|
||||
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x29, "RecipeBookSeenRecipe", "RecipeDisplayed", "CPacketRecipeInfo");
|
||||
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x2A, "RenameItem", "ItemName");
|
||||
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x2B, "ResourcePack", "ResourcePackStatus", "CPacketResourcePackStatus");
|
||||
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x2C, "SeenAdvancements", "Advancements", "CPacketSeenAdvancements");
|
||||
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x2D, "SelectTrade", "TrSel");
|
||||
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x2E, "SetBeacon", "Beacon");
|
||||
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x2F, "SetCarriedItem", "HeldItemSlot", "CPacketHeldItemChange");
|
||||
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x30, "SetCommandBlock");
|
||||
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x31, "SetCommandMinecart");
|
||||
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x32, "SetCreativeModeSlot", "SetCreativeSlot", "CPacketCreativeInventoryAction");
|
||||
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x33, "SetJigsawBlock", "SetJigsaw");
|
||||
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x34, "SetStructureBlock", "Struct");
|
||||
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x35, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
|
||||
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x36, "Swing", "ArmAnimation", "CPacketAnimation");
|
||||
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x37, "TeleportToEntity", "Spectate", "CPacketSpectate");
|
||||
public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn", "BlockPlace", "CPacketPlayerTryUseItemOnBlock");
|
||||
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x39, "UseItem", "CPacketPlayerTryUseItem");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.17
|
||||
|
@ -454,7 +496,13 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
* @deprecated Removed in 1.19.3
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x06, "ChatPreview");
|
||||
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 253, "ChatPreview");
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to USE_ITEM_ON
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType BLOCK_PLACE = USE_ITEM_ON.clone();
|
||||
|
||||
private static final Client INSTANCE = new Client();
|
||||
|
||||
|
@ -489,9 +537,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
private static final Sender SENDER = Sender.SERVER;
|
||||
|
||||
@ForceAsync
|
||||
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, "ServerInfo", "SPacketServerInfo");
|
||||
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, "StatusResponse", "ServerInfo", "SPacketServerInfo");
|
||||
@ForceAsync
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, "Pong", "SPacketPong");
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, "PongResponse", "Pong", "SPacketPong");
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link #SERVER_INFO}
|
||||
|
@ -520,9 +568,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
public static class Client extends PacketTypeEnum {
|
||||
private static final Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketServerQuery");
|
||||
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "StatusRequest", "Start", "CPacketServerQuery");
|
||||
@ForceAsync
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "Ping", "CPacketPing");
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "PingRequest", "Ping", "CPacketPing");
|
||||
|
||||
private static final Client INSTANCE = new Client();
|
||||
|
||||
|
@ -557,11 +605,12 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
private static final Sender SENDER = Sender.SERVER;
|
||||
|
||||
@ForceAsync
|
||||
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, "Disconnect", "SPacketDisconnect");
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "SPacketEncryptionRequest");
|
||||
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "Success", "SPacketLoginSuccess");
|
||||
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, "SetCompression", "SPacketEnableCompression");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, "CustomPayload", "SPacketCustomPayload");
|
||||
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, "LoginDisconnect", "Disconnect", "SPacketDisconnect");
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "Hello", "EncryptionBegin", "SPacketEncryptionRequest");
|
||||
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "GameProfile", "Success", "SPacketLoginSuccess");
|
||||
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, "LoginCompression", "SetCompression", "SPacketEnableCompression");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, "CustomQuery", "CustomPayload", "SPacketCustomPayload");
|
||||
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x05, "CookieRequest");
|
||||
|
||||
private static final Server INSTANCE = new Server();
|
||||
|
||||
|
@ -583,9 +632,11 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
public static class Client extends PacketTypeEnum {
|
||||
private static final Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketLoginStart");
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "CPacketEncryptionResponse");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomPayload", "CPacketCustomPayload");
|
||||
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Hello", "Start", "CPacketLoginStart");
|
||||
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "Key", "EncryptionBegin", "CPacketEncryptionResponse");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomQueryAnswer", "CustomPayload", "CPacketCustomPayload");
|
||||
public static final PacketType LOGIN_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "LoginAcknowledged");
|
||||
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x04, "CookieResponse");
|
||||
|
||||
private static final Client INSTANCE = new Client();
|
||||
|
||||
|
@ -605,6 +656,77 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packets sent and received during client configuration.
|
||||
* @author Pasqual Koschmieder
|
||||
*/
|
||||
public static class Configuration {
|
||||
static final Protocol PROTOCOL = Protocol.CONFIGURATION;
|
||||
|
||||
/**
|
||||
* Outgoing packets.
|
||||
*/
|
||||
public static class Server extends PacketTypeEnum {
|
||||
private static final Sender SENDER = Sender.SERVER;
|
||||
|
||||
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x00, "CookieRequest");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x01, "CustomPayload");
|
||||
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x02, "Disconnect");
|
||||
public static final PacketType FINISH_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x03, "FinishConfiguration");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x04, "KeepAlive");
|
||||
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x05, "Ping");
|
||||
public static final PacketType RESET_CHAT = new PacketType(PROTOCOL, SENDER, 0x06, "ResetChat");
|
||||
public static final PacketType REGISTRY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "RegistryData");
|
||||
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x08, "ResourcePackPop");
|
||||
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x09, "ResourcePackPush");
|
||||
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x0A, "StoreCookie");
|
||||
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x0B, "Transfer");
|
||||
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x0C, "UpdateEnabledFeatures");
|
||||
public static final PacketType UPDATE_TAGS = new PacketType(PROTOCOL, SENDER, 0x0D, "UpdateTags");
|
||||
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x0E, "ClientboundSelectKnownPacks");
|
||||
|
||||
/**
|
||||
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
|
||||
*/
|
||||
@Deprecated
|
||||
public static final PacketType RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 255, "ResourcePack");
|
||||
|
||||
private static final Server INSTANCE = new Server();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Server() { super(); }
|
||||
|
||||
public static Server getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming packets.
|
||||
*/
|
||||
public static class Client extends PacketTypeEnum {
|
||||
private static final Sender SENDER = Sender.CLIENT;
|
||||
|
||||
public static final PacketType CLIENT_INFORMATION = new PacketType(PROTOCOL, SENDER, 0x00, "ClientInformation");
|
||||
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x01, "CookieResponse");
|
||||
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomPayload");
|
||||
public static final PacketType FINISH_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x03, "FinishConfiguration");
|
||||
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x04, "KeepAlive");
|
||||
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x05, "Pong");
|
||||
public static final PacketType RESOURCE_PACK_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "ResourcePack");
|
||||
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x07, "ServerboundSelectKnownPacks");
|
||||
|
||||
private static final Client INSTANCE = new Client();
|
||||
|
||||
// Prevent accidental construction
|
||||
private Client() { super(); }
|
||||
|
||||
public static Client getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the different protocol or connection states.
|
||||
* @author Kristian
|
||||
|
@ -614,6 +736,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
PLAY("Play", "game"),
|
||||
STATUS("Status", "status"),
|
||||
LOGIN("Login", "login"),
|
||||
CONFIGURATION("Configuration", "configuration"),
|
||||
TRANSFER("Transfer", "transfer"), // TODO are these the right names?
|
||||
|
||||
/**
|
||||
* Only for packets removed in Minecraft 1.7.2
|
||||
|
@ -639,6 +763,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
case "PLAY": return PLAY;
|
||||
case "STATUS": return STATUS;
|
||||
case "LOGIN": return LOGIN;
|
||||
case "CONFIGURATION": return CONFIGURATION;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized vanilla enum " + vanilla);
|
||||
}
|
||||
|
@ -731,7 +856,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
private boolean forceAsync;
|
||||
|
||||
private boolean dynamic;
|
||||
private int hashCode;
|
||||
private transient int hashCode;
|
||||
|
||||
/**
|
||||
* Retrieve the current packet/legacy lookup.
|
||||
|
@ -747,7 +872,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
addPacketTypes(Status.Client.getInstance()).
|
||||
addPacketTypes(Status.Server.getInstance()).
|
||||
addPacketTypes(Login.Client.getInstance()).
|
||||
addPacketTypes(Login.Server.getInstance());
|
||||
addPacketTypes(Login.Server.getInstance()).
|
||||
addPacketTypes(Configuration.Client.getInstance()).
|
||||
addPacketTypes(Configuration.Server.getInstance());
|
||||
}
|
||||
return LOOKUP;
|
||||
}
|
||||
|
@ -854,6 +981,14 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
}
|
||||
}
|
||||
|
||||
private static String formatSimpleClassName(Protocol protocol, Sender sender, String name) {
|
||||
return "Packet" + protocol.getPacketName() + sender.getPacketName() + name;
|
||||
}
|
||||
|
||||
private static String formatSimpleMojangClassName(Protocol protocol, Sender sender, String name) {
|
||||
return sender.getMojangName() + name + "Packet";
|
||||
}
|
||||
|
||||
private static String formatMojangClassName(Protocol protocol, Sender sender, String name) {
|
||||
return "net.minecraft.network.protocol." + protocol.getMojangName() + "." + sender.getMojangName()
|
||||
+ name + "Packet";
|
||||
|
@ -924,7 +1059,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
return type;
|
||||
}
|
||||
|
||||
static Consumer<String> onDynamicCreate = x -> {};
|
||||
static BiConsumer<PacketType, String> onDynamicCreate = (type, className) -> {};
|
||||
static BiConsumer<PacketType, Integer> onIdMismatch = (type, newId) -> {};
|
||||
|
||||
/**
|
||||
* Retrieve a packet type from a protocol, sender, ID, and class for 1.8+
|
||||
|
@ -942,7 +1078,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
|
||||
// Check the map first
|
||||
String className = packetClass.getName();
|
||||
PacketType type = find(map, className);
|
||||
PacketType type = find(map, packetClass);
|
||||
if (type == null) {
|
||||
// Guess we don't support this packet :/
|
||||
type = new PacketType(protocol, sender, packetId, PROTOCOL_VERSION, className);
|
||||
|
@ -950,14 +1086,17 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
|
||||
// Many may be scheduled, but only the first will be executed
|
||||
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
|
||||
onDynamicCreate.accept(className);
|
||||
onDynamicCreate.accept(type, className);
|
||||
} else if (packetId != type.getCurrentId()) {
|
||||
onIdMismatch.accept(type, packetId);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private static PacketType find(Map<String, PacketType> map, String clazz) {
|
||||
PacketType ret = map.get(clazz);
|
||||
private static PacketType find(Map<String, PacketType> map, Class<?> packetClass) {
|
||||
String className = packetClass.getName();
|
||||
PacketType ret = map.get(className);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -967,7 +1106,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
List<String> aliases = check.getClassNames();
|
||||
if (aliases.size() > 1) {
|
||||
for (String alias : aliases) {
|
||||
if (alias.equals(clazz)) {
|
||||
if (alias.equals(className) || alias.equals(packetClass.getSimpleName())) {
|
||||
// We have a match!
|
||||
return check;
|
||||
}
|
||||
|
@ -981,8 +1120,11 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
/**
|
||||
* Lookup a packet type from a packet class.
|
||||
* @param packetClass - the packet class.
|
||||
* @return The corresponding packet type, or NULL if not found.
|
||||
* @return The corresponding packet type, never null.
|
||||
* @throws IllegalArgumentException if the given packet class is not a registered packet.
|
||||
* @deprecated since 1.20.2 there are packet classes that are shared between protocol states, therefore the result can be invalid.
|
||||
*/
|
||||
@Deprecated
|
||||
public static PacketType fromClass(Class<?> packetClass) {
|
||||
PacketType type = PacketRegistry.getPacketType(packetClass);
|
||||
|
||||
|
@ -1086,7 +1228,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
|
||||
this.currentId = currentId;
|
||||
this.version = version;
|
||||
|
||||
|
||||
this.classNames = new ArrayList<>();
|
||||
for (String classname : names) {
|
||||
if (isMcpPacketName(classname)) { // Minecraft MCP packets
|
||||
|
@ -1094,6 +1236,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
|
|||
} else {
|
||||
classNames.add(formatClassName(protocol, sender, classname));
|
||||
classNames.add(formatMojangClassName(protocol, sender, classname));
|
||||
classNames.add(formatSimpleClassName(protocol, sender, classname));
|
||||
classNames.add(formatSimpleMojangClassName(protocol, sender, classname));
|
||||
classNames.add(classname);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import com.google.common.collect.Multimap;
|
|||
* @author Kristian
|
||||
*/
|
||||
class PacketTypeLookup {
|
||||
|
||||
public static class ProtocolSenderLookup {
|
||||
// Unroll lookup for performance reasons
|
||||
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = new IntegerMap<>();
|
||||
|
@ -27,6 +28,8 @@ class PacketTypeLookup {
|
|||
public final IntegerMap<PacketType> STATUS_SERVER = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> LOGIN_CLIENT = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> LOGIN_SERVER = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> CONFIGURATION_CLIENT = new IntegerMap<>();
|
||||
public final IntegerMap<PacketType> CONFIGURATION_SERVER = new IntegerMap<>();
|
||||
|
||||
/**
|
||||
* Retrieve the correct integer map for a specific protocol and sender.
|
||||
|
@ -44,6 +47,8 @@ class PacketTypeLookup {
|
|||
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
|
||||
case LOGIN:
|
||||
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
|
||||
case CONFIGURATION:
|
||||
return sender == Sender.CLIENT ? CONFIGURATION_CLIENT : CONFIGURATION_SERVER;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unable to find protocol " + protocol);
|
||||
}
|
||||
|
@ -60,7 +65,9 @@ class PacketTypeLookup {
|
|||
public final Map<String, PacketType> STATUS_SERVER = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> LOGIN_CLIENT = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> LOGIN_SERVER = new ConcurrentHashMap<>();
|
||||
|
||||
public final Map<String, PacketType> CONFIGURATION_CLIENT = new ConcurrentHashMap<>();
|
||||
public final Map<String, PacketType> CONFIGURATION_SERVER = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Retrieve the correct integer map for a specific protocol and sender.
|
||||
* @param protocol - the protocol.
|
||||
|
@ -77,17 +84,14 @@ class PacketTypeLookup {
|
|||
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
|
||||
case LOGIN:
|
||||
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
|
||||
case CONFIGURATION:
|
||||
return sender == Sender.CLIENT ? CONFIGURATION_CLIENT : CONFIGURATION_SERVER;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unable to find protocol " + protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packet IDs from 1.6.4 and below
|
||||
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> serverLookup = new IntegerMap<>();
|
||||
private final IntegerMap<PacketType> clientLookup = new IntegerMap<>();
|
||||
|
||||
// Packets for 1.7.2
|
||||
private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup();
|
||||
|
||||
|
@ -119,9 +123,11 @@ class PacketTypeLookup {
|
|||
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
|
||||
* @param packetId - the legacy packet ID.
|
||||
* @return The corresponding packet type, or NULL if not found.
|
||||
* @deprecated no longer works and will always return null
|
||||
*/
|
||||
@Deprecated
|
||||
public PacketType getFromLegacy(int packetId) {
|
||||
return legacyLookup.get(packetId);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,20 +144,11 @@ class PacketTypeLookup {
|
|||
* @param packetId - the legacy packet ID.
|
||||
* @param preference - which packet type to look for first.
|
||||
* @return The corresponding packet type, or NULL if not found.
|
||||
* @deprecated no longer works and will always return null
|
||||
*/
|
||||
public PacketType getFromLegacy(int packetId, Sender preference) {
|
||||
if (preference == Sender.CLIENT)
|
||||
return getFirst(packetId, clientLookup, serverLookup);
|
||||
else
|
||||
return getFirst(packetId, serverLookup, clientLookup);
|
||||
}
|
||||
|
||||
// Helper method for looking up in two sets
|
||||
private <T> T getFirst(int packetId, IntegerMap<T> first, IntegerMap<T> second) {
|
||||
if (first.containsKey(packetId))
|
||||
return first.get(packetId);
|
||||
else
|
||||
return second.get(packetId);
|
||||
@Deprecated
|
||||
public PacketType getFromLegacy(int packetId, Sender preference) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -168,7 +165,8 @@ class PacketTypeLookup {
|
|||
}
|
||||
|
||||
public PacketType getFromCurrent(Protocol protocol, Sender sender, String name) {
|
||||
return classLookup.getMap(protocol, sender).get(name);
|
||||
Map<String, PacketType> map = classLookup.getMap(protocol, sender);
|
||||
return map.get(name);
|
||||
}
|
||||
|
||||
public ClassLookup getClassLookup() {
|
||||
|
|
|
@ -37,21 +37,19 @@ import com.google.common.collect.Iterables;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
|
@ -76,8 +74,6 @@ public class ProtocolLib extends JavaPlugin {
|
|||
|
||||
public static final ReportType REPORT_CANNOT_PARSE_MINECRAFT_VERSION = new ReportType(
|
||||
"Unable to retrieve current Minecraft version. Assuming %s");
|
||||
public static final ReportType REPORT_CANNOT_DETECT_CONFLICTING_PLUGINS = new ReportType(
|
||||
"Unable to detect conflicting plugin versions.");
|
||||
public static final ReportType REPORT_CANNOT_REGISTER_COMMAND = new ReportType("Cannot register command %s: %s");
|
||||
|
||||
public static final ReportType REPORT_CANNOT_CREATE_TIMEOUT_TASK = new ReportType(
|
||||
|
@ -166,7 +162,7 @@ public class ProtocolLib extends JavaPlugin {
|
|||
: new DefaultScheduler(this);
|
||||
|
||||
// Check for other versions
|
||||
this.checkConflictingVersions();
|
||||
scanForOtherProtocolLibJars();
|
||||
|
||||
// Handle unexpected Minecraft versions
|
||||
MinecraftVersion version = this.verifyMinecraftVersion();
|
||||
|
@ -309,6 +305,14 @@ public class ProtocolLib extends JavaPlugin {
|
|||
logger.addHandler(this.redirectHandler);
|
||||
}
|
||||
|
||||
private void highlyVisibleError(String... lines) {
|
||||
Logger directLogging = Logger.getLogger("Minecraft");
|
||||
|
||||
for (String line : ChatExtensions.toFlowerBox(lines, "*", 3, 1)) {
|
||||
directLogging.severe(line);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
try {
|
||||
|
@ -317,23 +321,14 @@ public class ProtocolLib extends JavaPlugin {
|
|||
|
||||
// Silly plugin reloaders!
|
||||
if (protocolManager == null) {
|
||||
Logger directLogging = Logger.getLogger("Minecraft");
|
||||
String[] message = new String[]{
|
||||
" ProtocolLib does not support plugin reloaders! ", " Please use the built-in reload command! "
|
||||
};
|
||||
|
||||
// Print as severe
|
||||
for (String line : ChatExtensions.toFlowerBox(message, "*", 3, 1)) {
|
||||
directLogging.severe(line);
|
||||
}
|
||||
|
||||
this.disablePlugin();
|
||||
highlyVisibleError(
|
||||
" ProtocolLib does not support plugin reloaders! ",
|
||||
" Please use the built-in reload command! "
|
||||
);
|
||||
disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for incompatible plugins
|
||||
this.checkForIncompatibility(manager);
|
||||
|
||||
// Set up command handlers
|
||||
this.registerCommand(CommandProtocol.NAME, this.commandProtocol);
|
||||
this.registerCommand(CommandPacket.NAME, this.commandPacket);
|
||||
|
@ -369,24 +364,6 @@ public class ProtocolLib extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkForIncompatibility(PluginManager manager) {
|
||||
for (String plugin : ProtocolLibrary.INCOMPATIBLE) {
|
||||
if (manager.getPlugin(plugin) != null) {
|
||||
// Special case for TagAPI and iTag
|
||||
if (plugin.equals("TagAPI")) {
|
||||
Plugin iTag = manager.getPlugin("iTag");
|
||||
if (iTag == null || iTag.getDescription().getVersion().startsWith("1.0")) {
|
||||
logger.severe("Detected incompatible plugin: TagAPI");
|
||||
}
|
||||
} else {
|
||||
logger.severe("Detected incompatible plugin: " + plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin authors: Notify me to remove these
|
||||
|
||||
// Used to check Minecraft version
|
||||
private MinecraftVersion verifyMinecraftVersion() {
|
||||
MinecraftVersion minimum = new MinecraftVersion(ProtocolLibrary.MINIMUM_MINECRAFT_VERSION);
|
||||
|
@ -416,50 +393,47 @@ public class ProtocolLib extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkConflictingVersions() {
|
||||
Pattern ourPlugin = Pattern.compile("ProtocolLib-(.*)\\.jar");
|
||||
MinecraftVersion currentVersion = new MinecraftVersion(this.getDescription().getVersion());
|
||||
MinecraftVersion newestVersion = null;
|
||||
|
||||
// Skip the file that contains this current instance however
|
||||
File loadedFile = this.getFile();
|
||||
|
||||
private void scanForOtherProtocolLibJars() {
|
||||
try {
|
||||
// Scan the plugin folder for newer versions of ProtocolLib
|
||||
// The plugin folder isn't always plugins/
|
||||
File loadedFile = this.getFile();
|
||||
File pluginFolder = this.getDataFolder().getParentFile();
|
||||
|
||||
File[] candidates = pluginFolder.listFiles();
|
||||
if (candidates != null) {
|
||||
for (File candidate : candidates) {
|
||||
if (candidate.isFile() && !candidate.equals(loadedFile)) {
|
||||
Matcher match = ourPlugin.matcher(candidate.getName());
|
||||
if (match.matches()) {
|
||||
MinecraftVersion version = new MinecraftVersion(match.group(1));
|
||||
|
||||
if (candidate.length() == 0) {
|
||||
// Delete and inform the user
|
||||
logger.info((candidate.delete() ? "Deleted " : "Could not delete ") + candidate);
|
||||
} else if (newestVersion == null || newestVersion.compareTo(version) < 0) {
|
||||
newestVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (candidates == null) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO This shows [ProtocolLib] and [ProtocolLibrary] in the message
|
||||
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_DETECT_CONFLICTING_PLUGINS).error(e));
|
||||
}
|
||||
|
||||
// See if the newest version is actually higher
|
||||
if (newestVersion != null && currentVersion.compareTo(newestVersion) < 0) {
|
||||
// We don't need to set internal classes or instances to NULL - that would break the other loaded plugin
|
||||
this.skipDisable = true;
|
||||
String ourName = loadedFile.getName();
|
||||
List<String> others = new ArrayList<>();
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Detected a newer version of ProtocolLib (%s) in plugin folder than the current (%s). Disabling.",
|
||||
newestVersion.getVersion(), currentVersion.getVersion()));
|
||||
for (File candidate : candidates) {
|
||||
if (!candidate.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String jarName = candidate.getName();
|
||||
if (jarName.equals(ourName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String jarNameLower = candidate.getName().toLowerCase();
|
||||
if (!jarNameLower.startsWith("protocollib") || !jarNameLower.endsWith(".jar")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
others.add(jarName);
|
||||
}
|
||||
|
||||
if (!others.isEmpty()) {
|
||||
highlyVisibleError(
|
||||
" Detected multiple ProtocolLib JAR files in the plugin directory! ",
|
||||
" You should remove all but one of them or there will likely be undesired behavior. ",
|
||||
" This JAR: " + loadedFile.getName(),
|
||||
" Other detected JARs: " + String.join(",", others)
|
||||
);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ProtocolLogger.debug("Failed to scan plugins directory for ProtocolLib jars", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,15 +535,15 @@ public class ProtocolLib extends JavaPlugin {
|
|||
|
||||
// that reloading the server might break ProtocolLib / plugins depending on it
|
||||
if (Util.isCurrentlyReloading()) {
|
||||
logger.severe("╔══════════════════════════════════════════════════════════════════╗");
|
||||
logger.severe("║ WARNING ║");
|
||||
logger.severe("║ RELOADING THE SERVER WHILE PROTOCOL LIB IS ENABLED MIGHT ║");
|
||||
logger.severe("║ LEAD TO UNEXPECTED ERRORS! ║");
|
||||
logger.severe("║ ║");
|
||||
logger.severe("║ Consider to cleanly restart your server if you encounter ║");
|
||||
logger.severe("║ any issues related to Protocol Lib before opening an issue ║");
|
||||
logger.severe("║ on GitHub! ║");
|
||||
logger.severe("╚══════════════════════════════════════════════════════════════════╝");
|
||||
highlyVisibleError(
|
||||
" WARNING ",
|
||||
" RELOADING THE SERVER WHILE PROTOCOLLIB IS ENABLED MIGHT ",
|
||||
" LEAD TO UNEXPECTED ERRORS! ",
|
||||
" ",
|
||||
" Consider cleanly restarting your server if you encounter ",
|
||||
" any issues related to ProtocolLib before opening an issue ",
|
||||
" on GitHub! "
|
||||
);
|
||||
}
|
||||
|
||||
// Clean up
|
||||
|
|
|
@ -37,12 +37,12 @@ public class ProtocolLibrary {
|
|||
/**
|
||||
* The maximum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.20.1";
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.20.4";
|
||||
|
||||
/**
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.20.1) was released.
|
||||
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.20.4) was released.
|
||||
*/
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2023-06-12";
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2023-12-07";
|
||||
|
||||
/**
|
||||
* Plugins that are currently incompatible with ProtocolLib.
|
||||
|
|
|
@ -17,12 +17,14 @@
|
|||
|
||||
package com.comphenix.protocol.async;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
|
@ -31,16 +33,13 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||
import com.comphenix.protocol.injector.collection.InboundPacketListenerSet;
|
||||
import com.comphenix.protocol.injector.collection.OutboundPacketListenerSet;
|
||||
import com.comphenix.protocol.scheduler.ProtocolScheduler;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a filter manager for asynchronous packets.
|
||||
* <p>
|
||||
|
@ -50,8 +49,8 @@ import org.bukkit.plugin.Plugin;
|
|||
*/
|
||||
public class AsyncFilterManager implements AsynchronousManager {
|
||||
|
||||
private SortedPacketListenerList serverTimeoutListeners;
|
||||
private SortedPacketListenerList clientTimeoutListeners;
|
||||
private OutboundPacketListenerSet outboundTimeoutListeners;
|
||||
private InboundPacketListenerSet inboundTimeoutListeners;
|
||||
private Set<PacketListener> timeoutListeners;
|
||||
|
||||
private PacketProcessingQueue serverProcessingQueue;
|
||||
|
@ -84,11 +83,11 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
*/
|
||||
public AsyncFilterManager(ErrorReporter reporter, ProtocolScheduler scheduler) {
|
||||
// Initialize timeout listeners
|
||||
this.serverTimeoutListeners = new SortedPacketListenerList();
|
||||
this.clientTimeoutListeners = new SortedPacketListenerList();
|
||||
this.outboundTimeoutListeners = new OutboundPacketListenerSet(null, reporter);
|
||||
this.inboundTimeoutListeners = new InboundPacketListenerSet(null, reporter);
|
||||
this.timeoutListeners = ConcurrentHashMap.newKeySet();
|
||||
|
||||
this.playerSendingHandler = new PlayerSendingHandler(reporter, serverTimeoutListeners, clientTimeoutListeners);
|
||||
this.playerSendingHandler = new PlayerSendingHandler(outboundTimeoutListeners, inboundTimeoutListeners);
|
||||
this.serverProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||
this.clientProcessingQueue = new PacketProcessingQueue(playerSendingHandler);
|
||||
this.playerSendingHandler.initializeScheduler();
|
||||
|
@ -130,9 +129,9 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
ListeningWhitelist receiving = listener.getReceivingWhitelist();
|
||||
|
||||
if (!ListeningWhitelist.isEmpty(sending))
|
||||
serverTimeoutListeners.addListener(listener, sending);
|
||||
outboundTimeoutListeners.addListener(listener);
|
||||
if (!ListeningWhitelist.isEmpty(receiving))
|
||||
serverTimeoutListeners.addListener(listener, receiving);
|
||||
inboundTimeoutListeners.addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,9 +144,8 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
ImmutableSet.Builder<PacketListener> builder = ImmutableSet.builder();
|
||||
|
||||
// Add every asynchronous packet listener
|
||||
for (PrioritizedListener<AsyncListenerHandler> handler :
|
||||
Iterables.concat(serverProcessingQueue.values(), clientProcessingQueue.values())) {
|
||||
builder.add(handler.getListener().getAsyncListener());
|
||||
for (AsyncListenerHandler handler : Iterables.concat(serverProcessingQueue.values(), clientProcessingQueue.values())) {
|
||||
builder.add(handler.getAsyncListener());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
@ -203,13 +201,9 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
if (listener == null)
|
||||
throw new IllegalArgumentException("listener cannot be NULL.");
|
||||
|
||||
ListeningWhitelist sending = listener.getSendingWhitelist();
|
||||
ListeningWhitelist receiving = listener.getReceivingWhitelist();
|
||||
|
||||
// Do it in the opposite order
|
||||
if (serverTimeoutListeners.removeListener(listener, sending).size() > 0 ||
|
||||
clientTimeoutListeners.removeListener(listener, receiving).size() > 0) {
|
||||
timeoutListeners.remove(listener);
|
||||
if (timeoutListeners.remove(listener)) {
|
||||
outboundTimeoutListeners.removeListener(listener);
|
||||
inboundTimeoutListeners.removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,9 +227,9 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
return null;
|
||||
|
||||
for (PacketType type : search.getTypes()) {
|
||||
for (PrioritizedListener<AsyncListenerHandler> element : queue.getListener(type)) {
|
||||
if (element.getListener().getAsyncListener() == target) {
|
||||
return element.getListener();
|
||||
for (AsyncListenerHandler element : queue.get(type)) {
|
||||
if (element.getAsyncListener() == target) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,10 +286,10 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
private void unregisterAsyncHandlers(PacketProcessingQueue processingQueue, Plugin plugin) {
|
||||
|
||||
// Iterate through every packet listener
|
||||
for (PrioritizedListener<AsyncListenerHandler> listener : processingQueue.values()) {
|
||||
for (AsyncListenerHandler listener : processingQueue.values()) {
|
||||
// Remove the listener
|
||||
if (Objects.equal(listener.getListener().getPlugin(), plugin)) {
|
||||
unregisterAsyncHandler(listener.getListener());
|
||||
if (Objects.equal(listener.getPlugin(), plugin)) {
|
||||
unregisterAsyncHandler(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -346,8 +340,7 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
|
||||
@Override
|
||||
public boolean hasAsynchronousListeners(PacketEvent packet) {
|
||||
Collection<?> list = getProcessingQueue(packet).getListener(packet.getPacketType());
|
||||
return list != null && list.size() > 0;
|
||||
return getProcessingQueue(packet).contains(packet.getPacketType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -388,8 +381,8 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
playerSendingHandler.cleanupAll();
|
||||
timeoutListeners.clear();
|
||||
|
||||
serverTimeoutListeners = null;
|
||||
clientTimeoutListeners = null;
|
||||
outboundTimeoutListeners = null;
|
||||
inboundTimeoutListeners = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -413,6 +406,23 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
|
||||
// Only send if the packet is ready
|
||||
if (marker.decrementProcessingDelay() == 0) {
|
||||
|
||||
// Now, get the next non-cancelled listener
|
||||
if (!marker.hasExpired()) {
|
||||
for (; marker.getListenerTraversal().hasNext(); ) {
|
||||
AsyncListenerHandler handler = marker.getListenerTraversal().next();
|
||||
|
||||
if (!handler.isCancelled()) {
|
||||
marker.incrementProcessingDelay();
|
||||
handler.enqueuePacket(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There are no more listeners - queue the packet for transmission
|
||||
signalFreeProcessingSlot(packet, onMainThread);
|
||||
|
||||
PacketSendingQueue queue = getSendingQueue(packet, false);
|
||||
|
||||
// No need to create a new queue if the player has logged out
|
||||
|
@ -450,11 +460,18 @@ public class AsyncFilterManager implements AsynchronousManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Signal that a packet has finished processing.
|
||||
* Signal that a packet has finished processing. Tries to process further packets
|
||||
* if a processing slot is still free.
|
||||
* @param packet - packet to signal.
|
||||
* @param onMainThread whether or not this method was run by the main thread.
|
||||
*/
|
||||
public void signalFreeProcessingSlot(PacketEvent packet) {
|
||||
getProcessingQueue(packet).signalProcessingDone();
|
||||
public void signalFreeProcessingSlot(PacketEvent packet, boolean onMainThread) {
|
||||
PacketProcessingQueue queue = getProcessingQueue(packet);
|
||||
// mark slot as done
|
||||
queue.signalProcessingDone();
|
||||
|
||||
// start processing next slot if possible
|
||||
queue.signalBeginProcessing(onMainThread);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
|
@ -32,14 +34,11 @@ import com.comphenix.protocol.events.PacketAdapter;
|
|||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.scheduler.Task;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
|
||||
import com.comphenix.protocol.timing.TimedTracker;
|
||||
import com.comphenix.protocol.timing.TimingListenerType;
|
||||
import com.comphenix.protocol.timing.TimingTrackerManager;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a handler for an asynchronous event.
|
||||
* <p>
|
||||
|
@ -100,9 +99,6 @@ public class AsyncListenerHandler {
|
|||
// Warn plugins that the async listener handler must be started
|
||||
private Task warningTask;
|
||||
|
||||
// Timing manager
|
||||
private TimedListenerManager timedManager = TimedListenerManager.getInstance();
|
||||
|
||||
/**
|
||||
* Construct a manager for an asynchronous packet handler.
|
||||
* @param mainThread - the main game thread.
|
||||
|
@ -326,9 +322,9 @@ public class AsyncListenerHandler {
|
|||
|
||||
scheduleAsync(() -> delegateCopy.apply(listenerLoop));
|
||||
}
|
||||
|
||||
|
||||
private void scheduleAsync(Runnable runnable) {
|
||||
listener.getPlugin().getServer().getScheduler().runTaskAsynchronously(listener.getPlugin(), runnable);
|
||||
filterManager.getScheduler().runTaskAsync(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -600,27 +596,13 @@ public class AsyncListenerHandler {
|
|||
marker.setListenerHandler(this);
|
||||
marker.setWorkerID(workerID);
|
||||
|
||||
// We're not THAT worried about performance here
|
||||
if (timedManager.isTiming()) {
|
||||
// Retrieve the tracker to use
|
||||
TimedTracker tracker = timedManager.getTracker(listener,
|
||||
packet.isServerPacket() ? ListenerType.ASYNC_SERVER_SIDE : ListenerType.ASYNC_CLIENT_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
if (packet.isServerPacket())
|
||||
listener.onPacketSending(packet);
|
||||
else
|
||||
listener.onPacketReceiving(packet);
|
||||
|
||||
// And we're done
|
||||
tracker.endTracking(token, packet.getPacketType());
|
||||
|
||||
} else {
|
||||
if (packet.isServerPacket())
|
||||
listener.onPacketSending(packet);
|
||||
else
|
||||
listener.onPacketReceiving(packet);
|
||||
}
|
||||
TimingTrackerManager.get(listener, packet.isServerPacket() ? TimingListenerType.ASYNC_OUTBOUND : TimingListenerType.ASYNC_INBOUND)
|
||||
.track(packet.getPacketType(), () -> {
|
||||
if (packet.isServerPacket())
|
||||
listener.onPacketSending(packet);
|
||||
else
|
||||
listener.onPacketReceiving(packet);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
|
@ -632,21 +614,6 @@ public class AsyncListenerHandler {
|
|||
filterManager.getErrorReporter().reportMinimal(listener.getPlugin(), methodName, e);
|
||||
}
|
||||
|
||||
// Now, get the next non-cancelled listener
|
||||
if (!marker.hasExpired()) {
|
||||
for (; marker.getListenerTraversal().hasNext(); ) {
|
||||
AsyncListenerHandler handler = marker.getListenerTraversal().next().getListener();
|
||||
|
||||
if (!handler.isCancelled()) {
|
||||
handler.enqueuePacket(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There are no more listeners - queue the packet for transmission
|
||||
filterManager.signalFreeProcessingSlot(packet);
|
||||
|
||||
// Note that listeners can opt to delay the packet transmission
|
||||
filterManager.signalPacketTransmission(packet);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import com.comphenix.protocol.PacketType;
|
|||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
@ -70,7 +69,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
|||
/**
|
||||
* Current list of async packet listeners.
|
||||
*/
|
||||
private transient Iterator<PrioritizedListener<AsyncListenerHandler>> listenerTraversal;
|
||||
private transient Iterator<AsyncListenerHandler> listenerTraversal;
|
||||
|
||||
// Timeout handling
|
||||
private long initialTime;
|
||||
|
@ -366,7 +365,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
|||
* Retrieve iterator for the next listener in line.
|
||||
* @return Next async packet listener iterator.
|
||||
*/
|
||||
Iterator<PrioritizedListener<AsyncListenerHandler>> getListenerTraversal() {
|
||||
Iterator<AsyncListenerHandler> getListenerTraversal() {
|
||||
return listenerTraversal;
|
||||
}
|
||||
|
||||
|
@ -374,7 +373,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
|
|||
* Set the iterator for the next listener.
|
||||
* @param listenerTraversal - the new async packet listener iterator.
|
||||
*/
|
||||
void setListenerTraversal(Iterator<PrioritizedListener<AsyncListenerHandler>> listenerTraversal) {
|
||||
void setListenerTraversal(Iterator<AsyncListenerHandler> listenerTraversal) {
|
||||
this.listenerTraversal = listenerTraversal;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,18 +17,20 @@
|
|||
|
||||
package com.comphenix.protocol.async;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
|
||||
import com.comphenix.protocol.concurrent.PacketTypeMultiMap;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.MinMaxPriorityQueue;
|
||||
|
||||
|
||||
|
@ -37,7 +39,7 @@ import com.google.common.collect.MinMaxPriorityQueue;
|
|||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncListenerHandler> {
|
||||
class PacketProcessingQueue {
|
||||
public static final ReportType REPORT_GUAVA_CORRUPT_MISSING =
|
||||
new ReportType("Guava is either missing or corrupt. Reverting to PriorityQueue.");
|
||||
|
||||
|
@ -66,6 +68,16 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||
// Packets for sending
|
||||
private PlayerSendingHandler sendingHandler;
|
||||
|
||||
private final PacketTypeMultiMap<AsyncListenerHandler> map = new PacketTypeMultiMap<>();
|
||||
|
||||
public void addListener(AsyncListenerHandler listener, ListeningWhitelist whitelist) {
|
||||
map.put(whitelist, listener);
|
||||
}
|
||||
|
||||
public List<PacketType> removeListener(AsyncListenerHandler listener, ListeningWhitelist whitelist) {
|
||||
return map.remove(whitelist, listener);
|
||||
}
|
||||
|
||||
public PacketProcessingQueue(PlayerSendingHandler sendingHandler) {
|
||||
this(sendingHandler, INITIAL_CAPACITY, DEFAULT_QUEUE_LIMIT, DEFAULT_MAXIMUM_CONCURRENCY);
|
||||
}
|
||||
|
@ -131,17 +143,17 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||
if (holder != null) {
|
||||
PacketEvent packet = holder.getEvent();
|
||||
AsyncMarker marker = packet.getAsyncMarker();
|
||||
Collection<PrioritizedListener<AsyncListenerHandler>> list = getListener(packet.getPacketType());
|
||||
Iterable<AsyncListenerHandler> list = map.get(packet.getPacketType());
|
||||
|
||||
marker.incrementProcessingDelay();
|
||||
|
||||
// Yes, removing the marker will cause the chain to stop
|
||||
if (list != null) {
|
||||
Iterator<PrioritizedListener<AsyncListenerHandler>> iterator = list.iterator();
|
||||
Iterator<AsyncListenerHandler> iterator = list.iterator();
|
||||
|
||||
if (iterator.hasNext()) {
|
||||
marker.setListenerTraversal(iterator);
|
||||
iterator.next().getListener().enqueuePacket(packet);
|
||||
iterator.next().enqueuePacket(packet);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -178,17 +190,31 @@ class PacketProcessingQueue extends AbstractConcurrentListenerMultimap<AsyncList
|
|||
public int getMaximumConcurrency() {
|
||||
return maximumConcurrency;
|
||||
}
|
||||
|
||||
public boolean contains(PacketType packetType) {
|
||||
return map.contains(packetType);
|
||||
}
|
||||
|
||||
public Iterable<AsyncListenerHandler> get(PacketType packetType) {
|
||||
return map.get(packetType);
|
||||
}
|
||||
|
||||
public ImmutableSet<PacketType> keySet() {
|
||||
return map.getPacketTypes();
|
||||
}
|
||||
|
||||
public Iterable<AsyncListenerHandler> values() {
|
||||
return map.values();
|
||||
}
|
||||
|
||||
public void cleanupAll() {
|
||||
// Cancel all the threads and every listener
|
||||
for (PrioritizedListener<AsyncListenerHandler> handler : values()) {
|
||||
if (handler != null) {
|
||||
handler.getListener().cancel();
|
||||
}
|
||||
for (AsyncListenerHandler handler : map.values()) {
|
||||
handler.cancel();
|
||||
}
|
||||
|
||||
// Remove the rest, just in case
|
||||
clearListeners();
|
||||
map.clear();
|
||||
|
||||
// Remove every packet in the queue
|
||||
processingQueue.clear();
|
||||
|
|
|
@ -24,27 +24,26 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.ConcurrentPlayerMap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.SortedPacketListenerList;
|
||||
import com.comphenix.protocol.injector.collection.InboundPacketListenerSet;
|
||||
import com.comphenix.protocol.injector.collection.OutboundPacketListenerSet;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Contains every sending queue for every player.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
class PlayerSendingHandler {
|
||||
private final ErrorReporter reporter;
|
||||
private final ConcurrentMap<Player, QueueContainer> playerSendingQueues;
|
||||
|
||||
// Timeout listeners
|
||||
private final SortedPacketListenerList serverTimeoutListeners;
|
||||
private final SortedPacketListenerList clientTimeoutListeners;
|
||||
private final OutboundPacketListenerSet outboundTimeoutListeners;
|
||||
private final InboundPacketListenerSet inboundTimeoutListeners;
|
||||
|
||||
// Asynchronous packet sending
|
||||
private Executor asynchronousSender;
|
||||
|
@ -58,37 +57,37 @@ class PlayerSendingHandler {
|
|||
* @author Kristian
|
||||
*/
|
||||
private class QueueContainer {
|
||||
private final PacketSendingQueue serverQueue;
|
||||
private final PacketSendingQueue clientQueue;
|
||||
private final PacketSendingQueue outboundQueue;
|
||||
private final PacketSendingQueue inboundQueue;
|
||||
|
||||
public QueueContainer() {
|
||||
// Server packets can be sent concurrently
|
||||
serverQueue = new PacketSendingQueue(false, asynchronousSender) {
|
||||
outboundQueue = new PacketSendingQueue(false, asynchronousSender) {
|
||||
@Override
|
||||
protected void onPacketTimeout(PacketEvent event) {
|
||||
if (!cleaningUp) {
|
||||
serverTimeoutListeners.invokePacketSending(reporter, event);
|
||||
outboundTimeoutListeners.invoke(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Client packets must be synchronized
|
||||
clientQueue = new PacketSendingQueue(true, asynchronousSender) {
|
||||
inboundQueue = new PacketSendingQueue(true, asynchronousSender) {
|
||||
@Override
|
||||
protected void onPacketTimeout(PacketEvent event) {
|
||||
if (!cleaningUp) {
|
||||
clientTimeoutListeners.invokePacketSending(reporter, event);
|
||||
inboundTimeoutListeners.invoke(event);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public PacketSendingQueue getServerQueue() {
|
||||
return serverQueue;
|
||||
public PacketSendingQueue getOutboundQueue() {
|
||||
return outboundQueue;
|
||||
}
|
||||
|
||||
public PacketSendingQueue getClientQueue() {
|
||||
return clientQueue;
|
||||
public PacketSendingQueue getInboundQueue() {
|
||||
return inboundQueue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,12 +97,9 @@ class PlayerSendingHandler {
|
|||
* @param serverTimeoutListeners - set of server timeout listeners.
|
||||
* @param clientTimeoutListeners - set of client timeout listeners.
|
||||
*/
|
||||
public PlayerSendingHandler(ErrorReporter reporter,
|
||||
SortedPacketListenerList serverTimeoutListeners, SortedPacketListenerList clientTimeoutListeners) {
|
||||
|
||||
this.reporter = reporter;
|
||||
this.serverTimeoutListeners = serverTimeoutListeners;
|
||||
this.clientTimeoutListeners = clientTimeoutListeners;
|
||||
public PlayerSendingHandler(OutboundPacketListenerSet serverTimeoutListeners, InboundPacketListenerSet clientTimeoutListeners) {
|
||||
this.outboundTimeoutListeners = serverTimeoutListeners;
|
||||
this.inboundTimeoutListeners = clientTimeoutListeners;
|
||||
|
||||
// Initialize storage of queues
|
||||
this.playerSendingQueues = ConcurrentPlayerMap.usingAddress();
|
||||
|
@ -154,7 +150,7 @@ class PlayerSendingHandler {
|
|||
|
||||
// Check for NULL again
|
||||
if (queues != null)
|
||||
return packet.isServerPacket() ? queues.getServerQueue() : queues.getClientQueue();
|
||||
return packet.isServerPacket() ? queues.getOutboundQueue() : queues.getInboundQueue();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
@ -165,8 +161,8 @@ class PlayerSendingHandler {
|
|||
public void sendAllPackets() {
|
||||
if (!cleaningUp) {
|
||||
for (QueueContainer queues : playerSendingQueues.values()) {
|
||||
queues.getClientQueue().cleanupAll();
|
||||
queues.getServerQueue().cleanupAll();
|
||||
queues.getInboundQueue().cleanupAll();
|
||||
queues.getOutboundQueue().cleanupAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +175,7 @@ class PlayerSendingHandler {
|
|||
public void sendServerPackets(List<PacketType> types, boolean synchronusOK) {
|
||||
if (!cleaningUp) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
queue.getServerQueue().signalPacketUpdate(types, synchronusOK);
|
||||
queue.getOutboundQueue().signalPacketUpdate(types, synchronusOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +187,7 @@ class PlayerSendingHandler {
|
|||
public void sendClientPackets(List<PacketType> types, boolean synchronusOK) {
|
||||
if (!cleaningUp) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
queue.getClientQueue().signalPacketUpdate(types, synchronusOK);
|
||||
queue.getInboundQueue().signalPacketUpdate(types, synchronusOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +198,7 @@ class PlayerSendingHandler {
|
|||
*/
|
||||
public void trySendServerPackets(boolean onMainThread) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
queue.getServerQueue().trySendPackets(onMainThread);
|
||||
queue.getOutboundQueue().trySendPackets(onMainThread);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,7 +208,7 @@ class PlayerSendingHandler {
|
|||
*/
|
||||
public void trySendClientPackets(boolean onMainThread) {
|
||||
for (QueueContainer queue : playerSendingQueues.values()) {
|
||||
queue.getClientQueue().trySendPackets(onMainThread);
|
||||
queue.getInboundQueue().trySendPackets(onMainThread);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +220,7 @@ class PlayerSendingHandler {
|
|||
List<PacketSendingQueue> result = new ArrayList<>();
|
||||
|
||||
for (QueueContainer queue : playerSendingQueues.values())
|
||||
result.add(queue.getServerQueue());
|
||||
result.add(queue.getOutboundQueue());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -236,7 +232,7 @@ class PlayerSendingHandler {
|
|||
List<PacketSendingQueue> result = new ArrayList<>();
|
||||
|
||||
for (QueueContainer queue : playerSendingQueues.values())
|
||||
result.add(queue.getClientQueue());
|
||||
result.add(queue.getInboundQueue());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.injector.PrioritizedListener;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* A thread-safe implementation of a listener multimap.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class AbstractConcurrentListenerMultimap<T> {
|
||||
|
||||
// The core of our map
|
||||
private final ConcurrentMap<PacketType, SortedCopyOnWriteArray<PrioritizedListener<T>>> mapListeners;
|
||||
|
||||
protected AbstractConcurrentListenerMultimap() {
|
||||
this.mapListeners = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to its requested list of packet receivers.
|
||||
*
|
||||
* @param listener - listener with a list of packets to receive notifications for.
|
||||
* @param whitelist - the packet whitelist to use.
|
||||
*/
|
||||
public void addListener(T listener, ListeningWhitelist whitelist) {
|
||||
final PrioritizedListener<T> prioritized = new PrioritizedListener<>(listener, whitelist.getPriority());
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
this.addListener(type, prioritized);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the listener to a specific packet notification list
|
||||
private void addListener(PacketType type, PrioritizedListener<T> listener) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<T>> list = this.mapListeners.get(type);
|
||||
|
||||
// We don't want to create this for every lookup
|
||||
if (list == null) {
|
||||
// It would be nice if we could use a PriorityBlockingQueue, but it doesn't preserve iterator order,
|
||||
// which is an essential feature for our purposes.
|
||||
final SortedCopyOnWriteArray<PrioritizedListener<T>> value = new SortedCopyOnWriteArray<>();
|
||||
|
||||
// We may end up creating multiple multisets, but we'll agree on which to use
|
||||
list = this.mapListeners.putIfAbsent(type, value);
|
||||
|
||||
if (list == null) {
|
||||
list = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Thread safe
|
||||
list.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given listener from the packet event list.
|
||||
*
|
||||
* @param listener - listener to remove.
|
||||
* @param whitelist - the packet whitelist that was used.
|
||||
* @return Every packet ID that was removed due to no listeners.
|
||||
*/
|
||||
public List<PacketType> removeListener(T listener, ListeningWhitelist whitelist) {
|
||||
List<PacketType> removedPackets = new ArrayList<>();
|
||||
|
||||
// Again, not terribly efficient. But adding or removing listeners should be a rare event.
|
||||
for (PacketType type : whitelist.getTypes()) {
|
||||
SortedCopyOnWriteArray<PrioritizedListener<T>> list = this.mapListeners.get(type);
|
||||
|
||||
if(list == null || list.isEmpty()) continue;
|
||||
|
||||
// Remove any listeners
|
||||
// Remove this listener. Note that priority is generally ignored.
|
||||
list.remove(new PrioritizedListener<T>(listener, whitelist.getPriority()));
|
||||
|
||||
if (list.isEmpty()) {
|
||||
this.mapListeners.remove(type);
|
||||
removedPackets.add(type);
|
||||
}
|
||||
// Move on to the next
|
||||
}
|
||||
return removedPackets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the registered listeners, in order from the lowest to the highest priority.
|
||||
* <p>
|
||||
* The returned list is thread-safe and doesn't require synchronization.
|
||||
*
|
||||
* @param type - packet type.
|
||||
* @return Registered listeners.
|
||||
*/
|
||||
public Collection<PrioritizedListener<T>> getListener(PacketType type) {
|
||||
return this.mapListeners.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every listener.
|
||||
*
|
||||
* @return Every listener.
|
||||
*/
|
||||
public Iterable<PrioritizedListener<T>> values() {
|
||||
return Iterables.concat(this.mapListeners.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every registered packet type:
|
||||
*
|
||||
* @return Registered packet type.
|
||||
*/
|
||||
public Set<PacketType> keySet() {
|
||||
return this.mapListeners.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all packet listeners.
|
||||
*/
|
||||
protected void clearListeners() {
|
||||
this.mapListeners.clear();
|
||||
}
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An implicitly sorted array list that preserves insertion order and maintains duplicates.
|
||||
*
|
||||
* @param <T> - type of the elements in the list.
|
||||
*/
|
||||
public class SortedCopyOnWriteArray<T extends Comparable<T>> implements Collection<T> {
|
||||
|
||||
// Prevent reordering
|
||||
private volatile List<T> list;
|
||||
|
||||
/**
|
||||
* Construct an empty sorted array.
|
||||
*/
|
||||
public SortedCopyOnWriteArray() {
|
||||
this.list = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a sorted array from the given list. The elements will be automatically sorted.
|
||||
*
|
||||
* @param wrapped - the collection whose elements are to be placed into the list.
|
||||
*/
|
||||
public SortedCopyOnWriteArray(Collection<T> wrapped) {
|
||||
this.list = new ArrayList<>(wrapped);
|
||||
Collections.sort(this.list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the given element in the proper location.
|
||||
*
|
||||
* @param value - element to insert.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean add(T value) {
|
||||
// We use NULL as a special marker, so we don't allow it
|
||||
if (value == null) throw new IllegalArgumentException("value cannot be NULL");
|
||||
|
||||
List<T> copy = new ArrayList<>();
|
||||
boolean inserted = false;
|
||||
|
||||
for (T element : this.list) {
|
||||
// If the value is now greater than the current element, it should be placed right before it
|
||||
if (!inserted && value.compareTo(element) < 0) {
|
||||
copy.add(value);
|
||||
inserted = true;
|
||||
}
|
||||
copy.add(element);
|
||||
}
|
||||
|
||||
// Don't forget to add it
|
||||
if (!inserted) {
|
||||
copy.add(value);
|
||||
}
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection<? extends T> values) {
|
||||
if (values == null) throw new IllegalArgumentException("values cannot be NULL");
|
||||
|
||||
if (values.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
|
||||
// Insert the new content and sort it
|
||||
copy.addAll(values);
|
||||
Collections.sort(copy);
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a new list with every element except the one given.
|
||||
* <p>
|
||||
* Objects will be compared using the given objects equals() method.
|
||||
*
|
||||
* @param value - value to remove.
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean remove(Object value) {
|
||||
List<T> copy = new ArrayList<>();
|
||||
boolean result = false;
|
||||
|
||||
// Note that there's not much to be gained from using BinarySearch, as we
|
||||
// have to copy (and thus read) the entire list regardless.
|
||||
|
||||
// Copy every element except the one given to us.
|
||||
for (T element : this.list) {
|
||||
if (!Objects.equal(value, element)) {
|
||||
copy.add(element);
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.list = copy;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> values) {
|
||||
// Special cases
|
||||
if (values == null) throw new IllegalArgumentException("values cannot be NULL");
|
||||
|
||||
if (values.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
copy.removeAll(values);
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> values) {
|
||||
// Special cases
|
||||
if (values == null) throw new IllegalArgumentException("values cannot be NULL");
|
||||
|
||||
if (values.isEmpty()) return false;
|
||||
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
copy.removeAll(values);
|
||||
|
||||
this.list = copy;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes from the list by making a copy of every element except the one with the given index.
|
||||
*
|
||||
* @param index - index of the element to remove.
|
||||
*/
|
||||
public synchronized void remove(int index) {
|
||||
List<T> copy = new ArrayList<>(this.list);
|
||||
|
||||
copy.remove(index);
|
||||
this.list = copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an element by index.
|
||||
*
|
||||
* @param index - index of element to retrieve.
|
||||
* @return The element at the given location.
|
||||
*/
|
||||
public T get(int index) {
|
||||
return this.list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the list.
|
||||
*
|
||||
* @return Size of the list.
|
||||
*/
|
||||
public int size() {
|
||||
return this.list.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an iterator over the elements in the given list. Warning: No not attempt to remove elements using the
|
||||
* iterator.
|
||||
*/
|
||||
public Iterator<T> iterator() {
|
||||
return Iterables.unmodifiableIterable(this.list).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.list = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object value) {
|
||||
return this.list.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> values) {
|
||||
return this.list.containsAll(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.list.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return this.list.toArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return this.list.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.list.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package com.comphenix.protocol.concurrent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Manages the association between packet types and their corresponding
|
||||
* listeners.
|
||||
* <p>
|
||||
* This class is thread-safe for modifications. All read methods work on a
|
||||
* lock-free, best-effort principle, ensuring fast access.
|
||||
* </p>
|
||||
*/
|
||||
public class PacketTypeListenerSet {
|
||||
|
||||
// Map to store packet types and their associated listeners
|
||||
private final Map<PacketType, Set<PacketListener>> typeMap = new HashMap<>();
|
||||
// Set to store packet classes of packet types that have listeners
|
||||
private final Set<Class<?>> classSet = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Adds a listener for a specific packet type.
|
||||
*
|
||||
* @param packetType the packet type
|
||||
* @param listener the listener to add
|
||||
* @return {@code true} if the listener and packetType was added to the set;
|
||||
* {@code false} otherwise
|
||||
* @throws NullPointerException if the packetType or listener is null
|
||||
*/
|
||||
public synchronized boolean add(PacketType packetType, PacketListener listener) {
|
||||
Objects.requireNonNull(packetType, "packetType cannot be null");
|
||||
Objects.requireNonNull(listener, "listener cannot be null");
|
||||
|
||||
Set<PacketListener> listenerSet = this.typeMap.computeIfAbsent(packetType, key -> new HashSet<>());
|
||||
if (!listenerSet.add(listener)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we can always add the packet class here as long as the listener got added
|
||||
this.classSet.add(packetType.getPacketClass());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for a specific packet type.
|
||||
*
|
||||
* @param packetType the packet type
|
||||
* @param listener the listener to remove
|
||||
* @return {@code true} if the set contained the specified listener;
|
||||
* {@code false} otherwise
|
||||
* @throws NullPointerException if the packetType or listener is null
|
||||
*/
|
||||
public synchronized boolean remove(PacketType packetType, PacketListener listener) {
|
||||
Objects.requireNonNull(packetType, "packetType cannot be null");
|
||||
Objects.requireNonNull(listener, "listener cannot be null");
|
||||
|
||||
Set<PacketListener> listenerSet = this.typeMap.get(packetType);
|
||||
if (listenerSet == null) {
|
||||
// this should never happen so better check for it during unit tests
|
||||
assert !classSet.contains(packetType.getPacketClass());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!listenerSet.remove(listener)) {
|
||||
// return since the listenerSet didn't change
|
||||
return false;
|
||||
}
|
||||
|
||||
// packet type has no listeners remove type
|
||||
if (listenerSet.isEmpty()) {
|
||||
this.typeMap.remove(packetType);
|
||||
this.classSet.remove(packetType.getPacketClass());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any listeners for a specific packet type.
|
||||
*
|
||||
* @param packetType the packet type
|
||||
* @return true if there are listeners for the packet type, false otherwise
|
||||
*/
|
||||
public boolean contains(PacketType packetType) {
|
||||
return this.typeMap.containsKey(packetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any listeners for a specific packet class.
|
||||
*
|
||||
* @param packetClass the packet class
|
||||
* @return true if there are listeners for the packet class, false otherwise
|
||||
*/
|
||||
public boolean contains(Class<?> packetClass) {
|
||||
return this.classSet.contains(packetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the packet types that have listeners.
|
||||
*
|
||||
* @return a set of packet types that have listeners
|
||||
*/
|
||||
public ImmutableSet<PacketType> values() {
|
||||
return ImmutableSet.copyOf(this.typeMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all listeners and their associated packet types.
|
||||
*/
|
||||
public void clear() {
|
||||
this.typeMap.clear();
|
||||
this.classSet.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package com.comphenix.protocol.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* A map-like data structure that associates {@link PacketType}s with sets of
|
||||
* values. The values are stored in a {@link SortedCopyOnWriteSet}, which
|
||||
* ensures that the elements are kept in a sorted order based on the
|
||||
* {@link ListenerPriority} of the {@link ListeningWhitelist}, while maintaining
|
||||
* their insertion order for elements with equal priorities.
|
||||
* <p>
|
||||
* This class is thread-safe for modifications and guarantees a
|
||||
* modification-free iteration of associated values per packet type. All read
|
||||
* methods work on a lock-free, best-effort principle, ensuring fast access.
|
||||
* </p>
|
||||
*
|
||||
* @param <T> the type of elements maintained by this map
|
||||
*/
|
||||
public class PacketTypeMultiMap<T> {
|
||||
|
||||
private final Map<PacketType, SortedCopyOnWriteSet<T, PriorityHolder>> typeMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Adds a value to the map, associating it with the {@link PacketType}s
|
||||
* contained in the specified {@link ListeningWhitelist}. If the value is
|
||||
* already present in the set (as determined by {@code equals}), it will not be
|
||||
* added again.
|
||||
*
|
||||
* @param key the whitelist containing the packet types to associate the value
|
||||
* with
|
||||
* @param value the value to be added
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
public synchronized void put(ListeningWhitelist key, T value) {
|
||||
Objects.requireNonNull(key, "key cannot be null");
|
||||
Objects.requireNonNull(value, "value cannot be null");
|
||||
|
||||
for (PacketType packetType : key.getTypes()) {
|
||||
this.typeMap.computeIfAbsent(packetType, type -> new SortedCopyOnWriteSet<>()).add(value,
|
||||
new PriorityHolder(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a value from the map, disassociating it from the {@link PacketType}s
|
||||
* contained in the specified {@link ListeningWhitelist}. If the value is not
|
||||
* present, the map remains unchanged.
|
||||
*
|
||||
* @param key the whitelist containing the packet types to disassociate the
|
||||
* value from
|
||||
* @param value the value to be removed
|
||||
* @return a list of packet types that got removed because they don't have any
|
||||
* associated values anymore
|
||||
* @throws NullPointerException if the key or value is null
|
||||
*/
|
||||
public synchronized List<PacketType> remove(ListeningWhitelist key, T value) {
|
||||
Objects.requireNonNull(key, "key cannot be null");
|
||||
Objects.requireNonNull(value, "value cannot be null");
|
||||
|
||||
List<PacketType> removedTypes = new ArrayList<>();
|
||||
|
||||
for (PacketType packetType : key.getTypes()) {
|
||||
SortedCopyOnWriteSet<T, PriorityHolder> entrySet = this.typeMap.get(packetType);
|
||||
if (entrySet == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// we shouldn't have empty entrySets
|
||||
assert !entrySet.isEmpty();
|
||||
|
||||
// continue if value wasn't removed
|
||||
if (!entrySet.remove(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove packet type without entries
|
||||
if (entrySet.isEmpty()) {
|
||||
this.typeMap.remove(packetType);
|
||||
removedTypes.add(packetType);
|
||||
}
|
||||
}
|
||||
|
||||
return removedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable set of all {@link PacketType}s currently in the map.
|
||||
*
|
||||
* @return an immutable set of packet types
|
||||
*/
|
||||
public ImmutableSet<PacketType> getPacketTypes() {
|
||||
return ImmutableSet.copyOf(this.typeMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified {@link PacketType} is contained in the map.
|
||||
*
|
||||
* @param packetType the packet type to check for
|
||||
* @return {@code true} if the packet type is contained in the map,
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public boolean contains(PacketType packetType) {
|
||||
return this.typeMap.containsKey(packetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterable of values associated with a specified {@link PacketType}.
|
||||
* If no values are associated with the packet type, an empty iterator is
|
||||
* returned.
|
||||
*
|
||||
* @param packetType the packet type to retrieve values for
|
||||
* @return an iterable of values associated with the packet type
|
||||
*/
|
||||
public Iterable<T> get(PacketType packetType) {
|
||||
return () -> {
|
||||
SortedCopyOnWriteSet<T, PriorityHolder> entrySet = this.typeMap.get(packetType);
|
||||
|
||||
if (entrySet != null) {
|
||||
return entrySet.iterator();
|
||||
}
|
||||
|
||||
return Collections.emptyIterator();
|
||||
};
|
||||
}
|
||||
|
||||
public Iterable<T> values() {
|
||||
return Iterables.concat(this.typeMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all entries from the map.
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
this.typeMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* A holder for priority information used to order elements within the
|
||||
* {@link SortedCopyOnWriteSet}.
|
||||
*/
|
||||
private static class PriorityHolder implements Comparable<PriorityHolder> {
|
||||
|
||||
private final ListenerPriority priority;
|
||||
|
||||
public PriorityHolder(ListeningWhitelist key) {
|
||||
this.priority = key.getPriority();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PriorityHolder other) {
|
||||
return Integer.compare(this.priority.getSlot(), other.priority.getSlot());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package com.comphenix.protocol.concurrent;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A collection that stores elements in a sorted order based on a provided
|
||||
* {@link Comparable}, while ensuring element equality is checked using their
|
||||
* {@code equals} method.
|
||||
* <p>
|
||||
* This class uses a copy-on-write strategy for updates, ensuring that iteration
|
||||
* over the collection is safe for concurrent use, even though the collection
|
||||
* itself is not thread-safe for modifications.
|
||||
* <p>
|
||||
* Elements are inserted into the set in a position determined by their natural
|
||||
* ordering. If multiple elements have comparables that are considered equal
|
||||
* (i.e., {@code compareTo} returns zero), they will maintain their insertion
|
||||
* order. If an element is already present in the set (as determined by
|
||||
* {@code equals}), it will not be added again.
|
||||
*
|
||||
* @param <E> the type of elements maintained by this set
|
||||
* @param <C> the type of the comparable used for ordering the elements
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class SortedCopyOnWriteSet<E, C extends Comparable<C>> implements Iterable<E> {
|
||||
|
||||
private volatile Entry<E, C>[] array = new Entry[0];
|
||||
|
||||
/**
|
||||
* Adds the specified element to this set in a sorted order based on the
|
||||
* provided {@code Comparable}. The element will be inserted before the first
|
||||
* position that is strictly greater than the element. This ensures that
|
||||
* elements maintain their insertion order when their comparables are considered
|
||||
* equal (i.e., when {@code compareTo} returns zero).
|
||||
* <p>
|
||||
* If the set already contains the element (as determined by {@code equals}),
|
||||
* the element is not added again.
|
||||
* </p>
|
||||
*
|
||||
* @param element the element to be added
|
||||
* @param comparable the comparable used to determine the element's position in
|
||||
* the sorted order
|
||||
* @return {@code true} if the element was added to the set; {@code false} if
|
||||
* the set already contained the element
|
||||
* @throws NullPointerException if the specified element is null
|
||||
*/
|
||||
public boolean add(E element, C comparable) {
|
||||
Objects.requireNonNull(element, "element cannot be null");
|
||||
|
||||
// create new entry
|
||||
Entry<E, C> entry = new Entry<>(element, comparable);
|
||||
|
||||
// Find correct insert index for element by compareTo, also use same loop to
|
||||
// scan for duplicate elements
|
||||
int insertIndex = -1;
|
||||
for (int index = 0; index < array.length; index++) {
|
||||
if (insertIndex == -1 && entry.compareTo(array[index]) < 0) {
|
||||
insertIndex = index;
|
||||
}
|
||||
|
||||
// array already contains element, return false
|
||||
if (array[index].is(element)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// insert at end of array
|
||||
if (insertIndex == -1) {
|
||||
insertIndex = array.length;
|
||||
}
|
||||
|
||||
// create a new array of size N+1
|
||||
Entry<E, C>[] newArray = new Entry[array.length + 1];
|
||||
|
||||
// copy the old array to the new array and insert the new element
|
||||
System.arraycopy(array, 0, newArray, 0, insertIndex);
|
||||
newArray[insertIndex] = entry;
|
||||
System.arraycopy(array, insertIndex, newArray, insertIndex + 1, array.length - insertIndex);
|
||||
|
||||
// copy new array to field
|
||||
array = newArray;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified element from this set if it is present.
|
||||
*
|
||||
* @param element the element to be removed
|
||||
* @return {@code true} if the set contained the specified element;
|
||||
* {@code false} otherwise
|
||||
* @throws NullPointerException if the specified element is null
|
||||
*/
|
||||
public boolean remove(E element) {
|
||||
Objects.requireNonNull(element, "element cannot be null");
|
||||
|
||||
// find the element in array
|
||||
int removeIndex = -1;
|
||||
for (int index = 0; index < array.length; index++) {
|
||||
if (array[index].is(element)) {
|
||||
removeIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// can't find element, return false
|
||||
if (removeIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// create a new array of size N-1
|
||||
Entry<E, C>[] newArray = new Entry[array.length - 1];
|
||||
|
||||
// copy the elements from the old array to the new array, excluding removed
|
||||
// element
|
||||
System.arraycopy(array, 0, newArray, 0, removeIndex);
|
||||
System.arraycopy(array, removeIndex + 1, newArray, removeIndex, array.length - removeIndex - 1);
|
||||
|
||||
// copy new array to field
|
||||
array = newArray;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this set contains no elements.
|
||||
*
|
||||
* @return {@code true} if this set contains no elements
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.array.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the elements in this set. The elements are returned
|
||||
* in natural order.
|
||||
*
|
||||
* @return an iterator over the elements in this set
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new ElementIterator(this.array);
|
||||
}
|
||||
|
||||
private class ElementIterator implements Iterator<E> {
|
||||
|
||||
private final Entry<E, C>[] array;
|
||||
private int cursor = 0;
|
||||
|
||||
public ElementIterator(Entry<E, C>[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.cursor < this.array.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (this.cursor >= this.array.length) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
int index = this.cursor++;
|
||||
return this.array[index].getElement();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Entry<E, C extends Comparable<C>> implements Comparable<Entry<E, C>> {
|
||||
|
||||
private E element;
|
||||
private C comperable;
|
||||
|
||||
public Entry(E element, C comperable) {
|
||||
this.element = element;
|
||||
this.comperable = comperable;
|
||||
}
|
||||
|
||||
public E getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Entry<E, C> other) {
|
||||
return this.comperable.compareTo(other.comperable);
|
||||
}
|
||||
|
||||
public boolean is(E element) {
|
||||
return this.element.equals(element);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,5 @@
|
|||
package com.comphenix.protocol.events;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
|
@ -17,7 +12,6 @@ import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
|||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
|
@ -31,6 +25,11 @@ import org.bukkit.potion.PotionEffectType;
|
|||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractStructure {
|
||||
protected transient Object handle;
|
||||
protected transient StructureModifier<Object> structureModifier;
|
||||
|
@ -410,7 +409,7 @@ public abstract class AbstractStructure {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of watchable objects.
|
||||
* Retrieves a read/write structure for collections of watchable objects before Minecraft 1.19.3.
|
||||
* <p>
|
||||
* This modifier will automatically marshal between the visible WrappedWatchableObject and the
|
||||
* internal Minecraft WatchableObject.
|
||||
|
@ -424,7 +423,7 @@ public abstract class AbstractStructure {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves a read/write structure for collections of data values.
|
||||
* Retrieves a read/write structure for collections of data values for Minecraft 1.19.3 or later.
|
||||
* @return A modifier for data values.
|
||||
*/
|
||||
public StructureModifier<List<WrappedDataValue>> getDataValueCollectionModifier() {
|
||||
|
@ -770,6 +769,10 @@ public abstract class AbstractStructure {
|
|||
* @return A modifier for MobEffectList fields.
|
||||
*/
|
||||
public StructureModifier<PotionEffectType> getEffectTypes() {
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
return getHolders(MinecraftReflection.getMobEffectListClass(), BukkitConverters.getEffectTypeConverter());
|
||||
}
|
||||
|
||||
// Convert to and from Bukkit
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getMobEffectListClass(),
|
||||
|
@ -794,11 +797,20 @@ public abstract class AbstractStructure {
|
|||
* @return A modifier for Holder fields
|
||||
* @param <T> Bukkit type
|
||||
*/
|
||||
public <T> StructureModifier<T> getHolders(Class<?> genericType,
|
||||
EquivalentConverter<T> converter) {
|
||||
public <T> StructureModifier<T> getHolders(Class<?> genericType, EquivalentConverter<T> converter) {
|
||||
Preconditions.checkNotNull(genericType, "genericType cannot be null");
|
||||
Preconditions.checkNotNull(converter, "converter cannot be null");
|
||||
|
||||
Class<?> holderClass = MinecraftReflection.getHolderClass();
|
||||
|
||||
WrappedRegistry registry = WrappedRegistry.getRegistry(genericType);
|
||||
if (registry == null) {
|
||||
throw new IllegalArgumentException("No registry found for " + genericType);
|
||||
}
|
||||
|
||||
return structureModifier.withParamType(
|
||||
MinecraftReflection.getHolderClass(),
|
||||
Converters.holder(converter, WrappedRegistry.getRegistry(genericType)),
|
||||
holderClass,
|
||||
Converters.ignoreNull(Converters.holder(converter, registry)),
|
||||
genericType
|
||||
);
|
||||
}
|
||||
|
@ -858,6 +870,55 @@ public abstract class AbstractStructure {
|
|||
EnumWrappers.getChatTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the DisplaySlot enum in 1.20.2.
|
||||
* @return A modifier for DisplaySlot enum fields.
|
||||
*/
|
||||
public StructureModifier<EnumWrappers.DisplaySlot> getDisplaySlots() {
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getDisplaySlotClass(),
|
||||
EnumWrappers.getDisplaySlotConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the RenderType enum.
|
||||
* @return A modifier for RenderType enum fields.
|
||||
*/
|
||||
public StructureModifier<EnumWrappers.RenderType> getRenderTypes() {
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getRenderTypeClass(),
|
||||
EnumWrappers.getRenderTypeConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the ChatFormatting enum.
|
||||
* @return A modifier for ChatFormatting enum fields.
|
||||
*/
|
||||
public StructureModifier<EnumWrappers.ChatFormatting> getChatFormattings() {
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getChatFormattingClass(),
|
||||
EnumWrappers.getChatFormattingConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for optional team parameters in 1.17+.
|
||||
* @return A modifier for optional team parameters fields.
|
||||
*/
|
||||
public StructureModifier<Optional<WrappedTeamParameters>> getOptionalTeamParameters() {
|
||||
return getOptionals(BukkitConverters.getWrappedTeamParametersConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the NumberFormat class in 1.20.4+.
|
||||
* @return A modifier for NumberFormat fields.
|
||||
*/
|
||||
public StructureModifier<WrappedNumberFormat> getNumberFormats() {
|
||||
return structureModifier.withType(
|
||||
MinecraftReflection.getNumberFormatClass().orElse(null),
|
||||
BukkitConverters.getWrappedNumberFormatConverter());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the MinecraftKey class.
|
||||
* @return A modifier for MinecraftKey fields.
|
||||
|
@ -868,6 +929,16 @@ public abstract class AbstractStructure {
|
|||
MinecraftKey.getConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for custom packet payloads (available since Minecraft 1.20.2).
|
||||
* @return A modifier for CustomPacketPayloads fields.
|
||||
*/
|
||||
public StructureModifier<CustomPacketPayloadWrapper> getCustomPacketPayloads() {
|
||||
return structureModifier.withType(
|
||||
CustomPacketPayloadWrapper.getCustomPacketPayloadClass(),
|
||||
CustomPacketPayloadWrapper.getConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for dimension IDs in 1.13.1+
|
||||
* @return A modifier for dimension IDs
|
||||
|
|
|
@ -31,9 +31,11 @@ import java.util.Set;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.StructureCache;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.ObjectWriter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
|
@ -56,10 +58,11 @@ import com.comphenix.protocol.utility.MinecraftMethods;
|
|||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a Minecraft packet indirectly.
|
||||
|
@ -78,7 +81,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
|||
// Used to clone packets
|
||||
private static final AggregateCloner DEEP_CLONER = AggregateCloner
|
||||
.newBuilder()
|
||||
.instanceProvider(StructureCache::newPacket)
|
||||
.instanceProvider(StructureCache::newInstance)
|
||||
.andThen(BukkitCloner.class)
|
||||
.andThen(ImmutableDetector.class)
|
||||
.andThen(JavaOptionalCloner.class)
|
||||
|
@ -89,7 +92,7 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
|||
|
||||
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner
|
||||
.newBuilder()
|
||||
.instanceProvider(StructureCache::newPacket)
|
||||
.instanceProvider(StructureCache::newInstance)
|
||||
.andThen(param -> {
|
||||
if (param == null)
|
||||
throw new IllegalArgumentException("Cannot be NULL.");
|
||||
|
@ -345,6 +348,11 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
|||
}
|
||||
|
||||
Function<Object, Object> deserializer = PACKET_DESERIALIZER_METHODS.computeIfAbsent(packetType, type -> {
|
||||
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass());
|
||||
if (streamCodec != null) {
|
||||
return streamCodec::decode;
|
||||
}
|
||||
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
// best guess - a constructor which takes a buffer as the only argument
|
||||
ConstructorAccessor bufferConstructor = Accessors.getConstructorAccessorOrNull(
|
||||
|
@ -392,7 +400,14 @@ public class PacketContainer extends AbstractStructure implements Serializable {
|
|||
}
|
||||
|
||||
Object targetBuffer = MinecraftReflection.createPacketDataSerializer(0);
|
||||
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);
|
||||
|
||||
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass());
|
||||
if (streamCodec != null) {
|
||||
streamCodec.encode(targetBuffer, handle);
|
||||
} else {
|
||||
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);
|
||||
}
|
||||
|
||||
return targetBuffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,25 @@
|
|||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
|
||||
import net.bytebuddy.description.ByteCodeElement;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
|
@ -35,22 +50,18 @@ import net.bytebuddy.implementation.bind.annotation.Origin;
|
|||
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.BanEntry;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a player object that can be serialized by Java.
|
||||
|
@ -141,6 +152,16 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
|||
return lastSeen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getRespawnLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO do we need to implement this?
|
||||
|
||||
public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
|
||||
|
@ -240,6 +261,24 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
|||
return banned;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BanEntry<PlayerProfile> ban(@Nullable String s, @Nullable Date date, @Nullable String s1) {
|
||||
setBanned(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BanEntry<PlayerProfile> ban(@Nullable String s, @Nullable Instant instant, @Nullable String s1) {
|
||||
setBanned(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BanEntry<PlayerProfile> ban(@Nullable String s, @Nullable Duration duration, @Nullable String s1) {
|
||||
setBanned(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setBanned(boolean banned) {
|
||||
this.banned = banned;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,12 @@ import com.comphenix.protocol.events.PacketEvent;
|
|||
*/
|
||||
public interface ListenerInvoker {
|
||||
|
||||
boolean hasInboundListener(PacketType packetType);
|
||||
|
||||
boolean hasOutboundListener(PacketType packetType);
|
||||
|
||||
boolean hasMainThreadListener(PacketType packetType);
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
*
|
||||
|
@ -46,6 +52,10 @@ public interface ListenerInvoker {
|
|||
*
|
||||
* @param packet - the packet.
|
||||
* @return The packet type.
|
||||
* @deprecated use
|
||||
* {@link com.comphenix.protocol.injector.packet.PacketRegistry#getPacketType(PacketType.Protocol, Class)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
PacketType getPacketType(Object packet);
|
||||
}
|
||||
|
|
|
@ -1,32 +1,5 @@
|
|||
package com.comphenix.protocol.injector;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.comphenix.protocol.injector.netty.manager.NetworkManagerInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -50,16 +23,43 @@ import org.bukkit.event.server.PluginDisableEvent;
|
|||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.async.AsyncFilterManager;
|
||||
import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
|
||||
import com.comphenix.protocol.injector.collection.InboundPacketListenerSet;
|
||||
import com.comphenix.protocol.injector.collection.OutboundPacketListenerSet;
|
||||
import com.comphenix.protocol.injector.collection.PacketListenerSet;
|
||||
import com.comphenix.protocol.injector.netty.WirePacket;
|
||||
import com.comphenix.protocol.injector.netty.manager.NetworkManagerInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
|
||||
// plugin verifier reports
|
||||
private static final ReportType PLUGIN_VERIFIER_ERROR = new ReportType("Plugin verifier error: %s");
|
||||
private static final ReportType INVALID_PLUGIN_VERIFY = new ReportType("Plugin %s does not %s on ProtocolLib");
|
||||
|
||||
// listener registration reports
|
||||
private static final ReportType UNSUPPORTED_PACKET = new ReportType(
|
||||
"Plugin %s tried to register listener for unknown packet %s [direction: from %s]");
|
||||
|
||||
// bukkit references
|
||||
private final Plugin plugin;
|
||||
private final Server server;
|
||||
|
@ -72,14 +72,14 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
private final PluginVerifier pluginVerifier;
|
||||
|
||||
// packet listeners
|
||||
private final SortedPacketListenerList inboundListeners;
|
||||
private final SortedPacketListenerList outboundListeners;
|
||||
private final PacketTypeListenerSet mainThreadPacketTypes;
|
||||
private final InboundPacketListenerSet inboundListeners;
|
||||
private final OutboundPacketListenerSet outboundListeners;
|
||||
|
||||
// only for api lookups
|
||||
private final Set<PacketListener> registeredListeners;
|
||||
|
||||
// injectors
|
||||
private final PacketInjector packetInjector;
|
||||
private final PlayerInjectionHandler playerInjectionHandler;
|
||||
private final NetworkManagerInjector networkManagerInjector;
|
||||
|
||||
|
@ -104,8 +104,9 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
|
||||
// packet listeners
|
||||
this.registeredListeners = new HashSet<>();
|
||||
this.inboundListeners = new SortedPacketListenerList();
|
||||
this.outboundListeners = new SortedPacketListenerList();
|
||||
this.mainThreadPacketTypes = new PacketTypeListenerSet();
|
||||
this.inboundListeners = new InboundPacketListenerSet(mainThreadPacketTypes, this.reporter);
|
||||
this.outboundListeners = new OutboundPacketListenerSet(mainThreadPacketTypes, this.reporter);
|
||||
|
||||
// injectors
|
||||
this.networkManagerInjector = new NetworkManagerInjector(
|
||||
|
@ -113,7 +114,6 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
builder.getServer(),
|
||||
this,
|
||||
builder.getReporter());
|
||||
this.packetInjector = this.networkManagerInjector.getPacketInjector();
|
||||
this.playerInjectionHandler = this.networkManagerInjector.getPlayerInjectionHandler();
|
||||
|
||||
// ensure that all packet types are loaded and synced
|
||||
|
@ -164,7 +164,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
// monitor listeners before doing so - they will not be able to change the event tho
|
||||
if (!filters) {
|
||||
// ensure we are on the main thread if any listener requires that
|
||||
if (this.playerInjectionHandler.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread()) {
|
||||
if (this.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread()) {
|
||||
NetworkMarker copy = marker; // okay fine
|
||||
ProtocolLibrary.getScheduler().scheduleSyncDelayedTask(
|
||||
() -> this.sendServerPacket(receiver, packet, copy, false), 1L);
|
||||
|
@ -173,7 +173,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
|
||||
// construct the event and post to all monitor listeners
|
||||
PacketEvent event = PacketEvent.fromServer(this, packet, marker, receiver, false);
|
||||
this.outboundListeners.invokePacketSending(this.reporter, event, ListenerPriority.MONITOR);
|
||||
this.outboundListeners.invoke(event, ListenerPriority.MONITOR);
|
||||
|
||||
// update the marker of the event without accidentally constructing it
|
||||
marker = NetworkMarker.getNetworkMarker(event);
|
||||
|
@ -216,7 +216,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
public void receiveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters) {
|
||||
if (!this.closed) {
|
||||
// make sure we are on the main thread if any listener of the packet needs it
|
||||
if (this.playerInjectionHandler.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread()) {
|
||||
if (this.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread()) {
|
||||
ProtocolLibrary.getScheduler().runTask(
|
||||
() -> this.receiveClientPacket(sender, packet, marker, filters));
|
||||
return;
|
||||
|
@ -226,7 +226,8 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
// check to which listeners we need to post the packet
|
||||
if (filters) {
|
||||
// post to all listeners
|
||||
PacketEvent event = this.packetInjector.packetReceived(packet, sender);
|
||||
PacketEvent event = PacketEvent.fromClient(this.networkManagerInjector, packet, null, sender);
|
||||
this.invokePacketReceiving(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
@ -235,7 +236,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
nmsPacket = event.getPacket().getHandle();
|
||||
} else {
|
||||
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender, false);
|
||||
this.inboundListeners.invokePacketRecieving(this.reporter, event, ListenerPriority.MONITOR);
|
||||
this.inboundListeners.invoke(event, ListenerPriority.MONITOR);
|
||||
}
|
||||
|
||||
// post to the player inject, reset our cancel state change
|
||||
|
@ -320,28 +321,20 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
if (outbound != null && outbound.isEnabled()) {
|
||||
// verification
|
||||
this.verifyWhitelist(listener, outbound);
|
||||
this.playerInjectionHandler.checkListener(listener);
|
||||
|
||||
// registration
|
||||
this.registeredListeners.add(listener);
|
||||
this.outboundListeners.addListener(listener, outbound);
|
||||
|
||||
// let the injectors know about the change as well
|
||||
this.registerPacketListenerInInjectors(listener, outbound.getTypes());
|
||||
this.outboundListeners.addListener(listener);
|
||||
}
|
||||
|
||||
// register as inbound listener if anything outbound is handled
|
||||
if (inbound != null && inbound.isEnabled()) {
|
||||
// verification
|
||||
this.verifyWhitelist(listener, inbound);
|
||||
this.playerInjectionHandler.checkListener(listener);
|
||||
|
||||
// registration
|
||||
this.registeredListeners.add(listener);
|
||||
this.inboundListeners.addListener(listener, inbound);
|
||||
|
||||
// let the injectors know about the change as well
|
||||
this.registerPacketListenerInInjectors(listener, inbound.getTypes());
|
||||
this.inboundListeners.addListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,18 +347,12 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
|
||||
// remove outbound listeners (if any)
|
||||
if (outbound != null && outbound.isEnabled()) {
|
||||
Collection<PacketType> removed = this.outboundListeners.removeListener(listener, outbound);
|
||||
if (!removed.isEmpty()) {
|
||||
this.unregisterPacketListenerInInjectors(removed);
|
||||
}
|
||||
this.outboundListeners.removeListener(listener);
|
||||
}
|
||||
|
||||
// remove inbound listeners (if any)
|
||||
if (inbound != null && inbound.isEnabled()) {
|
||||
Collection<PacketType> removed = this.inboundListeners.removeListener(listener, inbound);
|
||||
if (!removed.isEmpty()) {
|
||||
this.unregisterPacketListenerInInjectors(removed);
|
||||
}
|
||||
this.inboundListeners.removeListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -416,12 +403,12 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilterTypes() {
|
||||
return Collections.unmodifiableSet(this.playerInjectionHandler.getSendingFilters());
|
||||
return Collections.unmodifiableSet(this.outboundListeners.getPacketTypes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getReceivingFilterTypes() {
|
||||
return Collections.unmodifiableSet(this.packetInjector.getPacketHandlers());
|
||||
return Collections.unmodifiableSet(this.inboundListeners.getPacketTypes());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -471,7 +458,6 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void handleQuit(PlayerQuitEvent event) {
|
||||
PacketFilterManager.this.asyncFilterManager.removePlayer(event.getPlayer());
|
||||
PacketFilterManager.this.playerInjectionHandler.handleDisconnect(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
|
@ -494,11 +480,14 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
|
||||
// uninject all clutter
|
||||
this.networkManagerInjector.close();
|
||||
this.playerInjectionHandler.close();
|
||||
|
||||
// clear listener collections
|
||||
this.mainThreadPacketTypes.clear();
|
||||
this.inboundListeners.clear();
|
||||
this.outboundListeners.clear();
|
||||
|
||||
// cleanup
|
||||
this.registeredListeners.clear();
|
||||
this.packetInjector.cleanupAll();
|
||||
this.asyncFilterManager.cleanupAll();
|
||||
}
|
||||
}
|
||||
|
@ -513,6 +502,21 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInboundListener(PacketType packetType) {
|
||||
return this.inboundListeners.containsPacketType(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOutboundListener(PacketType packetType) {
|
||||
return this.outboundListeners.containsPacketType(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType packetType) {
|
||||
return this.mainThreadPacketTypes.contains(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokePacketReceiving(PacketEvent event) {
|
||||
if (!this.closed) {
|
||||
|
@ -541,7 +545,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
throw new IllegalArgumentException("Unable to associate given packet " + packet + " with a registered packet!");
|
||||
}
|
||||
|
||||
private void postPacketToListeners(SortedPacketListenerList listeners, PacketEvent event, boolean outbound) {
|
||||
private void postPacketToListeners(PacketListenerSet listeners, PacketEvent event, boolean outbound) {
|
||||
try {
|
||||
// append async marker if any async listener for the packet was registered
|
||||
if (this.asyncFilterManager.hasAsynchronousListeners(event)) {
|
||||
|
@ -549,11 +553,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
}
|
||||
|
||||
// post to sync listeners
|
||||
if (outbound) {
|
||||
listeners.invokePacketSending(this.reporter, event);
|
||||
} else {
|
||||
listeners.invokePacketRecieving(this.reporter, event);
|
||||
}
|
||||
listeners.invoke(event);
|
||||
|
||||
// check if we need to post the packet to the async handler
|
||||
if (!event.isCancelled() && event.getAsyncMarker() != null && !event.getAsyncMarker().isAsyncCancelled()) {
|
||||
|
@ -587,39 +587,4 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPacketListenerInInjectors(PacketListener listener, Collection<PacketType> packetTypes) {
|
||||
for (PacketType packetType : packetTypes) {
|
||||
// check the packet direction
|
||||
if (packetType.getSender() == Sender.SERVER) {
|
||||
// check if the packet is registered on the server side
|
||||
if (PacketRegistry.getServerPacketTypes().contains(packetType)) {
|
||||
this.playerInjectionHandler.addPacketHandler(packetType, listener.getSendingWhitelist().getOptions());
|
||||
} else {
|
||||
this.reporter.reportWarning(this, Report.newBuilder(UNSUPPORTED_PACKET)
|
||||
.messageParam(PacketAdapter.getPluginName(listener), packetType, packetType.getSender())
|
||||
.build());
|
||||
}
|
||||
} else if (packetType.getSender() == Sender.CLIENT) {
|
||||
// check if the packet is registered on the client side
|
||||
if (PacketRegistry.getClientPacketTypes().contains(packetType)) {
|
||||
this.packetInjector.addPacketHandler(packetType, listener.getReceivingWhitelist().getOptions());
|
||||
} else {
|
||||
this.reporter.reportWarning(this, Report.newBuilder(UNSUPPORTED_PACKET)
|
||||
.messageParam(PacketAdapter.getPluginName(listener), packetType, packetType.getSender())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterPacketListenerInInjectors(Collection<PacketType> packetTypes) {
|
||||
for (PacketType packetType : packetTypes) {
|
||||
if (packetType.getSender() == Sender.SERVER) {
|
||||
this.playerInjectionHandler.removePacketHandler(packetType);
|
||||
} else if (packetType.getSender() == Sender.CLIENT) {
|
||||
this.packetInjector.removePacketHandler(packetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,229 +0,0 @@
|
|||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.concurrency.AbstractConcurrentListenerMultimap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
|
||||
import com.comphenix.protocol.timing.TimedTracker;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Registry of synchronous packet listeners.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public final class SortedPacketListenerList extends AbstractConcurrentListenerMultimap<PacketListener> {
|
||||
// The current listener manager
|
||||
private TimedListenerManager timedManager = TimedListenerManager.getInstance();
|
||||
|
||||
public SortedPacketListenerList() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public void invokePacketRecieving(ErrorReporter reporter, PacketEvent event) {
|
||||
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
// The returned list is thread-safe
|
||||
if (timedManager.isTiming()) {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_CLIENT_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
// Measure and record the execution time
|
||||
invokeReceivingListener(reporter, event, element);
|
||||
tracker.endTracking(token, event.getPacketType());
|
||||
}
|
||||
} else {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
invokeReceivingListener(reporter, event, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener of the given priority.
|
||||
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
||||
* @param event - the packet event to invoke.
|
||||
* @param priorityFilter - the required priority for a listener to be invoked.
|
||||
*/
|
||||
public void invokePacketRecieving(ErrorReporter reporter, PacketEvent event, ListenerPriority priorityFilter) {
|
||||
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
// The returned list is thread-safe
|
||||
if (timedManager.isTiming()) {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
if (element.getPriority() == priorityFilter) {
|
||||
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_CLIENT_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
// Measure and record the execution time
|
||||
invokeReceivingListener(reporter, event, element);
|
||||
tracker.endTracking(token, event.getPacketType());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
if (element.getPriority() == priorityFilter) {
|
||||
invokeReceivingListener(reporter, event, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a particular receiving listener.
|
||||
* @param reporter - the error reporter.
|
||||
* @param event - the related packet event.
|
||||
* @param element - the listener to invoke.
|
||||
*/
|
||||
private void invokeReceivingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
||||
try {
|
||||
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
|
||||
element.getListener().onPacketReceiving(event);
|
||||
|
||||
} catch (OutOfMemoryError | ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketReceiving(PacketEvent)", e,
|
||||
event.getPacket().getHandle());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
public void invokePacketSending(ErrorReporter reporter, PacketEvent event) {
|
||||
invokePacketSending(reporter, event, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener of the given priority.
|
||||
* @param reporter - the error reporter that will be used to inform about listener exceptions.
|
||||
* @param event - the packet event to invoke.
|
||||
* @param priorityFilter - the priority for a listener to be invoked. If null is provided, every registered listener will be invoked
|
||||
*/
|
||||
public void invokePacketSending(ErrorReporter reporter, PacketEvent event, @Nullable ListenerPriority priorityFilter) {
|
||||
invokeUnpackedPacketSending(reporter, event, priorityFilter);
|
||||
if (event.getPacketType() == PacketType.Play.Server.BUNDLE && !event.isCancelled()) {
|
||||
// unpack the bundle and invoke for each packet in the bundle
|
||||
Iterable<PacketContainer> packets = event.getPacket().getPacketBundles().read(0);
|
||||
List<PacketContainer> outPackets = new ArrayList<>();
|
||||
for (PacketContainer subPacket : packets) {
|
||||
if(subPacket == null) {
|
||||
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "Failed to invoke packet event " + (priorityFilter == null ? "" : ("with priority " + priorityFilter)) + " in bundle because bundle contains null packet: " + packets, new Throwable());
|
||||
continue;
|
||||
}
|
||||
PacketEvent subPacketEvent = PacketEvent.fromServer(this, subPacket, event.getNetworkMarker(), event.getPlayer());
|
||||
invokeUnpackedPacketSending(reporter, subPacketEvent, priorityFilter);
|
||||
|
||||
if (!subPacketEvent.isCancelled()) {
|
||||
// if the packet event has been cancelled, the packet will be removed from the bundle
|
||||
PacketContainer packet = subPacketEvent.getPacket();
|
||||
if(packet == null) {
|
||||
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet container returned for " + subPacketEvent, new Throwable());
|
||||
} else if(packet.getHandle() == null) {
|
||||
ProtocolLibrary.getPlugin().getLogger().log(Level.WARNING, "null packet handle returned for " + subPacketEvent, new Throwable());
|
||||
} else {
|
||||
outPackets.add(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (packets.iterator().hasNext()) {
|
||||
event.getPacket().getPacketBundles().write(0, outPackets);
|
||||
} else {
|
||||
// cancel entire packet if each individual packet has been cancelled
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeUnpackedPacketSending(ErrorReporter reporter, PacketEvent event, @org.jetbrains.annotations.Nullable ListenerPriority priorityFilter) {
|
||||
Collection<PrioritizedListener<PacketListener>> list = getListener(event.getPacketType());
|
||||
|
||||
if (list == null)
|
||||
return;
|
||||
|
||||
if (timedManager.isTiming()) {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
if (priorityFilter == null || element.getPriority() == priorityFilter) {
|
||||
TimedTracker tracker = timedManager.getTracker(element.getListener(), ListenerType.SYNC_SERVER_SIDE);
|
||||
long token = tracker.beginTracking();
|
||||
|
||||
// Measure and record the execution time
|
||||
invokeSendingListener(reporter, event, element);
|
||||
tracker.endTracking(token, event.getPacketType());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (PrioritizedListener<PacketListener> element : list) {
|
||||
if (priorityFilter == null || element.getPriority() == priorityFilter) {
|
||||
invokeSendingListener(reporter, event, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a particular sending listener.
|
||||
* @param reporter - the error reporter.
|
||||
* @param event - the related packet event.
|
||||
* @param element - the listener to invoke.
|
||||
*/
|
||||
private void invokeSendingListener(ErrorReporter reporter, PacketEvent event, PrioritizedListener<PacketListener> element) {
|
||||
try {
|
||||
event.setReadOnly(element.getPriority() == ListenerPriority.MONITOR);
|
||||
element.getListener().onPacketSending(event);
|
||||
|
||||
} catch (OutOfMemoryError | ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
reporter.reportMinimal(element.getListener().getPlugin(), "onPacketSending(PacketEvent)", e,
|
||||
event.getPacket().getHandle());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,22 +20,29 @@ package com.comphenix.protocol.injector;
|
|||
import java.security.PublicKey;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.comphenix.protocol.reflect.instances.InstanceCreator;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.ZeroBuffer;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.bytebuddy.dynamic.DynamicType;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
|
||||
|
@ -50,57 +57,100 @@ import net.bytebuddy.matcher.ElementMatchers;
|
|||
public class StructureCache {
|
||||
|
||||
// Structure modifiers
|
||||
private static final Map<Class<?>, Supplier<Object>> PACKET_INSTANCE_CREATORS = new ConcurrentHashMap<>();
|
||||
private static final Map<Class<?>, Supplier<Object>> CACHED_INSTANCE_CREATORS = new ConcurrentHashMap<>();
|
||||
private static final Map<PacketType, StructureModifier<Object>> STRUCTURE_MODIFIER_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
// packet data serializer which always returns an empty nbt tag compound
|
||||
private static final Object TRICK_INIT_LOCK = new Object();
|
||||
private static boolean TRICK_TRIED = false;
|
||||
|
||||
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
|
||||
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
|
||||
private static Supplier<Object> TRICKED_DATA_SERIALIZER_BASE;
|
||||
private static Supplier<Object> TRICKED_DATA_SERIALIZER_JSON;
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link #newInstance(Class)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Object newPacket(Class<?> packetClass) {
|
||||
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> {
|
||||
// prefer construction via PacketDataSerializer constructor on 1.17 and above
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
|
||||
clazz,
|
||||
MinecraftReflection.getPacketDataSerializerClass());
|
||||
if (serializerAccessor != null) {
|
||||
// check if the method is possible
|
||||
if (tryInitTrickDataSerializer()) {
|
||||
return newInstance(packetClass);
|
||||
}
|
||||
|
||||
public static Object newInstance(Class<?> clazz) {
|
||||
Supplier<Object> creator = CACHED_INSTANCE_CREATORS.computeIfAbsent(clazz, StructureCache::determineBestCreator);
|
||||
return creator.get();
|
||||
}
|
||||
|
||||
static Supplier<Object> determineBestCreator(Class<?> clazz) {
|
||||
try {
|
||||
InstanceCreator creator = InstanceCreator.forClass(clazz);
|
||||
if (creator.get() != null) {
|
||||
return creator;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(clazz);
|
||||
|
||||
// use the new stream codec for versions above 1.20.5 if possible
|
||||
if (streamCodec != null && tryInitTrickDataSerializer()) {
|
||||
try {
|
||||
// first try with the base accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
|
||||
streamCodec.decode(serializer); // throwaway instance, for testing
|
||||
|
||||
// method is working
|
||||
return () -> streamCodec.decode(serializer);
|
||||
} catch (Exception ignored) {
|
||||
try {
|
||||
// try with the json accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
|
||||
streamCodec.decode(serializer); // throwaway instance, for testing
|
||||
|
||||
// method is working
|
||||
return () -> streamCodec.decode(serializer);
|
||||
} catch (Exception ignored1) {
|
||||
// shrug, fall back to default behaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prefer construction via PacketDataSerializer constructor on 1.17 and above
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
|
||||
clazz,
|
||||
MinecraftReflection.getPacketDataSerializerClass());
|
||||
if (serializerAccessor != null) {
|
||||
// check if the method is possible
|
||||
if (tryInitTrickDataSerializer()) {
|
||||
try {
|
||||
// first try with the base accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
|
||||
serializerAccessor.invoke(serializer); // throwaway instance, for testing
|
||||
|
||||
// method is working
|
||||
return () -> serializerAccessor.invoke(serializer);
|
||||
} catch (Exception ignored) {
|
||||
try {
|
||||
// first try with the base accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
|
||||
// try with the json accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
|
||||
serializerAccessor.invoke(serializer); // throwaway instance, for testing
|
||||
|
||||
// method is working
|
||||
return () -> serializerAccessor.invoke(serializer);
|
||||
} catch (Exception exception) {
|
||||
try {
|
||||
// try with the json accessor
|
||||
Object serializer = TRICKED_DATA_SERIALIZER_JSON.invoke(new ZeroBuffer());
|
||||
serializerAccessor.invoke(serializer); // throwaway instance, for testing
|
||||
|
||||
// method is working
|
||||
return () -> serializerAccessor.invoke(serializer);
|
||||
} catch (Exception ignored) {
|
||||
// shrug, fall back to default behaviour
|
||||
}
|
||||
} catch (Exception ignored1) {
|
||||
// shrug, fall back to default behaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try via DefaultInstances as fallback
|
||||
return () -> {
|
||||
Object packetInstance = DefaultInstances.DEFAULT.create(clazz);
|
||||
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + clazz);
|
||||
return packetInstance;
|
||||
};
|
||||
});
|
||||
return packetConstructor.get();
|
||||
// try via DefaultInstances as fallback
|
||||
return () -> {
|
||||
Object packetInstance = DefaultInstances.DEFAULT.create(clazz);
|
||||
Objects.requireNonNull(packetInstance, "Unable to create instance for class " + clazz + " - " + tryInitTrickDataSerializer() + " - " + streamCodec);
|
||||
return packetInstance;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,7 +168,9 @@ public class StructureCache {
|
|||
*
|
||||
* @param packetType - packet type.
|
||||
* @return A structure modifier.
|
||||
* @deprecated use {@link #getStructure(PacketType)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static StructureModifier<Object> getStructure(Class<?> packetType) {
|
||||
// Get the ID from the class
|
||||
PacketType type = PacketRegistry.getPacketType(packetType);
|
||||
|
@ -154,7 +206,61 @@ public class StructureCache {
|
|||
*/
|
||||
public static Object newNullDataSerializer() {
|
||||
tryInitTrickDataSerializer();
|
||||
return TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
|
||||
return TRICKED_DATA_SERIALIZER_BASE.get();
|
||||
}
|
||||
|
||||
static void initTrickDataSerializer() {
|
||||
Optional<Class<?>> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass();
|
||||
|
||||
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
|
||||
Object textCompound = WrappedChatComponent.fromText("").getHandle();
|
||||
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
|
||||
|
||||
// base builder which intercepts a few methods
|
||||
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
|
||||
.createSubclass(registryByteBuf.orElse(MinecraftReflection.getPacketDataSerializerClass()))
|
||||
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
|
||||
.method(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())
|
||||
.and(ElementMatchers.returns(ElementMatchers.isSubTypeOf(MinecraftReflection.getNBTBaseClass()))))
|
||||
.intercept(FixedValue.value(compound))
|
||||
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
|
||||
.intercept(FixedValue.value(textCompound))
|
||||
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
|
||||
.intercept(FixedValue.nullValue());
|
||||
Class<?> serializerBase = baseBuilder.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
|
||||
.getLoaded();
|
||||
|
||||
if (registryByteBuf.isPresent()) {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(serializerBase, true).getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterDerivedOf(ByteBuf.class)
|
||||
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
|
||||
.build()));
|
||||
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
|
||||
} else {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
|
||||
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer());
|
||||
}
|
||||
|
||||
//xtended builder which intercepts the read string method as well
|
||||
Class<?> withStringIntercept = baseBuilder
|
||||
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
|
||||
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
|
||||
.intercept(FixedValue.value("{}"))
|
||||
.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
|
||||
.getLoaded();
|
||||
|
||||
if (registryByteBuf.isPresent()) {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(withStringIntercept).getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterDerivedOf(ByteBuf.class)
|
||||
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
|
||||
.build()));
|
||||
TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
|
||||
} else {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
|
||||
TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,43 +278,13 @@ public class StructureCache {
|
|||
}
|
||||
|
||||
try {
|
||||
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
|
||||
Object textCompound = WrappedChatComponent.fromText("").getHandle();
|
||||
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
|
||||
// base builder which intercepts a few methods
|
||||
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
|
||||
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
|
||||
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
|
||||
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
|
||||
.intercept(FixedValue.value(compound))
|
||||
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
|
||||
.intercept(FixedValue.value(textCompound))
|
||||
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
|
||||
.intercept(FixedValue.nullValue());
|
||||
Class<?> serializerBase = baseBuilder.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
|
||||
.getLoaded();
|
||||
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
|
||||
|
||||
// extended builder which intercepts the read string method as well
|
||||
Class<?> withStringIntercept = baseBuilder
|
||||
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
|
||||
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
|
||||
.intercept(FixedValue.value("{}"))
|
||||
.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
|
||||
.getLoaded();
|
||||
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
|
||||
|
||||
// worked
|
||||
initTrickDataSerializer();
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
} finally {
|
||||
TRICK_TRIED = true;
|
||||
}
|
||||
|
||||
// didn't work
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package com.comphenix.protocol.injector.collection;
|
||||
|
||||
import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
|
||||
public class InboundPacketListenerSet extends PacketListenerSet {
|
||||
|
||||
public InboundPacketListenerSet(PacketTypeListenerSet mainThreadPacketTypes, ErrorReporter errorReporter) {
|
||||
super(mainThreadPacketTypes, errorReporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListeningWhitelist getListeningWhitelist(PacketListener packetListener) {
|
||||
return packetListener.getReceivingWhitelist();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeListener(PacketEvent event, PacketListener listener) {
|
||||
try {
|
||||
event.setReadOnly(listener.getReceivingWhitelist().getPriority() == ListenerPriority.MONITOR);
|
||||
listener.onPacketReceiving(event);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
errorReporter.reportMinimal(listener.getPlugin(), "onPacketReceiving(PacketEvent)", e,
|
||||
event.getPacket().getHandle());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.comphenix.protocol.injector.collection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
|
||||
public class OutboundPacketListenerSet extends PacketListenerSet {
|
||||
|
||||
public OutboundPacketListenerSet(PacketTypeListenerSet mainThreadPacketTypes, ErrorReporter errorReporter) {
|
||||
super(mainThreadPacketTypes, errorReporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ListeningWhitelist getListeningWhitelist(PacketListener packetListener) {
|
||||
return packetListener.getSendingWhitelist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(PacketEvent event, @Nullable ListenerPriority priorityFilter) {
|
||||
super.invoke(event, priorityFilter);
|
||||
|
||||
if (event.getPacketType() == PacketType.Play.Server.BUNDLE && !event.isCancelled()) {
|
||||
// unpack the bundle and invoke for each packet in the bundle
|
||||
Iterable<PacketContainer> packets = event.getPacket().getPacketBundles().read(0);
|
||||
List<PacketContainer> outPackets = new ArrayList<>();
|
||||
for (PacketContainer subPacket : packets) {
|
||||
// ignore null packets as the will throw an error in the packet encoder
|
||||
if (subPacket == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PacketEvent subPacketEvent = PacketEvent.fromServer(this, subPacket, event.getNetworkMarker(),
|
||||
event.getPlayer());
|
||||
super.invoke(subPacketEvent, priorityFilter);
|
||||
|
||||
|
||||
// if the packet has been cancelled, the packet will not be add to the bundle
|
||||
if (subPacketEvent.isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PacketContainer packet = subPacketEvent.getPacket();
|
||||
if (packet == null || packet.getHandle() == null) {
|
||||
// super.invoke() should prevent us from getting new null packet so we just ignore it here
|
||||
continue;
|
||||
} else {
|
||||
outPackets.add(packet);
|
||||
}
|
||||
}
|
||||
|
||||
if (packets.iterator().hasNext()) {
|
||||
event.getPacket().getPacketBundles().write(0, outPackets);
|
||||
} else {
|
||||
// cancel entire packet if each individual packet has been cancelled
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void invokeListener(PacketEvent event, PacketListener listener) {
|
||||
try {
|
||||
event.setReadOnly(listener.getSendingWhitelist().getPriority() == ListenerPriority.MONITOR);
|
||||
listener.onPacketSending(event);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
errorReporter.reportMinimal(listener.getPlugin(), "onPacketSending(PacketEvent)", e,
|
||||
event.getPacket().getHandle());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package com.comphenix.protocol.injector.collection;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.concurrent.PacketTypeListenerSet;
|
||||
import com.comphenix.protocol.concurrent.PacketTypeMultiMap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.timing.TimingListenerType;
|
||||
import com.comphenix.protocol.timing.TimingTrackerManager;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
public abstract class PacketListenerSet {
|
||||
|
||||
private static final ReportType REPORT_UNSUPPORTED_PACKET = new ReportType(
|
||||
"Plugin %s tried to register listener for unknown packet %s [direction: from %s]");
|
||||
private static final ReportType REPORT_NULL_PACKET = new ReportType(
|
||||
"Plugin %s tried to set a packet or packet handle to null [type: %s, direction: %s]");
|
||||
|
||||
protected final PacketTypeMultiMap<PacketListener> map = new PacketTypeMultiMap<>();
|
||||
|
||||
protected final PacketTypeListenerSet mainThreadPacketTypes;
|
||||
protected final ErrorReporter errorReporter;
|
||||
|
||||
public PacketListenerSet(PacketTypeListenerSet mainThreadPacketTypes, ErrorReporter errorReporter) {
|
||||
this.mainThreadPacketTypes = mainThreadPacketTypes;
|
||||
this.errorReporter = errorReporter;
|
||||
}
|
||||
|
||||
protected abstract ListeningWhitelist getListeningWhitelist(PacketListener packetListener);
|
||||
|
||||
public void addListener(PacketListener packetListener) {
|
||||
ListeningWhitelist listeningWhitelist = getListeningWhitelist(packetListener);
|
||||
|
||||
for (PacketType packetType : listeningWhitelist.getTypes()) {
|
||||
Set<PacketType> supportedPacketTypes = (packetType.getSender() == Sender.SERVER)
|
||||
? PacketRegistry.getServerPacketTypes()
|
||||
: PacketRegistry.getClientPacketTypes();
|
||||
|
||||
if (!supportedPacketTypes.contains(packetType)) {
|
||||
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_UNSUPPORTED_PACKET)
|
||||
.messageParam(PacketAdapter.getPluginName(packetListener), packetType, packetType.getSender())
|
||||
.build());
|
||||
|
||||
// remove unknown packet types
|
||||
listeningWhitelist.getTypes().remove(packetType);
|
||||
}
|
||||
}
|
||||
|
||||
this.map.put(listeningWhitelist, packetListener);
|
||||
|
||||
Set<ListenerOptions> options = listeningWhitelist.getOptions();
|
||||
for (PacketType packetType : listeningWhitelist.getTypes()) {
|
||||
if (this.mainThreadPacketTypes != null && !packetType.isAsyncForced()) {
|
||||
boolean isOutboundSync = packetType.getSender() == Sender.SERVER
|
||||
&& !options.contains(ListenerOptions.ASYNC);
|
||||
boolean isInboundSync = packetType.getSender() == Sender.CLIENT
|
||||
&& options.contains(ListenerOptions.SYNC);
|
||||
if (isOutboundSync || isInboundSync) {
|
||||
this.mainThreadPacketTypes.add(packetType, packetListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(PacketListener packetListener) {
|
||||
ListeningWhitelist listeningWhitelist = getListeningWhitelist(packetListener);
|
||||
this.map.remove(listeningWhitelist, packetListener);
|
||||
|
||||
if (this.mainThreadPacketTypes != null) {
|
||||
for (PacketType packetType : listeningWhitelist.getTypes()) {
|
||||
this.mainThreadPacketTypes.remove(packetType, packetListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean containsPacketType(PacketType packetType) {
|
||||
return this.map.contains(packetType);
|
||||
}
|
||||
|
||||
public final ImmutableSet<PacketType> getPacketTypes() {
|
||||
return this.map.getPacketTypes();
|
||||
}
|
||||
|
||||
public void invoke(PacketEvent event) {
|
||||
this.invoke(event, null);
|
||||
}
|
||||
|
||||
public void invoke(PacketEvent event, @Nullable ListenerPriority priorityFilter) {
|
||||
Iterable<PacketListener> listeners = this.map.get(event.getPacketType());
|
||||
|
||||
for (PacketListener listener : listeners) {
|
||||
ListeningWhitelist listeningWhitelist = listener.getReceivingWhitelist();
|
||||
if (priorityFilter != null && listeningWhitelist.getPriority() != priorityFilter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PacketContainer originalPacket = event.getPacket();
|
||||
if (originalPacket == null || originalPacket.getHandle() == null) {
|
||||
// ignore null packets, they are evil and shouldn't exist
|
||||
break;
|
||||
}
|
||||
|
||||
// invoke packet listener
|
||||
TimingTrackerManager
|
||||
.get(listener, event.isServerPacket() ? TimingListenerType.SYNC_OUTBOUND : TimingListenerType.SYNC_INBOUND)
|
||||
.track(event.getPacketType(), () -> invokeListener(event, listener));
|
||||
|
||||
// check for new null packets
|
||||
PacketContainer newPacket = event.getPacket();
|
||||
if (newPacket == null || newPacket.getHandle() == null) {
|
||||
errorReporter.reportWarning(this, Report.newBuilder(REPORT_NULL_PACKET)
|
||||
.messageParam(PacketAdapter.getPluginName(listener), originalPacket.getType(), originalPacket.getType().getSender())
|
||||
.build());
|
||||
// reset packet to previous packet
|
||||
event.setPacket(originalPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void invokeListener(PacketEvent event, PacketListener listener);
|
||||
|
||||
public void clear() {
|
||||
this.map.clear();
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.comphenix.protocol.injector.netty;
|
|||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
/**
|
||||
|
@ -22,7 +23,7 @@ public interface ChannelListener {
|
|||
* @param marker - the network marker.
|
||||
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
|
||||
*/
|
||||
PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker);
|
||||
PacketEvent onPacketSending(Injector injector, PacketContainer packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Invoked when a packet is being received from a client.
|
||||
|
@ -34,23 +35,11 @@ public interface ChannelListener {
|
|||
* @param marker - the associated network marker, if any.
|
||||
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
|
||||
*/
|
||||
PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker);
|
||||
PacketEvent onPacketReceiving(Injector injector, PacketContainer packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Determine if there is a packet listener for the given packet.
|
||||
*
|
||||
* @param packetClass - the packet class to check.
|
||||
* @return TRUE if there is such a listener, FALSE otherwise.
|
||||
*/
|
||||
boolean hasListener(Class<?> packetClass);
|
||||
boolean hasInboundListener(PacketType packetType);
|
||||
|
||||
/**
|
||||
* Determine if there is a server packet listener that must be executed on the main thread.
|
||||
*
|
||||
* @param packetClass - the packet class to check.
|
||||
* @return TRUE if there is, FALSE otherwise.
|
||||
*/
|
||||
boolean hasMainThreadListener(Class<?> packetClass);
|
||||
boolean hasOutboundListener(PacketType packetType);
|
||||
|
||||
boolean hasMainThreadListener(PacketType type);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import org.bukkit.entity.Player;
|
||||
|
@ -49,8 +50,21 @@ public interface Injector {
|
|||
* Retrieve the current protocol state.
|
||||
*
|
||||
* @return The current protocol.
|
||||
* @deprecated use {@link #getCurrentProtocol(PacketType.Sender)} instead.
|
||||
*/
|
||||
Protocol getCurrentProtocol();
|
||||
@Deprecated
|
||||
default Protocol getCurrentProtocol() {
|
||||
return this.getCurrentProtocol(PacketType.Sender.SERVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol state. Note that since 1.20.2 the client and server direction can be in different
|
||||
* protocol states.
|
||||
*
|
||||
* @param sender the side for which the state should be resolved.
|
||||
* @return The current protocol.
|
||||
*/
|
||||
Protocol getCurrentProtocol(PacketType.Sender sender);
|
||||
|
||||
/**
|
||||
* Retrieve the network marker associated with a given packet.
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final class ChannelProtocolUtil {
|
||||
|
||||
public static final BiFunction<Channel, PacketType.Sender, PacketType.Protocol> PROTOCOL_RESOLVER;
|
||||
|
||||
static {
|
||||
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
|
||||
List<Field> attributeKeys = FuzzyReflection.fromClass(networkManagerClass, true).getFieldList(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(AttributeKey.class)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.declaringClassExactType(networkManagerClass)
|
||||
.build());
|
||||
|
||||
BiFunction<Channel, PacketType.Sender, Object> baseResolver = null;
|
||||
if (attributeKeys.isEmpty()) {
|
||||
// since 1.20.5 the protocol is stored as final field in de-/encoder
|
||||
baseResolver = new Post1_20_5WrappedResolver();
|
||||
} else if (attributeKeys.size() == 1) {
|
||||
// if there is only one attribute key we can assume it's the correct one (1.8 - 1.20.1)
|
||||
Object protocolKey = Accessors.getFieldAccessor(attributeKeys.get(0)).get(null);
|
||||
baseResolver = new Pre1_20_2DirectResolver((AttributeKey<Object>) protocolKey);
|
||||
} else if (attributeKeys.size() > 1) {
|
||||
// most likely 1.20.2+: 1 protocol key per protocol direction
|
||||
AttributeKey<Object> serverBoundKey = null;
|
||||
AttributeKey<Object> clientBoundKey = null;
|
||||
|
||||
for (Field keyField : attributeKeys) {
|
||||
AttributeKey<Object> key = (AttributeKey<Object>) Accessors.getFieldAccessor(keyField).get(null);
|
||||
if (key.name().equals("protocol")) {
|
||||
// legacy (pre 1.20.2 name) - fall back to the old behaviour
|
||||
baseResolver = new Pre1_20_2DirectResolver(key);
|
||||
break;
|
||||
}
|
||||
|
||||
if (key.name().contains("protocol")) {
|
||||
// one of the two protocol keys for 1.20.2
|
||||
if (key.name().contains("server")) {
|
||||
serverBoundKey = key;
|
||||
} else {
|
||||
clientBoundKey = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (baseResolver == null) {
|
||||
if ((serverBoundKey == null || clientBoundKey == null)) {
|
||||
// neither pre 1.20.2 key nor 1.20.2+ keys are available
|
||||
throw new ExceptionInInitializerError("Unable to resolve protocol state attribute keys");
|
||||
} else {
|
||||
baseResolver = new Post1_20_2WrappedResolver(serverBoundKey, clientBoundKey);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ExceptionInInitializerError("Unable to resolve protocol state attribute key(s)");
|
||||
}
|
||||
|
||||
// decorate the base resolver by wrapping its return value into our packet type value
|
||||
PROTOCOL_RESOLVER = baseResolver.andThen(protocol -> PacketType.Protocol.fromVanilla((Enum<?>) protocol));
|
||||
}
|
||||
|
||||
private static final class Pre1_20_2DirectResolver implements BiFunction<Channel, PacketType.Sender, Object> {
|
||||
|
||||
private final AttributeKey<Object> attributeKey;
|
||||
|
||||
public Pre1_20_2DirectResolver(AttributeKey<Object> attributeKey) {
|
||||
this.attributeKey = attributeKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(Channel channel, PacketType.Sender sender) {
|
||||
return channel.attr(this.attributeKey).get();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Post1_20_2WrappedResolver implements BiFunction<Channel, PacketType.Sender, Object> {
|
||||
|
||||
private final AttributeKey<Object> serverBoundKey;
|
||||
private final AttributeKey<Object> clientBoundKey;
|
||||
|
||||
// lazy initialized when needed
|
||||
private FieldAccessor protocolAccessor;
|
||||
|
||||
public Post1_20_2WrappedResolver(AttributeKey<Object> serverBoundKey, AttributeKey<Object> clientBoundKey) {
|
||||
this.serverBoundKey = serverBoundKey;
|
||||
this.clientBoundKey = clientBoundKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object apply(Channel channel, PacketType.Sender sender) {
|
||||
AttributeKey<Object> key = this.getKeyForSender(sender);
|
||||
Object codecData = channel.attr(key).get();
|
||||
if (codecData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FieldAccessor protocolAccessor = this.getProtocolAccessor(codecData.getClass());
|
||||
return protocolAccessor.get(codecData);
|
||||
}
|
||||
|
||||
private AttributeKey<Object> getKeyForSender(PacketType.Sender sender) {
|
||||
switch (sender) {
|
||||
case SERVER:
|
||||
return this.clientBoundKey;
|
||||
case CLIENT:
|
||||
return this.serverBoundKey;
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||
}
|
||||
}
|
||||
|
||||
private FieldAccessor getProtocolAccessor(Class<?> codecClass) {
|
||||
if (this.protocolAccessor == null) {
|
||||
Class<?> enumProtocolClass = MinecraftReflection.getEnumProtocolClass();
|
||||
this.protocolAccessor = Accessors.getFieldAccessor(codecClass, enumProtocolClass, true);
|
||||
}
|
||||
|
||||
return this.protocolAccessor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.20.5 the protocol is stored as final field in de-/encoder
|
||||
*/
|
||||
private static final class Post1_20_5WrappedResolver implements BiFunction<Channel, PacketType.Sender, Object> {
|
||||
|
||||
// lazy initialized when needed
|
||||
private Function<Object, Object> serverProtocolAccessor;
|
||||
private Function<Object, Object> clientProtocolAccessor;
|
||||
|
||||
@Override
|
||||
public Object apply(Channel channel, PacketType.Sender sender) {
|
||||
Class<? extends ChannelHandler> handlerClass = this.getHandlerClass(sender)
|
||||
.asSubclass(ChannelHandler.class);
|
||||
|
||||
ChannelHandlerContext handlerContext = channel.pipeline().context(handlerClass);
|
||||
if (handlerContext == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Function<Object, Object> protocolAccessor = this.getProtocolAccessor(handlerClass, sender);
|
||||
return protocolAccessor.apply(handlerContext.handler());
|
||||
}
|
||||
|
||||
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler, PacketType.Sender sender) {
|
||||
switch (sender) {
|
||||
case SERVER:
|
||||
if (this.serverProtocolAccessor == null) {
|
||||
this.serverProtocolAccessor = getProtocolAccessor(codecHandler);
|
||||
}
|
||||
return this.serverProtocolAccessor;
|
||||
case CLIENT:
|
||||
if (this.clientProtocolAccessor == null) {
|
||||
this.clientProtocolAccessor = getProtocolAccessor(codecHandler);
|
||||
}
|
||||
return this.clientProtocolAccessor;
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getHandlerClass(PacketType.Sender sender) {
|
||||
switch (sender) {
|
||||
case SERVER:
|
||||
return MinecraftReflection.getMinecraftClass("network.PacketEncoder");
|
||||
case CLIENT:
|
||||
return MinecraftReflection.getMinecraftClass("network.PacketDecoder");
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
|
||||
}
|
||||
}
|
||||
|
||||
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler) {
|
||||
Class<?> protocolInfoClass = MinecraftReflection.getProtocolInfoClass();
|
||||
|
||||
MethodAccessor protocolAccessor = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(protocolInfoClass).getMethodByReturnTypeAndParameters("id",
|
||||
MinecraftReflection.getEnumProtocolClass(), new Class[0]));
|
||||
|
||||
FieldAccessor protocolInfoAccessor = Accessors.getFieldAccessor(codecHandler, protocolInfoClass, true);
|
||||
|
||||
// get ProtocolInfo from handler and get EnumProtocol of ProtocolInfo
|
||||
return (handler) -> {
|
||||
Object protocolInfo = protocolInfoAccessor.get(handler);
|
||||
return protocolAccessor.invoke(protocolInfo);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
|
@ -42,7 +43,7 @@ final class EmptyInjector implements Injector {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Protocol getCurrentProtocol() {
|
||||
public Protocol getCurrentProtocol(PacketType.Sender sender) {
|
||||
return Protocol.HANDSHAKING;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
|
@ -17,28 +21,33 @@ final class InboundPacketInterceptor extends ChannelInboundHandlerAdapter {
|
|||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
Class<?> messageClass = msg.getClass();
|
||||
if (this.shouldInterceptMessage(messageClass)) {
|
||||
// process the login if the packet is one before posting the packet to any handler to provide "real" data
|
||||
// the method invocation will do nothing if the packet is not a login packet
|
||||
if (MinecraftReflection.isPacketClass(msg)) {
|
||||
// process the login if the packet is one before posting the packet to any
|
||||
// handler to provide "real" data the method invocation will do nothing if the
|
||||
// packet is not a login packet
|
||||
this.injector.tryProcessLogin(msg);
|
||||
|
||||
// check if there are any listeners bound for the packet - if not just post the packet down the pipeline
|
||||
if (!this.channelListener.hasListener(messageClass)) {
|
||||
PacketType.Protocol protocol = this.injector.getInboundProtocol();
|
||||
PacketType packetType = PacketRegistry.getPacketType(protocol, msg.getClass());
|
||||
|
||||
if (packetType == null) {
|
||||
ProtocolLogger.debug("skipping unknown inbound packet type for {0}", msg.getClass());
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there are any listeners bound for the packet - if not just post the
|
||||
// packet down the pipeline
|
||||
if (!this.channelListener.hasInboundListener(packetType)) {
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// call all inbound listeners
|
||||
this.injector.processInboundPacket(ctx, msg, messageClass);
|
||||
this.injector.processInboundPacket(ctx, msg, packetType);
|
||||
} else {
|
||||
// just pass the message down the pipeline
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldInterceptMessage(Class<?> messageClass) {
|
||||
// only intercept minecraft packets and no garbage from other stuff in the channel
|
||||
return MinecraftReflection.getPacketClass().isAssignableFrom(messageClass);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
public class InboundProtocolReader extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final NettyChannelInjector injector;
|
||||
|
||||
private PacketType.Protocol protocol = null;
|
||||
|
||||
public InboundProtocolReader(NettyChannelInjector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
public PacketType.Protocol getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
this.protocol = this.injector.getCurrentProtocol(PacketType.Sender.CLIENT);
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
}
|
|
@ -2,8 +2,6 @@ package com.comphenix.protocol.injector.netty.channel;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -13,30 +11,41 @@ import java.util.concurrent.Callable;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.NetworkProcessor;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.utility.*;
|
||||
import com.comphenix.protocol.utility.ByteBuddyGenerated;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NettyChannelInjector implements Injector {
|
||||
|
||||
|
@ -58,11 +67,12 @@ public class NettyChannelInjector implements Injector {
|
|||
};
|
||||
|
||||
private static final String INTERCEPTOR_NAME = "protocol_lib_inbound_interceptor";
|
||||
private static final String PROTOCOL_READER_NAME = "protocol_lib_protocol_reader";
|
||||
private static final String WIRE_PACKET_ENCODER_NAME = "protocol_lib_wire_packet_encoder";
|
||||
|
||||
// all registered channel handlers to easier make sure we unregister them all from the pipeline
|
||||
private static final String[] PROTOCOL_LIB_HANDLERS = new String[]{
|
||||
WIRE_PACKET_ENCODER_NAME, INTERCEPTOR_NAME
|
||||
WIRE_PACKET_ENCODER_NAME, INTERCEPTOR_NAME, PROTOCOL_READER_NAME
|
||||
};
|
||||
|
||||
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
|
||||
|
@ -110,7 +120,8 @@ public class NettyChannelInjector implements Injector {
|
|||
|
||||
// lazy initialized fields, if we don't need them we don't bother about them
|
||||
private Object playerConnection;
|
||||
private FieldAccessor protocolAccessor;
|
||||
|
||||
private InboundProtocolReader inboundProtocolReader;
|
||||
|
||||
public NettyChannelInjector(
|
||||
Player player,
|
||||
|
@ -205,9 +216,32 @@ public class NettyChannelInjector implements Injector {
|
|||
return false;
|
||||
}
|
||||
|
||||
ChannelPipeline pipeline = this.wrappedChannel.pipeline();
|
||||
|
||||
// since 1.20.5 the encoder is renamed to outbound_config when the channel is
|
||||
// waiting for the next protocol phase
|
||||
String encoderName = pipeline.get("outbound_config") != null
|
||||
? "outbound_config" : "encoder";
|
||||
|
||||
// inject our handlers
|
||||
this.wrappedChannel.pipeline().addAfter("encoder", WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER);
|
||||
this.wrappedChannel.pipeline().addAfter(
|
||||
pipeline.addAfter(
|
||||
encoderName,
|
||||
WIRE_PACKET_ENCODER_NAME,
|
||||
WIRE_PACKET_ENCODER);
|
||||
|
||||
// since 1.20.2 the packet decoder will remove or reconfigure the protocol for
|
||||
// terminal packets which is why we need to read it before the packet gets
|
||||
// decoded
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
this.inboundProtocolReader = new InboundProtocolReader(this);
|
||||
pipeline.addBefore(
|
||||
"decoder",
|
||||
PROTOCOL_READER_NAME,
|
||||
this.inboundProtocolReader);
|
||||
}
|
||||
|
||||
// inject inbound packet interceptor
|
||||
pipeline.addAfter(
|
||||
"decoder",
|
||||
INTERCEPTOR_NAME,
|
||||
new InboundPacketInterceptor(this, this.channelListener));
|
||||
|
@ -286,6 +320,8 @@ public class NettyChannelInjector implements Injector {
|
|||
Object playerConnection = this.getPlayerConnection();
|
||||
if (playerConnection != null) {
|
||||
MinecraftMethods.getSendPacketMethod().invoke(playerConnection, packet);
|
||||
} else {
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(this.networkManager, packet);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
|
@ -321,18 +357,16 @@ public class NettyChannelInjector implements Injector {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getCurrentProtocol() {
|
||||
// ensure that the accessor to the protocol field is available
|
||||
if (this.protocolAccessor == null) {
|
||||
this.protocolAccessor = Accessors.getFieldAccessor(
|
||||
this.networkManager.getClass(),
|
||||
MinecraftReflection.getEnumProtocolClass(),
|
||||
true);
|
||||
public PacketType.Protocol getInboundProtocol() {
|
||||
if (this.inboundProtocolReader != null) {
|
||||
return this.inboundProtocolReader.getProtocol();
|
||||
}
|
||||
return getCurrentProtocol(PacketType.Sender.CLIENT);
|
||||
}
|
||||
|
||||
Object nmsProtocol = this.protocolAccessor.get(this.networkManager);
|
||||
return Protocol.fromVanilla((Enum<?>) nmsProtocol);
|
||||
@Override
|
||||
public Protocol getCurrentProtocol(PacketType.Sender sender) {
|
||||
return ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(this.wrappedChannel, sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -488,16 +522,17 @@ public class NettyChannelInjector implements Injector {
|
|||
}
|
||||
}
|
||||
|
||||
void processInboundPacket(ChannelHandlerContext ctx, Object packet, Class<?> packetClass) {
|
||||
if (this.channelListener.hasMainThreadListener(packetClass) && !this.server.isPrimaryThread()) {
|
||||
void processInboundPacket(ChannelHandlerContext ctx, Object packet, PacketType packetType) {
|
||||
if (this.channelListener.hasMainThreadListener(packetType) && !this.server.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be - re-schedule the packet on the main thread
|
||||
ProtocolLibrary.getScheduler().runTask(
|
||||
() -> this.processInboundPacket(ctx, packet, packetClass));
|
||||
() -> this.processInboundPacket(ctx, packet, packetType));
|
||||
return;
|
||||
}
|
||||
|
||||
// call packet handlers, a null result indicates that we shouldn't change anything
|
||||
PacketEvent interceptionResult = this.channelListener.onPacketReceiving(this, packet, null);
|
||||
PacketContainer packetContainer = new PacketContainer(packetType, packet);
|
||||
PacketEvent interceptionResult = this.channelListener.onPacketReceiving(this, packetContainer, null);
|
||||
if (interceptionResult == null) {
|
||||
this.ensureInEventLoop(ctx.channel().eventLoop(), () -> ctx.fireChannelRead(packet));
|
||||
return;
|
||||
|
@ -547,13 +582,21 @@ public class NettyChannelInjector implements Injector {
|
|||
return action;
|
||||
}
|
||||
|
||||
PacketType.Protocol protocol = this.getCurrentProtocol(PacketType.Sender.SERVER);
|
||||
PacketType packetType = PacketRegistry.getPacketType(protocol, packet.getClass());
|
||||
|
||||
if (packetType == null) {
|
||||
ProtocolLogger.debug("skipping unknown outbound packet type for {0}", packet.getClass());
|
||||
return action;
|
||||
}
|
||||
|
||||
// no listener and no marker - no magic :)
|
||||
if (!this.channelListener.hasListener(packet.getClass()) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
|
||||
if (!this.channelListener.hasOutboundListener(packetType) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// ensure that we are on the main thread if we need to
|
||||
if (this.channelListener.hasMainThreadListener(packet.getClass()) && !this.server.isPrimaryThread()) {
|
||||
if (this.channelListener.hasMainThreadListener(packetType) && !this.server.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be - re-schedule the packet on the main thread
|
||||
ProtocolLibrary.getScheduler().runTask(
|
||||
() -> this.sendServerPacket(packet, null, true));
|
||||
|
@ -562,7 +605,8 @@ public class NettyChannelInjector implements Injector {
|
|||
|
||||
// call all listeners which are listening to the outbound packet, if any
|
||||
// null indicates that no listener was affected by the packet, meaning that we can directly send the original packet
|
||||
PacketEvent event = this.channelListener.onPacketSending(this, packet, marker);
|
||||
PacketContainer packetContainer = new PacketContainer(packetType, packet);
|
||||
PacketEvent event = this.channelListener.onPacketSending(this, packetContainer, marker);
|
||||
if (event == null) {
|
||||
return action;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import java.util.function.Consumer;
|
|||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
final class ListeningList implements List<Object> {
|
||||
|
||||
private final List<Object> original;
|
||||
|
|
|
@ -7,9 +7,11 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
|
@ -18,8 +20,6 @@ import com.comphenix.protocol.injector.ListenerInvoker;
|
|||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.netty.channel.InjectionFactory;
|
||||
import com.comphenix.protocol.injector.packet.PacketInjector;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
|
@ -28,21 +28,15 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
|||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.Util;
|
||||
import com.comphenix.protocol.wrappers.Pair;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class NetworkManagerInjector implements ChannelListener {
|
||||
|
||||
private static final String INBOUND_INJECT_HANDLER_NAME = "protocol_lib_inbound_inject";
|
||||
private static final TemporaryPlayerFactory PLAYER_FACTORY = new TemporaryPlayerFactory();
|
||||
|
||||
private final PacketTypeSet inboundListeners = new PacketTypeSet();
|
||||
private final PacketTypeSet outboundListeners = new PacketTypeSet();
|
||||
private final PacketTypeSet mainThreadListeners = new PacketTypeSet();
|
||||
|
||||
// all list fields which we've overridden and need to revert to a non-proxying list afterwards
|
||||
private final Set<Pair<Object, FieldAccessor>> overriddenLists = new HashSet<>();
|
||||
|
||||
|
@ -51,7 +45,6 @@ public class NetworkManagerInjector implements ChannelListener {
|
|||
private final InjectionFactory injectionFactory;
|
||||
|
||||
// injectors based on this "global" injector
|
||||
private final PacketInjector packetInjector;
|
||||
private final PlayerInjectionHandler playerInjectionHandler;
|
||||
|
||||
private final InjectionChannelInitializer pipelineInjectorHandler;
|
||||
|
@ -76,25 +69,17 @@ public class NetworkManagerInjector implements ChannelListener {
|
|||
|
||||
// other injectors
|
||||
this.playerInjectionHandler = new NetworkManagerPlayerInjector(
|
||||
this.outboundListeners,
|
||||
this,
|
||||
this.injectionFactory,
|
||||
this.mainThreadListeners);
|
||||
this.packetInjector = new NetworkManagerPacketInjector(
|
||||
this.inboundListeners,
|
||||
this.listenerInvoker,
|
||||
this,
|
||||
this.mainThreadListeners);
|
||||
this.injectionFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent onPacketSending(Injector injector, Object packet, NetworkMarker marker) {
|
||||
public PacketEvent onPacketSending(Injector injector, PacketContainer packet, NetworkMarker marker) {
|
||||
// check if we need to intercept the packet
|
||||
Class<?> packetClass = packet.getClass();
|
||||
if (marker != null || MinecraftReflection.isBundlePacket(packetClass) || outboundListeners.contains(packetClass)) {
|
||||
Class<?> packetClass = packet.getHandle().getClass();
|
||||
if (marker != null || MinecraftReflection.isBundlePacket(packetClass) || hasOutboundListener(packet.getType())) {
|
||||
// wrap packet and construct the event
|
||||
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(packetClass), packet);
|
||||
PacketEvent packetEvent = PacketEvent.fromServer(this, container, marker, injector.getPlayer());
|
||||
PacketEvent packetEvent = PacketEvent.fromServer(this, packet, marker, injector.getPlayer());
|
||||
|
||||
// post to all listeners, then return the packet event we constructed
|
||||
this.listenerInvoker.invokePacketSending(packetEvent);
|
||||
|
@ -106,13 +91,9 @@ public class NetworkManagerInjector implements ChannelListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent onPacketReceiving(Injector injector, Object packet, NetworkMarker marker) {
|
||||
// check if we need to intercept the packet
|
||||
Class<?> packetClass = packet.getClass();
|
||||
if (marker != null || inboundListeners.contains(packetClass)) {
|
||||
// wrap the packet and construct the event
|
||||
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(packetClass), packet);
|
||||
PacketEvent packetEvent = PacketEvent.fromClient(this, container, marker, injector.getPlayer());
|
||||
public PacketEvent onPacketReceiving(Injector injector, PacketContainer packet, NetworkMarker marker) {
|
||||
if (marker != null || hasInboundListener(packet.getType())) {
|
||||
PacketEvent packetEvent = PacketEvent.fromClient(this, packet, marker, injector.getPlayer());
|
||||
|
||||
// post to all listeners, then return the packet event we constructed
|
||||
this.listenerInvoker.invokePacketReceiving(packetEvent);
|
||||
|
@ -124,18 +105,18 @@ public class NetworkManagerInjector implements ChannelListener {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasListener(Class<?> packetClass) {
|
||||
return this.outboundListeners.contains(packetClass) || this.inboundListeners.contains(packetClass);
|
||||
public boolean hasInboundListener(PacketType packetType) {
|
||||
return this.listenerInvoker.hasInboundListener(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOutboundListener(PacketType packetType) {
|
||||
return this.listenerInvoker.hasOutboundListener(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(Class<?> packetClass) {
|
||||
return this.mainThreadListeners.contains(packetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType type) {
|
||||
return this.mainThreadListeners.contains(type);
|
||||
public boolean hasMainThreadListener(PacketType packetType) {
|
||||
return this.listenerInvoker.hasMainThreadListener(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -238,7 +219,6 @@ public class NetworkManagerInjector implements ChannelListener {
|
|||
// just reset to the list we wrapped originally
|
||||
ListeningList ourList = (ListeningList) currentFieldValue;
|
||||
List<Object> original = ourList.getOriginal();
|
||||
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
||||
synchronized (original) {
|
||||
// revert the injection from all values of the list
|
||||
ourList.unProcessAll();
|
||||
|
@ -252,10 +232,6 @@ public class NetworkManagerInjector implements ChannelListener {
|
|||
this.injectionFactory.close();
|
||||
}
|
||||
|
||||
public PacketInjector getPacketInjector() {
|
||||
return this.packetInjector;
|
||||
}
|
||||
|
||||
public PlayerInjectionHandler getPlayerInjectionHandler() {
|
||||
return this.playerInjectionHandler;
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
package com.comphenix.protocol.injector.netty.manager;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.packet.AbstractPacketInjector;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class NetworkManagerPacketInjector extends AbstractPacketInjector {
|
||||
|
||||
private final ListenerInvoker invoker;
|
||||
private final ChannelListener channelListener;
|
||||
private final PacketTypeSet mainThreadListeners;
|
||||
|
||||
public NetworkManagerPacketInjector(
|
||||
PacketTypeSet inboundFilters,
|
||||
ListenerInvoker invoker,
|
||||
ChannelListener listener,
|
||||
PacketTypeSet mainThreadListeners
|
||||
) {
|
||||
super(inboundFilters);
|
||||
|
||||
this.invoker = invoker;
|
||||
this.channelListener = listener;
|
||||
this.mainThreadListeners = mainThreadListeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
if (!type.isAsyncForced() && options != null && options.contains(ListenerOptions.SYNC)) {
|
||||
this.mainThreadListeners.addType(type);
|
||||
}
|
||||
|
||||
return super.addPacketHandler(type, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePacketHandler(PacketType type) {
|
||||
this.mainThreadListeners.removeType(type);
|
||||
return super.removePacketHandler(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent packetReceived(PacketContainer packet, Player client) {
|
||||
PacketEvent event = PacketEvent.fromClient(this.channelListener, packet, null, client);
|
||||
this.invoker.invokePacketReceiving(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType type) {
|
||||
return this.mainThreadListeners.contains(type);
|
||||
}
|
||||
}
|
|
@ -1,36 +1,28 @@
|
|||
package com.comphenix.protocol.injector.netty.manager;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.netty.channel.InjectionFactory;
|
||||
import com.comphenix.protocol.injector.netty.channel.NettyChannelInjector;
|
||||
import com.comphenix.protocol.injector.player.AbstractPlayerInjectionHandler;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.Set;
|
||||
import org.bukkit.entity.Player;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
|
||||
final class NetworkManagerPlayerInjector extends AbstractPlayerInjectionHandler {
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
final class NetworkManagerPlayerInjector implements PlayerInjectionHandler {
|
||||
|
||||
private final ChannelListener listener;
|
||||
private final InjectionFactory injectionFactory;
|
||||
private final PacketTypeSet mainThreadListeners;
|
||||
|
||||
public NetworkManagerPlayerInjector(
|
||||
PacketTypeSet outboundListener,
|
||||
ChannelListener listener,
|
||||
InjectionFactory injectionFactory,
|
||||
PacketTypeSet mainThreadListeners
|
||||
InjectionFactory injectionFactory
|
||||
) {
|
||||
super(outboundListener);
|
||||
|
||||
this.listener = listener;
|
||||
this.injectionFactory = injectionFactory;
|
||||
this.mainThreadListeners = mainThreadListeners;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,12 +34,7 @@ final class NetworkManagerPlayerInjector extends AbstractPlayerInjectionHandler
|
|||
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||
this.injectionFactory.fromPlayer(player, this.listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDisconnect(Player player) {
|
||||
// noop
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
this.injectionFactory.fromPlayer(player, this.listener).uninject();
|
||||
|
@ -69,11 +56,6 @@ final class NetworkManagerPlayerInjector extends AbstractPlayerInjectionHandler
|
|||
this.injectionFactory.fromPlayer(player, this.listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType type) {
|
||||
return this.mainThreadListeners.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player player) {
|
||||
Injector injector = this.injectionFactory.fromPlayer(player, this.listener);
|
||||
|
@ -83,19 +65,4 @@ final class NetworkManagerPlayerInjector extends AbstractPlayerInjectionHandler
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
if (!type.isAsyncForced() && (options == null || !options.contains(ListenerOptions.ASYNC))) {
|
||||
this.mainThreadListeners.addType(type);
|
||||
}
|
||||
|
||||
super.addPacketHandler(type, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketHandler(PacketType type) {
|
||||
this.mainThreadListeners.removeType(type);
|
||||
super.removePacketHandler(type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractPacketInjector implements PacketInjector {
|
||||
|
||||
private final PacketTypeSet inboundFilters;
|
||||
|
||||
public AbstractPacketInjector(PacketTypeSet inboundFilters) {
|
||||
this.inboundFilters = inboundFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
this.inboundFilters.addType(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePacketHandler(PacketType type) {
|
||||
this.inboundFilters.removeType(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPacketHandler(PacketType type) {
|
||||
return this.inboundFilters.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getPacketHandlers() {
|
||||
return this.inboundFilters.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
this.inboundFilters.clear();
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
package com.comphenix.protocol.injector.packet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents an incoming packet injector.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface PacketInjector {
|
||||
|
||||
/**
|
||||
* Start intercepting packets with the given packet type.
|
||||
*
|
||||
* @param type - the type of the packets to start intercepting.
|
||||
* @param options - any listener options.
|
||||
* @return TRUE if we didn't already intercept these packets, FALSE otherwise.
|
||||
*/
|
||||
boolean addPacketHandler(PacketType type, Set<ListenerOptions> options);
|
||||
|
||||
/**
|
||||
* Stop intercepting packets with the given packet type.
|
||||
*
|
||||
* @param type - the type of the packets to stop intercepting.
|
||||
* @return TRUE if we successfuly stopped intercepting a given packet ID, FALSE otherwise.
|
||||
*/
|
||||
boolean removePacketHandler(PacketType type);
|
||||
|
||||
/**
|
||||
* Determine if packets with the given packet type is being intercepted.
|
||||
*
|
||||
* @param type - the packet type to lookup.
|
||||
* @return TRUE if we do, FALSE otherwise.
|
||||
*/
|
||||
boolean hasPacketHandler(PacketType type);
|
||||
|
||||
/**
|
||||
* Retrieve every intercepted packet type.
|
||||
*
|
||||
* @return Every intercepted packet type.
|
||||
*/
|
||||
Set<PacketType> getPacketHandlers();
|
||||
|
||||
/**
|
||||
* Let the packet listeners process the given packet.
|
||||
*
|
||||
* @param packet - a packet to process.
|
||||
* @param client - the client that sent the packet.
|
||||
* @return The resulting packet event.
|
||||
*/
|
||||
PacketEvent packetReceived(PacketContainer packet, Player client);
|
||||
|
||||
/**
|
||||
* Determine if we have packet listeners with the given type that must be executed on the main thread.
|
||||
*
|
||||
* @param type - the packet type.
|
||||
* @return TRUE if we do, FALSE otherwise.
|
||||
*/
|
||||
boolean hasMainThreadListener(PacketType type);
|
||||
|
||||
/**
|
||||
* Perform any necessary cleanup before unloading ProtocolLib.
|
||||
*/
|
||||
void cleanupAll();
|
||||
}
|
|
@ -19,44 +19,78 @@ package com.comphenix.protocol.injector.packet;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Sender;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
|
||||
|
||||
/**
|
||||
* Static packet registry in Minecraft.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class PacketRegistry {
|
||||
// Whether or not the registry has been initialized
|
||||
private static volatile boolean INITIALIZED = false;
|
||||
|
||||
static void reset() {
|
||||
synchronized (registryLock) {
|
||||
INITIALIZED = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a register we are currently building.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
protected static class Register {
|
||||
private static class Register {
|
||||
// The main lookup table
|
||||
final Map<PacketType, Optional<Class<?>>> typeToClass = new ConcurrentHashMap<>();
|
||||
|
||||
final Map<Class<?>, PacketType> classToType = new ConcurrentHashMap<>();
|
||||
final Map<Class<?>, WrappedStreamCodec> classToCodec = new ConcurrentHashMap<>();
|
||||
final Map<PacketType.Protocol, Map<Class<?>, PacketType>> protocolClassToType = new ConcurrentHashMap<>();
|
||||
|
||||
volatile Set<PacketType> serverPackets = new HashSet<>();
|
||||
volatile Set<PacketType> clientPackets = new HashSet<>();
|
||||
final List<MapContainer> containers = new ArrayList<>();
|
||||
|
||||
public Register() {}
|
||||
public Register() {
|
||||
}
|
||||
|
||||
public void registerPacket(PacketType type, Class<?> clazz, Sender sender) {
|
||||
typeToClass.put(type, Optional.of(clazz));
|
||||
|
||||
classToType.put(clazz, type);
|
||||
protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(clazz, type);
|
||||
|
||||
if (sender == Sender.CLIENT) {
|
||||
clientPackets.add(type);
|
||||
} else {
|
||||
|
@ -70,6 +104,7 @@ public class PacketRegistry {
|
|||
|
||||
/**
|
||||
* Determine if the current register is outdated.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
public boolean isOutdated() {
|
||||
|
@ -115,7 +150,8 @@ public class PacketRegistry {
|
|||
modifier = new StructureModifier<>(protocol.getClass().getSuperclass());
|
||||
}
|
||||
|
||||
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol).withType(Map.class);
|
||||
StructureModifier<Map<Object, Map<Integer, Class<?>>>> maps = modifier.withTarget(protocol)
|
||||
.withType(Map.class);
|
||||
for (Map.Entry<Object, Map<Integer, Class<?>>> entry : maps.read(0).entrySet()) {
|
||||
String direction = entry.getKey().toString();
|
||||
if (direction.contains("CLIENTBOUND")) { // Sent by Server
|
||||
|
@ -150,7 +186,7 @@ public class PacketRegistry {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static synchronized Register createNewRegister() {
|
||||
private static synchronized Register createRegisterV1_15_0() {
|
||||
Object[] protocols = ENUM_PROTOCOL.getEnumConstants();
|
||||
|
||||
// ID to Packet class maps
|
||||
|
@ -158,18 +194,17 @@ public class PacketRegistry {
|
|||
final Map<Object, Map<Class<?>, Integer>> clientMaps = new LinkedHashMap<>();
|
||||
|
||||
Register result = new Register();
|
||||
|
||||
Field mainMapField = null;
|
||||
Field packetMapField = null;
|
||||
Field holderClassField = null; // only 1.20.2+
|
||||
|
||||
// Iterate through the protocols
|
||||
for (Object protocol : protocols) {
|
||||
if (mainMapField == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(protocol.getClass(), true);
|
||||
mainMapField = fuzzy.getField(FuzzyFieldContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.FINAL)
|
||||
.typeDerivedOf(Map.class)
|
||||
.build());
|
||||
mainMapField = fuzzy.getField(FuzzyFieldContract.newBuilder().banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.FINAL).typeDerivedOf(Map.class).build());
|
||||
mainMapField.setAccessible(true);
|
||||
}
|
||||
|
||||
|
@ -184,19 +219,40 @@ public class PacketRegistry {
|
|||
for (Map.Entry<Object, Object> entry : directionMap.entrySet()) {
|
||||
Object holder = entry.getValue();
|
||||
if (packetMapField == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holder.getClass(), true);
|
||||
packetMapField = fuzzy.getField(FuzzyFieldContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.FINAL)
|
||||
.typeDerivedOf(Map.class)
|
||||
.build());
|
||||
Class<?> packetHolderClass = holder.getClass();
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
FuzzyReflection holderFuzzy = FuzzyReflection.fromClass(packetHolderClass, true);
|
||||
holderClassField = holderFuzzy
|
||||
.getField(FuzzyFieldContract.newBuilder().banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.FINAL)
|
||||
.typeMatches(FuzzyClassContract.newBuilder().method(FuzzyMethodContract
|
||||
.newBuilder().returnTypeExact(MinecraftReflection.getPacketClass())
|
||||
.parameterCount(2).parameterExactType(int.class, 0).parameterExactType(
|
||||
MinecraftReflection.getPacketDataSerializerClass(), 1)
|
||||
.build()).build())
|
||||
.build());
|
||||
holderClassField.setAccessible(true);
|
||||
packetHolderClass = holderClassField.getType();
|
||||
}
|
||||
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(packetHolderClass, true);
|
||||
packetMapField = fuzzy.getField(FuzzyFieldContract.newBuilder().banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.FINAL).typeDerivedOf(Map.class).build());
|
||||
packetMapField.setAccessible(true);
|
||||
}
|
||||
|
||||
Map<Class<?>, Integer> packetMap;
|
||||
Object holderInstance = holder;
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
try {
|
||||
holderInstance = holderClassField.get(holder);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to access packet map", ex);
|
||||
}
|
||||
}
|
||||
|
||||
Map<Class<?>, Integer> packetMap;
|
||||
try {
|
||||
packetMap = (Map<Class<?>, Integer>) packetMapField.get(holder);
|
||||
packetMap = (Map<Class<?>, Integer>) packetMapField.get(holderInstance);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new RuntimeException("Failed to access packet map", ex);
|
||||
}
|
||||
|
@ -213,13 +269,216 @@ public class PacketRegistry {
|
|||
// Maps we have to occasionally check have changed
|
||||
// TODO: Find equivalent in Object2IntMap
|
||||
|
||||
/* for (Object map : serverMaps.values()) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
/*
|
||||
* for (Object map : serverMaps.values()) { result.containers.add(new
|
||||
* MapContainer(map)); }
|
||||
*
|
||||
* for (Object map : clientMaps.values()) { result.containers.add(new
|
||||
* MapContainer(map)); }
|
||||
*/
|
||||
|
||||
for (Object protocol : protocols) {
|
||||
Enum<?> enumProtocol = (Enum<?>) protocol;
|
||||
PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol);
|
||||
|
||||
// Associate known types
|
||||
if (serverMaps.containsKey(protocol)) {
|
||||
associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER);
|
||||
}
|
||||
if (clientMaps.containsKey(protocol)) {
|
||||
associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
for (Object map : clientMaps.values()) {
|
||||
result.containers.add(new MapContainer(map));
|
||||
} */
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static synchronized Register createRegisterV1_20_5() {
|
||||
Object[] protocols = ENUM_PROTOCOL.getEnumConstants();
|
||||
|
||||
// PacketType<?> to class map
|
||||
final Map<Object, Class<?>> packetTypeMap = new HashMap<>();
|
||||
|
||||
// List of all class containing PacketTypes
|
||||
String[] packetTypesClassNames = new String[] {
|
||||
"common.CommonPacketTypes",
|
||||
"configuration.ConfigurationPacketTypes",
|
||||
"cookie.CookiePacketTypes",
|
||||
"game.GamePacketTypes",
|
||||
"handshake.HandshakePacketTypes",
|
||||
"login.LoginPacketTypes",
|
||||
"ping.PingPacketTypes",
|
||||
"status.StatusPacketTypes"
|
||||
};
|
||||
|
||||
Class<?> packetTypeClass = MinecraftReflection.getMinecraftClass("network.protocol.PacketType");
|
||||
|
||||
for (String packetTypesClassName : packetTypesClassNames) {
|
||||
Class<?> packetTypesClass = MinecraftReflection
|
||||
.getOptionalNMS("network.protocol." + packetTypesClassName)
|
||||
.orElse(null);
|
||||
if (packetTypesClass == null) {
|
||||
ProtocolLogger.debug("Can't find PacketType class: {0}, will skip it", packetTypesClassName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check every field for "static final PacketType<?>"
|
||||
for (Field field : packetTypesClass.getDeclaredFields()) {
|
||||
try {
|
||||
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object packetType = field.get(null);
|
||||
if (!packetTypeClass.isInstance(packetType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// retrieve the generic type T of the PacketType<T> field
|
||||
Type packet = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
|
||||
if (packet instanceof Class<?>) {
|
||||
packetTypeMap.put(packetType, (Class<?>) packet);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ID to Packet class maps
|
||||
final Map<Object, Map<Class<?>, Integer>> serverMaps = new LinkedHashMap<>();
|
||||
final Map<Object, Map<Class<?>, Integer>> clientMaps = new LinkedHashMap<>();
|
||||
|
||||
// global registry instance
|
||||
final Register result = new Register();
|
||||
|
||||
// List of all class containing ProtocolInfos
|
||||
String[] protocolClassNames = new String[] {
|
||||
"configuration.ConfigurationProtocols",
|
||||
"game.GameProtocols",
|
||||
"handshake.HandshakeProtocols",
|
||||
"login.LoginProtocols",
|
||||
"status.StatusProtocols"
|
||||
};
|
||||
|
||||
Class<?> protocolInfoClass = MinecraftReflection.getProtocolInfoClass();
|
||||
Class<?> protocolInfoUnboundClass = MinecraftReflection.getProtocolInfoUnboundClass();
|
||||
Class<?> streamCodecClass = MinecraftReflection.getStreamCodecClass();
|
||||
Class<?> idCodecClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec");
|
||||
Class<?> idCodecEntryClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec$Entry", "network.codec.IdDispatchCodec$b");
|
||||
Class<?> protocolDirectionClass = MinecraftReflection.getPacketFlowClass();
|
||||
|
||||
Function<?, ?> emptyFunction = input -> null;
|
||||
|
||||
FuzzyReflection protocolInfoReflection = FuzzyReflection.fromClass(protocolInfoClass);
|
||||
|
||||
MethodAccessor protocolAccessor = Accessors.getMethodAccessor(protocolInfoReflection
|
||||
.getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0]));
|
||||
|
||||
MethodAccessor directionAccessor = Accessors.getMethodAccessor(protocolInfoReflection
|
||||
.getMethodByReturnTypeAndParameters("flow", protocolDirectionClass, new Class[0]));
|
||||
|
||||
MethodAccessor codecAccessor = Accessors.getMethodAccessor(
|
||||
protocolInfoReflection.getMethodByReturnTypeAndParameters("codec", streamCodecClass, new Class[0]));
|
||||
|
||||
MethodAccessor bindAccessor = Accessors.getMethodAccessor(FuzzyReflection.fromClass(protocolInfoUnboundClass)
|
||||
.getMethodByReturnTypeAndParameters("bind", protocolInfoClass, new Class[] { Function.class }));
|
||||
|
||||
FuzzyReflection idCodecReflection = FuzzyReflection.fromClass(idCodecClass, true);
|
||||
|
||||
FieldAccessor byIdAccessor = Accessors.getFieldAccessor(idCodecReflection
|
||||
.getField(FuzzyFieldContract.newBuilder().typeDerivedOf(List.class).build()));
|
||||
|
||||
FieldAccessor toIdAccessor = Accessors.getFieldAccessor(idCodecReflection
|
||||
.getField(FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).build()));
|
||||
|
||||
FuzzyReflection idCodecEntryReflection = FuzzyReflection.fromClass(idCodecEntryClass, true);
|
||||
|
||||
MethodAccessor idCodecEntryTypeAccessor = Accessors.getMethodAccessor(idCodecEntryReflection
|
||||
.getMethodByReturnTypeAndParameters("type", Object.class, new Class[0]));
|
||||
|
||||
MethodAccessor idCodecEntrySerializerAccessor = Accessors.getMethodAccessor(idCodecEntryReflection
|
||||
.getMethodByReturnTypeAndParameters("serializer", streamCodecClass, new Class[0]));
|
||||
|
||||
for (String protocolClassName : protocolClassNames) {
|
||||
Class<?> protocolClass = MinecraftReflection
|
||||
.getOptionalNMS("network.protocol." + protocolClassName)
|
||||
.orElse(null);
|
||||
if (protocolClass == null) {
|
||||
ProtocolLogger.debug("Can't find protocol class: {0}, will skip it", protocolClassName);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Field field : protocolClass.getDeclaredFields()) {
|
||||
try {
|
||||
// ignore none static and final fields
|
||||
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object protocolInfo = field.get(null);
|
||||
|
||||
// bind unbound ProtocolInfo to empty function to get real ProtocolInfo
|
||||
if (protocolInfoUnboundClass.isInstance(protocolInfo)) {
|
||||
protocolInfo = bindAccessor.invoke(protocolInfo, new Object[] { emptyFunction });
|
||||
}
|
||||
|
||||
// ignore any field that isn't a ProtocolInfo
|
||||
if (!protocolInfoClass.isInstance(protocolInfo)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get codec and check if codec is instance of IdDispatchCodec
|
||||
// since that is the only support codec as of now
|
||||
Object codec = codecAccessor.invoke(protocolInfo);
|
||||
if (!idCodecClass.isInstance(codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// retrieve packetTypeMap and convert it to packetIdMap
|
||||
Map<Class<?>, Integer> packetMap = new HashMap<>();
|
||||
List<Object> serializerList = (List<Object>) byIdAccessor.get(codec);
|
||||
Map<Object, Integer> packetTypeIdMap = (Map<Object, Integer>) toIdAccessor.get(codec);
|
||||
|
||||
for (Map.Entry<Object, Integer> entry : packetTypeIdMap.entrySet()) {
|
||||
Class<?> packetClass = packetTypeMap.get(entry.getKey());
|
||||
if (packetClass == null) {
|
||||
throw new RuntimeException("packetType missing packet " + entry.getKey());
|
||||
}
|
||||
|
||||
packetMap.put(packetClass, entry.getValue());
|
||||
}
|
||||
|
||||
// retrieve packet codecs for packet construction and write methods
|
||||
for (Object entry : serializerList) {
|
||||
Object packetType = idCodecEntryTypeAccessor.invoke(entry);
|
||||
|
||||
Class<?> packetClass = packetTypeMap.get(packetType);
|
||||
if (packetClass == null) {
|
||||
throw new RuntimeException("packetType missing packet " + packetType);
|
||||
}
|
||||
|
||||
Object serializer = idCodecEntrySerializerAccessor.invoke(entry);
|
||||
result.classToCodec.put(packetClass, new WrappedStreamCodec(serializer));
|
||||
}
|
||||
|
||||
// get EnumProtocol and Direction of protocol info
|
||||
Object protocol = protocolAccessor.invoke(protocolInfo);
|
||||
String direction = directionAccessor.invoke(protocolInfo).toString();
|
||||
|
||||
if (direction.contains("CLIENTBOUND")) { // Sent by Server
|
||||
serverMaps.put(protocol, packetMap);
|
||||
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
|
||||
clientMaps.put(protocol, packetMap);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Object protocol : protocols) {
|
||||
Enum<?> enumProtocol = (Enum<?>) protocol;
|
||||
|
@ -238,8 +497,8 @@ public class PacketRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reverses a key->value map to value->key
|
||||
* Non-deterministic behavior when multiple keys are mapped to the same value
|
||||
* Reverses a key->value map to value->key Non-deterministic behavior when
|
||||
* multiple keys are mapped to the same value
|
||||
*/
|
||||
private static <K, V> Map<V, K> reverse(Map<K, V> map) {
|
||||
Map<V, K> newMap = new HashMap<>(map.size());
|
||||
|
@ -249,7 +508,8 @@ public class PacketRegistry {
|
|||
return newMap;
|
||||
}
|
||||
|
||||
protected static void associatePackets(Register register, Map<Integer, Class<?>> lookup, PacketType.Protocol protocol, Sender sender) {
|
||||
protected static void associatePackets(Register register, Map<Integer, Class<?>> lookup,
|
||||
PacketType.Protocol protocol, Sender sender) {
|
||||
for (Map.Entry<Integer, Class<?>> entry : lookup.entrySet()) {
|
||||
int packetId = entry.getKey();
|
||||
Class<?> packetClass = entry.getValue();
|
||||
|
@ -278,7 +538,7 @@ public class PacketRegistry {
|
|||
/**
|
||||
* Initializes the packet registry.
|
||||
*/
|
||||
private static void initialize() {
|
||||
static void initialize() {
|
||||
if (INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
@ -288,8 +548,10 @@ public class PacketRegistry {
|
|||
return;
|
||||
}
|
||||
|
||||
if (MinecraftVersion.BEE_UPDATE.atOrAbove()) {
|
||||
REGISTER = createNewRegister();
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
REGISTER = createRegisterV1_20_5();
|
||||
} else if (MinecraftVersion.BEE_UPDATE.atOrAbove()) {
|
||||
REGISTER = createRegisterV1_15_0();
|
||||
} else {
|
||||
REGISTER = createOldRegister();
|
||||
}
|
||||
|
@ -298,8 +560,21 @@ public class PacketRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the wrapped stream codec to de-/serialize the given packet class
|
||||
*
|
||||
* @param packetClass - the packet class
|
||||
* @return wrapped stream codec if existing, otherwise <code>null</code>
|
||||
*/
|
||||
@Nullable
|
||||
public static WrappedStreamCodec getStreamCodec(Class<?> packetClass) {
|
||||
initialize();
|
||||
return REGISTER.classToCodec.get(packetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given packet type is supported on the current server.
|
||||
*
|
||||
* @param type - the type to check.
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
|
@ -310,6 +585,7 @@ public class PacketRegistry {
|
|||
|
||||
/**
|
||||
* Retrieve every known and supported server packet type.
|
||||
*
|
||||
* @return Every server packet type.
|
||||
*/
|
||||
public static Set<PacketType> getServerPacketTypes() {
|
||||
|
@ -318,9 +594,10 @@ public class PacketRegistry {
|
|||
|
||||
return Collections.unmodifiableSet(REGISTER.serverPackets);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve every known and supported server packet type.
|
||||
*
|
||||
* @return Every server packet type.
|
||||
*/
|
||||
public static Set<PacketType> getClientPacketTypes() {
|
||||
|
@ -338,7 +615,8 @@ public class PacketRegistry {
|
|||
&& !Modifier.isAbstract(clazz.getModifiers())) {
|
||||
return clazz;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -347,8 +625,9 @@ public class PacketRegistry {
|
|||
/**
|
||||
* Retrieves the correct packet class from a given type.
|
||||
*
|
||||
* @param type - the packet type.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not injected classes.
|
||||
* @param type - the packet type.
|
||||
* @param forceVanilla - whether or not to look for vanilla classes, not
|
||||
* injected classes.
|
||||
* @return The associated class.
|
||||
* @deprecated forceVanilla no longer has any effect
|
||||
*/
|
||||
|
@ -363,7 +642,7 @@ public class PacketRegistry {
|
|||
// Try the lookup first (may be null, so check contains)
|
||||
Optional<Class<?>> res = REGISTER.typeToClass.get(type);
|
||||
if (res != null) {
|
||||
if(res.isPresent() && MinecraftReflection.isBundleDelimiter(res.get())) {
|
||||
if (res.isPresent() && MinecraftReflection.isBundleDelimiter(res.get())) {
|
||||
return MinecraftReflection.getPackedBundlePacketClass();
|
||||
}
|
||||
return res;
|
||||
|
@ -372,21 +651,25 @@ public class PacketRegistry {
|
|||
// Then try looking up the class names
|
||||
Class<?> clazz = searchForPacket(type.getClassNames());
|
||||
if (clazz != null) {
|
||||
// we'd like for it to be associated correctly from the get-go; this is OK on older versions though
|
||||
ProtocolLogger.warnAbove(type.getCurrentVersion(), "Updating associated class for {0} to {1}", type.name(), clazz);
|
||||
// we'd like for it to be associated correctly from the get-go; this is OK on
|
||||
// older versions though
|
||||
ProtocolLogger.warnAbove(type.getCurrentVersion(), "Updating associated class for {0} to {1}", type.name(),
|
||||
clazz);
|
||||
}
|
||||
|
||||
// cache it for next time
|
||||
associate(type, clazz);
|
||||
if(clazz != null && MinecraftReflection.isBundleDelimiter(clazz)) {
|
||||
clazz = MinecraftReflection.getPackedBundlePacketClass().orElseThrow(() -> new IllegalStateException("Packet bundle class not found."));
|
||||
if (clazz != null && MinecraftReflection.isBundleDelimiter(clazz)) {
|
||||
clazz = MinecraftReflection.getPackedBundlePacketClass()
|
||||
.orElseThrow(() -> new IllegalStateException("Packet bundle class not found."));
|
||||
}
|
||||
return Optional.ofNullable(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the packet class associated with a given type. First attempts to read from the
|
||||
* type-to-class mapping, and tries
|
||||
* Get the packet class associated with a given type. First attempts to read
|
||||
* from the type-to-class mapping, and tries
|
||||
*
|
||||
* @param type the packet type
|
||||
* @return The associated class
|
||||
*/
|
||||
|
@ -397,21 +680,54 @@ public class PacketRegistry {
|
|||
|
||||
/**
|
||||
* Retrieve the packet type of a given packet.
|
||||
*
|
||||
* @param packet - the class of the packet.
|
||||
* @return The packet type, or NULL if not found.
|
||||
* @deprecated major issues due to packets with shared classes being registered
|
||||
* in multiple states.
|
||||
*/
|
||||
@Deprecated
|
||||
public static PacketType getPacketType(Class<?> packet) {
|
||||
initialize();
|
||||
|
||||
if (MinecraftReflection.isBundlePacket(packet)) {
|
||||
return PacketType.Play.Server.BUNDLE;
|
||||
}
|
||||
|
||||
|
||||
return REGISTER.classToType.get(packet);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the associated packet type for a packet class in the given protocol
|
||||
* state.
|
||||
*
|
||||
* @param protocol the protocol state to retrieve the packet from.
|
||||
* @param packet the class identifying the packet type.
|
||||
* @return the packet type associated with the given class in the given protocol
|
||||
* state, or null if not found.
|
||||
*/
|
||||
public static PacketType getPacketType(PacketType.Protocol protocol, Class<?> packet) {
|
||||
initialize();
|
||||
if (MinecraftReflection.isBundlePacket(packet)) {
|
||||
return PacketType.Play.Server.BUNDLE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverts https://github.com/dmulloy2/ProtocolLib/pull/2568 for server versions 1.8 to 1.20.1.
|
||||
*
|
||||
* Since packet classes are not shared for these versions,
|
||||
* the protocol state is not needed to determine the packet type from class.
|
||||
*/
|
||||
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
return getPacketType(packet);
|
||||
}
|
||||
Map<Class<?>, PacketType> classToTypesForProtocol = REGISTER.protocolClassToType.get(protocol);
|
||||
return classToTypesForProtocol == null ? null : classToTypesForProtocol.get(packet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the packet type of a given packet.
|
||||
*
|
||||
* @param packet - the class of the packet.
|
||||
* @param sender - the sender of the packet, or NULL.
|
||||
* @return The packet type, or NULL if not found.
|
||||
|
@ -421,4 +737,4 @@ public class PacketRegistry {
|
|||
public static PacketType getPacketType(Class<?> packet, Sender sender) {
|
||||
return getPacketType(packet);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package com.comphenix.protocol.injector.player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.concurrency.PacketTypeSet;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractPlayerInjectionHandler implements PlayerInjectionHandler {
|
||||
|
||||
private final PacketTypeSet sendingFilters;
|
||||
|
||||
public AbstractPlayerInjectionHandler(PacketTypeSet sendingFilters) {
|
||||
this.sendingFilters = sendingFilters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPacketHandler(PacketType type, Set<ListenerOptions> options) {
|
||||
this.sendingFilters.addType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePacketHandler(PacketType type) {
|
||||
this.sendingFilters.removeType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PacketType> getSendingFilters() {
|
||||
return this.sendingFilters.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.sendingFilters.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReceivePackets() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(PacketListener listener) {
|
||||
// They're all fine!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkListener(Set<PacketListener> listeners) {
|
||||
// Yes, really
|
||||
}
|
||||
}
|
|
@ -1,14 +1,11 @@
|
|||
package com.comphenix.protocol.injector.player;
|
||||
|
||||
import java.util.Set;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListenerOptions;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface PlayerInjectionHandler {
|
||||
|
||||
|
@ -20,21 +17,6 @@ public interface PlayerInjectionHandler {
|
|||
*/
|
||||
int getProtocolVersion(Player player);
|
||||
|
||||
/**
|
||||
* Add an underlying packet handler of the given type.
|
||||
*
|
||||
* @param type - packet type to register.
|
||||
* @param options - any specified listener options.
|
||||
*/
|
||||
void addPacketHandler(PacketType type, Set<ListenerOptions> options);
|
||||
|
||||
/**
|
||||
* Remove an underlying packet handler of this type.
|
||||
*
|
||||
* @param type - packet type to unregister.
|
||||
*/
|
||||
void removePacketHandler(PacketType type);
|
||||
|
||||
/**
|
||||
* Initialize a player hook, allowing us to read server packets.
|
||||
* <p>
|
||||
|
@ -45,13 +27,6 @@ public interface PlayerInjectionHandler {
|
|||
*/
|
||||
void injectPlayer(Player player, ConflictStrategy strategy);
|
||||
|
||||
/**
|
||||
* Invoke special routines for handling disconnect before a player is uninjected.
|
||||
*
|
||||
* @param player - player to process.
|
||||
*/
|
||||
void handleDisconnect(Player player);
|
||||
|
||||
/**
|
||||
* Uninject the given player.
|
||||
*
|
||||
|
@ -85,49 +60,6 @@ public interface PlayerInjectionHandler {
|
|||
*/
|
||||
void updatePlayer(Player player);
|
||||
|
||||
/**
|
||||
* Determine if the given listeners are valid.
|
||||
*
|
||||
* @param listeners - listeners to check.
|
||||
*/
|
||||
void checkListener(Set<PacketListener> listeners);
|
||||
|
||||
/**
|
||||
* Determine if a listener is valid or not.
|
||||
* <p>
|
||||
* If not, a warning will be printed to the console.
|
||||
*
|
||||
* @param listener - listener to check.
|
||||
*/
|
||||
void checkListener(PacketListener listener);
|
||||
|
||||
/**
|
||||
* Retrieve the current list of registered sending listeners.
|
||||
*
|
||||
* @return List of the sending listeners's packet IDs.
|
||||
*/
|
||||
Set<PacketType> getSendingFilters();
|
||||
|
||||
/**
|
||||
* Whether or not this player injection handler can also receive packets.
|
||||
*
|
||||
* @return TRUE if it can, FALSE otherwise.
|
||||
*/
|
||||
boolean canReceivePackets();
|
||||
|
||||
/**
|
||||
* Close any lingering proxy injections.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Determine if we have packet listeners with the given type that must be executed on the main thread.
|
||||
*
|
||||
* @param type - the packet type.
|
||||
* @return TRUE if we do, FALSE otherwise.
|
||||
*/
|
||||
boolean hasMainThreadListener(PacketType type);
|
||||
|
||||
Channel getChannel(Player player);
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
|
||||
package com.comphenix.protocol.reflect;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.StructureCache;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
|
@ -46,18 +41,6 @@ public class ObjectWriter {
|
|||
* @return A structure modifier for the given type.
|
||||
*/
|
||||
private StructureModifier<Object> getModifier(Class<?> type) {
|
||||
Class<?> packetClass = MinecraftReflection.getPacketClass();
|
||||
|
||||
// Handle subclasses of the packet class with our custom structure cache, if possible
|
||||
if (!type.equals(packetClass) && packetClass.isAssignableFrom(type)) {
|
||||
// might be a packet, but some packets are not registered (for example PacketPlayInFlying, only the subtypes are present)
|
||||
PacketType packetType = PacketRegistry.getPacketType(type);
|
||||
if (packetType != null) {
|
||||
// packet is present, delegate to the cache
|
||||
return StructureCache.getStructure(packetType);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the structure modifier if we haven't already
|
||||
StructureModifier<Object> modifier = CACHE.get(type);
|
||||
if (modifier == null) {
|
||||
|
|
|
@ -143,8 +143,7 @@ public class StructureModifier<T> {
|
|||
for (FieldAccessor accessor : fields) {
|
||||
Field field = accessor.getField();
|
||||
if (!field.getType().isPrimitive() && !Modifier.isFinal(field.getModifiers())) {
|
||||
Object defaultInstance = DEFAULT_GENERATOR.getDefault(field.getType());
|
||||
if (defaultInstance != null) {
|
||||
if (DEFAULT_GENERATOR.hasDefault(field.getType())) {
|
||||
requireDefaults.put(accessor, currentFieldIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public class BukkitCloner implements Cloner {
|
|||
static {
|
||||
fromManual(MinecraftReflection::getItemStackClass, source ->
|
||||
MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone()));
|
||||
fromWrapper(MinecraftReflection::getDataWatcherClass, WrappedDataWatcher::new);
|
||||
fromManual(MinecraftReflection::getDataWatcherClass, source -> new WrappedDataWatcher(source).deepClone().getHandle());
|
||||
fromConverter(MinecraftReflection::getBlockPositionClass, BlockPosition.getConverter());
|
||||
fromWrapper(MinecraftReflection::getServerPingClass, WrappedServerPing::fromHandle);
|
||||
fromConverter(MinecraftReflection::getMinecraftKeyClass, MinecraftKey.getConverter());
|
||||
|
|
|
@ -62,12 +62,12 @@ public class ImmutableDetector implements Cloner {
|
|||
add(MinecraftReflection::getDataWatcherSerializerClass);
|
||||
add(MinecraftReflection::getBlockClass);
|
||||
add(MinecraftReflection::getItemClass);
|
||||
add("sounds.SoundEffect", "sounds.SoundEvents", "SoundEffect");
|
||||
add(MinecraftReflection::getSoundEffectClass);
|
||||
|
||||
if (MinecraftVersion.AQUATIC_UPDATE.atOrAbove()) {
|
||||
add(MinecraftReflection::getFluidTypeClass);
|
||||
add(MinecraftReflection::getParticleTypeClass);
|
||||
add("core.particles.Particle","core.particles.ParticleType", "Particle");
|
||||
add(MinecraftReflection::getParticleClass);
|
||||
}
|
||||
|
||||
if (MinecraftVersion.VILLAGE_UPDATE.atOrAbove()) {
|
||||
|
|
|
@ -29,6 +29,26 @@ public class FuzzyMatchers {
|
|||
return (value, parent) -> value.isArray() && componentMatcher.isMatch(value.getComponentType(), parent);
|
||||
}
|
||||
|
||||
public static AbstractFuzzyMatcher<Class<?>> except(Class<?> clazz) {
|
||||
return (value, parent) -> !clazz.isAssignableFrom(value);
|
||||
}
|
||||
|
||||
public static AbstractFuzzyMatcher<Class<?>> assignable(Class<?> clazz) {
|
||||
return (value, parent) -> clazz.isAssignableFrom(value);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static AbstractFuzzyMatcher<Class<?>> and(AbstractFuzzyMatcher<Class<?>>... matchers) {
|
||||
return (value, parent) -> {
|
||||
for (AbstractFuzzyMatcher<Class<?>> matcher : matchers) {
|
||||
if (!matcher.isMatch(value, parent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a fuzzy matcher that will match any class.
|
||||
*
|
||||
|
|
|
@ -26,6 +26,8 @@ import javax.annotation.Nullable;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
|
@ -164,7 +166,19 @@ public class DefaultInstances implements InstanceProvider {
|
|||
public <T> T getDefault(Class<T> type) {
|
||||
return getDefaultInternal(type, registered, 0);
|
||||
}
|
||||
|
||||
|
||||
private final ThreadLocal<Map<Class<?>, Boolean>> cache = ThreadLocal.withInitial(WeakHashMap::new);
|
||||
|
||||
/**
|
||||
* Determines if a given class has a default value.
|
||||
*
|
||||
* @param type - the class to check
|
||||
* @return true if the class has a default value, false otherwise
|
||||
*/
|
||||
public boolean hasDefault(Class<?> type) {
|
||||
return cache.get().computeIfAbsent(type, aClass -> getDefaultInternal(aClass, registered, 0) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the constructor with the fewest number of parameters.
|
||||
* @param <T> Type
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package com.comphenix.protocol.reflect.instances;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
|
||||
public final class InstanceCreator implements Supplier<Object> {
|
||||
private ConstructorAccessor constructor = null;
|
||||
private MethodAccessor factoryMethod = null;
|
||||
private Class<?>[] paramTypes = null;
|
||||
private boolean failed = false;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private InstanceCreator(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public static InstanceCreator forClass(Class<?> type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Type cannot be null.");
|
||||
}
|
||||
|
||||
return new InstanceCreator(type);
|
||||
}
|
||||
|
||||
private Object createInstance(Class<?> clazz) {
|
||||
try {
|
||||
return DefaultInstances.DEFAULT.create(clazz);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] createParams(Class<?>[] paramTypes) {
|
||||
Object[] params = new Object[paramTypes.length];
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
params[i] = createInstance(paramTypes[i]);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get() {
|
||||
Object[] params = paramTypes != null ? createParams(paramTypes) : null;
|
||||
|
||||
if (constructor != null) {
|
||||
return constructor.invoke(params);
|
||||
}
|
||||
|
||||
if (factoryMethod != null) {
|
||||
return factoryMethod.invoke(null, params);
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object result = null;
|
||||
int minCount = Integer.MAX_VALUE;
|
||||
|
||||
for (Constructor<?> testCtor : type.getConstructors()) {
|
||||
Class<?>[] paramTypes = testCtor.getParameterTypes();
|
||||
if (paramTypes.length > minCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object[] testParams = createParams(paramTypes);
|
||||
|
||||
try {
|
||||
result = testCtor.newInstance(testParams);
|
||||
minCount = paramTypes.length;
|
||||
this.constructor = Accessors.getConstructorAccessor(testCtor);
|
||||
this.paramTypes = paramTypes;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
minCount = Integer.MAX_VALUE;
|
||||
|
||||
for (Method testMethod : type.getDeclaredMethods()) {
|
||||
int modifiers = testMethod.getModifiers();
|
||||
if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testMethod.getReturnType() != type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?>[] paramTypes = testMethod.getParameterTypes();
|
||||
if (paramTypes.length > minCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object[] testParams = createParams(paramTypes);
|
||||
|
||||
try {
|
||||
result = testMethod.invoke(null, testParams);
|
||||
minCount = paramTypes.length;
|
||||
this.factoryMethod = Accessors.getMethodAccessor(testMethod);
|
||||
this.paramTypes = paramTypes;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
this.failed = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package com.comphenix.protocol.reflect.instances;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -24,6 +25,7 @@ import javax.annotation.Nullable;
|
|||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InstanceProvider {
|
||||
/**
|
||||
* Create an instance given a type, if possible.
|
||||
|
@ -31,5 +33,5 @@ public interface InstanceProvider {
|
|||
* @return The instance, or NULL if the type cannot be created.
|
||||
* @throws NotConstructableException Thrown to indicate that this type cannot or should never be constructed.
|
||||
*/
|
||||
public abstract Object create(@Nullable Class<?> type);
|
||||
Object create(@Nullable Class<?> type);
|
||||
}
|
||||
|
|
|
@ -53,12 +53,12 @@ public class MinecraftGenerator {
|
|||
}
|
||||
}
|
||||
return DEFAULT_ENTITY_TYPES;
|
||||
} else if (type.isAssignableFrom(Map.class)) {
|
||||
} else if (Map.class.isAssignableFrom(type)) {
|
||||
ConstructorAccessor ctor = FAST_MAP_CONSTRUCTORS.computeIfAbsent(type, __ -> {
|
||||
try {
|
||||
String name = type.getCanonicalName();
|
||||
if (name != null && name.contains("it.unimi.fastutils")) {
|
||||
Class<?> clz = Class.forName(name.substring(name.length() - 3) + "OpenHashMap");
|
||||
String name = type.getName();
|
||||
if (name.contains("it.unimi.dsi.fastutil")) {
|
||||
Class<?> clz = Class.forName(name.replace("Map", "OpenHashMap"));
|
||||
return Accessors.getConstructorAccessorOrNull(clz);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
|
|
@ -40,7 +40,7 @@ public class PrimitiveGenerator implements InstanceProvider {
|
|||
/**
|
||||
* Shared instance of this generator.
|
||||
*/
|
||||
public static PrimitiveGenerator INSTANCE = new PrimitiveGenerator();
|
||||
public static final PrimitiveGenerator INSTANCE = new PrimitiveGenerator();
|
||||
|
||||
// Our default string value
|
||||
private final String stringDefault;
|
||||
|
|
|
@ -29,4 +29,10 @@ public class DefaultScheduler implements ProtocolScheduler {
|
|||
int taskId = scheduler.scheduleSyncDelayedTask(plugin, task, delay);
|
||||
return taskId >= 0 ? new DefaultTask(scheduler, taskId) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskAsync(Runnable task) {
|
||||
int taskId = scheduler.runTaskAsynchronously(plugin, task).getTaskId();
|
||||
return taskId >= 0 ? new DefaultTask(scheduler, taskId) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.comphenix.protocol.scheduler;
|
||||
|
||||
import org.bukkit.scheduler.BukkitScheduler;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
public class DefaultTask implements Task {
|
||||
private final int taskId;
|
||||
|
|
|
@ -9,23 +9,32 @@ import org.bukkit.plugin.Plugin;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
public class FoliaScheduler implements ProtocolScheduler {
|
||||
private final Object foliaScheduler;
|
||||
private final Object foliaRegionScheduler;
|
||||
private final MethodAccessor runAtFixedRate;
|
||||
private final MethodAccessor runDelayed;
|
||||
private final MethodAccessor execute;
|
||||
private final MethodAccessor cancel;
|
||||
|
||||
private final Object foliaAsyncScheduler;
|
||||
private final MethodAccessor executeAsync;
|
||||
|
||||
private final Plugin plugin;
|
||||
|
||||
public FoliaScheduler(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
MethodAccessor getScheduler = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getGlobalRegionScheduler");
|
||||
this.foliaScheduler = getScheduler.invoke(Bukkit.getServer());
|
||||
this.foliaRegionScheduler = getScheduler.invoke(Bukkit.getServer());
|
||||
|
||||
this.runAtFixedRate = Accessors.getMethodAccessor(foliaScheduler.getClass(), "runAtFixedRate", Plugin.class,
|
||||
this.runAtFixedRate = Accessors.getMethodAccessor(foliaRegionScheduler.getClass(), "runAtFixedRate", Plugin.class,
|
||||
Consumer.class, long.class, long.class);
|
||||
this.execute = Accessors.getMethodAccessor(foliaScheduler.getClass(), "run", Plugin.class, Consumer.class);
|
||||
this.runDelayed = Accessors.getMethodAccessor(foliaScheduler.getClass(), "runDelayed", Plugin.class, Consumer.class, long.class);
|
||||
this.execute = Accessors.getMethodAccessor(foliaRegionScheduler.getClass(), "run", Plugin.class, Consumer.class);
|
||||
this.runDelayed = Accessors.getMethodAccessor(foliaRegionScheduler.getClass(), "runDelayed", Plugin.class, Consumer.class, long.class);
|
||||
|
||||
MethodAccessor getAsyncScheduler = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getAsyncScheduler");
|
||||
foliaAsyncScheduler = getAsyncScheduler.invoke(Bukkit.getServer());
|
||||
|
||||
this.executeAsync = Accessors.getMethodAccessor(foliaAsyncScheduler.getClass(), "runNow", Plugin.class, Consumer.class);
|
||||
|
||||
Class<?> taskClass = MinecraftReflection.getLibraryClass("io.papermc.paper.threadedregions.scheduler.ScheduledTask");
|
||||
this.cancel = Accessors.getMethodAccessor(taskClass, "cancel");
|
||||
|
@ -33,19 +42,25 @@ public class FoliaScheduler implements ProtocolScheduler {
|
|||
|
||||
@Override
|
||||
public Task scheduleSyncRepeatingTask(Runnable task, long delay, long period) {
|
||||
Object taskHandle = runAtFixedRate.invoke(foliaScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay, period);
|
||||
Object taskHandle = runAtFixedRate.invoke(foliaRegionScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay, period);
|
||||
return new FoliaTask(cancel, taskHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTask(Runnable task) {
|
||||
Object taskHandle = execute.invoke(foliaScheduler, plugin, (Consumer<Object>)(t -> task.run()));
|
||||
Object taskHandle = execute.invoke(foliaRegionScheduler, plugin, (Consumer<Object>)(t -> task.run()));
|
||||
return new FoliaTask(cancel, taskHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task scheduleSyncDelayedTask(Runnable task, long delay) {
|
||||
Object taskHandle = runDelayed.invoke(foliaScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay);
|
||||
Object taskHandle = runDelayed.invoke(foliaRegionScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay);
|
||||
return new FoliaTask(cancel, taskHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task runTaskAsync(Runnable task) {
|
||||
Object taskHandle = executeAsync.invoke(foliaAsyncScheduler, plugin, (Consumer<Object>)(t -> task.run()));
|
||||
return new FoliaTask(cancel, taskHandle);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package com.comphenix.protocol.scheduler;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLib;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public interface ProtocolScheduler {
|
||||
Task scheduleSyncRepeatingTask(Runnable task, long delay, long period);
|
||||
|
||||
Task runTask(Runnable task);
|
||||
|
||||
Task scheduleSyncDelayedTask(Runnable task, long delay);
|
||||
|
||||
Task runTaskAsync(Runnable task);
|
||||
}
|
||||
|
|
|
@ -1,125 +0,0 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* Represents an online algortihm of computing histograms over time.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class HistogramStream extends OnlineComputation {
|
||||
/**
|
||||
* Each bin in the histogram, indexed by time.
|
||||
*/
|
||||
protected List<StatisticsStream> bins;
|
||||
|
||||
/**
|
||||
* The current statistics stream we are updating.
|
||||
*/
|
||||
protected StatisticsStream current;
|
||||
|
||||
/**
|
||||
* The maximum number of observations in each bin.
|
||||
*/
|
||||
protected int binWidth;
|
||||
|
||||
/**
|
||||
* The number of total observations.
|
||||
*/
|
||||
protected int count;
|
||||
|
||||
/**
|
||||
* Construct a new histogram stream which splits up every observation in different bins, ordered by time.
|
||||
* @param binWidth - maximum number of observations in each bin.
|
||||
*/
|
||||
public HistogramStream(int binWidth) {
|
||||
this(new ArrayList<StatisticsStream>(), new StatisticsStream(), binWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new copy of the given histogram.
|
||||
* @param other - the histogram to copy.
|
||||
*/
|
||||
public HistogramStream(HistogramStream other) {
|
||||
// Deep cloning
|
||||
for (StatisticsStream stream : other.bins) {
|
||||
StatisticsStream copy = stream.copy();
|
||||
|
||||
// Update current
|
||||
if (stream == other.current)
|
||||
this.current = copy;
|
||||
this.bins.add(copy);
|
||||
}
|
||||
this.binWidth = other.binWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new histogram stream.
|
||||
* @param bins - list of bins.
|
||||
* @param current - the current selected bin. This will be added to the list if it is not already present.
|
||||
* @param binWidth - the desired number of observations in each bin.
|
||||
*/
|
||||
protected HistogramStream(List<StatisticsStream> bins, StatisticsStream current, int binWidth) {
|
||||
if (binWidth < 1)
|
||||
throw new IllegalArgumentException("binWidth cannot be less than 1");
|
||||
this.bins = Preconditions.checkNotNull(bins, "bins cannot be NULL");
|
||||
this.current = Preconditions.checkNotNull(current, "current cannot be NULL");
|
||||
this.binWidth = binWidth;
|
||||
|
||||
if (!this.bins.contains(current)) {
|
||||
this.bins.add(current);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HistogramStream copy() {
|
||||
return new HistogramStream(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an immutable view of every bin in the histogram.
|
||||
* @return Every bin in the histogram.
|
||||
*/
|
||||
public ImmutableList<StatisticsStream> getBins() {
|
||||
return ImmutableList.copyOf(bins);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observe(double value) {
|
||||
checkOverflow();
|
||||
count++;
|
||||
current.observe(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the current bin has overflowed. If so, construct a new bin and set it as the current.
|
||||
*/
|
||||
protected void checkOverflow() {
|
||||
if (current.getCount() >= binWidth) {
|
||||
bins.add(current = new StatisticsStream());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the total statistics of every bin in the histogram.
|
||||
* <p>
|
||||
* This method is not thread safe.
|
||||
* @return The total statistics.
|
||||
*/
|
||||
public StatisticsStream getTotal() {
|
||||
StatisticsStream sum = null;
|
||||
|
||||
for (StatisticsStream stream : bins) {
|
||||
sum = sum != null ? stream.add(sum) : stream;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
/**
|
||||
* Represents an online computation.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public abstract class OnlineComputation {
|
||||
|
||||
/**
|
||||
* Retrieve a wrapper for another online computation that is synchronized.
|
||||
*
|
||||
* @param computation - the computation.
|
||||
* @return The synchronized wrapper.
|
||||
*/
|
||||
public static OnlineComputation synchronizedComputation(final OnlineComputation computation) {
|
||||
return new OnlineComputation() {
|
||||
@Override
|
||||
public synchronized void observe(double value) {
|
||||
computation.observe(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getCount() {
|
||||
return computation.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized OnlineComputation copy() {
|
||||
return computation.copy();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of observations.
|
||||
*
|
||||
* @return Number of observations.
|
||||
*/
|
||||
public abstract int getCount();
|
||||
|
||||
/**
|
||||
* Observe a value.
|
||||
*
|
||||
* @param value - the observed value.
|
||||
*/
|
||||
public abstract void observe(double value);
|
||||
|
||||
/**
|
||||
* Construct a copy of the current online computation.
|
||||
*
|
||||
* @return The new copy.
|
||||
*/
|
||||
public abstract OnlineComputation copy();
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
|
||||
public class PluginTimingTracker implements TimingTracker {
|
||||
|
||||
private final Map<PacketType, StatisticsStream> statistics = new ConcurrentHashMap<>();
|
||||
private volatile boolean hasReceivedData = false;
|
||||
|
||||
@Override
|
||||
public void track(PacketType packetType, Runnable runnable) {
|
||||
long startTime = System.nanoTime();
|
||||
runnable.run();
|
||||
long endTime = System.nanoTime();
|
||||
|
||||
this.statistics.computeIfAbsent(packetType, key -> new StatisticsStream())
|
||||
.observe(endTime - startTime);
|
||||
|
||||
this.hasReceivedData = true;
|
||||
}
|
||||
|
||||
public boolean hasReceivedData() {
|
||||
return hasReceivedData;
|
||||
}
|
||||
|
||||
public Map<PacketType, StatisticsStream> getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ package com.comphenix.protocol.timing;
|
|||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class StatisticsStream extends OnlineComputation {
|
||||
public class StatisticsStream {
|
||||
// This algorithm is due to Donald Knuth, as described in:
|
||||
// Donald E. Knuth (1998). The Art of Computer Programming, volume 2:
|
||||
// Seminumerical Algorithms, 3rd edn., p. 232. Boston: Addison-Wesley.
|
||||
|
@ -37,18 +37,12 @@ public class StatisticsStream extends OnlineComputation {
|
|||
this.maximum = other.maximum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatisticsStream copy() {
|
||||
return new StatisticsStream(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe a value.
|
||||
*
|
||||
* @param value - the observed value.
|
||||
*/
|
||||
@Override
|
||||
public void observe(double value) {
|
||||
public synchronized void observe(double value) {
|
||||
double delta = value - this.mean;
|
||||
|
||||
// As per Knuth
|
||||
|
@ -145,7 +139,6 @@ public class StatisticsStream extends OnlineComputation {
|
|||
*
|
||||
* @return Number of observations.
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return this.count;
|
||||
}
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* Represents a system for recording the time spent by each packet listener.
|
||||
* @author Kristian
|
||||
*/
|
||||
public class TimedListenerManager {
|
||||
public enum ListenerType {
|
||||
ASYNC_SERVER_SIDE,
|
||||
ASYNC_CLIENT_SIDE,
|
||||
SYNC_SERVER_SIDE,
|
||||
SYNC_CLIENT_SIDE;
|
||||
}
|
||||
|
||||
// The shared manager
|
||||
private final static TimedListenerManager INSTANCE = new TimedListenerManager();
|
||||
// Running?
|
||||
private final static AtomicBoolean timing = new AtomicBoolean();
|
||||
// When it was started
|
||||
private volatile Date started;
|
||||
private volatile Date stopped;
|
||||
|
||||
// The map of time trackers
|
||||
private final ConcurrentMap<String, ImmutableMap<ListenerType, TimedTracker>> map = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Retrieve the shared listener manager.
|
||||
* <p>
|
||||
* This should never change.
|
||||
* @return The shared listener manager.
|
||||
*/
|
||||
public static TimedListenerManager getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start timing listeners.
|
||||
* @return TRUE if we started timing, FALSE if we are already timing listeners.
|
||||
*/
|
||||
public boolean startTiming() {
|
||||
if (setTiming(true)) {
|
||||
started = Calendar.getInstance().getTime();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**s
|
||||
* Stop timing listeners.
|
||||
* @return TRUE if we stopped timing, FALSE otherwise.
|
||||
*/
|
||||
public boolean stopTiming() {
|
||||
if (setTiming(false)) {
|
||||
stopped = Calendar.getInstance().getTime();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the time the listener was started.
|
||||
* @return The time it was started, or NULL if they have never been started.
|
||||
*/
|
||||
public Date getStarted() {
|
||||
return started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the time the time listeners was stopped.
|
||||
* @return The time they were stopped, or NULL if not found.
|
||||
*/
|
||||
public Date getStopped() {
|
||||
return stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not the timing manager is enabled.
|
||||
* @param value - TRUE if it should be enabled, FALSE otherwise.
|
||||
* @return TRUE if the value was changed, FALSE otherwise.
|
||||
*/
|
||||
private boolean setTiming(boolean value) {
|
||||
return timing.compareAndSet(!value, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are currently timing listeners.
|
||||
* @return TRUE if we are, FALSE otherwise.
|
||||
*/
|
||||
public boolean isTiming() {
|
||||
return timing.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all packet gathering data.
|
||||
*/
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve every tracked plugin.
|
||||
* @return Every tracked plugin.
|
||||
*/
|
||||
public Set<String> getTrackedPlugins() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the timed tracker associated with the given plugin and listener type.
|
||||
* @param plugin - the plugin.
|
||||
* @param type - the listener type.
|
||||
* @return The timed tracker.
|
||||
*/
|
||||
public TimedTracker getTracker(Plugin plugin, ListenerType type) {
|
||||
return getTracker(plugin.getName(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the timed tracker associated with the given listener and listener type.
|
||||
* @param listener - the listener.
|
||||
* @param type - the listener type.
|
||||
* @return The timed tracker.
|
||||
*/
|
||||
public TimedTracker getTracker(PacketListener listener, ListenerType type) {
|
||||
return getTracker(listener.getPlugin().getName(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the timed tracker associated with the given plugin and listener type.
|
||||
* @param pluginName - the plugin name.
|
||||
* @param type - the listener type.
|
||||
* @return The timed tracker.
|
||||
*/
|
||||
public TimedTracker getTracker(String pluginName, ListenerType type) {
|
||||
return getTrackers(pluginName).get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the map of timed trackers for a specific plugin.
|
||||
* @param pluginName - the plugin name.
|
||||
* @return Map of timed trackers.
|
||||
*/
|
||||
private ImmutableMap<ListenerType, TimedTracker> getTrackers(String pluginName) {
|
||||
ImmutableMap<ListenerType, TimedTracker> trackers = map.get(pluginName);
|
||||
|
||||
// Atomic pattern
|
||||
if (trackers == null) {
|
||||
ImmutableMap<ListenerType, TimedTracker> created = newTrackerMap();
|
||||
trackers = map.putIfAbsent(pluginName, created);
|
||||
|
||||
// Success!
|
||||
if (trackers == null) {
|
||||
trackers = created;
|
||||
}
|
||||
}
|
||||
return trackers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a new map of trackers for an unspecified plugin.
|
||||
* @return A map of listeners and timed trackers.
|
||||
*/
|
||||
private ImmutableMap<ListenerType, TimedTracker> newTrackerMap() {
|
||||
ImmutableMap.Builder<ListenerType, TimedTracker> builder = ImmutableMap.builder();
|
||||
|
||||
// Construct a map with every listener type
|
||||
for (ListenerType type : ListenerType.values()) {
|
||||
builder.put(type, new TimedTracker());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Tracks the invocation time for a particular plugin against a list of packets.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public class TimedTracker {
|
||||
|
||||
// Table of packets and invocations
|
||||
private final AtomicInteger observations = new AtomicInteger();
|
||||
private final Map<PacketType, StatisticsStream> packets = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Begin tracking an execution time.
|
||||
*
|
||||
* @return The current tracking token.
|
||||
*/
|
||||
public long beginTracking() {
|
||||
return System.nanoTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and record the execution time since the creation of the given tracking token.
|
||||
*
|
||||
* @param trackingToken - the tracking token.
|
||||
* @param type - the packet type.
|
||||
*/
|
||||
public synchronized void endTracking(long trackingToken, PacketType type) {
|
||||
StatisticsStream stream = this.packets.get(type);
|
||||
|
||||
// Lazily create a stream
|
||||
if (stream == null) {
|
||||
this.packets.put(type, stream = new StatisticsStream());
|
||||
}
|
||||
// Store this observation
|
||||
stream.observe(System.nanoTime() - trackingToken);
|
||||
this.observations.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the total number of observations.
|
||||
*
|
||||
* @return Total number of observations.
|
||||
*/
|
||||
public int getObservations() {
|
||||
return this.observations.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an map (indexed by packet type) of all relevant statistics.
|
||||
*
|
||||
* @return The map of statistics.
|
||||
*/
|
||||
public synchronized Map<PacketType, StatisticsStream> getStatistics() {
|
||||
final Map<PacketType, StatisticsStream> clone = new HashMap<>();
|
||||
|
||||
for (Entry<PacketType, StatisticsStream> entry : this.packets.entrySet()) {
|
||||
clone.put(
|
||||
entry.getKey(),
|
||||
new StatisticsStream(entry.getValue())
|
||||
);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
public enum TimingListenerType {
|
||||
|
||||
ASYNC_INBOUND, ASYNC_OUTBOUND, SYNC_INBOUND, SYNC_OUTBOUND;
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.timing.TimedListenerManager.ListenerType;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class TimingReportGenerator {
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class TimingReport {
|
||||
|
||||
private static final String NEWLINE = System.getProperty("line.separator");
|
||||
private static final String META_STARTED = "Started: %s" + NEWLINE;
|
||||
private static final String META_STOPPED = "Stopped: %s (after %s seconds)" + NEWLINE;
|
||||
|
@ -22,30 +22,39 @@ public class TimingReportGenerator {
|
|||
private static final String LISTENER_HEADER = " TYPE: %s " + NEWLINE;
|
||||
private static final String SEPERATION_LINE = " " + Strings.repeat("-", 139) + NEWLINE;
|
||||
private static final String STATISTICS_HEADER =
|
||||
" Protocol: Name: ID: Count: Min (ms): " +
|
||||
" Protocol: Name: Count: Min (ms): " +
|
||||
"Max (ms): Mean (ms): Std (ms): " + NEWLINE;
|
||||
private static final String STATISTICS_ROW = " %-15s %-29s %-19s %-12d %-15.6f %-15.6f %-15.6f %.6f " + NEWLINE;
|
||||
private static final String STATISTICS_ROW = " %-14s %-29s %-12d %-15.6f %-15.6f %-15.6f %.6f " + NEWLINE;
|
||||
private static final String SUM_MAIN_THREAD = " => Time on main thread: %.6f ms" + NEWLINE;
|
||||
|
||||
public void saveTo(File destination, TimedListenerManager manager) throws IOException {
|
||||
final Date started = manager.getStarted();
|
||||
final Date stopped = manager.getStopped();
|
||||
final long seconds = Math.abs((stopped.getTime() - started.getTime()) / 1000);
|
||||
private final Date startTime;
|
||||
private final Date stopTime;
|
||||
private final ImmutableMap<String, ImmutableMap<TimingListenerType, PluginTimingTracker>> trackerMap;
|
||||
|
||||
try (BufferedWriter writer = Files.newWriter(destination, StandardCharsets.UTF_8)) {
|
||||
public TimingReport(Date startTime, Date stopTime, ImmutableMap<String, ImmutableMap<TimingListenerType, PluginTimingTracker>> trackerMap) {
|
||||
this.startTime = startTime;
|
||||
this.stopTime = stopTime;
|
||||
this.trackerMap = trackerMap;
|
||||
}
|
||||
|
||||
public void saveTo(Path path) throws IOException {
|
||||
final long seconds = Math.abs((stopTime.getTime() - startTime.getTime()) / 1000);
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
|
||||
// Write some timing information
|
||||
writer.write(String.format(META_STARTED, started));
|
||||
writer.write(String.format(META_STOPPED, stopped, seconds));
|
||||
writer.write(String.format(META_STARTED, startTime));
|
||||
writer.write(String.format(META_STOPPED, stopTime, seconds));
|
||||
writer.write(NEWLINE);
|
||||
|
||||
for (String plugin : manager.getTrackedPlugins()) {
|
||||
writer.write(String.format(PLUGIN_HEADER, plugin));
|
||||
for (Map.Entry<String, ImmutableMap<TimingListenerType, PluginTimingTracker>> pluginEntry : trackerMap.entrySet()) {
|
||||
writer.write(String.format(PLUGIN_HEADER, pluginEntry.getKey()));
|
||||
|
||||
for (ListenerType type : ListenerType.values()) {
|
||||
TimedTracker tracker = manager.getTracker(plugin, type);
|
||||
for (Map.Entry<TimingListenerType, PluginTimingTracker> entry : pluginEntry.getValue().entrySet()) {
|
||||
TimingListenerType type = entry.getKey();
|
||||
PluginTimingTracker tracker = entry.getValue();
|
||||
|
||||
// We only care if it has any observations at all
|
||||
if (tracker.getObservations() > 0) {
|
||||
if (tracker.hasReceivedData()) {
|
||||
writer.write(String.format(LISTENER_HEADER, type));
|
||||
|
||||
writer.write(SEPERATION_LINE);
|
||||
|
@ -59,7 +68,7 @@ public class TimingReportGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
private void saveStatistics(Writer destination, TimedTracker tracker, ListenerType type) throws IOException {
|
||||
private void saveStatistics(Writer destination, PluginTimingTracker tracker, TimingListenerType type) throws IOException {
|
||||
Map<PacketType, StatisticsStream> streams = tracker.getStatistics();
|
||||
StatisticsStream sum = new StatisticsStream();
|
||||
int count = 0;
|
||||
|
@ -85,9 +94,9 @@ public class TimingReportGenerator {
|
|||
printStatistic(destination, null, sum);
|
||||
}
|
||||
// These are executed on the main thread
|
||||
if (type == ListenerType.SYNC_SERVER_SIDE) {
|
||||
if (type == TimingListenerType.SYNC_OUTBOUND) {
|
||||
destination.write(String.format(SUM_MAIN_THREAD,
|
||||
toMilli(sum.getCount() * sum.getMean())
|
||||
nanoToMillis(sum.getCount() * sum.getMean())
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -96,25 +105,20 @@ public class TimingReportGenerator {
|
|||
destination.write(String.format(STATISTICS_ROW,
|
||||
key != null ? key.getProtocol() : "SUM",
|
||||
key != null ? key.name() : "-",
|
||||
key != null ? getPacketId(key) : "-",
|
||||
stream.getCount(),
|
||||
toMilli(stream.getMinimum()),
|
||||
toMilli(stream.getMaximum()),
|
||||
toMilli(stream.getMean()),
|
||||
toMilli(stream.getStandardDeviation())
|
||||
nanoToMillis(stream.getMinimum()),
|
||||
nanoToMillis(stream.getMaximum()),
|
||||
nanoToMillis(stream.getMean()),
|
||||
nanoToMillis(stream.getStandardDeviation())
|
||||
));
|
||||
}
|
||||
|
||||
private String getPacketId(PacketType type) {
|
||||
return Strings.padStart(Integer.toString(type.getCurrentId()), 2, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value in nanoseconds to milliseconds.
|
||||
* @param value - the value.
|
||||
* @return The value in milliseconds.
|
||||
*/
|
||||
private double toMilli(double value) {
|
||||
private double nanoToMillis(double value) {
|
||||
return value / 1000000.0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
|
||||
public interface TimingTracker {
|
||||
|
||||
public static final TimingTracker EMPTY = (packetType, runnable) -> runnable.run();
|
||||
|
||||
void track(PacketType packetType, Runnable runnable);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.comphenix.protocol.timing;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.comphenix.protocol.events.PacketListener;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class TimingTrackerManager {
|
||||
|
||||
private final static AtomicBoolean IS_TRACKING = new AtomicBoolean();
|
||||
|
||||
private static volatile Date startTime;
|
||||
private static volatile Date stopTime;
|
||||
|
||||
private static final Map<String, ImmutableMap<TimingListenerType, PluginTimingTracker>> TRACKER_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static boolean startTracking() {
|
||||
if (IS_TRACKING.compareAndSet(false, true)) {
|
||||
startTime = Calendar.getInstance().getTime();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isTracking() {
|
||||
return IS_TRACKING.get();
|
||||
}
|
||||
|
||||
public static boolean stopTracking() {
|
||||
if (IS_TRACKING.compareAndSet(true, false)) {
|
||||
stopTime = Calendar.getInstance().getTime();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TimingReport createReportAndReset() {
|
||||
TimingReport report = new TimingReport(startTime, stopTime, ImmutableMap.copyOf(TRACKER_MAP));
|
||||
TRACKER_MAP.clear();
|
||||
return report;
|
||||
}
|
||||
|
||||
public static TimingTracker get(PacketListener listener, TimingListenerType type) {
|
||||
if (!IS_TRACKING.get()) {
|
||||
return TimingTracker.EMPTY;
|
||||
}
|
||||
|
||||
String plugin = listener.getPlugin().getName();
|
||||
return TRACKER_MAP.computeIfAbsent(plugin, k -> newTrackerMap()).get(type);
|
||||
}
|
||||
|
||||
private static ImmutableMap<TimingListenerType, PluginTimingTracker> newTrackerMap() {
|
||||
ImmutableMap.Builder<TimingListenerType, PluginTimingTracker> builder = ImmutableMap.builder();
|
||||
|
||||
for (TimingListenerType type : TimingListenerType.values()) {
|
||||
builder.put(type, new PluginTimingTracker());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ package com.comphenix.protocol.updater;
|
|||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.utility.Closer;
|
||||
import com.comphenix.protocol.utility.SchedulerUtil;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
|
|
@ -64,11 +64,16 @@ final class CachedPackage {
|
|||
* @param clazz - type of class.
|
||||
*/
|
||||
public void setPackageClass(String className, Class<?> clazz) {
|
||||
if (clazz != null) {
|
||||
this.cache.put(className, Optional.of(clazz));
|
||||
} else {
|
||||
this.cache.remove(className);
|
||||
Optional<Class<?>> previous = cache.get(className);
|
||||
if (previous != null && previous.isPresent()) {
|
||||
throw new IllegalStateException("Tried to redefine class " + className);
|
||||
}
|
||||
|
||||
cache.put(className, Optional.ofNullable(clazz));
|
||||
}
|
||||
|
||||
public void removePackageClass(String className) {
|
||||
cache.remove(className);
|
||||
}
|
||||
|
||||
private Optional<Class<?>> resolveClass(String className) {
|
||||
|
|
|
@ -62,7 +62,7 @@ public final class ChatExtensions {
|
|||
// since 1.19 system chat is extracted into a separate packet
|
||||
packet = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
|
||||
|
||||
packet.getStrings().write(0, component.getJson());
|
||||
packet.getChatComponents().write(0, component);
|
||||
packet.getBooleans().write(0, false);
|
||||
} else {
|
||||
packet = new PacketContainer(PacketType.Play.Server.CHAT);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.comphenix.protocol.utility;
|
||||
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
@ -33,7 +34,13 @@ public final class MinecraftFields {
|
|||
if (NETWORK_ACCESSOR == null) {
|
||||
Class<?> networkClass = MinecraftReflection.getNetworkManagerClass();
|
||||
Class<?> connectionClass = MinecraftReflection.getPlayerConnectionClass();
|
||||
NETWORK_ACCESSOR = Accessors.getFieldAccessor(connectionClass, networkClass, true);
|
||||
NETWORK_ACCESSOR = FuzzyReflection.fromClass(connectionClass, true)
|
||||
.getDeclaredFields(Object.class)
|
||||
.stream()
|
||||
.filter(field -> field.getType().equals(networkClass))
|
||||
.findFirst()
|
||||
.map(Accessors::getFieldAccessor)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unable to find the NetworkManager field in PlayerConnection"));
|
||||
}
|
||||
|
||||
// Retrieve the network manager
|
||||
|
|
|
@ -5,7 +5,9 @@ import java.lang.reflect.InvocationTargetException;
|
|||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
|
@ -15,9 +17,9 @@ import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
|||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.bind.annotation.Origin;
|
||||
|
@ -47,9 +49,29 @@ public final class MinecraftMethods {
|
|||
// Decorated PacketSerializer to identify methods
|
||||
private volatile static ConstructorAccessor decoratedDataSerializerAccessor;
|
||||
|
||||
private volatile static Function<ByteBuf, Object> friendlyBufBufConstructor;
|
||||
|
||||
private MinecraftMethods() {
|
||||
// sealed
|
||||
}
|
||||
|
||||
public static Function<ByteBuf, Object> getFriendlyBufBufConstructor() {
|
||||
if (friendlyBufBufConstructor == null) {
|
||||
Optional<Class<?>> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass();
|
||||
|
||||
if (registryByteBuf.isPresent()) {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(registryByteBuf.get()).getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterDerivedOf(ByteBuf.class)
|
||||
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
|
||||
.build()));
|
||||
friendlyBufBufConstructor = (byteBuf) -> accessor.invoke(byteBuf, MinecraftRegistryAccess.get());
|
||||
} else {
|
||||
ConstructorAccessor accessor = Accessors.getConstructorAccessor(MinecraftReflection.getPacketDataSerializerClass(), ByteBuf.class);
|
||||
friendlyBufBufConstructor = (byteBuf) -> accessor.invoke(byteBuf);
|
||||
}
|
||||
}
|
||||
return friendlyBufBufConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the send packet method in PlayerConnection/NetServerHandler.
|
||||
|
@ -145,6 +167,7 @@ public final class MinecraftMethods {
|
|||
* Retrieve the Packet.read(PacketDataSerializer) method.
|
||||
*
|
||||
* @return The packet read method.
|
||||
* @deprecated no longer works since 1.20.5
|
||||
*/
|
||||
public static MethodAccessor getPacketReadByteBufMethod() {
|
||||
initializePacket();
|
||||
|
@ -157,6 +180,7 @@ public final class MinecraftMethods {
|
|||
* This only exists in version 1.7.2 and above.
|
||||
*
|
||||
* @return The packet write method.
|
||||
* @deprecated no longer works since 1.20.5
|
||||
*/
|
||||
public static MethodAccessor getPacketWriteByteBufMethod() {
|
||||
initializePacket();
|
||||
|
@ -171,7 +195,7 @@ public final class MinecraftMethods {
|
|||
.method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)))
|
||||
.intercept(MethodDelegation.to(new Object() {
|
||||
@RuntimeType
|
||||
public Object delegate(@SuperCall Callable<?> zuper, @Origin Method method) throws Exception {
|
||||
public Object delegate(@SuperCall(nullIfImpossible = true) Callable<?> zuper, @Origin Method method) throws Exception {
|
||||
if (method.getName().contains("read")) {
|
||||
throw new ReadMethodException();
|
||||
}
|
||||
|
@ -195,6 +219,11 @@ public final class MinecraftMethods {
|
|||
* Initialize the two read() and write() methods.
|
||||
*/
|
||||
private static void initializePacket() {
|
||||
// write and read methods are no longer part of the packet interface since 1.20.5
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
throw new IllegalStateException("can't access packet read/write method after 1.20.5");
|
||||
}
|
||||
|
||||
// Initialize the methods
|
||||
if (packetReadByteBuf == null || packetWriteByteBuf == null) {
|
||||
// setups a decorated PacketDataSerializer which we can use to identity read/write methods in the packet class
|
||||
|
@ -203,7 +232,8 @@ public final class MinecraftMethods {
|
|||
}
|
||||
|
||||
// constructs a new decorated serializer
|
||||
Object decoratedSerializer = decoratedDataSerializerAccessor.invoke(Unpooled.EMPTY_BUFFER);
|
||||
Object serializerBacking = decoratedDataSerializerAccessor.invoke(Unpooled.EMPTY_BUFFER);
|
||||
Object decoratedSerializer = decoratedDataSerializerAccessor.invoke(serializerBacking);
|
||||
|
||||
// find all methods which might be the read or write methods
|
||||
List<Method> candidates = FuzzyReflection
|
||||
|
|
|
@ -88,6 +88,8 @@ public final class MinecraftProtocolVersion {
|
|||
map.put(new MinecraftVersion(1, 19, 4), 762);
|
||||
|
||||
map.put(new MinecraftVersion(1, 20, 0), 763);
|
||||
map.put(new MinecraftVersion(1, 20, 1), 763);
|
||||
map.put(new MinecraftVersion(1, 20, 2), 764);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package com.comphenix.protocol.utility;
|
|||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
|
@ -26,6 +27,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -43,6 +45,8 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
|
@ -112,6 +116,8 @@ public final class MinecraftReflection {
|
|||
private static MethodAccessor asCraftMirror = null;
|
||||
private static MethodAccessor isEmpty = null;
|
||||
|
||||
private static Boolean isMojangMapped = null;
|
||||
|
||||
private MinecraftReflection() {
|
||||
// No need to make this constructable.
|
||||
}
|
||||
|
@ -160,7 +166,7 @@ public final class MinecraftReflection {
|
|||
Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
|
||||
if (packageMatcher.matches()) {
|
||||
packageVersion = packageMatcher.group(1);
|
||||
} else {
|
||||
} else if (!MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { // ignore version prefix since it's no longer needed
|
||||
MinecraftVersion version = new MinecraftVersion(craftServer);
|
||||
|
||||
// Just assume R1 - it's probably fine (warn anyway)
|
||||
|
@ -508,7 +514,7 @@ public final class MinecraftReflection {
|
|||
.getMethodByName("getHandle");
|
||||
|
||||
// EntityPlayer is the return type
|
||||
return setMinecraftClass("EntityPlayer", getHandle.getReturnType());
|
||||
return setMinecraftClass("server.level.EntityPlayer", getHandle.getReturnType());
|
||||
} catch (IllegalArgumentException e1) {
|
||||
throw new RuntimeException("Could not find EntityPlayer class.", e1);
|
||||
}
|
||||
|
@ -578,7 +584,7 @@ public final class MinecraftReflection {
|
|||
try {
|
||||
return getMinecraftClass("world.level.World", "world.level.Level", "World");
|
||||
} catch (RuntimeException e) {
|
||||
return setMinecraftClass("World", getWorldServerClass().getSuperclass());
|
||||
return setMinecraftClass("world.level.World", getWorldServerClass().getSuperclass());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -638,7 +644,8 @@ public final class MinecraftReflection {
|
|||
}
|
||||
|
||||
public static boolean isBundleDelimiter(Class<?> packetClass) {
|
||||
return Optionals.Equals(getBundleDelimiterClass(), packetClass);
|
||||
Class<?> bundleDelimiterClass = getBundleDelimiterClass().orElse(null);
|
||||
return bundleDelimiterClass != null && (packetClass.equals(bundleDelimiterClass) || bundleDelimiterClass.isAssignableFrom(packetClass));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> getBundleDelimiterClass() {
|
||||
|
@ -668,6 +675,18 @@ public final class MinecraftReflection {
|
|||
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the component style serializer class.
|
||||
*
|
||||
* @return The serializer class.
|
||||
*/
|
||||
public static Class<?> getStyleSerializerClass() {
|
||||
return getMinecraftClass(
|
||||
"network.chat.Style$Serializer",
|
||||
"network.chat.ChatModifier$ChatModifierSerializer",
|
||||
"ChatModifier$ChatModifierSerializer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the ServerPing class.
|
||||
*
|
||||
|
@ -708,10 +727,10 @@ public final class MinecraftReflection {
|
|||
return getMinecraftClass("server.MinecraftServer", "MinecraftServer");
|
||||
} catch (RuntimeException e) {
|
||||
// Reset cache and try again
|
||||
setMinecraftClass("MinecraftServer", null);
|
||||
resetCacheForNMSClass("server.MinecraftServer");
|
||||
|
||||
useFallbackServer();
|
||||
return getMinecraftClass("MinecraftServer");
|
||||
return getMinecraftClass("server.MinecraftServer");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -743,10 +762,10 @@ public final class MinecraftReflection {
|
|||
return getMinecraftClass("server.players.PlayerList", "PlayerList");
|
||||
} catch (RuntimeException e) {
|
||||
// Reset cache and try again
|
||||
setMinecraftClass("PlayerList", null);
|
||||
resetCacheForNMSClass("server.players.PlayerList");
|
||||
|
||||
useFallbackServer();
|
||||
return getMinecraftClass("PlayerList");
|
||||
return getMinecraftClass("server.players.PlayerList");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -778,7 +797,7 @@ public final class MinecraftReflection {
|
|||
return getMinecraftClass("world.item.ItemStack", "ItemStack");
|
||||
} catch (RuntimeException e) {
|
||||
// Use the handle reference
|
||||
return setMinecraftClass("ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
|
||||
return setMinecraftClass("world.item.ItemStack", FuzzyReflection.fromClass(getCraftItemStackClass(), true)
|
||||
.getFieldByName("handle")
|
||||
.getType());
|
||||
}
|
||||
|
@ -805,6 +824,10 @@ public final class MinecraftReflection {
|
|||
return getNullableNMS("core.particles.ParticleType", "core.particles.SimpleParticleType", "ParticleType");
|
||||
}
|
||||
|
||||
public static Class<?> getParticleClass() {
|
||||
return getNullableNMS("core.particles.Particle");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the WorldType class.
|
||||
*
|
||||
|
@ -1005,6 +1028,78 @@ public final class MinecraftReflection {
|
|||
return getMinecraftClass("world.level.block.entity.TileEntity", "world.level.block.entity.BlockEntity", "TileEntity");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NMS team parameters class.
|
||||
*
|
||||
* @return The team parameters class.
|
||||
*/
|
||||
public static Optional<Class<?>> getTeamParametersClass() {
|
||||
Optional<Class<?>> clazz = getOptionalNMS(
|
||||
"network.protocol.game.ClientboundSetPlayerTeamPacket$Parameters",
|
||||
"network.protocol.game.PacketPlayOutScoreboardTeam$b"
|
||||
);
|
||||
|
||||
if (!clazz.isPresent()) {
|
||||
try {
|
||||
Class<?> clazz1 = PacketType.Play.Server.SCOREBOARD_TEAM.getPacketClass().getClasses()[0];
|
||||
setMinecraftClass("network.protocol.game.ClientboundSetPlayerTeamPacket$Parameters", clazz1);
|
||||
return Optional.of(clazz1);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NMS component style class.
|
||||
*
|
||||
* @return The component style class.
|
||||
*/
|
||||
public static Class<?> getComponentStyleClass() {
|
||||
return getMinecraftClass(
|
||||
"network.chat.Style",
|
||||
"network.chat.ChatModifier",
|
||||
"ChatModifier"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NMS NumberFormat class.
|
||||
*
|
||||
* @return The NumberFormat class.
|
||||
*/
|
||||
public static Optional<Class<?>> getNumberFormatClass() {
|
||||
return getOptionalNMS("network.chat.numbers.NumberFormat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NMS BlankFormat class.
|
||||
*
|
||||
* @return The FixedFormat class.
|
||||
*/
|
||||
public static Optional<Class<?>> getBlankFormatClass() {
|
||||
return getOptionalNMS("network.chat.numbers.BlankFormat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NMS FixedFormat class.
|
||||
*
|
||||
* @return The FixedFormat class.
|
||||
*/
|
||||
public static Optional<Class<?>> getFixedFormatClass() {
|
||||
return getOptionalNMS("network.chat.numbers.FixedFormat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NMS StyledFormat class.
|
||||
*
|
||||
* @return The StyledFormat class.
|
||||
*/
|
||||
public static Optional<Class<?>> getStyledFormatClass() {
|
||||
return getOptionalNMS("network.chat.numbers.StyledFormat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Gson class used by Minecraft.
|
||||
*
|
||||
|
@ -1090,30 +1185,50 @@ public final class MinecraftReflection {
|
|||
public static Class<?> getPlayerInfoDataClass() {
|
||||
try {
|
||||
return getMinecraftClass(
|
||||
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
|
||||
"network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
|
||||
"PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
|
||||
} catch (Exception ex) {
|
||||
// todo: ClientboundPlayerInfoUpdatePacket$b, maybe get this via field type
|
||||
"network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry",
|
||||
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
|
||||
"network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate",
|
||||
"PacketPlayOutPlayerInfo$PlayerInfoData",
|
||||
"PlayerInfoData"
|
||||
);
|
||||
} catch (Exception ignored) {
|
||||
return setMinecraftClass(
|
||||
"network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData",
|
||||
"network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry",
|
||||
PacketType.Play.Server.PLAYER_INFO.getPacketClass().getClasses()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> getOrInferMinecraftClass(String className, Supplier<Class<?>> supplier, String... aliases) {
|
||||
return getOptionalNMS(className, aliases).orElseGet(() -> {
|
||||
Class<?> clazz = supplier.get();
|
||||
return setMinecraftClass(className, clazz);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the entity use action class in 1.17.
|
||||
*
|
||||
* @return The EntityUseAction class
|
||||
*/
|
||||
public static Class<?> getEnumEntityUseActionClass() {
|
||||
Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
|
||||
FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
|
||||
try {
|
||||
return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return fuzzyReflection.getFieldByType("^.*(Action)").getType();
|
||||
}
|
||||
return getOrInferMinecraftClass("ServerboundInteractPacket.Action", () -> {
|
||||
Class<?> packetClass = PacketType.Play.Client.USE_ENTITY.getPacketClass();
|
||||
FuzzyReflection fuzzyReflection = FuzzyReflection.fromClass(packetClass, true);
|
||||
|
||||
Field field = fuzzyReflection.getField(FuzzyFieldContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(Object.class)
|
||||
.build());
|
||||
if (field != null) {
|
||||
return field.getType();
|
||||
}
|
||||
|
||||
try {
|
||||
return fuzzyReflection.getFieldByType("^.*(EnumEntityUseAction)").getType();
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return fuzzyReflection.getFieldByType("^.*(Action)").getType();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1212,10 +1327,11 @@ public final class MinecraftReflection {
|
|||
|
||||
public static MethodAccessor getNonNullListCreateAccessor() {
|
||||
try {
|
||||
Class<?> nonNullListType = MinecraftReflection.getNonNullListClass();
|
||||
Class<?> nonNullListType = getNonNullListClass();
|
||||
Method method = FuzzyReflection.fromClass(nonNullListType).getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(nonNullListType)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.parameterCount(0)
|
||||
.build());
|
||||
return Accessors.getMethodAccessor(method);
|
||||
} catch (Exception ex) {
|
||||
|
@ -1386,7 +1502,7 @@ public final class MinecraftReflection {
|
|||
* @param aliases Potential aliases
|
||||
* @return Optional that may contain the class
|
||||
*/
|
||||
private static Optional<Class<?>> getOptionalNMS(String className, String... aliases) {
|
||||
public static Optional<Class<?>> getOptionalNMS(String className, String... aliases) {
|
||||
if (minecraftPackage == null) {
|
||||
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
||||
}
|
||||
|
@ -1398,16 +1514,22 @@ public final class MinecraftReflection {
|
|||
* Retrieves a nullable NMS (net.minecraft.server) class. We will attempt to
|
||||
* look up the class and its aliases, but will return null if none is found.
|
||||
*
|
||||
* @deprecated - Use getOptionalNMS where possible
|
||||
* @param className NMS class name
|
||||
* @param aliases Potential aliases
|
||||
* @return The class, or null if not found
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class<?> getNullableNMS(String className, String... aliases) {
|
||||
return getOptionalNMS(className, aliases).orElse(null);
|
||||
}
|
||||
|
||||
private static void resetCacheForNMSClass(String className) {
|
||||
if (minecraftPackage == null) {
|
||||
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
|
||||
}
|
||||
|
||||
minecraftPackage.removePackageClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class object for the specific Minecraft class.
|
||||
*
|
||||
|
@ -1462,6 +1584,14 @@ public final class MinecraftReflection {
|
|||
.orElseThrow(() -> new RuntimeException("Failed to find class: " + className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> getOptionalLibraryClass(String className) {
|
||||
if (libraryPackage == null) {
|
||||
libraryPackage = new CachedPackage("", getClassSource());
|
||||
}
|
||||
|
||||
return libraryPackage.getPackageClass(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class object for the specific library class.
|
||||
*
|
||||
|
@ -1495,9 +1625,7 @@ public final class MinecraftReflection {
|
|||
*/
|
||||
public static Object getPacketDataSerializer(Object buffer) {
|
||||
try {
|
||||
// TODO: move this to MinecraftMethods, or at least, cache the constructor accessor
|
||||
Class<?> packetSerializer = getPacketDataSerializerClass();
|
||||
return packetSerializer.getConstructor(getByteBufClass()).newInstance(buffer);
|
||||
return MinecraftMethods.getFriendlyBufBufConstructor().apply((ByteBuf) buffer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Cannot construct packet serializer.", e);
|
||||
}
|
||||
|
@ -1645,9 +1773,13 @@ public final class MinecraftReflection {
|
|||
try {
|
||||
return getMinecraftLibraryClass(classname);
|
||||
} catch (RuntimeException ex) {
|
||||
Class<?> clazz = getMinecraftLibraryClass("org.bukkit.craftbukkit.libs." + classname);
|
||||
setMinecraftLibraryClass(classname, clazz);
|
||||
return clazz;
|
||||
try {
|
||||
Class<?> clazz = getMinecraftLibraryClass("org.bukkit.craftbukkit.libs." + classname);
|
||||
setMinecraftLibraryClass(classname, clazz);
|
||||
return clazz;
|
||||
} catch (Exception ignored) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1656,7 +1788,7 @@ public final class MinecraftReflection {
|
|||
*/
|
||||
private static void useFallbackServer() {
|
||||
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
|
||||
Constructor<?> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
|
||||
Constructor<?> selected = FuzzyReflection.fromClass(getCraftServer())
|
||||
.getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterMatches(getMinecraftObjectMatcher(), 0)
|
||||
.parameterCount(2)
|
||||
|
@ -1712,4 +1844,45 @@ public final class MinecraftReflection {
|
|||
public static Class<?> getHolderClass() {
|
||||
return getMinecraftClass("core.Holder");
|
||||
}
|
||||
|
||||
public static Class<?> getCraftServer() {
|
||||
return getCraftBukkitClass("CraftServer");
|
||||
}
|
||||
|
||||
public static Class<?> getHolderLookupProviderClass() {
|
||||
return getMinecraftClass("core.HolderLookup$a" /* Spigot Mappings */, "core.HolderLookup$Provider" /* Mojang Mappings */);
|
||||
}
|
||||
|
||||
public static Class<?> getRegistryAccessClass() {
|
||||
return getMinecraftClass("core.IRegistryCustom" /* Spigot Mappings */, "core.RegistryAccess" /* Mojang Mappings */);
|
||||
}
|
||||
|
||||
public static Class<?> getProtocolInfoClass() {
|
||||
return getMinecraftClass("network.ProtocolInfo");
|
||||
}
|
||||
|
||||
public static Class<?> getProtocolInfoUnboundClass() {
|
||||
return getMinecraftClass("network.ProtocolInfo$a" /* Spigot Mappings */, "network.ProtocolInfo$Unbound" /* Mojang Mappings */);
|
||||
}
|
||||
|
||||
public static Class<?> getPacketFlowClass() {
|
||||
return getMinecraftClass("network.protocol.EnumProtocolDirection" /* Spigot Mappings */, "network.protocol.PacketFlow" /* Mojang Mappings */);
|
||||
}
|
||||
|
||||
public static Class<?> getStreamCodecClass() {
|
||||
return getMinecraftClass("network.codec.StreamCodec");
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> getRegistryFriendlyByteBufClass() {
|
||||
return getOptionalNMS("network.RegistryFriendlyByteBuf");
|
||||
}
|
||||
|
||||
public static boolean isMojangMapped() {
|
||||
if (isMojangMapped == null) {
|
||||
String nmsWorldName = getWorldServerClass().getName();
|
||||
isMojangMapped = nmsWorldName.contains("ServerLevel");
|
||||
}
|
||||
|
||||
return isMojangMapped;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
|
||||
/**
|
||||
* Static getter for the registry access accessor which is need for most of the methods
|
||||
* since 1.20.5 that access the registry and in form.
|
||||
*/
|
||||
public class MinecraftRegistryAccess {
|
||||
|
||||
private static MethodAccessor GET_SERVER = null;
|
||||
private static MethodAccessor REGISTRY_ACCESS = null;
|
||||
|
||||
// lazy initialized
|
||||
private static Object registryAccess = null;
|
||||
|
||||
static {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
GET_SERVER = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getCraftServer(), false)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnDerivedOf(MinecraftReflection.getMinecraftServerClass())
|
||||
.build()));
|
||||
|
||||
REGISTRY_ACCESS = Accessors.getMethodAccessor(
|
||||
FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), false)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnDerivedOf(MinecraftReflection.getRegistryAccessClass())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the composite global registry access. Equiv. of
|
||||
* <pre>((CraftServer) Bukkit.getServer()).getServer().registryAccess()</pre>
|
||||
*
|
||||
* @return composite registy acesss
|
||||
*/
|
||||
public static Object get() {
|
||||
if (GET_SERVER == null || REGISTRY_ACCESS == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (registryAccess == null) {
|
||||
Object server = GET_SERVER.invoke(Bukkit.getServer());
|
||||
registryAccess = REGISTRY_ACCESS.invoke(server);
|
||||
}
|
||||
|
||||
return registryAccess;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,20 @@ import org.bukkit.Server;
|
|||
* @author Kristian
|
||||
*/
|
||||
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
|
||||
/**
|
||||
* Version 1.20.5 - the cookie and transfer packet update
|
||||
*/
|
||||
public static final MinecraftVersion v1_20_5 = new MinecraftVersion("1.20.5");
|
||||
|
||||
/**
|
||||
* Version 1.20.4 - the decorated pot update
|
||||
*/
|
||||
public static final MinecraftVersion v1_20_4 = new MinecraftVersion("1.20.4");
|
||||
|
||||
/**
|
||||
* Version 1.20.2 - the update that added the configuration protocol phase.
|
||||
*/
|
||||
public static final MinecraftVersion CONFIG_PHASE_PROTOCOL_UPDATE = new MinecraftVersion("1.20.2");
|
||||
/**
|
||||
* Version 1.20 - the trails and tails update
|
||||
*/
|
||||
|
@ -131,7 +144,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
|
|||
/**
|
||||
* The latest release version of minecraft.
|
||||
*/
|
||||
public static final MinecraftVersion LATEST = TRAILS_AND_TAILS;
|
||||
public static final MinecraftVersion LATEST = v1_20_5;
|
||||
|
||||
// used when serializing
|
||||
private static final long serialVersionUID = -8695133558996459770L;
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
package com.comphenix.protocol.utility;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SchedulerUtil {
|
||||
private Object foliaScheduler;
|
||||
private MethodAccessor runAtFixedRate;
|
||||
private MethodAccessor cancelTasks;
|
||||
private MethodAccessor execute;
|
||||
|
||||
private static SchedulerUtil getInstance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
private static class Holder {
|
||||
private static final SchedulerUtil INSTANCE = new SchedulerUtil();
|
||||
}
|
||||
|
||||
private SchedulerUtil() {
|
||||
if (Util.isUsingFolia()) {
|
||||
MethodAccessor getScheduler = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getGlobalRegionScheduler");
|
||||
foliaScheduler = getScheduler.invoke(Bukkit.getServer());
|
||||
|
||||
runAtFixedRate = Accessors.getMethodAccessor(foliaScheduler.getClass(), "runAtFixedRate", Plugin.class,
|
||||
Consumer.class, long.class, long.class);
|
||||
cancelTasks = Accessors.getMethodAccessor(foliaScheduler.getClass(), "cancelTasks", Plugin.class);
|
||||
execute = Accessors.getMethodAccessor(foliaScheduler.getClass(), "execute", Plugin.class, Runnable.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static int scheduleSyncRepeatingTask(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
return getInstance().doScheduleSyncRepeatingTask(plugin, runnable, delay, period);
|
||||
}
|
||||
|
||||
private int doScheduleSyncRepeatingTask(Plugin plugin, Runnable runnable, long delay, long period) {
|
||||
if (Util.isUsingFolia()) {
|
||||
runAtFixedRate.invoke(foliaScheduler, plugin, (Consumer<Object>)(task -> runnable.run()), delay, period);
|
||||
return 1;
|
||||
} else {
|
||||
return plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, runnable, delay, period);
|
||||
}
|
||||
}
|
||||
|
||||
private void doCancelTask(Plugin plugin, int id) {
|
||||
if (Util.isUsingFolia()) {
|
||||
cancelTasks.invoke(foliaScheduler, plugin);
|
||||
} else {
|
||||
plugin.getServer().getScheduler().cancelTask(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelTask(Plugin plugin, int id) {
|
||||
getInstance().doCancelTask(plugin, id);
|
||||
}
|
||||
|
||||
private void doExecute(Plugin plugin, Runnable runnable) {
|
||||
if (Util.isUsingFolia()) {
|
||||
execute.invoke(foliaScheduler, plugin, runnable);
|
||||
} else {
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, runnable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void execute(Plugin plugin, Runnable runnable) {
|
||||
getInstance().doExecute(plugin, runnable);
|
||||
}
|
||||
}
|
|
@ -1,20 +1,24 @@
|
|||
package com.comphenix.protocol.utility;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Base64;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.NettyByteBufAdapter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtType;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
|
@ -93,13 +97,24 @@ public class StreamSerializer {
|
|||
*/
|
||||
public void serializeCompound(DataOutputStream output, NbtCompound compound) {
|
||||
if (WRITE_NBT_METHOD == null) {
|
||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
|
||||
.getMethodByParameters("writeNbtCompound", MinecraftReflection.getNBTCompoundClass()));
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true);
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
FuzzyMethodContract writeNbtContract = FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.parameterExactArray(MinecraftReflection.getNBTBaseClass())
|
||||
.build();
|
||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(fuzzy.getMethod(writeNbtContract));
|
||||
} else {
|
||||
WRITE_NBT_METHOD = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("writeNbtCompound", MinecraftReflection.getNBTCompoundClass()));
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
|
||||
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
|
||||
|
||||
// 1.20.2+ will write the id automatically
|
||||
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
|
||||
}
|
||||
|
||||
// Get the NMS version of the compound
|
||||
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;
|
||||
|
@ -136,9 +151,14 @@ public class StreamSerializer {
|
|||
*/
|
||||
public void serializeString(DataOutputStream output, String text) {
|
||||
if (WRITE_STRING_METHOD == null) {
|
||||
WRITE_STRING_METHOD = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
|
||||
.getMethodByParameters("writeString", String.class));
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), false);
|
||||
WRITE_STRING_METHOD = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactType(String.class)
|
||||
.parameterCount(1)
|
||||
.returnDerivedOf(ByteBuf.class)
|
||||
.requireModifier(Modifier.PUBLIC)
|
||||
.banModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
|
||||
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
|
||||
|
|
|
@ -23,7 +23,6 @@ public final class Util {
|
|||
|
||||
private static final boolean SPIGOT = classExists("org.spigotmc.SpigotConfig");
|
||||
private static final boolean FOLIA = classExists("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
private static Class<?> cachedBundleClass;
|
||||
|
||||
public static boolean classExists(String className) {
|
||||
try {
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
|
||||
/**
|
||||
|
@ -25,7 +28,16 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|||
* Note: The Adventure API Component is not included in CraftBukkit, Bukkit or Spigot and but is present in PaperMC.
|
||||
*/
|
||||
public class AdventureComponentConverter {
|
||||
|
||||
private static final GsonComponentSerializer SERIALIZER;
|
||||
|
||||
static {
|
||||
if (MinecraftVersion.NETHER_UPDATE.atOrAbove()) {
|
||||
SERIALIZER = GsonComponentSerializer.gson();
|
||||
} else {
|
||||
SERIALIZER = GsonComponentSerializer.colorDownsamplingGson();
|
||||
}
|
||||
}
|
||||
|
||||
private AdventureComponentConverter() {
|
||||
}
|
||||
|
||||
|
@ -35,7 +47,34 @@ public class AdventureComponentConverter {
|
|||
* @return Component
|
||||
*/
|
||||
public static Component fromWrapper(WrappedChatComponent wrapper) {
|
||||
return GsonComponentSerializer.gson().deserialize(wrapper.getJson());
|
||||
return SERIALIZER.deserialize(wrapper.getJson());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link WrappedChatComponent} into a {@link Component}
|
||||
* @param wrapper ProtocolLib wrapper
|
||||
* @return Component
|
||||
*/
|
||||
public static Object fromWrapperAsObject(WrappedChatComponent wrapper) {
|
||||
return fromWrapper(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON {@link String} into a Component
|
||||
* @param json Json String
|
||||
* @return Component
|
||||
*/
|
||||
public static Component fromJson(final String json) {
|
||||
return GsonComponentSerializer.gson().deserialize(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON {@link String} into a Component object
|
||||
* @param json Json String
|
||||
* @return Component object
|
||||
*/
|
||||
public static Object fromJsonAsObject(final String json) {
|
||||
return fromJson(json);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,11 +83,29 @@ public class AdventureComponentConverter {
|
|||
* @return ProtocolLib wrapper
|
||||
*/
|
||||
public static WrappedChatComponent fromComponent(Component component) {
|
||||
return WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component));
|
||||
return WrappedChatComponent.fromJson(SERIALIZER.serialize(component));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link WrappedComponentStyle} into a {@link Style}
|
||||
* @param wrapper ProtocolLib wrapper
|
||||
* @return Style
|
||||
*/
|
||||
public static Style fromWrapper(WrappedComponentStyle wrapper) {
|
||||
return SERIALIZER.serializer().fromJson(wrapper.getJson(), Style.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link Style} into a ProtocolLib wrapper
|
||||
* @param style Style
|
||||
* @return ProtocolLib wrapper
|
||||
*/
|
||||
public static WrappedComponentStyle fromStyle(Style style) {
|
||||
return WrappedComponentStyle.fromJson((JsonObject) SERIALIZER.serializer().toJsonTree(style));
|
||||
}
|
||||
|
||||
public static Class<?> getComponentClass() {
|
||||
return Component.class;
|
||||
return Component.class;
|
||||
}
|
||||
|
||||
public static Component clone(Object component) {
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.wrappers.Either.Left;
|
||||
import com.comphenix.protocol.wrappers.Either.Right;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
@ -29,6 +29,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -39,6 +40,7 @@ import com.comphenix.protocol.PacketType;
|
|||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.injector.PacketConstructor;
|
||||
import com.comphenix.protocol.injector.PacketConstructor.Unwrapper;
|
||||
|
@ -54,8 +56,11 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
|||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.Either.Left;
|
||||
import com.comphenix.protocol.wrappers.Either.Right;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Dimension;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.FauxEnumConverter;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtBase;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
|
||||
|
@ -63,7 +68,6 @@ import com.google.common.base.Objects;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
|
@ -647,6 +651,14 @@ public class BukkitConverters {
|
|||
return ignoreNull(handle(WrappedLevelChunkData.LightData::getHandle, WrappedLevelChunkData.LightData::new, WrappedLevelChunkData.LightData.class));
|
||||
}
|
||||
|
||||
public static EquivalentConverter<WrappedTeamParameters> getWrappedTeamParametersConverter() {
|
||||
return ignoreNull(handle(WrappedTeamParameters::getHandle, WrappedTeamParameters::new, WrappedTeamParameters.class));
|
||||
}
|
||||
|
||||
public static EquivalentConverter<WrappedNumberFormat> getWrappedNumberFormatConverter() {
|
||||
return ignoreNull(handle(WrappedNumberFormat::getHandle, WrappedNumberFormat::fromHandle, WrappedNumberFormat.class));
|
||||
}
|
||||
|
||||
public static EquivalentConverter<PacketContainer> getPacketContainerConverter() {
|
||||
return ignoreNull(handle(PacketContainer::getHandle, PacketContainer::fromPacket, PacketContainer.class));
|
||||
}
|
||||
|
@ -1115,36 +1127,30 @@ public class BukkitConverters {
|
|||
static MethodAccessor getSoundEffect = null;
|
||||
static FieldAccessor soundKey = null;
|
||||
|
||||
static MethodAccessor getSoundEffectByKey = null;
|
||||
static MethodAccessor getSoundEffectBySound = null;
|
||||
static MethodAccessor getSoundByEffect = null;
|
||||
static MethodAccessor bukkitToMinecraft = null;
|
||||
static MethodAccessor minecraftToBukkit = null;
|
||||
|
||||
static Map<String, Sound> soundIndex = null;
|
||||
|
||||
public static EquivalentConverter<Sound> getSoundConverter() {
|
||||
// Try to create sound converter for new versions greater 1.16.4
|
||||
if (MinecraftVersion.NETHER_UPDATE_4.atOrAbove()) {
|
||||
if (getSoundEffectByKey == null || getSoundEffectBySound == null || getSoundByEffect == null) {
|
||||
if (bukkitToMinecraft == null && minecraftToBukkit == null) {
|
||||
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
|
||||
Class<?> soundEvent = MinecraftReflection.getSoundEffectClass();
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, false);
|
||||
|
||||
getSoundEffectByKey = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
|
||||
"getSoundEffect",
|
||||
MinecraftReflection.getSoundEffectClass(),
|
||||
String.class
|
||||
));
|
||||
bukkitToMinecraft = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(soundEvent)
|
||||
.parameterExactArray(Sound.class)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
|
||||
getSoundEffectBySound = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
|
||||
"getSoundEffect",
|
||||
MinecraftReflection.getSoundEffectClass(),
|
||||
Sound.class
|
||||
));
|
||||
|
||||
getSoundByEffect = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
|
||||
"getBukkit",
|
||||
Sound.class,
|
||||
MinecraftReflection.getSoundEffectClass()
|
||||
));
|
||||
minecraftToBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(Sound.class)
|
||||
.parameterExactArray(soundEvent)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
|
||||
return ignoreNull(new EquivalentConverter<Sound>() {
|
||||
|
@ -1156,16 +1162,17 @@ public class BukkitConverters {
|
|||
|
||||
@Override
|
||||
public Object getGeneric(Sound specific) {
|
||||
return getSoundEffectBySound.invoke(null, specific);
|
||||
return bukkitToMinecraft.invoke(null, specific);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sound getSpecific(Object generic) {
|
||||
try {
|
||||
return (Sound) getSoundByEffect.invoke(null, generic);
|
||||
return (Sound) minecraftToBukkit.invoke(null, generic);
|
||||
} catch (IllegalStateException ex) {
|
||||
if (ex.getCause() instanceof NullPointerException) {
|
||||
if (ex.getCause() instanceof NullPointerException || ex.getCause() instanceof NoSuchElementException) {
|
||||
// "null" sounds cause NPEs inside getSoundByEffect
|
||||
// "null" sounds can also trigger a NSE in newer versions because of Optional.get() usages
|
||||
return null;
|
||||
}
|
||||
throw ex;
|
||||
|
@ -1361,34 +1368,62 @@ public class BukkitConverters {
|
|||
|
||||
@Override
|
||||
public Object getGeneric(PotionEffectType specific) {
|
||||
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
|
||||
if (getMobEffect == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
|
||||
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(int.class)
|
||||
.returnTypeExact(clazz)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
if (getMobEffect == null) {
|
||||
Class<?> potionEffectTypeClass = MinecraftReflection.getCraftBukkitClass("potion.CraftPotionEffectType");
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(potionEffectTypeClass, false);
|
||||
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(PotionEffectType.class)
|
||||
.returnTypeExact(MinecraftReflection.getMobEffectListClass())
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
|
||||
int id = specific.getId();
|
||||
return getMobEffect.invoke(null, id);
|
||||
return getMobEffect.invoke(null, specific);
|
||||
} else {
|
||||
if (getMobEffect == null) {
|
||||
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
|
||||
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(int.class)
|
||||
.returnTypeExact(clazz)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
|
||||
int id = specific.getId();
|
||||
return getMobEffect.invoke(null, id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PotionEffectType getSpecific(Object generic) {
|
||||
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
|
||||
if (getMobEffectId == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
|
||||
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(clazz)
|
||||
.returnTypeExact(int.class)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
if (getMobEffectId == null) {
|
||||
Class<?> potionEffectTypeClass = MinecraftReflection.getCraftBukkitClass("potion.CraftPotionEffectType");
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(potionEffectTypeClass, false);
|
||||
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(MinecraftReflection.getMobEffectListClass())
|
||||
.returnTypeExact(PotionEffectType.class)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
|
||||
int id = (int) getMobEffectId.invoke(null, generic);
|
||||
return PotionEffectType.getById(id);
|
||||
return (PotionEffectType) getMobEffectId.invoke(null, generic);
|
||||
} else {
|
||||
if (getMobEffectId == null) {
|
||||
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
|
||||
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(clazz)
|
||||
.returnTypeExact(int.class)
|
||||
.requireModifier(Modifier.STATIC)
|
||||
.build()));
|
||||
}
|
||||
|
||||
int id = (int) getMobEffectId.invoke(null, generic);
|
||||
return PotionEffectType.getById(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ import java.util.Optional;
|
|||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
/**
|
||||
* Utility class for converters
|
||||
* @author dmulloy2
|
||||
|
@ -168,6 +170,7 @@ public class Converters {
|
|||
|
||||
@Override
|
||||
public Optional<T> getSpecific(Object generic) {
|
||||
if (generic == null) return Optional.empty();
|
||||
Optional<Object> optional = (Optional<Object>) generic;
|
||||
return optional.map(converter::getSpecific);
|
||||
}
|
||||
|
@ -286,6 +289,8 @@ public class Converters {
|
|||
|
||||
@Override
|
||||
public T getSpecific(Object generic) {
|
||||
Preconditions.checkNotNull(generic, "generic cannot be null");
|
||||
|
||||
if (holderGetValue == null) {
|
||||
Class<?> holderClass = MinecraftReflection.getHolderClass();
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holderClass, false);
|
||||
|
@ -297,6 +302,10 @@ public class Converters {
|
|||
.build()));
|
||||
}
|
||||
|
||||
if (holderGetValue == null) {
|
||||
throw new IllegalStateException("Unable to find Holder#value method.");
|
||||
}
|
||||
|
||||
Object value = holderGetValue.invoke(generic);
|
||||
return converter.getSpecific(value);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.ByteBuddyGenerated;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Objects;
|
||||
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.MethodCall;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.bind.annotation.Argument;
|
||||
import net.bytebuddy.implementation.bind.annotation.FieldValue;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
|
||||
/**
|
||||
* A wrapper for the CustomPacketPayload class in 1.20.2. Due to the nature of the class, not all types are supported
|
||||
* by default. Constructing a new wrapper instance will give out a handle to a completely new implemented type, that
|
||||
* allows to set a key and some kind of data of any choice.
|
||||
* <p>
|
||||
* Note that constructing this class from a generic handle is only possible for the spigot-specific UnknownPayload type.
|
||||
* All other payloads should be accessed via a structure modifier directly.
|
||||
*
|
||||
* @author Pasqual Koschmieder
|
||||
*/
|
||||
public final class CustomPacketPayloadWrapper {
|
||||
|
||||
private static final Class<?> MINECRAFT_KEY_CLASS;
|
||||
private static final Class<?> CUSTOM_PACKET_PAYLOAD_CLASS;
|
||||
|
||||
private static final ConstructorAccessor PAYLOAD_WRAPPER_CONSTRUCTOR;
|
||||
|
||||
private static final MethodAccessor GET_ID_PAYLOAD_METHOD;
|
||||
private static final MethodAccessor SERIALIZE_PAYLOAD_METHOD;
|
||||
|
||||
private static final EquivalentConverter<CustomPacketPayloadWrapper> CONVERTER;
|
||||
|
||||
static {
|
||||
try {
|
||||
MINECRAFT_KEY_CLASS = MinecraftReflection.getMinecraftKeyClass();
|
||||
CUSTOM_PACKET_PAYLOAD_CLASS = MinecraftReflection.getMinecraftClass("network.protocol.common.custom.CustomPacketPayload");
|
||||
|
||||
Method getPayloadId = FuzzyReflection.fromClass(CUSTOM_PACKET_PAYLOAD_CLASS).getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeExact(MINECRAFT_KEY_CLASS)
|
||||
.parameterCount(0)
|
||||
.build());
|
||||
GET_ID_PAYLOAD_METHOD = Accessors.getMethodAccessor(getPayloadId);
|
||||
|
||||
Method serializePayloadData = FuzzyReflection.fromClass(CUSTOM_PACKET_PAYLOAD_CLASS).getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.parameterDerivedOf(ByteBuf.class, 0)
|
||||
.build());
|
||||
SERIALIZE_PAYLOAD_METHOD = Accessors.getMethodAccessor(serializePayloadData);
|
||||
|
||||
Constructor<?> payloadWrapperConstructor = makePayloadWrapper();
|
||||
PAYLOAD_WRAPPER_CONSTRUCTOR = Accessors.getConstructorAccessor(payloadWrapperConstructor);
|
||||
|
||||
CONVERTER = new EquivalentConverter<CustomPacketPayloadWrapper>() {
|
||||
@Override
|
||||
public Object getGeneric(CustomPacketPayloadWrapper specific) {
|
||||
return specific.newHandle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomPacketPayloadWrapper getSpecific(Object generic) {
|
||||
return fromUnknownPayload(generic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<CustomPacketPayloadWrapper> getSpecificType() {
|
||||
return CustomPacketPayloadWrapper.class;
|
||||
}
|
||||
};
|
||||
} catch (Exception exception) {
|
||||
throw new ExceptionInInitializerError(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor<?> makePayloadWrapper() throws Exception {
|
||||
return new ByteBuddy()
|
||||
.subclass(Object.class)
|
||||
.name("com.comphenix.protocol.wrappers.ProtocolLibCustomPacketPayload")
|
||||
.implement(CUSTOM_PACKET_PAYLOAD_CLASS, ByteBuddyGenerated.class)
|
||||
.defineField("payload", byte[].class, Modifier.PRIVATE | Modifier.FINAL)
|
||||
.defineField("id", MinecraftReflection.getMinecraftKeyClass(), Modifier.PRIVATE | Modifier.FINAL)
|
||||
.defineConstructor(Modifier.PUBLIC)
|
||||
.withParameters(MinecraftReflection.getMinecraftKeyClass(), byte[].class)
|
||||
.intercept(MethodCall.invoke(Object.class.getConstructor())
|
||||
.andThen(FieldAccessor.ofField("id").setsArgumentAt(0))
|
||||
.andThen(FieldAccessor.ofField("payload").setsArgumentAt(1)))
|
||||
.method(ElementMatchers.returns(MinecraftReflection.getMinecraftKeyClass()).and(ElementMatchers.takesNoArguments()))
|
||||
.intercept(FieldAccessor.ofField("id"))
|
||||
.method(ElementMatchers.returns(void.class).and(ElementMatchers.takesArguments(MinecraftReflection.getPacketDataSerializerClass())))
|
||||
.intercept(MethodDelegation.to(CustomPacketPayloadInterceptionHandler.class))
|
||||
.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
|
||||
.getLoaded()
|
||||
.getConstructor(MinecraftReflection.getMinecraftKeyClass(), byte[].class);
|
||||
}
|
||||
|
||||
// ====== api methods ======
|
||||
|
||||
/**
|
||||
* The wrapped payload in the message.
|
||||
*/
|
||||
private final byte[] payload;
|
||||
/**
|
||||
* The wrapped key of the message.
|
||||
*/
|
||||
private final MinecraftKey id;
|
||||
/**
|
||||
* The generic id of the message, lazy initialized when needed.
|
||||
*/
|
||||
private Object genericId;
|
||||
|
||||
/**
|
||||
* Constructs a new payload wrapper instance using the given message payload and id.
|
||||
*
|
||||
* @param payload the payload of the message.
|
||||
* @param id the id of the message.
|
||||
* @throws NullPointerException if the given payload or id is null.
|
||||
*/
|
||||
public CustomPacketPayloadWrapper(byte[] payload, MinecraftKey id) {
|
||||
this.payload = Objects.requireNonNull(payload, "payload");
|
||||
this.id = Objects.requireNonNull(id, "id");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CustomPacketPayload class that is backing this wrapper (available since Minecraft 1.20.2).
|
||||
*
|
||||
* @return the CustomPacketPayload class.
|
||||
*/
|
||||
public static Class<?> getCustomPacketPayloadClass() {
|
||||
return CUSTOM_PACKET_PAYLOAD_CLASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a converter to convert this wrapper to a generic handle and an UnknownPayload type to this wrapper.
|
||||
*
|
||||
* @return a converter for this wrapper.
|
||||
*/
|
||||
public static EquivalentConverter<CustomPacketPayloadWrapper> getConverter() {
|
||||
return CONVERTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs this wrapper from any CustomPayload type.
|
||||
* <p>
|
||||
* Note: the buffer of the given payload (if any) will <strong>NOT</strong> be released by this operation. Make sure
|
||||
* to release the buffer manually if you discard the packet to prevent memory leaks.
|
||||
*
|
||||
* @param payload the instance of the custom payload to convert to this wrapper.
|
||||
* @return a wrapper holding the minecraft key and payload of the given custom payload instance.
|
||||
*/
|
||||
public static CustomPacketPayloadWrapper fromUnknownPayload(Object payload) {
|
||||
Object messageId = GET_ID_PAYLOAD_METHOD.invoke(payload);
|
||||
MinecraftKey id = MinecraftKey.getConverter().getSpecific(messageId);
|
||||
|
||||
// we read and retain the underlying buffer in case the class uses a buffer to store the data
|
||||
// this way, when passing the packet to further handling, the buffer is not released and can be re-used
|
||||
StructureModifier<Object> modifier = new StructureModifier<>(payload.getClass()).withTarget(payload);
|
||||
byte[] messagePayload = modifier.withType(ByteBuf.class).optionRead(0)
|
||||
.map(buffer -> {
|
||||
ByteBuf buf = (ByteBuf) buffer;
|
||||
byte[] data = StreamSerializer.getDefault().getBytesAndRelease(buf.markReaderIndex().retain());
|
||||
buf.resetReaderIndex();
|
||||
return data;
|
||||
})
|
||||
.orElseGet(() -> {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buffer);
|
||||
SERIALIZE_PAYLOAD_METHOD.invoke(payload, serializer);
|
||||
return StreamSerializer.getDefault().getBytesAndRelease(buffer);
|
||||
});
|
||||
|
||||
return new CustomPacketPayloadWrapper(messagePayload, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generic id of the wrapped message id.
|
||||
*
|
||||
* @return the generic key id.
|
||||
*/
|
||||
private Object getGenericId() {
|
||||
if (this.genericId == null) {
|
||||
this.genericId = MinecraftKey.getConverter().getGeneric(this.id);
|
||||
}
|
||||
return this.genericId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message payload of this wrapper. Changes made to the returned array will be reflected into this wrapper.
|
||||
*
|
||||
* @return the message payload.
|
||||
*/
|
||||
public byte[] getPayload() {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message id of this wrapper.
|
||||
*
|
||||
* @return the message id of this wrapper.
|
||||
*/
|
||||
public MinecraftKey getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <strong>NEW</strong> handle instance of a payload wrapper to use in a CustomPayload packet.
|
||||
*
|
||||
* @return a new payload wrapper instance using the provided message id and payload.
|
||||
*/
|
||||
public Object newHandle() {
|
||||
return PAYLOAD_WRAPPER_CONSTRUCTOR.invoke(this.getGenericId(), this.payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles interception of the ProtocolLib specific CustomPayloadWrapper implementation. For internal use only.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
static final class CustomPacketPayloadInterceptionHandler {
|
||||
public static void intercept(@FieldValue("payload") byte[] payload, @Argument(0) Object packetBuffer) {
|
||||
((ByteBuf) packetBuffer).writeBytes(payload);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,7 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.ExactReflection;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -22,6 +11,22 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.ExactReflection;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
/**
|
||||
* Represents a generic enum converter.
|
||||
* @author Kristian
|
||||
|
@ -121,7 +126,11 @@ public abstract class EnumWrappers {
|
|||
SUCCESSFULLY_LOADED,
|
||||
DECLINED,
|
||||
FAILED_DOWNLOAD,
|
||||
ACCEPTED
|
||||
ACCEPTED,
|
||||
DOWNLOADED,
|
||||
INVALID_URL,
|
||||
FAILED_RELOAD,
|
||||
DISCARDED;
|
||||
}
|
||||
|
||||
public enum PlayerInfoAction {
|
||||
|
@ -354,7 +363,8 @@ public abstract class EnumWrappers {
|
|||
FEET,
|
||||
LEGS,
|
||||
CHEST,
|
||||
HEAD
|
||||
HEAD,
|
||||
BODY
|
||||
}
|
||||
|
||||
public enum Hand {
|
||||
|
@ -404,7 +414,10 @@ public abstract class EnumWrappers {
|
|||
ROARING,
|
||||
SNIFFING,
|
||||
EMERGING,
|
||||
DIGGING;
|
||||
DIGGING,
|
||||
SLIDING,
|
||||
SHOOTING,
|
||||
INHALING;
|
||||
|
||||
private final static EquivalentConverter<EntityPose> POSE_CONVERTER = EnumWrappers.getEntityPoseConverter();
|
||||
|
||||
|
@ -453,6 +466,163 @@ public abstract class EnumWrappers {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.20.2
|
||||
*/
|
||||
public enum DisplaySlot {
|
||||
LIST,
|
||||
SIDEBAR,
|
||||
BELOW_NAME,
|
||||
TEAM_BLACK,
|
||||
TEAM_DARK_BLUE,
|
||||
TEAM_DARK_GREEN,
|
||||
TEAM_DARK_AQUA,
|
||||
TEAM_DARK_RED,
|
||||
TEAM_DARK_PURPLE,
|
||||
TEAM_GOLD,
|
||||
TEAM_GRAY,
|
||||
TEAM_DARK_GRAY,
|
||||
TEAM_BLUE,
|
||||
TEAM_GREEN,
|
||||
TEAM_AQUA,
|
||||
TEAM_RED,
|
||||
TEAM_LIGHT_PURPLE,
|
||||
TEAM_YELLOW,
|
||||
TEAM_WHITE;
|
||||
}
|
||||
|
||||
public enum RenderType {
|
||||
INTEGER,
|
||||
HEARTS
|
||||
}
|
||||
|
||||
public enum ChatFormatting {
|
||||
BLACK,
|
||||
DARK_BLUE,
|
||||
DARK_GREEN,
|
||||
DARK_AQUA,
|
||||
DARK_RED,
|
||||
DARK_PURPLE,
|
||||
GOLD,
|
||||
GRAY,
|
||||
DARK_GRAY,
|
||||
BLUE,
|
||||
GREEN,
|
||||
AQUA,
|
||||
RED,
|
||||
LIGHT_PURPLE,
|
||||
YELLOW,
|
||||
WHITE,
|
||||
OBFUSCATED,
|
||||
BOLD,
|
||||
STRIKETHROUGH,
|
||||
UNDERLINE,
|
||||
ITALIC,
|
||||
RESET;
|
||||
|
||||
public ChatColor toBukkit() {
|
||||
switch (this){
|
||||
case BLACK:
|
||||
return ChatColor.BLACK;
|
||||
case DARK_BLUE:
|
||||
return ChatColor.DARK_BLUE;
|
||||
case DARK_GREEN:
|
||||
return ChatColor.DARK_GREEN;
|
||||
case DARK_AQUA:
|
||||
return ChatColor.DARK_AQUA;
|
||||
case DARK_RED:
|
||||
return ChatColor.DARK_RED;
|
||||
case DARK_PURPLE:
|
||||
return ChatColor.DARK_PURPLE;
|
||||
case GOLD:
|
||||
return ChatColor.GOLD;
|
||||
case GRAY:
|
||||
return ChatColor.GRAY;
|
||||
case DARK_GRAY:
|
||||
return ChatColor.DARK_GRAY;
|
||||
case BLUE:
|
||||
return ChatColor.BLUE;
|
||||
case GREEN:
|
||||
return ChatColor.GREEN;
|
||||
case AQUA:
|
||||
return ChatColor.AQUA;
|
||||
case RED:
|
||||
return ChatColor.RED;
|
||||
case LIGHT_PURPLE:
|
||||
return ChatColor.LIGHT_PURPLE;
|
||||
case YELLOW:
|
||||
return ChatColor.YELLOW;
|
||||
case WHITE:
|
||||
return ChatColor.WHITE;
|
||||
case OBFUSCATED:
|
||||
return ChatColor.MAGIC;
|
||||
case BOLD:
|
||||
return ChatColor.BOLD;
|
||||
case STRIKETHROUGH:
|
||||
return ChatColor.STRIKETHROUGH;
|
||||
case UNDERLINE:
|
||||
return ChatColor.UNDERLINE;
|
||||
case ITALIC:
|
||||
return ChatColor.ITALIC;
|
||||
case RESET:
|
||||
return ChatColor.RESET;
|
||||
default:
|
||||
throw new IllegalStateException("Unimplemented Bukkit equivalent for " + name());
|
||||
}
|
||||
}
|
||||
|
||||
public static ChatFormatting fromBukkit(ChatColor color) {
|
||||
switch (color){
|
||||
case BLACK:
|
||||
return ChatFormatting.BLACK;
|
||||
case DARK_BLUE:
|
||||
return ChatFormatting.DARK_BLUE;
|
||||
case DARK_GREEN:
|
||||
return ChatFormatting.DARK_GREEN;
|
||||
case DARK_AQUA:
|
||||
return ChatFormatting.DARK_AQUA;
|
||||
case DARK_RED:
|
||||
return ChatFormatting.DARK_RED;
|
||||
case DARK_PURPLE:
|
||||
return ChatFormatting.DARK_PURPLE;
|
||||
case GOLD:
|
||||
return ChatFormatting.GOLD;
|
||||
case GRAY:
|
||||
return ChatFormatting.GRAY;
|
||||
case DARK_GRAY:
|
||||
return ChatFormatting.DARK_GRAY;
|
||||
case BLUE:
|
||||
return ChatFormatting.BLUE;
|
||||
case GREEN:
|
||||
return ChatFormatting.GREEN;
|
||||
case AQUA:
|
||||
return ChatFormatting.AQUA;
|
||||
case RED:
|
||||
return ChatFormatting.RED;
|
||||
case LIGHT_PURPLE:
|
||||
return ChatFormatting.LIGHT_PURPLE;
|
||||
case YELLOW:
|
||||
return ChatFormatting.YELLOW;
|
||||
case WHITE:
|
||||
return ChatFormatting.WHITE;
|
||||
case MAGIC:
|
||||
return ChatFormatting.OBFUSCATED;
|
||||
case BOLD:
|
||||
return ChatFormatting.BOLD;
|
||||
case STRIKETHROUGH:
|
||||
return ChatFormatting.STRIKETHROUGH;
|
||||
case UNDERLINE:
|
||||
return ChatFormatting.UNDERLINE;
|
||||
case ITALIC:
|
||||
return ChatFormatting.ITALIC;
|
||||
case RESET:
|
||||
return ChatFormatting.RESET;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown ChatColor " + color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> PROTOCOL_CLASS = null;
|
||||
private static Class<?> CLIENT_COMMAND_CLASS = null;
|
||||
private static Class<?> CHAT_VISIBILITY_CLASS = null;
|
||||
|
@ -474,6 +644,9 @@ public abstract class EnumWrappers {
|
|||
private static Class<?> DIRECTION_CLASS = null;
|
||||
private static Class<?> CHAT_TYPE_CLASS = null;
|
||||
private static Class<?> ENTITY_POSE_CLASS = null;
|
||||
private static Class<?> DISPLAY_SLOT_CLASS = null;
|
||||
private static Class<?> RENDER_TYPE_CLASS = null;
|
||||
private static Class<?> CHAT_FORMATTING_CLASS = null;
|
||||
|
||||
private static boolean INITIALIZED = false;
|
||||
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = new HashMap<>();
|
||||
|
@ -484,8 +657,6 @@ public abstract class EnumWrappers {
|
|||
* Initialize the wrappers, if we haven't already.
|
||||
*/
|
||||
private static void initialize() {
|
||||
|
||||
|
||||
if (INITIALIZED)
|
||||
return;
|
||||
|
||||
|
@ -493,7 +664,12 @@ public abstract class EnumWrappers {
|
|||
|
||||
PROTOCOL_CLASS = getEnum(PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass(), 0);
|
||||
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
|
||||
CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
|
||||
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
CHAT_VISIBILITY_CLASS = MinecraftReflection.getMinecraftClass("world.entity.player.EnumChatVisibility", "world.entity.player.ChatVisibility", "world.entity.player.ChatVisiblity"); // Some versions have a typo
|
||||
} else {
|
||||
CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
try {
|
||||
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.SERVER_DIFFICULTY.getPacketClass(), 0);
|
||||
|
@ -501,7 +677,12 @@ public abstract class EnumWrappers {
|
|||
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 1);
|
||||
}
|
||||
|
||||
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
GAMEMODE_CLASS = getEnum(MinecraftReflection.getPlayerInfoDataClass(), 0);
|
||||
} else {
|
||||
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
RESOURCE_PACK_STATUS_CLASS = getEnum(PacketType.Play.Client.RESOURCE_PACK_STATUS.getPacketClass(), 0);
|
||||
TITLE_ACTION_CLASS = getEnum(PacketType.Play.Server.TITLE.getPacketClass(), 0);
|
||||
WORLD_BORDER_ACTION_CLASS = getEnum(PacketType.Play.Server.WORLD_BORDER.getPacketClass(), 0);
|
||||
|
@ -533,8 +714,14 @@ public abstract class EnumWrappers {
|
|||
// In 1.17 the hand and use action class is no longer a field in the packet
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand", "world.InteractionHand");
|
||||
// class is named 'b' in the packet but class order differs in spigot and paper so we can only use the first method's return type (safest way)
|
||||
ENTITY_USE_ACTION_CLASS = MinecraftReflection.getEnumEntityUseActionClass().getMethods()[0].getReturnType();
|
||||
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
|
||||
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterCount(0)
|
||||
.returnTypeMatches(FuzzyMatchers.except(Void.class))
|
||||
.build());
|
||||
|
||||
ENTITY_USE_ACTION_CLASS = getType.getReturnType();
|
||||
} else {
|
||||
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
|
||||
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
|
||||
|
@ -549,6 +736,13 @@ public abstract class EnumWrappers {
|
|||
|
||||
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
|
||||
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("world.entity.EntityPose", "world.entity.Pose", "EntityPose");
|
||||
DISPLAY_SLOT_CLASS = MinecraftReflection.getNullableNMS("world.scores.DisplaySlot");
|
||||
|
||||
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS(
|
||||
"world.scores.criteria.ObjectiveCriteria$RenderType",
|
||||
"world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay",
|
||||
"IScoreboardCriteria$EnumScoreboardHealthDisplay");
|
||||
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("ChatFormatting", "EnumChatFormat");
|
||||
|
||||
associate(PROTOCOL_CLASS, Protocol.class, getProtocolConverter());
|
||||
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
|
||||
|
@ -570,6 +764,9 @@ public abstract class EnumWrappers {
|
|||
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
|
||||
associate(HAND_CLASS, Hand.class, getHandConverter());
|
||||
associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
|
||||
associate(DISPLAY_SLOT_CLASS, DisplaySlot.class, getDisplaySlotConverter());
|
||||
associate(RENDER_TYPE_CLASS, RenderType.class, getRenderTypeConverter());
|
||||
associate(CHAT_FORMATTING_CLASS, ChatFormatting.class, getChatFormattingConverter());
|
||||
|
||||
if (ENTITY_POSE_CLASS != null) {
|
||||
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
|
||||
|
@ -721,6 +918,21 @@ public abstract class EnumWrappers {
|
|||
return ENTITY_POSE_CLASS;
|
||||
}
|
||||
|
||||
public static Class<?> getDisplaySlotClass() {
|
||||
initialize();
|
||||
return DISPLAY_SLOT_CLASS;
|
||||
}
|
||||
|
||||
public static Class<?> getRenderTypeClass() {
|
||||
initialize();
|
||||
return RENDER_TYPE_CLASS;
|
||||
}
|
||||
|
||||
public static Class<?> getChatFormattingClass() {
|
||||
initialize();
|
||||
return CHAT_FORMATTING_CLASS;
|
||||
}
|
||||
|
||||
// Get the converters
|
||||
public static EquivalentConverter<Protocol> getProtocolConverter() {
|
||||
return new EnumConverter<>(getProtocolClass(), Protocol.class);
|
||||
|
@ -801,7 +1013,19 @@ public abstract class EnumWrappers {
|
|||
public static EquivalentConverter<ChatType> getChatTypeConverter() {
|
||||
return new EnumConverter<>(getChatTypeClass(), ChatType.class);
|
||||
}
|
||||
|
||||
|
||||
public static EquivalentConverter<DisplaySlot> getDisplaySlotConverter() {
|
||||
return new EnumConverter<>(getDisplaySlotClass(), DisplaySlot.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<RenderType> getRenderTypeConverter() {
|
||||
return new EnumConverter<>(getRenderTypeClass(), RenderType.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ChatFormatting> getChatFormattingConverter() {
|
||||
return new EnumConverter<>(getChatFormattingClass(), ChatFormatting.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.13+
|
||||
* @return {@link EnumConverter} or null (if bellow 1.13 / nms EnumPose class cannot be found)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
public interface IDataWatcher extends Iterable<WrappedWatchableObject> {
|
||||
|
||||
Map<Integer, WrappedWatchableObject> asMap();
|
||||
|
||||
Set<Integer> getIndexes();
|
||||
|
||||
List<WrappedWatchableObject> getWatchableObjects();
|
||||
|
||||
int size();
|
||||
|
||||
WrappedWatchableObject getWatchableObject(int index);
|
||||
|
||||
WrappedWatchableObject remove(int index);
|
||||
|
||||
boolean hasIndex(int index);
|
||||
|
||||
void clear();
|
||||
|
||||
Object getObject(int index);
|
||||
|
||||
Object getObject(WrappedDataWatcherObject object);
|
||||
|
||||
void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update);
|
||||
|
||||
void setObject(WrappedDataWatcherObject object, Object value, boolean update);
|
||||
|
||||
IDataWatcher deepClone();
|
||||
|
||||
Object getHandle();
|
||||
|
||||
@Deprecated
|
||||
Entity getEntity();
|
||||
|
||||
@Deprecated
|
||||
void setEntity(Entity entity);
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class InMemoryDataWatcher implements IDataWatcher {
|
||||
private Object entityHandle;
|
||||
private Map<Integer, WrappedWatchableObject> entries = new HashMap<>();
|
||||
|
||||
public InMemoryDataWatcher() {
|
||||
|
||||
}
|
||||
|
||||
public InMemoryDataWatcher(Object handle) {
|
||||
this.entityHandle = entityFromWatcherHandle(handle);
|
||||
this.populateFromHandle(handle);
|
||||
}
|
||||
|
||||
public InMemoryDataWatcher(Entity entity) {
|
||||
this.entityHandle = BukkitUnwrapper.getInstance().unwrapItem(entity);
|
||||
this.populateFromEntity(entity);
|
||||
}
|
||||
|
||||
public InMemoryDataWatcher(List<WrappedWatchableObject> objects) {
|
||||
for (WrappedWatchableObject obj : objects) {
|
||||
entries.put(obj.getIndex(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
private static FieldAccessor ENTITY_WATCHER_FIELD;
|
||||
private static FieldAccessor WATCHER_ENTITY_FIELD;
|
||||
private static FieldAccessor ARRAY_FIELD;
|
||||
private static ConstructorAccessor CONSTRUCTOR;
|
||||
|
||||
private static boolean ARRAY_BACKED = MinecraftVersion.v1_20_4.atOrAbove();
|
||||
|
||||
private static Class<?> SYNCED_DATA_HOLDER_CLASS = ARRAY_BACKED
|
||||
? MinecraftReflection.getMinecraftClass("network.syncher.SyncedDataHolder")
|
||||
: MinecraftReflection.getEntityClass();
|
||||
|
||||
public static WrappedDataWatcher getEntityWatcher(Entity entity) {
|
||||
return new WrappedDataWatcher(entity);
|
||||
}
|
||||
|
||||
private static Object entityFromWatcherHandle(Object handle) {
|
||||
if (WATCHER_ENTITY_FIELD == null) {
|
||||
WATCHER_ENTITY_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getDataWatcherClass(), SYNCED_DATA_HOLDER_CLASS, true);
|
||||
}
|
||||
|
||||
return WATCHER_ENTITY_FIELD.get(handle);
|
||||
}
|
||||
|
||||
public void populateFromEntity(Entity entity) {
|
||||
populateFromEntity(BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||
}
|
||||
|
||||
public void applyToEntity(Entity entity) {
|
||||
applyToEntity(BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||
}
|
||||
|
||||
private void applyToEntity(Object entityHandle) {
|
||||
if (ENTITY_WATCHER_FIELD == null) {
|
||||
ENTITY_WATCHER_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
|
||||
}
|
||||
|
||||
Object handle = getHandle(entityHandle);
|
||||
ENTITY_WATCHER_FIELD.set(entityHandle, handle);
|
||||
}
|
||||
|
||||
private void populateFromHandle(Object handle) {
|
||||
if (ARRAY_FIELD == null) {
|
||||
try {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handle.getClass(), true);
|
||||
ARRAY_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(Object[].class)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new FieldAccessException("Failed to find watchable object array", ex);
|
||||
}
|
||||
}
|
||||
|
||||
Object[] backing = (Object[]) ARRAY_FIELD.get(handle);
|
||||
for (Object itemHandle : backing) {
|
||||
if (itemHandle == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WrappedWatchableObject object = new WrappedWatchableObject(itemHandle);
|
||||
entries.put(object.getIndex(), object);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateFromEntity(Object entityHandle) {
|
||||
if (ENTITY_WATCHER_FIELD == null) {
|
||||
ENTITY_WATCHER_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
|
||||
}
|
||||
|
||||
Object handle = ENTITY_WATCHER_FIELD.get(entityHandle);
|
||||
populateFromHandle(handle);
|
||||
}
|
||||
|
||||
public Object getHandle() {
|
||||
return getHandle(entityHandle);
|
||||
}
|
||||
|
||||
public Object getHandle(Object entityHandle) {
|
||||
if (CONSTRUCTOR == null) {
|
||||
CONSTRUCTOR = Accessors.getConstructorAccessor(MinecraftReflection.getDataWatcherClass(),
|
||||
SYNCED_DATA_HOLDER_CLASS, MinecraftReflection.getArrayClass(MinecraftReflection.getDataWatcherItemClass()));
|
||||
}
|
||||
|
||||
if (CONSTRUCTOR == null) {
|
||||
throw new IllegalStateException("Cannot find constructor for DataWatcher.");
|
||||
}
|
||||
|
||||
Object[] items = new Object[entries.size()];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
items[i] = entries.get(i).getHandle();
|
||||
}
|
||||
|
||||
return CONSTRUCTOR.invoke(null, entityHandle, items);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public IDataWatcher deepClone() {
|
||||
InMemoryDataWatcher clone = new InMemoryDataWatcher();
|
||||
clone.entries = new HashMap<>(this.entries.size());
|
||||
clone.entityHandle = this.entityHandle;
|
||||
|
||||
for (WrappedWatchableObject object : this) {
|
||||
clone.setObject(object.getWatcherObject(), object.getValue(), false);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public Entity getEntity() {
|
||||
return entityHandle != null ? (Entity) MinecraftReflection.getBukkitEntity(entityHandle) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entity
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setEntity(Entity entity) {
|
||||
this.entityHandle = BukkitUnwrapper.getInstance().unwrapItem(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Map<Integer, WrappedWatchableObject> asMap() {
|
||||
return ImmutableMap.copyOf(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Set<Integer> getIndexes() {
|
||||
return entries.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<WrappedWatchableObject> getWatchableObjects() {
|
||||
return Lists.newArrayList(entries.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public WrappedWatchableObject getWatchableObject(int index) {
|
||||
return entries.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public WrappedWatchableObject remove(int index) {
|
||||
return entries.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean hasIndex(int index) {
|
||||
return entries.containsKey(index);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
WrappedWatchableObject obj = getWatchableObject(index);
|
||||
return obj != null ? obj.getValue() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Object getObject(WrappedDataWatcherObject object) {
|
||||
return getObject(object.getIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* @param value
|
||||
* @param update
|
||||
*/
|
||||
@Override
|
||||
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
|
||||
entries.put(object.getIndex(), value);
|
||||
|
||||
if (update) {
|
||||
value.setDirtyState(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* @param value
|
||||
* @param update
|
||||
*/
|
||||
@Override
|
||||
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
|
||||
setObject(object, new WrappedWatchableObject(object, value), update);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<WrappedWatchableObject> iterator() {
|
||||
return entries.values().iterator();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,469 @@
|
|||
/**
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2018 dmulloy2
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
|
||||
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
/**
|
||||
* Represents a DataWatcher
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class LegacyDataWatcher extends AbstractWrapper implements IDataWatcher {
|
||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
|
||||
|
||||
private static MethodAccessor GETTER = null;
|
||||
private static MethodAccessor SETTER = null;
|
||||
private static MethodAccessor REGISTER = null;
|
||||
|
||||
private static FieldAccessor ENTITY_DATA_FIELD = null;
|
||||
private static FieldAccessor ENTITY_FIELD = null;
|
||||
private static FieldAccessor MAP_FIELD = null;
|
||||
|
||||
private static ConstructorAccessor constructor = null;
|
||||
private static ConstructorAccessor eggConstructor = null;
|
||||
|
||||
private static Object fakeEntity = null;
|
||||
|
||||
// ---- Construction
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher wrapper around a NMS handle. The resulting
|
||||
* DataWatcher will likely have existing values that can be removed with
|
||||
* {@link #clear()}.
|
||||
*
|
||||
* @param handle DataWatcher handle
|
||||
*/
|
||||
public LegacyDataWatcher(Object handle) {
|
||||
super(HANDLE_TYPE);
|
||||
setHandle(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher using a fake egg entity. The
|
||||
* resulting DataWatcher will not have any keys or values and new ones will
|
||||
* have to be added using watcher objects.
|
||||
*/
|
||||
@Deprecated
|
||||
public LegacyDataWatcher() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher using a real entity. The resulting
|
||||
* DataWatcher will not have any keys or values and new ones will have to
|
||||
* be added using watcher objects.
|
||||
*
|
||||
* @param entity The entity
|
||||
*/
|
||||
@Deprecated
|
||||
public LegacyDataWatcher(Entity entity) {
|
||||
this(getHandleFromEntity(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher using a fake egg entity and a given
|
||||
* list of watchable objects.
|
||||
*
|
||||
* @param objects The list of objects
|
||||
*/
|
||||
@Deprecated
|
||||
public LegacyDataWatcher(List<WrappedWatchableObject> objects) {
|
||||
this(newHandle(fakeEntity(), objects));
|
||||
}
|
||||
|
||||
private static Object newHandle(Object entity, List<WrappedWatchableObject> objects) {
|
||||
if (constructor == null) {
|
||||
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(),
|
||||
MinecraftReflection.getArrayClass(MinecraftReflection.getDataWatcherItemClass()));
|
||||
}
|
||||
|
||||
Object[] genericItems = new Object[0];// (Object[]) ITEMS_CONVERTER.getGeneric(objects);
|
||||
return constructor.invoke(entity, genericItems);
|
||||
}
|
||||
|
||||
private static Object fakeEntity() {
|
||||
if (fakeEntity != null) {
|
||||
return fakeEntity;
|
||||
}
|
||||
|
||||
// We can create a fake egg without it affecting anything
|
||||
// Mojang added difficulty to lightning strikes, so this'll have to do
|
||||
if (eggConstructor == null) {
|
||||
eggConstructor = Accessors.getConstructorAccessor(
|
||||
MinecraftReflection.getMinecraftClass("world.entity.projectile.EntityEgg", "world.entity.projectile.ThrownEgg", "EntityEgg"),
|
||||
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class
|
||||
);
|
||||
}
|
||||
|
||||
Object world = BukkitUnwrapper.getInstance().unwrapItem(Bukkit.getWorlds().get(0));
|
||||
return fakeEntity = eggConstructor.invoke(world, 0, 0, 0);
|
||||
}
|
||||
|
||||
// ---- Collection Methods
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Deprecated
|
||||
private Map<Integer, Object> getMap() {
|
||||
if (MAP_FIELD == null) {
|
||||
try {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(Map.class)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new FieldAccessException("Failed to find watchable object map", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return (Map<Integer, Object>) MAP_FIELD.get(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contents of this DataWatcher as a map.
|
||||
* @return The contents
|
||||
*/
|
||||
@Deprecated
|
||||
public Map<Integer, WrappedWatchableObject> asMap() {
|
||||
Map<Integer, Object> backingMap = getMap();
|
||||
|
||||
return new ConvertedMap<Integer, Object, WrappedWatchableObject>(backingMap) {
|
||||
@Override
|
||||
protected WrappedWatchableObject toOuter(Object inner) {
|
||||
return inner != null ? new WrappedWatchableObject(inner) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object toInner(WrappedWatchableObject outer) {
|
||||
return outer != null ? outer.getHandle() : null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set containing the registered indexes.
|
||||
* @return The set
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getIndexes() {
|
||||
return getMap().keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the contents of this DataWatcher.
|
||||
* @return The contents
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<WrappedWatchableObject> getWatchableObjects() {
|
||||
return new ArrayList<>(asMap().values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<WrappedWatchableObject> iterator() {
|
||||
return getWatchableObjects().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of this DataWatcher's contents.
|
||||
* @return The size
|
||||
*/
|
||||
public int size() {
|
||||
return getMap().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item at a given index.
|
||||
*
|
||||
* @param index Index to get
|
||||
* @return The watchable object, or null if none exists
|
||||
*/
|
||||
public WrappedWatchableObject getWatchableObject(int index) {
|
||||
Object handle = getMap().get(index);
|
||||
|
||||
if (handle != null) {
|
||||
return new WrappedWatchableObject(handle);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Renamed to {@link #remove(int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public WrappedWatchableObject removeObject(int index) {
|
||||
return remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the item at a given index.
|
||||
*
|
||||
* @param index Index to remove
|
||||
* @return The previous value, or null if none existed
|
||||
*/
|
||||
public WrappedWatchableObject remove(int index) {
|
||||
Object removed = getMap().remove(index);
|
||||
return removed != null ? new WrappedWatchableObject(removed) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this DataWatcher has an object at a given index.
|
||||
*
|
||||
* @param index Index to check for
|
||||
* @return True if it does, false if not
|
||||
*/
|
||||
public boolean hasIndex(int index) {
|
||||
return getMap().containsKey(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set containing all the registered indexes
|
||||
* @return The set
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> indexSet() {
|
||||
return getIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the contents of this DataWatcher. The watcher will be empty after
|
||||
* this operation is called.
|
||||
*/
|
||||
@Deprecated
|
||||
public void clear() {
|
||||
getMap().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a watchable object by index.
|
||||
*
|
||||
* @param index Index of the object to retrieve.
|
||||
* @return The watched object or null if it doesn't exist.
|
||||
*/
|
||||
public Object getObject(int index) {
|
||||
return getObject(WrappedDataWatcherObject.fromIndex(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a watchable object by watcher object.
|
||||
*
|
||||
* @param object The watcher object
|
||||
* @return The watched object or null if it doesn't exist.
|
||||
*/
|
||||
public Object getObject(WrappedDataWatcherObject object) {
|
||||
Validate.notNull(object, "Watcher object cannot be null!");
|
||||
|
||||
if (GETTER == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactType(object.getHandleType())
|
||||
.returnTypeExact(Object.class)
|
||||
.build(), "get"));
|
||||
} else {
|
||||
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactType(int.class)
|
||||
.returnTypeExact(MinecraftReflection.getDataWatcherItemClass())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object value = GETTER.invoke(handle, object.getHandle());
|
||||
return WrappedWatchableObject.getWrapped(value);
|
||||
} catch (Exception ex) {
|
||||
// Nothing exists at this index
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object
|
||||
* @param value
|
||||
* @param update
|
||||
*/
|
||||
@Override
|
||||
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
|
||||
setObject(object, value.getRawValue(), update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DataWatcher Item associated with a given watcher object to a
|
||||
* new value. If there is not already an object at this index, the
|
||||
* specified watcher object must have a serializer.
|
||||
*
|
||||
* @param object Associated watcher object
|
||||
* @param value New value
|
||||
*
|
||||
* @throws IllegalArgumentException If the watcher object is null or must
|
||||
* have a serializer and does not have one.
|
||||
*/
|
||||
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
|
||||
Validate.notNull(object, "Watcher object cannot be null!");
|
||||
|
||||
if (SETTER == null && REGISTER == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.PUBLIC)
|
||||
.parameterExactArray(object.getHandleType(), Object.class)
|
||||
.build();
|
||||
List<Method> methods = fuzzy.getMethodList(contract);
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals("set") || method.getName().equals("watch") || method.getName().equals("b")) {
|
||||
SETTER = Accessors.getMethodAccessor(method);
|
||||
} else {
|
||||
REGISTER = Accessors.getMethodAccessor(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap the object
|
||||
value = WrappedWatchableObject.getUnwrapped(value);
|
||||
|
||||
if (hasIndex(object.getIndex())) {
|
||||
SETTER.invoke(handle, object.getHandle(), value);
|
||||
} else {
|
||||
object.checkSerializer();
|
||||
REGISTER.invoke(handle, object.getHandle(), value);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
getWatchableObject(object.getIndex()).setDirtyState(update);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Utility Methods
|
||||
|
||||
/**
|
||||
* Clone the content of the current DataWatcher.
|
||||
*
|
||||
* @return A cloned data watcher.
|
||||
*/
|
||||
public IDataWatcher deepClone() {
|
||||
LegacyDataWatcher clone = new LegacyDataWatcher(getEntity());
|
||||
for (WrappedWatchableObject wrapper : this) {
|
||||
clone.setObject(wrapper.getWatcherObject(), wrapper, false);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
private static Object getHandleFromEntity(Entity entity) {
|
||||
if (ENTITY_DATA_FIELD == null) {
|
||||
ENTITY_DATA_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
|
||||
}
|
||||
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
Object handle = ENTITY_DATA_FIELD.get(unwrapper.unwrapItem(entity));
|
||||
return handle;
|
||||
}
|
||||
|
||||
private Object getEntityHandle() {
|
||||
if (ENTITY_FIELD == null) {
|
||||
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||
}
|
||||
|
||||
Object entity = ENTITY_FIELD.get(handle);
|
||||
if (entity == null) {
|
||||
throw new NullPointerException(handle + "." + ENTITY_FIELD);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entity associated with this data watcher.
|
||||
* @return The entity, or NULL.
|
||||
*/
|
||||
public Entity getEntity() {
|
||||
Object entity = getEntityHandle();
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the entity associated with this data watcher.
|
||||
* @param entity - the new entity.
|
||||
*/
|
||||
public void setEntity(Entity entity) {
|
||||
if (ENTITY_FIELD == null) {
|
||||
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||
}
|
||||
|
||||
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null) return false;
|
||||
|
||||
if (obj instanceof LegacyDataWatcher) {
|
||||
LegacyDataWatcher other = (LegacyDataWatcher) obj;
|
||||
Iterator<WrappedWatchableObject> first = iterator(), second = other.iterator();
|
||||
|
||||
// Make sure they're the same size
|
||||
if (size() != other.size())
|
||||
return false;
|
||||
|
||||
for (; first.hasNext() && second.hasNext();) {
|
||||
// See if the two elements are equal
|
||||
if (!first.next().equals(second.next()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getWatchableObjects().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LegacyDataWatcher[handle=" + handle + "]";
|
||||
}
|
||||
}
|
|
@ -18,7 +18,6 @@ import javax.annotation.Nullable;
|
|||
import java.lang.reflect.Constructor;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a single attribute sent in packet 44.
|
||||
*
|
||||
|
@ -26,6 +25,8 @@ import java.util.*;
|
|||
*/
|
||||
public class WrappedAttribute extends AbstractWrapper {
|
||||
public static boolean KEY_WRAPPED = MinecraftVersion.NETHER_UPDATE.atOrAbove();
|
||||
public static boolean IS_STATIC = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
|
||||
public static boolean IS_IN_HOLDER = MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove();
|
||||
|
||||
// Shared structure modifier
|
||||
private static StructureModifier<Object> ATTRIBUTE_MODIFIER;
|
||||
|
@ -136,8 +137,13 @@ public class WrappedAttribute extends AbstractWrapper {
|
|||
*/
|
||||
public String getAttributeKey() {
|
||||
if (KEY_WRAPPED) {
|
||||
WrappedAttributeBase base = modifier.withType(ATTRIBUTE_BASE_CLASS, ATTRIBUTE_BASE).read(0);
|
||||
return base.key.replace("attribute.name.", ""); // TODO not entirely sure why this happens
|
||||
StructureModifier<WrappedAttributeBase> typedModifier = IS_IN_HOLDER
|
||||
? modifier.withParamType(MinecraftReflection.getHolderClass(),
|
||||
Converters.holder(ATTRIBUTE_BASE, WrappedRegistry.getAttributeRegistry()),
|
||||
ATTRIBUTE_BASE_CLASS)
|
||||
: modifier.withType(ATTRIBUTE_BASE_CLASS, ATTRIBUTE_BASE);
|
||||
WrappedAttributeBase base = typedModifier.read(0);
|
||||
return base.key.replace("attribute.name.", "");
|
||||
} else {
|
||||
return (String) modifier.withType(String.class).read(0);
|
||||
}
|
||||
|
@ -447,12 +453,10 @@ public class WrappedAttribute extends AbstractWrapper {
|
|||
throw new IllegalStateException("Base value has not been set.");
|
||||
}
|
||||
|
||||
boolean isStatic = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
|
||||
|
||||
if (ATTRIBUTE_CONSTRUCTOR == null) {
|
||||
FuzzyReflection ref = FuzzyReflection.fromClass(MinecraftReflection.getAttributeSnapshotClass(), true);
|
||||
FuzzyMethodContract.Builder contract = FuzzyMethodContract.newBuilder().parameterCount(isStatic ? 3 : 4);
|
||||
if (!isStatic) {
|
||||
FuzzyMethodContract.Builder contract = FuzzyMethodContract.newBuilder().parameterCount(IS_STATIC ? 3 : 4);
|
||||
if (!IS_STATIC) {
|
||||
contract.parameterDerivedOf(MinecraftReflection.getPacketClass(), 0);
|
||||
}
|
||||
contract.parameterExactType(double.class).parameterDerivedOf(Collection.class);
|
||||
|
@ -471,13 +475,15 @@ public class WrappedAttribute extends AbstractWrapper {
|
|||
if (attributeKey == null) {
|
||||
throw new IllegalArgumentException("Invalid attribute name: " + this.attributeKey);
|
||||
}
|
||||
|
||||
attributeKey = registry.getHolder(attributeKey);
|
||||
} else {
|
||||
attributeKey = this.attributeKey;
|
||||
}
|
||||
|
||||
try {
|
||||
Object handle;
|
||||
if (isStatic) {
|
||||
if (IS_STATIC) {
|
||||
handle = ATTRIBUTE_CONSTRUCTOR.newInstance(attributeKey, baseValue, getUnwrappedModifiers());
|
||||
} else {
|
||||
handle = ATTRIBUTE_CONSTRUCTOR.newInstance(packet.getHandle(), attributeKey, baseValue, getUnwrappedModifiers());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import java.io.StringReader;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
|
@ -9,8 +9,12 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
|
|||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Represents a chat component added in Minecraft 1.7.2
|
||||
|
@ -20,6 +24,8 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
private static final Class<?> SERIALIZER = MinecraftReflection.getChatSerializerClass();
|
||||
private static final Class<?> COMPONENT = MinecraftReflection.getIChatBaseComponentClass();
|
||||
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
|
||||
private static final Optional<Class<?>> MUTABLE_COMPONENT_CLASS
|
||||
= MinecraftReflection.getOptionalNMS("network.chat.IChatMutableComponent");
|
||||
|
||||
private static Object GSON = null;
|
||||
private static MethodAccessor DESERIALIZE = null;
|
||||
|
@ -30,22 +36,41 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
|
||||
|
||||
static {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
|
||||
FuzzyReflection reflection = FuzzyReflection.fromClass(SERIALIZER, true);
|
||||
|
||||
// Retrieve the correct methods
|
||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||
String.class, new Class<?>[] { COMPONENT }));
|
||||
|
||||
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
|
||||
|
||||
try {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
|
||||
.getMethodByReturnTypeAndParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// We'll handle it in the ComponentParser
|
||||
DESERIALIZE = null;
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeExact(String.class)
|
||||
.parameterDerivedOf(COMPONENT)
|
||||
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
|
||||
.build()));
|
||||
} else {
|
||||
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethodByReturnTypeAndParameters("serialize", /* a */
|
||||
String.class, new Class<?>[] { COMPONENT }));
|
||||
}
|
||||
|
||||
GSON = Accessors.getFieldAccessor(reflection.getFieldByType("gson", GSON_CLASS)).get(null);
|
||||
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnDerivedOf(COMPONENT)
|
||||
.parameterExactType(String.class)
|
||||
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
|
||||
.build()));
|
||||
} else if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(reflection
|
||||
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class }));
|
||||
} else {
|
||||
try {
|
||||
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
|
||||
.getMethodByReturnTypeAndParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// We'll handle it in the ComponentParser
|
||||
DESERIALIZE = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a component from a standard Minecraft message
|
||||
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class, boolean.class);
|
||||
|
||||
|
@ -57,7 +82,23 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
}
|
||||
}
|
||||
|
||||
private static Object serialize(Object handle) {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
return SERIALIZE_COMPONENT.invoke(null, handle, MinecraftRegistryAccess.get());
|
||||
}
|
||||
|
||||
return SERIALIZE_COMPONENT.invoke(null, handle);
|
||||
}
|
||||
|
||||
private static Object deserialize(String json) {
|
||||
if (MinecraftVersion.v1_20_5.atOrAbove()) {
|
||||
return DESERIALIZE.invoke(null, json, MinecraftRegistryAccess.get());
|
||||
}
|
||||
|
||||
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||
return DESERIALIZE.invoke(null, json);
|
||||
}
|
||||
|
||||
// Should be non-null on 1.9 and up
|
||||
if (DESERIALIZE != null) {
|
||||
return DESERIALIZE.invoke(null, GSON, json, COMPONENT, true);
|
||||
|
@ -152,7 +193,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
|
|||
*/
|
||||
public String getJson() {
|
||||
if (cache == null) {
|
||||
cache = (String) SERIALIZE_COMPONENT.invoke(null, handle);
|
||||
cache = (String) serialize(handle);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.codecs.WrappedCodec;
|
||||
import com.comphenix.protocol.wrappers.codecs.WrappedDynamicOps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
/**
|
||||
* A wrapper around the component style NMS class.
|
||||
*
|
||||
* @author vytskalt
|
||||
*/
|
||||
public class WrappedComponentStyle extends AbstractWrapper {
|
||||
private static final WrappedCodec CODEC; // 1.20.4+
|
||||
private static final Gson GSON; // Below 1.20.4
|
||||
|
||||
static {
|
||||
if (MinecraftVersion.v1_20_4.atOrAbove()) {
|
||||
FuzzyReflection fuzzySerializer = FuzzyReflection.fromClass(MinecraftReflection.getStyleSerializerClass(), true);
|
||||
Object codec = Accessors.getFieldAccessor(fuzzySerializer.getFieldByType("CODEC", MinecraftReflection.getCodecClass())).get(null);
|
||||
CODEC = WrappedCodec.fromHandle(codec);
|
||||
GSON = null;
|
||||
} else {
|
||||
FuzzyReflection fuzzySerializer = FuzzyReflection.fromClass(MinecraftReflection.getChatSerializerClass(), true);
|
||||
CODEC = null;
|
||||
GSON = (Gson) Accessors.getFieldAccessor(fuzzySerializer.getFieldByType("gson", Gson.class)).get(null);
|
||||
}
|
||||
}
|
||||
|
||||
public WrappedComponentStyle(Object handle) {
|
||||
super(MinecraftReflection.getComponentStyleClass());
|
||||
setHandle(handle);
|
||||
}
|
||||
|
||||
public JsonElement getJson() {
|
||||
if (CODEC != null) {
|
||||
return (JsonElement) CODEC.encode(handle, WrappedDynamicOps.json(false))
|
||||
.getOrThrow(JsonParseException::new);
|
||||
} else {
|
||||
return GSON.toJsonTree(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public static WrappedComponentStyle fromJson(JsonElement json) {
|
||||
Object handle;
|
||||
if (CODEC != null) {
|
||||
handle = CODEC.parse(json, WrappedDynamicOps.json(false))
|
||||
.getOrThrow(JsonParseException::new);
|
||||
} else {
|
||||
handle = GSON.fromJson(json, MinecraftReflection.getComponentStyleClass());
|
||||
}
|
||||
return new WrappedComponentStyle(handle);
|
||||
}
|
||||
}
|
|
@ -9,10 +9,16 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
|
|||
|
||||
/**
|
||||
* Represents a DataValue in 1.19.3+.
|
||||
* Use {@link WrappedWatchableObject} before 1.19.3.
|
||||
*/
|
||||
public class WrappedDataValue extends AbstractWrapper {
|
||||
|
||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getNullableNMS("network.syncher.DataWatcher$b", "network.syncher.SynchedEntityData$DataValue");
|
||||
// TODO: need a better solution
|
||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getNullableNMS(
|
||||
"network.syncher.SynchedEntityData$DataValue",
|
||||
"network.syncher.DataWatcher$b",
|
||||
"network.syncher.DataWatcher$c"
|
||||
);
|
||||
|
||||
private static ConstructorAccessor constructor;
|
||||
|
||||
|
@ -29,10 +35,28 @@ public class WrappedDataValue extends AbstractWrapper {
|
|||
this.modifier = new StructureModifier<>(this.handleType).withTarget(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WrappedDataValue from a NMS value.
|
||||
* ProtocolLib wrappers are not supported as arguments.
|
||||
* If implicit unwrapping of wrappers is required, use {@link WrappedDataValue#fromWrappedValue(int, Serializer, Object)}.
|
||||
* @param index the index of the metadata value
|
||||
* @param serializer the serializer corresponding for serializing. Can be null.
|
||||
* @param value The raw value for the DataValue. Can be null.
|
||||
*/
|
||||
public WrappedDataValue(int index, Serializer serializer, Object value) {
|
||||
this(newHandle(index, serializer, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WrappedDataValue from a possibly wrapped value and implicitly unwrap value if possible.
|
||||
* @param index the index of the metadata value
|
||||
* @param serializer the serializer corresponding for serializing. Can be null.
|
||||
* @param value The value for the DataValue. Can be null.
|
||||
*/
|
||||
public static WrappedDataValue fromWrappedValue(int index, Serializer serializer, Object value) {
|
||||
return new WrappedDataValue(index, serializer, value == null ? null : WrappedWatchableObject.getUnwrapped(value));
|
||||
}
|
||||
|
||||
private static Object newHandle(int index, Serializer serializer, Object value) {
|
||||
if (constructor == null) {
|
||||
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
|
||||
|
@ -41,14 +65,26 @@ public class WrappedDataValue extends AbstractWrapper {
|
|||
return constructor.invoke(index, serializer.getHandle(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity-type specific index of this DataValue
|
||||
* @return index of the DataValue
|
||||
*/
|
||||
public int getIndex() {
|
||||
return this.modifier.<Integer>withType(int.class).read(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity-type specific index of this DataValue
|
||||
* @param index New index of the DataValue
|
||||
*/
|
||||
public void setIndex(int index) {
|
||||
this.modifier.withType(int.class).write(0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current serializer for this DataValue.
|
||||
* @return serializer
|
||||
*/
|
||||
public Serializer getSerializer() {
|
||||
Object serializer = this.modifier.readSafely(1);
|
||||
if (serializer != null) {
|
||||
|
@ -63,22 +99,42 @@ public class WrappedDataValue extends AbstractWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the serializer for this DataValue
|
||||
* @param serializer serializer
|
||||
*/
|
||||
public void setSerializer(Serializer serializer) {
|
||||
this.modifier.writeSafely(1, serializer == null ? null : serializer.getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value associated and implicitly wraps it to corresponding ProtocolLib wrappers if possible.
|
||||
* @return Current value
|
||||
*/
|
||||
public Object getValue() {
|
||||
return WrappedWatchableObject.getWrapped(getRawValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current value associated and implicitly unwraps it to NMS types if a ProtocolLib wrapper is provided.
|
||||
* @param value New value for this DataValue
|
||||
*/
|
||||
public void setValue(Object value) {
|
||||
setRawValue(WrappedWatchableObject.getUnwrapped(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current, raw value.
|
||||
* @return Raw value (not wrapped)
|
||||
*/
|
||||
public Object getRawValue() {
|
||||
return this.modifier.readSafely(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the raw value for this DataValue. No unwrapping will be applied.
|
||||
* @param value NMS value
|
||||
*/
|
||||
public void setRawValue(Object value) {
|
||||
this.modifier.writeSafely(2, value);
|
||||
}
|
||||
|
|
|
@ -16,51 +16,52 @@
|
|||
*/
|
||||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.comphenix.protocol.injector.BukkitUnwrapper;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers.EntityPose;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents a DataWatcher
|
||||
* @author dmulloy2
|
||||
*/
|
||||
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject>, ClonableWrapper {
|
||||
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
|
||||
public class WrappedDataWatcher implements IDataWatcher {
|
||||
private static final boolean IN_MEMORY = MinecraftVersion.v1_20_4.atOrAbove();
|
||||
|
||||
private static MethodAccessor GETTER = null;
|
||||
private static MethodAccessor SETTER = null;
|
||||
private static MethodAccessor REGISTER = null;
|
||||
|
||||
private static FieldAccessor ENTITY_DATA_FIELD = null;
|
||||
private static FieldAccessor ENTITY_FIELD = null;
|
||||
private static FieldAccessor MAP_FIELD = null;
|
||||
|
||||
private static ConstructorAccessor constructor = null;
|
||||
private static ConstructorAccessor eggConstructor = null;
|
||||
|
||||
private static Object fakeEntity = null;
|
||||
@NotNull
|
||||
private final IDataWatcher impl;
|
||||
|
||||
// ---- Construction
|
||||
|
||||
private WrappedDataWatcher(IDataWatcher impl) {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new DataWatcher wrapper around a NMS handle. The resulting
|
||||
* DataWatcher will likely have existing values that can be removed with
|
||||
|
@ -69,8 +70,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @param handle DataWatcher handle
|
||||
*/
|
||||
public WrappedDataWatcher(Object handle) {
|
||||
super(HANDLE_TYPE);
|
||||
setHandle(handle);
|
||||
this.impl = IN_MEMORY
|
||||
? new InMemoryDataWatcher(handle)
|
||||
: new LegacyDataWatcher(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +81,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* have to be added using watcher objects.
|
||||
*/
|
||||
public WrappedDataWatcher() {
|
||||
this(newHandle(fakeEntity()));
|
||||
this.impl = IN_MEMORY
|
||||
? new InMemoryDataWatcher()
|
||||
: new LegacyDataWatcher();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +94,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @param entity The entity
|
||||
*/
|
||||
public WrappedDataWatcher(Entity entity) {
|
||||
this(newHandle(BukkitUnwrapper.getInstance().unwrapItem(entity)));
|
||||
this.impl = IN_MEMORY
|
||||
? new InMemoryDataWatcher(entity)
|
||||
: new LegacyDataWatcher(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,63 +106,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @param objects The list of objects
|
||||
*/
|
||||
public WrappedDataWatcher(List<WrappedWatchableObject> objects) {
|
||||
this();
|
||||
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
for (WrappedWatchableObject object : objects) {
|
||||
setObject(object.getWatcherObject(), object);
|
||||
}
|
||||
} else {
|
||||
for (WrappedWatchableObject object : objects) {
|
||||
setObject(object.getIndex(), object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Object newHandle(Object entity) {
|
||||
if (constructor == null) {
|
||||
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass());
|
||||
}
|
||||
|
||||
return constructor.invoke(entity);
|
||||
}
|
||||
|
||||
private static Object fakeEntity() {
|
||||
if (fakeEntity != null) {
|
||||
return fakeEntity;
|
||||
}
|
||||
|
||||
// We can create a fake egg without it affecting anything
|
||||
// Mojang added difficulty to lightning strikes, so this'll have to do
|
||||
if (eggConstructor == null) {
|
||||
eggConstructor = Accessors.getConstructorAccessor(
|
||||
MinecraftReflection.getMinecraftClass("world.entity.projectile.EntityEgg", "world.entity.projectile.ThrownEgg", "EntityEgg"),
|
||||
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class
|
||||
);
|
||||
}
|
||||
|
||||
Object world = BukkitUnwrapper.getInstance().unwrapItem(Bukkit.getWorlds().get(0));
|
||||
return fakeEntity = eggConstructor.invoke(world, 0, 0, 0);
|
||||
}
|
||||
|
||||
// ---- Collection Methods
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Integer, Object> getMap() {
|
||||
if (MAP_FIELD == null) {
|
||||
try {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
|
||||
.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeDerivedOf(Map.class)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new FieldAccessException("Failed to find watchable object map");
|
||||
}
|
||||
}
|
||||
|
||||
return (Map<Integer, Object>) MAP_FIELD.get(handle);
|
||||
this.impl = IN_MEMORY
|
||||
? new InMemoryDataWatcher(objects)
|
||||
: new LegacyDataWatcher(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,38 +116,30 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return The contents
|
||||
*/
|
||||
public Map<Integer, WrappedWatchableObject> asMap() {
|
||||
return new ConvertedMap<Integer, Object, WrappedWatchableObject>(getMap()) {
|
||||
@Override
|
||||
protected WrappedWatchableObject toOuter(Object inner) {
|
||||
return inner != null ? new WrappedWatchableObject(inner) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object toInner(WrappedWatchableObject outer) {
|
||||
return outer != null ? outer.getHandle() : null;
|
||||
}
|
||||
};
|
||||
return impl.asMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a set containing the registered indexes.
|
||||
* @return The set
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> getIndexes() {
|
||||
return getMap().keySet();
|
||||
return impl.getIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the contents of this DataWatcher.
|
||||
* @return The contents
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<WrappedWatchableObject> getWatchableObjects() {
|
||||
return new ArrayList<>(asMap().values());
|
||||
return impl.getWatchableObjects();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<WrappedWatchableObject> iterator() {
|
||||
return getWatchableObjects().iterator();
|
||||
return impl.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,7 +147,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return The size
|
||||
*/
|
||||
public int size() {
|
||||
return getMap().size();
|
||||
return impl.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,12 +157,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return The watchable object, or null if none exists
|
||||
*/
|
||||
public WrappedWatchableObject getWatchableObject(int index) {
|
||||
Object handle = getMap().get(index);
|
||||
if (handle != null) {
|
||||
return new WrappedWatchableObject(handle);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return impl.getWatchableObject(index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,8 +175,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return The previous value, or null if none existed
|
||||
*/
|
||||
public WrappedWatchableObject remove(int index) {
|
||||
Object removed = getMap().remove(index);
|
||||
return removed != null ? new WrappedWatchableObject(removed) : null;
|
||||
return impl.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -247,15 +185,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return True if it does, false if not
|
||||
*/
|
||||
public boolean hasIndex(int index) {
|
||||
return getMap().containsKey(index);
|
||||
return impl.hasIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set containing all the registered indexes
|
||||
* @return The set
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Integer> indexSet() {
|
||||
return getMap().keySet();
|
||||
return getIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -263,10 +202,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* this operation is called.
|
||||
*/
|
||||
public void clear() {
|
||||
getMap().clear();
|
||||
impl.clear();
|
||||
}
|
||||
|
||||
// ---- Object Getters
|
||||
// ---- 0: byte
|
||||
|
||||
/**
|
||||
* Get a watched byte.
|
||||
|
@ -278,6 +217,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return (Byte) getObject(index);
|
||||
}
|
||||
|
||||
public void setByte(int index, byte value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Byte.class)), value, update);
|
||||
}
|
||||
|
||||
// ---- short (unused)
|
||||
|
||||
/**
|
||||
* Get a watched short.
|
||||
*
|
||||
|
@ -288,6 +233,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return (Short) getObject(index);
|
||||
}
|
||||
|
||||
// ---- 1: varint (integer)
|
||||
|
||||
/**
|
||||
* Get a watched integer.
|
||||
*
|
||||
|
@ -298,6 +245,22 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return (Integer) getObject(index);
|
||||
}
|
||||
|
||||
public void setInteger(int index, Integer value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Integer.class)), value, update);
|
||||
}
|
||||
|
||||
// ---- 2: varlong (long)
|
||||
|
||||
public Long getLong(int index) {
|
||||
return (Long) getObject(index);
|
||||
}
|
||||
|
||||
public void setLong(int index, Long value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Long.class)), value, update);
|
||||
}
|
||||
|
||||
// ---- 3: float
|
||||
|
||||
/**
|
||||
* Get a watched float.
|
||||
*
|
||||
|
@ -308,6 +271,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return (Float) getObject(index);
|
||||
}
|
||||
|
||||
public void setFloat(int index, Float value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Float.class)), value, update);
|
||||
}
|
||||
|
||||
// ---- 4: string
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
*
|
||||
|
@ -318,6 +287,32 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return (String) getObject(index);
|
||||
}
|
||||
|
||||
public void setString(int index, String value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(String.class)), value, update);
|
||||
}
|
||||
|
||||
// ---- 5: text component
|
||||
|
||||
public WrappedChatComponent getChatComponent(int index) {
|
||||
return (WrappedChatComponent) getObject(index);
|
||||
}
|
||||
|
||||
public void setChatComponent(int index, WrappedChatComponent value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getChatComponentSerializer()), value, update);
|
||||
}
|
||||
|
||||
// ---- 6: optional text component
|
||||
|
||||
public Optional<WrappedChatComponent> getOptionalChatComponent(int index) {
|
||||
return (Optional<WrappedChatComponent>) getObject(index);
|
||||
}
|
||||
|
||||
public void setOptionalChatComponent(int index, Optional<WrappedChatComponent> value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getChatComponentSerializer(true)), value, update);
|
||||
}
|
||||
|
||||
// ---- 7: slot (item stack)
|
||||
|
||||
/**
|
||||
* Get a watched string.
|
||||
*
|
||||
|
@ -328,6 +323,158 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return (ItemStack) getObject(index);
|
||||
}
|
||||
|
||||
public void setItemStack(int index, ItemStack itemStack, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getItemStackSerializer(false)), itemStack, update);
|
||||
}
|
||||
|
||||
// ---- 8: boolean
|
||||
|
||||
public Boolean getBoolean(int index) {
|
||||
return (Boolean) getObject(index);
|
||||
}
|
||||
|
||||
public void setBoolean(int index, Boolean value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Boolean.class)), value, update);
|
||||
}
|
||||
|
||||
// ---- 9: rotations
|
||||
// TODO
|
||||
|
||||
// ---- 10: position
|
||||
|
||||
public BlockPosition getPosition(int index) {
|
||||
return (BlockPosition) getObject(index);
|
||||
}
|
||||
|
||||
public void setPosition(int index, BlockPosition position, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getBlockPositionSerializer(false)), position, update);
|
||||
}
|
||||
|
||||
// ---- 11: optional position
|
||||
|
||||
public Optional<BlockPosition> getOptionalPosition(int index) {
|
||||
return (Optional<BlockPosition>) getObject(index);
|
||||
}
|
||||
|
||||
public void setOptionalPosition(int index, java.util.Optional<BlockPosition> position, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getBlockPositionSerializer(true)), position, update);
|
||||
}
|
||||
|
||||
// ---- 12: direction
|
||||
|
||||
public Direction getDirection(int index) {
|
||||
return (Direction) getObject(index);
|
||||
}
|
||||
|
||||
public void setDirection(int index, Direction direction, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getDirectionSerializer()), direction, update);
|
||||
}
|
||||
|
||||
// ---- 13: optional uuid
|
||||
|
||||
public Optional<UUID> getOptionalUUID(int index) {
|
||||
return (Optional<UUID>) getObject(index);
|
||||
}
|
||||
|
||||
public void setOptionalUUID(int index, Optional<UUID> uuid, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getUUIDSerializer(true)), uuid, update);
|
||||
}
|
||||
|
||||
// ---- 14: block state
|
||||
|
||||
public WrappedBlockData getBlockState(int index) {
|
||||
return (WrappedBlockData) getObject(index);
|
||||
}
|
||||
|
||||
public void setBlockState(int index, WrappedBlockData blockData, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getBlockDataSerializer(false)), blockData, update);
|
||||
}
|
||||
|
||||
// ---- 15: optional block state
|
||||
|
||||
public Optional<WrappedBlockData> getOptionalBlockState(int index) {
|
||||
return (Optional<WrappedBlockData>) getObject(index);
|
||||
}
|
||||
|
||||
public void setOptionalBlockState(int index, Optional<WrappedBlockData> value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getBlockDataSerializer(true)), value, update);
|
||||
}
|
||||
|
||||
// ---- 16: NBT
|
||||
|
||||
public NbtCompound getNBTCompound(int index) {
|
||||
return (NbtCompound) getObject(index);
|
||||
}
|
||||
|
||||
public void setNBTCompound(int index, NbtCompound nbt, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.getNBTCompoundSerializer()), nbt, update);
|
||||
}
|
||||
|
||||
// ---- 17: particle
|
||||
|
||||
public WrappedParticle<?> getParticle(int index) {
|
||||
return (WrappedParticle<?>) getObject(index);
|
||||
}
|
||||
|
||||
public void setParticle(int index, WrappedParticle<?> particle, boolean update) {
|
||||
// TODO: is ParticleParam correct?
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(MinecraftReflection.getParticleParam(), false)), particle, update);
|
||||
}
|
||||
|
||||
// ---- 18: villager data
|
||||
|
||||
public WrappedVillagerData getVillagerData(int index) {
|
||||
return (WrappedVillagerData) getObject(index);
|
||||
}
|
||||
|
||||
public void setVillagerData(int index, WrappedVillagerData data, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(WrappedVillagerData.getNmsClass(), false)), data, update);
|
||||
}
|
||||
|
||||
// ---- 19: optional varint (int)
|
||||
|
||||
public Optional<Integer> getOptionalInteger(int index) {
|
||||
return (Optional<Integer>) getObject(index);
|
||||
}
|
||||
|
||||
public void setOptionalInteger(int index, Optional<Integer> value, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Integer.class, true)), value, update);
|
||||
}
|
||||
|
||||
// ---- 20: pose
|
||||
|
||||
public EntityPose getPose(int index) {
|
||||
return (EntityPose) getObject(index);
|
||||
}
|
||||
|
||||
public void setPose(int index, EntityPose pose, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(EnumWrappers.getEntityPoseClass(), false)), pose, update);
|
||||
}
|
||||
|
||||
// TODO: 21-25, 27
|
||||
|
||||
// ---- 21: cat variant
|
||||
|
||||
// ---- 22: frog variant
|
||||
|
||||
// ---- 23: optional global position
|
||||
|
||||
// ---- 24: painting variant
|
||||
|
||||
// ---- 25: sniffer state
|
||||
|
||||
// ---- 26: vector3
|
||||
|
||||
public Vector3F getVector3F(int index) {
|
||||
return (Vector3F) getObject(index);
|
||||
}
|
||||
|
||||
public void setVector3F(int index, Vector3F vector, boolean update) {
|
||||
setObject(new WrappedDataWatcherObject(index, Registry.get(Vector3F.getMinecraftClass(), false)), vector, update);
|
||||
}
|
||||
|
||||
// ---- 27: quaternion
|
||||
|
||||
/**
|
||||
* Retrieve a watchable object by index.
|
||||
*
|
||||
|
@ -345,31 +492,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return The watched object or null if it doesn't exist.
|
||||
*/
|
||||
public Object getObject(WrappedDataWatcherObject object) {
|
||||
Validate.notNull(object, "Watcher object cannot be null!");
|
||||
|
||||
if (GETTER == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactType(object.getHandleType())
|
||||
.returnTypeExact(Object.class)
|
||||
.build(), "get"));
|
||||
} else {
|
||||
GETTER = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactType(int.class)
|
||||
.returnTypeExact(MinecraftReflection.getDataWatcherItemClass())
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object value = GETTER.invoke(handle, object.getHandle());
|
||||
return WrappedWatchableObject.getWrapped(value);
|
||||
} catch (Exception ex) {
|
||||
// Nothing exists at this index
|
||||
return null;
|
||||
}
|
||||
return impl.getObject(object);
|
||||
}
|
||||
|
||||
// ---- Object Setters
|
||||
|
@ -474,38 +597,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* have a serializer and does not have one.
|
||||
*/
|
||||
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
|
||||
Validate.notNull(object, "Watcher object cannot be null!");
|
||||
|
||||
if (SETTER == null && REGISTER == null) {
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
|
||||
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.requireModifier(Modifier.PUBLIC)
|
||||
.parameterExactArray(object.getHandleType(), Object.class)
|
||||
.build();
|
||||
List<Method> methods = fuzzy.getMethodList(contract);
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals("set") || method.getName().equals("watch") || method.getName().equals("b")) {
|
||||
SETTER = Accessors.getMethodAccessor(method);
|
||||
} else {
|
||||
REGISTER = Accessors.getMethodAccessor(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap the object
|
||||
value = WrappedWatchableObject.getUnwrapped(value);
|
||||
|
||||
if (hasIndex(object.getIndex())) {
|
||||
SETTER.invoke(handle, object.getHandle(), value);
|
||||
} else {
|
||||
object.checkSerializer();
|
||||
REGISTER.invoke(handle, object.getHandle(), value);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
getWatchableObject(object.getIndex()).setDirtyState(update);
|
||||
}
|
||||
impl.setObject(object, value, update);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -523,19 +615,15 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return A cloned data watcher.
|
||||
*/
|
||||
public WrappedDataWatcher deepClone() {
|
||||
WrappedDataWatcher clone = new WrappedDataWatcher(getEntity());
|
||||
return new WrappedDataWatcher(impl.deepClone());
|
||||
}
|
||||
|
||||
if (MinecraftReflection.watcherObjectExists()) {
|
||||
for (WrappedWatchableObject wrapper : this) {
|
||||
clone.setObject(wrapper.getWatcherObject(), wrapper);
|
||||
}
|
||||
} else {
|
||||
for (WrappedWatchableObject wrapper : this) {
|
||||
clone.setObject(wrapper.getIndex(), wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Object getHandle() {
|
||||
return impl.getHandle();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -545,13 +633,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return Associated data watcher.
|
||||
*/
|
||||
public static WrappedDataWatcher getEntityWatcher(Entity entity) {
|
||||
if (ENTITY_DATA_FIELD == null) {
|
||||
ENTITY_DATA_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
|
||||
}
|
||||
|
||||
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
|
||||
Object handle = ENTITY_DATA_FIELD.get(unwrapper.unwrapItem(entity));
|
||||
return handle != null ? new WrappedDataWatcher(handle) : null;
|
||||
return new WrappedDataWatcher(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -559,16 +641,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @return The entity, or NULL.
|
||||
*/
|
||||
public Entity getEntity() {
|
||||
if (ENTITY_FIELD == null) {
|
||||
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||
}
|
||||
|
||||
Object entity = ENTITY_FIELD.get(handle);
|
||||
if (entity == null) {
|
||||
throw new NullPointerException(handle + "." + ENTITY_FIELD);
|
||||
}
|
||||
|
||||
return (Entity) MinecraftReflection.getBukkitEntity(entity);
|
||||
return impl.getEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -576,11 +649,23 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
* @param entity - the new entity.
|
||||
*/
|
||||
public void setEntity(Entity entity) {
|
||||
if (ENTITY_FIELD == null) {
|
||||
ENTITY_FIELD = Accessors.getFieldAccessor(HANDLE_TYPE, MinecraftReflection.getEntityClass(), true);
|
||||
}
|
||||
impl.setEntity(entity);
|
||||
}
|
||||
|
||||
ENTITY_FIELD.set(handle, BukkitUnwrapper.getInstance().unwrapItem(entity));
|
||||
/**
|
||||
* Exports the contents of this data watcher to a list of WrappedDataValues
|
||||
* for use in the ENTITY_METADATA packet
|
||||
* @return The data value collection
|
||||
*/
|
||||
public List<WrappedDataValue> toDataValueCollection() {
|
||||
List<WrappedWatchableObject> objects = impl.getWatchableObjects();
|
||||
List<WrappedDataValue> values = new ArrayList<>(objects.size());
|
||||
for (WrappedWatchableObject object : objects) {
|
||||
WrappedDataWatcherObject watcherObj = object.getWatcherObject();
|
||||
Object value = WrappedWatchableObject.getUnwrapped(object.getRawValue());
|
||||
values.add(new WrappedDataValue(watcherObj.getIndex(), watcherObj.getSerializer(), value));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private static final ImmutableBiMap<Class<?>, Integer> CLASS_TO_ID = new ImmutableBiMap.Builder<Class<?>, Integer>()
|
||||
|
@ -623,19 +708,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
|
||||
if (obj instanceof WrappedDataWatcher) {
|
||||
WrappedDataWatcher other = (WrappedDataWatcher) obj;
|
||||
Iterator<WrappedWatchableObject> first = iterator(), second = other.iterator();
|
||||
|
||||
// Make sure they're the same size
|
||||
if (size() != other.size())
|
||||
return false;
|
||||
|
||||
for (; first.hasNext() && second.hasNext();) {
|
||||
// See if the two elements are equal
|
||||
if (!first.next().equals(second.next()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return this.impl.equals(other.impl);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -643,12 +716,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getWatchableObjects().hashCode();
|
||||
return impl.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WrappedDataWatcher[handle=" + handle + "]";
|
||||
return impl.toString();
|
||||
}
|
||||
|
||||
// ---- 1.9 classes
|
||||
|
@ -912,7 +985,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
*/
|
||||
public static class Registry {
|
||||
private static boolean INITIALIZED = false;
|
||||
private static List<Serializer> REGISTRY = new ArrayList<>();
|
||||
|
||||
private static Map<Class<?>, Serializer> RAW_REGISTRY = null;
|
||||
private static Map<Class<?>, Serializer> OPTIONAL_REGISTRY = null;
|
||||
|
||||
/**
|
||||
* Gets the first serializer associated with a given class.
|
||||
|
@ -930,13 +1005,13 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
Validate.notNull(clazz,"Class cannot be null!");
|
||||
initialize();
|
||||
|
||||
for (Serializer serializer : REGISTRY) {
|
||||
if (serializer.getType().equals(clazz)) {
|
||||
return serializer;
|
||||
}
|
||||
Serializer serializer = RAW_REGISTRY.getOrDefault(clazz,
|
||||
OPTIONAL_REGISTRY.getOrDefault(clazz, null));
|
||||
if (serializer == null) {
|
||||
throw new IllegalArgumentException("No serializer found for " + clazz);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No serializer found for " + clazz);
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -953,16 +1028,12 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
Validate.notNull(clazz, "Class cannot be null!");
|
||||
initialize();
|
||||
|
||||
Validate.notEmpty(REGISTRY, "Registry has no elements!");
|
||||
|
||||
for (Serializer serializer : REGISTRY) {
|
||||
if (serializer.getType().equals(clazz)
|
||||
&& serializer.isOptional() == optional) {
|
||||
return serializer;
|
||||
}
|
||||
Serializer serializer = optional ? OPTIONAL_REGISTRY.get(clazz) : RAW_REGISTRY.get(clazz);
|
||||
if (serializer == null) {
|
||||
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
|
||||
return serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -974,7 +1045,13 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
Validate.notNull(handle, "handle cannot be null!");
|
||||
initialize();
|
||||
|
||||
for (Serializer serializer : REGISTRY) {
|
||||
for (Serializer serializer : RAW_REGISTRY.values()) {
|
||||
if (serializer.getHandle().equals(handle)) {
|
||||
return serializer;
|
||||
}
|
||||
}
|
||||
|
||||
for (Serializer serializer : OPTIONAL_REGISTRY.values()) {
|
||||
if (serializer.getHandle().equals(handle)) {
|
||||
return serializer;
|
||||
}
|
||||
|
@ -990,6 +1067,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
return;
|
||||
}
|
||||
|
||||
Map<Class<?>, Serializer> rawRegistry = new HashMap<>();
|
||||
Map<Class<?>, Serializer> optionalRegistry = new HashMap<>();
|
||||
|
||||
List<Field> candidates = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherRegistryClass(), true)
|
||||
.getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass());
|
||||
for (Field candidate : candidates) {
|
||||
|
@ -1023,9 +1103,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
|
|||
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
|
||||
}
|
||||
|
||||
REGISTRY.add(new Serializer(innerClass, serializer, optional));
|
||||
if (optional) {
|
||||
optionalRegistry.put(innerClass, new Serializer(innerClass, serializer, true));
|
||||
} else {
|
||||
rawRegistry.put(innerClass, new Serializer(innerClass, serializer, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RAW_REGISTRY = ImmutableMap.copyOf(rawRegistry);
|
||||
OPTIONAL_REGISTRY = ImmutableMap.copyOf(optionalRegistry);
|
||||
}
|
||||
|
||||
// ---- Helper methods
|
||||
|
|
|
@ -9,7 +9,9 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
|
|||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.reflect.instances.MinecraftGenerator;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.collection.ConvertedMultimap;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
@ -112,15 +114,7 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||
*/
|
||||
@Deprecated
|
||||
public WrappedGameProfile(String id, String name) {
|
||||
super(GAME_PROFILE);
|
||||
|
||||
if (CREATE_STRING_STRING != null) {
|
||||
setHandle(CREATE_STRING_STRING.invoke(id, name));
|
||||
} else if (CREATE_UUID_STRING != null) {
|
||||
setHandle(CREATE_UUID_STRING.invoke(parseUUID(id), name));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
|
||||
}
|
||||
this(parseUUID(id), name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,7 +131,17 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||
if (CREATE_STRING_STRING != null) {
|
||||
setHandle(CREATE_STRING_STRING.invoke(uuid != null ? uuid.toString() : null, name));
|
||||
} else if (CREATE_UUID_STRING != null) {
|
||||
setHandle(CREATE_UUID_STRING.invoke(uuid, name));
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
// 1.20.2+ requires all fields to have a value: null uuid -> UUID(0,0), null name -> empty name
|
||||
// it's not allowed to pass null for both, so we need to pre-check that
|
||||
if (uuid == null && (name == null || name.isEmpty())) {
|
||||
throw new IllegalArgumentException("Name and ID cannot both be blank");
|
||||
}
|
||||
|
||||
setHandle(CREATE_UUID_STRING.invoke(uuid == null ? MinecraftGenerator.SYS_UUID : uuid, name == null ? "" : name));
|
||||
} else {
|
||||
setHandle(CREATE_UUID_STRING.invoke(uuid, name));
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
|
||||
}
|
||||
|
@ -197,6 +201,10 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||
uuid = parseUUID(getId());
|
||||
} else if (GET_ID != null) {
|
||||
uuid = (UUID) GET_ID.invoke(handle);
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove() && MinecraftGenerator.SYS_UUID.equals(uuid)) {
|
||||
// see CraftPlayerProfile
|
||||
uuid = null;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported getId() method");
|
||||
}
|
||||
|
@ -224,7 +232,7 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||
if (GET_UUID_STRING != null) {
|
||||
return (String) GET_UUID_STRING.get(handle);
|
||||
} else if (GET_ID != null) {
|
||||
UUID uuid = (UUID) GET_ID.invoke(handle);
|
||||
UUID uuid = getUUID();
|
||||
return uuid != null ? uuid.toString() : null;
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported getId() method");
|
||||
|
@ -238,7 +246,12 @@ public class WrappedGameProfile extends AbstractWrapper {
|
|||
*/
|
||||
public String getName() {
|
||||
if (GET_NAME != null) {
|
||||
return (String) GET_NAME.invoke(handle);
|
||||
String name = (String) GET_NAME.invoke(handle);
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove() && name != null && name.isEmpty()) {
|
||||
// see CraftPlayerProfile
|
||||
name = null;
|
||||
}
|
||||
return name;
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported getName() method");
|
||||
}
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.comphenix.protocol.injector.StructureCache;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.ZeroBuffer;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Wrapper classes for ClientboundLevelChunkWithLightPacket
|
||||
|
@ -45,8 +46,11 @@ public final class WrappedLevelChunkData {
|
|||
static {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
|
||||
|
||||
LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE,
|
||||
MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class);
|
||||
LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(reflection.getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterDerivedOf(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.parameterExactType(int.class)
|
||||
.parameterExactType(int.class)
|
||||
.build()));
|
||||
BLOCK_ENTITIES_DATA_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(List.class)
|
||||
.build()));
|
||||
|
@ -160,8 +164,11 @@ public final class WrappedLevelChunkData {
|
|||
static {
|
||||
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
|
||||
|
||||
LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE,
|
||||
MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class);
|
||||
LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(reflection.getConstructor(FuzzyMethodContract.newBuilder()
|
||||
.parameterDerivedOf(MinecraftReflection.getPacketDataSerializerClass())
|
||||
.parameterExactType(int.class)
|
||||
.parameterExactType(int.class)
|
||||
.build()));
|
||||
BIT_SET_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, BitSet.class, true);
|
||||
BYTE_ARRAY_LIST_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, List.class, true);
|
||||
|
||||
|
@ -333,7 +340,7 @@ public final class WrappedLevelChunkData {
|
|||
*/
|
||||
public static LightData fromValues(BitSet skyYMask, BitSet blockYMask, BitSet emptySkyYMask, BitSet emptyBlockYMask,
|
||||
List<byte[]> skyUpdates, List<byte[]> blockUpdates) {
|
||||
LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(MinecraftReflection.getPacketDataSerializer(new ZeroBuffer()), 0, 0));
|
||||
LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0));
|
||||
|
||||
data.setSkyYMask(skyYMask);
|
||||
data.setBlockYMask(blockYMask);
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package com.comphenix.protocol.wrappers;
|
||||
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A wrapper around the NumberFormat NMS classes.
|
||||
*
|
||||
* @author vytskalt
|
||||
* @since 1.20.4
|
||||
*/
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
public class WrappedNumberFormat extends AbstractWrapper {
|
||||
private static final Object BLANK;
|
||||
private static final ConstructorAccessor FIXED_CONSTRUCTOR, STYLED_CONSTRUCTOR;
|
||||
|
||||
static {
|
||||
if (!isSupported()) {
|
||||
BLANK = null;
|
||||
FIXED_CONSTRUCTOR = null;
|
||||
STYLED_CONSTRUCTOR = null;
|
||||
} else {
|
||||
Class<?> blankClass = MinecraftReflection.getBlankFormatClass().get();
|
||||
FuzzyReflection fuzzyBlank = FuzzyReflection.fromClass(blankClass, true);
|
||||
BLANK = Accessors.getFieldAccessor(fuzzyBlank.getFieldByType("INSTANCE", blankClass)).get(null);
|
||||
|
||||
FIXED_CONSTRUCTOR = Accessors.getConstructorAccessor(
|
||||
MinecraftReflection.getFixedFormatClass().get(),
|
||||
MinecraftReflection.getIChatBaseComponentClass()
|
||||
);
|
||||
|
||||
STYLED_CONSTRUCTOR = Accessors.getConstructorAccessor(
|
||||
MinecraftReflection.getStyledFormatClass().get(),
|
||||
MinecraftReflection.getComponentStyleClass()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the NumberFormat classes exist on the current server version
|
||||
*/
|
||||
public static boolean isSupported() {
|
||||
return MinecraftReflection.getNumberFormatClass().isPresent();
|
||||
}
|
||||
|
||||
public static WrappedNumberFormat fromHandle(Object handle) {
|
||||
throwIfUnsupported();
|
||||
if (MinecraftReflection.getBlankFormatClass().get().isInstance(handle)) {
|
||||
return new Blank(handle);
|
||||
} else if (MinecraftReflection.getFixedFormatClass().get().isInstance(handle)) {
|
||||
return new Fixed(handle);
|
||||
} else if (MinecraftReflection.getStyledFormatClass().get().isInstance(handle)) {
|
||||
return new Styled(handle);
|
||||
} else {
|
||||
throw new IllegalArgumentException("handle is not a NumberFormat instance, but " + handle.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public static Blank blank() {
|
||||
throwIfUnsupported();
|
||||
return new Blank(WrappedNumberFormat.BLANK);
|
||||
}
|
||||
|
||||
public static Fixed fixed(@NotNull WrappedChatComponent content) {
|
||||
throwIfUnsupported();
|
||||
Object handle = FIXED_CONSTRUCTOR.invoke(content.getHandle());
|
||||
return new Fixed(handle);
|
||||
}
|
||||
|
||||
public static Styled styled(@NotNull WrappedComponentStyle style) {
|
||||
throwIfUnsupported();
|
||||
Object handle = STYLED_CONSTRUCTOR.invoke(style.getHandle());
|
||||
return new Styled(handle);
|
||||
}
|
||||
|
||||
private static void throwIfUnsupported() {
|
||||
if (!isSupported()) {
|
||||
throw new IllegalStateException("NumberFormat classes don't exist on this server version");
|
||||
}
|
||||
}
|
||||
|
||||
private WrappedNumberFormat(Class<?> handleType) {
|
||||
super(handleType);
|
||||
}
|
||||
|
||||
public static class Blank extends WrappedNumberFormat {
|
||||
private Blank(Object handle) {
|
||||
super(MinecraftReflection.getBlankFormatClass().get());
|
||||
setHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Fixed extends WrappedNumberFormat {
|
||||
private final StructureModifier<Object> modifier;
|
||||
|
||||
private Fixed(Object handle) {
|
||||
super(MinecraftReflection.getFixedFormatClass().get());
|
||||
setHandle(handle);
|
||||
this.modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
|
||||
}
|
||||
|
||||
public WrappedChatComponent getContent() {
|
||||
Object handle = modifier.withType(MinecraftReflection.getIChatBaseComponentClass()).read(0);
|
||||
return WrappedChatComponent.fromHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Styled extends WrappedNumberFormat {
|
||||
private final StructureModifier<Object> modifier;
|
||||
|
||||
private Styled(Object handle) {
|
||||
super(MinecraftReflection.getStyledFormatClass().get());
|
||||
setHandle(handle);
|
||||
this.modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
|
||||
}
|
||||
|
||||
public WrappedComponentStyle getStyle() {
|
||||
Object handle = modifier.withType(MinecraftReflection.getComponentStyleClass()).read(0);
|
||||
return new WrappedComponentStyle(handle);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue