NIO networking transition is now functional, has some issues with usernames not syncing properly and some latency issues on real world testing

This commit is contained in:
Halbear 2026-08-21 19:25:58 +01:00
parent 3e886e5ba8
commit c9ef43a2a0
23 changed files with 476 additions and 71 deletions

View file

@ -1,5 +1,6 @@
package net.halbear.Executable; package net.halbear.Executable;
import com.oracle.svm.core.annotate.Delete;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Main.GameCore; import net.halbear.Terrain4J.EngineCore.Main.GameCore;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
@ -24,6 +25,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class Server { public class Server {
@Deprecated
@Delete
public static Map<UUID, ServerConnectionThread> ConnectedClients = new ConcurrentHashMap<>(); public static Map<UUID, ServerConnectionThread> ConnectedClients = new ConcurrentHashMap<>();
public static Map<UUID, String> ClientNames = new ConcurrentHashMap<>(); public static Map<UUID, String> ClientNames = new ConcurrentHashMap<>();
@ -57,7 +61,11 @@ public class Server {
} }
public static void ProcessOutgoingPackets(){ public static void ProcessOutgoingPackets(){
Packet packet;
ClientSession.ProcessOutboundPackets();
while((packet = OutGoingPacketQueue.poll()) != null){
ClientSession.HandOutPacket(packet);
}
} }
@Deprecated @Deprecated
@ -94,13 +102,13 @@ public class Server {
public static void StartServer(String[] args) throws IOException { public static void StartServer(String[] args) throws IOException {
ServerSocket server = new ServerSocket(ServerConnectionThread.ServerPort);
Logger.info("\nLaunching New Terrain4J Powered Software!"); Logger.info("\nLaunching New Terrain4J Powered Software!");
RenderThread.Headless = true; RenderThread.Headless = true;
PrimaryRuntime.IsServer = true; PrimaryRuntime.IsServer = true;
if(LegacyNetworking){ if(LegacyNetworking){
ServerSocket server = new ServerSocket(ServerConnectionThread.ServerPort);
Thread acceptThread = new Thread(() -> { Thread acceptThread = new Thread(() -> {
while(true) { while(true) {
try { try {
@ -225,6 +233,7 @@ public class Server {
AtomicReference<String> PlayerConnections = new AtomicReference<>(""); AtomicReference<String> PlayerConnections = new AtomicReference<>("");
if(LegacyNetworking) {
Server.ConnectedClients.forEach((uuid, client) -> { Server.ConnectedClients.forEach((uuid, client) -> {
String Username = "N/A"; String Username = "N/A";
if (Server.ClientNames.containsKey(uuid)) { if (Server.ClientNames.containsKey(uuid)) {
@ -234,6 +243,14 @@ public class Server {
}); });
PrimaryRuntime.printLog("\nServer Runtime Info:\n\tPlayers Connected: " + Server.ConnectedClients.size() + "\n" + PlayerConnections.get() + "\n\n" + Profiling); PrimaryRuntime.printLog("\nServer Runtime Info:\n\tPlayers Connected: " + Server.ConnectedClients.size() + "\n" + PlayerConnections.get() + "\n\n" + Profiling);
} else{
ClientSession.GetClientSessionInstances().forEach((uuid, client) -> {
String Username = client.GetClientName();
PlayerConnections.set(PlayerConnections.get() + "\nConnection Instance UUID: " + uuid.toString() +"\n\tClient UUID: " + client.GetClientUUID() + "\n\tIP address -> " + client.GetIP() + "\n\tUsername -> " + Username);
});
PrimaryRuntime.printLog("\nServer Runtime Info:\n\tPlayers Connected: " + Server.ConnectedClients.size() + "\n" + PlayerConnections.get() + "\n\n" + Profiling);
}
break; break;
case "stop": case "stop":
case "end": case "end":

View file

@ -1,12 +1,10 @@
package net.halbear.Terrain4J.EngineCore.Logic; package net.halbear.Terrain4J.EngineCore.Logic;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.SettingsMenu;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.DeferredSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.DeferredSceneRender;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ForwardSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ForwardSceneRender;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.*; import java.io.*;

View file

@ -5,7 +5,6 @@ import net.halbear.Terrain4J.EngineCore.Logic.Physics.WorldPhysicsManager;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import org.joml.Vector3f; import org.joml.Vector3f;
import java.util.*; import java.util.*;

View file

@ -5,7 +5,6 @@ import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.*;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.Threads.InstancedPhysicsThread; import net.halbear.Terrain4J.EngineCore.Threads.InstancedPhysicsThread;
import org.joml.Vector2f; import org.joml.Vector2f;
import org.joml.Vector3f; import org.joml.Vector3f;

View file

@ -26,7 +26,6 @@ import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor;
import net.halbear.Terrain4J.EngineCore.Profiling.GPUProfiler; import net.halbear.Terrain4J.EngineCore.Profiling.GPUProfiler;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ServerSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ServerSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread; import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.GUI.GuiTexture; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.GUI.GuiTexture;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialData; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.VkModel.MaterialData;
@ -479,14 +478,16 @@ public class GameCore implements GameLogic {
} }
} }
private static final boolean UseLegacyNetworking = false;
@Override @Override
public void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds) { public void UpdateFastThread(EngineInstance engineInstance, long FrameDiffNanoSeconds) {
float PhysicsFramerate = 24f; float PhysicsFramerate = 24f;
float PhysicsFrameTime = 1000f/PhysicsFramerate; float PhysicsFrameTime = 1000f/PhysicsFramerate;
float DeltaTime = (float)FrameDiffNanoSeconds/(PhysicsFrameTime*1000000f); float DeltaTime = (float)FrameDiffNanoSeconds/(PhysicsFrameTime*1000000f);
if(PrimaryRuntime.IsServer){ if(PrimaryRuntime.IsServer){
Server.LegacyCollectIncomingPackets(); if(UseLegacyNetworking) Server.LegacyCollectIncomingPackets(); else Server.CollectIncomingPackets();
Server.LegacyProcessIncomingPackets(); if(UseLegacyNetworking) Server.LegacyProcessIncomingPackets(); else Server.ProcessIncomingPackets();
WorldPhysicsManager.PhysicsTick2(DeltaTime, PhysicsFramerate); WorldPhysicsManager.PhysicsTick2(DeltaTime, PhysicsFramerate);
@ -498,9 +499,10 @@ public class GameCore implements GameLogic {
} }
} }
Server.LegacyProcessOutgoingPackets(); if(UseLegacyNetworking) Server.LegacyProcessOutgoingPackets(); else Server.ProcessOutgoingPackets();
} else { } else {
ClientSideNetworkUtils.DecodePackets(engineInstance, this, engineInstance.scene()); ClientSideNetworkUtils.DecodePackets(engineInstance, this, engineInstance.scene());
if(!UseLegacyNetworking) ClientSideNetworkUtils.SendNIOPacketQueue();
WorldPhysicsManager.PhysicsTick2(DeltaTime, PhysicsFramerate); WorldPhysicsManager.PhysicsTick2(DeltaTime, PhysicsFramerate);
} }
} }

View file

@ -11,10 +11,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene; import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor; import net.halbear.Terrain4J.EngineCore.Profiling.CPUMonitor;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread; import net.halbear.Terrain4J.EngineCore.Threads.*;
import net.halbear.Terrain4J.EngineCore.Threads.FastTickThread;
import net.halbear.Terrain4J.EngineCore.Threads.MainThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.Console; import java.io.Console;
@ -51,7 +48,7 @@ public class PrimaryRuntime {
public final CPUMonitor cpuProfiler; public final CPUMonitor cpuProfiler;
private static GameLogic gameLogic; private static GameLogic gameLogic;
public static ClientConnectionThread ServerCon; public static ClientConnection ServerCon;
public static Thread MultiplayerConnectionThread; public static Thread MultiplayerConnectionThread;
public static GameLogic GetGameCore(){return gameLogic;} public static GameLogic GetGameCore(){return gameLogic;}
@ -84,9 +81,7 @@ public class PrimaryRuntime {
PrimaryThread = new MainThread(engineInstance, appLogic); PrimaryThread = new MainThread(engineInstance, appLogic);
FastThread = new FastTickThread(engineInstance, appLogic); FastThread = new FastTickThread(engineInstance, appLogic);
if(!IsServer) { if(!IsServer) {
ServerCon = new ClientConnectionThread(); CreateNewClientConnection();
MultiplayerConnectionThread = new Thread(ServerCon);
MultiplayerConnectionThread.start();
DrawingThread = new RenderThread(engineInstance, initData, appLogic); DrawingThread = new RenderThread(engineInstance, initData, appLogic);
} }
cpuProfiler = new CPUMonitor(); cpuProfiler = new CPUMonitor();
@ -94,6 +89,15 @@ public class PrimaryRuntime {
Thread.currentThread().setName("Terrain4J Primary Runtime Thread"); Thread.currentThread().setName("Terrain4J Primary Runtime Thread");
} }
private static final boolean UseLegacyNetworking = false;
public static void CreateNewClientConnection(){
Logger.info("Trying to connect");
ServerCon = UseLegacyNetworking ? new ClientConnectionThread() : new NIO_ClientConnectionThread(EngineConfig.getInstance().GetServerIP(), EngineConfig.getInstance().GetServerPort());
MultiplayerConnectionThread = ServerCon.GetNetworkedThread();
ServerCon.start();
}
public static void RejoinMultiplayer(){ public static void RejoinMultiplayer(){
MultiplayerConnectionThread.interrupt(); MultiplayerConnectionThread.interrupt();
try{ try{
@ -101,9 +105,7 @@ public class PrimaryRuntime {
} catch (InterruptedException e){ } catch (InterruptedException e){
Logger.error("Cant rejoin multiplayer thread: [{}]",e.getMessage()); Logger.error("Cant rejoin multiplayer thread: [{}]",e.getMessage());
} }
ServerCon = new ClientConnectionThread(); CreateNewClientConnection();
MultiplayerConnectionThread = new Thread(ServerCon);
MultiplayerConnectionThread.start();
} }
public static void StartPingTimer() { public static void StartPingTimer() {
@ -145,9 +147,7 @@ public class PrimaryRuntime {
return "Connection Error, IsServer Unreachable"; return "Connection Error, IsServer Unreachable";
} }
} else if (!ClientSideNetworkUtils.Connected) { } else if (!ClientSideNetworkUtils.Connected) {
ServerCon = new ClientConnectionThread(); CreateNewClientConnection();
MultiplayerConnectionThread = new Thread(ServerCon);
MultiplayerConnectionThread.start();
} }
return "Connecting..."; return "Connecting...";
@ -201,7 +201,7 @@ public class PrimaryRuntime {
UpdateTickRateThreshold(); UpdateTickRateThreshold();
PrimaryThread.StartThread(); PrimaryThread.StartThread();
DrawingThread.StartThread(); if(!IsServer)DrawingThread.StartThread();
FastThread.StartThread(); FastThread.StartThread();
FrameAccuracy = EngineConfig.getInstance().getAccuracy(); FrameAccuracy = EngineConfig.getInstance().getAccuracy();

View file

@ -14,7 +14,6 @@ import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIOverlay;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.DeferredSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.DeferredSceneRender;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ForwardSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ForwardSceneRender;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.nio.CharBuffer; import java.nio.CharBuffer;

View file

@ -12,7 +12,6 @@ import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIOverlay; import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIOverlay;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene; import net.halbear.Terrain4J.EngineCore.Main.Scene.Scene;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.DeferredSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.DeferredSceneRender;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ForwardSceneRender; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Structure.DisplayToScreen.ForwardSceneRender;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;

View file

@ -5,7 +5,6 @@ import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidBody;
import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidPhysicsController; import net.halbear.Terrain4J.EngineCore.Logic.Physics.RigidBody.RigidPhysicsController;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread; import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.tinylog.Logger; import org.tinylog.Logger;

View file

@ -14,7 +14,6 @@ import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.GameChat;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.Gimbal; import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.Gimbal;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.PerformanceOverlay; import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.PerformanceOverlay;
import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.StartMenu; import net.halbear.Terrain4J.EngineCore.Main.Scene.GUIs.StartMenu;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.Threads.RenderThread; import net.halbear.Terrain4J.EngineCore.Threads.RenderThread;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.DynamicMesh.DynamicTerrainMesh; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.DynamicMesh.DynamicTerrainMesh;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Projection.Project3D; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Projection.Project3D;

View file

@ -6,7 +6,7 @@ import net.halbear.Terrain4J.EngineCore.Logic.Rendering.VulkanContext;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.ActorElement; import net.halbear.Terrain4J.EngineCore.Main.Scene.ActorElement;
import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene; import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.DeferredRendering.MultiRenderTargetAttachments; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.DeferredRendering.MultiRenderTargetAttachments;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.DefaultPipeline; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.DefaultPipeline;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.*; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.*;
@ -417,7 +417,7 @@ public class DeferredSceneRender implements SceneRenderer {//dynamic rendering
if(i < Actors.size()) { if(i < Actors.size()) {
var Actor = Actors.get(i); var Actor = Actors.get(i);
if(Actor != null && Actor.GetPosition() != null) { if(Actor != null && Actor.GetPosition() != null) {
if(Actor instanceof ActorElement &&( !((ActorElement) Actor).RenderOnTopOfPlayer && Objects.equals(((ActorElement) Actor).GetParentID(), ClientConnectionThread.OwnedActorID) || new Vector3f().set(Actor.GetPosition()).sub(instance.scene().GetCamera().GetPosition()).absolute().lengthSquared() < 4f)) continue; if(Actor instanceof ActorElement &&( !((ActorElement) Actor).RenderOnTopOfPlayer && ClientSideNetworkUtils.OwnsActor(((ActorElement) Actor).GetParentID()) || new Vector3f().set(Actor.GetPosition()).sub(instance.scene().GetCamera().GetPosition()).absolute().lengthSquared() < 4f)) continue;
else { else {
VulkanModel model = modelsCache.GetModel(Actor.GetModelID()); VulkanModel model = modelsCache.GetModel(Actor.GetModelID());
List<VulkanMesh> VkMeshList = model.GetVkMeshList(); List<VulkanMesh> VkMeshList = model.GetVkMeshList();

View file

@ -7,7 +7,7 @@ import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.ActorElement; import net.halbear.Terrain4J.EngineCore.Main.Scene.ActorElement;
import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene; import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread; import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.DeferredRendering.MultiRenderTargetAttachments; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.DeferredRendering.MultiRenderTargetAttachments;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.DefaultPipeline; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.DefaultPipeline;
import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.*; import net.halbear.Terrain4J.EngineCore.RenderingAPI.Vulkan.Rendering.Pipeline.Images.*;
@ -408,7 +408,7 @@ public class ForwardSceneRender implements SceneRenderer {//dynamic rendering
if(i < Actors.size()) { if(i < Actors.size()) {
var Actor = Actors.get(i); var Actor = Actors.get(i);
if(Actor != null) { if(Actor != null) {
if(Actor instanceof ActorElement &&( !((ActorElement) Actor).RenderOnTopOfPlayer && Objects.equals(((ActorElement) Actor).GetParentID(), ClientConnectionThread.OwnedActorID) || new Vector3f().set(Actor.GetPosition()).sub(instance.scene().GetCamera().GetPosition()).absolute().lengthSquared() < 4f)) continue; if(Actor instanceof ActorElement &&( !((ActorElement) Actor).RenderOnTopOfPlayer && ClientSideNetworkUtils.OwnsActor(((ActorElement) Actor).GetParentID())|| new Vector3f().set(Actor.GetPosition()).sub(instance.scene().GetCamera().GetPosition()).absolute().lengthSquared() < 4f)) continue;
VulkanModel model = modelsCache.GetModel(Actor.GetModelID()); VulkanModel model = modelsCache.GetModel(Actor.GetModelID());
List<VulkanMesh> VkMeshList = model.GetVkMeshList(); List<VulkanMesh> VkMeshList = model.GetVkMeshList();
int MeshCount = VkMeshList.size(); int MeshCount = VkMeshList.size();

View file

@ -7,6 +7,7 @@ import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor; import net.halbear.Terrain4J.EngineCore.Main.Scene.Actor;
import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene; import net.halbear.Terrain4J.EngineCore.Main.Scene.IScene;
import net.halbear.Terrain4J.EngineCore.Main.Scene.PlayerActor; import net.halbear.Terrain4J.EngineCore.Main.Scene.PlayerActor;
import net.halbear.Terrain4J.EngineCore.Threads.EngineThread;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.IOException; import java.io.IOException;
@ -16,6 +17,7 @@ import java.nio.channels.Selector;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -102,9 +104,14 @@ public class ClientSession implements ServerConnection {
} }
} }
public String GetClientName(){return ClientName;}
public String GetIP(){
return IPAddress;
}
public UUID GetInstanceUUID(){return InstanceUUID;} public UUID GetInstanceUUID(){return InstanceUUID;}
public UUID GetClientUUID(){return InstanceUUID;} public UUID GetClientUUID(){return ClientUUID;}
public ClientSession(SocketChannel channel, IP_MODIFIER modifier, Selector connectionSelector, SelectionKey selectionKey) { public ClientSession(SocketChannel channel, IP_MODIFIER modifier, Selector connectionSelector, SelectionKey selectionKey) {
this.Channel = channel; this.Channel = channel;
@ -112,7 +119,19 @@ public class ClientSession implements ServerConnection {
this.connectionSelector = connectionSelector; this.connectionSelector = connectionSelector;
this.selectionKey = selectionKey; this.selectionKey = selectionKey;
this.InstanceUUID = UUID.randomUUID(); this.InstanceUUID = UUID.randomUUID();
IPAddress = "COULD_NOT_GET";
try {
IPAddress = NetworkingUtil.GetIPAddress(channel);
} catch (IOException e) {
System.out.println("Failed to get IP address for client session with UUID " + InstanceUUID);
e.printStackTrace();
Logger.error("Failed to get IP address for client session with UUID " + InstanceUUID);
}
ClientSessionInstances.put(this.InstanceUUID,this); ClientSessionInstances.put(this.InstanceUUID,this);
PendingClients.add(this);
System.out.println("New Client Session Created with UUID " + InstanceUUID);
AddOutgoingPacket(new Packet(Packet.QUERY_MAC_ADDRESS).SetTargetType((byte) 0).SetData(new byte[]{0,0,0,0}).SetID("_"));
AddOutgoingPacket(new Packet(Packet.QUERY_CLIENT_UUID).SetTargetType((byte) 0).SetData(new byte[]{0,0,0,0}).SetID("_"));
} }
public boolean IsValidated(){ public boolean IsValidated(){
@ -123,7 +142,7 @@ public class ClientSession implements ServerConnection {
if(PendingClients.isEmpty()) return; if(PendingClients.isEmpty()) return;
for(int i = 0; i < PendingClients.size(); i++){ for(int i = 0; i < PendingClients.size(); i++){
ClientSession Pending = PendingClients.peek(); ClientSession Pending = PendingClients.peek();
if(Pending.IsValidated()){ if(Pending.IsValidated() && Pending.CheckedUUID_Cache){
ClientSession session = PendingClients.poll(); ClientSession session = PendingClients.poll();
if(session == null) continue; if(session == null) continue;
session.InitialiseJoinedClient(); session.InitialiseJoinedClient();
@ -151,7 +170,7 @@ public class ClientSession implements ServerConnection {
} }
protected void InitialiseJoinedClient(){ protected void InitialiseJoinedClient(){
Packet PlayerCountPacket = CreatePlayerCountPacket(Server.ConnectedClients.size()); Packet PlayerCountPacket = CreatePlayerCountPacket(ClientSessions.size());
Server.OutGoingPacketQueue.add(PlayerCountPacket); Server.OutGoingPacketQueue.add(PlayerCountPacket);
EngineInstance engineInstance = PrimaryRuntime.GetEngineInstance(); EngineInstance engineInstance = PrimaryRuntime.GetEngineInstance();
IScene scene = engineInstance.scene(); IScene scene = engineInstance.scene();
@ -178,8 +197,7 @@ public class ClientSession implements ServerConnection {
BroadcastCreateActorPacket(player); BroadcastCreateActorPacket(player);
SendPossessActorPacket(player.GetID()); SendPossessActorPacket(player.GetID());
Packet UUIDUpdatePacket = CreatePlayerUUIDPacket(ClientUUID,this); Server.OutGoingPacketQueue.add(ServerSideNetworkUtils.CreateUsernameUpdatePacket(ClientUUID, ClientName));
Server.OutGoingPacketQueue.add(UUIDUpdatePacket);
Logger.debug("Queued possess packet for joining player's actor: " + player.GetID()); Logger.debug("Queued possess packet for joining player's actor: " + player.GetID());
} }
@ -194,11 +212,10 @@ public class ClientSession implements ServerConnection {
String Data = Packet.DecodeAsString(Inbound.Data); String Data = Packet.DecodeAsString(Inbound.Data);
if(ClientMacAddressCache.containsKey(Data)){ if(ClientMacAddressCache.containsKey(Data)){
UUID clientUUID = ClientMacAddressCache.get(Data); UUID clientUUID = ClientMacAddressCache.get(Data);
ClientSessions.put(clientUUID,PendingClients.poll()); ClientUUID = clientUUID;
OutboundPacketQueue.add(new Packet(Packet.PACKET_ASSIGN_UUID).SetData(Packet.EncodeString(clientUUID.toString())).SetID(clientUUID.toString())); OutboundPacketQueue.add(new Packet(Packet.PACKET_ASSIGN_UUID).SetData(Packet.EncodeString(clientUUID.toString())).SetID(clientUUID.toString()));
CheckedUUID_Cache = true; ClientSessions.remove(ClientUUID);
Validated = true; OutboundPacketQueue.add(new Packet(Packet.QUERY_CLIENT_UUID).SetData(new byte[]{0,0,0,0}).SetID("_"));
return;
} }
CheckedUUID_Cache = true; CheckedUUID_Cache = true;
} }
@ -206,11 +223,18 @@ public class ClientSession implements ServerConnection {
String Data = Packet.DecodeAsString(Inbound.Data); String Data = Packet.DecodeAsString(Inbound.Data);
ClientUUID = null; ClientUUID = null;
try{ try{
UUID newUUID = UUID.fromString(Data); String[] Halves = Data.split(":");
if(ClientSessions.containsKey(newUUID) || ClientMacAddressCache.containsKey(Data)){ UUID newUUID = null;
if(!Objects.equals(Halves[0], "NULL")) {
newUUID = UUID.fromString(Halves[0]);
}
if(Halves.length > 1) ClientName = Halves[1];
else ClientName = newUUID == null ? "NOT_INITIALISED" : "NewPlayer" + newUUID.toString().substring(0,5);
if((newUUID == null || ClientSessions.containsKey(newUUID))){
Validated = false; Validated = false;
UUID AssignUUID = UUID.randomUUID(); UUID AssignUUID = UUID.randomUUID();
OutboundPacketQueue.add(new Packet(Packet.PACKET_ASSIGN_UUID).SetData(Packet.EncodeString(AssignUUID.toString())).SetID(AssignUUID.toString())); OutboundPacketQueue.add(CreatePlayerUUIDPacket(AssignUUID, this));
OutboundPacketQueue.add(new Packet(Packet.QUERY_CLIENT_UUID).SetData(new byte[]{0,0,0,0}).SetID("_"));
} else{ } else{
Validated = CheckedUUID_Cache; Validated = CheckedUUID_Cache;
ClientUUID = newUUID; ClientUUID = newUUID;
@ -238,7 +262,7 @@ public class ClientSession implements ServerConnection {
public void ProcessIncomingPacket() { public void ProcessIncomingPacket() {
InBound.flip(); InBound.flip();
while (true) { while (EngineThread.running) {
if (InBound.remaining() < 6) { if (InBound.remaining() < 6) {
break; break;
} }
@ -275,6 +299,8 @@ public class ClientSession implements ServerConnection {
InBound.get(idData); InBound.get(idData);
String id = new String(idData, StandardCharsets.UTF_8); String id = new String(idData, StandardCharsets.UTF_8);
newIncomingPacket.SetID(id); newIncomingPacket.SetID(id);
newIncomingPacket.SessionUUID = InstanceUUID;
newIncomingPacket.ClientUUID = ClientUUID;
InboundPacketQueue.add(newIncomingPacket); InboundPacketQueue.add(newIncomingPacket);
} }

View file

@ -1,5 +1,8 @@
package net.halbear.Terrain4J.EngineCore.ServerNetworking; package net.halbear.Terrain4J.EngineCore.ServerNetworking;
import com.google.gson.Gson;
import com.google.gson.Strictness;
import com.google.gson.stream.JsonReader;
import net.halbear.Executable.Server; import net.halbear.Executable.Server;
import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig; import net.halbear.Terrain4J.EngineCore.Logic.EngineConfig;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance; import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
@ -11,14 +14,17 @@ import net.halbear.Terrain4J.EngineCore.Main.GameCore;
import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime; import net.halbear.Terrain4J.EngineCore.Main.PrimaryRuntime;
import net.halbear.Terrain4J.EngineCore.Main.Scene.*; import net.halbear.Terrain4J.EngineCore.Main.Scene.*;
import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread; import net.halbear.Terrain4J.EngineCore.Threads.ClientConnectionThread;
import net.halbear.Terrain4J.EngineCore.Threads.NIO_ClientConnectionThread;
import org.joml.Quaternionf; import org.joml.Quaternionf;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.tinylog.Logger; import org.tinylog.Logger;
import java.io.DataInputStream; import java.io.*;
import java.io.DataOutputStream; import java.net.NetworkInterface;
import java.io.IOException; import java.net.SocketException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -30,17 +36,86 @@ public class ClientSideNetworkUtils {
public static final Queue<Packet> OutGoingPacketQueue = new ConcurrentLinkedQueue<>(); public static final Queue<Packet> OutGoingPacketQueue = new ConcurrentLinkedQueue<>();
public static final Queue<Packet> IncomingPacketQueue = new ConcurrentLinkedQueue<>(); public static final Queue<Packet> IncomingPacketQueue = new ConcurrentLinkedQueue<>();
private static final Map<String, UUID> ServerClientUUIDs = new ConcurrentHashMap<>();
private static final String CachePath = "resources/ProgramResources/ServerCache/UUIDs.json";
private static final Queue<UUIDCache> UUIDCacheQueue = new ConcurrentLinkedQueue<>();
public static void CacheUUID(UUID uuid, String ServerIP){
UUIDCacheQueue.add(new UUIDCache(ServerIP, uuid.toString()));
ServerClientUUIDs.put(ServerIP, uuid);
}
public static void SendNIOPacketQueue(){
while(!OutGoingPacketQueue.isEmpty()){
Packet packet = OutGoingPacketQueue.poll();
NIO_ClientConnectionThread Connection = NIO_ClientConnectionThread.GetInstance();
if(packet != null) {
Connection.SendPacket(packet);
}
}
}
static{
if(Files.exists(Path.of(CachePath))) {
String FileContents = "NOT_READ";
try{
FileContents = Files.readString(Path.of(CachePath), StandardCharsets.UTF_8);
} catch (IOException e){
Logger.error("Error reading UUID cache file: [{}]", e.getMessage());
}
if(!FileContents.equals("NOT_READ")) {
Gson parser = new Gson();
try (JsonReader reader = new JsonReader(new StringReader(FileContents))) {
reader.setStrictness(Strictness.LENIENT);
while (reader.hasNext()) {
UUIDCache user = parser.fromJson(reader, UUIDCache.class);
UUID uuid = null;
try {
uuid = UUID.fromString(user.getClientUUID());
} catch (IllegalArgumentException e) {
Logger.error("Invalid UUID in cache file: [{}]", user.getClientUUID());
}
if(uuid != null) {
ServerClientUUIDs.put(user.getServerIP(), uuid);
UUIDCacheQueue.add(user);
}
}
} catch (Exception e) {
Logger.error("Error parsing UUID cache file: [{}]", e.getMessage());
}
}
} else{
try {
Files.createFile(Path.of(CachePath));
} catch (IOException e) {
Logger.error("Could not create UUID cache file: [{}]", e.getMessage());
}
}
}
public static void SaveUUIDCache(){
try (FileWriter writer = new FileWriter(CachePath)) {
Gson gson = new Gson();
gson.toJson(UUIDCacheQueue, writer);
} catch (IOException e) {
Logger.error("Error saving UUID cache file: [{}]", e.getMessage());
}
}
public static boolean Connected = false; public static boolean Connected = false;
public static boolean pinging = false; public static boolean pinging = false;
public static String ServerIP = "localhost"; public static String ServerIP = "localhost";
public static String ServerMessage = "Offline"; public static String ServerMessage = "Offline";
public static String clientUUID = null; public static String clientUUID = null;
public static UUID ClientUUID = null;
private static final Map<String, Actor> NetworkOwnedActors = new ConcurrentHashMap<>(); private static final Map<String, Actor> NetworkOwnedActors = new ConcurrentHashMap<>();
private static String PlayerControlledActorID = null; private static String PlayerControlledActorID = null;
@Deprecated @Deprecated
public static String OwnedActorID = "NULL"; public static String OwnedActorID = "NULL";
public static String PendingPossessActorID = "NULL"; public static String PendingPossessActorID = "NULL";
public static int PlayersOnline = 0; public static int PlayersOnline = 0;
public static String MacAddress = null;
public static boolean IsPlayerControlledActor(String actorID){ public static boolean IsPlayerControlledActor(String actorID){
return PlayerControlledActorID.equals(actorID); return PlayerControlledActorID.equals(actorID);
@ -106,6 +181,43 @@ public class ClientSideNetworkUtils {
return true; return true;
} }
static{
FetchMacAddress();
}
private static void FetchMacAddress(){
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface netInterface : Collections.list(interfaces)) {
if (netInterface.isLoopback() || !netInterface.isUp()) {
continue;
}
byte[] MacAddress = netInterface.getHardwareAddress();
if (MacAddress != null) {
ClientSideNetworkUtils.MacAddress = FormatMacAddress(MacAddress);
Logger.info("Interface: %-10s | MAC: %s%n",
netInterface.getDisplayName(),
FormatMacAddress(MacAddress));
}
}
} catch (SocketException e) {
Logger.error("Error retrieving network interfaces: " + e.getMessage());
}
}
private static String FormatMacAddress(byte[] mac) {
StringBuilder StringBuilder = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
StringBuilder.append(String.format("%02X", mac[i]));
if (i < mac.length - 1) {
StringBuilder.append("-");
}
}
return StringBuilder.toString();
}
public static final Map<Byte, Boolean> BypassNullTargets = new HashMap<>(); public static final Map<Byte, Boolean> BypassNullTargets = new HashMap<>();
static{ static{
@ -118,6 +230,8 @@ public class ClientSideNetworkUtils {
BypassNullTargets.put(Packet.PACKET_FIRE_RESULT, true); BypassNullTargets.put(Packet.PACKET_FIRE_RESULT, true);
BypassNullTargets.put(Packet.GAME_CHAT_DATA_PACKET, true); BypassNullTargets.put(Packet.GAME_CHAT_DATA_PACKET, true);
BypassNullTargets.put(Packet.PACKET_UPDATE_PLAYER_DISPLAY_NAME, true); BypassNullTargets.put(Packet.PACKET_UPDATE_PLAYER_DISPLAY_NAME, true);
BypassNullTargets.put(Packet.QUERY_MAC_ADDRESS, true);
BypassNullTargets.put(Packet.QUERY_CLIENT_UUID, true);
} }
public static void DecodePackets(EngineInstance engineInstance, GameCore gameCore, IScene scene){ public static void DecodePackets(EngineInstance engineInstance, GameCore gameCore, IScene scene){
@ -285,6 +399,16 @@ public class ClientSideNetworkUtils {
if(PlayerNameCached) PlayerName = Server.ClientNames.get(chat.clientFrom()); if(PlayerNameCached) PlayerName = Server.ClientNames.get(chat.clientFrom());
GameChats.put(chat,PlayerName); GameChats.put(chat,PlayerName);
break; break;
case Packet.QUERY_CLIENT_UUID:
Logger.info("Querying UUID");
CreateUUIDQueryPacket();
break;
case Packet.QUERY_MAC_ADDRESS:
Logger.info("Querying MAC Address");
if(MacAddress == null) FetchMacAddress();
if(MacAddress == null) break;
CreateMacAddressPacket(MacAddress);
break;
case Packet.PACKET_DESTROY_ACTOR: case Packet.PACKET_DESTROY_ACTOR:
GameChats.put(new GameChat(true, UUID.randomUUID(),"Destroying Actor " + packet.TargetID, Date.from(Instant.now())),"Server"); GameChats.put(new GameChat(true, UUID.randomUUID(),"Destroying Actor " + packet.TargetID, Date.from(Instant.now())),"Server");
Logger.debug("Destroying Actor [{}]", packet.TargetID); Logger.debug("Destroying Actor [{}]", packet.TargetID);
@ -311,6 +435,14 @@ public class ClientSideNetworkUtils {
case Packet.PACKET_ASSIGN_UUID: case Packet.PACKET_ASSIGN_UUID:
Logger.debug("decoding UUID packet"); Logger.debug("decoding UUID packet");
clientUUID = Packet.DecodeAsString(packet.Data); clientUUID = Packet.DecodeAsString(packet.Data);
UUID uuidOfClient = null;
try{
uuidOfClient = UUID.fromString(clientUUID);
} catch (Exception e){}
if(uuidOfClient != null) {
CacheUUID(uuidOfClient, ServerIP);
ClientUUID = uuidOfClient;
}
break; break;
case Packet.PACKET_FIRE_RESULT: case Packet.PACKET_FIRE_RESULT:
//Logger.debug("Fire Packet Recieved" );// //Logger.debug("Fire Packet Recieved" );//
@ -332,6 +464,16 @@ public class ClientSideNetworkUtils {
public static int ServerPort = 25565; public static int ServerPort = 25565;
public static void SetServerIP(String ServerIP, Thread serverThread){
if(Connected){
Connected = false;
serverThread.interrupt();
ClientSideNetworkUtils.ServerIP = ServerIP;
PrimaryRuntime.CreateNewClientConnection();
}
}
@Deprecated @Deprecated
public static void LegacySetServerIP(String ServerIP, Thread serverThread){ public static void LegacySetServerIP(String ServerIP, Thread serverThread){
if(Connected){ if(Connected){
@ -352,6 +494,22 @@ public class ClientSideNetworkUtils {
OutGoingPacketQueue.add(packet); OutGoingPacketQueue.add(packet);
} }
public static void CreateUUIDQueryPacket(){
Packet packet = new Packet(Packet.QUERY_CLIENT_UUID);
packet.SetTargetType((byte) 0);
packet.SetData(Packet.EncodeString( (ClientUUID == null ? "NULL" : ClientUUID.toString()) + ":" + EngineConfig.getInstance().GetUserDisplayName()));
packet.SetID("NULL");
OutGoingPacketQueue.add(packet);
}
public static void CreateMacAddressPacket(String Address){
Packet packet = new Packet(Packet.QUERY_MAC_ADDRESS);
packet.SetTargetType((byte) 0);
packet.SetData(Packet.EncodeString(Address));
packet.SetID("NULL");
OutGoingPacketQueue.add(packet);
}
public static void CreateGameChatPacket(String chatMessage){ public static void CreateGameChatPacket(String chatMessage){
Packet packet = new Packet(Packet.GAME_CHAT_DATA_PACKET); Packet packet = new Packet(Packet.GAME_CHAT_DATA_PACKET);
packet.SetTargetType((byte) 0); packet.SetTargetType((byte) 0);
@ -439,6 +597,16 @@ public class ClientSideNetworkUtils {
private static final boolean UseLegacyConnection = false; private static final boolean UseLegacyConnection = false;
public static synchronized void Reconnect() { public static synchronized void Reconnect() {
Logger.debug("Reconnecting");
Reconnect = true;
NIO_ClientConnectionThread.GetInstance().Disconnect();
Connected = false;
PrimaryRuntime.RejoinMultiplayer();
PrimaryRuntime.GetEngineInstance().scene().Reset();
}
@Deprecated
public static synchronized void LegacyReconnect() {
Logger.debug("Reconnecting"); Logger.debug("Reconnecting");
Reconnect = true; Reconnect = true;
if(UseLegacyConnection) { if(UseLegacyConnection) {

View file

@ -82,7 +82,7 @@ public class ServerSideNetworkUtils {
//to be implemented later //to be implemented later
break; break;
case Packet.PACKET_UPDATE_MODEL_ID: case Packet.PACKET_UPDATE_MODEL_ID:
if(!packet.TargetID.equals(ConnectedClients.get(packet.SessionUUID).OwnedActorID)) { if(!(sender.OwnsActor(packet.TargetID))) {
return; return;
} }
Packet ReboundPacket = CreateUpdateModelIDPacket(packet.TargetID, Packet.DecodeAsString(packet.Data)); Packet ReboundPacket = CreateUpdateModelIDPacket(packet.TargetID, Packet.DecodeAsString(packet.Data));

View file

@ -0,0 +1,23 @@
package net.halbear.Terrain4J.EngineCore.ServerNetworking;
import com.google.gson.annotations.SerializedName;
public class UUIDCache {
@SerializedName("server_ip")
private String ServerIP;
@SerializedName("client_uuid")
private String ClientUUID;
public UUIDCache(String serverIP, String clientUUID) {
this.ServerIP = serverIP;
this.ClientUUID = clientUUID;
}
public String getServerIP() {
return ServerIP;
}
public String getClientUUID() {
return ClientUUID;
}
}

View file

@ -0,0 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Threads;
public interface ClientConnection extends Runnable{
public void start();
public Thread GetNetworkedThread();
}

View file

@ -29,22 +29,36 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import static net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils.*; import static net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils.*;
@Deprecated @Deprecated
public class ClientConnectionThread implements Runnable{ @Delete
public class ClientConnectionThread implements ClientConnection{
public static Socket SocketToServer; public static Socket SocketToServer;
public static DataOutputStream outToServer; public static DataOutputStream outToServer;
public static DataInputStream in; public static DataInputStream in;
private final Thread NetworkThread;
public ClientConnectionThread(){ public ClientConnectionThread(){
Connected = true; Connected = true;
NetworkThread = new Thread(this);
NetworkThread.setName("Terrain4J Legacy Client Network Thread");
NetworkThread.setDaemon(true);
try { try {
SocketToServer = new Socket(ServerIP, ServerPort); SocketToServer = new Socket(ServerIP, ServerPort);
SocketToServer.setSoTimeout(5); SocketToServer.setSoTimeout(5);
} catch (IOException e) { } catch (IOException e) {
Connected = false; Connected = false;
//e.printStackTrace(); //e.printStackTrace();
} }
} }
public void start() {
NetworkThread.start();
}
@Override
public Thread GetNetworkedThread() {
return NetworkThread;
}
@Override @Override
public void run() { public void run() {

View file

@ -1,13 +1,28 @@
package net.halbear.Terrain4J.EngineCore.Threads; package net.halbear.Terrain4J.EngineCore.Threads;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils;
import net.halbear.Terrain4J.EngineCore.ServerNetworking.Packet;
import org.tinylog.Logger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.Selector; import java.nio.channels.Selector;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
public class NIO_ClientConnectionThread implements Runnable { import static net.halbear.Terrain4J.EngineCore.ServerNetworking.ClientSideNetworkUtils.SaveUUIDCache;
public class NIO_ClientConnectionThread implements ClientConnection {
public static NIO_ClientConnectionThread Instance;
public static NIO_ClientConnectionThread GetInstance(){ return Instance;}
private final ByteBuffer InBound = ByteBuffer.allocateDirect(4096); private final ByteBuffer InBound = ByteBuffer.allocateDirect(4096);
private final Queue<ByteBuffer> OutBoundQueue = new ConcurrentLinkedQueue<>(); private final Queue<ByteBuffer> OutBoundQueue = new ConcurrentLinkedQueue<>();
@ -16,14 +31,14 @@ public class NIO_ClientConnectionThread implements Runnable {
private final Thread NetworkThread; private final Thread NetworkThread;
private Selector Selector; private Selector NetworkSelector;
private SocketChannel ClientChannel; private SocketChannel ClientChannel;
private SelectionKey ChannelKey; private SelectionKey ChannelKey;
public NIO_ClientConnectionThread(String ServerAddress, int ServerPort) { public NIO_ClientConnectionThread(String ServerAddress, int ServerPort) {
this.ServerIP = ServerAddress; this.ServerIP = ServerAddress;
this.ServerPort = ServerPort; this.ServerPort = ServerPort;
NIO_ClientConnectionThread.Instance = this;
NetworkThread = new Thread(this); NetworkThread = new Thread(this);
NetworkThread.setName("Terrain4J Client Network Thread"); NetworkThread.setName("Terrain4J Client Network Thread");
NetworkThread.setDaemon(true); NetworkThread.setDaemon(true);
@ -34,7 +49,142 @@ public class NIO_ClientConnectionThread implements Runnable {
} }
@Override @Override
public void run() { public Thread GetNetworkedThread() {
return NetworkThread;
}
private void ConnectToServer(SelectionKey key) throws IOException {
if (ClientChannel.isConnectionPending()) {
if (ClientChannel.finishConnect()) {
ClientSideNetworkUtils.Connected = true;
this.ChannelKey = key;
key.interestOps(SelectionKey.OP_READ);
} else {
Disconnect();
}
}
}
@Override
public void run() {
try {
NetworkSelector = Selector.open();
ClientChannel = SocketChannel.open();
ClientChannel.configureBlocking(false);
ClientChannel.connect(new InetSocketAddress(ServerIP, ServerPort));
ClientChannel.register(NetworkSelector, SelectionKey.OP_CONNECT);
while (ClientChannel.isOpen() && EngineThread.running) {
NetworkSelector.select();
Iterator<SelectionKey> it = NetworkSelector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (!key.isValid()) continue;
if (key.isConnectable()) {
ConnectToServer(key);
}
if (key.isReadable()) {
HandleRead();
}
if (key.isWritable()) {
HandleWrite();
}
}
}
} catch (IOException e) {
System.err.println("Network client error: " + e.getMessage());
Disconnect();
}
}
private void HandleWrite() throws IOException {
while (!OutBoundQueue.isEmpty()) {
ByteBuffer buffer = OutBoundQueue.peek();
ClientChannel.write(buffer);
if (buffer.hasRemaining()) {
return;
}
OutBoundQueue.poll();
}
ChannelKey.interestOps(ChannelKey.interestOps() & ~SelectionKey.OP_WRITE);
}
public void SendPacket(Packet outPacket) {
byte[] dataToSend = Packet.GetOutgoingData(outPacket);
OutBoundQueue.add(ByteBuffer.wrap(dataToSend));
if (ChannelKey != null && NetworkSelector != null) {
ChannelKey.interestOps(ChannelKey.interestOps() | SelectionKey.OP_WRITE);
NetworkSelector.wakeup();
}
}
public void Disconnect() {
SaveUUIDCache();
try {
if (ClientChannel != null) ClientChannel.close();
if (NetworkSelector != null) NetworkSelector.close();
Logger.info("Client network loop cleanly shut down.");
} catch (IOException ignored) {}
}
private void HandleRead() throws IOException {
int bytesRead = ClientChannel.read(InBound);
if (bytesRead == -1) {
System.out.println("Disconnected by remote server.");
Disconnect();
return;
}
InBound.flip();
while (EngineThread.running) {
if (InBound.remaining() < 6) break;
InBound.mark();
byte packetTypeByte = InBound.get();
byte targetType = InBound.get();
int dataLength = InBound.getInt();
if (InBound.remaining() < dataLength + 4) {
InBound.reset();
break;
}
int currentPos = InBound.position();
InBound.position(currentPos + dataLength);
int idLength = InBound.getInt();
InBound.position(currentPos);
if (InBound.remaining() < dataLength + 4 + idLength) {
InBound.reset();
break;
}
Packet serverPacket = new Packet(packetTypeByte);
serverPacket.SetTargetType(targetType);
byte[] incomingData = new byte[dataLength];
InBound.get(incomingData);
serverPacket.Data = incomingData;
InBound.getInt();
byte[] idData = new byte[idLength];
InBound.get(idData);
serverPacket.SetID(new String(idData, StandardCharsets.UTF_8));
ClientSideNetworkUtils.IncomingPacketQueue.add(serverPacket);
}
InBound.compact();
} }
} }

View file

@ -1,5 +1,6 @@
package net.halbear.Terrain4J.EngineCore.Threads; package net.halbear.Terrain4J.EngineCore.Threads;
import com.oracle.svm.core.annotate.Delete;
import net.halbear.Executable.Server; import net.halbear.Executable.Server;
import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance; import net.halbear.Terrain4J.EngineCore.Logic.EngineInstance;
import net.halbear.Terrain4J.EngineCore.Main.GameCore; import net.halbear.Terrain4J.EngineCore.Main.GameCore;
@ -22,6 +23,7 @@ import static net.halbear.Executable.Server.LegacyRemoveClient;
import static net.halbear.Terrain4J.EngineCore.ServerNetworking.ServerSideNetworkUtils.*; import static net.halbear.Terrain4J.EngineCore.ServerNetworking.ServerSideNetworkUtils.*;
@Deprecated @Deprecated
@Delete
public class ServerConnectionThread implements Runnable, ServerConnection { public class ServerConnectionThread implements Runnable, ServerConnection {
public String OwnedActorID; public String OwnedActorID;
private final Socket clientSocket; private final Socket clientSocket;

View file

@ -37,9 +37,12 @@ public class ServerMultiClientConnectionThread implements Runnable {
private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>(); private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();
private Thread ConnectionThread; private Thread ConnectionThread;
static int ThreadNumbers =0;
public ServerMultiClientConnectionThread() throws IOException { public ServerMultiClientConnectionThread() throws IOException {
this.selector = Selector.open(); this.selector = Selector.open();
ConnectionThread = new Thread(this); ConnectionThread = new Thread(this);
ConnectionThread.setName("Server NIO Connection Manager [" + ThreadNumbers++ + "]");
ConnectionThread.setDaemon(true); ConnectionThread.setDaemon(true);
} }
@ -56,11 +59,13 @@ public class ServerMultiClientConnectionThread implements Runnable {
} }
IP_MODIFIER modifier = IP_STATUS_REGISTRY.getOrDefault(IPAddress, IP_MODIFIER.User); IP_MODIFIER modifier = IP_STATUS_REGISTRY.getOrDefault(IPAddress, IP_MODIFIER.User);
if (IP_STATUS_ALLOW_CONNECTION.containsKey(modifier) && IP_STATUS_ALLOW_CONNECTION.get(modifier)) { if (IP_STATUS_ALLOW_CONNECTION.containsKey(modifier) && IP_STATUS_ALLOW_CONNECTION.get(modifier)) {
String IP = IPAddress;
taskQueue.add(() -> { taskQueue.add(() -> {
try { try {
channel.configureBlocking(false); channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ); SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
key.attach(new ClientSession(channel, modifier, selector, key)); key.attach(new ClientSession(channel, modifier, selector, key));
System.out.println("Accepted new connection from " + IP);
} catch (IOException e) { } catch (IOException e) {
CloseChannel(channel); CloseChannel(channel);
} }

View file

@ -47,7 +47,7 @@ public class ServerSelectorMasterThread implements Runnable {
int index = Math.abs(ManagerIterator.getAndIncrement() % CONNECTION_MANAGER_COUNT); int index = Math.abs(ManagerIterator.getAndIncrement() % CONNECTION_MANAGER_COUNT);
ServerMultiClientConnectionThread targetWorker = ConnectionManagers[index]; ServerMultiClientConnectionThread targetWorker = ConnectionManagers[index];
System.out.println("Delegating new Connection [ " + clientChannel.socket().getInetAddress().getHostAddress() + "] to Connection Manager " + index);
targetWorker.acceptNewChannel(clientChannel); targetWorker.acceptNewChannel(clientChannel);
} }
} }
@ -75,7 +75,7 @@ public class ServerSelectorMasterThread implements Runnable {
serverChannel.bind(new InetSocketAddress(PORT)); serverChannel.bind(new InetSocketAddress(PORT));
serverChannel.configureBlocking(false); serverChannel.configureBlocking(false);
serverChannel.register(MasterSelector, SelectionKey.OP_ACCEPT); serverChannel.register(MasterSelector, SelectionKey.OP_ACCEPT);
System.out.println("Boss started on port " + PORT + ". 7 Workers ready to scale to 128+ connections."); System.out.println("Server Master Thread started on port " + PORT + " with " + CONNECTION_MANAGER_COUNT + " connection managers.");
ManagerIterator = new AtomicInteger(0); ManagerIterator = new AtomicInteger(0);
acceptThread.start(); acceptThread.start();

View file

@ -1,5 +1,5 @@
#created new properties file #created new properties file
#Tue Aug 18 20:22:42 BST 2026 #Fri Aug 21 19:04:58 BST 2026
AlphaToCoverage=false AlphaToCoverage=false
Debug_Shaders=false Debug_Shaders=false
DefaultTexturePath=resources/EngineResources/Texture/NoTexture.png DefaultTexturePath=resources/EngineResources/Texture/NoTexture.png
@ -28,11 +28,11 @@ frame_accuracy=0
main_thread_tickrate=120 main_thread_tickrate=120
master_tick_rate=240 master_tick_rate=240
server=false server=false
server_port=25565 server_ip=gameserver.halbear.net
server_ip=localhost server_port=25566
throttle_accuracy=4000000 throttle_accuracy=4000000
throttle_on_unfocus=false throttle_on_unfocus=false
user_display_name=halbear user_display_name=The sigmarest of sigmas
vkValidated=true vkValidated=true
vsync=false vsync=false
z_far_plane=0.1f z_far_plane=0.1f