233 lines
6.2 KiB
Java
233 lines
6.2 KiB
Java
package dev.wiing.gossip.client;
|
|
|
|
import dev.wiing.gossip.lib.PacketHandler;
|
|
import dev.wiing.gossip.lib.PacketManager;
|
|
import dev.wiing.gossip.lib.models.SecretUser;
|
|
import dev.wiing.gossip.lib.models.User;
|
|
import dev.wiing.gossip.lib.packets.*;
|
|
|
|
import java.io.IOException;
|
|
import java.net.Socket;
|
|
import java.net.SocketException;
|
|
import java.util.*;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
public class Connection {
|
|
|
|
private static final Connection instance = new Connection();
|
|
|
|
private final PacketManager packetManager = new PacketManager();
|
|
|
|
private final PacketHandler packetHandler = new PacketHandler();
|
|
|
|
private Thread handlerThread;
|
|
|
|
private Socket socket;
|
|
|
|
private SecretUser self;
|
|
|
|
private final List<Packet> queuedPackets = Collections.synchronizedList(new LinkedList<>());
|
|
|
|
private Connection() {
|
|
packetManager.registerPackets();
|
|
}
|
|
|
|
public static Connection getInstance() {
|
|
return instance;
|
|
}
|
|
|
|
public PacketManager getPacketManager() {
|
|
return packetManager;
|
|
}
|
|
|
|
public PacketHandler getPacketHandler() {
|
|
return packetHandler;
|
|
}
|
|
|
|
public static class PacketHandlerRunnable implements Runnable {
|
|
private final Connection connection;
|
|
|
|
public PacketHandlerRunnable(Connection connection) {
|
|
this.connection = connection;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
while (true) {
|
|
try {
|
|
Packet packet;
|
|
if (connection.getSocket().getInputStream().available() > 0 || connection.queuedPackets.isEmpty()) {
|
|
packet = connection.nextPacket(false);
|
|
} else {
|
|
packet = connection.findPacketOfTypes(connection.packetHandler.getListeningTypes());
|
|
}
|
|
|
|
if (packet == null) continue;
|
|
|
|
if (!connection.getPacketHandler().runPacket(packet)) {
|
|
connection.queuedPackets.add(packet);
|
|
}
|
|
} catch (SocketException e) {
|
|
break;
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void beginHandlingPackets() {
|
|
handlerThread = new Thread(new PacketHandlerRunnable(this));
|
|
handlerThread.start();
|
|
}
|
|
|
|
public Socket getSocket() {
|
|
return socket;
|
|
}
|
|
|
|
public void setSocket(Socket socket) {
|
|
this.socket = socket;
|
|
}
|
|
|
|
public void sendPacket(Packet packet) {
|
|
try {
|
|
getPacketManager().writePacket(getSocket().getOutputStream(), packet);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public void sendPacketAuthenticated(AuthRequiredPacket packet) {
|
|
packet.setAuth(getSelf().getUserSecret());
|
|
sendPacket(packet);
|
|
}
|
|
|
|
public Packet nextPacket(boolean useQueue) throws SocketException {
|
|
if (useQueue && !queuedPackets.isEmpty()) {
|
|
return queuedPackets.remove(0);
|
|
}
|
|
|
|
try {
|
|
return getPacketManager().readPacket(getSocket().getInputStream());
|
|
} catch (SocketException e) {
|
|
throw e;
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public Packet nextPacket() throws SocketException {
|
|
return nextPacket(true);
|
|
}
|
|
|
|
public Packet nextPacket(short packetType) throws SocketException {
|
|
while (true) {
|
|
Packet packet = nextPacket(false);
|
|
|
|
if (packet.getType() != packetType) {
|
|
queuedPackets.add(packet);
|
|
continue;
|
|
}
|
|
|
|
return packet;
|
|
}
|
|
}
|
|
|
|
public Packet findPacketOfTypes(List<Short> types) {
|
|
Packet packet = null;
|
|
|
|
int i;
|
|
for (i = 0; i < queuedPackets.size(); i++) {
|
|
Packet queuedPacket = queuedPackets.get(i);
|
|
|
|
if (types.contains(queuedPacket.getType())) {
|
|
packet = queuedPacket;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (packet != null) {
|
|
queuedPackets.remove(i);
|
|
return packet;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public boolean findAck(short acknowledgement) throws IOException {
|
|
|
|
while (true) {
|
|
while (socket.getInputStream().available() > 0);
|
|
|
|
AckPacket ack = null;
|
|
|
|
int i;
|
|
for (i = 0; i < queuedPackets.size(); i++) {
|
|
Packet queuedPacket = queuedPackets.get(i);
|
|
|
|
if (queuedPacket.getType() == AckPacket.TYPE) {
|
|
ack = (AckPacket) queuedPacket;
|
|
|
|
if (ack.getAcknowledgement() == acknowledgement) {
|
|
break;
|
|
}
|
|
|
|
ack = null;
|
|
}
|
|
}
|
|
|
|
if (ack != null) {
|
|
queuedPackets.remove(i);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public SecretUser getSelf() {
|
|
return self;
|
|
}
|
|
|
|
public void setSelf(SecretUser self) {
|
|
this.self = self;
|
|
|
|
userCache.put(self.getUserID(), new User(
|
|
self.getUsername(),
|
|
self.getAvatarID(),
|
|
self.getUserID()
|
|
));
|
|
}
|
|
|
|
private final Map<Long, User> userCache = new ConcurrentHashMap<>();
|
|
|
|
public User getUser(long userID) {
|
|
User result;
|
|
|
|
if ((result = userCache.getOrDefault(userID, null)) != null) {
|
|
return result;
|
|
}
|
|
|
|
UserFetchPacket fetch = new UserFetchPacket();
|
|
fetch.setUserID(userID);
|
|
Connection.getInstance().sendPacket(fetch);
|
|
|
|
Packet resp;
|
|
try {
|
|
resp = Connection.getInstance().nextPacket(UserDataPacket.TYPE);
|
|
} catch (SocketException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
if (resp.getType() == UserDataPacket.TYPE) {
|
|
UserDataPacket userData = (UserDataPacket)resp;
|
|
result = new User(
|
|
userData.getUsername(),
|
|
userData.getAvatarID(),
|
|
userData.getUserID()
|
|
);
|
|
userCache.put(result.getUserID(), result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|