Use ProtocolLib as a soft dependency

This commit is contained in:
games647
2016-06-14 17:21:46 +02:00
parent d90e3fdb44
commit 92c9ab5b76
13 changed files with 359 additions and 32 deletions

View File

@ -1,3 +1,7 @@
######1.6
* Removed ProtocolLib as required dependency. You can use ProtocolSupport or BungeeCord as alternative
######1.5.2
* Fixed BungeeCord force logins if there is a lobby server

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.5.2</version>
<version>1.6</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1,6 +1,5 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.fastlogin.core.LoginSession;
import com.github.games647.fastlogin.core.PlayerProfile;
@ -19,9 +18,11 @@ public class BukkitLoginSession extends LoginSession {
private final byte[] verifyToken;
private UUID uuid;
private WrappedSignedProperty skinProperty;
private boolean verified;
private String encodedSkinData;
private String skinSignature;
public BukkitLoginSession(String username, String serverId, byte[] verifyToken, boolean registered
, PlayerProfile profile) {
super(username, registered, profile);
@ -64,22 +65,23 @@ public class BukkitLoginSession extends LoginSession {
return ArrayUtils.clone(verifyToken);
}
/**
* Gets the premium skin of this player
*
* @return skin property or null if the player has no skin or is a cracked account
*/
public synchronized WrappedSignedProperty getSkin() {
return this.skinProperty;
public synchronized String getEncodedSkinData() {
return encodedSkinData;
}
public synchronized String getSkinSignature() {
return skinSignature;
}
/**
* Sets the premium skin property which was retrieved by the session server
*
* @param skinProperty premium skin property
* @param encodedData
* @param skinSignature
*/
public synchronized void setSkin(WrappedSignedProperty skinProperty) {
this.skinProperty = skinProperty;
public synchronized void setSkin(String encodedData, String skinSignature) {
this.encodedSkinData = encodedData;
this.skinSignature = skinSignature;
}
/**

View File

@ -0,0 +1,320 @@
package com.github.games647.fastlogin.bukkit;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Ticker;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.RemovalListener;
/**
* Represents a Guava CacheBuilder that is compatible with both Guava 10 and 13
*/
public class CacheBuilder<K, V> {
private CacheBuilder<K, V> builder;
private static Method BUILD_METHOD;
private static Method AS_MAP_METHOD;
@SuppressWarnings("unchecked")
private CacheBuilder() {
builder = (CacheBuilder<K, V>) CacheBuilder.newBuilder();
}
/**
* Construct a new safe cache builder.
*
* @param <K> Key type
* @param <V> Value type
*
* @return A new cache builder.
*/
public static <K, V> CacheBuilder<K, V> newBuilder() {
return new CacheBuilder<K, V>();
}
/**
* Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The table is
* internally partitioned to try to permit the indicated number of concurrent updates without contention. Because
* assignment of entries to these partitions is not necessarily uniform, the actual concurrency observed may vary.
* Ideally, you should choose a value to accommodate as many threads as will ever concurrently modify the table.
* Using a significantly higher value than you need can waste space and time, and a significantly lower value can
* lead to thread contention. But overestimates and underestimates within an order of magnitude do not usually have
* much noticeable impact. A value of one permits only one thread to modify the cache at a time, but since read
* operations can proceed concurrently, this still yields higher concurrency than full synchronization. Defaults to
* 4.
*
* <p>
* <b>Note:</b>The default may change in the future. If you care about this value, you should always choose it
* explicitly.
*
* @param concurrencyLevel New concurrency level
* @return This for chaining
*
* @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive
* @throws IllegalStateException if a concurrency level was already set
*/
public CacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) {
builder.concurrencyLevel(concurrencyLevel);
return this;
}
/**
* Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after
* the entry's creation, or last access. Access time is reset by
* {@link com.google.common.cache.Cache#get Cache.get()}, but not by operations on the view returned by
* {@link com.google.common.cache.Cache#asMap() Cache.asMap()}.
*
* <p>
* When {@code duration} is zero, elements will be evicted immediately after being loaded into the cache. This has
* the same effect as invoking {@link #maximumSize maximumSize}{@code (0)}. It can be useful in testing, or to
* disable caching temporarily without a code change.
*
* <p>
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
* occasional read operations in the absense of writes; though this behavior may change in the future.
*
* @param duration the length of time after an entry is last accessed that it should be automatically removed
* @param unit the unit that {@code duration} is expressed in
* @return This for chaining
*
* @throws IllegalArgumentException if {@code duration} is negative
* @throws IllegalStateException if the time to idle or time to live was already set
*/
public CacheBuilder<K, V> expireAfterAccess(long duration, TimeUnit unit) {
builder.expireAfterAccess(duration, unit);
return this;
}
/**
* Specifies that each entry should be automatically removed from the cache once a fixed duration has elapsed after
* the entry's creation, or the most recent replacement of its value.
*
* <p>
* When {@code duration} is zero, elements will be evicted immediately after being loaded into the cache. This has
* the same effect as invoking {@link #maximumSize maximumSize}{@code (0)}. It can be useful in testing, or to
* disable caching temporarily without a code change.
*
* <p>
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
* occasional read operations in the absense of writes; though this behavior may change in the future.
*
* @param duration the length of time after an entry is created that it should be automatically removed
* @param unit the unit that {@code duration} is expressed in
* @return This for chaining
*
* @throws IllegalArgumentException if {@code duration} is negative
* @throws IllegalStateException if the time to live or time to idle was already set
*/
public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) {
builder.expireAfterWrite(duration, unit);
return this;
}
/**
* Sets the minimum total size for the internal hash tables. For example, if the initial capacity is {@code 60}, and
* the concurrency level is {@code 8}, then eight segments are created, each having a hash table of size eight.
* Providing a large enough estimate at construction time avoids the need for expensive resizing operations later,
* but setting this value unnecessarily high wastes memory.
*
* @param initialCapacity - initial capacity
* @return This for chaining
*
* @throws IllegalArgumentException if {@code initialCapacity} is negative
* @throws IllegalStateException if an initial capacity was already set
*/
public CacheBuilder<K, V> initialCapacity(int initialCapacity) {
builder.initialCapacity(initialCapacity);
return this;
}
/**
* Specifies the maximum number of entries the cache may contain. Note that the cache <b>may evict an entry before
* this limit is exceeded</b>. As the cache size grows close to the maximum, the cache evicts entries that are less
* likely to be used again. For example, the cache may evict an entry because it hasn't been used recently or very
* often.
*
* <p>
* When {@code size} is zero, elements will be evicted immediately after being loaded into the cache. This has the
* same effect as invoking {@link #expireAfterWrite expireAfterWrite}{@code (0, unit)} or {@link #expireAfterAccess expireAfterAccess}{@code (0,
* unit)}. It can be useful in testing, or to disable caching temporarily without a code change.
*
* @param size the maximum size of the cache
* @return This for chaining
*
* @throws IllegalArgumentException if {@code size} is negative
* @throws IllegalStateException if a maximum size was already set
*/
public CacheBuilder<K, V> maximumSize(int size) {
builder.maximumSize(size);
return this;
}
/**
* Specifies a listener instance, which all caches built using this {@code CacheBuilder} will notify each time an
* entry is removed from the cache by any means.
*
* <p>
* Each cache built by this {@code CacheBuilder} after this method is called invokes the supplied listener after
* removing an element for any reason (see removal causes in
* {@link com.google.common.cache.RemovalCause RemovalCause}). It will invoke the listener during invocations of any
* of that cache's public methods (even read-only methods).
*
* <p>
* <b>Important note:</b> Instead of returning <em>this</em> as a {@code CacheBuilder} instance, this method returns
* {@code CacheBuilder<K1, V1>}. From this point on, either the original reference or the returned reference may be
* used to complete configuration and build the cache, but only the "generic" one is type-safe. That is, it will
* properly prevent you from building caches whose key or value types are incompatible with the types accepted by
* the listener already provided; the {@code CacheBuilder} type cannot do this. For best results, simply use the
* standard method-chaining idiom, as illustrated in the documentation at top, configuring a {@code CacheBuilder}
* and building your {@link com.google.common.cache.Cache Cache} all in a single statement.
*
* <p>
* <b>Warning:</b> if you ignore the above advice, and use this {@code CacheBuilder} to build a cache whose key or
* value type is incompatible with the listener, you will likely experience a {@link ClassCastException} at some
* <i>undefined</i> point in the future.
*
* @param <K1> Key type
* @param <V1> Value type
* @param listener - removal listener
* @return This for chaining
*
* @throws IllegalStateException if a removal listener was already set
*/
@SuppressWarnings("unchecked")
public <K1 extends K, V1 extends V> CacheBuilder<K1, V1> removalListener(RemovalListener<? super K1, ? super V1> listener) {
builder.removalListener(listener);
return (CacheBuilder<K1, V1>) this;
}
/**
* Specifies a nanosecond-precision time source for use in determining when entries should be expired. By default,
* {@link System#nanoTime} is used.
*
* <p>
* The primary intent of this method is to facilitate testing of caches which have been configured with
* {@link #expireAfterWrite} or {@link #expireAfterAccess}.
*
* @param ticker - ticker
* @return This for chaining
*
* @throws IllegalStateException if a ticker was already set
*/
public CacheBuilder<K, V> ticker(Ticker ticker) {
builder.ticker(ticker);
return this;
}
/**
* Specifies that each value (not key) stored in the cache should be wrapped in a
* {@link java.lang.ref.SoftReference SoftReference} (by default, strong references are used). Softly-referenced
* objects will be garbage-collected in a <i>globally</i>
* least-recently-used manner, in response to memory demand.
*
* <p>
* <b>Warning:</b> in most circumstances it is better to set a per-cache {@linkplain #maximumSize maximum size}
* instead of using soft references. You should only use this method if you are well familiar with the practical
* consequences of soft references.
*
* <p>
* <b>Note:</b> when this method is used, the resulting cache will use identity ({@code ==}) comparison to determine
* equality of values.
*
* @return This for chaining
*
* @throws IllegalStateException if the value strength was already set
*/
public CacheBuilder<K, V> softValues() {
builder.softValues();
return this;
}
/**
* Specifies that each key (not value) stored in the cache should be wrapped in a
* {@link java.lang.ref.WeakReference WeakReference} (by default, strong references are used).
*
* <p>
* <b>Warning:</b> when this method is used, the resulting cache will use identity ({@code ==}) comparison to
* determine equality of keys.
*
* @return This for chaining
*
* @throws IllegalStateException if the key strength was already set
*/
public CacheBuilder<K, V> weakKeys() {
builder.weakKeys();
return this;
}
/**
* Specifies that each value (not key) stored in the cache should be wrapped in a
* {@link java.lang.ref.WeakReference WeakReference} (by default, strong references are used).
*
* <p>
* Weak values will be garbage collected once they are weakly reachable. This makes them a poor candidate for
* caching; consider {@link #softValues} instead.
*
* <p>
* <b>Note:</b> when this method is used, the resulting cache will use identity ({@code ==}) comparison to determine
* equality of values.
*
* @return This for chaining
*
* @throws IllegalStateException if the value strength was already set
*/
public CacheBuilder<K, V> weakValues() {
builder.weakValues();
return this;
}
/**
* Returns the cache wrapped as a ConcurrentMap.
* <p>
* We can't return the direct Cache instance as it changed in Guava 13.
*
* @param <K1> Key type
* @param <V1> Value type
* @param loader - cache loader
* @return The cache as a a map.
*/
@SuppressWarnings("unchecked")
public <K1 extends K, V1 extends V> ConcurrentMap<K1, V1> build(CacheLoader<? super K1, V1> loader) {
Object cache = null;
if (BUILD_METHOD == null) {
try {
BUILD_METHOD = builder.getClass().getDeclaredMethod("build", CacheLoader.class);
BUILD_METHOD.setAccessible(true);
} catch (Exception e) {
throw new IllegalStateException("Unable to find CacheBuilder.build(CacheLoader)", e);
}
}
// Attempt to build the Cache
try {
cache = BUILD_METHOD.invoke(builder, loader);
} catch (Exception e) {
throw new IllegalStateException("Unable to invoke " + BUILD_METHOD + " on " + builder, e);
}
if (AS_MAP_METHOD == null) {
try {
AS_MAP_METHOD = cache.getClass().getMethod("asMap");
AS_MAP_METHOD.setAccessible(true);
} catch (Exception e) {
throw new IllegalStateException("Unable to find Cache.asMap() in " + cache, e);
}
}
// Retrieve it as a map
try {
return (ConcurrentMap<K1, V1>) AS_MAP_METHOD.invoke(cache);
} catch (Exception e) {
throw new IllegalStateException("Unable to invoke " + AS_MAP_METHOD + " on " + cache, e);
}
}
}

View File

@ -5,7 +5,6 @@ import com.avaje.ebeaninternal.api.ClassUtil;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
@ -42,7 +41,7 @@ public class FastLoginBukkit extends JavaPlugin {
//this map is thread-safe for async access (Packet Listener)
//SafeCacheBuilder is used in order to be version independent
private final ConcurrentMap<String, BukkitLoginSession> session = SafeCacheBuilder.<String, BukkitLoginSession>newBuilder()
private final ConcurrentMap<String, BukkitLoginSession> session = CacheBuilder.<String, BukkitLoginSession>newBuilder()
//2 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
.expireAfterWrite(1, TimeUnit.MINUTES)
//mapped by ip:port -> PlayerSession
@ -104,7 +103,7 @@ public class FastLoginBukkit extends JavaPlugin {
if (getServer().getPluginManager().isPluginEnabled("ProtocolSupport")) {
getServer().getPluginManager().registerEvents(new ProtocolSupportListener(this), this);
} else {
} else if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
//we are performing HTTP request on these so run it async (seperate from the Netty IO threads)
@ -116,6 +115,9 @@ public class FastLoginBukkit extends JavaPlugin {
asynchronousManager.registerAsyncHandler(startPacketListener).start(WORKER_THREADS);
asynchronousManager.registerAsyncHandler(encryptionPacketListener).start(WORKER_THREADS);
getServer().getPluginManager().registerEvents(new LoginSkinApplyListener(this), this);
} else {
getLogger().warning("Either ProtocolLib or ProtocolSupport have to be installed "
+ "if you don't use BungeeCord");
}
}

View File

@ -1,6 +1,5 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.fastlogin.core.FastLoginCore;
import com.github.games647.fastlogin.core.MojangApiConnector;
@ -50,7 +49,7 @@ public class MojangApiBukkit extends MojangApiConnector {
if (propertyName.equals("textures")) {
String skinValue = (String) skinProperty.get("value");
String signature = (String) skinProperty.get("signature");
playerSession.setSkin(WrappedSignedProperty.fromValues(propertyName, skinValue, signature));
playerSession.setSkin(skinValue, signature);
}
return true;

View File

@ -1,7 +1,5 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.comphenix.protocol.reflect.FuzzyReflection;
import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.data.LoginPlayerData;
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
@ -13,6 +11,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.apache.commons.lang.reflect.FieldUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -108,7 +107,7 @@ public class CrazyLoginHook implements BukkitAuthPlugin {
private PlayerListener getListener() {
PlayerListener listener;
try {
listener = FuzzyReflection.getFieldValue(crazyLoginPlugin, PlayerListener.class, true);
listener = (PlayerListener) FieldUtils.readField(crazyLoginPlugin, "playerListener", true);
} catch (Exception ex) {
crazyLoginPlugin.getLogger().log(Level.SEVERE, "Failed to get the listener instance for auto login", ex);
listener = null;

View File

@ -19,15 +19,17 @@ public class LoginSkinApplyListener implements Listener {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST)
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
Player player = loginEvent.getPlayer();
BukkitLoginSession session = plugin.getSessions().get(player.getAddress().toString());
if (session != null && plugin.getConfig().getBoolean("forwardSkin")) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = session.getSkin();
if (skin != null) {
String skinData = session.getEncodedSkinData();
String signature = session.getSkinSignature();
if (skinData != null && signature != null) {
WrappedSignedProperty skin = WrappedSignedProperty.fromValues("textures", skinData, signature);
gameProfile.getProperties().put("textures", skin);
}
}

View File

@ -14,12 +14,11 @@ dev-url: ${project.url}
# Load the plugin as early as possible to inject it for all players
load: STARTUP
# Without Protocollib the plugin does not work at all
depend: [ProtocolLib]
softdepend:
# We require either ProtocolLib or ProtocolSupport
- ProtocolSupport
# Auth plugins
- ProtocolLib
# Auth plugins - we delay the hook
# - xAuth
# - AuthMe
# - LogIt

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.5.2</version>
<version>1.6</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.5.2</version>
<version>1.6</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -8,7 +8,7 @@
<packaging>pom</packaging>
<name>FastLogin</name>
<version>1.5.2</version>
<version>1.6</version>
<inceptionYear>2015</inceptionYear>
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
<description>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.5.2</version>
<version>1.6</version>
<relativePath>../pom.xml</relativePath>
</parent>