Search in sources :

Example 1 with ServerWorldInterface

use of carpet.fakes.ServerWorldInterface in project fabric-carpet by gnembon.

the class Auxiliary method apply.

public static void apply(Expression expression) {
    expression.addContextFunction("sound", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        if (lv.size() == 0) {
            return ListValue.wrap(Registry.SOUND_EVENT.keySet().stream().map(ValueConversions::of));
        }
        String rawString = lv.get(0).getString();
        ResourceLocation soundName = InputValidator.identifierOf(rawString);
        Vector3Argument locator = Vector3Argument.findIn(lv, 1);
        if (Registry.SOUND_EVENT.get(soundName) == null)
            throw new ThrowStatement(rawString, Throwables.UNKNOWN_SOUND);
        float volume = 1.0F;
        float pitch = 1.0F;
        SoundSource mixer = SoundSource.MASTER;
        if (lv.size() > 0 + locator.offset) {
            volume = (float) NumericValue.asNumber(lv.get(0 + locator.offset)).getDouble();
            if (lv.size() > 1 + locator.offset) {
                pitch = (float) NumericValue.asNumber(lv.get(1 + locator.offset)).getDouble();
                if (lv.size() > 2 + locator.offset) {
                    String mixerName = lv.get(2 + locator.offset).getString();
                    mixer = mixerMap.get(mixerName.toLowerCase(Locale.ROOT));
                    if (mixer == null)
                        throw new InternalExpressionException(mixerName + " is not a valid mixer name");
                }
            }
        }
        Vec3 vec = locator.vec;
        double d0 = Math.pow(volume > 1.0F ? (double) (volume * 16.0F) : 16.0D, 2.0D);
        int count = 0;
        for (ServerPlayer player : cc.s.getLevel().getPlayers((p) -> p.distanceToSqr(vec) < d0)) {
            count++;
            player.connection.send(new ClientboundCustomSoundPacket(soundName, mixer, vec, volume, pitch));
        }
        return new NumericValue(count);
    });
    expression.addContextFunction("particle", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        if (lv.size() == 0)
            return ListValue.wrap(Registry.PARTICLE_TYPE.keySet().stream().map(ValueConversions::of));
        MinecraftServer ms = cc.s.getServer();
        ServerLevel world = cc.s.getLevel();
        Vector3Argument locator = Vector3Argument.findIn(lv, 1);
        String particleName = lv.get(0).getString();
        int count = 10;
        double speed = 0;
        float spread = 0.5f;
        ServerPlayer player = null;
        if (lv.size() > locator.offset) {
            count = (int) NumericValue.asNumber(lv.get(locator.offset)).getLong();
            if (lv.size() > 1 + locator.offset) {
                spread = (float) NumericValue.asNumber(lv.get(1 + locator.offset)).getDouble();
                if (lv.size() > 2 + locator.offset) {
                    speed = NumericValue.asNumber(lv.get(2 + locator.offset)).getDouble();
                    if (// should accept entity as well as long as it is player
                    lv.size() > 3 + locator.offset) {
                        player = ms.getPlayerList().getPlayerByName(lv.get(3 + locator.offset).getString());
                    }
                }
            }
        }
        ParticleOptions particle = ShapeDispatcher.getParticleData(particleName);
        Vec3 vec = locator.vec;
        if (player == null) {
            for (Player p : (world.players())) {
                world.sendParticles((ServerPlayer) p, particle, true, vec.x, vec.y, vec.z, count, spread, spread, spread, speed);
            }
        } else {
            world.sendParticles(player, particle, true, vec.x, vec.y, vec.z, count, spread, spread, spread, speed);
        }
        return Value.TRUE;
    });
    expression.addContextFunction("particle_line", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        ServerLevel world = cc.s.getLevel();
        String particleName = lv.get(0).getString();
        ParticleOptions particle = ShapeDispatcher.getParticleData(particleName);
        Vector3Argument pos1 = Vector3Argument.findIn(lv, 1);
        Vector3Argument pos2 = Vector3Argument.findIn(lv, pos1.offset);
        double density = 1.0;
        ServerPlayer player = null;
        if (lv.size() > pos2.offset + 0) {
            density = NumericValue.asNumber(lv.get(pos2.offset + 0)).getDouble();
            if (density <= 0) {
                throw new InternalExpressionException("Particle density should be positive");
            }
            if (lv.size() > pos2.offset + 1) {
                Value playerValue = lv.get(pos2.offset + 1);
                if (playerValue instanceof EntityValue) {
                    Entity e = ((EntityValue) playerValue).getEntity();
                    if (!(e instanceof ServerPlayer))
                        throw new InternalExpressionException("'particle_line' player argument has to be a player");
                    player = (ServerPlayer) e;
                } else {
                    player = cc.s.getServer().getPlayerList().getPlayerByName(playerValue.getString());
                }
            }
        }
        return new NumericValue(ShapeDispatcher.drawParticleLine((player == null) ? world.players() : Collections.singletonList(player), particle, pos1.vec, pos2.vec, density));
    });
    expression.addContextFunction("particle_box", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        ServerLevel world = cc.s.getLevel();
        String particleName = lv.get(0).getString();
        ParticleOptions particle = ShapeDispatcher.getParticleData(particleName);
        Vector3Argument pos1 = Vector3Argument.findIn(lv, 1);
        Vector3Argument pos2 = Vector3Argument.findIn(lv, pos1.offset);
        double density = 1.0;
        ServerPlayer player = null;
        if (lv.size() > pos2.offset + 0) {
            density = NumericValue.asNumber(lv.get(pos2.offset + 0)).getDouble();
            if (density <= 0) {
                throw new InternalExpressionException("Particle density should be positive");
            }
            if (lv.size() > pos2.offset + 1) {
                Value playerValue = lv.get(pos2.offset + 1);
                if (playerValue instanceof EntityValue) {
                    Entity e = ((EntityValue) playerValue).getEntity();
                    if (!(e instanceof ServerPlayer))
                        throw new InternalExpressionException("'particle_box' player argument has to be a player");
                    player = (ServerPlayer) e;
                } else {
                    player = cc.s.getServer().getPlayerList().getPlayerByName(playerValue.getString());
                }
            }
        }
        Vec3 a = pos1.vec;
        Vec3 b = pos2.vec;
        Vec3 from = new Vec3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
        Vec3 to = new Vec3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
        int particleCount = ShapeDispatcher.Box.particleMesh(player == null ? world.players() : Collections.singletonList(player), particle, density, from, to);
        return new NumericValue(particleCount);
    });
    // deprecated
    expression.alias("particle_rect", "particle_box");
    expression.addContextFunction("draw_shape", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        ServerLevel world = cc.s.getLevel();
        MinecraftServer server = world.getServer();
        Set<ServerPlayer> playerTargets = new HashSet<>();
        List<ShapeDispatcher.ShapeWithConfig> shapes = new ArrayList<>();
        if (// bulk
        lv.size() == 1) {
            Value specLoad = lv.get(0);
            if (!(specLoad instanceof ListValue))
                throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs");
            for (Value list : ((ListValue) specLoad).getItems()) {
                if (!(list instanceof ListValue))
                    throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs");
                shapes.add(ShapeDispatcher.fromFunctionArgs(server, world, ((ListValue) list).getItems(), playerTargets));
            }
        } else {
            shapes.add(ShapeDispatcher.fromFunctionArgs(server, world, lv, playerTargets));
        }
        ShapeDispatcher.sendShape((playerTargets.isEmpty()) ? cc.s.getLevel().players() : playerTargets, shapes);
        return Value.TRUE;
    });
    expression.addContextFunction("create_marker", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        BlockState targetBlock = null;
        Vector3Argument pointLocator;
        boolean interactable = true;
        String name;
        try {
            Value nameValue = lv.get(0);
            name = nameValue.isNull() ? "" : nameValue.getString();
            pointLocator = Vector3Argument.findIn(lv, 1, true, false);
            if (lv.size() > pointLocator.offset) {
                BlockArgument blockLocator = BlockArgument.findIn(cc, lv, pointLocator.offset, true, true, false);
                if (blockLocator.block != null)
                    targetBlock = blockLocator.block.getBlockState();
                if (lv.size() > blockLocator.offset) {
                    interactable = lv.get(blockLocator.offset).getBoolean();
                }
            }
        } catch (IndexOutOfBoundsException e) {
            throw new InternalExpressionException("'create_marker' requires a name and three coordinates, with optional direction, and optional block on its head");
        }
        ArmorStand armorstand = new ArmorStand(EntityType.ARMOR_STAND, cc.s.getLevel());
        double yoffset;
        if (targetBlock == null && name.isEmpty()) {
            yoffset = 0.0;
        } else if (!interactable && targetBlock == null) {
            yoffset = -0.41;
        } else {
            if (targetBlock == null) {
                yoffset = -armorstand.getBbHeight() - 0.41;
            } else {
                yoffset = -armorstand.getBbHeight() + 0.3;
            }
        }
        armorstand.moveTo(pointLocator.vec.x, // pointLocator.vec.y - ((!interactable && targetBlock == null)?0.41f:((targetBlock==null)?(armorstand.getHeight()+0.41):(armorstand.getHeight()-0.3))),
        pointLocator.vec.y + yoffset, pointLocator.vec.z, (float) pointLocator.yaw, (float) pointLocator.pitch);
        armorstand.addTag(MARKER_STRING + "_" + ((cc.host.getName() == null) ? "" : cc.host.getName()));
        armorstand.addTag(MARKER_STRING);
        if (targetBlock != null)
            armorstand.setItemSlot(EquipmentSlot.HEAD, new ItemStack(targetBlock.getBlock().asItem()));
        if (!name.isEmpty()) {
            armorstand.setCustomName(new TextComponent(name));
            armorstand.setCustomNameVisible(true);
        }
        armorstand.setHeadPose(new Rotations((int) pointLocator.pitch, 0, 0));
        armorstand.setNoGravity(true);
        armorstand.setInvisible(true);
        armorstand.setInvulnerable(true);
        armorstand.getEntityData().set(ArmorStand.DATA_CLIENT_FLAGS, (byte) (interactable ? 8 : 16 | 8));
        cc.s.getLevel().addFreshEntity(armorstand);
        return new EntityValue(armorstand);
    });
    expression.addContextFunction("remove_all_markers", 0, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        int total = 0;
        String markerName = MARKER_STRING + "_" + ((cc.host.getName() == null) ? "" : cc.host.getName());
        for (Entity e : cc.s.getLevel().getEntities(EntityType.ARMOR_STAND, (as) -> as.getTags().contains(markerName))) {
            total++;
            // discard // remove();
            e.discard();
        }
        return new NumericValue(total);
    });
    expression.addUnaryFunction("nbt", NBTSerializableValue::fromValue);
    expression.addUnaryFunction("escape_nbt", v -> new StringValue(StringTag.quoteAndEscape(v.getString())));
    expression.addUnaryFunction("parse_nbt", v -> {
        if (v instanceof NBTSerializableValue)
            return ((NBTSerializableValue) v).toValue();
        NBTSerializableValue ret = NBTSerializableValue.parseString(v.getString(), false);
        if (ret == null)
            return Value.NULL;
        return ret.toValue();
    });
    expression.addFunction("tag_matches", (lv) -> {
        int numParam = lv.size();
        if (numParam != 2 && numParam != 3)
            throw new InternalExpressionException("'tag_matches' requires 2 or 3 arguments");
        if (lv.get(1).isNull())
            return Value.TRUE;
        if (lv.get(0).isNull())
            return Value.FALSE;
        Tag source = ((NBTSerializableValue) (NBTSerializableValue.fromValue(lv.get(0)))).getTag();
        Tag match = ((NBTSerializableValue) (NBTSerializableValue.fromValue(lv.get(1)))).getTag();
        return BooleanValue.of(NbtUtils.compareNbt(match, source, numParam == 2 || lv.get(2).getBoolean()));
    });
    expression.addFunction("encode_nbt", lv -> {
        int argSize = lv.size();
        if (argSize == 0 || argSize > 2)
            throw new InternalExpressionException("'encode_nbt' requires 1 or 2 parameters");
        Value v = lv.get(0);
        boolean force = (argSize > 1) && lv.get(1).getBoolean();
        Tag tag;
        try {
            tag = v.toTag(force);
        } catch (NBTSerializableValue.IncompatibleTypeException ignored) {
            throw new InternalExpressionException("cannot reliably encode to a tag the value of '" + ignored.val.getPrettyString() + "'");
        }
        return new NBTSerializableValue(tag);
    });
    // "overridden" native call that prints to stderr
    expression.addContextFunction("print", -1, (c, t, lv) -> {
        if (lv.size() == 0 || lv.size() > 2)
            throw new InternalExpressionException("'print' takes one or two arguments");
        CommandSourceStack s = ((CarpetContext) c).s;
        MinecraftServer server = s.getServer();
        Value res = lv.get(0);
        List<ServerPlayer> targets = null;
        if (lv.size() == 2) {
            List<Value> playerValues = (res instanceof ListValue) ? ((ListValue) res).getItems() : Collections.singletonList(res);
            List<ServerPlayer> playerTargets = new ArrayList<>();
            playerValues.forEach(pv -> {
                ServerPlayer player = EntityValue.getPlayerByValue(server, pv);
                if (player == null)
                    throw new InternalExpressionException("Cannot target player " + pv.getString() + " in print");
                playerTargets.add(player);
            });
            targets = playerTargets;
            res = lv.get(1);
        }
        Component message = FormattedTextValue.getTextByValue(res);
        if (targets == null) {
            s.sendSuccess(message, false);
        } else {
            targets.forEach(p -> p.createCommandSourceStack().sendSuccess(message, false));
        }
        // pass through for variables
        return res;
    });
    expression.addContextFunction("display_title", -1, (c, t, lv) -> {
        if (lv.size() < 2)
            throw new InternalExpressionException("'display_title' needs at least a target, type and message, and optionally times");
        Value pVal = lv.get(0);
        if (!(pVal instanceof ListValue))
            pVal = ListValue.of(pVal);
        MinecraftServer server = ((CarpetContext) c).s.getServer();
        Stream<ServerPlayer> targets = ((ListValue) pVal).getItems().stream().map(v -> {
            ServerPlayer player = EntityValue.getPlayerByValue(server, v);
            if (player == null)
                throw new InternalExpressionException("'display_title' requires a valid online player or a list of players as first argument. " + v.getString() + " is not a player.");
            return player;
        });
        Function<Component, Packet<?>> packetGetter = null;
        // TitleS2CPacket.Action action;
        String actionString = lv.get(1).getString().toLowerCase(Locale.ROOT);
        switch(actionString) {
            case "title":
                packetGetter = ClientboundSetTitleTextPacket::new;
                // action = Action.TITLE;
                if (lv.size() < 3)
                    throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type");
                break;
            case "subtitle":
                packetGetter = ClientboundSetSubtitleTextPacket::new;
                if (lv.size() < 3)
                    throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type");
                // action = Action.SUBTITLE;
                break;
            case "actionbar":
                packetGetter = ClientboundSetActionBarTextPacket::new;
                if (lv.size() < 3)
                    throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type");
                // action = Action.ACTIONBAR;
                break;
            case "clear":
                // resetting default fade
                packetGetter = (x) -> new ClientboundClearTitlesPacket(true);
                // action = Action.CLEAR;
                break;
            case "player_list_header":
            case "player_list_footer":
                break;
            default:
                throw new InternalExpressionException("'display_title' requires 'title', 'subtitle', 'actionbar', 'player_list_header', 'player_list_footer' or 'clear' as second argument");
        }
        // if (action != Action.CLEAR && lv.size() < 3)
        // throw new InternalExpressionException("Third argument of 'display_title' must be present except for 'clear' type");
        Component title;
        boolean soundsTrue = false;
        if (lv.size() > 2) {
            pVal = lv.get(2);
            title = FormattedTextValue.getTextByValue(pVal);
            soundsTrue = pVal.getBoolean();
        } else
            // Will never happen, just to make lambda happy
            title = null;
        if (packetGetter == null) {
            Map<String, BaseComponent> map;
            if (actionString.equals("player_list_header"))
                map = HUDController.scarpet_headers;
            else
                map = HUDController.scarpet_footers;
            AtomicInteger total = new AtomicInteger(0);
            List<ServerPlayer> targetList = targets.collect(Collectors.toList());
            if (// null or empty string
            !soundsTrue)
                targetList.forEach(target -> {
                    map.remove(target.getScoreboardName());
                    total.getAndIncrement();
                });
            else
                targetList.forEach(target -> {
                    map.put(target.getScoreboardName(), (BaseComponent) title);
                    total.getAndIncrement();
                });
            HUDController.update_hud(((CarpetContext) c).s.getServer(), targetList);
            return NumericValue.of(total.get());
        }
        // TimesPacket
        ClientboundSetTitlesAnimationPacket timesPacket;
        if (lv.size() > 3) {
            if (lv.size() != 6)
                throw new InternalExpressionException("'display_title' needs all fade-in, stay and fade-out times");
            int in = NumericValue.asNumber(lv.get(3), "fade in for display_title").getInt();
            int stay = NumericValue.asNumber(lv.get(4), "stay for display_title").getInt();
            int out = NumericValue.asNumber(lv.get(5), "fade out for display_title").getInt();
            timesPacket = new ClientboundSetTitlesAnimationPacket(in, stay, out);
        // timesPacket = new TitleS2CPacket(Action.TIMES, null, in, stay, out);
        } else
            timesPacket = null;
        Packet<?> packet = packetGetter.apply(title);
        // TitleS2CPacket packet = new TitleS2CPacket(action, title);
        AtomicInteger total = new AtomicInteger(0);
        targets.forEach(p -> {
            if (timesPacket != null)
                p.connection.send(timesPacket);
            p.connection.send(packet);
            total.getAndIncrement();
        });
        return NumericValue.of(total.get());
    });
    expression.addFunction("format", values -> {
        if (values.size() == 0)
            throw new InternalExpressionException("'format' requires at least one component");
        if (values.get(0) instanceof ListValue && values.size() == 1)
            values = ((ListValue) values.get(0)).getItems();
        return new FormattedTextValue(Messenger.c(values.stream().map(Value::getString).toArray()));
    });
    expression.addContextFunction("run", 1, (c, t, lv) -> {
        CommandSourceStack s = ((CarpetContext) c).s;
        try {
            Component[] error = { null };
            List<Component> output = new ArrayList<>();
            Value retval = new NumericValue(s.getServer().getCommands().performCommand(new SnoopyCommandSource(s, error, output), lv.get(0).getString()));
            return ListValue.of(retval, ListValue.wrap(output.stream().map(FormattedTextValue::new).collect(Collectors.toList())), FormattedTextValue.of(error[0]));
        } catch (Exception exc) {
            return ListValue.of(Value.NULL, ListValue.of(), new FormattedTextValue(new TextComponent(exc.getMessage())));
        }
    });
    expression.addContextFunction("save", 0, (c, t, lv) -> {
        CommandSourceStack s = ((CarpetContext) c).s;
        s.getServer().getPlayerList().saveAll();
        s.getServer().saveAllChunks(true, true, true);
        for (ServerLevel world : s.getServer().getAllLevels()) {
            world.getChunkSource().tick(() -> true, false);
        }
        CarpetScriptServer.LOG.warn("Saved chunks");
        return Value.TRUE;
    });
    expression.addContextFunction("tick_time", 0, (c, t, lv) -> new NumericValue(((CarpetContext) c).s.getServer().getTickCount()));
    expression.addContextFunction("world_time", 0, (c, t, lv) -> {
        c.host.issueDeprecation("world_time()");
        return new NumericValue(((CarpetContext) c).s.getLevel().getGameTime());
    });
    expression.addContextFunction("day_time", -1, (c, t, lv) -> {
        Value time = new NumericValue(((CarpetContext) c).s.getLevel().getDayTime());
        if (lv.size() > 0) {
            long newTime = NumericValue.asNumber(lv.get(0)).getLong();
            if (newTime < 0)
                newTime = 0;
            // setTimeOfDay(newTime);
            ((CarpetContext) c).s.getLevel().setDayTime(newTime);
        }
        return time;
    });
    expression.addContextFunction("last_tick_times", -1, (c, t, lv) -> {
        c.host.issueDeprecation("last_tick_times()");
        return SystemInfo.get("server_last_tick_times", (CarpetContext) c);
    });
    expression.addContextFunction("game_tick", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        CommandSourceStack s = cc.s;
        if (CarpetServer.scriptServer == null)
            return Value.NULL;
        if (!s.getServer().isSameThread())
            throw new InternalExpressionException("Unable to run ticks from threads");
        if (CarpetServer.scriptServer.tickDepth > 16)
            throw new InternalExpressionException("'game_tick' function caused other 'game_tick' functions to run. You should not allow that.");
        try {
            CarpetServer.scriptServer.tickDepth++;
            ((MinecraftServerInterface) s.getServer()).forceTick(() -> System.nanoTime() - CarpetServer.scriptServer.tickStart < 50000000L);
            if (lv.size() > 0) {
                long ms_total = NumericValue.asNumber(lv.get(0)).getLong();
                long end_expected = CarpetServer.scriptServer.tickStart + ms_total * 1000000L;
                long wait = end_expected - System.nanoTime();
                if (wait > 0L) {
                    try {
                        Thread.sleep(wait / 1000000L);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
            // for the next tick
            CarpetServer.scriptServer.tickStart = System.nanoTime();
            Thread.yield();
        } finally {
            if (CarpetServer.scriptServer != null)
                CarpetServer.scriptServer.tickDepth--;
        }
        if (CarpetServer.scriptServer != null && CarpetServer.scriptServer.stopAll)
            throw new ExitStatement(Value.NULL);
        return Value.TRUE;
    });
    expression.addContextFunction("seed", -1, (c, t, lv) -> {
        CommandSourceStack s = ((CarpetContext) c).s;
        c.host.issueDeprecation("seed()");
        return new NumericValue(s.getLevel().getSeed());
    });
    expression.addContextFunction("relight", -1, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        BlockArgument locator = BlockArgument.findIn(cc, lv, 0);
        BlockPos pos = locator.block.getPos();
        ServerLevel world = cc.s.getLevel();
        ((ThreadedAnvilChunkStorageInterface) world.getChunkSource().chunkMap).relightChunk(new ChunkPos(pos));
        WorldTools.forceChunkUpdate(pos, world);
        return Value.TRUE;
    });
    // Should this be deprecated for system_info('source_dimension')?
    expression.addContextFunction("current_dimension", 0, (c, t, lv) -> ValueConversions.of(((CarpetContext) c).s.getLevel()));
    expression.addContextFunction("view_distance", 0, (c, t, lv) -> {
        c.host.issueDeprecation("view_distance()");
        return new NumericValue(((CarpetContext) c).s.getServer().getPlayerList().getViewDistance());
    });
    // lazy due to passthrough and context changing ability
    expression.addLazyFunction("in_dimension", 2, (c, t, lv) -> {
        CommandSourceStack outerSource = ((CarpetContext) c).s;
        Value dimensionValue = lv.get(0).evalValue(c);
        Level world = ValueConversions.dimFromValue(dimensionValue, outerSource.getServer());
        if (world == outerSource.getLevel())
            return lv.get(1);
        CommandSourceStack innerSource = outerSource.withLevel((ServerLevel) world);
        Context newCtx = c.recreate();
        ((CarpetContext) newCtx).s = innerSource;
        newCtx.variables = c.variables;
        Value retval = lv.get(1).evalValue(newCtx);
        return (cc, tt) -> retval;
    });
    expression.addContextFunction("plop", -1, (c, t, lv) -> {
        if (lv.size() == 0) {
            Map<Value, Value> plopData = new HashMap<>();
            CarpetContext cc = (CarpetContext) c;
            RegistryAccess registryManager = cc.s.getLevel().registryAccess();
            plopData.put(StringValue.of("scarpet_custom"), ListValue.wrap(FeatureGenerator.featureMap.keySet().stream().sorted().map(StringValue::of).collect(Collectors.toList())));
            plopData.put(StringValue.of("features"), ListValue.wrap(Registry.FEATURE.keySet().stream().sorted().map(ValueConversions::of).collect(Collectors.toList())));
            plopData.put(StringValue.of("configured_features"), ListValue.wrap(registryManager.registryOrThrow(Registry.CONFIGURED_FEATURE_REGISTRY).keySet().stream().sorted().map(ValueConversions::of).collect(Collectors.toList())));
            plopData.put(StringValue.of("structures"), ListValue.wrap(Registry.STRUCTURE_FEATURE.keySet().stream().sorted().map(ValueConversions::of).collect(Collectors.toList())));
            plopData.put(StringValue.of("configured_structures"), ListValue.wrap(registryManager.registryOrThrow(Registry.CONFIGURED_STRUCTURE_FEATURE_REGISTRY).keySet().stream().sorted().map(ValueConversions::of).collect(Collectors.toList())));
            return MapValue.wrap(plopData);
        }
        BlockArgument locator = BlockArgument.findIn((CarpetContext) c, lv, 0);
        if (lv.size() <= locator.offset)
            throw new InternalExpressionException("'plop' needs extra argument indicating what to plop");
        String what = lv.get(locator.offset).getString();
        Value[] result = new Value[] { Value.NULL };
        ((CarpetContext) c).s.getServer().executeBlocking(() -> {
            Boolean res = FeatureGenerator.plop(what, ((CarpetContext) c).s.getLevel(), locator.block.getPos());
            if (res == null)
                return;
            if (// there might be more of those
            what.equalsIgnoreCase("forest_rock"))
                WorldTools.forceChunkUpdate(locator.block.getPos(), ((CarpetContext) c).s.getLevel());
            result[0] = BooleanValue.of(res);
        });
        return result[0];
    });
    expression.addContextFunction("schedule", -1, (c, t, lv) -> {
        if (lv.size() < 2)
            throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name");
        long delay = NumericValue.asNumber(lv.get(0)).getLong();
        FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, false, false);
        CarpetServer.scriptServer.events.scheduleCall((CarpetContext) c, functionArgument.function, functionArgument.checkedArgs(), delay);
        return Value.TRUE;
    });
    expression.addImpureFunction("logger", lv -> {
        Value res;
        if (lv.size() == 1) {
            res = lv.get(0);
            CarpetScriptServer.LOG.info(res.getString());
        } else if (lv.size() == 2) {
            String level = lv.get(0).getString().toLowerCase(Locale.ROOT);
            res = lv.get(1);
            switch(level) {
                case "debug":
                    CarpetScriptServer.LOG.debug(res.getString());
                    break;
                case "warn":
                    CarpetScriptServer.LOG.warn(res.getString());
                    break;
                case "info":
                    CarpetScriptServer.LOG.info(res.getString());
                    break;
                case "fatal":
                // Somehow issue deprecation
                case "error":
                    CarpetScriptServer.LOG.error(res.getString());
                    break;
                default:
                    throw new InternalExpressionException("Unknown log level for 'logger': " + level);
            }
        } else
            throw new InternalExpressionException("logger takes 1 or 2 arguments");
        // pass through for variables
        return res;
    });
    expression.addContextFunction("list_files", 2, (c, t, lv) -> {
        FileArgument fdesc = FileArgument.from(lv, true, FileArgument.Reason.READ);
        Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc);
        if (files == null)
            return Value.NULL;
        return ListValue.wrap(files.map(StringValue::of).collect(Collectors.toList()));
    });
    expression.addContextFunction("read_file", 2, (c, t, lv) -> {
        FileArgument fdesc = FileArgument.from(lv, false, FileArgument.Reason.READ);
        Value retVal;
        if (fdesc.type == FileArgument.Type.NBT) {
            Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc);
            if (state == null)
                return Value.NULL;
            retVal = new NBTSerializableValue(state);
        } else if (fdesc.type == FileArgument.Type.JSON) {
            JsonElement json;
            json = ((CarpetScriptHost) c.host).readJsonFile(fdesc);
            Value parsedJson = GSON.fromJson(json, Value.class);
            if (parsedJson == null)
                retVal = Value.NULL;
            else
                retVal = parsedJson;
        } else {
            List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc);
            if (content == null)
                return Value.NULL;
            retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
        }
        return retVal;
    });
    expression.addContextFunction("delete_file", 2, (c, t, lv) -> BooleanValue.of(((CarpetScriptHost) c.host).removeResourceFile(FileArgument.from(lv, false, FileArgument.Reason.DELETE))));
    expression.addContextFunction("write_file", -1, (c, t, lv) -> {
        if (lv.size() < 3)
            throw new InternalExpressionException("'write_file' requires three or more arguments");
        FileArgument fdesc = FileArgument.from(lv, false, FileArgument.Reason.CREATE);
        boolean success;
        if (fdesc.type == FileArgument.Type.NBT) {
            Value val = lv.get(2);
            NBTSerializableValue tagValue = (val instanceof NBTSerializableValue) ? (NBTSerializableValue) val : new NBTSerializableValue(val.getString());
            Tag tag = tagValue.getTag();
            success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc);
        } else if (fdesc.type == FileArgument.Type.JSON) {
            List<String> data = Collections.singletonList(GSON.toJson(lv.get(2).toJson()));
            ((CarpetScriptHost) c.host).removeResourceFile(fdesc);
            success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
        } else {
            List<String> data = new ArrayList<>();
            if (lv.size() == 3) {
                Value val = lv.get(2);
                if (val instanceof ListValue) {
                    List<Value> lval = ((ListValue) val).getItems();
                    lval.forEach(v -> data.add(v.getString()));
                } else {
                    data.add(val.getString());
                }
            } else {
                for (int i = 2; i < lv.size(); i++) {
                    data.add(lv.get(i).getString());
                }
            }
            success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
        }
        return BooleanValue.of(success);
    });
    expression.addContextFunction("load_app_data", -1, (c, t, lv) -> {
        FileArgument fdesc = new FileArgument(null, FileArgument.Type.NBT, null, false, false, FileArgument.Reason.READ);
        if (lv.size() > 0) {
            c.host.issueDeprecation("load_app_data(...) with arguments");
            String resource = recognizeResource(lv.get(0), false);
            boolean shared = lv.size() > 1 && lv.get(1).getBoolean();
            fdesc = new FileArgument(resource, FileArgument.Type.NBT, null, false, shared, FileArgument.Reason.READ);
        }
        return NBTSerializableValue.of(((CarpetScriptHost) c.host).readFileTag(fdesc));
    });
    expression.addContextFunction("store_app_data", -1, (c, t, lv) -> {
        if (lv.size() == 0)
            throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
        Value val = lv.get(0);
        FileArgument fdesc = new FileArgument(null, FileArgument.Type.NBT, null, false, false, FileArgument.Reason.CREATE);
        if (lv.size() > 1) {
            c.host.issueDeprecation("store_app_data(...) with more than one argument");
            String resource = recognizeResource(lv.get(1), false);
            boolean shared = lv.size() > 2 && lv.get(2).getBoolean();
            fdesc = new FileArgument(resource, FileArgument.Type.NBT, null, false, shared, FileArgument.Reason.CREATE);
        }
        NBTSerializableValue tagValue = (val instanceof NBTSerializableValue) ? (NBTSerializableValue) val : new NBTSerializableValue(val.getString());
        return BooleanValue.of(((CarpetScriptHost) c.host).writeTagFile(tagValue.getTag(), fdesc));
    });
    expression.addContextFunction("statistic", 3, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        ServerPlayer player = EntityValue.getPlayerByValue(cc.s.getServer(), lv.get(0));
        if (player == null)
            return Value.NULL;
        ResourceLocation category;
        ResourceLocation statName;
        category = InputValidator.identifierOf(lv.get(1).getString());
        statName = InputValidator.identifierOf(lv.get(2).getString());
        StatType<?> type = Registry.STAT_TYPE.get(category);
        if (type == null)
            return Value.NULL;
        Stat<?> stat = getStat(type, statName);
        if (stat == null)
            return Value.NULL;
        return new NumericValue(player.getStats().getValue(stat));
    });
    // handle_event('event', function...)
    expression.addContextFunction("handle_event", -1, (c, t, lv) -> {
        if (lv.size() < 2)
            throw new InternalExpressionException("'handle_event' requires at least two arguments, event name, and a callback");
        String event = lv.get(0).getString();
        FunctionArgument callback = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
        CarpetScriptHost host = ((CarpetScriptHost) c.host);
        if (callback.function == null)
            return BooleanValue.of(host.getScriptServer().events.removeBuiltInEvent(event, host));
        // args don't need to be checked will be checked at the event
        return BooleanValue.of(host.getScriptServer().events.handleCustomEvent(event, host, callback.function, callback.args));
    });
    // signal_event('event', player or null, args.... ) -> number of apps notified
    expression.addContextFunction("signal_event", -1, (c, t, lv) -> {
        if (lv.size() == 0)
            throw new InternalExpressionException("'signal' requires at least one argument");
        CarpetContext cc = (CarpetContext) c;
        CarpetScriptServer server = ((CarpetScriptHost) c.host).getScriptServer();
        String eventName = lv.get(0).getString();
        // no such event yet
        if (CarpetEventServer.Event.getEvent(eventName, server) == null)
            return Value.NULL;
        ServerPlayer player = null;
        List<Value> args = Collections.emptyList();
        if (lv.size() > 1) {
            player = EntityValue.getPlayerByValue(server.server, lv.get(1));
            if (lv.size() > 2)
                args = lv.subList(2, lv.size());
        }
        int counts = ((CarpetScriptHost) c.host).getScriptServer().events.signalEvent(eventName, cc, player, args);
        if (counts < 0)
            return Value.NULL;
        return new NumericValue(counts);
    });
    // nbt_storage()
    // nbt_storage(key)
    // nbt_storage(key, nbt)
    expression.addContextFunction("nbt_storage", -1, (c, t, lv) -> {
        if (lv.size() > 2)
            throw new InternalExpressionException("'nbt_storage' requires 0, 1 or 2 arguments.");
        CarpetContext cc = (CarpetContext) c;
        CommandStorage storage = cc.s.getServer().getCommandStorage();
        if (lv.size() == 0)
            return ListValue.wrap(storage.keys().map(i -> new StringValue(nameFromRegistryId(i))).collect(Collectors.toList()));
        String key = lv.get(0).getString();
        CompoundTag old_nbt = storage.get(InputValidator.identifierOf(key));
        if (lv.size() == 2) {
            Value nbt = lv.get(1);
            NBTSerializableValue new_nbt = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt : NBTSerializableValue.parseString(nbt.getString(), true);
            storage.set(InputValidator.identifierOf(key), new_nbt.getCompoundTag());
        }
        return NBTSerializableValue.of(old_nbt);
    });
    // script run create_datapack('foo', {'foo' -> {'bar.json' -> {'c' -> true,'d' -> false,'e' -> {'foo' -> [1,2,3]},'a' -> 'foobar','b' -> 5}}})
    expression.addContextFunction("create_datapack", 2, (c, t, lv) -> {
        CarpetContext cc = (CarpetContext) c;
        String origName = lv.get(0).getString();
        String name = InputValidator.validateSimpleString(origName, true);
        MinecraftServer server = cc.s.getServer();
        for (String dpName : server.getPackRepository().getAvailableIds()) {
            if (dpName.equalsIgnoreCase("file/" + name + ".zip") || dpName.equalsIgnoreCase("file/" + name))
                return Value.NULL;
        }
        Value dpdata = lv.get(1);
        if (!(dpdata instanceof MapValue))
            throw new InternalExpressionException("datapack data needs to be a valid map type");
        PackRepository packManager = server.getPackRepository();
        Path dbFloder = server.getWorldPath(LevelResource.DATAPACK_DIR);
        Path packFloder = dbFloder.resolve(name + ".zip");
        if (Files.exists(packFloder) || Files.exists(dbFloder.resolve(name)))
            return Value.NULL;
        Boolean[] successful = new Boolean[] { true };
        server.executeBlocking(() -> {
            try {
                // Files.createDirectory(packFloder);
                try (FileSystem zipfs = FileSystems.newFileSystem(URI.create("jar:" + packFloder.toUri().toString()), Map.of("create", "true"))) {
                    Path zipRoot = zipfs.getPath("/");
                    zipValueToJson(zipRoot.resolve("pack.mcmeta"), MapValue.wrap(Map.of(StringValue.of("pack"), MapValue.wrap(Map.of(StringValue.of("pack_format"), new NumericValue(SharedConstants.getCurrentVersion().getPackVersion()), StringValue.of("description"), StringValue.of(name), StringValue.of("source"), StringValue.of("scarpet"))))));
                    walkTheDPMap((MapValue) dpdata, zipRoot);
                }
                packManager.reload();
                Pack resourcePackProfile = packManager.getPack("file/" + name + ".zip");
                if (resourcePackProfile == null || packManager.getSelectedPacks().contains(resourcePackProfile)) {
                    throw new IOException();
                }
                List<Pack> list = Lists.newArrayList(packManager.getSelectedPacks());
                resourcePackProfile.getDefaultPosition().insert(list, resourcePackProfile, p -> p, false);
                server.reloadResources(list.stream().map(Pack::getId).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 BooleanValue.of(successful[0]);
    });
    expression.addContextFunction("enable_hidden_dimensions", 0, (c, t, lv) -> {
        return Value.NULL;
    /*
            CarpetContext cc = (CarpetContext)c;
            // from minecraft.server.Main.main
            MinecraftServer server = cc.s.getServer();
            LevelStorageSource.LevelStorageAccess session = ((MinecraftServerInterface)server).getCMSession();
            DataPackConfig dataPackSettings = session.getDataPacks();
            PackRepository resourcePackManager = server.getPackRepository();

            WorldLoader.InitConfig initConfig = new WorldLoader.InitConfig(new WorldLoader.PackConfig(resourcePackManager, dataPackSettings == null ? DataPackConfig.DEFAULT : dataPackSettings, false), Commands.CommandSelection.DEDICATED, 4);


            final WorldData data = WorldLoader.load(initConfig, (resourceManager, dataPackConfigx) -> {
                RegistryAccess.Writable writable = RegistryAccess.builtinCopy();
                DynamicOps<Tag> dynamicOps = RegistryOps.createAndLoad(NbtOps.INSTANCE, writable, (ResourceManager) resourceManager);
                WorldData worldData = session.getDataTag(dynamicOps, dataPackConfigx, writable.allElementsLifecycle());
                return Pair.of(worldData, writable.freeze());
            }, WorldStem::new, Util.backgroundExecutor(), Runnable::run).join().worldData();
            WorldGenSettings generatorOptions = data.worldGenSettings();

            boolean bl = generatorOptions.isDebug();
            long l = generatorOptions.seed();
            long m = BiomeManager.obfuscateSeed(l);
            Map<ResourceKey<Level>, ServerLevel> existing_worlds = ((MinecraftServerInterface)server).getCMWorlds();
            List<Value> addeds = new ArrayList<>();
            for (Map.Entry<ResourceKey<LevelStem>, LevelStem> entry : generatorOptions.dimensions().entrySet()) {
                ResourceKey<LevelStem> registryKey = entry.getKey();
                if (!existing_worlds.containsKey(registryKey))
                {
                    ResourceKey<Level> resourceKey2 = ResourceKey.create(Registry.DIMENSION_REGISTRY, registryKey.location());
                    DerivedLevelData derivedLevelData = new DerivedLevelData(data, ((ServerWorldInterface) server.overworld()).getWorldPropertiesCM());
                    ServerLevel serverLevel2 = new ServerLevel(server, Util.backgroundExecutor(), session, derivedLevelData, resourceKey2, entry.getValue(), WorldTools.NOOP_LISTENER, bl, m, ImmutableList.of(), false);
                    server.overworld().getWorldBorder().addListener(new BorderChangeListener.DelegateBorderChangeListener(serverLevel2.getWorldBorder()));
                    existing_worlds.put(resourceKey2, serverLevel2);
                }
            }
            return ListValue.wrap(addeds);*/
    });
}
Also used : SoundSource(net.minecraft.sounds.SoundSource) ThreadedAnvilChunkStorageInterface(carpet.fakes.ThreadedAnvilChunkStorageInterface) Arrays(java.util.Arrays) ParticleOptions(net.minecraft.core.particles.ParticleOptions) FileArgument(carpet.script.argument.FileArgument) GsonBuilder(com.google.gson.GsonBuilder) ClientboundCustomSoundPacket(net.minecraft.network.protocol.game.ClientboundCustomSoundPacket) MinecraftServer(net.minecraft.server.MinecraftServer) Map(java.util.Map) CarpetScriptHost(carpet.script.CarpetScriptHost) SystemInfo(carpet.script.utils.SystemInfo) Value(carpet.script.value.Value) MapValue(carpet.script.value.MapValue) Path(java.nio.file.Path) RegistryOps(net.minecraft.resources.RegistryOps) NbtUtils(net.minecraft.nbt.NbtUtils) ShapeDispatcher(carpet.script.utils.ShapeDispatcher) ResourceManager(net.minecraft.server.packs.resources.ResourceManager) Set(java.util.Set) RegistryAccess(net.minecraft.core.RegistryAccess) ValueConversions(carpet.script.value.ValueConversions) TextComponent(net.minecraft.network.chat.TextComponent) Stream(java.util.stream.Stream) ExitStatement(carpet.script.exception.ExitStatement) Packet(net.minecraft.network.protocol.Packet) DerivedLevelData(net.minecraft.world.level.storage.DerivedLevelData) ItemStack(net.minecraft.world.item.ItemStack) Throwables(carpet.script.exception.Throwables) NbtIo(net.minecraft.nbt.NbtIo) SoundSource(net.minecraft.sounds.SoundSource) CommandSourceStack(net.minecraft.commands.CommandSourceStack) EntityType(net.minecraft.world.entity.EntityType) WorldGenSettings(net.minecraft.world.level.levelgen.WorldGenSettings) BlockState(net.minecraft.world.level.block.state.BlockState) ServerWorldInterface(carpet.fakes.ServerWorldInterface) ClientboundSetSubtitleTextPacket(net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket) ClientboundSetTitleTextPacket(net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket) BlockArgument(carpet.script.argument.BlockArgument) ClientboundSetActionBarTextPacket(net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket) ArrayList(java.util.ArrayList) CarpetEventServer(carpet.script.CarpetEventServer) ListValue(carpet.script.value.ListValue) ServerPlayer(net.minecraft.server.level.ServerPlayer) Lists(com.google.common.collect.Lists) Rotations(net.minecraft.core.Rotations) Vector3Argument(carpet.script.argument.Vector3Argument) NullValue(carpet.script.value.NullValue) CarpetScriptServer(carpet.script.CarpetScriptServer) WorldTools(carpet.script.utils.WorldTools) Component(net.minecraft.network.chat.Component) Files(java.nio.file.Files) Expression(carpet.script.Expression) BufferedWriter(java.io.BufferedWriter) FileUtils(org.apache.commons.io.FileUtils) IOException(java.io.IOException) Pair(com.mojang.datafixers.util.Pair) ChunkPos(net.minecraft.world.level.ChunkPos) PackRepository(net.minecraft.server.packs.repository.PackRepository) EquipmentSlot(net.minecraft.world.entity.EquipmentSlot) LevelStem(net.minecraft.world.level.dimension.LevelStem) LevelResource(net.minecraft.world.level.storage.LevelResource) FileSystems(java.nio.file.FileSystems) ScarpetJsonDeserializer(carpet.script.utils.ScarpetJsonDeserializer) WorldStem(net.minecraft.server.WorldStem) NumericValue(carpet.script.value.NumericValue) ResourceLocation(net.minecraft.resources.ResourceLocation) LazyListValue(carpet.script.value.LazyListValue) StringTag(net.minecraft.nbt.StringTag) Pack(net.minecraft.server.packs.repository.Pack) Registry(net.minecraft.core.Registry) Messenger(carpet.utils.Messenger) DynamicOps(com.mojang.serialization.DynamicOps) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Gson(com.google.gson.Gson) Locale(java.util.Locale) Context(carpet.script.Context) StatType(net.minecraft.stats.StatType) FormattedTextValue(carpet.script.value.FormattedTextValue) URI(java.net.URI) WorldData(net.minecraft.world.level.storage.WorldData) BooleanValue(carpet.script.value.BooleanValue) Stat(net.minecraft.stats.Stat) BiomeManager(net.minecraft.world.level.biome.BiomeManager) Commands(net.minecraft.commands.Commands) Math.min(java.lang.Math.min) FileSystem(java.nio.file.FileSystem) Collectors(java.util.stream.Collectors) Player(net.minecraft.world.entity.player.Player) ThrowStatement(carpet.script.exception.ThrowStatement) Util(net.minecraft.Util) List(java.util.List) CompoundTag(net.minecraft.nbt.CompoundTag) CommandStorage(net.minecraft.world.level.storage.CommandStorage) BlockPos(net.minecraft.core.BlockPos) MinecraftServerInterface(carpet.fakes.MinecraftServerInterface) FunctionArgument(carpet.script.argument.FunctionArgument) Math.max(java.lang.Math.max) Level(net.minecraft.world.level.Level) SnoopyCommandSource(carpet.script.utils.SnoopyCommandSource) CarpetContext(carpet.script.CarpetContext) NBTSerializableValue.nameFromRegistryId(carpet.script.value.NBTSerializableValue.nameFromRegistryId) NBTSerializableValue(carpet.script.value.NBTSerializableValue) Tag(net.minecraft.nbt.Tag) CarpetServer(carpet.CarpetServer) HUDController(carpet.logging.HUDController) HashMap(java.util.HashMap) ServerLevel(net.minecraft.server.level.ServerLevel) Function(java.util.function.Function) JsonElement(com.google.gson.JsonElement) HashSet(java.util.HashSet) ImmutableList(com.google.common.collect.ImmutableList) BaseComponent(net.minecraft.network.chat.BaseComponent) BorderChangeListener(net.minecraft.world.level.border.BorderChangeListener) InputValidator(carpet.script.utils.InputValidator) SharedConstants(net.minecraft.SharedConstants) StringValue(carpet.script.value.StringValue) ArmorStand(net.minecraft.world.entity.decoration.ArmorStand) LevelStorageSource(net.minecraft.world.level.storage.LevelStorageSource) ClientboundSetTitlesAnimationPacket(net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket) DataPackConfig(net.minecraft.world.level.DataPackConfig) ClientboundClearTitlesPacket(net.minecraft.network.protocol.game.ClientboundClearTitlesPacket) InternalExpressionException(carpet.script.exception.InternalExpressionException) NbtOps(net.minecraft.nbt.NbtOps) ResourceKey(net.minecraft.resources.ResourceKey) FeatureGenerator(carpet.helpers.FeatureGenerator) Entity(net.minecraft.world.entity.Entity) EntityValue(carpet.script.value.EntityValue) Vec3(net.minecraft.world.phys.Vec3) Collections(java.util.Collections) Entity(net.minecraft.world.entity.Entity) ParticleOptions(net.minecraft.core.particles.ParticleOptions) ExitStatement(carpet.script.exception.ExitStatement) HashMap(java.util.HashMap) RegistryAccess(net.minecraft.core.RegistryAccess) ArrayList(java.util.ArrayList) FileArgument(carpet.script.argument.FileArgument) ClientboundSetTitlesAnimationPacket(net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket) CarpetScriptServer(carpet.script.CarpetScriptServer) Vec3(net.minecraft.world.phys.Vec3) FileSystem(java.nio.file.FileSystem) BlockPos(net.minecraft.core.BlockPos) ChunkPos(net.minecraft.world.level.ChunkPos) ArrayList(java.util.ArrayList) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) StringValue(carpet.script.value.StringValue) Pack(net.minecraft.server.packs.repository.Pack) FunctionArgument(carpet.script.argument.FunctionArgument) ThreadedAnvilChunkStorageInterface(carpet.fakes.ThreadedAnvilChunkStorageInterface) ClientboundCustomSoundPacket(net.minecraft.network.protocol.game.ClientboundCustomSoundPacket) HashSet(java.util.HashSet) TextComponent(net.minecraft.network.chat.TextComponent) ClientboundCustomSoundPacket(net.minecraft.network.protocol.game.ClientboundCustomSoundPacket) Packet(net.minecraft.network.protocol.Packet) ClientboundSetSubtitleTextPacket(net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket) ClientboundSetTitleTextPacket(net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket) ClientboundSetActionBarTextPacket(net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket) ClientboundSetTitlesAnimationPacket(net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket) ClientboundClearTitlesPacket(net.minecraft.network.protocol.game.ClientboundClearTitlesPacket) ServerPlayer(net.minecraft.server.level.ServerPlayer) Player(net.minecraft.world.entity.player.Player) NBTSerializableValue(carpet.script.value.NBTSerializableValue) ListValue(carpet.script.value.ListValue) LazyListValue(carpet.script.value.LazyListValue) CommandStorage(net.minecraft.world.level.storage.CommandStorage) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) PackRepository(net.minecraft.server.packs.repository.PackRepository) ServerPlayer(net.minecraft.server.level.ServerPlayer) BlockArgument(carpet.script.argument.BlockArgument) Value(carpet.script.value.Value) MapValue(carpet.script.value.MapValue) ListValue(carpet.script.value.ListValue) NullValue(carpet.script.value.NullValue) NumericValue(carpet.script.value.NumericValue) LazyListValue(carpet.script.value.LazyListValue) FormattedTextValue(carpet.script.value.FormattedTextValue) BooleanValue(carpet.script.value.BooleanValue) NBTSerializableValue(carpet.script.value.NBTSerializableValue) StringValue(carpet.script.value.StringValue) EntityValue(carpet.script.value.EntityValue) Level(net.minecraft.world.level.Level) ServerLevel(net.minecraft.server.level.ServerLevel) InternalExpressionException(carpet.script.exception.InternalExpressionException) ItemStack(net.minecraft.world.item.ItemStack) MinecraftServerInterface(carpet.fakes.MinecraftServerInterface) ClientboundSetActionBarTextPacket(net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket) ServerLevel(net.minecraft.server.level.ServerLevel) BaseComponent(net.minecraft.network.chat.BaseComponent) CommandSourceStack(net.minecraft.commands.CommandSourceStack) CarpetContext(carpet.script.CarpetContext) ArmorStand(net.minecraft.world.entity.decoration.ArmorStand) ResourceLocation(net.minecraft.resources.ResourceLocation) FormattedTextValue(carpet.script.value.FormattedTextValue) ClientboundClearTitlesPacket(net.minecraft.network.protocol.game.ClientboundClearTitlesPacket) ThrowStatement(carpet.script.exception.ThrowStatement) TextComponent(net.minecraft.network.chat.TextComponent) Component(net.minecraft.network.chat.Component) BaseComponent(net.minecraft.network.chat.BaseComponent) CompoundTag(net.minecraft.nbt.CompoundTag) ClientboundSetSubtitleTextPacket(net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket) Context(carpet.script.Context) CarpetContext(carpet.script.CarpetContext) Path(java.nio.file.Path) CarpetScriptHost(carpet.script.CarpetScriptHost) EntityValue(carpet.script.value.EntityValue) MapValue(carpet.script.value.MapValue) IOException(java.io.IOException) Rotations(net.minecraft.core.Rotations) IOException(java.io.IOException) InternalExpressionException(carpet.script.exception.InternalExpressionException) MinecraftServer(net.minecraft.server.MinecraftServer) ClientboundSetTitleTextPacket(net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket) SnoopyCommandSource(carpet.script.utils.SnoopyCommandSource) BlockState(net.minecraft.world.level.block.state.BlockState) JsonElement(com.google.gson.JsonElement) Vector3Argument(carpet.script.argument.Vector3Argument) ValueConversions(carpet.script.value.ValueConversions) StringTag(net.minecraft.nbt.StringTag) CompoundTag(net.minecraft.nbt.CompoundTag) Tag(net.minecraft.nbt.Tag) NumericValue(carpet.script.value.NumericValue)

Example 2 with ServerWorldInterface

use of carpet.fakes.ServerWorldInterface in project fabric-carpet by gnembon.

the class ChunkMap_scarpetChunkCreationMixin method regenerateChunkRegion.

@Override
public Map<String, Integer> regenerateChunkRegion(final List<ChunkPos> requestedChunksList) {
    final Object2IntMap<String> report = new Object2IntOpenHashMap<>();
    final Set<ChunkPos> requestedChunks = new HashSet<>(requestedChunksList);
    // Load requested chunks
    final Set<ChunkPos> existingChunks = this.loadExistingChunks(requestedChunks, report);
    // Finish pending generation stages
    // This ensures that no generation events will be put back on the main thread after the chunks have been deleted
    final Set<ChunkAccess> affectedChunks = new HashSet<>();
    for (final ChunkPos pos : existingChunks) affectedChunks.add(this.getCurrentChunk(pos));
    report.put("affected_chunks", affectedChunks.size());
    // Load neighbors for light removal
    final Set<ChunkPos> neighbors = new HashSet<>();
    for (final ChunkAccess chunk : affectedChunks) {
        final ChunkPos pos = chunk.getPos();
        for (int x = -1; x <= 1; ++x) for (int z = -1; z <= 1; ++z) if (x != 0 || z != 0) {
            final ChunkPos nPos = new ChunkPos(pos.x + x, pos.z + z);
            if (!requestedChunks.contains(nPos))
                neighbors.add(nPos);
        }
    }
    this.loadExistingChunks(neighbors);
    // Determine affected neighbors
    final Set<ChunkAccess> affectedNeighbors = new HashSet<>();
    for (final ChunkPos pos : neighbors) {
        final ChunkAccess chunk = this.getCurrentChunk(pos);
        if (chunk.getStatus().isOrAfter(ChunkStatus.LIGHT.getParent()))
            affectedNeighbors.add(chunk);
    }
    for (final ChunkAccess chunk : affectedChunks) {
        final ChunkPos pos = chunk.getPos();
        // remove entities
        long longPos = pos.toLong();
        if (this.entitiesInLevel.contains(longPos) && chunk instanceof LevelChunk)
            ((SimpleEntityLookupInterface<Entity>) ((ServerWorldInterface) level).getEntityLookupCMPublic()).getChunkEntities(pos).forEach(entity -> {
                if (!(entity instanceof Player))
                    entity.discard();
            });
        if (chunk instanceof LevelChunk)
            ((LevelChunk) chunk).setLoaded(false);
        if (this.entitiesInLevel.remove(pos.toLong()) && chunk instanceof LevelChunk)
            // block entities only
            this.level.unload((LevelChunk) chunk);
        ((ServerLightingProviderInterface) this.lightEngine).invokeUpdateChunkStatus(pos);
        ((ServerLightingProviderInterface) this.lightEngine).removeLightData(chunk);
        this.progressListener.onStatusChange(pos, null);
    }
    for (final ChunkAccess chunk : affectedChunks) {
        final ChunkPos cPos = chunk.getPos();
        final long pos = cPos.toLong();
        final ChunkHolder oldHolder = this.updatingChunkMap.remove(pos);
        final ChunkHolder newHolder = new ChunkHolder(cPos, oldHolder.getTicketLevel(), level, this.lightEngine, this.queueSorter, (ChunkHolder.PlayerProvider) this);
        // enable chunk blending?
        ((ChunkHolderInterface) newHolder).setDefaultProtoChunk(cPos, this.mainThreadExecutor, level);
        this.updatingChunkMap.put(pos, newHolder);
        ((ChunkTicketManagerInterface) this.distanceManager).replaceHolder(oldHolder, newHolder);
    }
    this.modified = true;
    this.promoteChunkMap();
    for (final ChunkAccess chunk : affectedNeighbors) ((ServerLightingProviderInterface) this.lightEngine).removeLightData(chunk);
    for (final ChunkAccess chunk : affectedNeighbors) this.addRelightTicket(chunk.getPos());
    this.tickTicketManager();
    final List<CompletableFuture<?>> lightFutures = new ArrayList<>();
    for (final ChunkAccess chunk : affectedNeighbors) {
        final ChunkPos pos = chunk.getPos();
        lightFutures.add(this.getChunkRangeFuture(pos, 1, (pos_) -> ChunkStatus.LIGHT).thenCompose(either -> either.map(list -> ((ServerLightingProviderInterface) this.lightEngine).relight(chunk), unloaded -> {
            this.releaseRelightTicket(pos);
            return CompletableFuture.completedFuture(null);
        })));
    }
    // Force generation to previous states
    // This ensures that the world is in a consistent state after this method
    // Also, this is needed to ensure chunks are saved to disk
    final Map<ChunkPos, ChunkStatus> targetGenerationStatus = affectedChunks.stream().collect(Collectors.toMap(ChunkAccess::getPos, ChunkAccess::getStatus));
    for (final Entry<ChunkPos, ChunkStatus> entry : targetGenerationStatus.entrySet()) this.addTicket(entry.getKey(), entry.getValue());
    this.tickTicketManager();
    final List<Pair<ChunkStatus, CompletableFuture<?>>> targetGenerationFutures = new ArrayList<>();
    for (final Entry<ChunkPos, ChunkStatus> entry : targetGenerationStatus.entrySet()) targetGenerationFutures.add(Pair.of(entry.getValue(), this.updatingChunkMap.get(entry.getKey().toLong()).getOrScheduleFuture(entry.getValue(), (ChunkMap) (Object) this)));
    final Map<ChunkStatus, List<CompletableFuture<?>>> targetGenerationFuturesGrouped = targetGenerationFutures.stream().collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Entry::getValue, Collectors.toList())));
    for (final ChunkStatus status : ChunkStatus.getStatusList()) {
        final List<CompletableFuture<?>> futures = targetGenerationFuturesGrouped.get(status);
        if (futures == null)
            continue;
        report.put("layer_count_" + status.getName(), futures.size());
        final long start = System.currentTimeMillis();
        this.waitFor(futures);
        report.put("layer_time_" + status.getName(), (int) (System.currentTimeMillis() - start));
    }
    report.put("relight_count", lightFutures.size());
    final long relightStart = System.currentTimeMillis();
    this.waitFor(lightFutures);
    report.put("relight_time", (int) (System.currentTimeMillis() - relightStart));
    return report;
}
Also used : ChunkTicketManagerInterface(carpet.fakes.ChunkTicketManagerInterface) ThreadedAnvilChunkStorageInterface(carpet.fakes.ThreadedAnvilChunkStorageInterface) Either(com.mojang.datafixers.util.Either) Inject(org.spongepowered.asm.mixin.injection.Inject) CHUNK_GENERATED(carpet.script.CarpetEventServer.Event.CHUNK_GENERATED) CallbackInfoReturnable(org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable) Future(java.util.concurrent.Future) MinecraftServer(net.minecraft.server.MinecraftServer) Pair(org.apache.commons.lang3.tuple.Pair) Mixin(org.spongepowered.asm.mixin.Mixin) Map(java.util.Map) SimpleEntityLookupInterface(carpet.fakes.SimpleEntityLookupInterface) Object2IntOpenHashMap(it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap) TickTask(net.minecraft.server.TickTask) RegionFile(net.minecraft.world.level.chunk.storage.RegionFile) At(org.spongepowered.asm.mixin.injection.At) LongSet(it.unimi.dsi.fastutil.longs.LongSet) ChunkLoadingFailure(net.minecraft.server.level.ChunkHolder.ChunkLoadingFailure) ChunkProgressListener(net.minecraft.server.level.progress.ChunkProgressListener) Set(java.util.Set) CHUNK_LOADED(carpet.script.CarpetEventServer.Event.CHUNK_LOADED) ThreadedLevelLightEngine(net.minecraft.server.level.ThreadedLevelLightEngine) BlockableEventLoop(net.minecraft.util.thread.BlockableEventLoop) Final(org.spongepowered.asm.mixin.Final) Collectors(java.util.stream.Collectors) Player(net.minecraft.world.entity.player.Player) ChunkAccess(net.minecraft.world.level.chunk.ChunkAccess) ChunkTaskPriorityQueueSorter(net.minecraft.server.level.ChunkTaskPriorityQueueSorter) List(java.util.List) Util(net.minecraft.Util) TicketType(net.minecraft.server.level.TicketType) Entry(java.util.Map.Entry) Shadow(org.spongepowered.asm.mixin.Shadow) LevelChunk(net.minecraft.world.level.chunk.LevelChunk) ChunkHolder(net.minecraft.server.level.ChunkHolder) Unique(org.spongepowered.asm.mixin.Unique) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) ServerWorldInterface(carpet.fakes.ServerWorldInterface) ChunkMap(net.minecraft.server.level.ChunkMap) DistanceManager(net.minecraft.server.level.ChunkMap.DistanceManager) ServerLevel(net.minecraft.server.level.ServerLevel) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) IntFunction(java.util.function.IntFunction) WorldTools(carpet.script.utils.WorldTools) ServerLightingProviderInterface(carpet.fakes.ServerLightingProviderInterface) ChunkHolderInterface(carpet.fakes.ChunkHolderInterface) Long2ObjectLinkedOpenHashMap(it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap) ChunkPos(net.minecraft.world.level.ChunkPos) Entity(net.minecraft.world.entity.Entity) Object2IntMap(it.unimi.dsi.fastutil.objects.Object2IntMap) ChunkStatus(net.minecraft.world.level.chunk.ChunkStatus) ChunkHolder(net.minecraft.server.level.ChunkHolder) ArrayList(java.util.ArrayList) ChunkAccess(net.minecraft.world.level.chunk.ChunkAccess) CompletableFuture(java.util.concurrent.CompletableFuture) ChunkMap(net.minecraft.server.level.ChunkMap) Entry(java.util.Map.Entry) ChunkTicketManagerInterface(carpet.fakes.ChunkTicketManagerInterface) Object2IntOpenHashMap(it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap) ChunkPos(net.minecraft.world.level.ChunkPos) List(java.util.List) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Pair(org.apache.commons.lang3.tuple.Pair) Player(net.minecraft.world.entity.player.Player) LevelChunk(net.minecraft.world.level.chunk.LevelChunk) SimpleEntityLookupInterface(carpet.fakes.SimpleEntityLookupInterface) ChunkHolderInterface(carpet.fakes.ChunkHolderInterface) ChunkStatus(net.minecraft.world.level.chunk.ChunkStatus) ServerLightingProviderInterface(carpet.fakes.ServerLightingProviderInterface)

Aggregations

ServerWorldInterface (carpet.fakes.ServerWorldInterface)2 ThreadedAnvilChunkStorageInterface (carpet.fakes.ThreadedAnvilChunkStorageInterface)2 WorldTools (carpet.script.utils.WorldTools)2 CarpetServer (carpet.CarpetServer)1 ChunkHolderInterface (carpet.fakes.ChunkHolderInterface)1 ChunkTicketManagerInterface (carpet.fakes.ChunkTicketManagerInterface)1 MinecraftServerInterface (carpet.fakes.MinecraftServerInterface)1 ServerLightingProviderInterface (carpet.fakes.ServerLightingProviderInterface)1 SimpleEntityLookupInterface (carpet.fakes.SimpleEntityLookupInterface)1 FeatureGenerator (carpet.helpers.FeatureGenerator)1 HUDController (carpet.logging.HUDController)1 CarpetContext (carpet.script.CarpetContext)1 CarpetEventServer (carpet.script.CarpetEventServer)1 CHUNK_GENERATED (carpet.script.CarpetEventServer.Event.CHUNK_GENERATED)1 CHUNK_LOADED (carpet.script.CarpetEventServer.Event.CHUNK_LOADED)1 CarpetScriptHost (carpet.script.CarpetScriptHost)1 CarpetScriptServer (carpet.script.CarpetScriptServer)1 Context (carpet.script.Context)1 Expression (carpet.script.Expression)1 BlockArgument (carpet.script.argument.BlockArgument)1