Merge branch 'master' into more-throwables
This commit is contained in:
36 files changed
+610
-129
No files matched your search
@@ -42,7 +42,7 @@ import static carpet.settings.RuleCategory.CLIENT;
|
||||
@SuppressWarnings("CanBeFinal")
|
||||
public class CarpetSettings
|
||||
{
|
||||
public static final String carpetVersion = "1.4.27+v210217";
|
||||
public static final String carpetVersion = "1.4.28+v210224";
|
||||
public static final Logger LOG = LogManager.getLogger("carpet");
|
||||
public static boolean skipGenerationChecks = false;
|
||||
public static boolean impendingFillSkipUpdates = false;
|
||||
@@ -413,20 +413,26 @@ public class CarpetSettings
|
||||
|
||||
private static class ModulePermissionLevel extends Validator<String> {
|
||||
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
|
||||
CarpetSettings.runPermissionLevel = SettingsManager.getCommandLevel(newValue);
|
||||
int permissionLevel = SettingsManager.getCommandLevel(newValue);
|
||||
if (source != null && !source.hasPermissionLevel(permissionLevel))
|
||||
return null;
|
||||
CarpetSettings.runPermissionLevel = permissionLevel;
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
return newValue;
|
||||
}
|
||||
@Override
|
||||
public String description() { return "Also controls permission level of commands executed via `run()`";}
|
||||
public String description() { return "When changing the rule, you must at least have the permission level you are trying to give it";}
|
||||
}
|
||||
@Rule(
|
||||
desc = "Enables restrictions for arbitrary code execution with scarpet",
|
||||
extra = {
|
||||
"Users that don't have this permission level",
|
||||
"won't be able to load apps or /script run"
|
||||
"won't be able to load apps or /script run.",
|
||||
"It is also the permission level apps will",
|
||||
"have when running commands with run()"
|
||||
},
|
||||
category = {COMMAND, SCARPET},
|
||||
validate = ModulePermissionLevel.class
|
||||
category = {SCARPET},
|
||||
validate = {Validator._COMMAND_LEVEL_VALIDATOR.class, ModulePermissionLevel.class}
|
||||
)
|
||||
public static String commandScriptACE = "ops";
|
||||
|
||||
@@ -532,6 +538,14 @@ public class CarpetSettings
|
||||
)
|
||||
public static int maxEntityCollisions = 0;
|
||||
|
||||
@Rule(
|
||||
desc = "Customizable server list ping (Multiplayer menu) playerlist sample limit",
|
||||
options = {"0", "12", "20", "40"},
|
||||
category = CREATIVE,
|
||||
strict = false,
|
||||
validate = Validator.NONNEGATIVE_NUMBER.class
|
||||
)
|
||||
public static int pingPlayerListLimit = 12;
|
||||
/*
|
||||
|
||||
@Rule(
|
||||
@@ -860,7 +874,7 @@ public class CarpetSettings
|
||||
|
||||
@Rule(
|
||||
desc = "Lightning kills the items that drop when lightning kills an entity",
|
||||
extra = {"Setting to true will prevent lightning from killing drops", "Fixes (https://bugs.mojang.com/browse/MC-195640)."},
|
||||
extra = {"Setting to true will prevent lightning from killing drops", "Fixes [MC-206922](https://bugs.mojang.com/browse/MC-206922)."},
|
||||
category = {BUGFIX}
|
||||
)
|
||||
public static boolean lightningKillsDropsFix = false;
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec2f;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
@@ -271,7 +272,13 @@ public class PlayerCommand
|
||||
Messenger.m(context.getSource(), "rb Player name: "+playerName+" is too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
MinecraftServer server = source.getMinecraftServer();
|
||||
if (!World.isInBuildLimit(new BlockPos(pos.x, pos.y, pos.z)))
|
||||
{
|
||||
Messenger.m(context.getSource(), "rb Player "+playerName+" cannot be placed outside of the world");
|
||||
return 0;
|
||||
}
|
||||
PlayerEntity player = EntityPlayerMPFake.createFake(playerName, server, pos.x, pos.y, pos.z, facing.y, facing.x, dimType, mode);
|
||||
if (player == null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package carpet.fakes;
|
||||
|
||||
import net.minecraft.resource.ServerResourceManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
@@ -13,4 +14,5 @@ public interface MinecraftServerInterface
|
||||
void forceTick(BooleanSupplier sup);
|
||||
LevelStorage.Session getCMSession();
|
||||
Map<RegistryKey<World>, ServerWorld> getCMWorlds();
|
||||
ServerResourceManager getResourceManager();
|
||||
}
|
||||
@@ -107,20 +107,22 @@ public class OptimizedExplosion
|
||||
|
||||
explosionSound++;
|
||||
|
||||
Entity explodingEntity = eAccess.getEntity();
|
||||
for (int k2 = 0; k2 < entitylist.size(); ++k2) {
|
||||
Entity entity = entitylist.get(k2);
|
||||
|
||||
if (entity == eAccess.getEntity()) {
|
||||
|
||||
if (entity == explodingEntity) {
|
||||
// entitylist.remove(k2);
|
||||
removeFast(entitylist, k2);
|
||||
k2--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entity instanceof TntEntity &&
|
||||
entity.getX() == eAccess.getEntity().getX() &&
|
||||
entity.getY() == eAccess.getEntity().getY() &&
|
||||
entity.getZ() == eAccess.getEntity().getZ()) {
|
||||
if (entity instanceof TntEntity && explodingEntity != null &&
|
||||
entity.getX() == explodingEntity.getX() &&
|
||||
entity.getY() == explodingEntity.getY() &&
|
||||
entity.getZ() == explodingEntity.getZ()) {
|
||||
if (eLogger != null) {
|
||||
eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public abstract class MinecraftServer_pingPlayerSampleLimit
|
||||
{
|
||||
|
||||
@ModifyConstant(method = "tick", constant = @Constant(intValue = 12), require = 0, allow = 1)
|
||||
private int modifyPlayerSampleLimit(int value)
|
||||
{
|
||||
return CarpetSettings.pingPlayerListLimit;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package carpet.mixins;
|
||||
|
||||
import carpet.fakes.MinecraftServerInterface;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import net.minecraft.resource.ServerResourceManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.ServerTask;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
@@ -44,6 +45,8 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
||||
|
||||
@Shadow @Final private Map<RegistryKey<World>, ServerWorld> worlds;
|
||||
|
||||
@Shadow private ServerResourceManager serverResourceManager;
|
||||
|
||||
@Override
|
||||
public void forceTick(BooleanSupplier isAhead)
|
||||
{
|
||||
@@ -58,6 +61,11 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerResourceManager getResourceManager() {
|
||||
return serverResourceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RegistryKey<World>, ServerWorld> getCMWorlds()
|
||||
{
|
||||
|
||||
@@ -25,10 +25,4 @@ public class PlayerManager_coreMixin
|
||||
CarpetServer.onPlayerLoggedIn(player);
|
||||
PLAYER_CONNECTS.onPlayerEvent(player);
|
||||
}
|
||||
|
||||
@Inject(method = "onDataPacksReloaded", at = @At("HEAD"))
|
||||
private void onResourcesRefreshedReloadCommand(CallbackInfo ci)
|
||||
{
|
||||
CarpetServer.onReload(server);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package carpet.mixins;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.server.command.ReloadCommand;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ReloadCommand.class)
|
||||
public class ReloadCommand_reloadAppsMixin {
|
||||
//method_13530(Lcom/mojang/brigadier/context/CommandContext;)I
|
||||
// internal of register.
|
||||
@SuppressWarnings("UnresolvedMixinReference")
|
||||
@Inject(method = "method_13530", at = @At("TAIL"))
|
||||
private static void onReload(CommandContext<ServerCommandSource> context, CallbackInfoReturnable<Integer> cir)
|
||||
{
|
||||
// can't fetch here the reference to the server
|
||||
CarpetServer.onReload(context.getSource().getMinecraftServer());
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -136,7 +137,8 @@ public class CarpetScriptServer
|
||||
{
|
||||
try {
|
||||
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||
Files.createDirectories(folder);
|
||||
if (!Files.exists(folder))
|
||||
Files.createDirectories(folder);
|
||||
Optional<Path>
|
||||
scriptPath = Files.list(folder)
|
||||
.filter(script ->
|
||||
@@ -149,7 +151,8 @@ public class CarpetScriptServer
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||
{
|
||||
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
Files.createDirectories(globalFolder);
|
||||
if (!Files.exists(globalFolder))
|
||||
Files.createDirectories(globalFolder);
|
||||
scriptPath = Files.walk(globalFolder)
|
||||
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
||||
@@ -194,7 +197,8 @@ public class CarpetScriptServer
|
||||
}
|
||||
try {
|
||||
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||
Files.createDirectories(worldScripts);
|
||||
if (!Files.exists(worldScripts))
|
||||
Files.createDirectories(worldScripts);
|
||||
Files.list(worldScripts)
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
@@ -202,8 +206,9 @@ public class CarpetScriptServer
|
||||
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
||||
{
|
||||
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
Files.createDirectories(globalScripts);
|
||||
Files.walk(globalScripts)
|
||||
if (!Files.exists(globalScripts))
|
||||
Files.createDirectories(globalScripts);
|
||||
Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS)
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.FixedCommandSource;
|
||||
import carpet.script.utils.InputValidator;
|
||||
import carpet.script.utils.ScarpetJsonDeserializer;
|
||||
import carpet.script.utils.ShapeDispatcher;
|
||||
import carpet.script.utils.WorldTools;
|
||||
@@ -37,6 +38,10 @@ import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.utils.Messenger;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.command.DataCommandStorage;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -47,12 +52,18 @@ import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
import net.minecraft.resource.DataPackSettings;
|
||||
import net.minecraft.resource.ReloadableResourceManagerImpl;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ServerResourceManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
@@ -65,13 +76,27 @@ import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.dynamic.RegistryOps;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.EulerAngle;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.SaveProperties;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.source.BiomeAccess;
|
||||
import net.minecraft.world.border.WorldBorderListener;
|
||||
import net.minecraft.world.dimension.DimensionOptions;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.gen.GeneratorOptions;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.UnmodifiableLevelProperties;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
@@ -80,6 +105,13 @@ import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -98,7 +130,7 @@ import static java.lang.Math.min;
|
||||
public class Auxiliary {
|
||||
public static final String MARKER_STRING = "__scarpet_marker";
|
||||
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
|
||||
public static final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
|
||||
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
|
||||
|
||||
public static String recognizeResource(Value value, boolean isFloder)
|
||||
{
|
||||
@@ -849,7 +881,7 @@ public class Auxiliary {
|
||||
exc = e.getCause();
|
||||
throw new ThrowStatement("Failed to read JSON file: "+exc.getMessage(), Throwables.JSON_READ);
|
||||
}
|
||||
Value parsedJson = gson.fromJson(json, Value.class);
|
||||
Value parsedJson = GSON.fromJson(json, Value.class);
|
||||
if (parsedJson == null)
|
||||
retVal = Value.NULL;
|
||||
else
|
||||
@@ -888,7 +920,7 @@ public class Auxiliary {
|
||||
}
|
||||
else if (fdesc.getMiddle().equals("json"))
|
||||
{
|
||||
List<String> data = Collections.singletonList(gson.toJson(lv.get(2).evalValue(c).toJson()));
|
||||
List<String> data = Collections.singletonList(GSON.toJson(lv.get(2).evalValue(c).toJson()));
|
||||
((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle());
|
||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
|
||||
}
|
||||
@@ -1066,8 +1098,179 @@ public class Auxiliary {
|
||||
if (old_nbt == null) return LazyValue.NULL;
|
||||
return (_c, _t) -> new NBTSerializableValue(old_nbt);
|
||||
});
|
||||
|
||||
// script run create_datapack('foo', {'foo' -> {'bar.json' -> {'c' -> true,'d' -> false,'e' -> {'foo' -> [1,2,3]},'a' -> 'foobar','b' -> 5}}})
|
||||
expression.addLazyFunction("create_datapack", 2, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
String origName = lv.get(0).evalValue(c).getString();
|
||||
String name = InputValidator.validateSimpleString(origName, true);
|
||||
MinecraftServer server = cc.s.getMinecraftServer();
|
||||
for (String dpName : server.getDataPackManager().getNames())
|
||||
{
|
||||
if (dpName.equalsIgnoreCase("file/"+name+".zip") ||
|
||||
dpName.equalsIgnoreCase("file/"+name))
|
||||
return LazyValue.NULL;
|
||||
|
||||
}
|
||||
Value dpdata = lv.get(1).evalValue(c);
|
||||
if (!(dpdata instanceof MapValue))
|
||||
throw new InternalExpressionException("datapack data needs to be a valid map type");
|
||||
ResourcePackManager packManager = server.getDataPackManager();
|
||||
Path dbFloder = server.getSavePath(WorldSavePath.DATAPACKS);
|
||||
Path packFloder = dbFloder.resolve(name+".zip");
|
||||
if (Files.exists(packFloder) || Files.exists(dbFloder.resolve(name))) return LazyValue.NULL;
|
||||
Boolean [] successful = new Boolean[]{true};
|
||||
server.submitAndJoin( () ->
|
||||
{
|
||||
try {
|
||||
//Files.createDirectory(packFloder);
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(URI.create("jar:" + packFloder.toUri().toString()), ImmutableMap.of("create", "true"))) {
|
||||
Path zipRoot = zipfs.getPath("/");
|
||||
zipValueToJson(zipRoot.resolve("pack.mcmeta"), MapValue.wrap(
|
||||
ImmutableMap.of(StringValue.of("pack"), MapValue.wrap(ImmutableMap.of(
|
||||
StringValue.of("pack_format"), new NumericValue(SharedConstants.getGameVersion().getPackVersion()),
|
||||
StringValue.of("description"), StringValue.of(name),
|
||||
StringValue.of("source"), StringValue.of("scarpet")
|
||||
)))
|
||||
));
|
||||
walkTheDPMap((MapValue) dpdata, zipRoot);
|
||||
}
|
||||
packManager.scanPacks();
|
||||
ResourcePackProfile resourcePackProfile = packManager.getProfile("file/" + name + ".zip");
|
||||
if (resourcePackProfile == null || packManager.getEnabledProfiles().contains(resourcePackProfile)) {
|
||||
throw new IOException();
|
||||
}
|
||||
List<ResourcePackProfile> list = Lists.newArrayList(packManager.getEnabledProfiles());
|
||||
resourcePackProfile.getInitialPosition().insert(list, resourcePackProfile, p -> p, false);
|
||||
|
||||
|
||||
server.reloadResources(list.stream().map(ResourcePackProfile::getName).collect(Collectors.toList())).
|
||||
exceptionally(exc -> {
|
||||
successful[0] = false;
|
||||
return null;
|
||||
}).join();
|
||||
if (!successful[0]) {
|
||||
throw new IOException();
|
||||
}
|
||||
} catch (IOException e)
|
||||
{
|
||||
successful[0] = false;
|
||||
try {
|
||||
FileUtils.forceDelete(packFloder.toFile());
|
||||
} catch (IOException ignored) {
|
||||
throw new InternalExpressionException("Failed to install a datapack and failed to clean up after it");
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
return successful[0]?LazyValue.TRUE:LazyValue.FALSE;
|
||||
});
|
||||
|
||||
expression.addLazyFunction("enable_hidden_dimensions", 0, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
// from minecraft.server.Main.main
|
||||
MinecraftServer server = cc.s.getMinecraftServer();
|
||||
LevelStorage.Session session = ((MinecraftServerInterface)server).getCMSession();
|
||||
DataPackSettings dataPackSettings = session.getDataPackSettings();
|
||||
ResourcePackManager resourcePackManager = server.getDataPackManager();
|
||||
DataPackSettings dataPackSettings2 = MinecraftServer.loadDataPacks(resourcePackManager, dataPackSettings == null ? DataPackSettings.SAFE_MODE : dataPackSettings, false);
|
||||
ServerResourceManager serverRM = ((MinecraftServerInterface)server).getResourceManager();
|
||||
ReloadableResourceManagerImpl resourceManager = (ReloadableResourceManagerImpl) serverRM.getResourceManager();
|
||||
|
||||
//believe the other one will fillup based on the datapacks only.
|
||||
resourceManager.close();
|
||||
resourcePackManager.createResourcePacks().forEach(resourceManager::addPack);
|
||||
|
||||
//not sure its needed, but doesn't seem to have a negative effect and might be used in some custom shtuff
|
||||
serverRM.loadRegistryTags();
|
||||
|
||||
RegistryOps<Tag> registryOps = RegistryOps.of(NbtOps.INSTANCE, serverRM.getResourceManager(), (DynamicRegistryManager.Impl) server.getRegistryManager());
|
||||
SaveProperties saveProperties = session.readLevelProperties(registryOps, dataPackSettings2);
|
||||
if (saveProperties == null) return LazyValue.NULL;
|
||||
//session.backupLevelDataFile(server.getRegistryManager(), saveProperties); // no need
|
||||
|
||||
// MinecraftServer.createWorlds
|
||||
// save properties should now contain dimension settings
|
||||
GeneratorOptions generatorOptions = saveProperties.getGeneratorOptions();
|
||||
boolean bl = generatorOptions.isDebugWorld();
|
||||
long l = generatorOptions.getSeed();
|
||||
long m = BiomeAccess.hashSeed(l);
|
||||
Map<RegistryKey<World>, ServerWorld> existing_worlds = ((MinecraftServerInterface)server).getCMWorlds();
|
||||
List<Value> addeds = new ArrayList<>();
|
||||
for (Map.Entry<RegistryKey<DimensionOptions>, DimensionOptions> entry : generatorOptions.getDimensions().getEntries()) {
|
||||
RegistryKey<DimensionOptions> registryKey = entry.getKey();
|
||||
if (!existing_worlds.containsKey(registryKey))
|
||||
{
|
||||
addeds.add(ValueConversions.of(registryKey.getValue()));
|
||||
RegistryKey<World> registryKey2 = RegistryKey.of(Registry.DIMENSION, registryKey.getValue());
|
||||
DimensionType dimensionType3 = entry.getValue().getDimensionType();
|
||||
ChunkGenerator chunkGenerator3 = entry.getValue().getChunkGenerator();
|
||||
UnmodifiableLevelProperties unmodifiableLevelProperties = new UnmodifiableLevelProperties(saveProperties, saveProperties.getMainWorldProperties());
|
||||
ServerWorld serverWorld2 = new ServerWorld(server, Util.getMainWorkerExecutor(), session, unmodifiableLevelProperties, registryKey2, dimensionType3, WorldTools.NOOP_LISTENER, chunkGenerator3, bl, m, ImmutableList.of(), false);
|
||||
server.getOverworld().getWorldBorder().addListener(new WorldBorderListener.WorldBorderSyncer(serverWorld2.getWorldBorder()));
|
||||
existing_worlds.put(registryKey2, serverWorld2);
|
||||
}
|
||||
}
|
||||
Value ret = ListValue.wrap(addeds);
|
||||
return (_c, _t) -> ret;
|
||||
});
|
||||
}
|
||||
|
||||
private static void zipValueToJson(Path path, Value output) throws IOException
|
||||
{
|
||||
JsonElement element = output.toJson();
|
||||
if (element == null)
|
||||
throw new InternalExpressionException("Cannot interpret "+output.getPrettyString()+" as a json object");
|
||||
String string = GSON.toJson(element);
|
||||
Files.createDirectories(path.getParent());
|
||||
BufferedWriter bufferedWriter = Files.newBufferedWriter(path);
|
||||
Throwable incident = null;
|
||||
try
|
||||
{
|
||||
bufferedWriter.write(string);
|
||||
}
|
||||
catch (Throwable shitHappened)
|
||||
{
|
||||
incident = shitHappened;
|
||||
throw shitHappened;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (incident != null) {
|
||||
try {
|
||||
bufferedWriter.close();
|
||||
} catch (Throwable otherShitHappened) {
|
||||
incident.addSuppressed(otherShitHappened);
|
||||
}
|
||||
} else {
|
||||
bufferedWriter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void walkTheDPMap(MapValue node, Path path) throws IOException
|
||||
{
|
||||
Map<Value,Value> items = node.getMap();
|
||||
for (Map.Entry<Value, Value> entry : items.entrySet())
|
||||
{
|
||||
Value val = entry.getValue();
|
||||
String strkey = entry.getKey().getString();
|
||||
Path child = path.resolve(strkey);
|
||||
if (strkey.endsWith(".json"))
|
||||
{
|
||||
zipValueToJson(child, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(val instanceof MapValue)) throw new InternalExpressionException("Value of "+strkey+" should be a map");
|
||||
Files.createDirectory(child);
|
||||
walkTheDPMap((MapValue) val, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
|
||||
{
|
||||
T key = type.getRegistry().get(id);
|
||||
|
||||
@@ -93,7 +93,7 @@ public class Inventories {
|
||||
Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem();
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
Value ret = ListValue.wrap(tagManager.getItems().getTagsFor(item).stream().map(ValueConversions::of).collect(Collectors.toList()));
|
||||
Value ret = ListValue.wrap(tagManager.getItems().getTags().entrySet().stream().filter(e -> e.getValue().contains(item)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
String tag = lv.get(1).evalValue(c).getString();
|
||||
|
||||
@@ -1127,7 +1127,8 @@ public class WorldAccess {
|
||||
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, 0, true);
|
||||
if (blockLocator.offset == lv.size())
|
||||
{
|
||||
Value ret = ListValue.wrap(tagManager.getBlocks().getTagsFor(blockLocator.block.getBlockState().getBlock()).stream().map(ValueConversions::of).collect(Collectors.toList()));
|
||||
Block target = blockLocator.block.getBlockState().getBlock();
|
||||
Value ret = ListValue.wrap(tagManager.getBlocks().getTags().entrySet().stream().filter(e -> e.getValue().contains(target)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
String tag = lv.get(blockLocator.offset).evalValue(c).getString();
|
||||
@@ -1381,6 +1382,7 @@ public class WorldAccess {
|
||||
{
|
||||
if (lv.size() == 0) throw new InternalExpressionException("'custom_dimension' requires at least one argument");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
cc.host.issueDeprecation("custom_dimension()");
|
||||
String worldKey = lv.get(0).evalValue(c).getString();
|
||||
|
||||
Long seed = null;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class Sys {
|
||||
return (cc, tt) -> Value.FALSE;
|
||||
}
|
||||
}
|
||||
Value retval = new NumericValue(v.getBoolean());
|
||||
Value retval = v.getBoolean()?Value.TRUE:Value.FALSE;
|
||||
return (cc, tt) -> retval;
|
||||
});
|
||||
|
||||
@@ -56,7 +56,9 @@ public class Sys {
|
||||
{
|
||||
if (v instanceof NumericValue)
|
||||
{
|
||||
return v;
|
||||
NumericValue num = (NumericValue)v;
|
||||
if (num.isInteger()) return new NumericValue(num.getLong());
|
||||
return new NumericValue(num.getDouble());
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package carpet.script.utils;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class InputValidator {
|
||||
public static String validateSimpleString(String input, boolean strict)
|
||||
{
|
||||
String simplified = input.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9+_]", "");
|
||||
if (simplified.isEmpty() || (strict && !simplified.equals(input)))
|
||||
throw new InternalExpressionException("simple name can only contain numbers, letter and _");
|
||||
return simplified;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import carpet.script.value.MapValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.settings.ParsedRule;
|
||||
import carpet.settings.SettingsManager;
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
@@ -49,6 +50,7 @@ public class SystemInfo {
|
||||
String tlf = serverPath.getName(nodeCount-2).toString();
|
||||
return StringValue.of(tlf);
|
||||
});
|
||||
put("world_dimensions", c -> ListValue.wrap(c.s.getMinecraftServer().getWorldRegistryKeys().stream().map(k -> ValueConversions.of(k.getValue())).collect(Collectors.toList())));
|
||||
put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName()));
|
||||
put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore()));
|
||||
put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion())));
|
||||
|
||||
@@ -157,5 +157,5 @@ public class WorldTools
|
||||
@Override public void stop() { }
|
||||
}
|
||||
|
||||
private static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||
public static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.nbt.ByteTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public class BooleanValue extends NumericValue
|
||||
{
|
||||
public static final BooleanValue FALSE = new BooleanValue(false);
|
||||
public static final BooleanValue TRUE = new BooleanValue(true);
|
||||
|
||||
boolean boolValue;
|
||||
private BooleanValue(boolean boolval) {
|
||||
super(boolval);
|
||||
boolValue = boolval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return boolValue?"true":"false";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString() {
|
||||
return getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeString() {
|
||||
return "bool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
return new BooleanValue(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Boolean.hashCode(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force) {
|
||||
return ByteTag.of(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson() {
|
||||
return new JsonPrimitive(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInteger() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ public class EntityValue extends Value
|
||||
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
|
||||
put("on_ground", (e, a) -> new NumericValue(e.isOnGround()));
|
||||
put("name", (e, a) -> new StringValue(e.getName().getString()));
|
||||
put("display_name", (e, a) -> new StringValue(e.getDisplayName().getString()));
|
||||
put("display_name", (e, a) -> new FormattedTextValue(e.getDisplayName()));
|
||||
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
|
||||
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
|
||||
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
|
||||
@@ -429,7 +429,7 @@ public class EntityValue extends Value
|
||||
put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
|
||||
|
||||
put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
|
||||
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTagsFor(e.getType()).stream().map(ValueConversions::of).collect(Collectors.toList())));
|
||||
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTags().entrySet().stream().filter(entry -> entry.getValue().contains(e.getType())).map(entry -> ValueConversions.of(entry.getKey())).collect(Collectors.toList())));
|
||||
// deprecated
|
||||
put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
|
||||
|
||||
@@ -462,7 +462,7 @@ public class EntityValue extends Value
|
||||
return ListValue.of(new NumericValue(look.x),new NumericValue(look.y),new NumericValue(look.z));
|
||||
});
|
||||
put("is_burning", (e, a) -> new NumericValue(e.isOnFire()));
|
||||
//put("fire", (e, a) -> new NumericValue(e.getFire())); needs mixing
|
||||
put("fire", (e, a) -> new NumericValue(e.getFireTicks()));
|
||||
put("silent", (e, a)-> new NumericValue(e.isSilent()));
|
||||
put("gravity", (e, a) -> new NumericValue(!e.hasNoGravity()));
|
||||
put("immune_to_fire", (e, a) -> new NumericValue(e.isFireImmune()));
|
||||
|
||||
@@ -341,7 +341,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
int from = normalizeIndex(fromDesc, size);
|
||||
if (toDesc == null)
|
||||
return new ListValue((Collection<? extends Value>) getItems().subList(from, size));
|
||||
int to = normalizeIndex(toDesc, size);
|
||||
int to = normalizeIndex(toDesc, size+1);
|
||||
if (from > to)
|
||||
return ListValue.of();
|
||||
return new ListValue((Collection<? extends Value>) getItems().subList(from, to));
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -16,7 +17,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class MapValue extends AbstractListValue implements ContainerValueInterface
|
||||
{
|
||||
private Map<Value, Value> map;
|
||||
private final Map<Value, Value> map;
|
||||
|
||||
private MapValue()
|
||||
{
|
||||
@@ -268,7 +269,9 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonObject jsonMap = new JsonObject();
|
||||
map.forEach((k, v) -> jsonMap.add(k.getString(), v.toJson()));
|
||||
List<Value> keys = new ArrayList<>(map.keySet());
|
||||
Collections.sort(keys);
|
||||
keys.forEach( k -> jsonMap.add(k.getString(), map.get(k).toJson()));
|
||||
return jsonMap;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.nbt.Tag;
|
||||
|
||||
public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
public static final NullValue NULL = new NullValue();
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
@@ -32,7 +33,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
return new NullValue();
|
||||
}
|
||||
public NullValue() {super(0.0D);}
|
||||
private NullValue() {super(false);}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
|
||||
@@ -15,12 +15,12 @@ import java.util.stream.Collectors;
|
||||
|
||||
public abstract class Value implements Comparable<Value>, Cloneable
|
||||
{
|
||||
public static NumericValue FALSE = new NumericValue(0);
|
||||
public static NumericValue TRUE = new NumericValue(1);
|
||||
public static NumericValue ZERO = FALSE;
|
||||
public static NumericValue ONE = TRUE;
|
||||
public static NumericValue FALSE = BooleanValue.FALSE;
|
||||
public static NumericValue TRUE = BooleanValue.TRUE;
|
||||
public static NumericValue ZERO = new NumericValue(0);
|
||||
public static NumericValue ONE = new NumericValue(1);
|
||||
|
||||
public static NullValue NULL = new NullValue();
|
||||
public static NullValue NULL = NullValue.NULL;
|
||||
|
||||
public String boundVariable;
|
||||
|
||||
@@ -186,7 +186,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
int size = value.length();
|
||||
int from = ListValue.normalizeIndex(fromDesc, size);
|
||||
if (toDesc == null) return new StringValue(value.substring(from));
|
||||
int to = ListValue.normalizeIndex(toDesc, size);
|
||||
int to = ListValue.normalizeIndex(toDesc, size+1);
|
||||
if (from > to) return StringValue.EMPTY;
|
||||
return new StringValue(value.substring(from, to));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__command() ->
|
||||
_command() ->
|
||||
(
|
||||
print('camera: scarpet app.');
|
||||
print('-------------------');
|
||||
@@ -11,7 +11,7 @@ __command() ->
|
||||
print(' "/camera place_player - Move player to the selected point');
|
||||
print(' "/camera move" - Move the selected point to players location');
|
||||
print(' "/camera duration <X>" - Set new selected path duration');
|
||||
print(' "/camera split_point>" - Split selected path in half.');
|
||||
print(' "/camera split_point" - Split selected path in half.');
|
||||
print(' "/camera delete_point" - Remove current key point');
|
||||
print(' "/camera trim_path" - Remove all key points from selected up');
|
||||
print('');
|
||||
@@ -57,6 +57,47 @@ __command() ->
|
||||
print('');
|
||||
null
|
||||
);
|
||||
__config() ->{
|
||||
'commands'->{
|
||||
''->'_command',
|
||||
'<command>'->'_call',
|
||||
'add <seconds>'->'add',
|
||||
'prepend <seconds>'->'prepend',
|
||||
'duration <seconds>' -> 'duration',
|
||||
'save_as <name>'->'save_as',
|
||||
'load <name>'->'load',
|
||||
'interpolation <interpolation>'->['__interpolation',true],
|
||||
'interpolation gauss'->['__interpolation','gauss',true],
|
||||
'interpolation gauss <float>'->_(float)->(__interpolation('gauss_'+str(float),true)),
|
||||
'repeat <seconds> <last_delay>'->'repeat',
|
||||
'stretch <factor>'->'stretch'
|
||||
},
|
||||
'arguments'->{
|
||||
'seconds'->{'type'->'int','suggest'->[]},
|
||||
'last_delay'->{'type'->'int','suggest'->[]},
|
||||
'name'->{'type'->'string','suggest'->[]},
|
||||
'interpolation'->{'type'->'term','options'->['linear','catmull_rom']},
|
||||
'factor'->{'type'->'int','min'->25,'max'->400},
|
||||
'command'->{'type'->'term','options'->[
|
||||
'start',
|
||||
'clear',
|
||||
'select',
|
||||
'place_player',
|
||||
'move',
|
||||
'split_point',
|
||||
'delete_point',
|
||||
'trim_path',
|
||||
'transpose',
|
||||
'play',
|
||||
'show',
|
||||
'hide',
|
||||
'prefer_smooth_play',
|
||||
'prefer_synced_play'
|
||||
]}
|
||||
}
|
||||
};
|
||||
|
||||
_call(command)->call(command);
|
||||
|
||||
global_points = null;
|
||||
global_dimension = null;
|
||||
@@ -150,7 +191,6 @@ __assert_point_selected(validator) ->
|
||||
);
|
||||
|
||||
//select a custom interpolation method
|
||||
interpolation(method) -> __interpolation(method, true);
|
||||
__interpolation(method, verbose) ->
|
||||
(
|
||||
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
|
||||
@@ -159,7 +199,7 @@ __interpolation(method, verbose) ->
|
||||
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
|
||||
global_interpolator = if (
|
||||
method == 'linear', '__interpolator_linear',
|
||||
method == 'cr', '__interpolator_cr',
|
||||
method == 'catmull_rom', '__interpolator_cr',
|
||||
method == 'gauss', _(s, p) -> __interpolator_gauB(s, p, 0),
|
||||
method ~ '^gauss_',
|
||||
(
|
||||
@@ -167,13 +207,12 @@ __interpolation(method, verbose) ->
|
||||
type = replace(type,'_','.');
|
||||
variance = round(60*number(type));
|
||||
_(s, p, outer(variance)) -> __interpolator_gauB(s, p, variance);
|
||||
),
|
||||
exit('Choose one of the following methods: linear, gauss, gauss_<deviation>, cr')
|
||||
)
|
||||
);
|
||||
__update();
|
||||
if(verbose, 'Interpolation changed to '+method, '');
|
||||
);
|
||||
__interpolation('cr', false);
|
||||
__interpolation('catmull_rom', false);
|
||||
|
||||
// adds a point to the end of the path with delay in seconds
|
||||
add(delay) ->
|
||||
@@ -216,9 +255,6 @@ repeat(times, last_section_delay) ->
|
||||
stretch(percentage) ->
|
||||
(
|
||||
if (err = __is_not_valid_for_motion(), exit(err));
|
||||
if (percentage < 25 || percentage > 400,
|
||||
exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes')
|
||||
);
|
||||
ratio = percentage/100;
|
||||
previous_path_length = global_points:(-1):1;
|
||||
for(global_points, _:1 = _:1*ratio );
|
||||
@@ -660,4 +696,4 @@ __on_close() ->
|
||||
(
|
||||
global_playing_path = false;
|
||||
global_showing_path = false;
|
||||
);
|
||||
);
|
||||
@@ -24,6 +24,7 @@
|
||||
"ServerPlayNetworkHandler_interactionUpdatesMixin",
|
||||
|
||||
"ServerMetadata_motdMixin",
|
||||
"MinecraftServer_pingPlayerSampleLimit",
|
||||
|
||||
"MinecraftServer_coreMixin",
|
||||
|
||||
@@ -175,6 +176,7 @@
|
||||
"PointOfInterest_scarpetMixin",
|
||||
"ServerWorld_scarpetMixin",
|
||||
"HungerManagerMixin_scarpetEventsMixin",
|
||||
"ReloadCommand_reloadAppsMixin",
|
||||
|
||||
"RecipeManager_scarpetMixin",
|
||||
"Ingredient_scarpetMixin",
|
||||
|
||||
Reference in new issue
Block a user