Implemented u_gbl and Added ConnectionIds for each Connection

- Needs lots of testing
This commit is contained in:
Julia Butenhoff 2022-07-18 13:39:00 -05:00 committed by Julia Butenhoff
parent 1dae89176d
commit 2fe09b3116
11 changed files with 241 additions and 7 deletions

View File

@ -3,7 +3,10 @@ package com.icedberries.UBFunkeysServer.ArkOne;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.BasePlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.GalaxyPlugin;
import com.icedberries.UBFunkeysServer.ArkOne.Plugins.UserPlugin;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import javagrinko.spring.tcp.Connection;
import javagrinko.spring.tcp.Server;
import javagrinko.spring.tcp.TcpController;
import javagrinko.spring.tcp.TcpHandler;
import org.springframework.beans.factory.annotation.Autowired;
@ -19,6 +22,13 @@ public class ArkOneController implements TcpHandler {
public static final String IP_ADDRESS = "127.0.0.1";
// Services
@Autowired
private UserService userService;
@Autowired
private ArkOneSender arkOneSender;
// Plugins
@Autowired
BasePlugin basePlugin;
@ -57,13 +67,16 @@ public class ArkOneController implements TcpHandler {
responses.add(basePlugin.GetServiceDetails(commandInfo.getAttribute("s")));
break;
case "a_lru":
responses.add(basePlugin.LoginRegisteredUser(commandInfo));
responses.add(basePlugin.LoginRegisteredUser(commandInfo, connection.getClientIdentifier()));
break;
// Plugin 1 (User)
case "u_reg":
responses.add(userPlugin.RegisterUser(commandInfo));
break;
case "u_gbl":
responses.add(userPlugin.GetBuddyList(commandInfo));
break;
case "p":
responses.add(userPlugin.Ping());
break;
@ -113,6 +126,24 @@ public class ArkOneController implements TcpHandler {
@Override
public void disconnectEvent(Connection connection) {
// No need to log anything
//TODO: WRITE DISCONNECT LOGIC for u_cos
// SOME HOW STORE THE USER ID HERE SO WE CAN SET THEIR isOnline status to offline
User user = userService.findByConnectionId(connection.getClientIdentifier()).orElse(null);
if (user != null) {
// Update the online status to offline and clear the connection ID
user.setIsOnline(0);
user.setConnectionId(null);
// Update the user in the DB
userService.save(user);
// Notify other users that they went offline
try {
arkOneSender.SendStatusUpdate("u_cos", "o", "0", user.getUUID());
} catch(Exception e) {
// Do nothing since the client is disconnecting anyway
}
}
}
}

View File

@ -0,0 +1,87 @@
package com.icedberries.UBFunkeysServer.ArkOne;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import javagrinko.spring.tcp.Connection;
import javagrinko.spring.tcp.Server;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
@Service
public class ArkOneSender {
@Autowired
Server server;
@Autowired
private UserService userService;
public void SendStatusUpdate(String statusHeader, String shortHeader, String status, Integer userUUID)
throws ParserConfigurationException, TransformerException {
ArrayList<String> buddyList = new ArrayList<>(Arrays.asList(userService.getBuddyList(userUUID).split(",")));
for (String buddy : buddyList) {
if (!buddy.equals("")) {
int isOnline = 0;
UUID connectionId = null;
User buddyUser = userService.findByUUID(Integer.valueOf(buddy)).orElse(null);
if (buddyUser != null) {
isOnline = buddyUser.getIsOnline();
if (isOnline == 1) {
connectionId = buddyUser.getConnectionId();
// Build announcement
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement(statusHeader);
rootElement.setAttribute(shortHeader, status);
rootElement.setAttribute("id", String.valueOf(userUUID));
doc.appendChild(rootElement);
// Send announcement to user
SendToUser(connectionId, ArkOneParser.RemoveXMLTag(doc));
}
}
}
}
}
public void SendToUser(UUID clientId, String message) {
Connection connection = null;
for (Connection conn : server.getConnections()) {
if (conn.getClientIdentifier() == clientId) {
connection = conn;
}
}
if (connection != null) {
try {
// Append a 0x00 to the end of the response
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(message.getBytes());
outputStream.write((byte)0x00);
connection.send(outputStream.toByteArray());
} catch (IOException e) {
System.out.println("[ArkOne][ERROR] Failed to send message to user [" + clientId + "]: " + message);
e.printStackTrace();
}
}
}
}

View File

@ -14,6 +14,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.util.UUID;
@Service
public class BasePlugin {
@ -151,7 +152,7 @@ public class BasePlugin {
return ArkOneParser.RemoveXMLTag(doc);
}
public String LoginRegisteredUser(Element element) throws ParserConfigurationException, TransformerException {
public String LoginRegisteredUser(Element element, UUID connectionId) throws ParserConfigurationException, TransformerException {
// Response r codes:
// 0 - Accepted Login
// 1 - Already exist in Terrapinia
@ -179,6 +180,10 @@ public class BasePlugin {
} else {
// Login success
uuid = String.valueOf(user.getUUID());
// Store the UUID for our connection to the server to the DB
user.setConnectionId(connectionId);
userService.save(user);
}
}

View File

@ -1,6 +1,7 @@
package com.icedberries.UBFunkeysServer.ArkOne.Plugins;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneParser;
import com.icedberries.UBFunkeysServer.ArkOne.ArkOneSender;
import com.icedberries.UBFunkeysServer.domain.User;
import com.icedberries.UBFunkeysServer.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
@ -13,6 +14,9 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
@Service
public class UserPlugin {
@ -23,6 +27,9 @@ public class UserPlugin {
@Autowired
private UserService userService;
@Autowired
private ArkOneSender arkOneSender;
public String RegisterUser(Element element) throws ParserConfigurationException, TransformerException {
String username = element.getAttribute("l");
String password = element.getAttribute("p");
@ -71,6 +78,64 @@ public class UserPlugin {
return ArkOneParser.RemoveXMLTag(doc);
}
public String GetBuddyList(Element element) throws ParserConfigurationException, TransformerException {
//TODO: VERIFY THE ATTRIBUTE NAME
User user = userService.findByUUID(Integer.valueOf(element.getAttribute("id"))).orElse(null);
// Start of response
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
Element rootElement = doc.createElement("u_gbl");
rootElement.setAttribute("r", "0");
doc.appendChild(rootElement);
// User should never be null here but check just in case
if (user == null) {
return ArkOneParser.RemoveXMLTag(doc);
}
ArrayList<String> buddyList = new ArrayList<>(Arrays.asList(user.getRawBuddyList().split(",")));
// Skip NULL or blank buddy lists
if (buddyList.size() > 0) {
for (String buddy : buddyList) {
if (buddy.equals("")) { continue; }
Integer buddyUUID = Integer.valueOf(buddy);
// Get the buddy's information
User buddyUser = userService.findByUUID(buddyUUID).orElse(null);
// Make sure they aren't null
if (buddyUser == null) {
continue;
}
// Get information off their data to build a xml tag
Element buddyElement = doc.createElement("buddy");
buddyElement.setAttribute("id", String.valueOf(buddyUser.getUUID()));
buddyElement.setAttribute("n", buddyUser.getUsername());
buddyElement.setAttribute("s", String.valueOf(buddyUser.getChatStatus()));
buddyElement.setAttribute("o", String.valueOf(buddyUser.getIsOnline()));
buddyElement.setAttribute("ph", String.valueOf(buddyUser.getPhoneStatus()));
rootElement.appendChild(buddyElement);
}
}
// The client doesn't set online status. So the server sets it when it gets all the user's buddies
user.setIsOnline(1);
userService.save(user);
if (buddyList.size() > 0) {
arkOneSender.SendStatusUpdate("u_cos", "o",
String.valueOf(user.getIsOnline()), user.getUUID());
}
return ArkOneParser.RemoveXMLTag(doc);
}
public String Ping() {
return "<p t=\"30\" />";
}

View File

@ -14,6 +14,7 @@ import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.ArrayList;
import java.util.UUID;
@Getter
@Setter
@ -64,9 +65,6 @@ public class User {
@Column(name = "buddyList")
private String rawBuddyList;
@Transient
private ArrayList<User> buddyList;
// Connection ID to send data to from other Users
private String connectionId;
private java.util.UUID connectionId;
}

View File

@ -7,6 +7,7 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.UUID;
@Repository
public interface UserRepository extends CrudRepository<User, Integer> {
@ -20,4 +21,10 @@ public interface UserRepository extends CrudRepository<User, Integer> {
@Query("select user from User user where user.username = :username")
Optional<User> findByUsername(@Param("username") String username);
@Query("select user.rawBuddyList from User user where user.UUID = :uuid")
String getBuddyList(@Param("uuid") Integer uuid);
@Query("select user from User user where user.connectionId = :connectionId")
Optional<User> findByConnectionId(@Param("connectionId") UUID connectionId);
}

View File

@ -3,6 +3,7 @@ package com.icedberries.UBFunkeysServer.service;
import com.icedberries.UBFunkeysServer.domain.User;
import java.util.Optional;
import java.util.UUID;
public interface UserService {
@ -15,4 +16,8 @@ public interface UserService {
Boolean existsByUsername(String username);
Optional<User> findByUsername(String username);
String getBuddyList(Integer uuid);
Optional<User> findByConnectionId(UUID connectionId);
}

View File

@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.UUID;
@Service
@RequiredArgsConstructor
@ -39,4 +40,14 @@ public class UserServiceImpl implements UserService {
public Optional<User> findByUsername(String username) {
return userRepository.findByUsername(username);
}
@Override
public String getBuddyList(Integer uuid) {
return userRepository.getBuddyList(uuid);
}
@Override
public Optional<User> findByConnectionId(UUID connectionId) {
return userRepository.findByConnectionId(connectionId);
}
}

View File

@ -3,6 +3,7 @@ package javagrinko.spring.tcp;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.util.UUID;
public interface Connection {
InetAddress getAddress();
@ -10,6 +11,8 @@ public interface Connection {
void addListener(Listener listener);
void start();
void close() throws IOException;
UUID getClientIdentifier();
void setClientIdentifier(UUID newId);
interface Listener {
void messageReceived(Connection connection, byte[] bytes)
throws InvocationTargetException, IllegalAccessException;

View File

@ -11,6 +11,7 @@ import java.net.InetAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
public class TcpConnection implements Connection {
@ -20,6 +21,8 @@ public class TcpConnection implements Connection {
private Socket socket;
private List<Listener> listeners = new CopyOnWriteArrayList<>();
private UUID clientIdentifier = null;
TcpConnection(Socket socket) {
this.socket = socket;
try {
@ -93,4 +96,14 @@ public class TcpConnection implements Connection {
public void close() throws IOException {
socket.close();
}
@Override
public UUID getClientIdentifier() {
return clientIdentifier;
}
@Override
public void setClientIdentifier(UUID newId) {
this.clientIdentifier = newId;
}
}

View File

@ -9,6 +9,7 @@ import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
@Component
@ -89,6 +90,10 @@ public class TcpServer implements Server, Connection.Listener {
throws InvocationTargetException, IllegalAccessException {
logger.info("New connection! Ip: " + connection.getAddress().getCanonicalHostName() + ".");
connections.add(connection);
// Generate a UUID for this connection
connection.setClientIdentifier(UUID.randomUUID());
logger.info("Current connections count: " + connections.size());
for (Connection.Listener listener : listeners) {
listener.connected(connection);
@ -100,6 +105,10 @@ public class TcpServer implements Server, Connection.Listener {
throws InvocationTargetException, IllegalAccessException {
logger.info("Disconnect! Ip: " + connection.getAddress().getCanonicalHostName() + ".");
connections.remove(connection);
// Remove the UUID
connection.setClientIdentifier(null);
logger.info("Current connections count: " + connections.size());
for (Connection.Listener listener : listeners) {
listener.disconnected(connection);