ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelProxy.java

353 lines
9.2 KiB
Java

/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import java.lang.reflect.Field;
import java.net.SocketAddress;
import java.util.Map;
import java.util.concurrent.Callable;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
public abstract class ChannelProxy implements Channel {
// Mark that a certain object does not contain a message field
private static final FieldAccessor MARK_NO_MESSAGE = new FieldAccessor() {
@Override
public void set(Object instance, Object value) { }
@Override
public Object get(Object instance) { return null; }
@Override
public Field getField() { return null; };
};
// Looking up packets in inner classes
private static Map<Class<?>, FieldAccessor> MESSAGE_LOOKUP = Maps.newConcurrentMap();
// The underlying channel
protected Channel delegate;
protected Class<?> messageClass;
// Event loop proxy
private transient EventLoopProxy loopProxy;
public ChannelProxy(Channel delegate, Class<?> messageClass) {
this.delegate = delegate;
this.messageClass = messageClass;
}
/**
* Invoked when a packet is scheduled for transmission in the event loop.
* @param <T> Type
* @param callable - callable to schedule for execution.
* @param packetAccessor - accessor for modifying the packet in the callable.
* @return The callable that will be scheduled, or NULL to cancel.
*/
protected abstract <T> Callable<T> onMessageScheduled(Callable<T> callable, FieldAccessor packetAccessor);
/**
* Invoked when a packet is scheduled for transmission in the event loop.
* @param runnable - the runnable that contains a packet to be scheduled.
* @param packetAccessor - accessor for modifying the packet in the runnable.
* @return The runnable that will be scheduled, or NULL to cancel.
*/
protected abstract Runnable onMessageScheduled(Runnable runnable, FieldAccessor packetAccessor);
@Override
public <T> Attribute<T> attr(AttributeKey<T> paramAttributeKey) {
return delegate.attr(paramAttributeKey);
}
@Override
public ChannelFuture bind(SocketAddress paramSocketAddress) {
return delegate.bind(paramSocketAddress);
}
@Override
public ChannelPipeline pipeline() {
return delegate.pipeline();
}
@Override
public ChannelFuture connect(SocketAddress paramSocketAddress) {
return delegate.connect(paramSocketAddress);
}
@Override
public ByteBufAllocator alloc() {
return delegate.alloc();
}
@Override
public ChannelPromise newPromise() {
return delegate.newPromise();
}
@Override
public EventLoop eventLoop() {
if (loopProxy == null) {
loopProxy = new EventLoopProxy() {
@Override
protected EventLoop getDelegate() {
return delegate.eventLoop();
}
@Override
protected Runnable schedulingRunnable(final Runnable runnable) {
final FieldAccessor accessor = getMessageAccessor(runnable);
if (accessor != null) {
Runnable result = onMessageScheduled(runnable, accessor);;
return result != null ? result : getEmptyRunnable();
}
return runnable;
}
@Override
protected <T> Callable<T> schedulingCallable(Callable<T> callable) {
FieldAccessor accessor = getMessageAccessor(callable);
if (accessor != null) {
Callable<T> result = onMessageScheduled(callable, accessor);;
return result != null ? result : EventLoopProxy.<T>getEmptyCallable();
}
return callable;
}
};
}
return loopProxy;
}
/**
* Retrieve a way to access the packet field of an object.
* @param value - the object.
* @return The packet field accessor, or NULL if not found.
*/
private FieldAccessor getMessageAccessor(Object value) {
Class<?> clazz = value.getClass();
FieldAccessor accessor = MESSAGE_LOOKUP.get(clazz);
if (accessor == null) {
try {
accessor = Accessors.getFieldAccessor(clazz, messageClass, true);
} catch (IllegalArgumentException e) {
accessor = MARK_NO_MESSAGE;
}
// Save the result
MESSAGE_LOOKUP.put(clazz, accessor);
}
return accessor != MARK_NO_MESSAGE ? accessor : null;
}
@Override
public ChannelFuture connect(SocketAddress paramSocketAddress1,
SocketAddress paramSocketAddress2) {
return delegate.connect(paramSocketAddress1, paramSocketAddress2);
}
@Override
public ChannelProgressivePromise newProgressivePromise() {
return delegate.newProgressivePromise();
}
@Override
public Channel parent() {
return delegate.parent();
}
@Override
public ChannelConfig config() {
return delegate.config();
}
@Override
public ChannelFuture newSucceededFuture() {
return delegate.newSucceededFuture();
}
@Override
public boolean isOpen() {
return delegate.isOpen();
}
@Override
public ChannelFuture disconnect() {
return delegate.disconnect();
}
@Override
public boolean isRegistered() {
return delegate.isRegistered();
}
@Override
public ChannelFuture newFailedFuture(Throwable paramThrowable) {
return delegate.newFailedFuture(paramThrowable);
}
@Override
public ChannelFuture close() {
return delegate.close();
}
@Override
public boolean isActive() {
return delegate.isActive();
}
@Override
@Deprecated
public ChannelFuture deregister() {
return delegate.deregister();
}
@Override
public ChannelPromise voidPromise() {
return delegate.voidPromise();
}
@Override
public ChannelMetadata metadata() {
return delegate.metadata();
}
@Override
public ChannelFuture bind(SocketAddress paramSocketAddress,
ChannelPromise paramChannelPromise) {
return delegate.bind(paramSocketAddress, paramChannelPromise);
}
@Override
public SocketAddress localAddress() {
return delegate.localAddress();
}
@Override
public SocketAddress remoteAddress() {
return delegate.remoteAddress();
}
@Override
public ChannelFuture connect(SocketAddress paramSocketAddress,
ChannelPromise paramChannelPromise) {
return delegate.connect(paramSocketAddress, paramChannelPromise);
}
@Override
public ChannelFuture closeFuture() {
return delegate.closeFuture();
}
@Override
public boolean isWritable() {
return delegate.isWritable();
}
@Override
public Channel flush() {
return delegate.flush();
}
@Override
public ChannelFuture connect(SocketAddress paramSocketAddress1,
SocketAddress paramSocketAddress2, ChannelPromise paramChannelPromise) {
return delegate.connect(paramSocketAddress1, paramSocketAddress2, paramChannelPromise);
}
@Override
public Channel read() {
return delegate.read();
}
@Override
public Unsafe unsafe() {
return delegate.unsafe();
}
@Override
public ChannelFuture disconnect(ChannelPromise paramChannelPromise) {
return delegate.disconnect(paramChannelPromise);
}
@Override
public ChannelFuture close(ChannelPromise paramChannelPromise) {
return delegate.close(paramChannelPromise);
}
@Override
@Deprecated
public ChannelFuture deregister(ChannelPromise paramChannelPromise) {
return delegate.deregister(paramChannelPromise);
}
@Override
public ChannelFuture write(Object paramObject) {
return delegate.write(paramObject);
}
@Override
public ChannelFuture write(Object paramObject, ChannelPromise paramChannelPromise) {
return delegate.write(paramObject, paramChannelPromise);
}
@Override
public ChannelFuture writeAndFlush(Object paramObject, ChannelPromise paramChannelPromise) {
return delegate.writeAndFlush(paramObject, paramChannelPromise);
}
@Override
public ChannelFuture writeAndFlush(Object paramObject) {
return delegate.writeAndFlush(paramObject);
}
@Override
public int compareTo(Channel o) {
return delegate.compareTo(o);
}
/* Added in Netty 4.1, seem to be unused
public long bytesBeforeUnwritable() {
return delegate.bytesBeforeUnwritable();
}
public long bytesBeforeWritable() {
return delegate.bytesBeforeWritable();
}
public ChannelId id() {
return delegate.id();
}
public <T> boolean hasAttr(AttributeKey<T> key) {
return delegate.hasAttr(key);
}
*/
}