Beginning work on server block

This commit is contained in:
Nicolas BARBOTIN 2018-02-05 01:57:37 +01:00
parent 3136dd7697
commit d77587ca38
8 changed files with 514 additions and 0 deletions

View File

@ -11,6 +11,7 @@ This is the unfinished port of the WebDisplays mod for Minecraft 1.12.2. The tex
* minePad management: check GuiContainer.draggedStack for minePad
* Change mod name to WD2
* Put a limit on screen resolution
* Auto-find rotation for BOTTOM & TOP screens
### Config elements
* Site blacklist

Binary file not shown.

View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2018 BARBOTIN Nicolas
*/
package net.montoyo.wd.miniserv;
import java.io.*;
import java.util.function.Consumer;
public final class OutgoingPacket {
private ByteArrayOutputStream baos;
private DataOutputStream dos;
private Consumer<OutgoingPacket> onFinish;
public OutgoingPacket() {
baos = new ByteArrayOutputStream();
dos = new DataOutputStream(baos);
}
public final void writeInt(int i) {
try {
dos.writeInt(i);
} catch(IOException ex) {}
}
public final void writeByte(int b) {
try {
dos.writeByte(b);
} catch(IOException ex) {}
}
public final void writeShort(int s) {
try {
dos.writeShort(s);
} catch(IOException ex) {}
}
public final void writeBoolean(boolean b) {
try {
dos.writeBoolean(b);
} catch(IOException ex) {}
}
public final void writeBytes(byte[] data) {
try {
dos.write(data);
} catch(IOException ex) {}
}
public final void writeBytes(byte[] data, int offset, int size) {
try {
dos.write(data, offset, size);
} catch(IOException ex) {}
}
public final void writeString(String str) {
byte[] bytes;
try {
bytes = str.getBytes("UTF-8");
} catch(UnsupportedEncodingException ex) {
return; //Meh, shouldn't happen
}
try {
dos.writeShort(bytes.length);
dos.write(bytes);
} catch(IOException ex) {}
}
public final int writeStream(InputStream is, int max) throws IOException {
final int origMax = max;
final byte[] buf = new byte[8192];
while(max > 0) {
int read = is.read(buf, 0, (max < buf.length) ? max : buf.length);
if(read <= 0)
return origMax - max;
dos.write(buf, 0, read);
max -= read;
}
return origMax;
}
public final byte[] finish() {
if(onFinish != null)
onFinish.accept(this);
byte[] bytes = baos.toByteArray();
baos = null;
dos = null;
return bytes;
}
public final void setOnFinishAction(Consumer<OutgoingPacket> action) {
onFinish = action;
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2018 BARBOTIN Nicolas
*/
package net.montoyo.wd.miniserv;
public enum PacketID {
PING, //C->S and S->C
BEGIN_FILE_UPLOAD, //C->S
FILE_PART, //C->S and S->C
GET_FILE; //C->S
public static PacketID fromInt(int i) {
PacketID[] values = values();
return (i < 0 || i >= values.length) ? null : values[i];
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 BARBOTIN Nicolas
*/
package net.montoyo.wd.miniserv;
import net.montoyo.wd.utilities.Log;
import java.nio.ByteBuffer;
public final class PacketReader {
private final byte[] sizeArray = new byte[4];
private byte[] packetData;
private int pos = 0;
private boolean needSize = true;
public final boolean readFrom(ByteBuffer buf) {
if(needSize) {
//Read packet size
if(readByteArray(sizeArray, buf)) {
int packetSize = (sizeArray[0] << 24) | (sizeArray[1] << 16) | (sizeArray[2] << 8) | sizeArray[3];
needSize = false;
pos = 0;
if(packetSize < 5 || packetSize > 65536) {
Log.warning("Got invalid packet from client of size %d, things won't go well...", packetSize);
return true; //Abort packet reading
}
packetData = new byte[packetSize];
} else
return false;
}
return readByteArray(packetData, buf);
}
private boolean readByteArray(byte[] dst, ByteBuffer src) {
int remaining = dst.length - pos;
int read = (src.remaining() >= remaining) ? remaining : src.remaining();
src.get(dst, pos, read);
pos += read;
return (pos >= dst.length);
}
public final byte[] getPacketData() {
return packetData;
}
public final void reset() {
packetData = null;
pos = 0;
needSize = true;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2018 BARBOTIN Nicolas
*/
package net.montoyo.wd.miniserv;
import java.nio.ByteBuffer;
public final class PacketWriter {
private final byte[] sizeBytes = new byte[4];
private byte[] packet;
private int pos = 0;
private boolean needToWriteSize = true;
public final boolean writeTo(ByteBuffer bb) {
if(packet == null)
return true; //Next packet please
if(needToWriteSize) {
if(writeByteArray(bb, sizeBytes)) {
needToWriteSize = false;
pos = 0;
} else
return false;
}
if(writeByteArray(bb, packet)) {
packet = null;
return true;
} else
return false;
}
private boolean writeByteArray(ByteBuffer dst, byte[] src) {
int remaining = src.length - pos;
int written = (dst.remaining() >= remaining) ? remaining : dst.remaining();
dst.put(src, pos, written);
pos += written;
return (pos >= src.length);
}
public final void reset(byte[] pkt) {
final int len = pkt.length + 4;
sizeBytes[0] = (byte) ((len >> 24) & 0xFF);
sizeBytes[1] = (byte) ((len >> 16) & 0xFF);
sizeBytes[2] = (byte) ((len >> 8) & 0xFF);
sizeBytes[3] = (byte) ( len & 0xFF);
packet = pkt;
pos = 0;
needToWriteSize = true;
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2018 BARBOTIN Nicolas
*/
package net.montoyo.wd.miniserv.server;
import net.montoyo.wd.utilities.Log;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
public class Server extends Thread {
private ServerSocketChannel server;
private Selector selector;
private int port = 25566;
private final ArrayList<ServerClient> clientList = new ArrayList<>();
private final HashMap<SocketChannel, ServerClient> clientMap = new HashMap<>();
private final ByteBuffer readBuffer = ByteBuffer.allocateDirect(8192);
public Server() {
setDaemon(true);
}
@Override
public void start() {
try {
server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
} catch(Throwable t) {
t.printStackTrace();
return;
}
super.start();
}
@Override
public void run() {
boolean running = true;
while(running) {
try {
loopUnsafe();
} catch(Throwable t) {
t.printStackTrace();
running = false;
}
}
}
private void loopUnsafe() throws Throwable {
selector.select();
for(SelectionKey key: selector.selectedKeys()) {
if(key.isAcceptable()) {
SocketChannel chan;
try {
chan = server.accept();
} catch(Throwable t) {
Log.warningEx("Could not accept client", t);
chan = null;
}
if(chan != null) {
chan.configureBlocking(false);
chan.register(selector, SelectionKey.OP_READ);
ServerClient toAdd = new ServerClient(chan, selector);
clientMap.put(chan, toAdd);
clientList.add(toAdd);
}
} else if(key.isReadable()) {
ServerClient cli = clientMap.get(key.channel());
if(cli == null)
Log.warning("Received read info from unknown client");
else {
try {
readBuffer.clear();
int read = cli.getChannel().read(readBuffer);
if(read < 0)
cli.setShouldRemove(); //End of stream
else {
readBuffer.position(0);
readBuffer.limit(read);
cli.readyRead(readBuffer);
}
} catch(Throwable t) {
Log.warningEx("Could not read data from client", t);
cli.setShouldRemove();
}
}
} else if(key.isWritable()) {
ServerClient cli = clientMap.get(key.channel());
if(cli == null)
Log.warning("Received write info from unknown client");
else {
try {
cli.readyWrite();
} catch(Throwable t) {
Log.warningEx("Could not write data to client", t);
cli.setShouldRemove();
}
}
}
}
for(int i = clientList.size() - 1; i >= 0; i--) {
ServerClient cli = clientList.get(i);
if(cli.shouldRemove())
removeClient(cli);
}
}
private void removeClient(ServerClient cli) {
clientMap.remove(cli.getChannel());
clientList.remove(cli);
try {
cli.getChannel().close();
} catch(Throwable t) {}
}
}

View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2018 BARBOTIN Nicolas
*/
package net.montoyo.wd.miniserv.server;
import net.montoyo.wd.miniserv.OutgoingPacket;
import net.montoyo.wd.miniserv.PacketID;
import net.montoyo.wd.miniserv.PacketReader;
import net.montoyo.wd.miniserv.PacketWriter;
import net.montoyo.wd.utilities.Log;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayDeque;
public class ServerClient {
private final SocketChannel socket;
private final Selector selector;
private SelectionKey writeKey;
private boolean remove;
private final ByteBuffer sendBuffer = ByteBuffer.allocateDirect(8192);
private final ArrayDeque<OutgoingPacket> sendQueue = new ArrayDeque<>();
private final PacketReader packetReader = new PacketReader();
private final PacketWriter packetWriter = new PacketWriter();
ServerClient(SocketChannel s, Selector ss) {
socket = s;
selector = ss;
sendBuffer.limit(0); //Set empty
}
void readyRead(ByteBuffer bb) {
while(bb.remaining() > 0) {
if(packetReader.readFrom(bb)) { //End of packet
byte[] pkt = packetReader.getPacketData();
if(pkt != null) {
try {
handlePacket(pkt);
} catch(IOException ex) {
Log.warningEx("IOException while trying to handle packet", ex);
}
}
packetReader.reset();
}
}
}
private void handlePacket(byte[] pkt) throws IOException {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(pkt));
PacketID pid = PacketID.fromInt(dis.readByte());
if(pid == null) {
Log.warning("Caught packet with invalid ID from client");
return;
}
OutgoingPacket response = null;
switch(pid) {
case PING:
response = new OutgoingPacket();
response.writeByte(PacketID.PING.ordinal());
break;
case BEGIN_FILE_UPLOAD:
break;
case FILE_PART:
break;
case GET_FILE:
break;
}
if(response != null)
sendPacket(response);
}
void readyWrite() throws Throwable {
if(sendBuffer.remaining() > 0 || fillSendBuffer()) {
if(socket.write(sendBuffer) < 0)
remove = true;
} else if(writeKey != null) {
writeKey.cancel();
writeKey = null;
}
}
void setShouldRemove() {
remove = true;
}
public boolean shouldRemove() {
return remove;
}
SocketChannel getChannel() {
return socket;
}
private boolean fillSendBuffer() {
sendBuffer.clear();
do {
if(packetWriter.writeTo(sendBuffer)) {
OutgoingPacket pkt = sendQueue.poll();
if(pkt == null)
return sendBuffer.remaining() > 0;
packetWriter.reset(pkt.finish());
}
} while(sendBuffer.remaining() > 0);
return true;
}
public void sendPacket(OutgoingPacket pkt) {
sendQueue.offer(pkt);
if(writeKey == null) {
try {
writeKey = socket.register(selector, SelectionKey.OP_WRITE);
} catch(ClosedChannelException ex) {
Log.warningEx("Couldn't send packet", ex);
}
}
}
}