276 lines
10 KiB
Java
276 lines
10 KiB
Java
package carpet.script.external;
|
|
|
|
import carpet.CarpetServer;
|
|
import carpet.CarpetSettings;
|
|
import carpet.api.settings.CarpetRule;
|
|
import carpet.api.settings.RuleHelper;
|
|
import carpet.api.settings.SettingsManager;
|
|
import carpet.api.settings.Validator;
|
|
import carpet.fakes.MinecraftServerInterface;
|
|
import carpet.logging.HUDController;
|
|
import carpet.network.ServerNetworkHandler;
|
|
import carpet.patches.EntityPlayerMPFake;
|
|
import carpet.script.CarpetEventServer;
|
|
import carpet.script.CarpetExpression;
|
|
import carpet.script.CarpetScriptHost;
|
|
import carpet.script.CarpetScriptServer;
|
|
import carpet.script.Module;
|
|
import carpet.script.exception.InternalExpressionException;
|
|
import carpet.script.exception.LoadException;
|
|
import carpet.script.utils.AppStoreManager;
|
|
import carpet.script.value.MapValue;
|
|
import carpet.script.value.StringValue;
|
|
import carpet.utils.CarpetProfiler;
|
|
import carpet.utils.Messenger;
|
|
import net.fabricmc.api.EnvType;
|
|
import net.fabricmc.loader.api.FabricLoader;
|
|
import net.fabricmc.loader.api.ModContainer;
|
|
import net.fabricmc.loader.api.SemanticVersion;
|
|
import net.fabricmc.loader.api.Version;
|
|
import net.fabricmc.loader.api.VersionParsingException;
|
|
import net.fabricmc.loader.api.metadata.version.VersionPredicate;
|
|
import net.minecraft.commands.CommandSourceStack;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.world.entity.player.Player;
|
|
|
|
import javax.annotation.Nullable;
|
|
import java.io.IOException;
|
|
import java.nio.file.FileVisitOption;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.stream.Stream;
|
|
|
|
public class Carpet
|
|
{
|
|
public static Map<String, Component> getScarpetHeaders()
|
|
{
|
|
return HUDController.scarpet_headers;
|
|
}
|
|
|
|
public static Map<String, Component> getScarpetFooters()
|
|
{
|
|
return HUDController.scarpet_footers;
|
|
}
|
|
|
|
public static void updateScarpetHUDs(MinecraftServer server, List<ServerPlayer> players)
|
|
{
|
|
HUDController.update_hud(server, players);
|
|
}
|
|
|
|
public static Component Messenger_compose(Object... messages)
|
|
{
|
|
return Messenger.c(messages);
|
|
}
|
|
|
|
public static void Messenger_message(CommandSourceStack source, Object... messages)
|
|
{
|
|
Messenger.m(source, messages);
|
|
}
|
|
|
|
public static ThreadLocal<Boolean> getImpendingFillSkipUpdates()
|
|
{
|
|
return CarpetSettings.impendingFillSkipUpdates;
|
|
}
|
|
|
|
public static Runnable startProfilerSection(String name)
|
|
{
|
|
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, name, CarpetProfiler.TYPE.GENERAL);
|
|
return () -> CarpetProfiler.end_current_section(token);
|
|
}
|
|
|
|
public static void MinecraftServer_addScriptServer(MinecraftServer server, CarpetScriptServer scriptServer)
|
|
{
|
|
((MinecraftServerInterface) server).addScriptServer(scriptServer);
|
|
}
|
|
|
|
public static boolean isValidCarpetPlayer(ServerPlayer player)
|
|
{
|
|
return ServerNetworkHandler.isValidCarpetPlayer(player);
|
|
}
|
|
|
|
public static String getPlayerStatus(ServerPlayer player)
|
|
{
|
|
return ServerNetworkHandler.getPlayerStatus(player);
|
|
}
|
|
|
|
public static MapValue getAllCarpetRules()
|
|
{
|
|
Collection<CarpetRule<?>> rules = CarpetServer.settingsManager.getCarpetRules();
|
|
MapValue carpetRules = new MapValue(Collections.emptyList());
|
|
rules.forEach(rule -> carpetRules.put(new StringValue(rule.name()), new StringValue(RuleHelper.toRuleString(rule.value()))));
|
|
CarpetServer.extensions.forEach(e -> {
|
|
SettingsManager manager = e.extensionSettingsManager();
|
|
if (manager == null)
|
|
{
|
|
return;
|
|
}
|
|
manager.getCarpetRules().forEach(rule -> carpetRules.put(new StringValue(manager.identifier() + ":" + rule.name()), new StringValue(RuleHelper.toRuleString(rule.value()))));
|
|
});
|
|
return carpetRules;
|
|
}
|
|
|
|
public static String getCarpetVersion()
|
|
{
|
|
return CarpetSettings.carpetVersion;
|
|
}
|
|
|
|
@Nullable
|
|
public static String isModdedPlayer(Player p)
|
|
{
|
|
if (p instanceof final EntityPlayerMPFake fake)
|
|
{
|
|
return fake.isAShadow ? "shadow" : "fake";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static boolean isTickProcessingPaused(MinecraftServer server)
|
|
{
|
|
return !((MinecraftServerInterface)server).getTickRateManager().runsNormally();
|
|
}
|
|
|
|
public static void handleExtensionsAPI(CarpetExpression expression)
|
|
{
|
|
CarpetServer.extensions.forEach(e -> e.scarpetApi(expression));
|
|
}
|
|
|
|
public static boolean getFillUpdates()
|
|
{
|
|
return CarpetSettings.fillUpdates;
|
|
}
|
|
|
|
@Nullable
|
|
public static Module fetchGlobalModule(String name, boolean allowLibraries) throws IOException
|
|
{
|
|
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
|
{
|
|
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
|
if (!Files.exists(globalFolder))
|
|
{
|
|
Files.createDirectories(globalFolder);
|
|
}
|
|
try (Stream<Path> folderWalker = Files.walk(globalFolder))
|
|
{
|
|
Optional<Path> scriptPath = folderWalker
|
|
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
|
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
|
.findFirst();
|
|
if (scriptPath.isPresent())
|
|
{
|
|
return Module.fromPath(scriptPath.get());
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static void addGlobalModules(final List<String> moduleNames, boolean includeBuiltIns) throws IOException
|
|
{
|
|
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
|
{
|
|
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
|
if (!Files.exists(globalScripts))
|
|
{
|
|
Files.createDirectories(globalScripts);
|
|
}
|
|
try (Stream<Path> folderWalker = Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS))
|
|
{
|
|
folderWalker
|
|
.filter(f -> f.toString().endsWith(".sc"))
|
|
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$", "").toLowerCase(Locale.ROOT)));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void assertRequirementMet(CarpetScriptHost host, String requiredModId, String stringPredicate)
|
|
{
|
|
VersionPredicate predicate;
|
|
try
|
|
{
|
|
predicate = VersionPredicate.parse(stringPredicate);
|
|
}
|
|
catch (VersionParsingException e)
|
|
{
|
|
throw new InternalExpressionException("Failed to parse version conditions for '" + requiredModId + "' in 'requires': " + e.getMessage());
|
|
}
|
|
|
|
ModContainer mod = FabricLoader.getInstance().getModContainer(requiredModId).orElse(null);
|
|
if (mod != null)
|
|
{
|
|
Version presentVersion = mod.getMetadata().getVersion();
|
|
if (predicate.test(presentVersion) || (FabricLoader.getInstance().isDevelopmentEnvironment() && !(presentVersion instanceof SemanticVersion)))
|
|
{ // in a dev env, mod version is usually replaced with ${version}, and that isn't semantic
|
|
return;
|
|
}
|
|
}
|
|
throw new LoadException(String.format("%s requires a version of mod '%s' matching '%s', which is missing!", host.getName(), requiredModId, stringPredicate));
|
|
}
|
|
|
|
// to be ran once during CarpetEventServer.Event static init
|
|
public static void initCarpetEvents() {
|
|
CarpetEventServer.Event carpetRuleChanges = new CarpetEventServer.Event("carpet_rule_changes", 2, true)
|
|
{
|
|
@Override
|
|
public void handleAny(final Object... args)
|
|
{
|
|
CarpetRule<?> rule = (CarpetRule<?>) args[0];
|
|
CommandSourceStack source = (CommandSourceStack) args[1];
|
|
String id = rule.settingsManager().identifier();
|
|
String namespace;
|
|
if (!id.equals("carpet"))
|
|
{
|
|
namespace = id + ":";
|
|
} else
|
|
{
|
|
namespace = "";
|
|
}
|
|
handler.call(() -> Arrays.asList(
|
|
new StringValue(namespace + rule.name()),
|
|
new StringValue(RuleHelper.toRuleString(rule.value()))
|
|
), () -> source);
|
|
}
|
|
};
|
|
SettingsManager.registerGlobalRuleObserver((source, changedRule, userInput) -> carpetRuleChanges.handleAny(changedRule, source));
|
|
}
|
|
|
|
public static class ScarpetAppStoreValidator extends Validator<String>
|
|
{
|
|
@Override
|
|
public String validate(@Nullable CommandSourceStack source, CarpetRule<String> currentRule, String newValue, String stringInput)
|
|
{
|
|
if (newValue.equals(currentRule.value()))
|
|
{
|
|
// Don't refresh the local repo if it's the same (world change), helps preventing hitting rate limits from github when
|
|
// getting suggestions. Pending is a way to invalidate the cache when it gets old, and investigating api usage further
|
|
return newValue;
|
|
}
|
|
if (newValue.equalsIgnoreCase("none"))
|
|
{
|
|
AppStoreManager.setScarpetRepoLink(null);
|
|
return newValue;
|
|
}
|
|
if (newValue.endsWith("/"))
|
|
{
|
|
newValue = newValue.substring(0, newValue.length() - 1);
|
|
}
|
|
AppStoreManager.setScarpetRepoLink("https://api.github.com/repos/" + newValue + "/");
|
|
return newValue;
|
|
}
|
|
|
|
@Override
|
|
public String description()
|
|
{
|
|
return "Appstore link should point to a valid github repository";
|
|
}
|
|
}
|
|
}
|