Beginning work on server block
This commit is contained in:
parent
3136dd7697
commit
d77587ca38
|
|
@ -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.
102
src/main/java/net/montoyo/wd/miniserv/OutgoingPacket.java
Normal file
102
src/main/java/net/montoyo/wd/miniserv/OutgoingPacket.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
19
src/main/java/net/montoyo/wd/miniserv/PacketID.java
Normal file
19
src/main/java/net/montoyo/wd/miniserv/PacketID.java
Normal 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];
|
||||
}
|
||||
|
||||
}
|
||||
59
src/main/java/net/montoyo/wd/miniserv/PacketReader.java
Normal file
59
src/main/java/net/montoyo/wd/miniserv/PacketReader.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
56
src/main/java/net/montoyo/wd/miniserv/PacketWriter.java
Normal file
56
src/main/java/net/montoyo/wd/miniserv/PacketWriter.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
139
src/main/java/net/montoyo/wd/miniserv/server/Server.java
Normal file
139
src/main/java/net/montoyo/wd/miniserv/server/Server.java
Normal 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) {}
|
||||
}
|
||||
|
||||
}
|
||||
138
src/main/java/net/montoyo/wd/miniserv/server/ServerClient.java
Normal file
138
src/main/java/net/montoyo/wd/miniserv/server/ServerClient.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user