Add modifiers for SoundCategory and MobEffectList

Fixes #156
This commit is contained in:
Dan Mulloy 2016-03-19 22:30:01 -04:00
parent 13b905e762
commit 0e6a7a39a0
6 changed files with 153 additions and 22 deletions

View File

@ -46,6 +46,7 @@ import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import com.comphenix.protocol.PacketType;
@ -86,6 +87,7 @@ import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
import com.comphenix.protocol.wrappers.EnumWrappers.ResourcePackStatus;
import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
import com.comphenix.protocol.wrappers.EnumWrappers.WorldBorderAction;
import com.comphenix.protocol.wrappers.MultiBlockChangeInfo;
@ -578,12 +580,11 @@ public class PacketContainer implements Serializable {
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getWatchableObjectClass(),
MinecraftReflection.getDataWatcherItemClass(),
BukkitConverters.getWatchableObjectConverter())
);
}
/**
* Retrieves a read/write structure for block fields.
* <p>
@ -845,6 +846,26 @@ public class PacketContainer implements Serializable {
EnumWrappers.getParticleClass(), EnumWrappers.getParticleConverter());
}
/**
* Retrieve a read/write structure for the MobEffectList class in 1.9.
* @return A modifier for MobEffectList fields.
*/
public StructureModifier<PotionEffectType> getEffectTypes() {
// Convert to and from Bukkit
return structureModifier.<PotionEffectType>withType(
MinecraftReflection.getMobEffectListClass(), BukkitConverters.getEffectTypeConverter());
}
/**
* Retrieve a read/write structure for the SoundCategory enum in 1.9.
* @return A modifier for SoundCategory enum fields.
*/
public StructureModifier<SoundCategory> getSoundCategories() {
// Convert to and from the enums
return structureModifier.<SoundCategory>withType(
EnumWrappers.getSoundCategoryClass(), EnumWrappers.getSoundCategoryConverter());
}
/**
* Retrieves the ID of this packet.
* <p>

View File

@ -1341,6 +1341,11 @@ public class MinecraftReflection {
return getMinecraftClass("MinecraftKey");
}
public static Class<?> getMobEffectListClass() {
// TODO Implement a fallback
return getMinecraftClass("MobEffectList");
}
/**
* Retrieve the ServerConnection abstract class.
* @return The ServerConnection class.

View File

@ -1105,7 +1105,7 @@ public class BukkitConverters {
put(MinecraftReflection.getItemStackClass(), (EquivalentConverter) getItemStackConverter()).
put(MinecraftReflection.getNBTBaseClass(), (EquivalentConverter) getNbtConverter()).
put(MinecraftReflection.getNBTCompoundClass(), (EquivalentConverter) getNbtConverter()).
put(MinecraftReflection.getWatchableObjectClass(), (EquivalentConverter) getWatchableObjectConverter()).
put(MinecraftReflection.getDataWatcherItemClass(), (EquivalentConverter) getWatchableObjectConverter()).
put(MinecraftReflection.getMobEffectClass(), (EquivalentConverter) getPotionEffectConverter()).
put(MinecraftReflection.getNmsWorldClass(), (EquivalentConverter) getWorldConverter());
@ -1146,4 +1146,38 @@ public class BukkitConverters {
}
return unwrappers;
}
private static MethodAccessor getMobEffectId = null;
private static MethodAccessor getMobEffect = null;
public static EquivalentConverter<PotionEffectType> getEffectTypeConverter() {
return new IgnoreNullConverter<PotionEffectType>() {
@Override
public Class<PotionEffectType> getSpecificType() {
return PotionEffectType.class;
}
@Override
protected Object getGenericValue(Class<?> genericType, PotionEffectType specific) {
if (getMobEffect == null) {
getMobEffect = Accessors.getMethodAccessor(genericType, "fromId", int.class);
}
int id = specific.getId();
return getMobEffect.invoke(null, id);
}
@Override
protected PotionEffectType getSpecificValue(Object generic) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
if (getMobEffectId == null) {
getMobEffectId = Accessors.getMethodAccessor(clazz, "getId", clazz);
}
int id = (int) getMobEffectId.invoke(null, generic);
return PotionEffectType.getById(id);
}
};
}
}

View File

@ -272,6 +272,40 @@ public abstract class EnumWrappers {
}
}
public enum SoundCategory {
MASTER("master"),
MUSIC("music"),
RECORDS("record"),
WEATHER("weather"),
BLOCKS("block"),
HOSTILE("hostile"),
NEUTRAL("neutral"),
PLAYERS("player"),
AMBIENT("ambient"),
VOICE("voice");
private static final Map<String, SoundCategory> LOOKUP;
static {
LOOKUP = new HashMap<>();
for (SoundCategory category : values()) {
LOOKUP.put(category.key, category);
}
}
private final String key;
private SoundCategory(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public static SoundCategory getByKey(String key) {
return LOOKUP.get(key.toLowerCase());
}
}
private static Class<?> PROTOCOL_CLASS = null;
private static Class<?> CLIENT_COMMAND_CLASS = null;
private static Class<?> CHAT_VISIBILITY_CLASS = null;
@ -287,6 +321,7 @@ public abstract class EnumWrappers {
private static Class<?> PLAYER_ACTION_CLASS = null;
private static Class<?> SCOREBOARD_ACTION_CLASS = null;
private static Class<?> PARTICLE_CLASS = null;
private static Class<?> SOUND_CATEGORY_CLASS = null;
private static boolean INITIALIZED = false;
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = Maps.newHashMap();
@ -319,6 +354,7 @@ public abstract class EnumWrappers {
PLAYER_ACTION_CLASS = getEnum(PacketType.Play.Client.ENTITY_ACTION.getPacketClass(), 0);
SCOREBOARD_ACTION_CLASS = getEnum(PacketType.Play.Server.SCOREBOARD_SCORE.getPacketClass(), 0);
PARTICLE_CLASS = getEnum(PacketType.Play.Server.WORLD_PARTICLES.getPacketClass(), 0);
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.CUSTOM_SOUND_EFFECT.getPacketClass(), 0);
associate(PROTOCOL_CLASS, Protocol.class, getClientCommandConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
@ -335,6 +371,7 @@ public abstract class EnumWrappers {
associate(PLAYER_ACTION_CLASS, PlayerAction.class, getEntityActionConverter());
associate(SCOREBOARD_ACTION_CLASS, ScoreboardAction.class, getUpdateScoreActionConverter());
associate(PARTICLE_CLASS, Particle.class, getParticleConverter());
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
INITIALIZED = true;
}
@ -443,6 +480,11 @@ public abstract class EnumWrappers {
return PARTICLE_CLASS;
}
public static Class<?> getSoundCategoryClass() {
initialize();
return SOUND_CATEGORY_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<Protocol>(Protocol.class);
@ -504,6 +546,10 @@ public abstract class EnumWrappers {
return new EnumConverter<Particle>(Particle.class);
}
public static EquivalentConverter<SoundCategory> getSoundCategoryConverter() {
return new EnumConverter<SoundCategory>(SoundCategory.class);
}
/**
* Retrieve a generic enum converter for use with StructureModifiers.
* @param enumClass - Enum class

View File

@ -25,37 +25,61 @@ import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.google.common.base.Objects;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
* @author dmulloy2
*/
public class PlayerInfoData {
private static Constructor<?> constructor;
protected final WrappedGameProfile profile;
protected final int ping;
protected final NativeGameMode gameMode;
protected final WrappedChatComponent displayName;
private final int latency;
private final NativeGameMode gameMode;
private final WrappedGameProfile profile;
private final WrappedChatComponent displayName;
// This is the same order as the NMS class, minus the packet (which isn't a field)
public PlayerInfoData(WrappedGameProfile profile, int ping, NativeGameMode gameMode, WrappedChatComponent displayName) {
this.ping = ping;
this.gameMode = gameMode;
public PlayerInfoData(WrappedGameProfile profile, int latency, NativeGameMode gameMode, WrappedChatComponent displayName) {
this.profile = profile;
this.latency = latency;
this.gameMode = gameMode;
this.displayName = displayName;
}
/**
* Gets the GameProfile of the player represented by this data.
* @return The GameProfile
*/
public WrappedGameProfile getProfile() {
return profile;
}
/**
* @deprecated Replaced by {@link #getLatency()}
*/
@Deprecated
public int getPing() {
return ping;
return latency;
}
/**
* Gets the latency between the client and the server.
* @return The latency
*/
public int getLatency() {
return latency;
}
/**
* Gets the GameMode of the player represented by this data.
* @return The GameMode
*/
public NativeGameMode getGameMode() {
return gameMode;
}
/**
* Gets the display name of the player represented by this data.
* @return The display name
*/
public WrappedChatComponent getDisplayName() {
return displayName;
}
@ -91,7 +115,7 @@ public class PlayerInfoData {
Object result = constructor.newInstance(
null,
specific.profile.handle,
specific.ping,
specific.latency,
EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.getGameModeClass(), specific.gameMode),
specific.displayName != null ? specific.displayName.handle : null
);
@ -112,7 +136,7 @@ public class PlayerInfoData {
WrappedGameProfile gameProfile = gameProfiles.read(0);
StructureModifier<Integer> ints = modifier.withType(int.class);
int ping = ints.read(0);
int latency = ints.read(0);
StructureModifier<NativeGameMode> gameModes = modifier.withType(
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
@ -122,7 +146,7 @@ public class PlayerInfoData {
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
WrappedChatComponent displayName = displayNames.read(0);
return new PlayerInfoData(gameProfile, ping, gameMode, displayName);
return new PlayerInfoData(gameProfile, latency, gameMode, displayName);
}
// Otherwise, return null
@ -146,7 +170,7 @@ public class PlayerInfoData {
// Only compare objects of similar type
if (obj instanceof PlayerInfoData) {
PlayerInfoData other = (PlayerInfoData) obj;
return profile.equals(other.profile) && ping == other.ping && gameMode == other.gameMode
return profile.equals(other.profile) && latency == other.latency && gameMode == other.gameMode
&& displayName.equals(other.displayName);
}
return false;
@ -154,12 +178,12 @@ public class PlayerInfoData {
@Override
public int hashCode() {
return Objects.hashCode(profile, ping, gameMode, displayName);
return Objects.hashCode(latency, gameMode, profile, displayName);
}
@Override
public String toString() {
return String.format("PlayerInfoData { profile=%s, ping=%s, gameMode=%s, displayName=%s }",
profile, ping, gameMode, displayName);
return String.format("PlayerInfoData[latency=%s, gameMode=%s, profile=%s, displayName=%s",
latency, gameMode, profile, displayName);
}
}

View File

@ -33,7 +33,7 @@ public class SimpleMinecraftClient {
/**
* Query the local server for ping information.
* @return The server information.
* @throws IOException
* @throws IOException
*/
public String queryLocalPing() throws IOException {
return queryServerPing(new InetSocketAddress("localhost", 25565));
@ -173,7 +173,7 @@ public class SimpleMinecraftClient {
}
}
private static class HandshakePacket extends SimplePacket {
private static class HandshakePacket extends SimplePacket {
private int protocol;
private String host;
private int port;
@ -209,6 +209,7 @@ public class SimpleMinecraftClient {
serializer.serializeVarInt(output, type.getCurrentId());
}
@SuppressWarnings("unused")
public void read(PacketType type, DataInputStream input) throws IOException {
// Note - we don't read the packet id
if (this.type != type) {