Basic protocol
This commit is contained in:
parent
1e1d861d91
commit
72b4e547fb
@ -0,0 +1,25 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection;
|
||||||
|
|
||||||
|
public class ConnectionException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6133726852202889620L;
|
||||||
|
|
||||||
|
public ConnectionException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,31 +1,31 @@
|
|||||||
package me.mrletsplay.shareclientcore.connection;
|
package me.mrletsplay.shareclientcore.connection;
|
||||||
|
|
||||||
import java.io.IOException;
|
import me.mrletsplay.shareclientcore.connection.message.Message;
|
||||||
|
|
||||||
public class DummyConnection implements RemoteConnection {
|
public class DummyConnection implements RemoteConnection {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect() throws IOException, InterruptedException {
|
public void connect(String sessionID) throws ConnectionException {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int retrieveSiteID() {
|
public int getSiteID() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(Change... changes) {
|
public void send(Message message) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(RemoteListener listener) {
|
public void addListener(MessageListener listener) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeListener(RemoteListener listener) {
|
public void removeListener(MessageListener listener) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection;
|
||||||
|
|
||||||
|
import me.mrletsplay.shareclientcore.connection.message.Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A message listener that can receive remote messages
|
||||||
|
*/
|
||||||
|
public interface MessageListener {
|
||||||
|
|
||||||
|
public void onMessage(Message message);
|
||||||
|
|
||||||
|
}
|
@ -1,20 +1,22 @@
|
|||||||
package me.mrletsplay.shareclientcore.connection;
|
package me.mrletsplay.shareclientcore.connection;
|
||||||
|
|
||||||
import java.io.IOException;
|
import me.mrletsplay.shareclientcore.connection.message.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a connection to a remote user or server
|
* Represents a connection to a remote user or server
|
||||||
*/
|
*/
|
||||||
public interface RemoteConnection {
|
public interface RemoteConnection {
|
||||||
|
|
||||||
public void connect() throws IOException, InterruptedException;
|
public static final int PROTOCOL_VERSION = 1;
|
||||||
|
|
||||||
public int retrieveSiteID();
|
public void connect(String sessionID) throws ConnectionException;
|
||||||
|
|
||||||
public void send(Change... changes);
|
public int getSiteID();
|
||||||
|
|
||||||
public void addListener(RemoteListener listener);
|
public void send(Message message) throws ConnectionException;
|
||||||
|
|
||||||
public void removeListener(RemoteListener listener);
|
public void addListener(MessageListener listener);
|
||||||
|
|
||||||
|
public void removeListener(MessageListener listener);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package me.mrletsplay.shareclientcore.connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents something that can receive remote changes
|
|
||||||
*/
|
|
||||||
public interface RemoteListener {
|
|
||||||
|
|
||||||
public void onRemoteChange(Change... changes);
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,10 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface SerializableObject {
|
||||||
|
|
||||||
|
public void serialize(DataOutputStream out) throws IOException;
|
||||||
|
|
||||||
|
}
|
@ -1,109 +1,86 @@
|
|||||||
package me.mrletsplay.shareclientcore.connection;
|
package me.mrletsplay.shareclientcore.connection;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.java_websocket.client.WebSocketClient;
|
import org.java_websocket.client.WebSocketClient;
|
||||||
import org.java_websocket.handshake.ServerHandshake;
|
import org.java_websocket.handshake.ServerHandshake;
|
||||||
|
|
||||||
import me.mrletsplay.shareclientcore.document.Char;
|
import me.mrletsplay.shareclientcore.connection.message.ClientHelloMessage;
|
||||||
import me.mrletsplay.shareclientcore.document.Identifier;
|
import me.mrletsplay.shareclientcore.connection.message.Message;
|
||||||
|
import me.mrletsplay.shareclientcore.connection.message.ServerHelloMessage;
|
||||||
|
|
||||||
public class WebSocketConnection implements RemoteConnection {
|
public class WebSocketConnection implements RemoteConnection {
|
||||||
|
|
||||||
private WSClient client;
|
private WSClient client;
|
||||||
private Set<RemoteListener> listeners;
|
private String username;
|
||||||
|
private Set<MessageListener> listeners;
|
||||||
|
private int siteID;
|
||||||
|
|
||||||
public WebSocketConnection(URI uri, Map<String, String> httpHeaders) {
|
private Object wait = new Object();
|
||||||
|
private boolean helloReceived;
|
||||||
|
private ConnectionException connectException;
|
||||||
|
|
||||||
|
public WebSocketConnection(URI uri, String username, Map<String, String> httpHeaders) {
|
||||||
this.client = new WSClient(uri, httpHeaders);
|
this.client = new WSClient(uri, httpHeaders);
|
||||||
|
this.username = username;
|
||||||
this.listeners = new HashSet<>();
|
this.listeners = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebSocketConnection(URI uri) {
|
public WebSocketConnection(URI uri, String username) {
|
||||||
this(uri, null);
|
this(uri, username, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect() throws IOException, InterruptedException {
|
public void connect(String sessionID) throws ConnectionException {
|
||||||
if(!client.connectBlocking()) throw new IOException("Failed to connect to WebSocket server");
|
try {
|
||||||
}
|
if(!client.connectBlocking(30, TimeUnit.SECONDS)) throw new IOException("Failed to connect to WebSocket server");
|
||||||
|
send(new ClientHelloMessage(username, sessionID));
|
||||||
@Override
|
wait.wait(30_000L);
|
||||||
public int retrieveSiteID() {
|
if(!helloReceived) throw new ConnectionException("Server did not send hello");
|
||||||
// TODO: implement
|
if(connectException != null) throw connectException;
|
||||||
return new Random().nextInt();
|
} catch (InterruptedException | IOException e) {
|
||||||
}
|
throw new ConnectionException("Failed to establish connection", e);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(Change... changes) {
|
|
||||||
for(Change c : changes) {
|
|
||||||
client.send(serialize(c));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(RemoteListener listener) {
|
public int getSiteID() {
|
||||||
|
return siteID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(Message message) throws ConnectionException {
|
||||||
|
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dOut = new DataOutputStream(bOut);
|
||||||
|
|
||||||
|
try {
|
||||||
|
dOut.writeUTF(message.getType().name());
|
||||||
|
message.serialize(dOut);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ConnectionException("Failed to serialize message", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.send(bOut.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(MessageListener listener) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeListener(RemoteListener listener) {
|
public void removeListener(MessageListener listener) {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] serialize(Change change) {
|
|
||||||
try {
|
|
||||||
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
|
|
||||||
DataOutputStream dOut = new DataOutputStream(bOut);
|
|
||||||
dOut.writeInt(change.document());
|
|
||||||
dOut.writeUTF(change.type().name());
|
|
||||||
|
|
||||||
Char ch = change.character();
|
|
||||||
dOut.writeInt(ch.position().length);
|
|
||||||
for(int i = 0; i < ch.position().length; i++) {
|
|
||||||
Identifier id = ch.position()[i];
|
|
||||||
dOut.writeInt(id.digit());
|
|
||||||
dOut.writeInt(id.site());
|
|
||||||
}
|
|
||||||
|
|
||||||
dOut.writeInt(ch.lamport());
|
|
||||||
dOut.writeChar(ch.value());
|
|
||||||
return bOut.toByteArray();
|
|
||||||
}catch(IOException e) {
|
|
||||||
throw new RuntimeException("Something went very wrong", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Change deserialize(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
DataInputStream dIn = new DataInputStream(new ByteArrayInputStream(bytes));
|
|
||||||
int document = dIn.readInt();
|
|
||||||
ChangeType type = ChangeType.valueOf(dIn.readUTF());
|
|
||||||
|
|
||||||
Identifier[] pos = new Identifier[dIn.readInt()];
|
|
||||||
for(int i = 0; i < pos.length; i++) {
|
|
||||||
pos[i] = new Identifier(dIn.readInt(), dIn.readInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
int lamport = dIn.readInt();
|
|
||||||
char value = dIn.readChar();
|
|
||||||
return new Change(document, type, new Char(pos, lamport, value));
|
|
||||||
}catch(IllegalArgumentException e) {
|
|
||||||
throw new IllegalArgumentException("Failed to deserialize change", e);
|
|
||||||
}catch(IOException e) {
|
|
||||||
throw new RuntimeException("Something went very wrong", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class WSClient extends WebSocketClient {
|
private class WSClient extends WebSocketClient {
|
||||||
|
|
||||||
public WSClient(URI serverUri) {
|
public WSClient(URI serverUri) {
|
||||||
@ -116,7 +93,7 @@ public class WebSocketConnection implements RemoteConnection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(ServerHandshake handshakedata) {
|
public void onOpen(ServerHandshake handshakedata) {
|
||||||
// TODO: request site id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -126,9 +103,28 @@ public class WebSocketConnection implements RemoteConnection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(ByteBuffer bytes) {
|
public void onMessage(ByteBuffer bytes) {
|
||||||
byte[] bytesArray = new byte[bytes.remaining()];
|
Message m;
|
||||||
bytes.get(bytesArray);
|
try {
|
||||||
listeners.forEach(l -> l.onRemoteChange(deserialize(bytesArray)));
|
m = Message.deserialize(bytes);
|
||||||
|
}catch(IOException e) {
|
||||||
|
e.printStackTrace(); // TODO: custom logging (e.g. via error callback)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m instanceof ServerHelloMessage hello) {
|
||||||
|
helloReceived = true;
|
||||||
|
siteID = hello.siteID();
|
||||||
|
|
||||||
|
if(hello.protocolVersion() != PROTOCOL_VERSION) {
|
||||||
|
connectException = new ConnectionException(String.format("Protocol version mismatch: (Server has %s, Client has %s)", hello.protocolVersion(), PROTOCOL_VERSION));
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
wait.notifyAll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.forEach(l -> l.onMessage(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record BasicMessage(MessageType type) implements Message {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import me.mrletsplay.shareclientcore.connection.Change;
|
||||||
|
import me.mrletsplay.shareclientcore.connection.ChangeType;
|
||||||
|
import me.mrletsplay.shareclientcore.document.Char;
|
||||||
|
|
||||||
|
public record ChangeMessage(Change change) implements Message {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(change.document());
|
||||||
|
out.writeUTF(change.type().name());
|
||||||
|
change.character().serialize(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChangeMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
try {
|
||||||
|
return new ChangeMessage(new Change(in.readInt(), ChangeType.valueOf(in.readUTF()), Char.deserialize(in)));
|
||||||
|
}catch(IllegalArgumentException e) {
|
||||||
|
throw new IOException("Invalid change type", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record ClientHelloMessage(String username, String sessionID) implements Message {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.CLIENT_HELLO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeUTF(username);
|
||||||
|
out.writeUTF(sessionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClientHelloMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
return new ClientHelloMessage(in.readUTF(), in.readUTF());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import me.mrletsplay.shareclientcore.connection.SerializableObject;
|
||||||
|
|
||||||
|
public interface Message extends SerializableObject {
|
||||||
|
|
||||||
|
public MessageType getType();
|
||||||
|
|
||||||
|
public static Message deserialize(ByteBuffer buffer) throws IOException {
|
||||||
|
byte[] bytesArray = new byte[buffer.remaining()];
|
||||||
|
buffer.get(bytesArray);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DataInputStream dIn = new DataInputStream(new ByteArrayInputStream(bytesArray));
|
||||||
|
MessageType type = MessageType.valueOf(dIn.readUTF());
|
||||||
|
|
||||||
|
Message m;
|
||||||
|
switch(type) {
|
||||||
|
case CLIENT_HELLO -> m = ClientHelloMessage.deserialize(dIn);
|
||||||
|
case SERVER_HELLO -> m = ServerHelloMessage.deserialize(dIn);
|
||||||
|
case PEER_JOIN -> m = PeerJoinMessage.deserialize(dIn);
|
||||||
|
case PEER_LEAVE -> m = PeerLeaveMessage.deserialize(dIn);
|
||||||
|
case CHANGE -> m = ChangeMessage.deserialize(dIn);
|
||||||
|
default -> m = new BasicMessage(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}catch(IllegalArgumentException e) {
|
||||||
|
throw new IOException("Invalid message type", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
public enum MessageType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client hello containing user information
|
||||||
|
*/
|
||||||
|
CLIENT_HELLO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server hello containing protocol information
|
||||||
|
*/
|
||||||
|
SERVER_HELLO,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peer has joined
|
||||||
|
*/
|
||||||
|
PEER_JOIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peer has left
|
||||||
|
*/
|
||||||
|
PEER_LEAVE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full synchronization message, containing the full contents of a file
|
||||||
|
*/
|
||||||
|
FULL_SYNC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for the synchronization of a particular file or all shared files
|
||||||
|
*/
|
||||||
|
REQUEST_FULL_SYNC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single change made by one peer
|
||||||
|
*/
|
||||||
|
CHANGE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for the checksum of a file
|
||||||
|
*/
|
||||||
|
REQUEST_CHECKSUM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checksum of a file
|
||||||
|
*/
|
||||||
|
CHECKSUM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creation of a file
|
||||||
|
*/
|
||||||
|
CREATE_FILE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletion of a file
|
||||||
|
*/
|
||||||
|
DELETE_FILE,
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record PeerJoinMessage(String peerName, int peerSiteID) implements Message {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.PEER_JOIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeUTF(peerName);
|
||||||
|
out.writeInt(peerSiteID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeerJoinMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
return new PeerJoinMessage(in.readUTF(), in.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record PeerLeaveMessage(int peerSiteID) implements Message {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.PEER_LEAVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(peerSiteID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeerLeaveMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
return new PeerLeaveMessage(in.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package me.mrletsplay.shareclientcore.connection.message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record ServerHelloMessage(int protocolVersion, int siteID) implements Message {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageType getType() {
|
||||||
|
return MessageType.SERVER_HELLO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(protocolVersion);
|
||||||
|
out.writeInt(siteID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServerHelloMessage deserialize(DataInputStream in) throws IOException {
|
||||||
|
return new ServerHelloMessage(in.readInt(), in.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,14 @@
|
|||||||
package me.mrletsplay.shareclientcore.document;
|
package me.mrletsplay.shareclientcore.document;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public record Char(Identifier[] position, int lamport, char value) {
|
import me.mrletsplay.shareclientcore.connection.SerializableObject;
|
||||||
|
|
||||||
|
public record Char(Identifier[] position, int lamport, char value) implements SerializableObject {
|
||||||
|
|
||||||
public static final Char START_OF_DOCUMENT = new Char(new Identifier[] { new Identifier(1, 0) }, 0, '^');
|
public static final Char START_OF_DOCUMENT = new Char(new Identifier[] { new Identifier(1, 0) }, 0, '^');
|
||||||
public static final Char END_OF_DOCUMENT = new Char(new Identifier[] { new Identifier(Util.BASE - 1, 0) }, 0, '$');
|
public static final Char END_OF_DOCUMENT = new Char(new Identifier[] { new Identifier(Util.BASE - 1, 0) }, 0, '$');
|
||||||
@ -28,4 +33,28 @@ public record Char(Identifier[] position, int lamport, char value) {
|
|||||||
return lamport == other.lamport && Arrays.equals(position, other.position) && value == other.value;
|
return lamport == other.lamport && Arrays.equals(position, other.position) && value == other.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(DataOutputStream out) throws IOException {
|
||||||
|
out.writeInt(position.length);
|
||||||
|
for(int i = 0; i < position.length; i++) {
|
||||||
|
Identifier id = position[i];
|
||||||
|
out.writeInt(id.digit());
|
||||||
|
out.writeInt(id.site());
|
||||||
|
}
|
||||||
|
|
||||||
|
out.writeInt(lamport);
|
||||||
|
out.writeChar(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Char deserialize(DataInputStream in) throws IOException {
|
||||||
|
Identifier[] pos = new Identifier[in.readInt()];
|
||||||
|
for(int i = 0; i < pos.length; i++) {
|
||||||
|
pos[i] = new Identifier(in.readInt(), in.readInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
int lamport = in.readInt();
|
||||||
|
char value = in.readChar();
|
||||||
|
return new Char(pos, lamport, value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,13 @@ import java.util.Set;
|
|||||||
|
|
||||||
import me.mrletsplay.shareclientcore.connection.Change;
|
import me.mrletsplay.shareclientcore.connection.Change;
|
||||||
import me.mrletsplay.shareclientcore.connection.ChangeType;
|
import me.mrletsplay.shareclientcore.connection.ChangeType;
|
||||||
|
import me.mrletsplay.shareclientcore.connection.ConnectionException;
|
||||||
|
import me.mrletsplay.shareclientcore.connection.MessageListener;
|
||||||
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
|
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
|
||||||
import me.mrletsplay.shareclientcore.connection.RemoteListener;
|
import me.mrletsplay.shareclientcore.connection.message.ChangeMessage;
|
||||||
|
import me.mrletsplay.shareclientcore.connection.message.Message;
|
||||||
|
|
||||||
public class SharedDocument implements RemoteListener {
|
public class SharedDocument implements MessageListener {
|
||||||
|
|
||||||
private RemoteConnection connection;
|
private RemoteConnection connection;
|
||||||
private CharBag charBag;
|
private CharBag charBag;
|
||||||
@ -27,7 +30,7 @@ public class SharedDocument implements RemoteListener {
|
|||||||
charBag.add(Char.END_OF_DOCUMENT);
|
charBag.add(Char.END_OF_DOCUMENT);
|
||||||
|
|
||||||
this.document = 0; // TODO: implement
|
this.document = 0; // TODO: implement
|
||||||
this.site = connection.retrieveSiteID();
|
this.site = connection.getSiteID();
|
||||||
this.listeners = new HashSet<>();
|
this.listeners = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +56,13 @@ public class SharedDocument implements RemoteListener {
|
|||||||
charBefore = ch;
|
charBefore = ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.send(changes);
|
for(Change c : changes) {
|
||||||
|
try {
|
||||||
|
connection.send(new ChangeMessage(c));
|
||||||
|
} catch (ConnectionException e) {
|
||||||
|
e.printStackTrace(); // TODO: throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,7 +82,14 @@ public class SharedDocument implements RemoteListener {
|
|||||||
charBag.remove(toRemove);
|
charBag.remove(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.send(changes);
|
|
||||||
|
for(Change c : changes) {
|
||||||
|
try {
|
||||||
|
connection.send(new ChangeMessage(c));
|
||||||
|
} catch (ConnectionException e) {
|
||||||
|
e.printStackTrace(); // TODO: throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,8 +136,9 @@ public class SharedDocument implements RemoteListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRemoteChange(Change... changes) {
|
public void onMessage(Message message) {
|
||||||
for(Change c : changes) {
|
if(message instanceof ChangeMessage change) {
|
||||||
|
Change c = change.change();
|
||||||
System.out.println("Change: " + c + " | " + Arrays.toString(c.character().position()));
|
System.out.println("Change: " + c + " | " + Arrays.toString(c.character().position()));
|
||||||
switch(c.type()) {
|
switch(c.type()) {
|
||||||
case ADD -> remoteInsert(c.character());
|
case ADD -> remoteInsert(c.character());
|
||||||
|
Loading…
Reference in New Issue
Block a user