diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 6311d590..058c4fe9 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1798,6 +1798,8 @@ 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 +AI manager thread safeing +AI manager better error handling diff --git a/src/main/java/electrosphere/logger/Logger.java b/src/main/java/electrosphere/logger/Logger.java index f51b5f6f..b1a51296 100644 --- a/src/main/java/electrosphere/logger/Logger.java +++ b/src/main/java/electrosphere/logger/Logger.java @@ -141,6 +141,17 @@ public class Logger { } } + /** + * Logs an error message. + * This should be used every time we throw any kind of error in the engine + * @param e The exception to report + */ + public void ERROR(Throwable e){ + if(level == LogLevel.LOOP_DEBUG || level == LogLevel.DEBUG || level == LogLevel.INFO || level == LogLevel.WARNING || level == LogLevel.ERROR){ + e.printStackTrace(); + } + } + /** * Prints a message at the specified logging level * @param level The logging level diff --git a/src/main/java/electrosphere/server/ai/AIManager.java b/src/main/java/electrosphere/server/ai/AIManager.java index 3a60d502..d9bfc2f6 100644 --- a/src/main/java/electrosphere/server/ai/AIManager.java +++ b/src/main/java/electrosphere/server/ai/AIManager.java @@ -1,10 +1,12 @@ package electrosphere.server.ai; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.concurrent.locks.ReentrantLock; import electrosphere.data.creature.ai.AITreeData; import electrosphere.entity.Entity; @@ -17,6 +19,11 @@ import electrosphere.server.ai.services.TimerService; * Server manager for all entity AIs */ public class AIManager { + + /** + * Lock for thread-safeing the manager + */ + ReentrantLock lock = new ReentrantLock(); /** * The list of ais @@ -69,15 +76,21 @@ public class AIManager { * Simulates all AIs currently available */ public void simulate(){ + lock.lock(); //exec the services this.execServices(); //simulate each tree if(this.isActive()){ for(AI ai : aiList){ - ai.simulate(); + try { + ai.simulate(); + } catch(Error|Exception e){ + LoggerInterface.loggerAI.ERROR(e); + } } } + lock.unlock(); } /** @@ -85,6 +98,7 @@ public class AIManager { * @param isActive true to simulate ai each frame, false otherwise */ public void setActive(boolean isActive){ + lock.lock(); //turn off ai components if deactivating ai if(this.active && !isActive){ for(AI ai : aiList){ @@ -93,6 +107,7 @@ public class AIManager { } //actually set this.active = isActive; + lock.unlock(); } /** @@ -108,7 +123,10 @@ public class AIManager { * @return The list of AIs */ public List getAIList(){ - return this.aiList; + lock.lock(); + List rVal = Collections.unmodifiableList(this.aiList); + lock.unlock(); + return rVal; } /** @@ -120,11 +138,13 @@ public class AIManager { if(entity == null){ LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Entity provided is null!")); } + lock.lock(); AI ai = AI.constructAI(entity, treeData); aiList.add(ai); entityAIMap.put(entity,ai); aiEntityMap.put(ai,entity); AI.setAI(entity, ai); + lock.unlock(); } /** @@ -132,10 +152,12 @@ public class AIManager { * @param entity The entity */ public void removeAI(Entity entity){ + lock.lock(); AI targetAI = entityAIMap.get(entity); aiList.remove(targetAI); aiEntityMap.remove(targetAI); entityAIMap.remove(entity); + lock.unlock(); } /** @@ -182,9 +204,11 @@ public class AIManager { * Shuts down the ai manager */ public void shutdown(){ + lock.lock(); this.pathfindingService.shutdown(); this.nearbyEntityService.shutdown(); this.timerService.shutdown(); + lock.unlock(); } }