// Path: src/net/corpwar/lib/corpnet/util/PeerConnected.java
// public class PeerConnected implements Serializable{
// 
//     public PeerConnected(String ipToPeer, int peerPort, long pingTime) {
//         this.peerPort = peerPort;
//         this.ipToPeer = ipToPeer;
//         this.pingTime = pingTime;
//     }
// 
//     public int peerPort;
// 
//     public String ipToPeer;
// 
//     public long pingTime;
// }
// 
// Path: src/net/corpwar/lib/corpnet/util/PeerList.java
// public class PeerList implements Serializable {
// 
//     public List<PeerConnected> peerConnected = new ArrayList<>();
// 
// 
// 
// }
// 
// Path: src/net/corpwar/lib/corpnet/util/SendDataQue.java
// public class SendDataQue {
// 
//     // Data to send
//     private byte[] aByte;
// 
//     // What kind of message to send
//     private NetworkSendType networkSendType;
// 
//     // When this data was added for sending
//     private long addedTime;
// 
//     public SendDataQue() {
//     }
// 
//     public SendDataQue setValues(byte[] bytes, NetworkSendType networkSendType) {
//         this.aByte = bytes;
//         this.networkSendType = networkSendType;
//         this.addedTime = System.currentTimeMillis();
//         return this;
//     }
// 
//     public byte[] getaByte() {
//         return aByte;
//     }
// 
//     public NetworkSendType getNetworkSendType() {
//         return networkSendType;
//     }
// 
//     public long getAddedTime() {
//         return addedTime;
//     }
// }
// 
// Path: src/net/corpwar/lib/corpnet/util/SerializationUtils.java
// public class SerializationUtils {
// 
//     private static SerializationUtils serializationUtils = null;
//     private static final Logger LOG = Logger.getLogger(SerializationUtils.class.getName());
//     private static final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 
// 
//     private SerializationUtils() {
//     }
// 
//     public static SerializationUtils getInstance() {
//         if (serializationUtils == null) {
//             serializationUtils = new SerializationUtils();
//         }
//         return serializationUtils;
//     }
// 
//     public <T> byte[] serialize(T obj) {
//         ObjectOutputStream oos = null;
//         try {
//             oos = new ObjectOutputStream(baos);
//             oos.writeObject(obj);
//             oos.reset();
//         } catch (IOException e) {
//             LOG.log(Level.SEVERE, "Error IOException", e);
//         } finally {
//             if (oos != null) {
//                 try {
//                     oos.close();
//                 } catch (IOException e) {
//                     LOG.log(Level.SEVERE, "Error IOException", e);
//                 }
//             }
//         }
//         byte[] byteObj = baos.toByteArray();
//         baos.reset();
//         return byteObj;
//     }
// 
//     public <T> T deserialize(byte[] bytes) {
//         T obj = null;
//         ByteArrayInputStream bais = null;
//         ObjectInputStream ois = null;
// 
//         try {
//             bais = new ByteArrayInputStream(bytes);
//             ois = new ObjectInputStream(bais);
//             obj = (T) ois.readObject();
//         } catch (IOException e) {
//             LOG.log(Level.SEVERE, "Error IOException", e);
//         } catch (ClassNotFoundException e) {
//             LOG.log(Level.SEVERE, "Error Class not found", e);
//             return null;
//         } finally {
//             if (ois != null) {
//                 try {
//                     ois.close();
//                 } catch (IOException e) {
//                     LOG.log(Level.SEVERE, "Error IOException", e);
//                 }
//             }
//         }
//         return obj;
//     }
// }


import java.util.logging.Level;
import java.util.logging.Logger;
import net.corpwar.lib.corpnet.master.*;
import net.corpwar.lib.corpnet.util.PeerConnected;
import net.corpwar.lib.corpnet.util.PeerList;
import net.corpwar.lib.corpnet.util.SendDataQue;
import net.corpwar.lib.corpnet.util.SerializationUtils;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
    public synchronized void sendData(Connection connection, byte[] data, NetworkSendType sendType) {
        byte[] sendData;
        ByteBuffer byteBufferSendData;
        LOG.log(Level.FINEST, "DataSent: " + data.length + " SendType: " + sendType.name());
        try {
            if (data.length + byteBufferSize <= bufferSize) {
                byteBufferSendData = ByteBuffer.allocate(byteBufferSize + data.length);
                sendingPackage = connection.getNetworkPackage(data, sendType);
                byteBufferSendData.putInt(protocolVersionHash).put((byte) sendType.getTypeCode()).putInt(sendingPackage.getSequenceNumber()).put(data);
                sendData = byteBufferSendData.array();
                DatagramPacket dp = new DatagramPacket(sendData, sendData.length, connection.getAddress(), connection.getPort());
                datagramSocket.send(dp);
                connection.getNetworkPackagePool().giveBack(sendingPackage);
            } else {
                int len = 0;
                byte[] dataToSend;
                int splitIdSize = 4;
                int splitId = connection.getGlobalSplitSequenceNumber();
                while (true) {
                    if (data.length - len > bufferSize - byteBufferSize - splitIdSize - 4) {
                        dataToSend = Arrays.copyOfRange(data, len, bufferSize - byteBufferSize - splitIdSize - 4 + len);
                        byteBufferSendData = ByteBuffer.allocate(bufferSize - 4);
                        byteBufferSendData.putInt(protocolVersionHash);
                        if (sendType == NetworkSendType.RELIABLE_GAME_DATA || sendType == NetworkSendType.RELIABLE_SPLIT_GAME_DATA) {
                            byteBufferSendData.put((byte) NetworkSendType.RELIABLE_SPLIT_GAME_DATA.getTypeCode());
                            sendingPackage = connection.getNetworkPackage(dataToSend, NetworkSendType.RELIABLE_SPLIT_GAME_DATA);
                        } else if (sendType == NetworkSendType.PEER_DATA || sendType == NetworkSendType.PEER_SPLIT_DATA) {
                            byteBufferSendData.put((byte) NetworkSendType.PEER_SPLIT_DATA.getTypeCode());
                            sendingPackage = connection.getNetworkPackage(dataToSend, NetworkSendType.PEER_SPLIT_DATA);
                        } else {
// Path: src/net/corpwar/lib/corpnet/util/PeerConnected.java
// public class PeerConnected implements Serializable{
// 
//     public PeerConnected(String ipToPeer, int peerPort, long pingTime) {
//         this.peerPort = peerPort;
//         this.ipToPeer = ipToPeer;
//         this.pingTime = pingTime;
//     }
// 
//     public int peerPort;
// 
//     public String ipToPeer;
// 
//     public long pingTime;
// }
// 
// Path: src/net/corpwar/lib/corpnet/util/PeerList.java
// public class PeerList implements Serializable {
// 
//     public List<PeerConnected> peerConnected = new ArrayList<>();
// 
// 
// 
// }
// 
// Path: src/net/corpwar/lib/corpnet/util/SendDataQue.java
// public class SendDataQue {
// 
//     // Data to send
//     private byte[] aByte;
// 
//     // What kind of message to send
//     private NetworkSendType networkSendType;
// 
//     // When this data was added for sending
//     private long addedTime;
// 
//     public SendDataQue() {
//     }
// 
//     public SendDataQue setValues(byte[] bytes, NetworkSendType networkSendType) {
//         this.aByte = bytes;
//         this.networkSendType = networkSendType;
//         this.addedTime = System.currentTimeMillis();
//         return this;
//     }
// 
//     public byte[] getaByte() {
//         return aByte;
//     }
// 
//     public NetworkSendType getNetworkSendType() {
//         return networkSendType;
//     }
// 
//     public long getAddedTime() {
//         return addedTime;
//     }
// }
// 
// Path: src/net/corpwar/lib/corpnet/util/SerializationUtils.java
// public class SerializationUtils {
// 
//     private static SerializationUtils serializationUtils = null;
//     private static final Logger LOG = Logger.getLogger(SerializationUtils.class.getName());
//     private static final ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 
// 
//     private SerializationUtils() {
//     }
// 
//     public static SerializationUtils getInstance() {
//         if (serializationUtils == null) {
//             serializationUtils = new SerializationUtils();
//         }
//         return serializationUtils;
//     }
// 
//     public <T> byte[] serialize(T obj) {
//         ObjectOutputStream oos = null;
//         try {
//             oos = new ObjectOutputStream(baos);
//             oos.writeObject(obj);
//             oos.reset();
//         } catch (IOException e) {
//             LOG.log(Level.SEVERE, "Error IOException", e);
//         } finally {
//             if (oos != null) {
//                 try {
//                     oos.close();
//                 } catch (IOException e) {
//                     LOG.log(Level.SEVERE, "Error IOException", e);
//                 }
//             }
//         }
//         byte[] byteObj = baos.toByteArray();
//         baos.reset();
//         return byteObj;
//     }
// 
//     public <T> T deserialize(byte[] bytes) {
//         T obj = null;
//         ByteArrayInputStream bais = null;
//         ObjectInputStream ois = null;
// 
//         try {
//             bais = new ByteArrayInputStream(bytes);
//             ois = new ObjectInputStream(bais);
//             obj = (T) ois.readObject();
//         } catch (IOException e) {
//             LOG.log(Level.SEVERE, "Error IOException", e);
//         } catch (ClassNotFoundException e) {
//             LOG.log(Level.SEVERE, "Error Class not found", e);
//             return null;
//         } finally {
//             if (ois != null) {
//                 try {
//                     ois.close();
//                 } catch (IOException e) {
//                     LOG.log(Level.SEVERE, "Error IOException", e);
//                 }
//             }
//         }
//         return obj;
//     }
// }


// Path: src/net/corpwar/lib/corpnet/PeerToPeer.java
import java.util.logging.Level;
import java.util.logging.Logger;
import net.corpwar.lib.corpnet.master.*;
import net.corpwar.lib.corpnet.util.PeerConnected;
import net.corpwar.lib.corpnet.util.PeerList;
import net.corpwar.lib.corpnet.util.SendDataQue;
import net.corpwar.lib.corpnet.util.SerializationUtils;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

    public synchronized void sendData(Connection connection, byte[] data, NetworkSendType sendType) {
        byte[] sendData;
        ByteBuffer byteBufferSendData;
        LOG.log(Level.FINEST, "DataSent: " + data.length + " SendType: " + sendType.name());
        try {
            if (data.length + byteBufferSize <= bufferSize) {
                byteBufferSendData = ByteBuffer.allocate(byteBufferSize + data.length);
                sendingPackage = connection.getNetworkPackage(data, sendType);
                byteBufferSendData.putInt(protocolVersionHash).put((byte) sendType.getTypeCode()).putInt(sendingPackage.getSequenceNumber()).put(data);
                sendData = byteBufferSendData.array();
                DatagramPacket dp = new DatagramPacket(sendData, sendData.length, connection.getAddress(), connection.getPort());
                datagramSocket.send(dp);
                connection.getNetworkPackagePool().giveBack(sendingPackage);
            } else {
                int len = 0;
                byte[] dataToSend;
                int splitIdSize = 4;
                int splitId = connection.getGlobalSplitSequenceNumber();
                while (true) {
                    if (data.length - len > bufferSize - byteBufferSize - splitIdSize - 4) {
                        dataToSend = Arrays.copyOfRange(data, len, bufferSize - byteBufferSize - splitIdSize - 4 + len);
                        byteBufferSendData = ByteBuffer.allocate(bufferSize - 4);
                        byteBufferSendData.putInt(protocolVersionHash);
                        if (sendType == NetworkSendType.RELIABLE_GAME_DATA || sendType == NetworkSendType.RELIABLE_SPLIT_GAME_DATA) {
                            byteBufferSendData.put((byte) NetworkSendType.RELIABLE_SPLIT_GAME_DATA.getTypeCode());
                            sendingPackage = connection.getNetworkPackage(dataToSend, NetworkSendType.RELIABLE_SPLIT_GAME_DATA);
                        } else if (sendType == NetworkSendType.PEER_DATA || sendType == NetworkSendType.PEER_SPLIT_DATA) {
                            byteBufferSendData.put((byte) NetworkSendType.PEER_SPLIT_DATA.getTypeCode());
                            sendingPackage = connection.getNetworkPackage(dataToSend, NetworkSendType.PEER_SPLIT_DATA);
                        } else {
                            byteBufferSendData.put((byte) NetworkSendType.UNRELIABLE_SPLIT_GAME_DATA.getTypeCode());