diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index c1fb79b8..6311d590 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1797,6 +1797,7 @@ Utilities to free all of a type of resource Slowdown entity tests to prevent VSCode from exploding when running tests Fix static state caching between tests in visual shader construction Safe executor service creation in non final static contexts +Fix reloading client world repeatedly diff --git a/src/main/java/electrosphere/engine/Globals.java b/src/main/java/electrosphere/engine/Globals.java index 3cbf07b7..11c9f4d3 100644 --- a/src/main/java/electrosphere/engine/Globals.java +++ b/src/main/java/electrosphere/engine/Globals.java @@ -729,6 +729,7 @@ public class Globals { Globals.realmManager.reset(); } Globals.dbController.disconnect(); + Globals.serviceManager.unloadScene(); } /** diff --git a/src/main/java/electrosphere/engine/loadingthreads/MainMenuLoading.java b/src/main/java/electrosphere/engine/loadingthreads/MainMenuLoading.java index eb1aea35..45635c16 100644 --- a/src/main/java/electrosphere/engine/loadingthreads/MainMenuLoading.java +++ b/src/main/java/electrosphere/engine/loadingthreads/MainMenuLoading.java @@ -35,6 +35,8 @@ public class MainMenuLoading { Globals.unloadScene(); Globals.threadManager.interruptLabel(ThreadLabel.NETWORKING_CLIENT); Globals.threadManager.interruptLabel(ThreadLabel.NETWORKING_SERVER); + Globals.threadManager.awaitThreadClose(ThreadLabel.NETWORKING_CLIENT); + Globals.threadManager.awaitThreadClose(ThreadLabel.NETWORKING_SERVER); // //reveal in game main menu diff --git a/src/main/java/electrosphere/engine/service/Service.java b/src/main/java/electrosphere/engine/service/Service.java index 2c0c00db..e9992fb0 100644 --- a/src/main/java/electrosphere/engine/service/Service.java +++ b/src/main/java/electrosphere/engine/service/Service.java @@ -15,6 +15,11 @@ public interface Service { */ public void destroy(); + /** + * Resets the service on closing the client and/or server + */ + public void unloadScene(); + /** * Returns the name of the service * @return diff --git a/src/main/java/electrosphere/engine/service/ServiceManager.java b/src/main/java/electrosphere/engine/service/ServiceManager.java index bf57e6c2..182b096c 100644 --- a/src/main/java/electrosphere/engine/service/ServiceManager.java +++ b/src/main/java/electrosphere/engine/service/ServiceManager.java @@ -62,4 +62,13 @@ public class ServiceManager { this.trackedServices = null; } + /** + * Fires all functions required to unload a scene. + */ + public void unloadScene(){ + for(Service serivce : this.trackedServices){ + serivce.unloadScene(); + } + } + } diff --git a/src/main/java/electrosphere/engine/signal/SignalServiceImpl.java b/src/main/java/electrosphere/engine/signal/SignalServiceImpl.java index e93d4b5b..5557950f 100644 --- a/src/main/java/electrosphere/engine/signal/SignalServiceImpl.java +++ b/src/main/java/electrosphere/engine/signal/SignalServiceImpl.java @@ -123,5 +123,12 @@ public class SignalServiceImpl implements SignalService { public Collection getSubscriptionTargets() { return this.targetTypes; } + + @Override + public void unloadScene(){ + this.threadLock.acquireUninterruptibly(); + this.signals.clear(); + this.threadLock.release(); + } } diff --git a/src/main/java/electrosphere/engine/signal/SignalSystem.java b/src/main/java/electrosphere/engine/signal/SignalSystem.java index d9f7b5b6..ecee846c 100644 --- a/src/main/java/electrosphere/engine/signal/SignalSystem.java +++ b/src/main/java/electrosphere/engine/signal/SignalSystem.java @@ -142,5 +142,9 @@ public class SignalSystem implements Service { public void post(SignalType type, Runnable runnable){ this.post(type,(Object)runnable); } + + @Override + public void unloadScene(){ + } } diff --git a/src/main/java/electrosphere/engine/threads/LabeledThread.java b/src/main/java/electrosphere/engine/threads/LabeledThread.java index a40be5f2..3bb61a75 100644 --- a/src/main/java/electrosphere/engine/threads/LabeledThread.java +++ b/src/main/java/electrosphere/engine/threads/LabeledThread.java @@ -9,9 +9,21 @@ public class LabeledThread { * The label associated with the thread */ public static enum ThreadLabel { + /** + * The server socket networking thread + */ NETWORKING_SERVER, + /** + * The client networking thread + */ NETWORKING_CLIENT, + /** + * The main asset loading thread + */ ASSET_LOADING, + /** + * The main loading thread + */ LOADING, } diff --git a/src/main/java/electrosphere/engine/threads/ThreadManager.java b/src/main/java/electrosphere/engine/threads/ThreadManager.java index 543653a2..ddae9e9f 100644 --- a/src/main/java/electrosphere/engine/threads/ThreadManager.java +++ b/src/main/java/electrosphere/engine/threads/ThreadManager.java @@ -83,8 +83,12 @@ public class ThreadManager { * @param thread The thread to start */ public void start(ThreadLabel label, Thread thread){ + if(label.toString() == null || label.toString().length() < 1){ + throw new Error("Invalid label name! " + label.toString()); + } threadLock.lock(); activeThreads.add(new LabeledThread(label, thread)); + thread.setName(label.toString()); thread.start(); threadLock.unlock(); } @@ -117,6 +121,22 @@ public class ThreadManager { return Collections.unmodifiableList(loadingThreads); } + /** + * Waits for a named thread to close + * @param label the label of the thread + */ + public void awaitThreadClose(ThreadLabel label){ + boolean running = true; + while(running){ + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException e) { + running = false; + } + running = this.isRunning(label); + } + } + /** * Tries to close all threads */ @@ -197,6 +217,39 @@ public class ThreadManager { threadLock.unlock(); } + /** + * Updates the list of active threads + */ + private void updateActiveThreads(){ + this.threadLock.lock(); + List clearQueue = new LinkedList(); + for(LabeledThread thread : activeThreads){ + if(!thread.getThread().isAlive()){ + clearQueue.add(thread); + } + } + activeThreads.removeAll(clearQueue); + this.threadLock.unlock(); + } + + /** + * Checks if a thread label is running + * @param label The label + * @return true if it is actively running, false otherwise + */ + public boolean isRunning(ThreadLabel label){ + boolean rVal = false; + threadLock.lock(); + this.updateActiveThreads(); + for(LabeledThread thread : activeThreads){ + if(thread.getLabel() == label){ + rVal = true; + } + } + threadLock.unlock(); + return rVal; + } + /** * Requests a fixed-size thread pool * @param threads The number of threads diff --git a/src/main/java/electrosphere/net/client/ClientNetworking.java b/src/main/java/electrosphere/net/client/ClientNetworking.java index 8c186d06..27da88ff 100644 --- a/src/main/java/electrosphere/net/client/ClientNetworking.java +++ b/src/main/java/electrosphere/net/client/ClientNetworking.java @@ -241,6 +241,17 @@ public class ClientNetworking implements Runnable { } } + if(this.socket != null){ + try { + this.socket.close(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR(e); + } + } + + //null out global state + Globals.clientConnection = null; + LoggerInterface.loggerNetworking.INFO("Client networking thread ended"); } diff --git a/src/main/java/electrosphere/net/server/Server.java b/src/main/java/electrosphere/net/server/Server.java index b11abcbb..2b7af256 100644 --- a/src/main/java/electrosphere/net/server/Server.java +++ b/src/main/java/electrosphere/net/server/Server.java @@ -117,7 +117,9 @@ public class Server implements Runnable { LoggerInterface.loggerEngine.DEBUG("Failed to sleep", e); } } - this.isOpen = false; + this.close(); + //null out global state + Globals.server = null; LoggerInterface.loggerNetworking.INFO("Server socket thread ended"); } diff --git a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java index 0f01b2fb..0db33bd8 100644 --- a/src/main/java/electrosphere/net/server/ServerConnectionHandler.java +++ b/src/main/java/electrosphere/net/server/ServerConnectionHandler.java @@ -297,6 +297,14 @@ public class ServerConnectionHandler implements Runnable { } } + if(this.socket != null){ + try { + this.socket.close(); + } catch (IOException e) { + LoggerInterface.loggerNetworking.ERROR(e); + } + } + LoggerInterface.loggerNetworking.INFO("Server connection thread ended"); } diff --git a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java index 58a4fe79..ba52f5b1 100644 --- a/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java +++ b/src/main/java/electrosphere/server/physics/block/manager/ServerBlockManager.java @@ -10,7 +10,6 @@ import electrosphere.server.physics.block.diskmap.ServerBlockChunkDiskMap; import electrosphere.util.annotation.Exclude; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.Consumer; import org.joml.Vector3i; @@ -46,7 +45,7 @@ public class ServerBlockManager { * The threadpool for chunk generation */ @Exclude - static final ExecutorService chunkExecutorService = Executors.newFixedThreadPool(ThreadCounts.SERVER_BLOCK_GENERATION_THREADS); + ExecutorService chunkExecutorService = Globals.threadManager.requestFixedThreadPool(ThreadCounts.SERVER_BLOCK_GENERATION_THREADS); /** * Constructor diff --git a/src/main/java/electrosphere/server/physics/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/server/physics/terrain/manager/ServerTerrainManager.java index c6b9695c..deedc3a3 100644 --- a/src/main/java/electrosphere/server/physics/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/server/physics/terrain/manager/ServerTerrainManager.java @@ -20,7 +20,6 @@ import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.Consumer; import org.joml.Vector3i; @@ -78,7 +77,7 @@ public class ServerTerrainManager { * The threadpool for chunk generation */ @Exclude - static final ExecutorService chunkExecutorService = Executors.newFixedThreadPool(ThreadCounts.SERVER_TERRAIN_GENERATION_THREADS); + ExecutorService chunkExecutorService = Globals.threadManager.requestFixedThreadPool(ThreadCounts.SERVER_TERRAIN_GENERATION_THREADS); /** * Constructor diff --git a/src/main/java/electrosphere/server/service/CharacterService.java b/src/main/java/electrosphere/server/service/CharacterService.java index 54aac1a5..dd909ea6 100644 --- a/src/main/java/electrosphere/server/service/CharacterService.java +++ b/src/main/java/electrosphere/server/service/CharacterService.java @@ -275,4 +275,11 @@ public class CharacterService extends SignalServiceImpl { lock.unlock(); } + @Override + public void unloadScene(){ + super.unloadScene(); + this.loadedCharacterMap.clear(); + this.characterEntityMap.clear(); + } + }