me when i forget to commit my stuff.

This commit is contained in:
Xircon 2026-02-21 00:20:13 -05:00
parent f02aaa461a
commit 9a1e6e925a
20 changed files with 584 additions and 36 deletions

6
.gitignore vendored
View File

@ -40,4 +40,8 @@ bin/
.vscode/
### Mac OS ###
.DS_Store
.DS_Store
## Logs ##
/server/logs/
/client/logs/

2
.idea/misc.xml generated
View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -1,18 +1,32 @@
plugins {
id("java")
application
}
group = "net.xircon"
version = "1.0-SNAPSHOT"
val log4jVersion = "2.25.1"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation (platform("org.apache.logging.log4j:log4j-bom:$log4jVersion"))
implementation("org.apache.logging.log4j:log4j-core:$log4jVersion")
implementation("org.apache.logging.log4j:log4j-api:$log4jVersion")
implementation("com.formdev:flatlaf:3.7")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
application {
mainClass = "net.xircon.xenon.client.Xenon"
}
tasks.test {

View File

@ -1,17 +0,0 @@
package net.xircon;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}

View File

@ -0,0 +1,143 @@
package net.xircon.xenon.client;
import com.formdev.flatlaf.FlatDarkLaf;
import net.xircon.xenon.client.io.Log;
import net.xircon.xenon.client.io.Util;
import net.xircon.xenon.client.networking.Networking;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.Socket;
import java.util.Objects;
public class Xenon implements Runnable {
private Thread mainThread;
public static void main(String[] args) {new Xenon().start();}
private static Socket mainSocket;
static final String propertiesFile = "client.properties";
// Properties
public static int PortVoIP = 49190;
public static int PortText = 49180;
public static int Width = 800;
public static int Height = 600;
private static String nickname = "Test User";
public void start() {
mainThread = new Thread(this, "mainThread");
mainThread.start();
}
@Override
public void run() {
Log.info("Starting Xenon Client!");
// Get Property Values
PortVoIP = Objects.requireNonNull(Util.GetProperties(propertiesFile, "PortVoIP", Util.PROPERTY_TYPE_INT)).intValue;
PortText = Objects.requireNonNull(Util.GetProperties(propertiesFile, "PortText", Util.PROPERTY_TYPE_INT)).intValue;
Width = Objects.requireNonNull(Util.GetProperties(propertiesFile, "Width", Util.PROPERTY_TYPE_INT)).intValue;
Height = Objects.requireNonNull(Util.GetProperties(propertiesFile, "Height", Util.PROPERTY_TYPE_INT)).intValue;
// Start Java Swing GUI
JFrame mainFrame = new JFrame("Xenon Client");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(Width, Height);
try {
UIManager.setLookAndFeel( new FlatDarkLaf());
} catch (UnsupportedLookAndFeelException e) {
Log.err("Java Swing GUI Look and Feel Did Not Correctly Apply! Falling Back to Default!");
}
// Creating the MenuBar and adding components
JMenuBar mb = new JMenuBar();
// Menu 1
JMenu m1 = new JMenu("File");
JMenuItem m11 = new JMenuItem("Open");
JMenuItem m12 = new JMenuItem("Save as");
mb.add(m1);
m1.add(m11);
m1.add(m12);
// Menu 2
JMenu m2 = new JMenu("Connections");
JMenuItem m21 = new JMenuItem(new AbstractAction("Connect To Server") {
@Override
public void actionPerformed(ActionEvent e) {
String IPAddress = JOptionPane.showInputDialog(null, "IP Address Of Server", "127.0.0.1");
if (IPAddress == null) {return;}
else {
mainSocket = Networking.connectToServer(IPAddress, PortText);
}
}
});
JMenuItem m22 = new JMenuItem(new AbstractAction("Disconnect From Server") {
@Override
public void actionPerformed(ActionEvent e) {
Networking.disconnectFromServer(mainSocket);
}
});
JMenuItem m23 = new JMenuItem(new AbstractAction("Change Nickname") {
@Override
public void actionPerformed(ActionEvent e) {
String Nick = JOptionPane.showInputDialog(null, "New Nickname", nickname);
if (Nick == null) {return;}
else {
nickname = Nick;
}
}
});
mb.add(m2);
m2.add(m21);
m2.add(m22);
m2.add(m23);
// Menu 3
JMenu m3 = new JMenu("Help");
JMenuItem m31 = new JMenuItem(new AbstractAction("About") {
@Override
public void actionPerformed(ActionEvent actionEvent) {
JOptionPane.showMessageDialog(null, "Xenon Created By Xircon\nLicenced under the GNU GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.en.html)",
"About Xenon", JOptionPane.INFORMATION_MESSAGE);
}
});
mb.add(m3);
m3.add(m31);
// Creating the panel at bottom and adding components
JPanel panel = new JPanel(); // the panel is not visible in output
panel.setLayout(new BorderLayout());
JTextField sendField = new JTextField(); // accepts up to 255 characters
sendField.setToolTipText("Enter Message");
sendField.setPreferredSize(new Dimension(200 ,20 ));
JButton send = new JButton("Send");
JButton upload = new JButton("Upload");
panel.add(sendField, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BorderLayout());
buttonPanel.add(send, BorderLayout.WEST);
buttonPanel.add(upload, BorderLayout.EAST);
panel.add(buttonPanel, BorderLayout.EAST);
// Text Sending Handler
send.addActionListener(e -> {
if (mainSocket == null) {JOptionPane.showMessageDialog(null, "Ensure you are Connected to a Server!", "Error", JOptionPane.ERROR_MESSAGE);}
else {
Log.info("Sending " + sendField.getText());
Networking.sendMessage(sendField.getText(), nickname, mainSocket);
SwingUtilities.invokeLater(() -> sendField.setText(""));
}
});
// Text Area at the Center
JTextArea ta = new JTextArea();
//Adding Components to the Frame.
mainFrame.getContentPane().add(BorderLayout.SOUTH, panel);
mainFrame.getContentPane().add(BorderLayout.NORTH, mb);
mainFrame.getContentPane().add(BorderLayout.CENTER, ta);
// Set GUI To Visible
mainFrame.setVisible(true);
}
}

View File

@ -0,0 +1,25 @@
package net.xircon.xenon.client.io;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
public class Log {
public static final Logger logger = LogManager.getLogger();
public static void fatal(String message) {
logger.fatal(message);
JOptionPane.showMessageDialog(null, message, "Fatal Error", JOptionPane.ERROR_MESSAGE);
throw new RuntimeException(message);
}
public static void err(String message) {
logger.error(message);
}
public static void warn(String message) {
logger.warn(message);
}
public static void info(String message) {
logger.info(message);
}
}

View File

@ -0,0 +1,58 @@
package net.xircon.xenon.client.io;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Properties;
public class Util {
public static class Property {
public boolean boolValue = false;
public int intValue = 0;
public String stringValue = "";
}
public static final int PROPERTY_TYPE_BOOLEAN = 1;
public static final int PROPERTY_TYPE_INT = 2;
public static final int PROPERTY_TYPE_STRING = 3;
@Nullable
public static Property GetProperties(String propertiesFile, String propertyName, int propertyType) {
Properties properties = new Properties();
Property property = new Property();
if (false); // TODO: Add Checker for file later, and make fallback to resources folder
else try (InputStream propertiesinput = Util.class.getClassLoader().getResourceAsStream(propertiesFile)) {
properties.load(propertiesinput);
if (properties.containsKey(propertyName) && propertyType == PROPERTY_TYPE_BOOLEAN) { // Booleans
String prop = properties.getProperty(propertyName);
property.boolValue = prop.equals("true");
return property;
}
if (properties.containsKey(propertyName) && propertyType == PROPERTY_TYPE_INT) { // Ints
int prop = Integer.parseInt(properties.getProperty(propertyName));
if (prop >=0 ) property.intValue=prop;
return property;
}
if (properties.containsKey(propertyName) && propertyType == PROPERTY_TYPE_STRING) { // Strings
String prop = properties.getProperty(propertyName);
if (prop != null ) property.stringValue=prop;
return property;
}
} catch (IOException e) {
Log.warn("[Property Loader] Failed to Read server.properties! Falling Back to Defaults! {" + e.getMessage() + "}");
}
Log.warn("[Property Loader] Unable to Find Property/Invalid Type!");
return null;
}
public static String GetExternalIP() throws IOException {
URL whatismyip = new URL("http://checkip.amazonaws.com");
BufferedReader in = new BufferedReader(new InputStreamReader(
whatismyip.openStream()));
// Return IP As String
return in.readLine();
}
}

View File

@ -0,0 +1,61 @@
package net.xircon.xenon.client.networking;
import net.xircon.xenon.client.io.Log;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
public class Networking {
private static final String nameTerminator = "\uffff";
@Nullable
public static Socket connectToServer(String Address, int Port) {
try {
Log.info("Connecting To Server " + Address + ":" + Port);
Socket socket = new Socket();
SocketAddress address = new InetSocketAddress(Address, Port);
socket.connect(address, 5000);
Log.info("Connected to Server!");
socket.setKeepAlive(true);
return socket;
} catch (SocketTimeoutException e) {
Log.warn("Connection timed out!");
return null;
} catch (Exception e) {
Log.err("Failed to Connect to Server!");
return null;
}
}
public static void disconnectFromServer(Socket socket) {
try {
Log.info("Disconnecting from Server");
socket.close();
} catch (IOException e) {
Log.err("Failed to Close Socket!");
} catch (NullPointerException e) {
Log.warn("Ignoring Disconnect Request, Null Socket!");
}
}
public static String sendMessage(String message, String nickname, Socket socket) {
try {
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
output.println(nickname + nameTerminator + message);
String messageFromServer = input.readLine();
Log.info("Server Message: " + messageFromServer);
return messageFromServer;
} catch (IOException e) {
Log.warn("Could not Read/Send Message!");
return null;
}
}
}

View File

@ -0,0 +1,15 @@
package net.xircon.xenon.client.networking.textclient;
import java.net.Socket;
public class TextClient implements Runnable {
public TextClient(Socket serverSocket) {
}
@Override
public void run() {
}
}

View File

@ -0,0 +1,5 @@
# Client Properties
PortText=49180
PortVoIP=49190
Width=960
Height=600

View File

@ -0,0 +1,12 @@
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%t] %msg%n
appender.file.type=File
appender.file.name=LOGFILE
appender.file.fileName=logs/technitium.log
appender.file.layout.type=PatternLayout
appender.file.layout.pattern=[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] [%t] %msg%n
appender.file.filter.threshold.type = ThresholdFilter
appender.file.filter.threshold.level = info
rootLogger=debug, STDOUT, LOGFILE

View File

@ -1,18 +1,31 @@
plugins {
id("java")
application
}
group = "net.xircon"
version = "1.0-SNAPSHOT"
val log4jVersion = "2.25.1"
repositories {
mavenCentral()
}
dependencies {
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation (platform("org.apache.logging.log4j:log4j-bom:$log4jVersion"))
implementation("org.apache.logging.log4j:log4j-core:$log4jVersion")
implementation("org.apache.logging.log4j:log4j-api:$log4jVersion")
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
application {
mainClass = "net.xircon.xenon.server.Xenon"
}
tasks.test {

View File

@ -1,11 +0,0 @@
package net.xircon;
public class Main {
public static void main(String[] args) {
System.out.println()
for (int i = 1; i <= 5; i++) {
System.out.println("i = " + i);
}
}
}

View File

@ -0,0 +1,56 @@
package net.xircon.xenon.server;
import net.xircon.xenon.server.io.*;
import net.xircon.xenon.server.networking.Networking;
import net.xircon.xenon.server.networking.textserver.TextServer;
import java.io.IOException;
import java.net.*;
import java.util.Objects;
//import java.net.
public class Xenon implements Runnable {
private Thread serverThread;
public static void main(String[] args) {new Xenon().start();}
private ServerSocket textServerSocket;
private static final String propertiesFile = "server.properties";
// Sever Properties
public static String ServerName = "Xenon Test Server";
public static int PortVoIP = 49190;
public static int PortText = 49180;
public void start() {
serverThread = new Thread(this, "serverThread");
serverThread.start();
}
@Override
public void run() {
Log.info("Starting Xenon Server!");
// Get Property Values
PortVoIP = Objects.requireNonNull(Util.GetProperties(propertiesFile, "PortVoIP", Util.PROPERTY_TYPE_INT)).intValue;
PortText = Objects.requireNonNull(Util.GetProperties(propertiesFile, "PortText", Util.PROPERTY_TYPE_INT)).intValue;
ServerName = Objects.requireNonNull(Util.GetProperties(propertiesFile, "ServerName", Util.PROPERTY_TYPE_STRING)).stringValue;
Log.info("Server is on Port " + PortVoIP + " For VoIP and Port " + PortText + " For Text.");
// Print External IP and Handle IOException
try {
Log.info("Server's External IP is " + Util.GetExternalIP());
} catch (IOException e) {
Log.err("IOException Whilst Fetching External IP! {" + e + "}");
}
Log.info("Server Name is " + '"' + ServerName + '"');
textServerSocket = Networking.createTextServer(PortText);
TextServer textServer = new TextServer(textServerSocket);
Thread textServerThread = new Thread(textServer);
textServerThread.start();
}
}

View File

@ -0,0 +1,25 @@
package net.xircon.xenon.server.io;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
public class Log {
public static final Logger logger = LogManager.getLogger();
public static void fatal(String message) {
logger.fatal(message);
JOptionPane.showMessageDialog(null, message, "Fatal Error", JOptionPane.ERROR_MESSAGE);
throw new RuntimeException(message);
}
public static void err(String message) {
logger.error(message);
}
public static void warn(String message) {
logger.warn(message);
}
public static void info(String message) {
logger.info(message);
}
}

View File

@ -0,0 +1,57 @@
package net.xircon.xenon.server.io;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.net.*;
import java.io.*;
public class Util {
public static class Property {
public boolean boolValue = false;
public int intValue = 0;
public String stringValue = "";
}
public static final int PROPERTY_TYPE_BOOLEAN = 1;
public static final int PROPERTY_TYPE_INT = 2;
public static final int PROPERTY_TYPE_STRING = 3;
@Nullable
public static Property GetProperties(String propertiesFile, String propertyName, int propertyType) {
Properties properties = new Properties();
Property property = new Property();
if (false); // TODO: Add Checker for file later, and make fallback to resources folder
else try (InputStream propertiesinput = Util.class.getClassLoader().getResourceAsStream(propertiesFile)) {
properties.load(propertiesinput);
if (properties.containsKey(propertyName) && propertyType == PROPERTY_TYPE_BOOLEAN) { // Booleans
String prop = properties.getProperty(propertyName);
property.boolValue = prop.equals("true");
return property;
}
if (properties.containsKey(propertyName) && propertyType == PROPERTY_TYPE_INT) { // Ints
int prop = Integer.parseInt(properties.getProperty(propertyName));
if (prop >=0 ) property.intValue=prop;
return property;
}
if (properties.containsKey(propertyName) && propertyType == PROPERTY_TYPE_STRING) { // Strings
String prop = properties.getProperty(propertyName);
if (prop != null ) property.stringValue=prop;
return property;
}
} catch (IOException e) {
Log.warn("[Property Loader] Failed to Read server.properties! Falling Back to Defaults! {" + e.getMessage() + "}");
}
Log.warn("[Property Loader] Unable to Find Property/Invalid Type!");
return null;
}
public static String GetExternalIP() throws IOException {
URL whatismyip = new URL("http://checkip.amazonaws.com");
BufferedReader in = new BufferedReader(new InputStreamReader(
whatismyip.openStream()));
// Return IP As String
return in.readLine();
}
}

View File

@ -0,0 +1,21 @@
package net.xircon.xenon.server.networking;
import net.xircon.xenon.server.io.Log;
import javax.annotation.Nullable;
import java.io.IOException;
import java.net.ServerSocket;
public class Networking {
@Nullable
public static ServerSocket createTextServer(int Port) {
try {
ServerSocket serverSocket = new ServerSocket(Port);
Log.info("Text Server Waiting For Clients");
return serverSocket;
} catch (IOException e) {
Log.info("Failed to Create Text Server!");
return null;
}
}
}

View File

@ -0,0 +1,32 @@
package net.xircon.xenon.server.networking.textserver;
import net.xircon.xenon.server.io.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientHandler implements Runnable {
private Socket clientSocket;
public ClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
try {
BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter output = output = new PrintWriter(clientSocket.getOutputStream(), true);
String message = input.readLine();
String namedMessage[] = message.split("\uffff", -1);
Log.info("<" + namedMessage[0] + "> " + namedMessage[1]);
output.println("Server Received Message!");
clientSocket.setKeepAlive(true);
} catch (IOException e) {
Log.warn("Could not Read Message From Client");
}
}
}

View File

@ -0,0 +1,31 @@
package net.xircon.xenon.server.networking.textserver;
import net.xircon.xenon.server.io.Log;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TextServer implements Runnable {
private ServerSocket mainServerSocket;
public TextServer(ServerSocket serverSocket) {
this.mainServerSocket = serverSocket;
}
@Override
public void run() {
while (true) {
try {
Socket clientSocket = mainServerSocket.accept();
Log.info("Client Connected: " + clientSocket);
ClientHandler clientHandler = new ClientHandler(clientSocket);
Thread thread = new Thread(clientHandler);
thread.start();
} catch (IOException e) {
Log.err("Text Server Did not correctly Accept a Client!");
}
}
}
}

View File

@ -0,0 +1,4 @@
# Server.properties
PortText=49180
PortVoIP=49190
ServerName=Xircon's Test Server