<|start_filename|>src/main/resources/data/chickenchunks/recipes/spot_loader.json<|end_filename|>
{
	"type" : "minecraft:crafting_shaped",
	"result" : {
		"item" : "chickenchunks:spot_loader",
		"count": 10
	},
	"pattern": [
		"PPP",
		"PCP",
		"PPP"
	],
	"key": {
		"P": {
			"type": "minecraft:item",
			"item": "minecraft:ender_pearl"
		},
		"C": {
			"type": "minecraft:item",
			"item": "chickenchunks:chunk_loader"
		}
	}
	
}


<|start_filename|>src/main/java/codechicken/chunkloader/command/ChickenChunksCommand.java<|end_filename|>
package codechicken.chunkloader.command;

import codechicken.chunkloader.handler.ChickenChunksConfig;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.CommandSource;
import net.minecraft.command.arguments.GameProfileArgument;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent;

import java.util.Collection;
import java.util.UUID;

import static codechicken.chunkloader.ChickenChunks.MOD_ID;
import static net.minecraft.command.Commands.argument;
import static net.minecraft.command.Commands.literal;

/**
 * Created by covers1624 on 28/7/20.
 */
public class ChickenChunksCommand {

    private static final String RESTRICTIONS_RESET_FOR = MOD_ID + ":commands.restrictions.player.reset";

    private static final String OFFLINE_ENABLE_FOR = MOD_ID + ":commands.restrictions.player.offline.enable";
    private static final String OFFLINE_DISABLE_FOR = MOD_ID + ":commands.restrictions.player.offline.disable";
    private static final String OFFLINE_RESET_FOR = MOD_ID + ":commands.restrictions.player.offline.reset";

    private static final String TIMEOUT_SET_FOR = MOD_ID + ":commands.restrictions.player.timeout.set";
    private static final String TIMEOUT_RESET_FOR = MOD_ID + ":commands.restrictions.player.timeout.reset";

    private static final String ALLOWED_CHUNKS_SET_FOR = MOD_ID + ":commands.restrictions.player.allowed_chunks.set";
    private static final String ALLOWED_CHUNKS_RESET_FOR = MOD_ID + ":commands.restrictions.player.allowed_chunks.reset";

    private static final String CHUNKS_PER_LOADER_SET_FOR = MOD_ID + ":commands.restrictions.player.chunks_per_loader.set";
    private static final String CHUNKS_PER_LOADER_RESET_FOR = MOD_ID + ":commands.restrictions.player.chunks_per_loader.reset";

    public static void init() {
        MinecraftForge.EVENT_BUS.addListener(ChickenChunksCommand::onRegisterCommands);
    }

    private static void onRegisterCommands(RegisterCommandsEvent event) {
        CommandDispatcher<CommandSource> dispatcher = event.getDispatcher();
        dispatcher.register(literal("chickenchunks")//
                .requires(e -> e.hasPermission(4))//
                .then(literal("restrict")//
                        .then(literal("player")//
                                .then(argument("players", GameProfileArgument.gameProfile())//
                                        .then(literal("reset").executes(ChickenChunksCommand::resetRestrictions))//
                                        .then(literal("allow_offline")//
                                                .then(literal("toggle").executes(ChickenChunksCommand::toggleOfflineFor))//
                                                .then(literal("enable").executes(ChickenChunksCommand::enableOfflineFor))//
                                                .then(literal("disable").executes(ChickenChunksCommand::disableOfflineFor))//
                                                .then(literal("reset").executes(ChickenChunksCommand::resetOfflineFor))//
                                        )//
                                        .then(literal("offline_timeout")//
                                                .then(argument("timeout", IntegerArgumentType.integer()).executes(ChickenChunksCommand::setTimeoutFor))//
                                                .then(literal("reset").executes(ChickenChunksCommand::resetTimeoutFor))//
                                        )//
                                        .then(literal("allowed_chunks")//
                                                .then(argument("allowed_chunks", IntegerArgumentType.integer()).executes(ChickenChunksCommand::setAllowedChunksFor))//
                                                .then(literal("reset").executes(ChickenChunksCommand::resetAllowedChunksFor))//
                                        )//
                                        .then(literal("chunks_per_loader")//
                                                .then(argument("chunks_per_loader", IntegerArgumentType.integer()).executes(ChickenChunksCommand::setChunksPerLoaderFor))//
                                                .then(literal("reset").executes(ChickenChunksCommand::resetChunksPerLoaderFor))//
                                        )//
                                )//
                        )//
                )//
        );
    }

    private static int resetRestrictions(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.resetRestrictions(profile.getId());
            src.sendSuccess(new TranslationTextComponent(RESTRICTIONS_RESET_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    private static int toggleOfflineFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            boolean state = !restrictions.canLoadOffline();
            restrictions.setAllowOffline(state);
            src.sendSuccess(new TranslationTextComponent(state ? OFFLINE_ENABLE_FOR : OFFLINE_DISABLE_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    private static int enableOfflineFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        return setOfflineFor(ctx, true);
    }

    private static int disableOfflineFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        return setOfflineFor(ctx, false);
    }

    private static int setOfflineFor(CommandContext<CommandSource> ctx, boolean state) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.setAllowOffline(state);
            src.sendSuccess(new TranslationTextComponent(state ? OFFLINE_ENABLE_FOR : OFFLINE_DISABLE_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    private static int resetOfflineFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.remAllowOffline();
            src.sendSuccess(new TranslationTextComponent(OFFLINE_RESET_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    public static int setTimeoutFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        int timeout = IntegerArgumentType.getInteger(ctx, "timeout");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.setOfflineTimeout(timeout);
            src.sendSuccess(new TranslationTextComponent(TIMEOUT_SET_FOR, profile.getName(), timeout), true);
        }
        return profiles.size();
    }

    private static int resetTimeoutFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.remOfflineTimeout();
            src.sendSuccess(new TranslationTextComponent(TIMEOUT_RESET_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    public static int setAllowedChunksFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        int chunks = IntegerArgumentType.getInteger(ctx, "allowed_chunks");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.setTotalAllowedChunks(chunks);
            src.sendSuccess(new TranslationTextComponent(ALLOWED_CHUNKS_SET_FOR, profile.getName(), chunks), true);
        }
        return profiles.size();
    }

    private static int resetAllowedChunksFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.remTotalAllowedChunks();
            src.sendSuccess(new TranslationTextComponent(ALLOWED_CHUNKS_RESET_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    public static int setChunksPerLoaderFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        int chunks = IntegerArgumentType.getInteger(ctx, "chunks_per_loader");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.setChunksPerLoader(chunks);
            src.sendSuccess(new TranslationTextComponent(CHUNKS_PER_LOADER_SET_FOR, profile.getName(), chunks), true);
        }
        return profiles.size();
    }

    private static int resetChunksPerLoaderFor(CommandContext<CommandSource> ctx) throws CommandSyntaxException {
        CommandSource src = ctx.getSource();
        Collection<GameProfile> profiles = GameProfileArgument.getGameProfiles(ctx, "players");
        for (GameProfile profile : profiles) {
            ChickenChunksConfig.Restrictions restrictions = ChickenChunksConfig.getOrCreateRestrictions(profile.getId());
            restrictions.remChunksPerLoader();
            src.sendSuccess(new TranslationTextComponent(CHUNKS_PER_LOADER_RESET_FOR, profile.getName()), true);
        }
        return profiles.size();
    }

    private static ChickenChunksConfig.Restrictions getPlayerRestrictions(UUID player) {
        return ChickenChunksConfig.getOrCreateRestrictions(player);
    }
}


<|start_filename|>src/main/java/codechicken/chunkloader/handler/ChickenChunksConfig.java<|end_filename|>
package codechicken.chunkloader.handler;

import codechicken.lib.config.ConfigTag;
import codechicken.lib.config.StandardConfigFile;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.server.MinecraftServer;

import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

/**
 * Created by covers1624 on 5/4/20.
 */
public class ChickenChunksConfig {

    private static ConfigTag config;
    private static ConfigTag playerRestrictions;
    private static final Map<UUID, Restrictions> perPlayerRestrictions = new HashMap<>();
    private static boolean opsBypassRestrictions;
    private static boolean opsAccessAllLoaders;
    private static boolean globalAllowOffline;
    private static int globalOfflineTimeout;
    private static int globalTotalAllowedChunks;
    private static int globalChunksPerLoader;

    public static void load() {
        config = new StandardConfigFile(Paths.get("./config/ChickenChunks.cfg")).load();
        opsBypassRestrictions = config.getTag("opsBypassRestrictions")//
                .setComment("If Players with OP permissions bypass chunk loading restrictions.")//
                .setDefaultBoolean(false)//
                .getBoolean();
        opsAccessAllLoaders = config.getTag("opsAccessAllLoaders")//
                .setComment("If Players with OP permissions can manage other users ChunkLoaders")//
                .setDefaultBoolean(true)//
                .getBoolean();
        globalAllowOffline = config.getTag("allowOffline")//
                .setComment("If chunks should stay loaded when a ChunkLoader's owner is offline.")//
                .setDefaultBoolean(true)//
                .getBoolean();
        globalOfflineTimeout = config.getTag("offlineTimeout")//
                .setComment("How long in minutes ChickenChunks should wait after a Player logs out to unload their chunks. Only effective when allowOffline=false")//
                .setDefaultInt(0)//
                .getInt();
        globalTotalAllowedChunks = config.getTag("totalAllowedChunks")//
                .setComment("The number of chunks each player is allowed to load in total.")//
                .setDefaultInt(5000)//
                .getInt();
        globalChunksPerLoader = config.getTag("chunksPerLoader")//
                .setComment("The number of chunks each ChunkLoader is allowed to load in total.")//
                .setDefaultInt(400)//
                .getInt();
        playerRestrictions = config.getTag("playerRestrictions")//
                .setSyncToClient()//
                .setComment("Specifies restrictions for each player, Use /chickenchunks instead.");
        //TODO Re add this when the config system gets rewritten..
        //ConfigSyncManager.registerSync(new ResourceLocation(MOD_ID, "player_restrictions"), playerRestrictions);
        playerRestrictions.setSyncCallback((tag, syncType) -> {
            perPlayerRestrictions.clear();
            for (String uuidString : tag.getChildNames()) {
                ConfigTag playerTag = tag.getTag(uuidString);
                UUID uuid = UUID.fromString(uuidString);
                perPlayerRestrictions.put(uuid, new Restrictions(playerTag));
            }
        });
        config.save();
        playerRestrictions.runSync();
    }

    public static boolean doesBypassRestrictions(MinecraftServer server, UUID playerUUID) {
        ServerPlayerEntity player = server.getPlayerList().getPlayer(playerUUID);
        if (player != null && server.getPlayerList().getOps().contains(player.getGameProfile())) {
            return opsBypassRestrictions;
        }
        return false;
    }

    public static boolean doesBypassLoaderAccess(ServerPlayerEntity player) {
        if (player.getServer().getPlayerList().getOps().contains(player.getGameProfile())) {
            return opsAccessAllLoaders;
        }
        return false;
    }

    public static Restrictions getOrCreateRestrictions(UUID player) {
        return perPlayerRestrictions.computeIfAbsent(player, e -> {
            ConfigTag tag = playerRestrictions.getTag(player.toString()).markDirty();
            tag.save();
            return new Restrictions(tag);
        });
    }

    public static void resetRestrictions(UUID player) {
        perPlayerRestrictions.remove(player);
        playerRestrictions.deleteTag(player.toString());
        playerRestrictions.save();
    }

    public static Restrictions getRestrictions(UUID player) {
        return perPlayerRestrictions.getOrDefault(player, Restrictions.EMPTY);
    }

    public static class Restrictions {

        public static final Restrictions EMPTY = new Restrictions();

        private Optional<Boolean> allowOffline = Optional.empty();
        private Optional<Integer> offlineTimeout = Optional.empty();
        private Optional<Integer> totalAllowedChunks = Optional.empty();
        private Optional<Integer> chunksPerLoader = Optional.empty();

        private ConfigTag tag;

        public Restrictions() {
        }

        public Restrictions(ConfigTag tag) {
            this.tag = tag;
            allowOffline = Optional.ofNullable(tag.getTagIfPresent("allowOffline"))//
                    .map(ConfigTag::getBoolean);
            offlineTimeout = Optional.ofNullable(tag.getTagIfPresent("offlineTimeout"))//
                    .map(ConfigTag::getInt);
            totalAllowedChunks = Optional.ofNullable(tag.getTagIfPresent("totalAllowedChunks"))//
                    .map(ConfigTag::getInt);
            chunksPerLoader = Optional.ofNullable(tag.getTagIfPresent("chunksPerLoader"))//
                    .map(ConfigTag::getInt);
        }

        public boolean canLoadOffline() {
            return allowOffline.orElse(globalAllowOffline);
        }

        public int getOfflineTimeout() {
            return offlineTimeout.orElse(globalOfflineTimeout);
        }

        public int getTotalAllowedChunks() {
            return totalAllowedChunks.orElse(globalTotalAllowedChunks);
        }

        public int getChunksPerLoader() {
            return chunksPerLoader.orElse(globalChunksPerLoader);
        }

        public void setAllowOffline(boolean state) {
            tag.getTag("allowOffline").setBoolean(state).save();
            allowOffline = Optional.of(state);
        }

        public void setOfflineTimeout(int num) {
            tag.getTag("offlineTimeout").setInt(num).save();
            offlineTimeout = Optional.of(num);
        }

        public void setTotalAllowedChunks(int num) {
            tag.getTag("totalAllowedChunks").setInt(num).save();
            totalAllowedChunks = Optional.of(num);
        }

        public void setChunksPerLoader(int num) {
            tag.getTag("chunksPerLoader").setInt(num).save();
            chunksPerLoader = Optional.of(num);
        }

        public void remAllowOffline() {
            tag.deleteTag("allowOffline").save();
            allowOffline = Optional.empty();
        }

        public void remOfflineTimeout() {
            tag.deleteTag("offlineTimeout").save();
            offlineTimeout = Optional.empty();
        }

        public void remTotalAllowedChunks() {
            tag.deleteTag("totalAllowedChunks").save();
            totalAllowedChunks = Optional.empty();
        }

        public void remChunksPerLoader() {
            tag.deleteTag("chunksPerLoader").save();
            chunksPerLoader = Optional.empty();
        }

        public boolean isEmpty() {
            return !allowOffline.isPresent() && !offlineTimeout.isPresent() && !totalAllowedChunks.isPresent() && !chunksPerLoader.isPresent();
        }
    }

}


<|start_filename|>src/main/resources/assets/chickenchunks/lang/tr_tr.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Chunk Yükleyici",
  "block.chickenchunks.spot_loader": "Nokta Yükleyici",
  "chickenchunks.gui.name": "Chunk Yükleyici",
  "chickenchunks.gui.showlasers": "Lazerleri Aç",
  "chickenchunks.gui.hidelasers": "Lazerleri Kapat",
  "chickenchunks.gui.chunk": "%d Chunk",
  "chickenchunks.gui.chunks": "%d Chunklar",
  "chickenchunks.gui.radius": "Etki Alanı",
  "chickenchunks.shape.square": "Kare",
  "chickenchunks.shape.circle": "Daire",
  "chickenchunks.shape.linex": "X Hattı",
  "chickenchunks.shape.linez": "Z Hattı",
  "chickenchunks.accessdenied": "Bu Chunk Yükleyici size ait değildir.",
  "command.ccdebug": "/ccdebug [boyut]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Bu Chunk Yükleyici size ait olmadığı için kullanamazsınız.",
  "command.chunkloaders.alreadyopen": "Chunk Görüntüleyicisi zaten açık."
}

<|start_filename|>src/main/resources/assets/chickenchunks/lang/pt_br.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Carregador de Chunk",
  "block.chickenchunks.spot_loader": "Carregador Local",
  "chickenchunks.gui.name": "Carregador de Chunk",
  "chickenchunks.gui.showlasers": "Mostrar Lasers",
  "chickenchunks.gui.hidelasers": "Esconder Lasers",
  "chickenchunks.gui.chunk": "%d Chunk",
  "chickenchunks.gui.chunks": "%d Chunks",
  "chickenchunks.gui.radius": "Raio",
  "chickenchunks.shape.square": "Quadrado",
  "chickenchunks.shape.circle": "Círculo",
  "chickenchunks.shape.linex": "Linha X",
  "chickenchunks.shape.linez": "Linha Z",
  "chickenchunks.accessdenied": "Este carregador de Chunk não pertence a você.",
  "command.ccdebug": "/ccdebug [dimensão]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Você não está autorizado a usar o Visualizador de Chunks.",
  "command.chunkloaders.alreadyopen": "Visualizador de chunks já aberto."
}

<|start_filename|>src/main/resources/assets/chickenchunks/lang/it_it.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Carica-chunk",
  "block.chickenchunks.spot_loader": "Carica-punto",
  "chickenchunks.gui.name": "Carica-chunk",
  "chickenchunks.gui.showlasers": "mostra lasers",
  "chickenchunks.gui.hidelasers": "Nascondi lasers",
  "chickenchunks.gui.chunk": "%d Chunk",
  "chickenchunks.gui.chunks": "%d Chunks",
  "chickenchunks.gui.radius": "Raggio",
  "chickenchunks.shape.square": "Quadrato",
  "chickenchunks.shape.circle": "Cerchio",
  "chickenchunks.shape.linex": "Linea X",
  "chickenchunks.shape.linez": "Linea Z",
  "chickenchunks.accessdenied": "Questo Carica-chunk non ti appartiene.",
  "command.ccdebug": "/ccdebug [dimensione]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Non hai il permesso di usare il ChunkViewer.",
  "command.chunkloaders.alreadyopen": "ChunkViewer già aperto."
}

<|start_filename|>src/main/java/codechicken/chunkloader/api/ChunkLoaderShape.java<|end_filename|>
package codechicken.chunkloader.api;

import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TranslationTextComponent;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public enum ChunkLoaderShape {
    SQUARE("square"),
    CIRCLE("circle"),
    LINE_X("linex"),
    LINE_Z("linez");

    private final String name;
    private static final ChunkLoaderShape[] VALUES = values();
    private static final ChunkLoaderShape[] NEXT_LOOKUP = Arrays.stream(VALUES)//
            .map(e -> VALUES[(e.ordinal() + 1) % VALUES.length])//
            .toArray(ChunkLoaderShape[]::new);
    private static final ChunkLoaderShape[] PREV_LOOKUP = Arrays.stream(VALUES)//
            .map(e -> VALUES[Math.floorMod((e.ordinal() - 1), VALUES.length)])//
            .toArray(ChunkLoaderShape[]::new);

    ChunkLoaderShape(String s) {
        name = s;
    }

    public ChunkLoaderShape next() {
        return NEXT_LOOKUP[ordinal()];
    }

    public ChunkLoaderShape prev() {
        return PREV_LOOKUP[ordinal()];
    }

    public Set<ChunkPos> getLoadedChunks(int chunkx, int chunkz, int radius) {
        Set<ChunkPos> chunks = new HashSet<>();
        switch (this) {
            case SQUARE:
                for (int cx = chunkx - radius; cx <= chunkx + radius; cx++) {
                    for (int cz = chunkz - radius; cz <= chunkz + radius; cz++) {
                        chunks.add(new ChunkPos(cx, cz));
                    }
                }
                break;
            case LINE_X:
                for (int cx = chunkx - radius; cx <= chunkx + radius; cx++) {
                    chunks.add(new ChunkPos(cx, chunkz));
                }
                break;
            case LINE_Z:
                for (int cz = chunkz - radius; cz <= chunkz + radius; cz++) {
                    chunks.add(new ChunkPos(chunkx, cz));
                }
                break;
            case CIRCLE:
                for (int cx = chunkx - radius; cx <= chunkx + radius; cx++) {
                    for (int cz = chunkz - radius; cz <= chunkz + radius; cz++) {
                        double distSquared = (cx - chunkx) * (cx - chunkx) + (cz - chunkz) * (cz - chunkz);
                        if (distSquared <= radius * radius) {
                            chunks.add(new ChunkPos(cx, cz));
                        }
                    }
                }

        }
        return chunks;
    }

    public ITextComponent getTranslation() {
        return new TranslationTextComponent("chickenchunks.shape." + name);
    }
}


<|start_filename|>src/main/java/codechicken/chunkloader/proxy/Proxy.java<|end_filename|>
package codechicken.chunkloader.proxy;

import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;

public class Proxy {

    public void commonSetup(FMLCommonSetupEvent event) {

    }

    public void clientSetup(FMLClientSetupEvent event) {
    }
}


<|start_filename|>src/main/java/codechicken/chunkloader/api/IChunkLoader.java<|end_filename|>
package codechicken.chunkloader.api;

import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;

import java.util.Set;
import java.util.UUID;

public interface IChunkLoader {

    UUID getOwner();

    @Deprecated
    String getMod();

    World world();

    BlockPos pos();

    void deactivate();

    void activate();

    boolean isValid();

    Set<ChunkPos> getChunks();
}


<|start_filename|>src/main/java/codechicken/chunkloader/client/ChunkLoaderItemModel.java<|end_filename|>
package codechicken.chunkloader.client;

import codechicken.lib.model.bakedmodels.WrappedItemModel;
import codechicken.lib.render.CCModelLibrary;
import codechicken.lib.render.CCRenderState;
import codechicken.lib.render.RenderUtils;
import codechicken.lib.render.item.IItemRenderer;
import codechicken.lib.util.ClientUtils;
import codechicken.lib.util.TransformUtils;
import codechicken.lib.vec.Matrix4;
import codechicken.lib.vec.Rotation;
import codechicken.lib.vec.Vector3;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.IModelTransform;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.item.ItemStack;

/**
 * Created by covers1624 on 5/07/2017.
 */
public class ChunkLoaderItemModel extends WrappedItemModel implements IItemRenderer {

    private final boolean spotLoader;

    public ChunkLoaderItemModel(IBakedModel wrappedModel, boolean spotLoader) {
        super(wrappedModel);
        this.spotLoader = spotLoader;
    }

    @Override
    public void renderItem(ItemStack stack, TransformType transformType, MatrixStack mStack, IRenderTypeBuffer getter, int packedLight, int packedOverlay) {
        renderWrapped(stack, transformType, mStack, getter, packedLight, packedOverlay, false);

        double rot = ClientUtils.getRenderTime() / 6F;
        double height;
        double size;

        if (!spotLoader) {
            height = 0.9;
            size = 0.08;
        } else {
            height = 0.55;
            size = 0.05;
        }

        CCRenderState ccrs = CCRenderState.instance();
        ccrs.brightness = packedLight;
        ccrs.overlay = packedOverlay;
        ccrs.reset();

        Matrix4 pearlMat = RenderUtils.getMatrix(new Matrix4(mStack), new Vector3(0.5, height, 0.5), new Rotation(rot, Vector3.Y_POS), size);
        ccrs.brightness = 15728880;
        ccrs.bind(TileChunkLoaderRenderer.pearlType, getter);
        CCModelLibrary.icosahedron4.render(ccrs, pearlMat);
        ccrs.reset();
    }

    @Override
    public IModelTransform getModelTransform() {
        return TransformUtils.DEFAULT_BLOCK;
    }

    @Override
    public boolean useAmbientOcclusion() {
        return true;
    }

    @Override
    public boolean isGui3d() {
        return true;
    }

    @Override
    public boolean usesBlockLight() {
        return true;
    }
}


<|start_filename|>src/main/resources/assets/chickenchunks/lang/fr_fr.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Chargeur de Chunk",
  "block.chickenchunks.spot_loader": "Chargeur de Lieu",
  "chickenchunks.gui.name": "Chargeur de Chunk",
  "chickenchunks.gui.showlasers": "Montrer les rayons",
  "chickenchunks.gui.hidelasers": "Cacher les rayons",
  "chickenchunks.gui.chunk": "%d Chunk",
  "chickenchunks.gui.chunks": "%d Chunks",
  "chickenchunks.gui.radius": "Rayon",
  "chickenchunks.shape.square": "Carré",
  "chickenchunks.shape.circle": "Cercle",
  "chickenchunks.shape.linex": "Ligne X",
  "chickenchunks.shape.linez": "Ligne Z",
  "chickenchunks.accessdenied": "Ce Chargeur de Chunk ne vous appartient pas.",
  "command.ccdebug": "/ccdebug [dimension]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Vous n'êtes pas autorisé à utiliser le visualiseur de Chunks.",
  "command.chunkloaders.alreadyopen": "Le visualiseur de Chunks est déjà ouvert."
}

<|start_filename|>src/main/java/codechicken/chunkloader/block/BlockSpotLoader.java<|end_filename|>
package codechicken.chunkloader.block;

import codechicken.chunkloader.tile.TileSpotLoader;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;

import javax.annotation.Nullable;

/**
 * Created by covers1624 on 2/11/19.
 */
public class BlockSpotLoader extends BlockChunkLoader {

    public static final VoxelShape SHAPE = VoxelShapes.box(0.25F, 0, 0.25F, 0.75F, 0.4375F, 0.75F);

    @Nullable
    @Override
    public TileEntity createTileEntity(BlockState state, IBlockReader world) {
        return new TileSpotLoader();
    }

    @Override
    public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
        return SHAPE;
    }

    @Override
    public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) {
        return ActionResultType.PASS;
    }
}


<|start_filename|>src/main/java/codechicken/chunkloader/block/BlockChunkLoader.java<|end_filename|>
package codechicken.chunkloader.block;

import codechicken.chunkloader.handler.ChickenChunksConfig;
import codechicken.chunkloader.network.ChickenChunksNetwork;
import codechicken.chunkloader.tile.TileChunkLoader;
import codechicken.chunkloader.tile.TileChunkLoaderBase;
import codechicken.lib.packet.PacketCustom;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.text.TranslationTextComponent;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;

import javax.annotation.Nullable;

import static codechicken.chunkloader.network.ChickenChunksNetwork.*;

public class BlockChunkLoader extends Block {

    public static final VoxelShape SHAPE = VoxelShapes.box(0, 0, 0, 1, 0.75F, 1);

    public BlockChunkLoader() {
        super(Block.Properties.of(Material.STONE)//
                .strength(20F, 100F)//
                .sound(SoundType.STONE)//
        );
    }

    @Override
    public boolean hasTileEntity(BlockState state) {
        return true;
    }

    @Nullable
    @Override
    public TileEntity createTileEntity(BlockState state, IBlockReader world) {
        return new TileChunkLoader();
    }

    @Override
    public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
        return SHAPE;
    }

    @Override
    public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, Direction side) {
        return true;
    }

    @Override
    public ActionResultType use(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) {
        if (!world.isClientSide) {
            TileChunkLoader tile = (TileChunkLoader) world.getBlockEntity(pos);
            if (tile.owner == null) {
                player.sendMessage(new TranslationTextComponent("chickenchunks.brokentile"), Util.NIL_UUID);
            } else if (tile.owner.equals(player.getUUID()) || ChickenChunksConfig.doesBypassLoaderAccess((ServerPlayerEntity) player)) {
                PacketCustom packet = new PacketCustom(NET_CHANNEL, C_OPEN_LOADER_GUI);
                packet.writePos(pos);
                packet.sendToPlayer((ServerPlayerEntity) player);
            } else {
                player.sendMessage(new TranslationTextComponent("chickenchunks.accessdenied"), Util.NIL_UUID);
            }
        }
        return ActionResultType.SUCCESS;
    }

    @Override
    public void setPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack) {
        if (world.isClientSide) {
            return;
        }
        TileChunkLoaderBase loader = (TileChunkLoaderBase) world.getBlockEntity(pos);
        loader.onBlockPlacedBy(placer);
    }
}


<|start_filename|>src/main/resources/assets/chickenchunks/lang/zh_tw.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "區塊載入器",
  "block.chickenchunks.spot_loader": "單點載入器",
  "chickenchunks.gui.name": "區塊載入器",
  "chickenchunks.gui.showlasers": "顯示鐳射",
  "chickenchunks.gui.hidelasers": "隱藏鐳射",
  "chickenchunks.gui.chunk": "%d 個區塊",
  "chickenchunks.gui.chunks": "%d 個區塊",
  "chickenchunks.gui.radius": "半徑",
  "chickenchunks.shape.square": "方形",
  "chickenchunks.shape.circle": "圓形",
  "chickenchunks.shape.linex": "線形 X",
  "chickenchunks.shape.linez": "線形 Z"
}

<|start_filename|>src/main/resources/assets/chickenchunks/lang/pl_pl.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Blok ładujący chunki",
  "block.chickenchunks.spot_loader": "Blok ładujący miejsca",
  "chickenchunks.gui.name": "Blok ładujący chunki",
  "chickenchunks.gui.showlasers": "Pokazuj lasery",
  "chickenchunks.gui.hidelasers": "Chowaj lasery",
  "chickenchunks.gui.chunk": "%d chunk",
  "chickenchunks.gui.chunks": "%d chunków",
  "chickenchunks.gui.radius": "Promień",
  "chickenchunks.shape.square": "Kwadrat",
  "chickenchunks.shape.circle": "Koło",
  "chickenchunks.shape.linex": "Linia X",
  "chickenchunks.shape.linez": "Linia Z",
  "chickenchunks.accessdenied": "Ten Blok ładujący chunki nie należy do ciebie.",
  "command.ccdebug": "/ccdebug [wymiar]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Nie maż zezwolenia na używanie Pokazywacza chunków.",
  "command.chunkloaders.alreadyopen": "Pokazywacz chunków już otwarty."
}

<|start_filename|>src/main/java/codechicken/chunkloader/ChickenChunks.java<|end_filename|>
package codechicken.chunkloader;

import codechicken.chunkloader.command.ChickenChunksCommand;
import codechicken.chunkloader.handler.ChickenChunksConfig;
import codechicken.chunkloader.network.ChickenChunksNetwork;
import codechicken.chunkloader.proxy.Proxy;
import codechicken.chunkloader.proxy.ProxyClient;
import codechicken.chunkloader.world.ChunkLoaderHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLDedicatedServerSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Mod (ChickenChunks.MOD_ID)
public class ChickenChunks {

    public static Logger logger = LogManager.getLogger("ChickenChunks");

    public static final String MOD_ID = "chickenchunks";

    public static Proxy proxy;

    public ChickenChunks() {
        proxy = DistExecutor.safeRunForDist(() -> ProxyClient::new, () -> Proxy::new);
        ChickenChunksConfig.load();
        FMLJavaModLoadingContext.get().getModEventBus().register(this);
        ChickenChunksCommand.init();
        ChickenChunksNetwork.init();
    }

    @SubscribeEvent
    public void onCommonSetup(FMLCommonSetupEvent event) {
        proxy.commonSetup(event);
        ChunkLoaderHandler.init();
    }

    @SubscribeEvent
    public void onClientSetup(FMLClientSetupEvent event) {
        proxy.clientSetup(event);
    }

    @SubscribeEvent
    public void onServerSetup(FMLDedicatedServerSetupEvent event) {
    }

    private static String modifyDesc(String desc) {
        desc += "\n";
        desc += "    Credits: Sanguine - Texture\n";
        return desc;
    }
}


<|start_filename|>src/main/java/codechicken/chunkloader/api/IChunkLoaderHandler.java<|end_filename|>
package codechicken.chunkloader.api;

import codechicken.chunkloader.world.ChunkLoaderHandler;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;

import java.util.Set;

/**
 * Handler for interacting with the ChickenChunks loading backend.
 * IChunkLoader implementations are expected to be TileEntities at this moment.
 * This Capability only exists on the {@link World#OVERWORLD} and handles all dimensions.
 * Due to complexity limitations, its not viable to move this to a per-world capability at the current moment.
 * <p>
 * Created by covers1624 on 5/4/20.
 */
public interface IChunkLoaderHandler {

    void addChunkLoader(IChunkLoader loader);

    void removeChunkLoader(IChunkLoader loader);

    boolean canLoadChunks(IChunkLoader loader, Set<ChunkPos> newChunks);

    void updateLoader(IChunkLoader loader);

    static IChunkLoaderHandler getCapability(IWorld world) {
        if (!(world instanceof ServerWorld)) {
            throw new IllegalArgumentException("ServerWorld required.");
        }
        ServerWorld overWorld = (ServerWorld) world;
        if (((ServerWorld) world).dimension() != World.OVERWORLD) {
            overWorld = overWorld.getServer().getLevel(World.OVERWORLD);
        }
        return overWorld.getCapability(ChunkLoaderHandler.HANDLER_CAPABILITY).orElse(null);
    }
}


<|start_filename|>src/main/resources/assets/chickenchunks/lang/cs_cz.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Načítač chunků",
  "block.chickenchunks.spot_loader": "Načítač míst",
  "chickenchunks.gui.name": "Načítač chunků",
  "chickenchunks.gui.showlasers": "Zobrazit lasery",
  "chickenchunks.gui.hidelasers": "Schovat lasery",
  "chickenchunks.gui.chunk": "%d chunk",
  "chickenchunks.gui.chunks": "%d chunků",
  "chickenchunks.gui.radius": "Vzdálenost",
  "chickenchunks.shape.square": "Čtverec",
  "chickenchunks.shape.circle": "Kruh",
  "chickenchunks.shape.linex": "Řada X",
  "chickenchunks.shape.linez": "Řada Z",
  "chickenchunks.accessdenied": "Tento načítač chunků ti nepatří.",
  "command.ccdebug": "/ccdebug [dimenze]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Nejsi oprávněn používat Zobrazovač chunků.",
  "command.chunkloaders.alreadyopen": "Zobrazovač chunků je již otevřený."
}

<|start_filename|>src/main/resources/assets/chickenchunks/lang/fr_ca.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Chargeur de chunks",
  "block.chickenchunks.spot_loader": "Chargeur de lieu",
  "chickenchunks.gui.name": "Chunks à charger",
  "chickenchunks.gui.showlasers": "Lasers : Non",
  "chickenchunks.gui.hidelasers": "Lasers : Oui",
  "chickenchunks.gui.chunk": "%d chunk",
  "chickenchunks.gui.chunks": "%d chunks",
  "chickenchunks.gui.radius": "Rayon",
  "chickenchunks.shape.square": "Carré",
  "chickenchunks.shape.circle": "Cercle",
  "chickenchunks.shape.linex": "Ligne X",
  "chickenchunks.shape.linez": "Ligne Z",
  "chickenchunks.accessdenied": "Ce chargeur de chunks ne vous appartient pas.",
  "command.ccdebug": "/ccdebug [dimension]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Vous n'êtes pas autorisé(e) d'utiliser le visualiseur de chunks.",
  "command.chunkloaders.alreadyopen": "Le visualiseur de chunks est déjà ouvert."
}

<|start_filename|>src/main/resources/assets/chickenchunks/lang/ja_jp.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "チャンクローダー",
  "block.chickenchunks.spot_loader": "スポットローダー",
  "chickenchunks.gui.name": "チャンクローダー",
  "chickenchunks.gui.showlasers": "レーザー:表示",
  "chickenchunks.gui.hidelasers": "レーザー:非表示",
  "chickenchunks.gui.chunk": "%d チャンク",
  "chickenchunks.gui.chunks": "%d チャンク",
  "chickenchunks.gui.radius": "範囲",
  "chickenchunks.shape.square": "四角形",
  "chickenchunks.shape.circle": "円形",
  "chickenchunks.shape.linex": "X線上",
  "chickenchunks.shape.linez": "Z線上",
  "chickenchunks.accessdenied": "このチャンクローダーはあなたの管理下ではありません。",
  "command.ccdebug": "/ccdebug [dimension]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "あなたはチャンクビューワを使用できません。",
  "command.chunkloaders.alreadyopen": "チャンクビューワは既に開いています。"
}

<|start_filename|>src/main/resources/assets/chickenchunks/lang/de_de.json<|end_filename|>
{
  "block.chickenchunks.chunk_loader": "Chunkloader",
  "block.chickenchunks.spot_loader": "Spotloader",
  "chickenchunks.gui.name": "Chunkloader",
  "chickenchunks.gui.showlasers": "Zeige Laser",
  "chickenchunks.gui.hidelasers": "Verstecke Laser",
  "chickenchunks.gui.chunk": "%d Chunk",
  "chickenchunks.gui.chunks": "%d Chunks",
  "chickenchunks.gui.radius": "Radius",
  "chickenchunks.shape.square": "Quadrat",
  "chickenchunks.shape.circle": "Kreis",
  "chickenchunks.shape.linex": "Gerade X",
  "chickenchunks.shape.linez": "Gerade Z",
  "chickenchunks.accessdenied": "Dieser Chunkloader gehört dir nicht.",
  "command.ccdebug": "/ccdebug [dimension]",
  "command.chunkloaders": "/chunkloaders",
  "command.chunkloaders.denied": "Dir ist die Vervendung des ChunkViewer nicht gestattet.",
  "command.chunkloaders.alreadyopen": "ChunkViewer ist bereits geöffnet."
}