9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-24 01:19:25 +00:00
Files
Leaf/patches/server/0044-Hearse-Base-codes.patch
2023-01-15 12:31:48 -05:00

686 lines
27 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: wangxyper <wangxyper@163.com>
Date: Sun, 15 Jan 2023 09:56:15 +0800
Subject: [PATCH] Hearse: Base codes
Original license: MIT
Original project: https://github.com/NaturalCodeClub/Hearse
diff --git a/src/main/java/co/earthme/hearse/Hearse.java b/src/main/java/co/earthme/hearse/Hearse.java
new file mode 100644
index 0000000000000000000000000000000000000000..692fef51b2f15dd1ddc28773a381b9da3b42725e
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/Hearse.java
@@ -0,0 +1,27 @@
+package co.earthme.hearse;
+
+import co.earthme.hearse.commands.EntityCountCommand;
+import co.earthme.hearse.commands.WorkerCommand;
+import co.earthme.hearse.server.ServerEntityTickHook;
+import co.earthme.hearse.workers.WorkerThreadPoolManager;
+import net.minecraft.server.MinecraftServer;
+
+public class Hearse {
+ private static final WorkerThreadPoolManager workerManager = new WorkerThreadPoolManager();
+
+ public static void initAll(){
+ HearseConfig.init();
+ ServerEntityTickHook.init();
+ MinecraftServer.getServer().server.getCommandMap().register("workers","hearse",new WorkerCommand());
+ MinecraftServer.getServer().server.getCommandMap().register("entitycount","hearse",new EntityCountCommand());
+ }
+
+ public static void onServerStop(){
+ HearseConfig.save();
+ workerManager.shutdownAllNow();
+ }
+
+ public static WorkerThreadPoolManager getWorkerManager() {
+ return workerManager;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/HearseConfig.java b/src/main/java/co/earthme/hearse/HearseConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..73b5e76660b5162a7a0b327ddc7dcc3295b86699
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/HearseConfig.java
@@ -0,0 +1,49 @@
+package co.earthme.hearse;
+
+import org.bukkit.configuration.InvalidConfigurationException;
+import org.bukkit.configuration.file.YamlConfiguration;
+import java.io.File;
+import java.io.IOException;
+
+public class HearseConfig {
+ private static final YamlConfiguration configEntry = new YamlConfiguration();
+ private static final File CONFIG_FILE = new File("hearse.yml");
+
+ public static void init(){
+ try {
+ configEntry.load(CONFIG_FILE);
+ }catch (IOException ignored){
+ } catch (InvalidConfigurationException e) {
+ e.printStackTrace();
+ }
+ configEntry.options().copyDefaults(true);
+ }
+
+ public static void save(){
+ try {
+ configEntry.save(CONFIG_FILE);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static int getInt(String key,int def){
+ configEntry.addDefault(key,def);
+ return configEntry.getInt(key);
+ }
+
+ public static long getLong(String key,int def){
+ configEntry.addDefault(key,def);
+ return configEntry.getLong(key);
+ }
+
+ public static String getString(String key,String def){
+ configEntry.addDefault(key,def);
+ return configEntry.getString(key);
+ }
+
+ public static boolean getBoolean(String key,boolean def){
+ configEntry.addDefault(key,def);
+ return configEntry.getBoolean(key);
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java b/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..de759c808040058062078130b527e78215216ebb
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/commands/EntityCountCommand.java
@@ -0,0 +1,36 @@
+package co.earthme.hearse.commands;
+
+import com.google.common.collect.Maps;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import java.util.Map;
+
+public class EntityCountCommand extends Command {
+ public EntityCountCommand() {
+ super("entitycount");
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ final Map<String,Integer> counts = Maps.newHashMap();
+ for (ServerLevel level : MinecraftServer.getServer().getAllLevels()){
+ for (Entity entity : level.entityTickList.entities){
+ final String name = entity.getType().getName();
+ if (!counts.containsKey(name)){
+ counts.put(name,0);
+ }
+ counts.replace(name,counts.get(name)+1);
+ }
+ }
+ sender.sendMessage("Exists entity Counts:");
+ for (Map.Entry<String,Integer> entry : counts.entrySet()){
+ sender.sendMessage(ChatColor.BLUE+String.format("%s:%s",entry.getKey(),entry.getValue()));
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/commands/WorkerCommand.java b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a4a6869a7278beadd97af006f4b5fae578b83ed
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/commands/WorkerCommand.java
@@ -0,0 +1,72 @@
+package co.earthme.hearse.commands;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class WorkerCommand extends Command {
+ public WorkerCommand() {
+ super("workers");
+ this.setPermission("hearse.commands.workers");
+ this.setDescription("You can see or edit the server workers by using this command");
+ this.setUsage("/workers <status,setThreadCount,forceStop> <workername>");
+ }
+
+ @Override
+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
+ final List<String> ret = new ArrayList<>();
+ if (args.length == 1){
+ ret.add("status");
+ ret.add("setThreadCount");
+ ret.add("forceStop");
+ }
+ if (args.length == 2){
+ for (Map.Entry<String, WorkerThreadPoolExecutor> entry : Hearse.getWorkerManager().getManagedWorkers().entrySet()){
+ ret.add(entry.getKey());
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ if (args.length >= 2){
+ final String action = args[0];
+ final String workerName = args[1];
+ final WorkerThreadPoolExecutor searchedWorker = Hearse.getWorkerManager().getTargetWorker(workerName);
+ if (searchedWorker == null){
+ sender.sendMessage(ChatColor.RED+"Target worker not found!");
+ return true;
+ }
+ switch (action){
+ case "status":
+ sender.sendMessage(ChatColor.GREEN+"Worker: "+workerName+" Status:"+ searchedWorker);
+ break;
+ case "setThreadCount":
+ if (args.length == 3){
+ try {
+ searchedWorker.setCorePoolSize(Integer.parseInt(args[2]));
+ sender.sendMessage(ChatColor.GREEN+"Finished!");
+ }catch (NumberFormatException e){
+ sender.sendMessage(ChatColor.RED+"Please supply an integer!");
+ }
+ }else{
+ sender.sendMessage(ChatColor.RED+"Please supply an integer!");
+ }
+ break;
+ case "forceStop":
+ searchedWorker.shutdownNow();
+ sender.sendMessage(ChatColor.YELLOW+"Worker "+workerName+" has been stopped!");
+ break;
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
new file mode 100644
index 0000000000000000000000000000000000000000..421d4926ac674b5eb12d9613ceb6d20185ea557d
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThread.java
@@ -0,0 +1,18 @@
+package co.earthme.hearse.concurrent;
+
+import io.papermc.paper.util.TickThread;
+
+public class WorkerThread extends TickThread {
+
+ public WorkerThread(String name) {
+ super(name);
+ }
+
+ public WorkerThread(Runnable run, String name) {
+ super(run, name);
+ }
+
+ public static boolean isWorker(){
+ return Thread.currentThread() instanceof WorkerThread;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e65b1eba68003a9f7ce5080d07a521817831ff48
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadFactory.java
@@ -0,0 +1,5 @@
+package co.earthme.hearse.concurrent;
+
+public interface WorkerThreadFactory {
+ WorkerThread getNewThread(Runnable task);
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e010bf23c9fc26284212a4388172f5d7d5a4b99
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/WorkerThreadPoolExecutor.java
@@ -0,0 +1,76 @@
+package co.earthme.hearse.concurrent;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Queue;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.LockSupport;
+
+public class WorkerThreadPoolExecutor extends ThreadPoolExecutor {
+ private final Queue<TaskEntry> taskEntries = new ConcurrentLinkedQueue<>();
+
+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull WorkerThreadFactory workerThreadFactory) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread);
+ }
+
+ public WorkerThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, @NotNull TimeUnit unit, @NotNull BlockingQueue<Runnable> workQueue, @NotNull WorkerThreadFactory workerThreadFactory, @NotNull RejectedExecutionHandler handler) {
+ super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, workerThreadFactory::getNewThread, handler);
+ }
+
+ public int getCurrentNotProcessingTasks(){
+ return this.getQueue().size();
+ }
+
+ public void clearAllTasks(){
+ this.getQueue().clear();
+ }
+
+ public void executeWithSubTask(Runnable mainTask,Runnable subTask){
+ final TaskEntry wrapped = new TaskEntry(subTask,mainTask);
+ this.taskEntries.offer(wrapped);
+ this.execute(wrapped);
+ }
+
+ public void runAllSubTasks(){
+ TaskEntry task;
+ while ((task = this.taskEntries.poll())!=null){
+ while (!task.allRunned()){
+ LockSupport.parkNanos(this,10000000);
+ }
+ }
+ }
+
+ private static class TaskEntry implements Runnable{
+ private final Runnable mainTask;
+ private final Runnable subTask;
+ private volatile boolean mainTaskFinished = false;
+
+ public TaskEntry(Runnable subTask,Runnable mainTask){
+ this.subTask = subTask;
+ this.mainTask = mainTask;
+ }
+
+ public boolean allRunned(){
+ if (!this.mainTaskFinished){
+ return false;
+ }
+ try {
+ this.subTask.run();
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.mainTask.run();
+ }catch(Exception e){
+ e.printStackTrace();
+ }finally {
+ this.mainTaskFinished = true;
+ }
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..03a29509821a17faac2dc8ab810a2693b03bfbc6
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/concurrent/threadfactory/DefaultWorkerFactory.java
@@ -0,0 +1,42 @@
+package co.earthme.hearse.concurrent.threadfactory;
+
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadFactory;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
+import net.minecraft.server.MinecraftServer;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DefaultWorkerFactory implements WorkerThreadFactory {
+ private static final AtomicInteger poolId = new AtomicInteger();
+ private final AtomicInteger threadId = new AtomicInteger();
+ private final String bound;
+ private final List<Thread> createdThreads = ObjectLists.synchronize(new ObjectArrayList<>());
+
+ public DefaultWorkerFactory(String bound){
+ poolId.getAndIncrement();
+ this.bound = bound;
+ }
+
+ public List<Thread> getCreatedThreads() {
+ return this.createdThreads;
+ }
+
+ @Override
+ public WorkerThread getNewThread(Runnable task) {
+ final WorkerThread workerThread = new WorkerThread(()->{
+ try {
+ task.run();
+ }finally {
+ this.createdThreads.remove(Thread.currentThread());
+ }
+ },"pool-"+poolId.get()+"-worker-"+threadId.getAndIncrement()+"-bound-"+this.bound);
+ this.createdThreads.add(workerThread);
+ workerThread.setDaemon(true);
+ workerThread.setPriority(Thread.NORM_PRIORITY - 2);
+ workerThread.setContextClassLoader(MinecraftServer.class.getClassLoader());
+ return workerThread;
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0e7a9cf79ddf00827daba0aa9c7a32fa76b0c7c
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/server/ServerEntityTickHook.java
@@ -0,0 +1,102 @@
+package co.earthme.hearse.server;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.HearseConfig;
+import co.earthme.hearse.concurrent.WorkerThreadFactory;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.player.Player;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ServerEntityTickHook {
+ private static final Logger logger = LogManager.getLogger();
+ private static volatile boolean firstTick = false;
+ private static final WorkerThreadFactory defFactory = new DefaultWorkerFactory("entity");
+ private static final AtomicInteger threadId = new AtomicInteger();
+ private static WorkerThreadPoolExecutor worker;
+ private static boolean asyncEntityEnabled;
+
+ public static void executeAsyncTask(Runnable task){
+ if (!asyncEntityEnabled){
+ throw new RejectedExecutionException();
+ }
+ worker.execute(task);
+ }
+
+ public static void init(){
+ boolean asyncEntityEnabled1 = HearseConfig.getBoolean("optimizations.enable-async-entity",true);
+ final int workerCount = HearseConfig.getInt("workers.async-entity-worker-count",Runtime.getRuntime().availableProcessors());
+ if (asyncEntityEnabled1){
+ worker = new WorkerThreadPoolExecutor(
+ workerCount,
+ workerCount,
+ 0L,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ defFactory
+ );
+ Hearse.getWorkerManager().addWorker("entity",worker);
+ }
+ asyncEntityEnabled = asyncEntityEnabled1;
+ }
+
+ public static void executeAsyncTaskWithMainThreadCallback(Runnable task,Runnable callBack){
+ if (!asyncEntityEnabled){
+ throw new IllegalStateException();
+ }
+ worker.executeWithSubTask(task,callBack);
+ }
+
+ public static void callTickStart(){
+ if (!firstTick){
+ firstTick = true;
+ return;
+ }
+ if (!asyncEntityEnabled){
+ return;
+ }
+ worker.runAllSubTasks();
+ }
+
+ public static void callAsyncEntityTick(Entity entity, ServerLevel level){
+ MinecraftServer.getServer().executeMidTickTasks();
+ Runnable task = ()->{
+ entity.activatedPriorityReset = false;
+ if (!entity.isRemoved()) {
+ entity.checkDespawn();
+ Entity entity1 = entity.getVehicle();
+ if (entity1 != null) {
+ if (!entity1.isRemoved() && entity1.hasPassenger(entity)) {
+ return;
+ }
+ entity.stopRiding();
+ }
+ try {
+ level.tickNonPassenger(entity);
+ } catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable;
+ level.getCraftServer().getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerInternalException(throwable.getMessage(), throwable)));
+ throwable.printStackTrace();
+ }
+ }
+ };
+ if (!asyncEntityEnabled){
+ task.run();
+ return;
+ }
+ try {
+ worker.execute(task);
+ }catch (RejectedExecutionException e){
+ logger.warn("Worker rejected our task.Falling back to sync entity updating");
+ asyncEntityEnabled = false;
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
new file mode 100644
index 0000000000000000000000000000000000000000..8085eb700d8e5c20ebb5bfeceb78198c6e973019
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/server/ServerLevelTickHook.java
@@ -0,0 +1,78 @@
+package co.earthme.hearse.server;
+
+import co.earthme.hearse.Hearse;
+import co.earthme.hearse.HearseConfig;
+import co.earthme.hearse.concurrent.WorkerThread;
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import co.earthme.hearse.concurrent.threadfactory.DefaultWorkerFactory;
+import net.minecraft.CrashReport;
+import net.minecraft.ReportedException;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.LockSupport;
+import java.util.function.BooleanSupplier;
+
+public class ServerLevelTickHook {
+ private static final DefaultWorkerFactory workerFactory = new DefaultWorkerFactory("world");
+ private static WorkerThreadPoolExecutor worker;
+ private static boolean enabledParaWorld;
+ private static volatile boolean inited = false;
+ private static final AtomicInteger activeTaskCount = new AtomicInteger();
+ private static final Logger logger = LogManager.getLogger();
+
+ public static void initWorker(){
+ enabledParaWorld = HearseConfig.getBoolean("optimizations.enableparallelworldtick",true);
+ if (enabledParaWorld){
+ worker = new WorkerThreadPoolExecutor(
+ MinecraftServer.getServer().levels.size(),
+ MinecraftServer.getServer().levels.size(),
+ Long.MAX_VALUE,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>(),
+ workerFactory
+ );
+ worker.allowCoreThreadTimeOut(true);
+ worker.prestartAllCoreThreads();
+ Hearse.getWorkerManager().addWorker("world",worker);
+ for (Thread worker : workerFactory.getCreatedThreads()){
+ logger.warn("World worker name:{}.This can help you to slove the lag problems when you using parallel world ticking",worker.getName());
+ }
+ }
+ inited = true;
+ }
+
+ public static boolean isInited(){
+ return inited;
+ }
+
+ public static void callWorldTick(ServerLevel worldserver, BooleanSupplier shouldKeepTicking){
+ activeTaskCount.getAndIncrement();
+ worker.execute(()->{
+ try {
+ try {
+ worldserver.tick(shouldKeepTicking);
+ for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
+ regionManager.recalculateRegions();
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ worldserver.explosionDensityCache.clear();
+ }finally {
+ activeTaskCount.getAndDecrement();
+ }
+ });
+ }
+
+ public static void awaitWorldTicKTasks(){
+ while (activeTaskCount.get() > 0){
+ LockSupport.parkNanos("Await world ticking",1000000);
+ }
+ }
+}
diff --git a/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..527dba288e1988773fd5a89f076f92084034f421
--- /dev/null
+++ b/src/main/java/co/earthme/hearse/workers/WorkerThreadPoolManager.java
@@ -0,0 +1,68 @@
+package co.earthme.hearse.workers;
+
+import co.earthme.hearse.concurrent.WorkerThreadPoolExecutor;
+import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class WorkerThreadPoolManager {
+ private final Map<String,WorkerThreadPoolExecutor> managedWorkers = Maps.newConcurrentMap();
+
+ public void addWorker(String bound,WorkerThreadPoolExecutor worker){
+ this.managedWorkers.put(bound,worker);
+ }
+
+ public void shutdownAll() throws InterruptedException {
+ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
+ if (!worker.isShutdown()){
+ worker.getQueue().clear(); //Clear the tasks.We don't need wait them
+ worker.shutdown();
+ while (worker.awaitTermination(100, TimeUnit.MILLISECONDS)) {}
+ }
+ }
+ }
+
+ @Deprecated
+ public Map<String, WorkerThreadPoolExecutor> getManagedWorkers() {
+ return Maps.newHashMap(this.managedWorkers);
+ }
+
+ @Deprecated
+ public WorkerThreadPoolExecutor getTargetWorker(String bound){
+ return this.managedWorkers.get(bound);
+ }
+
+ public Map<String,List<Runnable>> shutdownAllNow(){
+ final Map<String,List<Runnable>> ret = Maps.newHashMap();
+ for (Map.Entry<String,WorkerThreadPoolExecutor> entry : this.managedWorkers.entrySet()){
+ final String workerName = entry.getKey();
+ final WorkerThreadPoolExecutor worker = entry.getValue();
+ if (!worker.isShutdown()){
+ try {
+ final List<Runnable> taskNotRunned = worker.shutdownNow();
+ ret.put(workerName,taskNotRunned);
+ }catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ }
+ return ret;
+ }
+
+ public void shutdownAll(long singleWorkerAwaitTimeOutCount) throws InterruptedException {
+ long counter = singleWorkerAwaitTimeOutCount;
+ for (WorkerThreadPoolExecutor worker : this.managedWorkers.values()){
+ if (!worker.isShutdown()){
+ worker.shutdown();
+ while (worker.awaitTermination(1, TimeUnit.MILLISECONDS)) {
+ if (counter == 0){
+ break;
+ }
+ counter--;
+ }
+ counter = singleWorkerAwaitTimeOutCount;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index f7e8b6e1872a397c96afc938754726b0d4e493b4..2448673ee847fe3bc05f1269737aae5b43ae8291 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -308,6 +308,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
return (EntityType) Registry.register(BuiltInRegistries.ENTITY_TYPE, id, (EntityType<T>) type.build(id)); // CraftBukkit - decompile error
}
+ // Purpur start
+ public static EntityType<?> getFromBukkitType(org.bukkit.entity.EntityType bukkitType) {
+ return getFromKey(new ResourceLocation(bukkitType.getKey().toString()));
+ }
+
+ public static EntityType<?> getFromKey(ResourceLocation location) {
+ return BuiltInRegistries.ENTITY_TYPE.get(location);
+ }
+ // Purpur end
+
public static ResourceLocation getKey(EntityType<?> type) {
return BuiltInRegistries.ENTITY_TYPE.getKey(type);
}
@@ -522,6 +532,16 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
return this.category;
}
+ // Purpur start
+ public String getName() {
+ return BuiltInRegistries.ENTITY_TYPE.getKey(this).getPath();
+ }
+
+ public String getTranslatedName() {
+ return getDescription().getString();
+ }
+ // Purpur end
+
public String getDescriptionId() {
if (this.descriptionId == null) {
this.descriptionId = Util.makeDescriptionId("entity", BuiltInRegistries.ENTITY_TYPE.getKey(this));