mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-24 19:46:33 +01:00
Use socket as key instead of input stream.
This commit is contained in:
parent
56807cbd3a
commit
c32d225ef3
@ -284,7 +284,6 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
|||||||
|
|
||||||
// Unsafe variant of the above
|
// Unsafe variant of the above
|
||||||
private PlayerInjector injectPlayerInternal(Player player, Object injectionPoint, GamePhase phase) {
|
private PlayerInjector injectPlayerInternal(Player player, Object injectionPoint, GamePhase phase) {
|
||||||
|
|
||||||
PlayerInjector injector = playerInjection.get(player);
|
PlayerInjector injector = playerInjection.get(player);
|
||||||
PlayerInjectHooks tempHook = getPlayerHook(phase);
|
PlayerInjectHooks tempHook = getPlayerHook(phase);
|
||||||
PlayerInjectHooks permanentHook = tempHook;
|
PlayerInjectHooks permanentHook = tempHook;
|
||||||
@ -310,21 +309,24 @@ class ProxyPlayerInjectionHandler implements PlayerInjectionHandler {
|
|||||||
if (injector.canInject(phase)) {
|
if (injector.canInject(phase)) {
|
||||||
injector.initialize(injectionPoint);
|
injector.initialize(injectionPoint);
|
||||||
|
|
||||||
DataInputStream inputStream = injector.getInputStream(false);
|
// Get socket and socket injector
|
||||||
Socket socket = injector.getSocket();
|
Socket socket = injector.getSocket();
|
||||||
|
SocketInjector previous = null;
|
||||||
|
|
||||||
// Guard against NPE here too
|
// Due to a race condition, the main server "accept connections" thread may
|
||||||
SocketInjector previous = socket != null ? inputStreamLookup.getSocketInjector(socket) : null;
|
// get a closed network manager with a NULL input stream,
|
||||||
|
if (socket == null) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Close any previously associated hooks before we proceed
|
// Close any previously associated hooks before we proceed
|
||||||
if (previous != null) {
|
if (previous != null && previous instanceof PlayerInjector) {
|
||||||
uninjectPlayer(previous.getPlayer(), true);
|
uninjectPlayer(previous.getPlayer(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
injector.injectManager();
|
injector.injectManager();
|
||||||
|
|
||||||
// Save injector
|
// Save injector
|
||||||
inputStreamLookup.setSocketInjector(inputStream, injector);
|
inputStreamLookup.setSocketInjector(socket, injector);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@ import java.io.InputStream;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
@ -14,15 +12,10 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
import com.google.common.collect.MapMaker;
|
|
||||||
|
|
||||||
public abstract class AbstractInputStreamLookup {
|
public abstract class AbstractInputStreamLookup {
|
||||||
// Used to access the inner input stream of a filtered input stream
|
// Used to access the inner input stream of a filtered input stream
|
||||||
private static Field filteredInputField;
|
private static Field filteredInputField;
|
||||||
|
|
||||||
// Using weak keys and values ensures that we will not hold up garbage collection
|
|
||||||
protected ConcurrentMap<InputStream, SocketInjector> ownerSocket = new MapMaker().weakKeys().makeMap();
|
|
||||||
protected ConcurrentMap<SocketAddress, InputStream> addressLookup = new MapMaker().weakValues().makeMap();
|
|
||||||
|
|
||||||
// Error reporter
|
// Error reporter
|
||||||
protected final ErrorReporter reporter;
|
protected final ErrorReporter reporter;
|
||||||
@ -82,63 +75,45 @@ public abstract class AbstractInputStreamLookup {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the associated socket injector for a player.
|
* Retrieve the associated socket injector for a player.
|
||||||
* @param filtered - the indentifying filtered input stream.
|
* @param input - the indentifying filtered input stream.
|
||||||
* @return The socket injector we have associated with this player.
|
* @return The socket injector we have associated with this player.
|
||||||
*/
|
*/
|
||||||
public abstract SocketInjector getSocketInjector(InputStream input);
|
public abstract SocketInjector getSocketInjector(InputStream input);
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a injector by its address.
|
|
||||||
* @param address - the address of the socket.
|
|
||||||
* @return The socket injector.
|
|
||||||
*/
|
|
||||||
public abstract SocketInjector getSocketInjector(SocketAddress address);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve an injector by its socket.
|
* Retrieve an injector by its socket.
|
||||||
* @param socket - the socket.
|
* @param socket - the socket.
|
||||||
* @return The socket injector.
|
* @return The socket injector.
|
||||||
*/
|
*/
|
||||||
public SocketInjector getSocketInjector(Socket socket) {
|
public abstract SocketInjector getSocketInjector(Socket socket);
|
||||||
if (socket == null)
|
|
||||||
throw new IllegalArgumentException("The socket cannot be NULL.");
|
|
||||||
return getSocketInjector(socket.getRemoteSocketAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a given input stream with the provided socket injector.
|
* Retrieve a injector by its address.
|
||||||
* @param input - the filtered input stream to associate.
|
* @param address - the address of the socket.
|
||||||
* @param injector - the injector.
|
* @return The socket injector, or NULL if not found.
|
||||||
* @throws FieldAccessException Unable to access input stream.
|
|
||||||
*/
|
*/
|
||||||
public void setSocketInjector(FilterInputStream input, SocketInjector injector) {
|
public abstract SocketInjector getSocketInjector(SocketAddress address);
|
||||||
setSocketInjector(getInputStream(input), injector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a given input stream with the provided socket injector.
|
* Associate a given socket the provided socket injector.
|
||||||
* @param input - the input stream to associate.
|
* @param input - the socket to associate.
|
||||||
* @param injector - the injector.
|
* @param injector - the injector.
|
||||||
*/
|
*/
|
||||||
public void setSocketInjector(InputStream input, SocketInjector injector) {
|
public abstract void setSocketInjector(Socket socket, SocketInjector injector);
|
||||||
SocketInjector previous = ownerSocket.put(input, injector);
|
|
||||||
|
/**
|
||||||
// Any previous temporary players will also be associated
|
* If a player can hold a reference to its parent injector, this method will update that reference.
|
||||||
if (previous != null) {
|
* @param previous - the previous injector.
|
||||||
Player player = previous.getPlayer();
|
* @param current - the new injector.
|
||||||
|
*/
|
||||||
if (player instanceof InjectContainer) {
|
|
||||||
InjectContainer container = (InjectContainer) player;
|
|
||||||
container.setInjector(injector);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the reference to any previous injector
|
|
||||||
onPreviousSocketOverwritten(previous, injector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onPreviousSocketOverwritten(SocketInjector previous, SocketInjector current) {
|
protected void onPreviousSocketOverwritten(SocketInjector previous, SocketInjector current) {
|
||||||
// Do nothing
|
Player player = previous.getPlayer();
|
||||||
|
|
||||||
|
// Default implementation
|
||||||
|
if (player instanceof InjectContainer) {
|
||||||
|
InjectContainer container = (InjectContainer) player;
|
||||||
|
container.setInjector(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@ package com.comphenix.protocol.injector.server;
|
|||||||
* @author Kristian
|
* @author Kristian
|
||||||
*/
|
*/
|
||||||
class InjectContainer {
|
class InjectContainer {
|
||||||
private SocketInjector injector;
|
private volatile SocketInjector injector;
|
||||||
|
|
||||||
public SocketInjector getInjector() {
|
public SocketInjector getInjector() {
|
||||||
return injector;
|
return injector;
|
||||||
|
@ -12,6 +12,7 @@ import java.net.SocketAddress;
|
|||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -34,6 +35,11 @@ class InputStreamProxyLookup extends AbstractInputStreamLookup {
|
|||||||
private static final int READ_TIMEOUT = 5000;
|
private static final int READ_TIMEOUT = 5000;
|
||||||
private static final int CONNECT_TIMEOUT = 1000;
|
private static final int CONNECT_TIMEOUT = 1000;
|
||||||
|
|
||||||
|
// Using weak keys and values ensures that we will not hold up garbage collection
|
||||||
|
protected ConcurrentMap<InputStream, SocketInjector> ownerSocket = new MapMaker().weakKeys().makeMap();
|
||||||
|
protected ConcurrentMap<SocketAddress, InputStream> addressLookup = new MapMaker().weakValues().makeMap();
|
||||||
|
protected ConcurrentMap<Socket, InputStream> socketLookup = new MapMaker().weakKeys().makeMap();
|
||||||
|
|
||||||
// Fake connections
|
// Fake connections
|
||||||
private Set<SocketAddress> fakeConnections = Collections.newSetFromMap(
|
private Set<SocketAddress> fakeConnections = Collections.newSetFromMap(
|
||||||
new MapMaker().weakKeys().<SocketAddress, Boolean>makeMap()
|
new MapMaker().weakKeys().<SocketAddress, Boolean>makeMap()
|
||||||
@ -89,7 +95,7 @@ class InputStreamProxyLookup extends AbstractInputStreamLookup {
|
|||||||
if (address != null) {
|
if (address != null) {
|
||||||
InputStream previousStream = addressLookup.
|
InputStream previousStream = addressLookup.
|
||||||
putIfAbsent(delegate.getRemoteSocketAddress(), input);
|
putIfAbsent(delegate.getRemoteSocketAddress(), input);
|
||||||
|
|
||||||
// Ensure that this is our first time
|
// Ensure that this is our first time
|
||||||
if (previousStream == null) {
|
if (previousStream == null) {
|
||||||
// Create a new temporary player
|
// Create a new temporary player
|
||||||
@ -99,6 +105,9 @@ class InputStreamProxyLookup extends AbstractInputStreamLookup {
|
|||||||
|
|
||||||
// Update it
|
// Update it
|
||||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, socketInjector);
|
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, socketInjector);
|
||||||
|
|
||||||
|
// Socket lookup
|
||||||
|
socketLookup.put(this, input);
|
||||||
|
|
||||||
// Associate the socket with a given input stream
|
// Associate the socket with a given input stream
|
||||||
setSocketInjector(input, socketInjector);
|
setSocketInjector(input, socketInjector);
|
||||||
@ -115,6 +124,54 @@ class InputStreamProxyLookup extends AbstractInputStreamLookup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketInjector getSocketInjector(Socket socket) {
|
||||||
|
InputStream stream = getStream(socket);
|
||||||
|
|
||||||
|
if (stream != null)
|
||||||
|
return getSocketInjector(stream);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSocketInjector(Socket socket, SocketInjector injector) {
|
||||||
|
InputStream stream = getStream(socket);
|
||||||
|
|
||||||
|
if (stream != null) {
|
||||||
|
socketLookup.put(socket, stream);
|
||||||
|
setSocketInjector(stream, injector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the referenced socket injector by input stream.
|
||||||
|
* @param stream - the input stream.
|
||||||
|
* @param injector - the injector to reference.
|
||||||
|
*/
|
||||||
|
public void setSocketInjector(InputStream stream, SocketInjector injector) {
|
||||||
|
SocketInjector previous = ownerSocket.put(stream, injector);
|
||||||
|
|
||||||
|
// Handle overwrite
|
||||||
|
if (previous != null) {
|
||||||
|
onPreviousSocketOverwritten(previous, injector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getStream(Socket socket) {
|
||||||
|
InputStream result = socketLookup.get(socket);
|
||||||
|
|
||||||
|
// Use the socket as well
|
||||||
|
if (result == null) {
|
||||||
|
try {
|
||||||
|
result = socket.getInputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Unable to retrieve input stream from socket " + socket, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketInjector getSocketInjector(InputStream input) {
|
public SocketInjector getSocketInjector(InputStream input) {
|
||||||
return ownerSocket.get(input);
|
return ownerSocket.get(input);
|
||||||
@ -213,6 +270,9 @@ class InputStreamProxyLookup extends AbstractInputStreamLookup {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreviousSocketOverwritten(SocketInjector previous, SocketInjector current) {
|
protected void onPreviousSocketOverwritten(SocketInjector previous, SocketInjector current) {
|
||||||
|
// Don't forget this
|
||||||
|
super.onPreviousSocketOverwritten(previous, current);
|
||||||
|
|
||||||
if (previous instanceof DelegatedSocketInjector) {
|
if (previous instanceof DelegatedSocketInjector) {
|
||||||
DelegatedSocketInjector delegated = (DelegatedSocketInjector) previous;
|
DelegatedSocketInjector delegated = (DelegatedSocketInjector) previous;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import java.io.InputStream;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import org.bukkit.Server;
|
import org.bukkit.Server;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -13,8 +14,14 @@ import com.comphenix.protocol.error.ErrorReporter;
|
|||||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||||
import com.comphenix.protocol.reflect.FieldUtils;
|
import com.comphenix.protocol.reflect.FieldUtils;
|
||||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
|
||||||
class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
||||||
|
// Using weak keys and values ensures that we will not hold up garbage collection
|
||||||
|
protected ConcurrentMap<Socket, SocketInjector> ownerSocket = new MapMaker().weakKeys().makeMap();
|
||||||
|
protected ConcurrentMap<SocketAddress, Socket> addressLookup = new MapMaker().weakValues().makeMap();
|
||||||
|
protected ConcurrentMap<InputStream, Socket> inputLookup = new MapMaker().weakValues().makeMap();
|
||||||
|
|
||||||
// Used to create fake players
|
// Used to create fake players
|
||||||
private TemporaryPlayerFactory tempPlayerFactory = new TemporaryPlayerFactory();
|
private TemporaryPlayerFactory tempPlayerFactory = new TemporaryPlayerFactory();
|
||||||
|
|
||||||
@ -33,39 +40,86 @@ class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketInjector getSocketInjector(InputStream input) {
|
public SocketInjector getSocketInjector(Socket socket) {
|
||||||
SocketInjector injector = ownerSocket.get(input);
|
SocketInjector result = ownerSocket.get(socket);
|
||||||
|
|
||||||
if (injector != null) {
|
if (result == null) {
|
||||||
return injector;
|
Player player = tempPlayerFactory.createTemporaryPlayer(server);
|
||||||
} else {
|
SocketInjector created = new TemporarySocketInjector(player, socket);
|
||||||
try {
|
|
||||||
Socket socket = getSocket(input);
|
result = ownerSocket.putIfAbsent(socket, created);
|
||||||
Player player = tempPlayerFactory.createTemporaryPlayer(server);
|
|
||||||
SocketInjector created = new TemporarySocketInjector(player, socket);
|
|
||||||
|
|
||||||
// Update injector
|
|
||||||
TemporaryPlayerFactory.setInjectorInPlayer(player, created);
|
|
||||||
|
|
||||||
// Save address too
|
|
||||||
addressLookup.put(socket.getRemoteSocketAddress(), input);
|
|
||||||
|
|
||||||
// Associate the socket with a given input stream
|
|
||||||
setSocketInjector(input, created);
|
|
||||||
return created;
|
|
||||||
|
|
||||||
} catch (IllegalAccessException e) {
|
if (result == null) {
|
||||||
throw new FieldAccessException("Cannot find or access socket field for " + input, e);
|
// We won - use our created injector
|
||||||
|
TemporaryPlayerFactory.setInjectorInPlayer(player, created);
|
||||||
|
result = created;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SocketInjector getSocketInjector(InputStream input) {
|
||||||
|
try {
|
||||||
|
Socket socket = getSocket(input);
|
||||||
|
|
||||||
|
// Guard against NPE
|
||||||
|
if (socket != null)
|
||||||
|
return getSocketInjector(socket);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new FieldAccessException("Cannot find or access socket field for " + input, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use reflection to get the underlying socket from an input stream.
|
||||||
|
* @param stream - the socket stream to lookup.
|
||||||
|
* @return The underlying socket, or NULL if not found.
|
||||||
|
* @throws IllegalAccessException Unable to access socket field.
|
||||||
|
*/
|
||||||
|
private Socket getSocket(InputStream stream) throws IllegalAccessException {
|
||||||
|
// Extra check, just in case
|
||||||
|
if (stream instanceof FilterInputStream)
|
||||||
|
return getSocket(getInputStream((FilterInputStream) stream));
|
||||||
|
|
||||||
|
Socket result = inputLookup.get(stream);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
result = lookupSocket(stream);
|
||||||
|
|
||||||
|
// Save it
|
||||||
|
inputLookup.put(stream, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSocketInjector(Socket socket, SocketInjector injector) {
|
||||||
|
if (socket == null)
|
||||||
|
throw new IllegalArgumentException("socket cannot be NULL");
|
||||||
|
if (injector == null)
|
||||||
|
throw new IllegalArgumentException("injector cannot be NULL.");
|
||||||
|
|
||||||
|
SocketInjector previous = ownerSocket.put(socket, injector);
|
||||||
|
|
||||||
|
// Save the address lookup too
|
||||||
|
addressLookup.put(socket.getRemoteSocketAddress(), socket);
|
||||||
|
|
||||||
|
// Any previous temporary players will also be associated
|
||||||
|
if (previous != null) {
|
||||||
|
// Update the reference to any previous injector
|
||||||
|
onPreviousSocketOverwritten(previous, injector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketInjector getSocketInjector(SocketAddress address) {
|
public SocketInjector getSocketInjector(SocketAddress address) {
|
||||||
InputStream input = addressLookup.get(address);
|
Socket socket = addressLookup.get(address);
|
||||||
|
|
||||||
if (input != null)
|
if (socket != null)
|
||||||
return getSocketInjector(input);
|
return getSocketInjector(socket);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -75,9 +129,15 @@ class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Socket getSocket(InputStream stream) throws IllegalAccessException {
|
/**
|
||||||
|
* Lookup the underlying socket of a stream through reflection.
|
||||||
|
* @param stream - the socket stream.
|
||||||
|
* @return The underlying socket.
|
||||||
|
* @throws IllegalAccessException If reflection failed.
|
||||||
|
*/
|
||||||
|
private static Socket lookupSocket(InputStream stream) throws IllegalAccessException {
|
||||||
if (stream instanceof FilterInputStream) {
|
if (stream instanceof FilterInputStream) {
|
||||||
return getSocket(getInputStream((FilterInputStream) stream));
|
return lookupSocket(getInputStream((FilterInputStream) stream));
|
||||||
} else {
|
} else {
|
||||||
// Just do it
|
// Just do it
|
||||||
Field socketField = FuzzyReflection.fromObject(stream, true).
|
Field socketField = FuzzyReflection.fromObject(stream, true).
|
||||||
|
Loading…
Reference in New Issue
Block a user