diff --git a/src/main/java/org/ultramine/commands/CommandContext.java b/src/main/java/org/ultramine/commands/CommandContext.java index 86466eb..4dab2e5 100644 --- a/src/main/java/org/ultramine/commands/CommandContext.java +++ b/src/main/java/org/ultramine/commands/CommandContext.java @@ -11,17 +11,24 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; import net.minecraft.world.WorldServer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ultramine.core.economy.service.Economy; +import org.ultramine.core.economy.account.PlayerAccount; import org.ultramine.core.service.InjectService; import org.ultramine.server.data.ServerDataLoader; import org.ultramine.server.data.player.PlayerData; import org.ultramine.server.util.BasicTypeFormatter; import org.ultramine.server.util.BasicTypeParser; import org.ultramine.core.permissions.Permissions; +import org.ultramine.server.util.GlobalExecutors; +import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -29,11 +36,16 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; public class CommandContext { @InjectService private static Permissions perms; + @InjectService + private static Economy economy; + private static final Logger log = LogManager.getLogger(); private ICommandSender sender; private String[] args; private IExtendedCommand command; @@ -148,11 +160,20 @@ if(contains(arg)) checkSenderPermission(permission, msg); } - - + + + /* thread-safe */ + public void sendMessage(IChatComponent comp) + { + if(Thread.currentThread() == getServer().getServerThread()) + sender.addChatMessage(comp); + else + GlobalExecutors.nextTick().execute(() -> sender.addChatMessage(comp)); + } + public void sendMessage(EnumChatFormatting tplColor, EnumChatFormatting argsColor, String msg, Object... args) { - sender.addChatMessage(BasicTypeFormatter.formatMessage(tplColor, argsColor, msg, args)); + sendMessage(BasicTypeFormatter.formatMessage(tplColor, argsColor, msg, args)); } public void sendMessage(EnumChatFormatting argsColor, String msg, Object... args) @@ -241,6 +262,63 @@ return getServer().getConfigurationManager().getDataLoader(); } + /* thread-safe */ + public void handleException(@Nullable Throwable throwable) + { + if(throwable == null) + return; + if(throwable instanceof CompletionException && throwable.getCause() != null) + throwable = throwable.getCause(); + if(throwable instanceof WrongUsageException) + { + CommandException cmdEx = (CommandException) throwable; + ChatComponentTranslation msg = new ChatComponentTranslation("commands.generic.usage", new ChatComponentTranslation(cmdEx.getMessage(), cmdEx.getErrorOjbects())); + msg.getChatStyle().setColor(EnumChatFormatting.RED); + sendMessage(msg); + } + else if(throwable instanceof CommandException) + { + CommandException cmdEx = (CommandException) throwable; + ChatComponentTranslation msg = new ChatComponentTranslation(cmdEx.getMessage(), cmdEx.getErrorOjbects()); + msg.getChatStyle().setColor(EnumChatFormatting.RED); + sendMessage(msg); + } + else + { + ChatComponentTranslation msg = new ChatComponentTranslation("commands.generic.exception"); + msg.getChatStyle().setColor(EnumChatFormatting.RED); + sendMessage(msg); + log.error("Couldn\'t process command", throwable); + } + } + + /* thread-safe */ + public CompletableFuture handleException(CompletableFuture future) + { + return future.handle((o, throwable) -> { + handleException(throwable); + return null; + }); + } + + /* thread-safe */ + public void finishAfter(CompletableFuture future) + { + future.whenComplete((o, throwable) -> finish(throwable)); + } + + /* thread-safe */ + public void finish() + { + // TODO For async commands, respond RCon only after this method invocation + } + + public void finish(@Nullable Throwable throwable) + { + handleException(throwable); + finish(); + } + public class Argument { @@ -391,6 +469,11 @@ { return new OfflinePlayer(getServer(), asPlayerData()); } + + public PlayerAccount asAccount() + { + return economy.getPlayerAccount(asPlayerData().getProfile()); + } public WorldServer asWorld() { diff --git a/src/main/java/org/ultramine/commands/basic/TechCommands.java b/src/main/java/org/ultramine/commands/basic/TechCommands.java index 417875e..a573f26 100644 --- a/src/main/java/org/ultramine/commands/basic/TechCommands.java +++ b/src/main/java/org/ultramine/commands/basic/TechCommands.java @@ -36,13 +36,10 @@ import org.ultramine.server.UltramineServerModContainer; import org.ultramine.server.BackupManager.BackupDescriptor; import org.ultramine.server.WorldsConfig.WorldConfig; -import org.ultramine.server.WorldsConfig.WorldConfig.Border; import org.ultramine.server.WorldsConfig.WorldConfig.ImportFrom; import org.ultramine.server.chunk.ChunkProfiler; -import org.ultramine.server.chunk.IChunkLoadCallback; import org.ultramine.server.chunk.OffHeapChunkStorage; import org.ultramine.server.util.BasicTypeParser; -import org.ultramine.server.util.GlobalExecutors; import org.ultramine.server.world.MultiWorld; import org.ultramine.server.world.WorldDescriptor; import org.ultramine.server.world.WorldState; @@ -290,7 +287,8 @@ ctx.sendMessage(success); else ctx.sendMessage(RED, RED, fail, e.toString()); - }, GlobalExecutors.nextTick()); + ctx.finish(); + }); } @Command( diff --git a/src/main/java/org/ultramine/server/util/GlobalExecutors.java b/src/main/java/org/ultramine/server/util/GlobalExecutors.java index dc08c9e..bbcc5b0 100644 --- a/src/main/java/org/ultramine/server/util/GlobalExecutors.java +++ b/src/main/java/org/ultramine/server/util/GlobalExecutors.java @@ -1,5 +1,6 @@ package org.ultramine.server.util; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.SynchronousQueue; @@ -7,6 +8,7 @@ import java.util.concurrent.TimeUnit; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.minecraft.server.MinecraftServer; import org.ultramine.server.internal.SyncServerExecutorImpl; public class GlobalExecutors @@ -17,7 +19,18 @@ 60L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactoryBuilder().setNameFormat("UM IO cached #%d").setDaemon(true).build()); - private static final SyncServerExecutor sync = new SyncServerExecutorImpl(); + private static final SyncServerExecutor syncNextTick = new SyncServerExecutorImpl(); + private static final Executor syncNow = new Executor() + { + @Override + public void execute(Runnable toRun) + { + if(Thread.currentThread() == MinecraftServer.getServer().getServerThread()) + toRun.run(); + else + syncNextTick.execute(toRun); + } + }; /** * Обрабатывает задачи на сохранение чего-либо на диск/в БД. Используется @@ -56,6 +69,15 @@ */ public static SyncServerExecutor nextTick() { - return sync; + return syncNextTick; + } + + /** + * Executes tasks in main server thread. Executes synchronously, in {@code execute} method context, if it is invoked + * from main server thread, or on next tick otherwise. + */ + public static Executor syncServer() + { + return syncNow; } }