226 lines
9.4 KiB
Java
226 lines
9.4 KiB
Java
package net.minecraft.server;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.network.chat.TextComponent;
|
|
import net.minecraft.world.phys.Vec2;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.commands.CommandSource;
|
|
import net.minecraft.server.packs.resources.Resource;
|
|
import java.io.IOException;
|
|
import java.util.concurrent.CompletionException;
|
|
import org.apache.commons.io.IOUtils;
|
|
import java.nio.charset.StandardCharsets;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.tags.Tag;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import net.minecraft.server.packs.resources.SimpleResource;
|
|
import net.minecraft.server.packs.resources.ResourceManager;
|
|
import java.util.function.Consumer;
|
|
import java.util.Collection;
|
|
import java.util.Iterator;
|
|
import net.minecraft.commands.CommandSourceStack;
|
|
import com.mojang.brigadier.CommandDispatcher;
|
|
import net.minecraft.world.level.GameRules;
|
|
import java.util.Optional;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Maps;
|
|
import net.minecraft.tags.TagCollection;
|
|
import java.util.List;
|
|
import java.util.ArrayDeque;
|
|
import net.minecraft.commands.CommandFunction;
|
|
import java.util.Map;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import org.apache.logging.log4j.Logger;
|
|
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
|
|
|
|
public class ServerFunctionManager implements ResourceManagerReloadListener {
|
|
private static final Logger LOGGER;
|
|
private static final ResourceLocation TICK_FUNCTION_TAG;
|
|
private static final ResourceLocation LOAD_FUNCTION_TAG;
|
|
public static final int PATH_PREFIX_LENGTH;
|
|
public static final int PATH_SUFFIX_LENGTH;
|
|
private final MinecraftServer server;
|
|
private final Map<ResourceLocation, CommandFunction> functions;
|
|
private boolean isInFunction;
|
|
private final ArrayDeque<QueuedCommand> commandQueue;
|
|
private final List<QueuedCommand> nestedCalls;
|
|
private final TagCollection<CommandFunction> tags;
|
|
private final List<CommandFunction> ticking;
|
|
private boolean postReload;
|
|
|
|
public ServerFunctionManager(final MinecraftServer minecraftServer) {
|
|
this.functions = Maps.newHashMap();
|
|
this.commandQueue = new ArrayDeque<QueuedCommand>();
|
|
this.nestedCalls = Lists.newArrayList();
|
|
this.tags = new TagCollection<CommandFunction>(this::get, "tags/functions", true, "function");
|
|
this.ticking = Lists.newArrayList();
|
|
this.server = minecraftServer;
|
|
}
|
|
|
|
public Optional<CommandFunction> get(final ResourceLocation sm) {
|
|
return Optional.<CommandFunction>ofNullable(this.functions.get(sm));
|
|
}
|
|
|
|
public MinecraftServer getServer() {
|
|
return this.server;
|
|
}
|
|
|
|
public int getCommandLimit() {
|
|
return this.server.getGameRules().getInt(GameRules.RULE_MAX_COMMAND_CHAIN_LENGTH);
|
|
}
|
|
|
|
public Map<ResourceLocation, CommandFunction> getFunctions() {
|
|
return this.functions;
|
|
}
|
|
|
|
public CommandDispatcher<CommandSourceStack> getDispatcher() {
|
|
return this.server.getCommands().getDispatcher();
|
|
}
|
|
|
|
public void tick() {
|
|
this.server.getProfiler().push(ServerFunctionManager.TICK_FUNCTION_TAG::toString);
|
|
for (final CommandFunction cn3 : this.ticking) {
|
|
this.execute(cn3, this.getGameLoopSender());
|
|
}
|
|
this.server.getProfiler().pop();
|
|
if (this.postReload) {
|
|
this.postReload = false;
|
|
final Collection<CommandFunction> collection2 = this.getTags().getTagOrEmpty(ServerFunctionManager.LOAD_FUNCTION_TAG).getValues();
|
|
this.server.getProfiler().push(ServerFunctionManager.LOAD_FUNCTION_TAG::toString);
|
|
for (final CommandFunction cn4 : collection2) {
|
|
this.execute(cn4, this.getGameLoopSender());
|
|
}
|
|
this.server.getProfiler().pop();
|
|
}
|
|
}
|
|
|
|
public int execute(final CommandFunction cn, final CommandSourceStack cq) {
|
|
final int integer4 = this.getCommandLimit();
|
|
if (this.isInFunction) {
|
|
if (this.commandQueue.size() + this.nestedCalls.size() < integer4) {
|
|
this.nestedCalls.add(new QueuedCommand(this, cq, new CommandFunction.FunctionEntry(cn)));
|
|
}
|
|
return 0;
|
|
}
|
|
try {
|
|
this.isInFunction = true;
|
|
int integer5 = 0;
|
|
final CommandFunction.Entry[] arr6 = cn.getEntries();
|
|
for (int integer6 = arr6.length - 1; integer6 >= 0; --integer6) {
|
|
this.commandQueue.push(new QueuedCommand(this, cq, arr6[integer6]));
|
|
}
|
|
while (!this.commandQueue.isEmpty()) {
|
|
try {
|
|
final QueuedCommand a7 = this.commandQueue.removeFirst();
|
|
this.server.getProfiler().push(a7::toString);
|
|
a7.execute(this.commandQueue, integer4);
|
|
if (!this.nestedCalls.isEmpty()) {
|
|
Lists.<QueuedCommand>reverse(this.nestedCalls).forEach(this.commandQueue::addFirst);
|
|
this.nestedCalls.clear();
|
|
}
|
|
}
|
|
finally {
|
|
this.server.getProfiler().pop();
|
|
}
|
|
if (++integer5 >= integer4) {
|
|
return integer5;
|
|
}
|
|
}
|
|
return integer5;
|
|
}
|
|
finally {
|
|
this.commandQueue.clear();
|
|
this.nestedCalls.clear();
|
|
this.isInFunction = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onResourceManagerReload(final ResourceManager zb) {
|
|
this.functions.clear();
|
|
this.ticking.clear();
|
|
final Collection<ResourceLocation> collection3 = zb.listResources("functions", string -> string.endsWith(".mcfunction"));
|
|
final List<CompletableFuture<CommandFunction>> list2 = Lists.newArrayList();
|
|
for (final ResourceLocation sm6 : collection3) {
|
|
final String string2 = sm6.getPath();
|
|
final ResourceLocation sm7 = new ResourceLocation(sm6.getNamespace(), string2.substring(ServerFunctionManager.PATH_PREFIX_LENGTH, string2.length() - ServerFunctionManager.PATH_SUFFIX_LENGTH));
|
|
list2.add(CompletableFuture.<List<String>>supplyAsync(() -> readLinesAsync(zb, sm6), SimpleResource.IO_EXECUTOR).thenApplyAsync(list -> CommandFunction.fromLines(sm7, this, list), this.server.getBackgroundTaskExecutor()).<CommandFunction>handle((cn, throwable) -> this.addFunction(cn, throwable, sm6)));
|
|
}
|
|
CompletableFuture.allOf(list2.<CompletableFuture<?>>toArray(new CompletableFuture[0])).join();
|
|
if (!this.functions.isEmpty()) {
|
|
ServerFunctionManager.LOGGER.info("Loaded {} custom command functions", this.functions.size());
|
|
}
|
|
this.tags.load(this.tags.prepare(zb, this.server.getBackgroundTaskExecutor()).join());
|
|
this.ticking.addAll(this.tags.getTagOrEmpty(ServerFunctionManager.TICK_FUNCTION_TAG).getValues());
|
|
this.postReload = true;
|
|
}
|
|
|
|
@Nullable
|
|
private CommandFunction addFunction(final CommandFunction cn, @Nullable final Throwable throwable, final ResourceLocation sm) {
|
|
if (throwable != null) {
|
|
ServerFunctionManager.LOGGER.error("Couldn't load function at {}", sm, throwable);
|
|
return null;
|
|
}
|
|
synchronized (this.functions) {
|
|
this.functions.put(cn.getId(), cn);
|
|
}
|
|
return cn;
|
|
}
|
|
|
|
private static List<String> readLinesAsync(final ResourceManager zb, final ResourceLocation sm) {
|
|
try (final Resource za3 = zb.getResource(sm)) {
|
|
return (List<String>)IOUtils.readLines(za3.getInputStream(), StandardCharsets.UTF_8);
|
|
}
|
|
catch (IOException iOException3) {
|
|
throw new CompletionException(iOException3);
|
|
}
|
|
}
|
|
|
|
public CommandSourceStack getGameLoopSender() {
|
|
return this.server.createCommandSourceStack().withPermission(2).withSuppressedOutput();
|
|
}
|
|
|
|
public CommandSourceStack getCompilationContext() {
|
|
return new CommandSourceStack(CommandSource.NULL, Vec3.ZERO, Vec2.ZERO, null, this.server.getFunctionCompilationLevel(), "", new TextComponent(""), this.server, null);
|
|
}
|
|
|
|
public TagCollection<CommandFunction> getTags() {
|
|
return this.tags;
|
|
}
|
|
|
|
static {
|
|
LOGGER = LogManager.getLogger();
|
|
TICK_FUNCTION_TAG = new ResourceLocation("tick");
|
|
LOAD_FUNCTION_TAG = new ResourceLocation("load");
|
|
PATH_PREFIX_LENGTH = "functions/".length();
|
|
PATH_SUFFIX_LENGTH = ".mcfunction".length();
|
|
}
|
|
|
|
public static class QueuedCommand {
|
|
private final ServerFunctionManager manager;
|
|
private final CommandSourceStack sender;
|
|
private final CommandFunction.Entry entry;
|
|
|
|
public QueuedCommand(final ServerFunctionManager sy, final CommandSourceStack cq, final CommandFunction.Entry c) {
|
|
this.manager = sy;
|
|
this.sender = cq;
|
|
this.entry = c;
|
|
}
|
|
|
|
public void execute(final ArrayDeque<QueuedCommand> arrayDeque, final int integer) {
|
|
try {
|
|
this.entry.execute(this.manager, this.sender, arrayDeque, integer);
|
|
}
|
|
catch (Throwable t) {}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return this.entry.toString();
|
|
}
|
|
}
|
|
}
|