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