KenM 17 years ago
parent
commit
b35d4bd59d
27 changed files with 2696 additions and 1107 deletions
  1. 0 226
      MMOCore/src/com/l2jserver/mmocore/network/MMOConnection.java
  2. 0 82
      MMOCore/src/com/l2jserver/mmocore/network/SelectorConfig.java
  3. 0 754
      MMOCore/src/com/l2jserver/mmocore/network/SelectorThread.java
  4. 1 1
      MMOCore/src/org/mmocore/network/AbstractPacket.java
  5. 13 19
      MMOCore/src/org/mmocore/network/HeaderHandler.java
  6. 114 0
      MMOCore/src/org/mmocore/network/HeaderInfo.java
  7. 1 1
      MMOCore/src/org/mmocore/network/IAcceptFilter.java
  8. 1 1
      MMOCore/src/org/mmocore/network/IClientFactory.java
  9. 1 1
      MMOCore/src/org/mmocore/network/IMMOExecutor.java
  10. 1 1
      MMOCore/src/org/mmocore/network/IPacketHandler.java
  11. 38 0
      MMOCore/src/org/mmocore/network/ISocket.java
  12. 1 1
      MMOCore/src/org/mmocore/network/MMOClient.java
  13. 453 0
      MMOCore/src/org/mmocore/network/MMOConnection.java
  14. 1 1
      MMOCore/src/org/mmocore/network/ReceivablePacket.java
  15. 138 0
      MMOCore/src/org/mmocore/network/SelectorConfig.java
  16. 1253 0
      MMOCore/src/org/mmocore/network/SelectorThread.java
  17. 24 19
      MMOCore/src/org/mmocore/network/SendablePacket.java
  18. 48 0
      MMOCore/src/org/mmocore/network/TCPHeaderHandler.java
  19. 71 0
      MMOCore/src/org/mmocore/network/TCPSocket.java
  20. 51 0
      MMOCore/src/org/mmocore/network/UDPHeaderHandler.java
  21. 71 0
      MMOCore/src/org/mmocore/network/UDPSocket.java
  22. 76 0
      MMOCore/src/org/mmocore/test/testserver/SelectorHelper.java
  23. 48 0
      MMOCore/src/org/mmocore/test/testserver/ServerClient.java
  24. 35 0
      MMOCore/src/org/mmocore/test/testserver/TestRecvPacket.java
  25. 42 0
      MMOCore/src/org/mmocore/test/testserver/TestSendPacket.java
  26. 39 0
      MMOCore/src/org/mmocore/test/testserver/TestServer.java
  27. 175 0
      MMOCore/src/org/mmocore/util/collections/concurrent/SemiConcurrentLinkedList.java

+ 0 - 226
MMOCore/src/com/l2jserver/mmocore/network/MMOConnection.java

@@ -1,226 +0,0 @@
-/* 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, 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.
- *
- * http://www.gnu.org/copyleft/gpl.html
- */
-package com.l2jserver.mmocore.network;
-
-import java.nio.ByteBuffer;
-import java.nio.channels.CancelledKeyException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
-
-
-import javolution.util.FastList;
-
-/**
- * @author KenM
- *
- */
-public class MMOConnection<T extends MMOClient>
-{
-    private final SelectorThread<T> _selectorThread;
-    private T _client;
-    
-    private SocketChannel _socketChannel;
-    private FastList<SendablePacket<T>> _sendQueue = new FastList<SendablePacket<T>>();
-    private SelectionKey _selectionKey;
-    
-    private ByteBuffer _readBuffer;
-    private ByteBuffer _writeBuffer;
-    
-    private boolean _pendingClose;
-    
-    public MMOConnection(SelectorThread<T> selectorThread, SocketChannel sc, SelectionKey key)
-    {
-        _selectorThread = selectorThread;
-        this.setSocketChannel(sc);
-        this.setSelectionKey(key);
-    }
-    
-    protected void setClient(T client)
-    {
-        _client = client;
-    }
-    
-    protected T getClient()
-    {
-        return _client;
-    }
-    
-    public void sendPacket(SendablePacket<T> sp)
-    {
-        sp.setClient(this.getClient());
-        synchronized (this.getSendQueue())
-        {
-            if (!_pendingClose)
-            {
-                try
-                {
-                    this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() | SelectionKey.OP_WRITE);
-                    this.getSendQueue().addLast(sp);
-                }
-                catch (CancelledKeyException e)
-                {
-                    // ignore
-                }
-            }
-        }
-    }
-    
-    protected SelectorThread<T> getSelectorThread()
-    {
-        return _selectorThread;
-    }
-    
-    protected void setSelectionKey(SelectionKey key)
-    {
-        _selectionKey = key;
-    }
-    
-    protected SelectionKey getSelectionKey()
-    {
-        return _selectionKey;
-    }
-    
-    protected void enableReadInterest()
-    {
-        try
-        {
-            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() | SelectionKey.OP_READ);
-        }
-        catch (CancelledKeyException e)
-        {
-            // ignore
-        }
-    }
-    
-    protected void disableReadInterest()
-    {
-        try
-        {
-            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() & ~SelectionKey.OP_READ);
-        }
-        catch (CancelledKeyException e)
-        {
-            // ignore
-        }
-    }
-    
-    protected void enableWriteInterest()
-    {
-        try
-        {
-            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() | SelectionKey.OP_WRITE);
-        }
-        catch (CancelledKeyException e)
-        {
-            // ignore
-        }
-    }
-    
-    protected void disableWriteInterest()
-    {
-        try
-        {
-            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() & ~SelectionKey.OP_WRITE);
-        }
-        catch (CancelledKeyException e)
-        {
-            // ignore
-        }
-    }
-    
-    protected void setSocketChannel(SocketChannel sc)
-    {
-        _socketChannel = sc;
-    }
-    
-    public SocketChannel getSocketChannel()
-    {
-        return _socketChannel;
-    }
-    
-    protected FastList<SendablePacket<T>> getSendQueue()
-    {
-        return _sendQueue;
-    }
-    
-    protected void setWriteBuffer(ByteBuffer buf)
-    {
-        _writeBuffer = buf;
-    }
-    
-    protected ByteBuffer getWriteBuffer()
-    {
-        return _writeBuffer;
-    }
-    
-    protected void setReadBuffer(ByteBuffer buf)
-    {
-        _readBuffer = buf;
-    }
-    
-    protected ByteBuffer getReadBuffer()
-    {
-        return _readBuffer;
-    }
-    
-    public boolean isClosed()
-    {
-        return _pendingClose;
-    }
-    
-    protected void closeNow()
-    {
-        synchronized (this.getSendQueue())
-        {
-            _pendingClose = true;
-            this.getSendQueue().clear();
-            this.disableWriteInterest();
-        }
-        this.getSelectorThread().closeConnection(this);
-    }
-    
-    public void close(SendablePacket<T> sp)
-    {
-        synchronized (this.getSendQueue())
-        {
-            this.getSendQueue().clear();
-            this.sendPacket(sp);
-            _pendingClose = true;
-        }
-        this.getSelectorThread().closeConnection(this);
-    }
-    
-    protected void closeLater()
-    {
-        synchronized (this.getSendQueue())
-        {
-            _pendingClose = true;
-        }
-        this.getSelectorThread().closeConnection(this);
-    }
-    
-    protected void onDisconection()
-    {
-        this.getClient().onDisconection();
-    }
-    
-    protected void onForcedDisconnection()
-    {
-        this.getClient().onForcedDisconnection();
-    }
-}

+ 0 - 82
MMOCore/src/com/l2jserver/mmocore/network/SelectorConfig.java

@@ -1,82 +0,0 @@
-/* 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, 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.
- *
- * http://www.gnu.org/copyleft/gpl.html
- */
-package com.l2jserver.mmocore.network;
-
-import java.nio.ByteOrder;
-
-/**
- * @author KenM
- *
- */
-public class SelectorConfig
-{
-    
-    private int READ_BUFFER_SIZE = 64*1024;
-    private int WRITE_BUFFER_SIZE = 64*1024;
-    private HeaderSize HEADER_TYPE = HeaderSize.SHORT_HEADER;
-    private int HELPER_BUFFER_SIZE = 64*1024;
-    private int HELPER_BUFFER_COUNT = 20;
-    private ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
-    
-    /**
-     * BYTE_HEADER: unsigned byte, max size: 255 <BR>
-     * SHORT_HEADER: unsigned short, max size: 65535<BR>
-     * INT_HEADER: signed integer, max size: Integer.MAX_SIZE<BR>
-     * @author KenM
-     */
-    public static enum HeaderSize
-    { 
-        BYTE_HEADER,
-        SHORT_HEADER,
-        INT_HEADER,
-    }
-    
-    public SelectorConfig()
-    {
-    }
-    
-    public int getReadBufferSize()
-    {
-        return READ_BUFFER_SIZE;
-    }
-    
-    public int getWriteBufferSize()
-    {
-        return READ_BUFFER_SIZE;
-    }
-    
-    public int getHelperBufferSize()
-    {
-        return HELPER_BUFFER_SIZE;
-    }
-    
-    public int getHelperBufferCount()
-    {
-        return HELPER_BUFFER_COUNT;
-    }
-    
-    public ByteOrder getByteOrder()
-    {
-        return BYTE_ORDER;
-    }
-    
-    public HeaderSize getHeaderType()
-    {
-        return HEADER_TYPE;
-    }
-}

+ 0 - 754
MMOCore/src/com/l2jserver/mmocore/network/SelectorThread.java

@@ -1,754 +0,0 @@
-/* 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, 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.
- *
- * http://www.gnu.org/copyleft/gpl.html
- */
-package com.l2jserver.mmocore.network;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.Iterator;
-import java.util.Set;
-
-import com.l2jserver.mmocore.network.SelectorConfig.HeaderSize;
-
-
-import javolution.util.FastList;
-
-/**
- * @author KenM<BR>
- * Parts of design based on networkcore from WoodenGil
- */
-public class SelectorThread<T extends MMOClient> extends Thread
-{
-    private Selector _selector;
-    
-    // Implementations
-    private IPacketHandler<T> _packetHandler;
-    private IMMOExecutor<T> _executor;
-    private IClientFactory<T> _clientFactory;
-    private IAcceptFilter _acceptFilter;
-    
-    private boolean _shutdown;
-
-    // Pending Close
-    private FastList<MMOConnection<T>> _pendingClose = new FastList<MMOConnection<T>>();
-    
-    // Configs
-    private InetAddress ADDRESS;
-    private int PORT;
-    private int HELPER_BUFFER_SIZE;
-    private int HELPER_BUFFER_COUNT;
-    private int HEADER_SIZE = 2;
-    private ByteOrder BYTE_ORDER;
-    private HeaderSize HEADER_TYPE;
-
-    // MAIN BUFFERS
-    private ByteBuffer WRITE_BUFFER;
-    private ByteBuffer READ_BUFFER;
-
-    // ByteBuffers General Purpose Pool
-    private final FastList<ByteBuffer> _bufferPool = new FastList<ByteBuffer>();
-
-    public SelectorThread(SelectorConfig sc, IPacketHandler<T> packetHandler, IMMOExecutor<T> executor) throws IOException
-    {
-        this.readConfig(sc);
-
-        this.initBufferPool();
-        this.setPacketHandler(packetHandler);
-        this.setExecutor(executor);
-        this.initializeSelector();
-    }
-
-    public SelectorThread(SelectorServerConfig ssc, IPacketHandler<T> packetHandler, IClientFactory<T> clientFactory, IMMOExecutor<T> executor) throws IOException
-    {
-        this.readConfig(ssc);
-        
-        PORT = ssc.getPort();
-        ADDRESS = ssc.getAddress();
-        
-        this.initBufferPool();
-        this.setPacketHandler(packetHandler);
-        this.setClientFactory(clientFactory);
-        this.setExecutor(executor);
-        this.initializeSelector();
-    }
-
-    protected void readConfig(SelectorConfig sc)
-    {
-        HELPER_BUFFER_SIZE = sc.getHelperBufferSize();
-        HELPER_BUFFER_COUNT = sc.getHelperBufferCount();
-        BYTE_ORDER = sc.getByteOrder();
-
-        WRITE_BUFFER = ByteBuffer.wrap(new byte[sc.getWriteBufferSize()]).order(BYTE_ORDER);
-        READ_BUFFER = ByteBuffer.wrap(new byte[sc.getReadBufferSize()]).order(BYTE_ORDER);
-
-        HEADER_TYPE = sc.getHeaderType();
-    }
-
-    protected void initBufferPool()
-    {
-        for (int i = 0; i < HELPER_BUFFER_COUNT; i++)
-        {
-            this.getFreeBuffers().addLast(ByteBuffer.wrap(new byte[HELPER_BUFFER_SIZE]).order(BYTE_ORDER));
-        }
-    }
-
-    public void openServerSocket() throws IOException
-    {
-
-        ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.configureBlocking(false);
-
-        ServerSocket ss = ssc.socket();
-        if (ADDRESS == null)
-        {
-            ss.bind(new InetSocketAddress(PORT));
-        }
-        else
-        {
-            ss.bind(new InetSocketAddress(ADDRESS, PORT));
-        }
-
-        ssc.register(this.getSelector(), SelectionKey.OP_ACCEPT);
-    }
-
-    protected void initializeSelector() throws IOException
-    {
-        this.setName("SelectorThread-"+this.getId());
-        this.setSelector(Selector.open());
-    }
-
-    public ByteBuffer getPooledBuffer()
-    {
-        if (this.getFreeBuffers().isEmpty())
-        {
-            return ByteBuffer.wrap(new byte[HELPER_BUFFER_SIZE]).order(BYTE_ORDER);
-        }
-        else
-        {
-            return this.getFreeBuffers().removeFirst();
-        }
-    }
-
-    public void recycleBuffer(ByteBuffer buf)
-    {
-        if (this.getFreeBuffers().size() < HELPER_BUFFER_COUNT)
-        {
-            buf.clear();
-            this.getFreeBuffers().addLast(buf);
-        }
-    }
-
-    public FastList<ByteBuffer> getFreeBuffers()
-    {
-        return _bufferPool;
-    }
-    
-    public SelectionKey registerClientSocket(SocketChannel sc, int interestOps) throws ClosedChannelException
-    {
-        SelectionKey sk = null;
-        
-        sk = sc.register(this.getSelector(), interestOps);
-
-        return sk;
-    }
-
-    public void run()
-    {
-        System.out.println("Selector Started");
-        int totalKeys = 0;
-        Iterator<SelectionKey> iter;
-        SelectionKey key;
-        MMOConnection<T> con;
-        FastList.Node<MMOConnection<T>> n, end, temp;
-        
-        // main loop
-        for (;;)
-        {
-            // check for shutdown
-            if (this.isShuttingDown())
-            {
-                this.closeSelectorThread();
-                break;
-            }
-
-            try
-            {
-                totalKeys = this.getSelector().selectNow();
-            }
-            catch (IOException e)
-            {
-                //TODO logging
-                e.printStackTrace();
-            }
-            //System.out.println("Selector Selected "+totalKeys);
-
-            if (totalKeys > 0)
-            {
-                Set<SelectionKey> keys = this.getSelector().selectedKeys();
-                iter = keys.iterator();
-
-
-
-                while (iter.hasNext())
-                {
-                    key = iter.next();
-                    iter.remove();
-
-                    switch (key.readyOps())
-                    {
-                        case SelectionKey.OP_CONNECT:
-                            this.finishConnection(key);
-                            break;
-                        case SelectionKey.OP_ACCEPT:
-                            this.acceptConnection(key);
-                            break;
-                        case SelectionKey.OP_READ:
-                            this.readPacket(key);
-                            break;
-                        case SelectionKey.OP_WRITE:
-                            this.writePacket(key);
-                            break;
-                        case SelectionKey.OP_READ | SelectionKey.OP_WRITE:
-                            this.writePacket(key);
-                            // key might have been invalidated on writePacket
-                            if (key.isValid())
-                            {
-                                this.readPacket(key);
-                            }
-                            break;
-                    }
-                }
-            }
-            
-            // process pending close
-            for (n = this.getPendingClose().head(), end = this.getPendingClose().tail(); (n = n.getNext()) != end;)
-            {
-                con = n.getValue();
-                if (con.getSendQueue().isEmpty())
-                {
-                    temp = n.getPrevious();
-                    this.getPendingClose().delete(n);
-                    n = temp;
-                    this.closeConnectionImpl(con);
-                }
-            }
-            
-            try
-            {
-                Thread.sleep(1);
-            }
-            catch (InterruptedException e)
-            {
-                e.printStackTrace();
-            }
-        }
-
-
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void finishConnection(SelectionKey key)
-    {
-        try
-        {
-            ((SocketChannel) key.channel()).finishConnect();
-        }
-        catch (IOException e)
-        {
-            T client = (T) key.attachment();
-            client.getConnection().onForcedDisconnection();
-            this.closeConnectionImpl(client.getConnection());
-        }
-
-        // key might have been invalidated on finishConnect()
-        if (key.isValid())
-        {
-            key.interestOps(key.interestOps() | SelectionKey.OP_READ);
-            key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
-        }
-    }
-
-    protected void acceptConnection(SelectionKey key)
-    {
-        SocketChannel sc;
-        try
-        {
-            while ((sc = ((ServerSocketChannel) key.channel()).accept()) != null)
-            {
-                if (this.getAcceptFilter() == null || this.getAcceptFilter().accept(sc))
-                {
-                    sc.configureBlocking(false);
-                    SelectionKey clientKey = sc.register(this.getSelector(), SelectionKey.OP_READ /*| SelectionKey.OP_WRITE*/);
-                    clientKey.attach(this.getClientFactory().create(new MMOConnection<T>(this, sc, clientKey)));
-                }
-                else
-                {
-                    sc.socket().close();
-                }
-            }
-        }
-        catch (IOException e)
-        {
-            e.printStackTrace();
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void readPacket(SelectionKey key)
-    {
-        T client = (T) key.attachment();
-        MMOConnection con = client.getConnection();
-
-        ByteBuffer buf;
-        if ((buf = con.getReadBuffer()) == null)
-        {
-            buf = READ_BUFFER;
-        }
-        else
-        {
-            //buf.limit(buf.capacity());
-            //buf.position()
-            //System.out.println("TEM BUF PENDENTE LIMIT: "+buf.limit());
-        }
-        int result = -2;
-        
-        // if we try to to do a read with no space in the buffer it will read 0 bytes
-        // going into infinite loop
-        if (buf.position() == buf.limit())
-        {
-            // should never happen
-            System.out.println("POS ANTES SC.READ(): "+buf.position()+" limit: "+buf.limit());
-            System.out.println("NOOBISH ERROR "+( buf == READ_BUFFER ? "READ_BUFFER" : "temp"));
-            System.exit(0);
-        }
-
-        try
-        {
-            result = con.getSocketChannel().read(buf);
-        }
-        catch (IOException e)
-        {
-            //error handling goes bellow
-        }
-        
-        //System.out.println("LEU: "+result+" pos: "+buf.position());
-        if (result > 0)
-        {
-            // TODO this should be done vefore even reading
-            if (!con.isClosed())
-            {
-                buf.flip();
-                // try to read as many packets as possible
-                while (this.tryReadPacket(client, buf));
-            }
-        }
-        else if (result == 0)
-        {
-            // read interest but nothing to read? wtf?
-            System.out.println("NOOBISH ERROR 2 THE MISSION");
-            System.exit(0);
-        }
-        else if (result == -1)
-        {
-            this.closeConnectionImpl(con);
-        }
-        else
-        {
-            con.onForcedDisconnection();
-            this.closeConnectionImpl(con);
-        }
-    }
-
-    protected boolean tryReadPacket(T client, ByteBuffer buf)
-    {
-        MMOConnection con = client.getConnection();
-        //System.out.println("BUFF POS ANTES DE LER: "+buf.position()+" - REMAINING: "+buf.remaining());
-
-        if (buf.hasRemaining())
-        {
-            int result = buf.remaining();
-
-            // then check if there are enough bytes for the header
-            if (result >= HEADER_SIZE)
-            {
-                // then read header and check if we have the whole packet
-                int size = this.getHeaderValue(buf);
-                //System.out.println("IF: ("+size+" <= "+result+") => (size <= result)");
-                if (size <= result)
-                {
-                    //System.out.println("BOA");
-
-                    // avoid parsing dummy packets (packets without body)
-                    if (size > HEADER_SIZE)
-                    {
-                        this.parseClientPacket(buf, size, client);
-                    }
-
-                    // if we are done with this buffer
-                    if (!buf.hasRemaining())
-                    {
-                        //System.out.println("BOA 2");
-                        if (buf != READ_BUFFER)
-                        {
-                            con.setReadBuffer(null);
-                            this.recycleBuffer(buf);
-                        }
-                        else
-                        {
-                            READ_BUFFER.clear();
-                        }
-
-                        return false;
-                    }
-                    else
-                    {
-                        // do nothing
-                    }
-
-                    return true;
-                }
-                else
-                {
-                    //System.out.println("ENABLEI");
-                    client.getConnection().enableReadInterest();
-                    
-                    //System.out.println("LIMIT "+buf.limit());
-                    if (buf == READ_BUFFER)
-                    {
-                        buf.position(buf.position() - HEADER_SIZE);
-                        this.allocateReadBuffer(con);
-                    }
-                    else
-                    {
-                        buf.position(buf.position() - HEADER_SIZE);
-                        buf.compact();
-                    }
-                    return false;
-                }
-            }
-            else
-            {
-                if (buf == READ_BUFFER)
-                {
-                    this.allocateReadBuffer(con);
-                }
-                return false;
-            }
-        }
-        else
-        {
-            //con.disableReadInterest();
-            return false; //empty buffer
-        }
-    }
-
-    protected void allocateReadBuffer(MMOConnection con)
-    {
-        //System.out.println("con: "+Integer.toHexString(con.hashCode()));
-        //Util.printHexDump(READ_BUFFER);
-        con.setReadBuffer(this.getPooledBuffer().put(READ_BUFFER));
-        READ_BUFFER.clear();
-    }
-
-    protected void parseClientPacket(ByteBuffer buf, int size, T client)
-    {
-        int pos = buf.position();
-        
-        boolean ret = client.decrypt(buf, size - HEADER_SIZE);
-        
-        buf.position(pos);
-       
-        if (buf.hasRemaining() && ret)
-        {
-            //  apply limit
-            int limit = buf.limit();
-            buf.limit(pos + size - HEADER_SIZE);
-            ReceivablePacket<T> cp = this.getPacketHandler().handlePacket(buf, client);
-
-            if (cp != null)
-            {
-                cp.setByteBuffer(buf);
-                cp.setClient(client);
-                
-                if (cp.read())
-                {
-                    this.getExecutor().execute(cp);
-                }
-            }
-            buf.limit(limit);
-        }
-        buf.position(pos + size - HEADER_SIZE);
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void writePacket(SelectionKey key)
-    {
-        T client = ((T) key.attachment());
-        MMOConnection<T> con = client.getConnection();
-
-        ByteBuffer buf;
-        boolean sharedBuffer = false;
-        if ((buf = con.getWriteBuffer()) == null)
-        {
-            SendablePacket<T> sp = null;
-            synchronized (con.getSendQueue())
-            {
-                if (!con.getSendQueue().isEmpty())
-                {
-                    sp = con.getSendQueue().removeFirst();
-                }
-            }
-            if (sp == null)
-            {
-                System.out.println("OMG WRITE BUT NO WRITE");
-                return;
-            }
-            //System.out.println("WRITING: "+sp.getClass().getSimpleName());
-            this.prepareWriteBuffer(client, sp);
-            buf = sp.getByteBuffer();
-            //System.out.println("WRITED:: "+sp.getClass().getSimpleName());
-            //System.out.println("wp:" +buf.position());
-            buf.flip();
-            
-            sharedBuffer = true;
-        }
-
-        int size = buf.remaining();
-        int result = -1;
-
-        try
-        {
-            result = con.getSocketChannel().write(buf);
-        }
-        catch (IOException e)
-        {
-            // error handling goes on the if bellow
-        }
-
-        // check if no error happened
-        if (result > 0)
-        {
-            // check if we writed everything
-            if (result == size)
-            {
-                //System.out.println("WRITEI COMPLETO");
-                synchronized (con.getSendQueue())
-                {
-                    if (con.getSendQueue().isEmpty())
-                    {
-                        con.disableWriteInterest();
-                    }
-                }
-
-                // if it was a pooled buffer we can recycle
-                if (!sharedBuffer)
-                {
-                    this.recycleBuffer(buf);
-                    con.setWriteBuffer(null);
-                }
-            }
-            else //incomplete write
-            {
-                //System.out.println("WRITEI INCOMPLETO");
-                // if its the main buffer allocate a pool one
-                if (sharedBuffer)
-                {
-                    con.setWriteBuffer(this.getPooledBuffer().put(buf));
-                }
-                else
-                {
-                    con.setWriteBuffer(buf);
-                }
-            }
-        }
-        else
-        {
-            con.onForcedDisconnection();
-            this.closeConnectionImpl(con);
-        }
-    }
-
-    protected void prepareWriteBuffer(T client, SendablePacket<T> sp)
-    {
-        WRITE_BUFFER.clear();
-
-        //set the write buffer
-        sp.setByteBuffer(WRITE_BUFFER);
-
-        // reserve space for the size
-        int headerPos = sp.getByteBuffer().position();
-        sp.getByteBuffer().position(headerPos + HEADER_SIZE);
-
-        // write contents
-        sp.write();
-        
-
-        int size = sp.getByteBuffer().position() - headerPos - HEADER_SIZE;
-        sp.getByteBuffer().position(headerPos + HEADER_SIZE);
-        client.encrypt(sp.getByteBuffer(), size);
-        
-        // write size
-        sp.writeHeader(HEADER_TYPE, headerPos);
-    }
-
-    protected int getHeaderValue(ByteBuffer buf)
-    {
-        switch (HEADER_TYPE)
-        {
-            case BYTE_HEADER:
-                return buf.get() & 0xFF;
-            case SHORT_HEADER:
-                return buf.getShort() & 0xFFFF;
-            case INT_HEADER:
-                return buf.getInt();
-        }
-        return -1; // O.o
-    }
-
-    protected void setSelector(Selector selector)
-    {
-        _selector = selector;
-    }
-
-    public Selector getSelector()
-    {
-        return _selector;
-    }
-
-    protected void setExecutor(IMMOExecutor<T> executor)
-    {
-        _executor = executor;
-    }
-
-    protected IMMOExecutor<T> getExecutor()
-    {
-        return _executor;
-    }
-
-    protected void setPacketHandler(IPacketHandler<T> packetHandler)
-    {
-        _packetHandler = packetHandler;
-    }
-
-    public IPacketHandler<T> getPacketHandler()
-    {
-        return _packetHandler;
-    }
-
-    protected void setClientFactory(IClientFactory<T> clientFactory)
-    {
-        _clientFactory = clientFactory;
-    }
-
-    public IClientFactory<T> getClientFactory()
-    {
-        return _clientFactory;
-    }
-
-    public void setAcceptFilter(IAcceptFilter acceptFilter)
-    {
-        _acceptFilter = acceptFilter;
-    }
-    
-    public IAcceptFilter getAcceptFilter()
-    {
-        return _acceptFilter;
-    }
-    
-    public void closeConnection(MMOConnection<T> con)
-    {
-        synchronized (this.getPendingClose())
-        {
-            this.getPendingClose().addLast(con);
-        }
-    }
-    
-    protected void closeConnectionImpl(MMOConnection<T> con)
-    {
-        // notify connection
-        con.onDisconection();
-        
-        try
-        {
-            // close socket and the SocketChannel
-            con.getSocketChannel().socket().close();
-        }
-        catch (IOException e)
-        {
-            // ignore, we are closing anyway
-        }
-        
-        // clear attachment
-        con.getSelectionKey().attach(null);
-        // cancel key
-        con.getSelectionKey().cancel();
-    }
-    
-    protected FastList<MMOConnection<T>> getPendingClose()
-    {
-        return _pendingClose;
-    }
-    
-    public void shutdown()
-    {
-        _shutdown = true;
-    }
-
-    public boolean isShuttingDown()
-    {
-        return _shutdown;
-    }
-
-    protected void closeAllChannels()
-    {
-        Set<SelectionKey> keys = this.getSelector().keys();
-        for (Iterator<SelectionKey> iter = keys.iterator(); iter.hasNext();)
-        {
-            SelectionKey key = iter.next();
-            try
-            {
-                key.channel().close();
-            }
-            catch (IOException e)
-            {
-                // ignore
-            }
-        }
-    }
-
-    protected void closeSelectorThread() 
-    {
-        this.closeAllChannels();
-        try
-        {
-            this.getSelector().close();
-        }
-        catch (IOException e)
-        {
-            // Ignore
-        }
-    }
-}

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/AbstractPacket.java → MMOCore/src/org/mmocore/network/AbstractPacket.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 import java.nio.ByteBuffer;
 

+ 13 - 19
MMOCore/src/com/l2jserver/mmocore/network/SelectorServerConfig.java → MMOCore/src/org/mmocore/network/HeaderHandler.java

@@ -15,37 +15,31 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
-
-import java.net.InetAddress;
+package org.mmocore.network;
 
 /**
  * @author KenM
  *
  */
-public class SelectorServerConfig extends SelectorConfig
+public abstract class HeaderHandler<T extends MMOClient, H extends HeaderHandler<T,H>>
 {
-    private final int SERVER_PORT;
-    private final InetAddress SERVER_ADDRESS;
-    
-    public SelectorServerConfig(int port)
-    {
-        this(null, port);
-    }
+    private final H _subHeaderHandler;
     
-    public SelectorServerConfig(InetAddress address, int port)
+    public HeaderHandler(H subHeaderHandler)
     {
-        SERVER_PORT = port;
-        SERVER_ADDRESS = address;
+        _subHeaderHandler = subHeaderHandler;
     }
-    
-    public int getPort()
+
+    /**
+     * @return the subHeaderHandler
+     */
+    public final H getSubHeaderHandler()
     {
-        return SERVER_PORT;
+        return _subHeaderHandler;
     }
     
-    public InetAddress getAddress()
+    public final boolean isChildHeaderHandler()
     {
-        return SERVER_ADDRESS;
+        return this.getSubHeaderHandler() == null;
     }
 }

+ 114 - 0
MMOCore/src/org/mmocore/network/HeaderInfo.java

@@ -0,0 +1,114 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+public final class HeaderInfo<T>
+{
+    private int _headerPending;
+    private int _dataPending;
+    private boolean _multiPacket;
+    private T _client;
+    
+    public HeaderInfo()
+    {
+        
+    }
+    
+    public HeaderInfo<T> set(int headerPending, int dataPending, boolean multiPacket, T client)
+    {
+        this.setHeaderPending(headerPending);
+        this.setDataPending(dataPending);
+        this.setMultiPacket(multiPacket);
+        this.setClient(client);
+        return this;
+    }
+    
+    protected boolean headerFinished()
+    {
+        return getHeaderPending() == 0;
+    }
+    
+    protected boolean packetFinished()
+    {
+        return getDataPending() == 0;
+    }
+    
+    /**
+     * @param dataPending the dataPending to set
+     */
+    private void setDataPending(int dataPending)
+    {
+        _dataPending = dataPending;
+    }
+    
+    /**
+     * @return the dataPending
+     */
+    protected int getDataPending()
+    {
+        return _dataPending;
+    }
+
+    /**
+     * @param headerPending the headerPending to set
+     */
+    private void setHeaderPending(int headerPending)
+    {
+        _headerPending = headerPending;
+    }
+
+    /**
+     * @return the headerPending
+     */
+    protected int getHeaderPending()
+    {
+        return _headerPending;
+    }
+
+    /**
+     * @param client the client to set
+     */
+    protected void setClient(T client)
+    {
+        _client = client;
+    }
+
+    /**
+     * @return the client
+     */
+    protected T getClient()
+    {
+        return _client;
+    }
+
+    /**
+     * @param multiPacket the multiPacket to set
+     */
+    private void setMultiPacket(boolean multiPacket)
+    {
+        _multiPacket = multiPacket;
+    }
+
+    /**
+     * @return the multiPacket
+     */
+    public boolean isMultiPacket()
+    {
+        return _multiPacket;
+    }
+}

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/IAcceptFilter.java → MMOCore/src/org/mmocore/network/IAcceptFilter.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 import java.nio.channels.SocketChannel;
 

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/IClientFactory.java → MMOCore/src/org/mmocore/network/IClientFactory.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 /**
  * @author KenM

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/IMMOExecutor.java → MMOCore/src/org/mmocore/network/IMMOExecutor.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 
 /**

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/IPacketHandler.java → MMOCore/src/org/mmocore/network/IPacketHandler.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 import java.nio.ByteBuffer;
 

+ 38 - 0
MMOCore/src/org/mmocore/network/ISocket.java

@@ -0,0 +1,38 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * @author KenM
+ *
+ */
+public interface ISocket
+{
+    public void close() throws IOException;
+    
+    public WritableByteChannel getWritableByteChannel();
+    
+    public ReadableByteChannel getReadableByteChannel();
+    
+    public InetAddress getInetAddress();
+}

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/MMOClient.java → MMOCore/src/org/mmocore/network/MMOClient.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 import java.nio.ByteBuffer;
 

+ 453 - 0
MMOCore/src/org/mmocore/network/MMOConnection.java

@@ -0,0 +1,453 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.CancelledKeyException;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.WritableByteChannel;
+
+import org.mmocore.util.collections.concurrent.SemiConcurrentLinkedList;
+
+
+/**
+ * @author KenM
+ *
+ */
+public class MMOConnection<T extends MMOClient>
+{
+    private final SelectorThread<T> _selectorThread;
+    private T _client;
+    
+    private ISocket _socket;
+    private WritableByteChannel _writableByteChannel;
+    private ReadableByteChannel _readableByteChannel;
+    
+    private SemiConcurrentLinkedList<SendablePacket<T>> _sendQueue = new SemiConcurrentLinkedList<SendablePacket<T>>();
+    private SelectionKey _selectionKey;
+    
+    private int _readHeaderPending;
+    private ByteBuffer _readBuffer;
+    
+    private ByteBuffer _primaryWriteBuffer;
+    private ByteBuffer _secondaryWriteBuffer;
+    
+    private boolean _pendingClose;
+    
+    public MMOConnection(SelectorThread<T> selectorThread, ISocket socket, SelectionKey key)
+    {
+        _selectorThread = selectorThread;
+        this.setSocket(socket);
+        this.setWritableByteChannel(socket.getWritableByteChannel());
+        this.setReadableByteChannel(socket.getReadableByteChannel());
+        this.setSelectionKey(key);
+    }
+    
+    public MMOConnection(T client, SelectorThread<T> selectorThread, ISocket socket, SelectionKey key)
+    {
+        this(selectorThread, socket, key);
+        this.setClient(client);
+    }
+    
+    protected void setClient(T client)
+    {
+        _client = client;
+    }
+    
+    public T getClient()
+    {
+        return _client;
+    }
+    
+    public void sendPacket(SendablePacket<T> sp)
+    {
+        sp.setClient(this.getClient());
+        synchronized (this.getSendQueue())
+        {
+            if (!_pendingClose)
+            {
+                try
+                {
+                    this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() | SelectionKey.OP_WRITE);
+                    this.getSendQueue().addLast(sp);
+                }
+                catch (CancelledKeyException e)
+                {
+                    // ignore
+                }
+            }
+        }
+    }
+    
+    protected SelectorThread<T> getSelectorThread()
+    {
+        return _selectorThread;
+    }
+    
+    protected void setSelectionKey(SelectionKey key)
+    {
+        _selectionKey = key;
+    }
+    
+    protected SelectionKey getSelectionKey()
+    {
+        return _selectionKey;
+    }
+    
+    protected void enableReadInterest()
+    {
+        try
+        {
+            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() | SelectionKey.OP_READ);
+        }
+        catch (CancelledKeyException e)
+        {
+            // ignore
+        }
+    }
+    
+    protected void disableReadInterest()
+    {
+        try
+        {
+            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() & ~SelectionKey.OP_READ);
+        }
+        catch (CancelledKeyException e)
+        {
+            // ignore
+        }
+    }
+    
+    protected void enableWriteInterest()
+    {
+        try
+        {
+            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() | SelectionKey.OP_WRITE);
+        }
+        catch (CancelledKeyException e)
+        {
+            // ignore
+        }
+    }
+    
+    protected void disableWriteInterest()
+    {
+        try
+        {
+            this.getSelectionKey().interestOps(this.getSelectionKey().interestOps() & ~SelectionKey.OP_WRITE);
+        }
+        catch (CancelledKeyException e)
+        {
+            // ignore
+        }
+    }
+    
+    /**
+     * @param socket the socket to set
+     */
+    protected void setSocket(ISocket socket)
+    {
+        _socket = socket;
+    }
+
+    /**
+     * @return the socket
+     */
+    public ISocket getSocket()
+    {
+        return _socket;
+    }
+
+    protected void setWritableByteChannel(WritableByteChannel wbc)
+    {
+        _writableByteChannel = wbc;
+    }
+    
+    public WritableByteChannel getWritableChannel()
+    {
+        return _writableByteChannel;
+    }
+    
+    protected void setReadableByteChannel(ReadableByteChannel rbc)
+    {
+        _readableByteChannel = rbc;
+    }
+    
+    public ReadableByteChannel getReadableByteChannel()
+    {
+        return _readableByteChannel;
+    }
+    
+    protected SemiConcurrentLinkedList<SendablePacket<T>> getSendQueue()
+    {
+        return _sendQueue;
+    }
+    
+    protected void createWriteBuffer(ByteBuffer buf)
+    {
+        if (_primaryWriteBuffer == null)
+        {
+            //System.err.println("APPENDING FOR NULL");
+            //System.err.flush();
+            _primaryWriteBuffer = this.getSelectorThread().getPooledBuffer();
+            _primaryWriteBuffer.put(buf);
+        }
+        else
+        {
+            //System.err.println("PREPENDING ON EXISTING");
+            //System.err.flush();
+            
+            ByteBuffer temp = this.getSelectorThread().getPooledBuffer();
+            temp.put(buf);
+            
+            int remaining = temp.remaining();
+            _primaryWriteBuffer.flip();
+            int limit = _primaryWriteBuffer.limit();
+            
+            if (remaining >= _primaryWriteBuffer.remaining())
+            {
+                temp.put(_primaryWriteBuffer);
+                this.getSelectorThread().recycleBuffer(_primaryWriteBuffer);
+                _primaryWriteBuffer = temp;
+            }
+            else
+            {
+                _primaryWriteBuffer.limit(remaining);
+                temp.put(_primaryWriteBuffer);
+                _primaryWriteBuffer.limit(limit);
+                _primaryWriteBuffer.compact();
+                _secondaryWriteBuffer = _primaryWriteBuffer;
+                _primaryWriteBuffer = temp;
+            }
+        }
+    }
+    
+    /*
+    protected void appendIntoWriteBuffer(ByteBuffer buf)
+    {
+        // if we already have a buffer
+        if (_secondaryWriteBuffer != null && (_primaryWriteBuffer != null && !_primaryWriteBuffer.hasRemaining()))
+        {
+            _secondaryWriteBuffer.put(buf);
+            
+            if (MMOCore.ASSERTIONS_ENABLED)
+            {
+                // correct state
+                assert _primaryWriteBuffer == null || !_primaryWriteBuffer.hasRemaining();
+                // full write
+                assert !buf.hasRemaining();
+            }
+        }
+        else if (_primaryWriteBuffer != null)
+        {
+            int size = Math.min(buf.limit(), _primaryWriteBuffer.remaining());
+            _primaryWriteBuffer.put(buf.array(), buf.position(), size);
+            buf.position(buf.position() + size);
+            
+            // primary wasnt enough
+            if (buf.hasRemaining())
+            {
+                _secondaryWriteBuffer = this.getSelectorThread().getPooledBuffer();
+                _secondaryWriteBuffer.put(buf);
+            }
+            
+            if (MMOCore.ASSERTIONS_ENABLED)
+            {
+                // full write
+                assert !buf.hasRemaining();
+            }
+        }
+        else
+        {
+            // a single empty buffer should be always enough by design
+            _primaryWriteBuffer = this.getSelectorThread().getPooledBuffer();
+            _primaryWriteBuffer.put(buf);
+            System.err.println("ESCREVI "+_primaryWriteBuffer.position());
+            if (MMOCore.ASSERTIONS_ENABLED)
+            {
+                // full write
+                assert !buf.hasRemaining();
+            }
+        }
+    }*/
+    
+    /*protected void prependIntoPendingWriteBuffer(ByteBuffer buf)
+    {
+        int remaining = buf.remaining();
+        
+        //do we already have some buffer
+        if (_primaryWriteBuffer != null && _primaryWriteBuffer.hasRemaining())
+        {
+            if (remaining == _primaryWriteBuffer.capacity())
+            {
+                if (MMOCore.ASSERTIONS_ENABLED)
+                {
+                    assert _secondaryWriteBuffer == null;
+                }
+                
+                _secondaryWriteBuffer = _primaryWriteBuffer;
+                _primaryWriteBuffer = this.getSelectorThread().getPooledBuffer();
+                _primaryWriteBuffer.put(buf);
+            }
+            else if (remaining < _primaryWriteBuffer.remaining())
+            {
+                
+            }
+        }
+        else
+        {
+            
+        }
+    }*/
+    
+    protected boolean hasPendingWriteBuffer()
+    {
+        return _primaryWriteBuffer != null;
+    }
+    
+    protected void movePendingWriteBufferTo(ByteBuffer dest)
+    {
+        //System.err.println("PRI SIZE: "+_primaryWriteBuffer.position());
+        //System.err.flush();
+        _primaryWriteBuffer.flip();
+        dest.put(_primaryWriteBuffer);
+        this.getSelectorThread().recycleBuffer(_primaryWriteBuffer);
+        _primaryWriteBuffer = _secondaryWriteBuffer;
+        _secondaryWriteBuffer = null;
+    }
+    
+    /*protected void finishPrepending(int written)
+    {
+        _primaryWriteBuffer.position(Math.min(written, _primaryWriteBuffer.limit()));
+        // discard only the written bytes
+        _primaryWriteBuffer.compact();
+        
+        if (_secondaryWriteBuffer != null)
+        {
+            _secondaryWriteBuffer.flip();
+            _primaryWriteBuffer.put(_secondaryWriteBuffer);
+            
+            if (!_secondaryWriteBuffer.hasRemaining())
+            {
+                this.getSelectorThread().recycleBuffer(_secondaryWriteBuffer);
+                _secondaryWriteBuffer = null;
+            }
+            else
+            {
+                _secondaryWriteBuffer.compact();
+            }
+        }
+    }*/
+    
+    protected ByteBuffer getWriteBuffer()
+    {
+        ByteBuffer ret = _primaryWriteBuffer;
+        if (_secondaryWriteBuffer != null)
+        {
+            _primaryWriteBuffer = _secondaryWriteBuffer;
+            _secondaryWriteBuffer = null;
+        }
+        return ret;
+    }
+
+    protected void setPendingHeader(int size)
+    {
+        _readHeaderPending = size;
+    }
+    
+    protected int getPendingHeader()
+    {
+        return _readHeaderPending;
+    }
+    
+    protected void setReadBuffer(ByteBuffer buf)
+    {
+        _readBuffer = buf;
+    }
+    
+    protected ByteBuffer getReadBuffer()
+    {
+        return _readBuffer;
+    }
+    
+    public boolean isClosed()
+    {
+        return _pendingClose;
+    }
+    
+    protected void closeNow()
+    {
+        synchronized (this.getSendQueue())
+        {
+            _pendingClose = true;
+            this.getSendQueue().clear();
+            this.disableWriteInterest();
+        }
+        this.getSelectorThread().closeConnection(this);
+    }
+    
+    public void close(SendablePacket<T> sp)
+    {
+        synchronized (this.getSendQueue())
+        {
+            this.getSendQueue().clear();
+            this.sendPacket(sp);
+            _pendingClose = true;
+        }
+        this.getSelectorThread().closeConnection(this);
+    }
+    
+    protected void closeLater()
+    {
+        synchronized (this.getSendQueue())
+        {
+            _pendingClose = true;
+        }
+        this.getSelectorThread().closeConnection(this);
+    }
+    
+    protected void releaseBuffers()
+    {
+        if (_primaryWriteBuffer != null)
+        {
+            this.getSelectorThread().recycleBuffer(_primaryWriteBuffer);
+            _primaryWriteBuffer = null;
+            if (_secondaryWriteBuffer != null)
+            {
+                this.getSelectorThread().recycleBuffer(_secondaryWriteBuffer);
+                _secondaryWriteBuffer = null;
+            }
+        }
+        if (_readBuffer != null)
+        {
+            this.getSelectorThread().recycleBuffer(_readBuffer);
+            _readBuffer = null;
+        }
+    }
+    
+    protected void onDisconection()
+    {
+        this.getClient().onDisconection();
+    }
+    
+    protected void onForcedDisconnection()
+    {
+        this.getClient().onForcedDisconnection();
+    }
+}

+ 1 - 1
MMOCore/src/com/l2jserver/mmocore/network/ReceivablePacket.java → MMOCore/src/org/mmocore/network/ReceivablePacket.java

@@ -15,7 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
+package org.mmocore.network;
 
 
 import javolution.text.TextBuilder;

+ 138 - 0
MMOCore/src/org/mmocore/network/SelectorConfig.java

@@ -0,0 +1,138 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.nio.ByteOrder;
+
+/**
+ * @author KenM
+ *
+ */
+public class SelectorConfig<T extends MMOClient>
+{
+    private final UDPHeaderHandler<T> UDP_HEADER_HANDLER;
+    private final TCPHeaderHandler<T> TCP_HEADER_HANDLER;
+    
+    private int READ_BUFFER_SIZE = 64*1024;
+    private int WRITE_BUFFER_SIZE = 64*1024;
+    private int MAX_SEND_PER_PASS = 1;
+    private int SLEEP_TIME = 10;
+    private HeaderSize HEADER_TYPE = HeaderSize.SHORT_HEADER;
+    private int HELPER_BUFFER_SIZE = 64*1024;
+    private int HELPER_BUFFER_COUNT = 20;
+    private ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
+    
+    /**
+     * BYTE_HEADER: unsigned byte, max size: 255 <BR>
+     * SHORT_HEADER: unsigned short, max size: 65535<BR>
+     * INT_HEADER: signed integer, max size: Integer.MAX_SIZE<BR>
+     * @author KenM
+     */
+    public static enum HeaderSize
+    { 
+        BYTE_HEADER,
+        SHORT_HEADER,
+        INT_HEADER,
+    }
+    
+    public SelectorConfig(UDPHeaderHandler<T> udpHeaderHandler, TCPHeaderHandler<T> tcpHeaderHandler)
+    {
+        UDP_HEADER_HANDLER = udpHeaderHandler;
+        TCP_HEADER_HANDLER = tcpHeaderHandler;
+    }
+    
+    public int getReadBufferSize()
+    {
+        return READ_BUFFER_SIZE;
+    }
+    
+    public int getWriteBufferSize()
+    {
+        return READ_BUFFER_SIZE;
+    }
+    
+    public int getHelperBufferSize()
+    {
+        return HELPER_BUFFER_SIZE;
+    }
+    
+    public int getHelperBufferCount()
+    {
+        return HELPER_BUFFER_COUNT;
+    }
+    
+    public ByteOrder getByteOrder()
+    {
+        return BYTE_ORDER;
+    }
+    
+    public HeaderSize getHeaderType()
+    {
+        return HEADER_TYPE;
+    }
+
+    public UDPHeaderHandler<T> getUDPHeaderHandler()
+    {
+        return UDP_HEADER_HANDLER;
+    }
+    
+    public TCPHeaderHandler<T> getTCPHeaderHandler()
+    {
+        return TCP_HEADER_HANDLER;
+    }
+
+    /**
+     * Server will try to send maxSendPerPass packets per socket write call however it may send less if the write buffer was filled before achieving this value.
+     * 
+     * @param The maximum number of packets to be sent on a single socket write call
+     */
+    public void setMaxSendPerPass(int maxSendPerPass)
+    {
+        MAX_SEND_PER_PASS = maxSendPerPass;
+    }
+
+    /**
+     * @return The maximum number of packets sent in an socket write call
+     */
+    public int getMaxSendPerPass()
+    {
+        return MAX_SEND_PER_PASS;
+    }
+
+    /**
+     * Defines how much time (in milis) should the selector sleep, an higher value increases throughput but also increases latency(to a max of the sleep value itself).<BR>
+     * Also an extremely high value(usually > 100) will decrease throughput due to the server not doing enough sends per second (depends on max sends per pass).<BR>
+     * <BR>
+     * Recommended values:<BR>
+     * 1 for minimal latency.<BR>
+     * 10-30 for an latency/troughput trade-off based on your needs.<BR>
+     * @param sleepTime the sleepTime to set
+     */
+    public void setSelectorSleepTime(int sleepTime)
+    {
+        SLEEP_TIME = sleepTime;
+    }
+
+    /**
+     * @return the sleepTime setting for the selector
+     */
+    public int getSelectorSleepTime()
+    {
+        return SLEEP_TIME;
+    }
+}

+ 1253 - 0
MMOCore/src/org/mmocore/network/SelectorThread.java

@@ -0,0 +1,1253 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.mmocore.util.collections.concurrent.SemiConcurrentLinkedList;
+
+
+
+import javolution.util.FastList;
+
+/**
+ * @author KenM<BR>
+ * Parts of design based on networkcore from WoodenGil
+ */
+public class SelectorThread<T extends MMOClient> extends Thread
+{
+    private Selector _selector;
+    
+    // Implementations
+    private final IPacketHandler<T> _packetHandler;
+    private final IPacketHandler<T> _udpPacketHandler;
+    private IMMOExecutor<T> _executor;
+    private IClientFactory<T> _clientFactory;
+    private IAcceptFilter _acceptFilter;
+    private final UDPHeaderHandler<T> _udpHeaderHandler;
+    private final TCPHeaderHandler<T> _tcpHeaderHandler;
+    
+    private boolean _shutdown;
+    
+    // Pending Close
+    private FastList<MMOConnection<T>> _pendingClose = new FastList<MMOConnection<T>>();
+    
+    // Configs
+    private final int HELPER_BUFFER_SIZE;
+    private final int HELPER_BUFFER_COUNT;
+    private final int MAX_SEND_PER_PASS;
+    private int HEADER_SIZE = 2;
+    private final ByteOrder BYTE_ORDER;
+    private final long SLEEP_TIME;
+    
+    // MAIN BUFFERS
+    private final ByteBuffer DIRECT_WRITE_BUFFER;
+    private final ByteBuffer WRITE_BUFFER;
+    private final ByteBuffer READ_BUFFER;
+
+    // ByteBuffers General Purpose Pool
+    private final FastList<ByteBuffer> _bufferPool = new FastList<ByteBuffer>();
+
+    public SelectorThread(SelectorConfig<T> sc, IPacketHandler<T> udpPacketHandler, IPacketHandler<T> packetHandler, IMMOExecutor<T> executor, IClientFactory<T> clientFactory, IAcceptFilter acceptFilter) throws IOException
+    {
+        HELPER_BUFFER_SIZE = sc.getHelperBufferSize();
+        HELPER_BUFFER_COUNT = sc.getHelperBufferCount();
+        MAX_SEND_PER_PASS = sc.getMaxSendPerPass();
+        BYTE_ORDER = sc.getByteOrder();
+        SLEEP_TIME = sc.getSelectorSleepTime();
+        
+        DIRECT_WRITE_BUFFER = ByteBuffer.allocateDirect(sc.getWriteBufferSize()).order(BYTE_ORDER);
+        WRITE_BUFFER = ByteBuffer.wrap(new byte[sc.getWriteBufferSize()]).order(BYTE_ORDER);
+        READ_BUFFER = ByteBuffer.wrap(new byte[sc.getReadBufferSize()]).order(BYTE_ORDER);
+
+        _udpHeaderHandler = sc.getUDPHeaderHandler();
+        _tcpHeaderHandler = sc.getTCPHeaderHandler();
+        this.initBufferPool();
+        _acceptFilter = acceptFilter;
+        _packetHandler = packetHandler;
+        _udpPacketHandler = udpPacketHandler;
+        _clientFactory = clientFactory;
+        this.setExecutor(executor);
+        this.initializeSelector();
+    }
+
+    protected void initBufferPool()
+    {
+        for (int i = 0; i < HELPER_BUFFER_COUNT; i++)
+        {
+            this.getFreeBuffers().addLast(ByteBuffer.wrap(new byte[HELPER_BUFFER_SIZE]).order(BYTE_ORDER));
+        }
+    }
+
+    public void openServerSocket(InetAddress address, int tcpPort) throws IOException
+    {
+        ServerSocketChannel selectable = ServerSocketChannel.open();
+        selectable.configureBlocking(false);
+
+        ServerSocket ss = selectable.socket();
+        if (address == null)
+        {
+            ss.bind(new InetSocketAddress(tcpPort));
+        }
+        else
+        {
+            ss.bind(new InetSocketAddress(address, tcpPort));
+        }
+        selectable.register(this.getSelector(), SelectionKey.OP_ACCEPT);
+    }
+    
+    public void openDatagramSocket(InetAddress address, int udpPort) throws IOException
+    {
+        DatagramChannel selectable = DatagramChannel.open();
+        selectable.configureBlocking(false);
+
+        DatagramSocket ss = selectable.socket();
+        if (address == null)
+        {
+            ss.bind(new InetSocketAddress(udpPort));
+        }
+        else
+        {
+            ss.bind(new InetSocketAddress(address, udpPort));
+        }
+        selectable.register(this.getSelector(), SelectionKey.OP_READ);
+    }
+
+    protected void initializeSelector() throws IOException
+    {
+        this.setName("SelectorThread-"+this.getId());
+        this.setSelector(Selector.open());
+    }
+
+    protected ByteBuffer getPooledBuffer()
+    {
+        if (this.getFreeBuffers().isEmpty())
+        {
+            return ByteBuffer.wrap(new byte[HELPER_BUFFER_SIZE]).order(BYTE_ORDER);
+        }
+        else
+        {
+            return this.getFreeBuffers().removeFirst();
+        }
+    }
+
+    public void recycleBuffer(ByteBuffer buf)
+    {
+        if (this.getFreeBuffers().size() < HELPER_BUFFER_COUNT)
+        {
+            buf.clear();
+            this.getFreeBuffers().addLast(buf);
+        }
+    }
+
+    public FastList<ByteBuffer> getFreeBuffers()
+    {
+        return _bufferPool;
+    }
+    
+    public SelectionKey registerClientSocket(SelectableChannel sc, int interestOps) throws ClosedChannelException
+    {
+        SelectionKey sk = null;
+        
+        sk = sc.register(this.getSelector(), interestOps);
+        //sk.attach(ob)
+        return sk;
+    }
+
+    public void run()
+    {
+        //System.out.println("Selector Started");
+        int totalKeys = 0;
+        Iterator<SelectionKey> iter;
+        SelectionKey key;
+        MMOConnection<T> con;
+        FastList.Node<MMOConnection<T>> n, end, temp;
+        
+        // main loop
+        for (;;)
+        {
+            // check for shutdown
+            if (this.isShuttingDown())
+            {
+                this.closeSelectorThread();
+                break;
+            }
+
+            try
+            {
+                totalKeys = this.getSelector().selectNow();
+            }
+            catch (IOException e)
+            {
+                //TODO logging
+                e.printStackTrace();
+            }
+            //System.out.println("Selector Selected "+totalKeys);
+
+            if (totalKeys > 0)
+            {
+                Set<SelectionKey> keys = this.getSelector().selectedKeys();
+                iter = keys.iterator();
+
+
+
+                while (iter.hasNext())
+                {
+                    key = iter.next();
+                    iter.remove();
+                    
+                    switch (key.readyOps())
+                    {
+                        case SelectionKey.OP_CONNECT:
+                            this.finishConnection(key);
+                            break;
+                        case SelectionKey.OP_ACCEPT:
+                            this.acceptConnection(key);
+                            break;
+                        case SelectionKey.OP_READ:
+                            this.readPacket(key);
+                            break;
+                        case SelectionKey.OP_WRITE:
+                            this.writePacket2(key);
+                            break;
+                        case SelectionKey.OP_READ | SelectionKey.OP_WRITE:
+                            this.writePacket2(key);
+                            // key might have been invalidated on writePacket
+                            if (key.isValid())
+                            {
+                                this.readPacket(key);
+                            }
+                            break;
+                    }
+                    
+                    
+                }
+            }
+            
+            // process pending close
+            for (n = this.getPendingClose().head(), end = this.getPendingClose().tail(); (n = n.getNext()) != end;)
+            {
+                con = n.getValue();
+                if (con.getSendQueue().isEmpty())
+                {
+                    temp = n.getPrevious();
+                    this.getPendingClose().delete(n);
+                    n = temp;
+                    this.closeConnectionImpl(con);
+                }
+            }
+            
+            try
+            {
+                Thread.sleep(SLEEP_TIME);
+            }
+            catch (InterruptedException e)
+            {
+                e.printStackTrace();
+            }
+        }
+
+
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void finishConnection(SelectionKey key)
+    {
+        try
+        {
+            ((SocketChannel) key.channel()).finishConnect();
+        }
+        catch (IOException e)
+        {
+            MMOConnection<T> con = (MMOConnection<T>) key.attachment();
+            T client = con.getClient();
+            client.getConnection().onForcedDisconnection();
+            this.closeConnectionImpl(client.getConnection());
+        }
+
+        // key might have been invalidated on finishConnect()
+        if (key.isValid())
+        {
+            key.interestOps(key.interestOps() | SelectionKey.OP_READ);
+            key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
+        }
+    }
+
+    protected void acceptConnection(SelectionKey key)
+    {
+        SocketChannel sc;
+        try
+        {
+            while ((sc = ((ServerSocketChannel) key.channel()).accept()) != null)
+            {
+                if (this.getAcceptFilter() == null || this.getAcceptFilter().accept(sc))
+                {
+                    sc.configureBlocking(false);
+                    SelectionKey clientKey = sc.register(this.getSelector(), SelectionKey.OP_READ /*| SelectionKey.OP_WRITE*/);
+                    
+                    MMOConnection<T> con = new MMOConnection<T>(this, new TCPSocket(sc.socket()), clientKey);
+                    T client = this.getClientFactory().create(con);
+                    clientKey.attach(con);
+                }
+                else
+                {
+                    key.cancel();
+                    sc.socket().close();
+                }
+            }
+        }
+        catch (IOException e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    protected void readPacket(SelectionKey key)
+    {
+        if (key.channel() instanceof SocketChannel)
+        {
+            this.readTCPPacket(key);
+        }
+        else
+        {
+            this.readUDPPacket(key);
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected void readTCPPacket(SelectionKey key)
+    {
+        MMOConnection<T> con = (MMOConnection<T>) key.attachment();
+        T client = con.getClient();
+        
+        ByteBuffer buf;
+        if ((buf = con.getReadBuffer()) == null)
+        {
+            buf = READ_BUFFER;
+        }
+        else
+        {
+            //buf.limit(buf.capacity());
+            //buf.position()
+            //System.out.println("TEM BUF PENDENTE LIMIT: "+buf.limit());
+        }
+        int result = -2;
+        
+        // if we try to to do a read with no space in the buffer it will read 0 bytes
+        // going into infinite loop
+        if (buf.position() == buf.limit())
+        {
+            // should never happen
+            System.out.println("POS ANTES SC.READ(): "+buf.position()+" limit: "+buf.limit());
+            System.out.println("NOOBISH ERROR "+( buf == READ_BUFFER ? "READ_BUFFER" : "temp"));
+            System.exit(0);
+        }
+        
+        //System.out.println("POS ANTES SC.READ(): "+buf.position()+" limit: "+buf.limit()+" - buf: "+(buf == READ_BUFFER ? "READ_BUFFER" : "TEMP"));
+        
+        try
+        {
+            result = con.getReadableByteChannel().read(buf);
+        }
+        catch (IOException e)
+        {
+            //error handling goes bellow
+        }
+        
+        //System.out.println("LEU: "+result+" pos: "+buf.position());
+        if (result > 0)
+        {
+            // TODO this should be done vefore even reading
+            if (!con.isClosed())
+            {
+                buf.flip();
+                // try to read as many packets as possible
+                while (this.tryReadPacket2(key, client, buf));
+            }
+        }
+        else if (result == 0)
+        {
+            // read interest but nothing to read? wtf?
+            System.out.println("NOOBISH ERROR 2 THE MISSION");
+            System.exit(0);
+        }
+        else if (result == -1)
+        {
+            this.closeConnectionImpl(con);
+        }
+        else
+        {
+            con.onForcedDisconnection();
+            this.closeConnectionImpl(con);
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected void readUDPPacket(SelectionKey key)
+    {
+        int result = -2;
+        ByteBuffer buf = READ_BUFFER;
+        
+        DatagramChannel dc = ((DatagramChannel) key.channel());
+        if (!dc.isConnected())
+        {
+            try
+            {
+                dc.configureBlocking(false);
+                SocketAddress address = dc.receive(buf);
+                buf.flip();
+                this._udpHeaderHandler.onUDPConnection(this, dc, address, buf);
+            }
+            catch (IOException e)
+            {
+
+            }
+            buf.clear();
+        }
+        else
+        {
+            //System.err.println("UDP CONN "+buf.remaining());
+            try
+            {
+                result = dc.read(buf);
+            }
+            catch (IOException e)
+            {
+                //error handling goes bellow
+                //System.err.println("UDP ERR: "+e.getMessage());
+            }
+
+            //System.out.println("LEU: "+result+" pos: "+buf.position());
+            if (result > 0)
+            {
+                buf.flip();
+                // try to read as many packets as possible
+                while (this.tryReadUDPPacket(key, buf));
+            }
+            else if (result == 0)
+            {
+                // read interest but nothing to read? wtf?
+                System.out.println("CRITICAL ERROR ON SELECTOR");
+                System.exit(0);
+            }
+            else
+            {
+                // TODO kill and cleanup this UDP connection
+                //System.err.println("UDP ERROR: "+result);
+            }
+        }
+    }
+
+    /*protected boolean tryReadPacket(T client, ByteBuffer buf)
+    {
+        MMOConnection con = client.getConnection();
+        //System.out.println("BUFF POS ANTES DE LER: "+buf.position()+" - REMAINING: "+buf.remaining());
+
+        if (buf.hasRemaining())
+        {
+            int result = buf.remaining();
+            
+            // then check if there are enough bytes for the header
+            if (result >= HEADER_SIZE)
+            {
+                // then read header and check if we have the whole packet
+                int size = this.getHeaderValue(buf);
+                //System.out.println("IF: ("+size+" <= "+result+") => (size <= result)");
+                if (size <= result)
+                {
+                    //System.out.println("BOA");
+
+                    // avoid parsing dummy packets (packets without body)
+                    if (size > HEADER_SIZE)
+                    {
+                        this.parseClientPacket(this.getPacketHandler(), buf, size, client);
+                    }
+
+                    // if we are done with this buffer
+                    if (!buf.hasRemaining())
+                    {
+                        //System.out.println("BOA 2");
+                        if (buf != READ_BUFFER)
+                        {
+                            con.setReadBuffer(null);
+                            this.recycleBuffer(buf);
+                        }
+                        else
+                        {
+                            READ_BUFFER.clear();
+                        }
+
+                        return false;
+                    }
+                    else
+                    {
+                        // nothing
+                    }
+
+                    return true;
+                }
+                else
+                {
+                    //System.out.println("ENABLEI");
+                    client.getConnection().enableReadInterest();
+                    
+                    //System.out.println("LIMIT "+buf.limit());
+                    if (buf == READ_BUFFER)
+                    {
+                        buf.position(buf.position() - HEADER_SIZE);
+                        this.allocateReadBuffer(con);
+                    }
+                    else
+                    {
+                        buf.position(buf.position() - HEADER_SIZE);
+                        buf.compact();
+                    }
+                    return false;
+                }
+            }
+            else
+            {
+                if (buf == READ_BUFFER)
+                {
+                    this.allocateReadBuffer(con);
+                }
+                else
+                {
+                    buf.compact();
+                }
+                return false;
+            }
+        }
+        else
+        {
+            //con.disableReadInterest();
+            return false; //empty buffer
+        }
+    }*/
+    
+    @SuppressWarnings("unchecked")
+    protected boolean tryReadPacket2(SelectionKey key, T client, ByteBuffer buf)
+    {
+        MMOConnection<T> con = client.getConnection();
+        //System.out.println("BUFF POS ANTES DE LER: "+buf.position()+" - REMAINING: "+buf.remaining());
+
+        if (buf.hasRemaining())
+        {
+            TCPHeaderHandler<T> handler = _tcpHeaderHandler;
+            // parse all jeaders
+            HeaderInfo<T> ret;
+            while (!handler.isChildHeaderHandler())
+            {
+                handler.handleHeader(key, buf);
+                handler = handler.getSubHeaderHandler();
+            }
+            // last header
+            ret = handler.handleHeader(key, buf);
+            
+            if (ret != null)
+            {
+                int result = buf.remaining();
+
+                // then check if header was processed
+                if (ret.headerFinished())
+                {
+                    // get expected packet size
+                    int size = ret.getDataPending();
+
+                    //System.out.println("IF: ("+size+" <= "+result+") => (size <= result)");
+                    // do we got enough bytes for the packet?
+                    if (size <= result)
+                    {
+                        // avoid parsing dummy packets (packets without body)
+                        if (size > 0)
+                        {
+                            int pos = buf.position();
+                            this.parseClientPacket(this.getPacketHandler(), buf, size, client);
+                            buf.position(pos + size);
+                        }
+
+                        // if we are done with this buffer
+                        if (!buf.hasRemaining())
+                        {
+                            //System.out.println("BOA 2");
+                            if (buf != READ_BUFFER)
+                            {
+                                con.setReadBuffer(null);
+                                this.recycleBuffer(buf);
+                            }
+                            else
+                            {
+                                READ_BUFFER.clear();
+                            }
+
+                            return false;
+                        }
+                        else
+                        {
+                            // nothing
+                        }
+
+                        return true;
+                    }
+                    else
+                    {
+                        // we dont have enough bytes for the dataPacket so we need to read
+                        client.getConnection().enableReadInterest();
+
+                        //System.out.println("LIMIT "+buf.limit());
+                        if (buf == READ_BUFFER)
+                        {
+                            buf.position(buf.position() - HEADER_SIZE);
+                            this.allocateReadBuffer(con);
+                        }
+                        else
+                        {
+                            buf.position(buf.position() - HEADER_SIZE);
+                            buf.compact();
+                        }
+                        return false;
+                    }
+                }
+                else
+                {
+                    // we dont have enough data for header so we need to read
+                    client.getConnection().enableReadInterest();
+
+                    if (buf == READ_BUFFER)
+                    {
+                        this.allocateReadBuffer(con);
+                    }
+                    else
+                    {
+                        buf.compact();
+                    }
+                    return false;
+                }
+            }
+            else
+            {
+                // null ret means critical error
+                // kill the connection
+                this.closeConnectionImpl(con);
+                return false;
+            }
+        }
+        else
+        {
+            //con.disableReadInterest();
+            return false; //empty buffer
+        }
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected boolean tryReadUDPPacket(SelectionKey key, ByteBuffer buf)
+    {
+        if (buf.hasRemaining())
+        {
+            UDPHeaderHandler<T> handler = _udpHeaderHandler;
+            // parse all jeaders
+            HeaderInfo<T> ret;
+            while (!handler.isChildHeaderHandler())
+            {
+                handler.handleHeader(buf);
+                handler = handler.getSubHeaderHandler();
+            }
+            // last header
+            ret = handler.handleHeader(buf);
+            
+            if (ret != null)
+            {
+                int result = buf.remaining();
+                
+                
+                // then check if header was processed
+                if (ret.headerFinished())
+                {
+                    T client = ret.getClient();
+                    MMOConnection<T> con = client.getConnection();
+
+                    // get expected packet size
+                    int size = ret.getDataPending();
+
+                    //System.out.println("IF: ("+size+" <= "+result+") => (size <= result)");
+                    // do we got enough bytes for the packet?
+                    if (size <= result)
+                    {
+                        if (ret.isMultiPacket())
+                        {
+                            while (buf.hasRemaining())
+                            {
+                                this.parseClientPacket(_udpPacketHandler, buf, buf.remaining(), client);
+                            }
+                        }
+                        else
+                        {
+                            // avoid parsing dummy packets (packets without body)
+                            if (size > 0)
+                            {
+                                int pos = buf.position();
+                                this.parseClientPacket(_udpPacketHandler, buf, size, client);
+                                buf.position(pos + size);
+                            }
+                        }
+
+                        // if we are done with this buffer
+                        if (!buf.hasRemaining())
+                        {
+                            //System.out.println("BOA 2");
+                            if (buf != READ_BUFFER)
+                            {
+                                con.setReadBuffer(null);
+                                this.recycleBuffer(buf);
+                            }
+                            else
+                            {
+                                READ_BUFFER.clear();
+                            }
+
+                            return false;
+                        }
+                        else
+                        {
+                            // nothing
+                        }
+
+                        return true;
+                    }
+                    else
+                    {
+                        // we dont have enough bytes for the dataPacket so we need to read
+                        client.getConnection().enableReadInterest();
+
+                        //System.out.println("LIMIT "+buf.limit());
+                        if (buf == READ_BUFFER)
+                        {
+                            buf.position(buf.position() - HEADER_SIZE);
+                            this.allocateReadBuffer(con);
+                        }
+                        else
+                        {
+                            buf.position(buf.position() - HEADER_SIZE);
+                            buf.compact();
+                        }
+                        return false;
+                    }
+                }
+                else
+                {
+                    buf.clear(); // READ_BUFFER
+                    return false;
+                }
+            }
+            else
+            {
+                buf.clear(); // READ_BUFFER
+                return false;
+            }
+        }
+        else
+        {
+            //con.disableReadInterest();
+            buf.clear();
+            return false; //empty buffer
+        }
+    }
+
+    protected void allocateReadBuffer(MMOConnection con)
+    {
+        //System.out.println("con: "+Integer.toHexString(con.hashCode()));
+        //Util.printHexDump(READ_BUFFER);
+        con.setReadBuffer(this.getPooledBuffer().put(READ_BUFFER));
+        READ_BUFFER.clear();
+    }
+
+    protected void parseClientPacket(IPacketHandler<T> handler, ByteBuffer buf, int dataSize, T client)
+    {
+        int pos = buf.position();
+        
+        boolean ret = client.decrypt(buf, dataSize);
+        
+        buf.position(pos);
+        
+        //System.out.println("pCP -> BUF: POS: "+buf.position()+" - LIMIT: "+buf.limit()+" == Packet: SIZE: "+dataSize);
+        
+        if (buf.hasRemaining() && ret)
+        {
+            //  apply limit
+            int limit = buf.limit();
+            buf.limit(pos + dataSize);
+            //System.out.println("pCP2 -> BUF: POS: "+buf.position()+" - LIMIT: "+buf.limit()+" == Packet: SIZE: "+size);
+            ReceivablePacket<T> cp = handler.handlePacket(buf, client);
+
+            if (cp != null)
+            {
+                cp.setByteBuffer(buf);
+                cp.setClient(client);
+                
+                if (cp.read())
+                {
+                    this.getExecutor().execute(cp);
+                }
+            }
+            buf.limit(limit);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    /*protected void writePacket(SelectionKey key)
+    {
+        T client = ((T) key.attachment());
+        MMOConnection<T> con = client.getConnection();
+
+        ByteBuffer buf;
+        boolean sharedBuffer = false;
+        if ((buf = con.getWriteBuffer()) == null)
+        {
+            SendablePacket<T> sp = null;
+            synchronized (con.getSendQueue())
+            {
+                if (!con.getSendQueue().isEmpty())
+                {
+                    sp = con.getSendQueue().removeFirst();
+                }
+            }
+            if (sp == null)
+            {
+                System.out.println("OMG WRITE BUT NO WRITE");
+                return;
+            }
+            //System.out.println("WRITING: "+sp.getClass().getSimpleName());
+            this.prepareWriteBuffer(client, sp);
+            buf = sp.getByteBuffer();
+            //System.out.println("WRITED:: "+sp.getClass().getSimpleName());
+            //System.out.println("wp:" +buf.position());
+            buf.flip();
+            
+            sharedBuffer = true;
+        }
+
+        int size = buf.remaining();
+        int result = -1;
+
+        try
+        {
+            result = con.getSocketChannel().write(buf);
+        }
+        catch (IOException e)
+        {
+            // error handling goes on the if bellow
+        }
+
+        // check if no error happened
+        if (result > 0)
+        {
+            // check if we writed everything
+            if (result == size)
+            {
+                //System.out.println("WRITEI COMPLETO");
+                synchronized (con.getSendQueue())
+                {
+                    if (con.getSendQueue().isEmpty())
+                    {
+                        con.disableWriteInterest();
+                    }
+                }
+
+                // if it was a pooled buffer we can recycle
+                if (!sharedBuffer)
+                {
+                    this.recycleBuffer(buf);
+                    con.setWriteBuffer(null);
+                }
+            }
+            else //incomplete write
+            {
+                //System.out.println("WRITEI INCOMPLETO");
+                // if its the main buffer allocate a pool one
+                if (sharedBuffer)
+                {
+                    con.setWriteBuffer(this.getPooledBuffer().put(buf));
+                }
+                else
+                {
+                    con.setWriteBuffer(buf);
+                }
+            }
+        }
+        else
+        {
+            con.onForcedDisconnection();
+            this.closeConnectionImpl(con);
+        }
+    }*/
+
+    protected void prepareWriteBuffer(T client, SendablePacket<T> sp)
+    {
+        WRITE_BUFFER.clear();
+
+        //set the write buffer
+        sp.setByteBuffer(WRITE_BUFFER);
+
+        // reserve space for the size
+        int headerPos = sp.getByteBuffer().position();
+        int headerSize = sp.getHeaderSize();
+        sp.getByteBuffer().position(headerPos + headerSize);
+
+        // write contents
+        sp.write();
+        
+
+        int dataSize = sp.getByteBuffer().position() - headerPos - headerSize;
+        sp.getByteBuffer().position(headerPos + headerSize);
+        client.encrypt(sp.getByteBuffer(), dataSize);
+        
+        // write size
+        sp.writeHeader(dataSize);
+        //sp.writeHeader(HEADER_TYPE, headerPos);
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected void writePacket2(SelectionKey key)
+    {
+        MMOConnection<T> con = (MMOConnection<T>) key.attachment();
+        T client = con.getClient();
+        
+        this.prepareWriteBuffer2(con);
+        DIRECT_WRITE_BUFFER.flip();
+        
+        int size = DIRECT_WRITE_BUFFER.remaining();
+        
+        //System.err.println("WRITE SIZE: "+size);
+        int result = -1;
+
+        try
+        {
+            result = con.getWritableChannel().write(DIRECT_WRITE_BUFFER);
+        }
+        catch (IOException e)
+        {
+            // error handling goes on the if bellow
+            System.err.println("IOError: "+e.getMessage());
+        }
+
+        // check if no error happened
+        if (result >= 0)
+        {
+            // check if we writed everything
+            if (result == size)
+            {
+                // complete write
+                //System.err.println("FULL WRITE");
+                //System.err.flush();
+                
+                // if there was a a pending write then we need to finish the operation
+                /*if (con.getWriterMark() > 0)
+                {
+                    con.finishPrepending(con.getWriterMark());
+                }*/
+                
+                synchronized (con.getSendQueue())
+                {
+                    if (con.getSendQueue().isEmpty() && !con.hasPendingWriteBuffer())
+                    {
+                        con.disableWriteInterest();
+                    }
+                }
+            }
+            else //incomplete write
+            {               
+                con.createWriteBuffer(DIRECT_WRITE_BUFFER);
+                //System.err.println("DEBUG: INCOMPLETE WRITE - write size: "+size);
+                //System.err.flush();
+            }
+            
+            if (result == 0)
+            {
+                //System.err.println("DEBUG: write result: 0 - write size: "+size+" - DWB rem: "+DIRECT_WRITE_BUFFER.remaining());
+                //System.err.flush();
+            }
+            
+            
+        }
+        else
+        {
+            //System.err.println("IOError: "+result);
+            //System.err.flush();
+            con.onForcedDisconnection();
+            this.closeConnectionImpl(con);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void prepareWriteBuffer2(MMOConnection<T> con)
+    {
+        DIRECT_WRITE_BUFFER.clear();
+
+
+        // if theres pending content add it
+        if (con.hasPendingWriteBuffer())
+        {
+            con.movePendingWriteBufferTo(DIRECT_WRITE_BUFFER);
+            //System.err.println("ADDED PENDING TO DIRECT "+DIRECT_WRITE_BUFFER.position());
+        }
+
+        if (DIRECT_WRITE_BUFFER.remaining() > 1 && !con.hasPendingWriteBuffer())
+        {
+            
+            int i = 0;
+
+            SemiConcurrentLinkedList<SendablePacket<T>> sendQueue = con.getSendQueue();
+            SemiConcurrentLinkedList<SendablePacket<T>>.Node<SendablePacket<T>> n, temp, end;
+            SendablePacket<T> sp;
+
+            for (n = sendQueue.getStart(), end = sendQueue.getEnd(); (n = n.getNext()) != end && i++ < MAX_SEND_PER_PASS;)
+            {
+                sp = n.getValue();
+                // put into WriteBuffer
+                this.putPacketIntoWriteBuffer(con.getClient(), sp);
+
+                // delete packet from queue
+                temp = n.getPrevious();
+                sendQueue.remove(n);
+                n = temp;
+
+                WRITE_BUFFER.flip();
+                //System.err.println("WB SIZE: "+WRITE_BUFFER.limit());
+                if (DIRECT_WRITE_BUFFER.remaining() >= WRITE_BUFFER.limit())
+                {
+                    /*if (i == 0)
+                    {
+                        // mark begining of new data from previous pending data
+                        con.setWriterMark(DIRECT_WRITE_BUFFER.position());
+                    }*/
+                    DIRECT_WRITE_BUFFER.put(WRITE_BUFFER);
+                }
+                else
+                {
+                    // there is no more space in the direct buffer
+                    //con.addWriteBuffer(this.getPooledBuffer().put(WRITE_BUFFER));
+                    con.createWriteBuffer(WRITE_BUFFER);
+                    break;
+                }
+            }
+        }
+    }
+    
+    protected final void putPacketIntoWriteBuffer(T client, SendablePacket<T> sp)
+    {
+        WRITE_BUFFER.clear();
+        
+        // set the write buffer
+        sp.setByteBuffer(WRITE_BUFFER);
+
+        // reserve space for the size
+        int headerPos = sp.getByteBuffer().position();
+        int headerSize = sp.getHeaderSize();
+        sp.getByteBuffer().position(headerPos + headerSize);
+        
+        // write content to buffer
+        sp.write();
+        
+        // size (incl header)
+        int dataSize = sp.getByteBuffer().position() - headerPos - headerSize;
+        sp.getByteBuffer().position(headerPos + headerSize);
+        client.encrypt(sp.getByteBuffer(), dataSize);
+        
+        // recalculate size after encryption
+        dataSize = sp.getByteBuffer().position() - headerPos - headerSize;
+        
+        // prepend header
+        //this.prependHeader(headerPos, size);
+        sp.getByteBuffer().position(headerPos);
+        sp.writeHeader(dataSize);
+        sp.getByteBuffer().position(headerPos + headerSize + dataSize);
+    }
+    
+    /*protected void prependHeader(int pos, int size)
+    {
+        switch (HEADER_TYPE)
+        {
+            case BYTE_HEADER:
+                WRITE_BUFFER.put(pos, (byte) size);
+                break;
+            case SHORT_HEADER:
+                WRITE_BUFFER.putShort(pos, (short) size);
+                break;
+            case INT_HEADER:
+                WRITE_BUFFER.putInt(pos, size);
+                break;
+        }
+    }*/
+
+    /*protected int getHeaderValue(ByteBuffer buf)
+    {
+        switch (HEADER_TYPE)
+        {
+            case BYTE_HEADER:
+                return buf.get() & 0xFF;
+            case SHORT_HEADER:
+                return buf.getShort() & 0xFFFF;
+            case INT_HEADER:
+                return buf.getInt();
+        }
+        return -1; // O.o
+    }*/
+
+    protected void setSelector(Selector selector)
+    {
+        _selector = selector;
+    }
+
+    public Selector getSelector()
+    {
+        return _selector;
+    }
+
+    protected void setExecutor(IMMOExecutor<T> executor)
+    {
+        _executor = executor;
+    }
+
+    protected IMMOExecutor<T> getExecutor()
+    {
+        return _executor;
+    }
+
+    public IPacketHandler<T> getPacketHandler()
+    {
+        return _packetHandler;
+    }
+
+    protected void setClientFactory(IClientFactory<T> clientFactory)
+    {
+        _clientFactory = clientFactory;
+    }
+
+    public IClientFactory<T> getClientFactory()
+    {
+        return _clientFactory;
+    }
+
+    public void setAcceptFilter(IAcceptFilter acceptFilter)
+    {
+        _acceptFilter = acceptFilter;
+    }
+    
+    public IAcceptFilter getAcceptFilter()
+    {
+        return _acceptFilter;
+    }
+    
+    public void closeConnection(MMOConnection<T> con)
+    {
+        synchronized (this.getPendingClose())
+        {
+            this.getPendingClose().addLast(con);
+        }
+    }
+    
+    protected void closeConnectionImpl(MMOConnection<T> con)
+    {
+        try
+        {
+            // notify connection
+            con.onDisconection();
+        }
+        finally
+        {
+            try
+            {
+                // close socket and the SocketChannel
+                con.getSocket().close();
+            }
+            catch (IOException e)
+            {
+                // ignore, we are closing anyway
+            }
+            finally
+            {
+                con.releaseBuffers();
+                // clear attachment
+                con.getSelectionKey().attach(null);
+                // cancel key
+                con.getSelectionKey().cancel();
+            }
+        }
+    }
+    
+    protected FastList<MMOConnection<T>> getPendingClose()
+    {
+        return _pendingClose;
+    }
+    
+    public void shutdown()
+    {
+        _shutdown = true;
+    }
+
+    public boolean isShuttingDown()
+    {
+        return _shutdown;
+    }
+
+    protected void closeAllChannels()
+    {
+        Set<SelectionKey> keys = this.getSelector().keys();
+        for (Iterator<SelectionKey> iter = keys.iterator(); iter.hasNext();)
+        {
+            SelectionKey key = iter.next();
+            try
+            {
+                key.channel().close();
+            }
+            catch (IOException e)
+            {
+                // ignore
+            }
+        }
+    }
+
+    protected void closeSelectorThread() 
+    {
+        this.closeAllChannels();
+        try
+        {
+            this.getSelector().close();
+        }
+        catch (IOException e)
+        {
+            // Ignore
+        }
+    }
+}

+ 24 - 19
MMOCore/src/com/l2jserver/mmocore/network/SendablePacket.java → MMOCore/src/org/mmocore/network/SendablePacket.java

@@ -15,10 +15,7 @@
  *
  * http://www.gnu.org/copyleft/gpl.html
  */
-package com.l2jserver.mmocore.network;
-
-
-import com.l2jserver.mmocore.network.SelectorConfig.HeaderSize;
+package org.mmocore.network;
 
 /**
  * @author KenM
@@ -26,6 +23,26 @@ import com.l2jserver.mmocore.network.SelectorConfig.HeaderSize;
  */
 public abstract class SendablePacket<T extends MMOClient> extends AbstractPacket<T>
 {
+    protected void putShort(int value)
+    {
+        this.getByteBuffer().putShort((short) value);
+    }
+    
+    protected void putInt(int value)
+    {
+        this.getByteBuffer().putInt(value);
+    }
+    
+    protected void putDouble(double value)
+    {
+        this.getByteBuffer().putDouble(value);
+    }
+    
+    protected void putFloat(float value)
+    {
+        this.getByteBuffer().putFloat(value);
+    }
+    
     protected void writeC(int data)
     {
         this.getByteBuffer().put((byte) data);
@@ -73,19 +90,7 @@ public abstract class SendablePacket<T extends MMOClient> extends AbstractPacket
     
     protected abstract void write();
     
-    protected void writeHeader(HeaderSize ht, int pos)
-    {
-        switch (ht)
-        {
-            case BYTE_HEADER:
-                this.getByteBuffer().put(pos, (byte) this.getByteBuffer().position());
-                break;
-            case SHORT_HEADER:
-                this.getByteBuffer().putShort(pos, (short) this.getByteBuffer().position());
-                break;
-            case INT_HEADER:
-                this.getByteBuffer().putInt(pos, this.getByteBuffer().position());
-                break;
-        }
-    }
+    protected abstract int getHeaderSize();
+    
+    protected abstract void writeHeader(int dataSize);
 }

+ 48 - 0
MMOCore/src/org/mmocore/network/TCPHeaderHandler.java

@@ -0,0 +1,48 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+
+/**
+ * @author KenM
+ *
+ */
+public abstract class TCPHeaderHandler<T extends MMOClient> extends HeaderHandler<T, TCPHeaderHandler<T>>
+{
+    /**
+     * @param subHeaderHandler
+     */
+    public TCPHeaderHandler(TCPHeaderHandler<T> subHeaderHandler)
+    {
+        super(subHeaderHandler);
+    }
+
+    private final HeaderInfo<T> _headerInfoReturn = new HeaderInfo<T>();
+    
+    public abstract HeaderInfo handleHeader(SelectionKey key, ByteBuffer buf);
+    
+    /**
+     * @return the headerInfoReturn
+     */
+    protected final HeaderInfo<T> getHeaderInfoReturn()
+    {
+        return _headerInfoReturn;
+    }
+}

+ 71 - 0
MMOCore/src/org/mmocore/network/TCPSocket.java

@@ -0,0 +1,71 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * @author KenM
+ *
+ */
+public class TCPSocket implements ISocket
+{
+    private final Socket _socket;
+
+    public TCPSocket(Socket socket)
+    {
+        _socket = socket;
+    }
+    
+    /* (non-Javadoc)
+     * @see com.l2jserver.mmocore.network.ISocket#close()
+     */
+    public void close() throws IOException
+    {
+        _socket.close();
+    }
+
+    /* (non-Javadoc)
+     * @see com.l2jserver.mmocore.network.ISocket#getReadableByteChannel()
+     */
+    public ReadableByteChannel getReadableByteChannel()
+    {
+        return _socket.getChannel();
+    }
+
+    /* (non-Javadoc)
+     * @see com.l2jserver.mmocore.network.ISocket#getWritableByteChannel()
+     */
+    public WritableByteChannel getWritableByteChannel()
+    {
+        return _socket.getChannel();
+    }
+
+    /* (non-Javadoc)
+     * @see org.mmocore.network.ISocket#getInetAddress()
+     */
+    public InetAddress getInetAddress()
+    {
+        return _socket.getInetAddress();
+    }
+    
+}

+ 51 - 0
MMOCore/src/org/mmocore/network/UDPHeaderHandler.java

@@ -0,0 +1,51 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+
+/**
+ * @author KenM
+ *
+ */
+public abstract class UDPHeaderHandler<T extends MMOClient>  extends HeaderHandler<T, UDPHeaderHandler<T>>
+{
+    /**
+     * @param subHeaderHandler
+     */
+    public UDPHeaderHandler(UDPHeaderHandler<T> subHeaderHandler)
+    {
+        super(subHeaderHandler);
+    }
+
+    private final HeaderInfo<T> _headerInfoReturn = new HeaderInfo<T>();
+    
+    protected abstract HeaderInfo handleHeader(ByteBuffer buf);
+    
+    protected abstract void onUDPConnection(SelectorThread<T> selector, DatagramChannel dc, SocketAddress key, ByteBuffer buf);
+    
+    /**
+     * @return the headerInfoReturn
+     */
+    protected final HeaderInfo<T> getHeaderInfoReturn()
+    {
+        return _headerInfoReturn;
+    }
+}

+ 71 - 0
MMOCore/src/org/mmocore/network/UDPSocket.java

@@ -0,0 +1,71 @@
+/* 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, 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.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package org.mmocore.network;
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * @author KenM
+ *
+ */
+public class UDPSocket implements ISocket
+{
+    private final DatagramSocket _socket;
+
+    public UDPSocket(DatagramSocket socket)
+    {
+        _socket = socket;
+    }
+    
+    /* (non-Javadoc)
+     * @see com.l2jserver.mmocore.network.ISocket#close()
+     */
+    public void close() throws IOException
+    {
+        _socket.close();
+    }
+
+    /* (non-Javadoc)
+     * @see com.l2jserver.mmocore.network.ISocket#getReadableByteChannel()
+     */
+    public ReadableByteChannel getReadableByteChannel()
+    {
+        return _socket.getChannel();
+    }
+
+    /* (non-Javadoc)
+     * @see com.l2jserver.mmocore.network.ISocket#getWritableByteChannel()
+     */
+    public WritableByteChannel getWritableByteChannel()
+    {
+        return _socket.getChannel();
+    }
+
+    /* (non-Javadoc)
+     * @see org.mmocore.network.ISocket#getInetAddress()
+     */
+    public InetAddress getInetAddress()
+    {
+        return _socket.getInetAddress();
+    }
+    
+}

+ 76 - 0
MMOCore/src/org/mmocore/test/testserver/SelectorHelper.java

@@ -0,0 +1,76 @@
+/**
+ * 
+ */
+package org.mmocore.test.testserver;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.mmocore.network.HeaderInfo;
+import org.mmocore.network.TCPHeaderHandler;
+import org.mmocore.network.IClientFactory;
+import org.mmocore.network.IMMOExecutor;
+import org.mmocore.network.IPacketHandler;
+import org.mmocore.network.MMOConnection;
+import org.mmocore.network.ReceivablePacket;
+
+
+/**
+ * @author KenM
+ *
+ */
+public class SelectorHelper extends TCPHeaderHandler<ServerClient> implements IPacketHandler<ServerClient>, IMMOExecutor<ServerClient>, IClientFactory<ServerClient>
+{
+    public SelectorHelper()
+    {
+        super(null);
+    }
+
+    ExecutorService _tpe = Executors.newFixedThreadPool(4);
+    
+    public ReceivablePacket<ServerClient> handlePacket(ByteBuffer buf, ServerClient client)
+    {
+        return new TestRecvPacket();
+    }
+
+    public void execute(ReceivablePacket<ServerClient> packet)
+    {
+        _tpe.execute(packet);
+    }
+
+    public ServerClient create(MMOConnection<ServerClient> con)
+    {
+        System.out.println("new client");
+        /*try
+        {
+            con.getSocketChannel().socket().setSendBufferSize(2*65536);
+        }
+        catch (SocketException e)
+        {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }*/
+        ServerClient sc = new ServerClient(con);
+        return sc;
+    }
+
+    @Override
+    public HeaderInfo<ServerClient> handleHeader(SelectionKey key, ByteBuffer buf)
+    {
+        int avaliable = buf.remaining();
+        if (avaliable >= 2)
+        {
+            int dataSize = buf.getShort() & 0xFFFF;
+            System.err.println("DATASIZE: "+dataSize);
+            return this.getHeaderInfoReturn().set(0, dataSize - 2, false, null);
+        }
+        else
+        {
+            System.err.println("HEADER PENDING: "+(2 - avaliable));
+            return this.getHeaderInfoReturn().set(2 - avaliable, 0, false, null);
+        }
+    }
+
+}

+ 48 - 0
MMOCore/src/org/mmocore/test/testserver/ServerClient.java

@@ -0,0 +1,48 @@
+/**
+ * 
+ */
+package org.mmocore.test.testserver;
+
+import java.nio.ByteBuffer;
+
+import org.mmocore.network.MMOClient;
+import org.mmocore.network.MMOConnection;
+import org.mmocore.network.SendablePacket;
+
+
+/**
+ * @author KenM
+ *
+ */
+public final class ServerClient extends MMOClient<MMOConnection<ServerClient>>
+{
+
+    public ServerClient(MMOConnection<ServerClient> con)
+    {
+        super(con);
+    }
+
+    @Override
+    public boolean decrypt(ByteBuffer buf, int size)
+    {
+        return true;
+    }
+
+    @Override
+    public boolean encrypt(ByteBuffer buf, int size)
+    {
+        buf.position(buf.position() + size);
+        return true;
+    }
+
+    public void sendPacket(SendablePacket<ServerClient> sp)
+    {
+        //System.out.println("SENDPACKET");
+        this.getConnection().sendPacket(sp);
+    }
+    
+    public void onDisconection()
+    {
+        //System.out.println("CLIENT DISCONNECTED\n=============================\n");
+    }
+}

+ 35 - 0
MMOCore/src/org/mmocore/test/testserver/TestRecvPacket.java

@@ -0,0 +1,35 @@
+/**
+ * 
+ */
+package org.mmocore.test.testserver;
+
+import org.mmocore.network.ReceivablePacket;
+
+/**
+ * @author KenM
+ *
+ */
+public class TestRecvPacket extends ReceivablePacket<ServerClient>
+{
+    private int _value;
+    
+    @Override
+    protected boolean read()
+    {
+        _value = readD();
+        return true;
+    }
+
+    @Override
+    public void run()
+    {
+        System.out.println("ServerRecebeu "+_value);
+        TestSendPacket tsp = new TestSendPacket(_value);
+        this.getClient().sendPacket(tsp);
+        this.getClient().sendPacket(tsp);
+        this.getClient().sendPacket(tsp);
+        this.getClient().sendPacket(tsp);
+        this.getClient().sendPacket(tsp);
+        this.getClient().sendPacket(tsp);
+    }
+}

+ 42 - 0
MMOCore/src/org/mmocore/test/testserver/TestSendPacket.java

@@ -0,0 +1,42 @@
+/**
+ * 
+ */
+package org.mmocore.test.testserver;
+
+import org.mmocore.network.SendablePacket;
+
+/**
+ * @author KenM
+ *
+ */
+public class TestSendPacket extends SendablePacket<ServerClient>
+{
+    private int _value;
+    
+    public TestSendPacket(int value)
+    {
+        _value = value;
+    }
+    
+    @Override
+    protected void write()
+    {
+        writeD(_value);
+        for (int i = 0; i < 48000; i++)
+        {
+            writeC(i%256);
+        }
+    }
+
+    @Override
+    protected int getHeaderSize()
+    {
+        return 2;
+    }
+
+    @Override
+    protected void writeHeader(int dataSize)
+    {
+        writeH(dataSize + 2);
+    }
+}

+ 39 - 0
MMOCore/src/org/mmocore/test/testserver/TestServer.java

@@ -0,0 +1,39 @@
+/**
+ * 
+ */
+package org.mmocore.test.testserver;
+
+import java.io.IOException;
+
+import org.mmocore.network.SelectorConfig;
+import org.mmocore.network.SelectorThread;
+
+
+/**
+ * @author KenM
+ *
+ */
+public class TestServer
+{
+    public static int PORT = 0xCAFE;
+    
+    public static void main(String[] args) throws IOException
+    {
+        if (args.length > 0)
+        {
+            PORT = Integer.parseInt(args[0]);
+        }
+        
+        SelectorHelper sh = new SelectorHelper();
+        SelectorConfig<ServerClient> ssc = new SelectorConfig<ServerClient>(null, sh);
+        ssc.setMaxSendPerPass(2);
+        SelectorThread<ServerClient> selector = new SelectorThread<ServerClient>(ssc, null, sh, sh, sh, null);
+        selector.openServerSocket(null, PORT);
+        //selector.openDatagramSocket();
+        selector.start();
+        
+        System.err.println("TestServer active on port "+PORT);
+    }
+
+
+}

+ 175 - 0
MMOCore/src/org/mmocore/util/collections/concurrent/SemiConcurrentLinkedList.java

@@ -0,0 +1,175 @@
+/**
+ * 
+ */
+package org.mmocore.util.collections.concurrent;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * This a semi-concurrent linked list implementation that aims to provide 
+ * a high speed solution that fullfill the most used pattern of the MMOCore.<BR>
+ * 
+ * Iterations forward only
+ * 1 thread appends (N if synchronized)
+ * 1 thread read/delete
+ * 
+ * @author KenM
+ *
+ */
+public class SemiConcurrentLinkedList<E> implements Iterable<E>
+{
+    private volatile Node<E> _start = new Node<E>();
+    private volatile Node<E> _end = new Node<E>();
+    private int _size;
+    
+    public SemiConcurrentLinkedList()
+    {
+        this.clear();
+    }
+    
+    public void addLast(E elem)
+    {
+        Node<E> oldEndNode = _end;
+        
+        // assign the value to the current last node
+        oldEndNode._value = elem;
+        
+        // create the new end node
+        Node<E> newEndNode = new Node<E>(oldEndNode, null, null);
+        
+        // assign the new end as the next of the current end
+        oldEndNode.setNext(newEndNode);
+        
+        // place the new end
+        _end = newEndNode;
+        
+        // increment list size
+        _size++;
+    }
+    
+    public void remove(Node<E> node)
+    {
+        Node<E> previous = node.getPrevious();
+        Node<E> next = node.getNext();
+        
+        // set the next to skip the node being deleted
+        previous.setNext(next);
+        
+        // adjust previous
+        next.setPrevious(previous);
+        
+        _size--;
+    }
+    
+    public int size()
+    {
+        return _size;
+    }
+    
+    public final boolean isEmpty()
+    {
+        return _size == 0;
+    }
+    
+    public void clear()
+    {
+        _start.setNext(_end);
+        _end.setPrevious(_start);
+    }
+    
+    public Node<E> getStart()
+    {
+        return _start;
+    }
+    
+    public Node<E> getEnd()
+    {
+        return _end;
+    }
+    
+    public Iterator<E> iterator()
+    {
+        return new SemiConcurrentIterator(_start);
+    }
+    
+    public class SemiConcurrentIterator implements Iterator<E>
+    {
+        private Node<E> _current;
+        //private Node<V> _last;
+        
+        protected SemiConcurrentIterator(Node<E> start)
+        {
+            _current = start;
+        }
+        
+        public boolean hasNext()
+        {
+            return _current.getNext() != _end;
+        }
+
+        public E next()
+        {
+            _current = _current.getNext();
+            if (_current == _end)
+            {
+                throw new NoSuchElementException();
+            }
+            return _current.getValue();
+        }
+
+        public void remove()
+        {
+            SemiConcurrentLinkedList.this.remove(_current);
+            _current = _current.getPrevious();
+        }
+    }
+    
+    public final class Node<T>
+    {
+        private Node<T> _previous;
+        private Node<T> _next;
+        private T _value;
+        
+        protected Node()
+        {
+        }
+        
+        protected Node(T value)
+        {
+            _value = value;
+        }
+        
+        protected Node(Node<T> previous, Node<T> next, T value)
+        {
+            _previous = previous;
+            _next = next;
+            _value = value;
+        }
+        
+        public Node<T> getNext()
+        {
+            return _next;
+        }
+        
+        protected void setNext(Node<T> node)
+        {
+            _next = node;
+        }
+        
+        public Node<T> getPrevious()
+        {
+            return _previous;
+        }
+        
+        protected void setPrevious(Node<T> node)
+        {
+            _previous = node;
+        }
+        
+        public T getValue()
+        {
+            return _value;
+        }
+    }
+}