mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-12-25 10:38:19 +01:00
Makes more sense to put this in the reflect lookup.
This commit is contained in:
parent
bec05967d3
commit
8964246e22
@ -1,119 +1,87 @@
|
||||
package com.comphenix.protocol.injector.server;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
|
||||
public abstract class AbstractInputStreamLookup {
|
||||
// Used to access the inner input stream of a filtered input stream
|
||||
private static Field filteredInputField;
|
||||
|
||||
// Error reporter
|
||||
protected final ErrorReporter reporter;
|
||||
|
||||
// Reference to the server itself
|
||||
protected final Server server;
|
||||
|
||||
protected AbstractInputStreamLookup(ErrorReporter reporter, Server server) {
|
||||
this.reporter = reporter;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying input stream that is associated with a given filter input stream.
|
||||
* @param filtered - the filter input stream.
|
||||
* @return The underlying input stream that is being filtered.
|
||||
* @throws FieldAccessException Unable to access input stream.
|
||||
*/
|
||||
protected static InputStream getInputStream(FilterInputStream filtered) {
|
||||
if (filteredInputField == null)
|
||||
filteredInputField = FuzzyReflection.fromClass(FilterInputStream.class, true).
|
||||
getFieldByType("in", InputStream.class);
|
||||
|
||||
InputStream current = filtered;
|
||||
|
||||
try {
|
||||
// Iterate until we find the real input stream
|
||||
while (current instanceof FilterInputStream) {
|
||||
current = (InputStream) FieldUtils.readField(filteredInputField, current, true);
|
||||
}
|
||||
return current;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot access filtered input field.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the given server thread or dedicated connection.
|
||||
* @param container - class that contains a ServerSocket field.
|
||||
*/
|
||||
public abstract void inject(Object container);
|
||||
|
||||
/**
|
||||
* Invoked when the world has loaded.
|
||||
*/
|
||||
public abstract void postWorldLoaded();
|
||||
|
||||
/**
|
||||
* Retrieve the associated socket injector for a player.
|
||||
* @param input - the indentifying filtered input stream.
|
||||
* @return The socket injector we have associated with this player.
|
||||
*/
|
||||
public abstract SocketInjector waitSocketInjector(InputStream input);
|
||||
|
||||
/**
|
||||
* Retrieve an injector by its socket.
|
||||
* @param socket - the socket.
|
||||
* @return The socket injector.
|
||||
*/
|
||||
public abstract SocketInjector waitSocketInjector(Socket socket);
|
||||
|
||||
/**
|
||||
* Retrieve a injector by its address.
|
||||
* @param address - the address of the socket.
|
||||
* @return The socket injector, or NULL if not found.
|
||||
*/
|
||||
public abstract SocketInjector waitSocketInjector(SocketAddress address);
|
||||
|
||||
/**
|
||||
* Attempt to get a socket injector without blocking the thread.
|
||||
* @param address - the address to lookup.
|
||||
* @return The socket injector, or NULL if not found.
|
||||
*/
|
||||
public abstract SocketInjector peekSocketInjector(SocketAddress address);
|
||||
|
||||
/**
|
||||
* Associate a given socket address to the provided socket injector.
|
||||
* @param address - the socket address to associate.
|
||||
* @param injector - the injector.
|
||||
*/
|
||||
public abstract void setSocketInjector(SocketAddress address, SocketInjector injector);
|
||||
|
||||
/**
|
||||
* If a player can hold a reference to its parent injector, this method will update that reference.
|
||||
* @param previous - the previous injector.
|
||||
* @param current - the new injector.
|
||||
*/
|
||||
protected void onPreviousSocketOverwritten(SocketInjector previous, SocketInjector current) {
|
||||
Player player = previous.getPlayer();
|
||||
|
||||
// Default implementation
|
||||
if (player instanceof InjectorContainer) {
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(player, current);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the injection should be undone.
|
||||
*/
|
||||
public abstract void cleanupAll();
|
||||
package com.comphenix.protocol.injector.server;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
|
||||
public abstract class AbstractInputStreamLookup {
|
||||
// Error reporter
|
||||
protected final ErrorReporter reporter;
|
||||
|
||||
// Reference to the server itself
|
||||
protected final Server server;
|
||||
|
||||
protected AbstractInputStreamLookup(ErrorReporter reporter, Server server) {
|
||||
this.reporter = reporter;
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the given server thread or dedicated connection.
|
||||
* @param container - class that contains a ServerSocket field.
|
||||
*/
|
||||
public abstract void inject(Object container);
|
||||
|
||||
/**
|
||||
* Invoked when the world has loaded.
|
||||
*/
|
||||
public abstract void postWorldLoaded();
|
||||
|
||||
/**
|
||||
* Retrieve the associated socket injector for a player.
|
||||
* @param input - the indentifying filtered input stream.
|
||||
* @return The socket injector we have associated with this player.
|
||||
*/
|
||||
public abstract SocketInjector waitSocketInjector(InputStream input);
|
||||
|
||||
/**
|
||||
* Retrieve an injector by its socket.
|
||||
* @param socket - the socket.
|
||||
* @return The socket injector.
|
||||
*/
|
||||
public abstract SocketInjector waitSocketInjector(Socket socket);
|
||||
|
||||
/**
|
||||
* Retrieve a injector by its address.
|
||||
* @param address - the address of the socket.
|
||||
* @return The socket injector, or NULL if not found.
|
||||
*/
|
||||
public abstract SocketInjector waitSocketInjector(SocketAddress address);
|
||||
|
||||
/**
|
||||
* Attempt to get a socket injector without blocking the thread.
|
||||
* @param address - the address to lookup.
|
||||
* @return The socket injector, or NULL if not found.
|
||||
*/
|
||||
public abstract SocketInjector peekSocketInjector(SocketAddress address);
|
||||
|
||||
/**
|
||||
* Associate a given socket address to the provided socket injector.
|
||||
* @param address - the socket address to associate.
|
||||
* @param injector - the injector.
|
||||
*/
|
||||
public abstract void setSocketInjector(SocketAddress address, SocketInjector injector);
|
||||
|
||||
/**
|
||||
* If a player can hold a reference to its parent injector, this method will update that reference.
|
||||
* @param previous - the previous injector.
|
||||
* @param current - the new injector.
|
||||
*/
|
||||
protected void onPreviousSocketOverwritten(SocketInjector previous, SocketInjector current) {
|
||||
Player player = previous.getPlayer();
|
||||
|
||||
// Default implementation
|
||||
if (player instanceof InjectorContainer) {
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(player, current);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the injection should be undone.
|
||||
*/
|
||||
public abstract void cleanupAll();
|
||||
}
|
@ -1,164 +1,191 @@
|
||||
package com.comphenix.protocol.injector.server;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bukkit.Server;
|
||||
|
||||
import com.comphenix.protocol.concurrency.BlockingHashMap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
||||
// The default lookup timeout
|
||||
private static final long DEFAULT_TIMEOUT = 2000; // ms
|
||||
|
||||
// Using weak keys and values ensures that we will not hold up garbage collection
|
||||
protected BlockingHashMap<SocketAddress, SocketInjector> addressLookup = new BlockingHashMap<SocketAddress, SocketInjector>();
|
||||
protected ConcurrentMap<InputStream, SocketAddress> inputLookup = new MapMaker().weakValues().makeMap();
|
||||
|
||||
// The timeout
|
||||
private final long injectorTimeout;
|
||||
|
||||
public InputStreamReflectLookup(ErrorReporter reporter, Server server) {
|
||||
this(reporter, server, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a reflect lookup with a given default injector timeout.
|
||||
* <p>
|
||||
* This timeout defines the maximum amount of time to wait until an injector has been discovered.
|
||||
* @param reporter - the error reporter.
|
||||
* @param server - the current Bukkit server.
|
||||
* @param injectorTimeout - the injector timeout.
|
||||
*/
|
||||
public InputStreamReflectLookup(ErrorReporter reporter, Server server, long injectorTimeout) {
|
||||
super(reporter, server);
|
||||
this.injectorTimeout = injectorTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Object container) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postWorldLoaded() {
|
||||
// Nothing again
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector peekSocketInjector(SocketAddress address) {
|
||||
try {
|
||||
return addressLookup.get(address, 0, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// Whatever
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector waitSocketInjector(SocketAddress address) {
|
||||
try {
|
||||
// Note that we actually SWALLOW interrupts here - this is because Minecraft uses interrupts to
|
||||
// periodically wake up waiting readers and writers. We have to wait for the dedicated server thread
|
||||
// to catch up, so we'll swallow these interrupts.
|
||||
//
|
||||
// TODO: Consider if we should raise the thread priority of the dedicated server listener thread.
|
||||
return addressLookup.get(address, injectorTimeout, TimeUnit.MILLISECONDS, true);
|
||||
} catch (InterruptedException e) {
|
||||
// This cannot be!
|
||||
throw new IllegalStateException("Impossible exception occured!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector waitSocketInjector(Socket socket) {
|
||||
return waitSocketInjector(socket.getRemoteSocketAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector waitSocketInjector(InputStream input) {
|
||||
try {
|
||||
SocketAddress address = waitSocketAddress(input);
|
||||
|
||||
// Guard against NPE
|
||||
if (address != null)
|
||||
return waitSocketInjector(address);
|
||||
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 address from an input stream.
|
||||
* @param stream - the socket stream to lookup.
|
||||
* @return The underlying socket address, or NULL if not found.
|
||||
* @throws IllegalAccessException Unable to access socket field.
|
||||
*/
|
||||
private SocketAddress waitSocketAddress(InputStream stream) throws IllegalAccessException {
|
||||
// Extra check, just in case
|
||||
if (stream instanceof FilterInputStream)
|
||||
return waitSocketAddress(getInputStream((FilterInputStream) stream));
|
||||
|
||||
SocketAddress result = inputLookup.get(stream);
|
||||
|
||||
if (result == null) {
|
||||
Socket socket = lookupSocket(stream);
|
||||
|
||||
// Save it
|
||||
result = socket.getRemoteSocketAddress();
|
||||
inputLookup.put(stream, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSocketInjector(SocketAddress address, SocketInjector injector) {
|
||||
if (address == null)
|
||||
throw new IllegalArgumentException("address cannot be NULL");
|
||||
if (injector == null)
|
||||
throw new IllegalArgumentException("injector cannot be NULL.");
|
||||
|
||||
SocketInjector previous = addressLookup.put(address, injector);
|
||||
|
||||
// Any previous temporary players will also be associated
|
||||
if (previous != null) {
|
||||
// Update the reference to any previous injector
|
||||
onPreviousSocketOverwritten(previous, injector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return lookupSocket(getInputStream((FilterInputStream) stream));
|
||||
} else {
|
||||
// Just do it
|
||||
Field socketField = FuzzyReflection.fromObject(stream, true).
|
||||
getFieldByType("socket", Socket.class);
|
||||
|
||||
return (Socket) FieldUtils.readField(socketField, stream, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.comphenix.protocol.injector.server;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bukkit.Server;
|
||||
|
||||
import com.comphenix.protocol.concurrency.BlockingHashMap;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.google.common.collect.MapMaker;
|
||||
|
||||
class InputStreamReflectLookup extends AbstractInputStreamLookup {
|
||||
// Used to access the inner input stream of a filtered input stream
|
||||
private static Field filteredInputField;
|
||||
|
||||
// The default lookup timeout
|
||||
private static final long DEFAULT_TIMEOUT = 2000; // ms
|
||||
|
||||
// Using weak keys and values ensures that we will not hold up garbage collection
|
||||
protected BlockingHashMap<SocketAddress, SocketInjector> addressLookup = new BlockingHashMap<SocketAddress, SocketInjector>();
|
||||
protected ConcurrentMap<InputStream, SocketAddress> inputLookup = new MapMaker().weakValues().makeMap();
|
||||
|
||||
// The timeout
|
||||
private final long injectorTimeout;
|
||||
|
||||
public InputStreamReflectLookup(ErrorReporter reporter, Server server) {
|
||||
this(reporter, server, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a reflect lookup with a given default injector timeout.
|
||||
* <p>
|
||||
* This timeout defines the maximum amount of time to wait until an injector has been discovered.
|
||||
* @param reporter - the error reporter.
|
||||
* @param server - the current Bukkit server.
|
||||
* @param injectorTimeout - the injector timeout.
|
||||
*/
|
||||
public InputStreamReflectLookup(ErrorReporter reporter, Server server, long injectorTimeout) {
|
||||
super(reporter, server);
|
||||
this.injectorTimeout = injectorTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Object container) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postWorldLoaded() {
|
||||
// Nothing again
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector peekSocketInjector(SocketAddress address) {
|
||||
try {
|
||||
return addressLookup.get(address, 0, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// Whatever
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector waitSocketInjector(SocketAddress address) {
|
||||
try {
|
||||
// Note that we actually SWALLOW interrupts here - this is because Minecraft uses interrupts to
|
||||
// periodically wake up waiting readers and writers. We have to wait for the dedicated server thread
|
||||
// to catch up, so we'll swallow these interrupts.
|
||||
//
|
||||
// TODO: Consider if we should raise the thread priority of the dedicated server listener thread.
|
||||
return addressLookup.get(address, injectorTimeout, TimeUnit.MILLISECONDS, true);
|
||||
} catch (InterruptedException e) {
|
||||
// This cannot be!
|
||||
throw new IllegalStateException("Impossible exception occured!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector waitSocketInjector(Socket socket) {
|
||||
return waitSocketInjector(socket.getRemoteSocketAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketInjector waitSocketInjector(InputStream input) {
|
||||
try {
|
||||
SocketAddress address = waitSocketAddress(input);
|
||||
|
||||
// Guard against NPE
|
||||
if (address != null)
|
||||
return waitSocketInjector(address);
|
||||
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 address from an input stream.
|
||||
* @param stream - the socket stream to lookup.
|
||||
* @return The underlying socket address, or NULL if not found.
|
||||
* @throws IllegalAccessException Unable to access socket field.
|
||||
*/
|
||||
private SocketAddress waitSocketAddress(InputStream stream) throws IllegalAccessException {
|
||||
// Extra check, just in case
|
||||
if (stream instanceof FilterInputStream)
|
||||
return waitSocketAddress(getInputStream((FilterInputStream) stream));
|
||||
|
||||
SocketAddress result = inputLookup.get(stream);
|
||||
|
||||
if (result == null) {
|
||||
Socket socket = lookupSocket(stream);
|
||||
|
||||
// Save it
|
||||
result = socket.getRemoteSocketAddress();
|
||||
inputLookup.put(stream, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the underlying input stream that is associated with a given filter input stream.
|
||||
* @param filtered - the filter input stream.
|
||||
* @return The underlying input stream that is being filtered.
|
||||
* @throws FieldAccessException Unable to access input stream.
|
||||
*/
|
||||
protected static InputStream getInputStream(FilterInputStream filtered) {
|
||||
if (filteredInputField == null)
|
||||
filteredInputField = FuzzyReflection.fromClass(FilterInputStream.class, true).
|
||||
getFieldByType("in", InputStream.class);
|
||||
|
||||
InputStream current = filtered;
|
||||
|
||||
try {
|
||||
// Iterate until we find the real input stream
|
||||
while (current instanceof FilterInputStream) {
|
||||
current = (InputStream) FieldUtils.readField(filteredInputField, current, true);
|
||||
}
|
||||
return current;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot access filtered input field.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSocketInjector(SocketAddress address, SocketInjector injector) {
|
||||
if (address == null)
|
||||
throw new IllegalArgumentException("address cannot be NULL");
|
||||
if (injector == null)
|
||||
throw new IllegalArgumentException("injector cannot be NULL.");
|
||||
|
||||
SocketInjector previous = addressLookup.put(address, injector);
|
||||
|
||||
// Any previous temporary players will also be associated
|
||||
if (previous != null) {
|
||||
// Update the reference to any previous injector
|
||||
onPreviousSocketOverwritten(previous, injector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupAll() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return lookupSocket(getInputStream((FilterInputStream) stream));
|
||||
} else {
|
||||
// Just do it
|
||||
Field socketField = FuzzyReflection.fromObject(stream, true).
|
||||
getFieldByType("socket", Socket.class);
|
||||
|
||||
return (Socket) FieldUtils.readField(socketField, stream, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user