Removed never used NET library
This commit is contained in:
parent
69b72ef90d
commit
c984b63cee
@ -18,7 +18,6 @@ that are detailed in their respective Readme file (if any).
|
|||||||
- `pandalib-players` A library to handle classes representing online or offline players;
|
- `pandalib-players` A library to handle classes representing online or offline players;
|
||||||
- `pandalib-players-permissible` An extension of `pandalib-players` with support for the permission system `pandalib-permissions`;
|
- `pandalib-players-permissible` An extension of `pandalib-players` with support for the permission system `pandalib-permissions`;
|
||||||
- `pandalib-netapi` A poorly designed, but working TCP network library;
|
- `pandalib-netapi` A poorly designed, but working TCP network library;
|
||||||
- `pandalib-net` A better-designed, packet-based TCP network library (_still in development_);
|
|
||||||
- `pandalib-commands` An abstract command manager working on top of [Brigadier](https://github.com/Mojang/brigadier);
|
- `pandalib-commands` An abstract command manager working on top of [Brigadier](https://github.com/Mojang/brigadier);
|
||||||
- `pandalib-cli` Utility and helper classes for a standalone CLI Java application.
|
- `pandalib-cli` Utility and helper classes for a standalone CLI Java application.
|
||||||
- `pandalib-core` A catch-all module for some helper classes that didn’t have their own module yet;
|
- `pandalib-core` A catch-all module for some helper classes that didn’t have their own module yet;
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
# pandalib-net
|
|
||||||
|
|
||||||
A TCP network library that uses the standard Java socket API, to ease the ommunication between the different processes
|
|
||||||
running the server network Pandacube.
|
|
||||||
|
|
||||||
It’s still in development (actually not touched since years), and it’s supposed to be a replacement for the old
|
|
||||||
`pandalib-netapi`. This module is then marked as Beta using the Google Guava annotation.
|
|
||||||
|
|
||||||
- Packet based communication
|
|
||||||
- Supports Request/Answer packets
|
|
||||||
- Uses binary packet id and data
|
|
||||||
* Input streams are handled in separate Threads
|
|
@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
|
||||||
<artifactId>pandalib-parent</artifactId>
|
|
||||||
<groupId>fr.pandacube.lib</groupId>
|
|
||||||
<version>1.0-SNAPSHOT</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<artifactId>pandalib-net</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>fr.pandacube.lib</groupId>
|
|
||||||
<artifactId>pandalib-util</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<version>31.0.1-jre</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,65 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
public class Array8Bit {
|
|
||||||
|
|
||||||
public static final int BIT_COUNT = Byte.SIZE;
|
|
||||||
|
|
||||||
private boolean[] values = new boolean[BIT_COUNT];
|
|
||||||
|
|
||||||
public Array8Bit(byte b) {
|
|
||||||
fromByte(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bits (index 0 is the lowest significant bit)
|
|
||||||
*/
|
|
||||||
public Array8Bit(boolean[] bits) {
|
|
||||||
if (bits == null || bits.length != BIT_COUNT)
|
|
||||||
throw new IllegalArgumentException("bits is null or bits.length != "+BIT_COUNT);
|
|
||||||
values = Arrays.copyOf(bits, BIT_COUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* i = 0 is the lowest significant bit
|
|
||||||
*/
|
|
||||||
public boolean getBit(int i) {
|
|
||||||
return values[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* i = 0 is the lowest significant bit
|
|
||||||
*/
|
|
||||||
public void setBit(int i, boolean b) {
|
|
||||||
values[i] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void fromByte(byte b) {
|
|
||||||
int mask = 1;
|
|
||||||
for (int i = 0; i < BIT_COUNT; i++) {
|
|
||||||
values[i] = (b & mask) != 0;
|
|
||||||
mask <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public byte toByte() {
|
|
||||||
byte b = 0;
|
|
||||||
for (int i=BIT_COUNT-1; i>=0; i--) {
|
|
||||||
b <<= 1;
|
|
||||||
if (values[i]) b |= 1;
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,275 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
public final class ByteBuffer implements Cloneable {
|
|
||||||
|
|
||||||
public static final Charset NETWORK_CHARSET = StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
private java.nio.ByteBuffer buff;
|
|
||||||
|
|
||||||
public ByteBuffer() {
|
|
||||||
this(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer(int initSize) {
|
|
||||||
buff = java.nio.ByteBuffer.allocate(initSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a ByteBuffer that is initially <b>backed</b> by the provided byte array.
|
|
||||||
* The position of this buffer will be 0.
|
|
||||||
* If this ByteBuffer needs a biffer array, the provided array is replaced by a new one,
|
|
||||||
* making the provided array not related to this ByteBuffer anymore.
|
|
||||||
* @param data array of byte that serve as a backend for this ByteBuffer.
|
|
||||||
*/
|
|
||||||
public ByteBuffer(byte[] data) {
|
|
||||||
buff = java.nio.ByteBuffer.wrap(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void askForBufferExtension(int needed) {
|
|
||||||
while (buff.remaining() < needed) {
|
|
||||||
java.nio.ByteBuffer newBuff = java.nio.ByteBuffer.wrap(Arrays.copyOf(buff.array(), buff.array().length * 2));
|
|
||||||
newBuff.position(buff.position());
|
|
||||||
buff = newBuff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This clone method also clone the underlying array.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
|
||||||
@Override
|
|
||||||
public ByteBuffer clone() {
|
|
||||||
return new ByteBuffer(Arrays.copyOf(buff.array(), buff.array().length));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#get()
|
|
||||||
*/
|
|
||||||
public byte getByte() {
|
|
||||||
return buff.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#get(byte[])
|
|
||||||
*/
|
|
||||||
public byte[] getByteArray(byte[] b) {
|
|
||||||
buff.get(b);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next byte array wich is preceded with his size as integer,
|
|
||||||
* or null if the founded size is negative.
|
|
||||||
*/
|
|
||||||
public byte[] getSizedByteArray() {
|
|
||||||
int size = getInt();
|
|
||||||
if (size < 0) return null;
|
|
||||||
return getByteArray(new byte[size]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#getChar()
|
|
||||||
*/
|
|
||||||
public char getChar() {
|
|
||||||
return buff.getChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#getShort()
|
|
||||||
*/
|
|
||||||
public short getShort() {
|
|
||||||
return buff.getShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#getInt()
|
|
||||||
*/
|
|
||||||
public int getInt() {
|
|
||||||
return buff.getInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#getLong()
|
|
||||||
*/
|
|
||||||
public long getLong() {
|
|
||||||
return buff.getLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#getFloat()
|
|
||||||
*/
|
|
||||||
public float getFloat() {
|
|
||||||
return buff.getFloat();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#getDouble()
|
|
||||||
*/
|
|
||||||
public double getDouble() {
|
|
||||||
return buff.getDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#put(byte)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putByte(byte b) {
|
|
||||||
askForBufferExtension(Byte.BYTES);
|
|
||||||
buff.put(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#put(byte[])
|
|
||||||
*/
|
|
||||||
public ByteBuffer putByteArray(byte[] b) {
|
|
||||||
askForBufferExtension(b.length * Byte.BYTES);
|
|
||||||
buff.put(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer putSizedByteArray(byte[] b) {
|
|
||||||
if (b == null) {
|
|
||||||
return putInt(-1);
|
|
||||||
}
|
|
||||||
putInt(b.length);
|
|
||||||
return putByteArray(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#putChar(char)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putChar(char value) {
|
|
||||||
askForBufferExtension(Character.BYTES);
|
|
||||||
buff.putChar(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#putShort(short)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putShort(short value) {
|
|
||||||
askForBufferExtension(Short.BYTES);
|
|
||||||
buff.putShort(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#putInt(int)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putInt(int value) {
|
|
||||||
askForBufferExtension(Integer.BYTES);
|
|
||||||
buff.putInt(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#putLong(long)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putLong(long value) {
|
|
||||||
askForBufferExtension(Long.BYTES);
|
|
||||||
buff.putLong(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#putFloat(float)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putFloat(float value) {
|
|
||||||
askForBufferExtension(Float.BYTES);
|
|
||||||
buff.putFloat(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#putDouble(double)
|
|
||||||
*/
|
|
||||||
public ByteBuffer putDouble(double value) {
|
|
||||||
askForBufferExtension(Double.BYTES);
|
|
||||||
buff.putDouble(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#position()
|
|
||||||
*/
|
|
||||||
public int getPosition() {
|
|
||||||
return buff.position();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#position(int)
|
|
||||||
*/
|
|
||||||
public void setPosition(int p) {
|
|
||||||
buff.position(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#capacity()
|
|
||||||
*/
|
|
||||||
public int capacity() {
|
|
||||||
return buff.capacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param s null String are supported
|
|
||||||
*/
|
|
||||||
public ByteBuffer putString(String s) {
|
|
||||||
if (s == null) {
|
|
||||||
return putInt(-1);
|
|
||||||
}
|
|
||||||
return putSizedByteArray(s.getBytes(NETWORK_CHARSET));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* returned string can be null
|
|
||||||
*/
|
|
||||||
public String getString() {
|
|
||||||
byte[] binaryString = getSizedByteArray();
|
|
||||||
return (binaryString == null) ? null : new String(binaryString, NETWORK_CHARSET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param list The list can be null, and any String can be null too.
|
|
||||||
*/
|
|
||||||
public ByteBuffer putListOfString(List<String> list) {
|
|
||||||
if (list == null) {
|
|
||||||
return putInt(-1);
|
|
||||||
}
|
|
||||||
putInt(list.size());
|
|
||||||
for (String str : list)
|
|
||||||
putString(str);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a List of String. The list can be null, and any element can be null too.
|
|
||||||
*/
|
|
||||||
public List<String> getListOfString() {
|
|
||||||
int size = getInt();
|
|
||||||
if (size < 0)
|
|
||||||
return null;
|
|
||||||
List<String> list = new ArrayList<>();
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
list.add(getString());
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see java.nio.ByteBuffer#array()
|
|
||||||
*/
|
|
||||||
public byte[] array() {
|
|
||||||
return buff.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
public class PPacket {
|
|
||||||
public final String name;
|
|
||||||
/* package */ int id;
|
|
||||||
public final byte[] content;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new PPacket based on the content of the provided buffer before his position.
|
|
||||||
* @param n the name of the packet.
|
|
||||||
* @param buff the buffer where the data comes from. Only the content before {@link ByteBuffer#getPosition()} is copied.
|
|
||||||
*/
|
|
||||||
public PPacket(String n, ByteBuffer buff) {
|
|
||||||
this(n, Arrays.copyOf(buff.array(), buff.getPosition()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public PPacket(String n, byte[] c) {
|
|
||||||
name = n;
|
|
||||||
content = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* package */ PPacket(String n, int i, byte[] c) {
|
|
||||||
this(n, c);
|
|
||||||
id = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer getContentAsBuffer() {
|
|
||||||
return new ByteBuffer(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static PPacket buildSingleStringContentPacket(String name, String content) {
|
|
||||||
return new PPacket(name, new ByteBuffer().putString(content));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* package */ static PPacket buildLoginPacket(String password) {
|
|
||||||
return buildSingleStringContentPacket("login", password);
|
|
||||||
}
|
|
||||||
/* package */ static PPacket buildBadFormatPacket(String message) {
|
|
||||||
return buildSingleStringContentPacket("bad_format", message);
|
|
||||||
}
|
|
||||||
/* package */ static PPacket buildLoginBadPacket() {
|
|
||||||
return new PPacket("login_bad", new byte[0]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
public class PPacketAnswer extends PPacket {
|
|
||||||
/* package */ final int answer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new PPacketAnswer based on the content of the provided buffer before his position.
|
|
||||||
* @param n the name of the packet.
|
|
||||||
* @param buff the buffer where the data comes from. Only the content before {@link ByteBuffer#getPosition()} is copied.
|
|
||||||
*/
|
|
||||||
public PPacketAnswer(PPacket answered, String n, ByteBuffer buff) {
|
|
||||||
this(answered, n, Arrays.copyOf(buff.array(), buff.getPosition()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public PPacketAnswer(PPacket answered, String n, byte[] c) {
|
|
||||||
super(n, c);
|
|
||||||
answer = answered.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* package */ PPacketAnswer(String n, int i, int a, byte[] c) {
|
|
||||||
super(n, i, c);
|
|
||||||
answer = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static PPacketAnswer buildSingleStringContentPacketAnswer(PPacket answered, String name, String content) {
|
|
||||||
ByteBuffer pwBuff = new ByteBuffer().putString(content);
|
|
||||||
return new PPacketAnswer(answered, name, Arrays.copyOf(pwBuff.array(), pwBuff.getPosition()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* package */ static PPacketAnswer buildLoginOkPacket(PPacket loginPacket) {
|
|
||||||
return new PPacketAnswer(loginPacket, "login_ok", new byte[0]);
|
|
||||||
}
|
|
||||||
/* package */ static PPacketAnswer buildExceptionPacket(PPacket answered, String message) {
|
|
||||||
return buildSingleStringContentPacketAnswer(answered, "exception", message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface PPacketListener<P extends PPacket> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when we receive a packet (except responses)
|
|
||||||
* @param connection the connection from where the packet comes
|
|
||||||
* @param packet the received packet
|
|
||||||
*/
|
|
||||||
void onPacketReceive(PSocket connection, P packet);
|
|
||||||
|
|
||||||
}
|
|
@ -1,157 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
import fr.pandacube.lib.util.Log;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
public class PServer extends Thread implements Closeable {
|
|
||||||
private static final AtomicInteger connectionCounterId = new AtomicInteger(0);
|
|
||||||
|
|
||||||
private final int port;
|
|
||||||
private ServerSocket socket;
|
|
||||||
private final String socketName;
|
|
||||||
|
|
||||||
private final List<TCPServerClientConnection> clients = Collections.synchronizedList(new ArrayList<>());
|
|
||||||
|
|
||||||
private final AtomicBoolean isClosed = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
|
|
||||||
private final List<PPacketListener<PPacket>> globalPacketListeners = Collections.synchronizedList(new ArrayList<>());
|
|
||||||
private final List<PSocketConnectionListener> clientConnectionListeners = Collections.synchronizedList(new ArrayList<>());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final String password;
|
|
||||||
|
|
||||||
public PServer(int port, String sckName, String password) {
|
|
||||||
super("PServer " + sckName);
|
|
||||||
setDaemon(true);
|
|
||||||
if (port <= 0 || port > 65535) throw new IllegalArgumentException("le numéro de port est invalide");
|
|
||||||
socketName = sckName;
|
|
||||||
this.port = port;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
socket = new ServerSocket();
|
|
||||||
socket.setReceiveBufferSize(PSocket.NETWORK_TCP_BUFFER_SIZE);
|
|
||||||
socket.setPerformancePreferences(0, 1, 0);
|
|
||||||
socket.bind(new InetSocketAddress(port));
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
Socket socketClient = socket.accept();
|
|
||||||
socketClient.setSendBufferSize(PSocket.NETWORK_TCP_BUFFER_SIZE);
|
|
||||||
socketClient.setSoTimeout(PSocket.NETWORK_TIMEOUT);
|
|
||||||
|
|
||||||
TCPServerClientConnection co = new TCPServerClientConnection(socketClient,
|
|
||||||
connectionCounterId.getAndIncrement());
|
|
||||||
co.start();
|
|
||||||
}
|
|
||||||
} catch (SocketException ignored) {
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.warning("Plus aucune connexion ne peux être acceptée", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void addPacketListener(PPacketListener<PPacket> l) {
|
|
||||||
globalPacketListeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removePacketListener(PPacketListener<PPacket> l) {
|
|
||||||
return globalPacketListeners.remove(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addConnectionListener(PSocketConnectionListener l) {
|
|
||||||
clientConnectionListeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeConnectionListener(PSocketConnectionListener l) {
|
|
||||||
clientConnectionListeners.remove(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected class TCPServerClientConnection extends PSocket {
|
|
||||||
|
|
||||||
boolean loggedIn;
|
|
||||||
|
|
||||||
private TCPServerClientConnection(Socket s, int coId) {
|
|
||||||
super(s, "Conn#" + coId + " via PServer " + socketName, password);
|
|
||||||
addConnectionListener(new PSocketConnectionListener() {
|
|
||||||
@Override
|
|
||||||
public void onDisconnect(PSocket connection) {
|
|
||||||
try {
|
|
||||||
clientConnectionListeners.forEach(l -> l.onDisconnect(connection));
|
|
||||||
} finally {
|
|
||||||
clients.remove((TCPServerClientConnection)connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onConnect(PSocket connection) {
|
|
||||||
clients.add((TCPServerClientConnection)connection);
|
|
||||||
clientConnectionListeners.forEach(l -> l.onConnect(connection));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
addPacketListener((conn, packet) ->
|
|
||||||
globalPacketListeners.forEach(l -> {
|
|
||||||
try {
|
|
||||||
l.onPacketReceive(conn, packet);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.severe("Exception while calling PPacketListener.onPacketReceive().", e);
|
|
||||||
sendSilently(PPacketAnswer.buildExceptionPacket(packet, e.toString()));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
try {
|
|
||||||
if (isClosed.get()) return;
|
|
||||||
isClosed.set(true);
|
|
||||||
|
|
||||||
clients.forEach(PSocket::close);
|
|
||||||
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
|
||||||
return isClosed.get() || socket.isClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public List<PSocket> getClients() {
|
|
||||||
synchronized (clients) {
|
|
||||||
return new ArrayList<>(clients);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.getClass().getName() + "{thread=" + getName() + ", socket=" + socket + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,350 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
import fr.pandacube.lib.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper for a {@link Socket}. The connection must point to a software using {@link PServer}
|
|
||||||
* as wrapper for the target {@link ServerSocket}.
|
|
||||||
* <br>
|
|
||||||
* This class provides a simple way to exchange data between client and server :
|
|
||||||
* <ul>
|
|
||||||
* <li>Maintained connection with the server</li>
|
|
||||||
* <li>Login with a password (send in the first packet)</li>
|
|
||||||
* <li>Binary packet id</li>
|
|
||||||
* <li>Binary data</li>
|
|
||||||
* <li>Input stream in a separate Thread</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Beta
|
|
||||||
public class PSocket extends Thread implements Closeable {
|
|
||||||
|
|
||||||
public static final int NETWORK_TCP_BUFFER_SIZE = 1024 * 1024;
|
|
||||||
|
|
||||||
public static final int NETWORK_TIMEOUT = 0; // no timeout (milli-seconds)
|
|
||||||
|
|
||||||
private boolean server = false;
|
|
||||||
private Socket socket;
|
|
||||||
private final SocketAddress addr;
|
|
||||||
private DataInputStream in;
|
|
||||||
private DataOutputStream out;
|
|
||||||
private final Object outSynchronizer = new Object();
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
private final AtomicBoolean isClosed = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
private final List<PPacketListener<PPacket>> packetListeners = Collections.synchronizedList(new ArrayList<>());
|
|
||||||
private final List<PSocketConnectionListener> connectionListeners = Collections.synchronizedList(new ArrayList<>());
|
|
||||||
private final Map<Integer, PPacketListener<PPacketAnswer>> answersCallbacks = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
|
|
||||||
private int nextSendId = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new PSocket that will connect to the specified SocketAddress.
|
|
||||||
* @param a The target server to connect to
|
|
||||||
* @param connName the name of the connection, used to name the Thread used to receive the packet.
|
|
||||||
* @param pass the password to send to the server.
|
|
||||||
*/
|
|
||||||
public PSocket(SocketAddress a, String connName, String pass) {
|
|
||||||
super("PSocket " + connName);
|
|
||||||
setDaemon(true);
|
|
||||||
if (a == null) throw new IllegalArgumentException("les arguments ne peuvent pas être null");
|
|
||||||
addr = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* package */ PSocket(Socket s, String connName, String pass) {
|
|
||||||
this(s.getRemoteSocketAddress(), connName, pass);
|
|
||||||
socket = s;
|
|
||||||
server = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (socket == null) {
|
|
||||||
socket = new Socket();
|
|
||||||
socket.setReceiveBufferSize(NETWORK_TCP_BUFFER_SIZE);
|
|
||||||
socket.setSendBufferSize(NETWORK_TCP_BUFFER_SIZE);
|
|
||||||
|
|
||||||
socket.setSoTimeout(10000); // initial timeout before login
|
|
||||||
|
|
||||||
socket.connect(addr);
|
|
||||||
|
|
||||||
in = new DataInputStream(socket.getInputStream());
|
|
||||||
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// password check
|
|
||||||
if (server) {
|
|
||||||
PPacket packet = readPacket();
|
|
||||||
if (packet == null || packet instanceof PPacketAnswer || !"login".equals(packet.name)) {
|
|
||||||
send(PPacket.buildLoginBadPacket());
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String receivedPassword = new ByteBuffer(packet.content).getString();
|
|
||||||
if (!Objects.equals(receivedPassword, password)) {
|
|
||||||
send(PPacket.buildLoginBadPacket());
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch(Exception e) {
|
|
||||||
send(PPacket.buildLoginBadPacket());
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
send(PPacketAnswer.buildLoginOkPacket(packet));
|
|
||||||
// login ok at this point
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
send(PPacket.buildLoginPacket(password));
|
|
||||||
PPacket packet = readPacket();
|
|
||||||
if (packet == null) {
|
|
||||||
Log.severe("bad packet received from server. Disconnecting.");
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (packet.name.equals("login_bad")) {
|
|
||||||
Log.severe("Wrong password to connect to server. Disconnecting.");
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!packet.name.equals("login_ok")) {
|
|
||||||
Log.severe("Unexpected packet from server. Disconnecting.");
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// login ok at this point
|
|
||||||
}
|
|
||||||
password = null;
|
|
||||||
|
|
||||||
socket.setSoTimeout(NETWORK_TIMEOUT);
|
|
||||||
|
|
||||||
Log.info(getName() + " connected.");
|
|
||||||
|
|
||||||
connectionListeners.forEach(l -> {
|
|
||||||
try {
|
|
||||||
l.onConnect(this);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.severe("Exception while calling PSocketConnectionListener.onConnect().", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
while (!socket.isClosed()) {
|
|
||||||
PPacket packet = readPacket();
|
|
||||||
|
|
||||||
if (packet == null) {
|
|
||||||
send(PPacket.buildBadFormatPacket("Bad format for the last packet received. Closing connection."));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet instanceof PPacketAnswer) {
|
|
||||||
try {
|
|
||||||
answersCallbacks.remove(((PPacketAnswer)packet).answer).onPacketReceive(this, (PPacketAnswer)packet);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.severe("Exception while calling PPacketListener.onPacketReceive().", e);
|
|
||||||
send(PPacketAnswer.buildExceptionPacket(packet, e.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
packetListeners.forEach(l -> {
|
|
||||||
try {
|
|
||||||
l.onPacketReceive(this, packet);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.severe("Exception while calling PPacketListener.onPacketReceive().", e);
|
|
||||||
sendSilently(PPacketAnswer.buildExceptionPacket(packet, e.toString()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.severe(e);
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the packet read in the socket, or null if the packet is in a bad format.
|
|
||||||
* @return the packet
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private PPacket readPacket() throws IOException {
|
|
||||||
byte nSize = in.readByte();
|
|
||||||
if (nSize == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
boolean answer = nSize < 0;
|
|
||||||
if (answer)
|
|
||||||
nSize *= -1;
|
|
||||||
|
|
||||||
|
|
||||||
byte[] nBytes = new byte[nSize];
|
|
||||||
in.readFully(nBytes);
|
|
||||||
String name = new String(nBytes, ByteBuffer.NETWORK_CHARSET);
|
|
||||||
|
|
||||||
|
|
||||||
int packetId = in.readInt();
|
|
||||||
|
|
||||||
|
|
||||||
int answerId = (answer) ? in.readInt() : -1;
|
|
||||||
|
|
||||||
|
|
||||||
int cSize = in.readInt();
|
|
||||||
if (cSize < 0 || cSize > 0xFFFFFF) { // can't be more that 16 MiB
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
byte[] content = new byte[cSize];
|
|
||||||
in.readFully(content);
|
|
||||||
|
|
||||||
return answer ? new PPacketAnswer(name, packetId, answerId, content) : new PPacket(name, packetId, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the provided packet, without waiting for an answer.
|
|
||||||
*/
|
|
||||||
public void send(PPacket packet) throws IOException {
|
|
||||||
if (packet == null)
|
|
||||||
throw new IllegalArgumentException("packet can't be null");
|
|
||||||
if (packet.name == null)
|
|
||||||
throw new IllegalArgumentException("packet.name can't be null");
|
|
||||||
if (packet.content == null)
|
|
||||||
throw new IllegalArgumentException("packet.content can't be null");
|
|
||||||
|
|
||||||
byte[] nameBytes = packet.name.getBytes(ByteBuffer.NETWORK_CHARSET);
|
|
||||||
if (nameBytes.length > 127)
|
|
||||||
throw new IllegalArgumentException("packet.name must take fewer than 128 bytes when converted to UTF-8");
|
|
||||||
byte nameSize = (byte)nameBytes.length;
|
|
||||||
|
|
||||||
boolean answer = packet instanceof PPacketAnswer;
|
|
||||||
|
|
||||||
if (answer) nameSize *= -1;
|
|
||||||
|
|
||||||
synchronized (outSynchronizer) {
|
|
||||||
int packetId = nextSendId++;
|
|
||||||
|
|
||||||
packet.id = packetId;
|
|
||||||
|
|
||||||
out.write(new byte[] {nameSize});
|
|
||||||
out.write(nameBytes);
|
|
||||||
out.write(packetId);
|
|
||||||
if (answer)
|
|
||||||
out.write(((PPacketAnswer)packet).answer);
|
|
||||||
out.write(packet.content.length);
|
|
||||||
out.write(packet.content);
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendSilently(PPacket packet) {
|
|
||||||
try {
|
|
||||||
send(packet);
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void send(PPacket packet, PPacketListener<PPacketAnswer> answerCallback) throws IOException {
|
|
||||||
synchronized (answersCallbacks) {
|
|
||||||
/*
|
|
||||||
* This synch block ensure that the callback will be put in the listeners Map before
|
|
||||||
* we receve the answer (in case this is really really fast)
|
|
||||||
*/
|
|
||||||
send(packet);
|
|
||||||
answersCallbacks.put(packet.id, answerCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void addPacketListener(PPacketListener<PPacket> l) {
|
|
||||||
packetListeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removePacketListener(PPacketListener<PPacket> l) {
|
|
||||||
return packetListeners.remove(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void addConnectionListener(PSocketConnectionListener l) {
|
|
||||||
connectionListeners.add(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeConnectionListener(PSocketConnectionListener l) {
|
|
||||||
connectionListeners.remove(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
try {
|
|
||||||
synchronized (outSynchronizer) {
|
|
||||||
if (isClosed.get()) return;
|
|
||||||
|
|
||||||
Log.info(getName() + " closing...");
|
|
||||||
|
|
||||||
connectionListeners.forEach(l -> {
|
|
||||||
try {
|
|
||||||
l.onDisconnect(this);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.severe("Exception while calling PSocketConnectionListener.onDisconnect().", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.close();
|
|
||||||
isClosed.set(true);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.warning(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketAddress getRemoteAddress() {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
|
||||||
return isClosed.get() || socket.isClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return this.getClass().getName() + "{thread=" + getName() + ", socket=" + socket + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package fr.pandacube.lib.net;
|
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
|
|
||||||
@Beta
|
|
||||||
public interface PSocketConnectionListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a socket is connected
|
|
||||||
* @param connection the connection
|
|
||||||
*/
|
|
||||||
void onConnect(PSocket connection);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called just before a socket is disconnected
|
|
||||||
* @param connection the connection
|
|
||||||
*/
|
|
||||||
void onDisconnect(PSocket connection);
|
|
||||||
|
|
||||||
}
|
|
1
pom.xml
1
pom.xml
@ -68,7 +68,6 @@
|
|||||||
<module>pandalib-commands</module>
|
<module>pandalib-commands</module>
|
||||||
<module>pandalib-core</module>
|
<module>pandalib-core</module>
|
||||||
<module>pandalib-db</module>
|
<module>pandalib-db</module>
|
||||||
<module>pandalib-net</module>
|
|
||||||
<module>pandalib-netapi</module>
|
<module>pandalib-netapi</module>
|
||||||
<module>pandalib-paper</module>
|
<module>pandalib-paper</module>
|
||||||
<module>pandalib-paper-permissions</module>
|
<module>pandalib-paper-permissions</module>
|
||||||
|
Loading…
Reference in New Issue
Block a user