minecraft-source/src/net/minecraft/server/ServerFunctionManager.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();
}
}
}