mirror of
https://github.com/Leahnaya/UBFunkeysServer.git
synced 2026-09-07 08:15:16 -05:00
Added JavaGrinko's TcpController Library
- Started implementing it into the ArkOneController class - Needs testing to verify it can communicate with the game
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package com.icedberries.UBFunkeysServer.ArkOne;
|
||||
|
||||
import javagrinko.spring.tcp.Connection;
|
||||
import javagrinko.spring.tcp.TcpController;
|
||||
import javagrinko.spring.tcp.TcpHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@TcpController
|
||||
public class ArkOneController implements TcpHandler {
|
||||
|
||||
@Override
|
||||
public void receiveData(Connection connection, byte[] data) {
|
||||
//TODO: IMPLEMENT PLUGIN CHECKING AND FORWARDING HERE
|
||||
//Currently just echos back the received data until implemented
|
||||
String s = new String(data);
|
||||
try {
|
||||
connection.send(s.toUpperCase().getBytes());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectEvent(Connection connection) {
|
||||
// Just log connections
|
||||
System.out.println("[ArkOne][EVENT] Client connection from: " + connection.getAddress().getCanonicalHostName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnectEvent(Connection connection) {
|
||||
// Just log disconnections
|
||||
System.out.println("[ArkOne][EVENT] Client disconnected: " + connection.getAddress().getCanonicalHostName());
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,7 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
@SpringBootApplication
|
||||
@ComponentScan(basePackages = "com.icedberries")
|
||||
@SpringBootApplication(scanBasePackages = {"com.icedberries", "javagrinko.spring"})
|
||||
public class UBFunkeysServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package javagrinko.spring.starter;
|
||||
|
||||
import javagrinko.spring.tcp.Server;
|
||||
import javagrinko.spring.tcp.TcpControllerBeanPostProcessor;
|
||||
import javagrinko.spring.tcp.TcpServer;
|
||||
import javagrinko.spring.tcp.TcpServerAutoStarterApplicationListener;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TcpServerProperties.class)
|
||||
@ConditionalOnProperty(prefix = "javagrinko.tcp-server", name = {"port", "auto-start"})
|
||||
public class TcpServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
TcpServerAutoStarterApplicationListener tcpServerAutoStarterApplicationListener() {
|
||||
return new TcpServerAutoStarterApplicationListener();
|
||||
}
|
||||
|
||||
@Bean
|
||||
TcpControllerBeanPostProcessor tcpControllerBeanPostProcessor() {
|
||||
return new TcpControllerBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Server server(){
|
||||
return new TcpServer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package javagrinko.spring.starter;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "tcp.server")
|
||||
public class TcpServerProperties {
|
||||
|
||||
@Value("${tcp.server.port}")
|
||||
private Integer port = 1234;
|
||||
|
||||
@Value("${tcp.server.auto-start}")
|
||||
private Boolean autoStart = true;
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public boolean getAutoStart() {
|
||||
return autoStart;
|
||||
}
|
||||
|
||||
public void setAutoStart(boolean autoStart) {
|
||||
this.autoStart = autoStart;
|
||||
}
|
||||
}
|
||||
23
src/main/java/javagrinko/spring/tcp/Connection.java
Normal file
23
src/main/java/javagrinko/spring/tcp/Connection.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetAddress;
|
||||
|
||||
public interface Connection {
|
||||
InetAddress getAddress();
|
||||
void send(byte[] bytes) throws IOException;
|
||||
void addListener(Listener listener);
|
||||
void start();
|
||||
void close() throws IOException;
|
||||
interface Listener {
|
||||
void messageReceived(Connection connection, byte[] bytes)
|
||||
throws InvocationTargetException, IllegalAccessException;
|
||||
|
||||
void connected(Connection connection)
|
||||
throws InvocationTargetException, IllegalAccessException;
|
||||
|
||||
void disconnected(Connection connection)
|
||||
throws InvocationTargetException, IllegalAccessException;
|
||||
}
|
||||
}
|
||||
13
src/main/java/javagrinko/spring/tcp/Server.java
Normal file
13
src/main/java/javagrinko/spring/tcp/Server.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Server {
|
||||
int getConnectionsCount();
|
||||
void setPort(Integer port);
|
||||
void start();
|
||||
void stop();
|
||||
List<Connection> getConnections();
|
||||
void addListener(Connection.Listener listener);
|
||||
}
|
||||
96
src/main/java/javagrinko/spring/tcp/TcpConnection.java
Normal file
96
src/main/java/javagrinko/spring/tcp/TcpConnection.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class TcpConnection implements Connection {
|
||||
private static Log logger = LogFactory.getLog(TcpConnection.class);
|
||||
private InputStream inputStream;
|
||||
private OutputStream outputStream;
|
||||
private Socket socket;
|
||||
private List<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
TcpConnection(Socket socket) {
|
||||
this.socket = socket;
|
||||
try {
|
||||
inputStream = socket.getInputStream();
|
||||
outputStream = socket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getAddress() {
|
||||
return socket.getInetAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(byte[] bytes) throws IOException {
|
||||
outputStream.write(bytes);
|
||||
logger.trace("Sent message");
|
||||
if (logger.isTraceEnabled()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02X ", b));
|
||||
}
|
||||
logger.trace(" ==> " + sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
byte buf[] = new byte[64 * 1024];
|
||||
try {
|
||||
int count = inputStream.read(buf);
|
||||
if (count > 0) {
|
||||
byte[] bytes = Arrays.copyOf(buf, count);
|
||||
for (Listener listener : listeners) {
|
||||
listener.messageReceived(this, bytes);
|
||||
}
|
||||
} else {
|
||||
socket.close();
|
||||
disconnectAll();
|
||||
break;
|
||||
}
|
||||
} catch (IOException | IllegalAccessException | InvocationTargetException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
disconnectAll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void disconnectAll() {
|
||||
for (Listener listener : listeners) {
|
||||
try {
|
||||
listener.disconnected(this);
|
||||
} catch (InvocationTargetException | IllegalAccessException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
11
src/main/java/javagrinko/spring/tcp/TcpController.java
Normal file
11
src/main/java/javagrinko/spring/tcp/TcpController.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Component
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TcpController {
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class TcpControllerBeanPostProcessor implements BeanPostProcessor {
|
||||
private Map<String, Class> cache = new HashMap<>();
|
||||
|
||||
@Autowired
|
||||
private Server server;
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
Class<?> beanClass = bean.getClass();
|
||||
if (bean instanceof TcpHandler) {
|
||||
cache.put(beanName, beanClass);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (cache.containsKey(beanName)) {
|
||||
TcpHandler tcpHandler = (TcpHandler) bean;
|
||||
server.addListener(new Connection.Listener() {
|
||||
@Override
|
||||
public void messageReceived(Connection connection, byte[] bytes)
|
||||
throws InvocationTargetException, IllegalAccessException {
|
||||
tcpHandler.receiveData(connection, bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(Connection connection) throws InvocationTargetException, IllegalAccessException {
|
||||
tcpHandler.connectEvent(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Connection connection) throws InvocationTargetException, IllegalAccessException {
|
||||
tcpHandler.disconnectEvent(connection);
|
||||
}
|
||||
});
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
7
src/main/java/javagrinko/spring/tcp/TcpHandler.java
Normal file
7
src/main/java/javagrinko/spring/tcp/TcpHandler.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
public interface TcpHandler {
|
||||
void receiveData(Connection connection, byte[] data);
|
||||
void connectEvent(Connection connection);
|
||||
void disconnectEvent(Connection connection);
|
||||
}
|
||||
108
src/main/java/javagrinko/spring/tcp/TcpServer.java
Normal file
108
src/main/java/javagrinko/spring/tcp/TcpServer.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@Component
|
||||
public class TcpServer implements Server, Connection.Listener {
|
||||
private static Log logger = LogFactory.getLog(TcpServer.class);
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
private volatile boolean isStop;
|
||||
private List<Connection> connections = new CopyOnWriteArrayList<>();
|
||||
private List<Connection.Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public void setPort(Integer port) {
|
||||
try {
|
||||
serverSocket = new ServerSocket(port);
|
||||
logger.info("Server start at port " + port);
|
||||
} catch (IOException e) {
|
||||
logger.error("Port " + port + " busy.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConnectionsCount() {
|
||||
return connections.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
new Thread(() -> {
|
||||
while (!isStop) {
|
||||
try {
|
||||
Socket socket = serverSocket.accept();
|
||||
if (socket.isConnected()) {
|
||||
TcpConnection tcpConnection = new TcpConnection(socket);
|
||||
tcpConnection.start();
|
||||
tcpConnection.addListener(this);
|
||||
connected(tcpConnection);
|
||||
}
|
||||
} catch (IOException | IllegalAccessException | InvocationTargetException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
isStop = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Connection> getConnections() {
|
||||
return connections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Connection.Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(Connection connection, byte[] bytes)
|
||||
throws InvocationTargetException, IllegalAccessException {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received message from " + connection.getAddress().getCanonicalHostName());
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
sb.append(String.format("%02X ", b));
|
||||
}
|
||||
logger.trace(" <== " + sb.toString());
|
||||
}
|
||||
for (Connection.Listener listener : listeners) {
|
||||
listener.messageReceived(connection, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(Connection connection)
|
||||
throws InvocationTargetException, IllegalAccessException {
|
||||
logger.info("New connection! Ip: " + connection.getAddress().getCanonicalHostName() + ".");
|
||||
connections.add(connection);
|
||||
logger.info("Current connections count: " + connections.size());
|
||||
for (Connection.Listener listener : listeners) {
|
||||
listener.connected(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Connection connection)
|
||||
throws InvocationTargetException, IllegalAccessException {
|
||||
logger.info("Disconnect! Ip: " + connection.getAddress().getCanonicalHostName() + ".");
|
||||
connections.remove(connection);
|
||||
logger.info("Current connections count: " + connections.size());
|
||||
for (Connection.Listener listener : listeners) {
|
||||
listener.disconnected(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package javagrinko.spring.tcp;
|
||||
|
||||
import javagrinko.spring.starter.TcpServerProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class TcpServerAutoStarterApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
@Autowired
|
||||
private TcpServerProperties properties;
|
||||
|
||||
@Autowired
|
||||
private Server server;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
boolean autoStart = properties.getAutoStart();
|
||||
if (autoStart){
|
||||
server.setPort(properties.getPort());
|
||||
server.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user