Запрет выгрузки чанков

Версия Minecraft
1.7.10
API
Forge
236
4
22
Здравствуйте всем. Хотелось бы спросить, может ли кто-то объяснить, как создать тайл для блока, который может "запрещать" выгружать чанк(и) вокруг себя при каком-то условии. Ну что-то вроде мировых якорей RailCraft`a. Покопавшись на forge англо-форумах нашёл, что вроде бы это делается через ForgeChunkManager.Ticket. Однако, попробовав создать тикет в тайле я тут же получил ошибку и краш. Вот собственно код, которым я пытался создать тикет:
Java:
 public Ticket ticket;
@Override
    public void validate() {
        super.validate();
        ticket = ForgeChunkManager.requestTicket(Main.INSTANCE, this.worldObj, ForgeChunkManager.Type.NORMAL);
        if(ticket != null)
        ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(this.xCoord / 16, this.zCoord / 16));
    }
А в ошибке java.lang.RuntimeException: Invalid ticket request.
Конкретно на этом форме я нашёл лишь 1 тему с похожим вопросом, но там ответа увы нет. Надеюсь вы подскажете, как правильно держать чанки активными тайлом при условии, заранее спасибо.
 
Решение
Где-то в преините:
ForgeChunkManager.setForcedChunkLoadingCallback(Инстанс_мода, new Класс_с_реализацией());. В реализации должно быть чет такое:
Java:
    @Override
    public void ticketsLoaded(List<ForgeChunkManager.Ticket> tickets, World world) {
        for (ForgeChunkManager.Ticket ticket : tickets) {
           if (!ticket.isPlayerTicket()) {
              int x = ticket.getModData().getInteger("xCoord");
              int y = ticket.getModData().getInteger("yCoord");
              int z = ticket.getModData().getInteger("zCoord");
              if (y >= 0) {
                  TileEntity tile = world.getTileEntity(x, y, z);
                  if (tile instanceof МойЛоадер) {
                     МойЛоадер loader = (МойЛоадер)...

Eifel

Модератор
1,623
78
608
Для этого тебе еще надо зарегать свой ForgeChunkManager.LoadingCallback, реализовать в нем соответственный метод, который будет обновлять тикет в твоем тайле (если пришел другой тикет, соответственно старый надо релизнуть и установить новый). + делать реквест на тикет только когда твой текущий равный null, ну и тогда уже закидываешь чанки в твой тикет, вместе с координатами нужного тайла. Дополнительно, при инвалидации/уборке тайла так же надо не забывать анфорснуть чанки и релизнуть свой тикет.
 
236
4
22
Спасибо, буду разбираться (если потяну ;)). Правда пока не очень понятно, как зарегать этот ForgeChunkManager.LoadingCallback. В implements добавил - создал недостающий метод (пока не переписывал тело метода, по дефолту - инвалид тикет). Но обычно методом проб и ошибок решается такое.
 

Eifel

Модератор
1,623
78
608
Где-то в преините:
ForgeChunkManager.setForcedChunkLoadingCallback(Инстанс_мода, new Класс_с_реализацией());. В реализации должно быть чет такое:
Java:
    @Override
    public void ticketsLoaded(List<ForgeChunkManager.Ticket> tickets, World world) {
        for (ForgeChunkManager.Ticket ticket : tickets) {
           if (!ticket.isPlayerTicket()) {
              int x = ticket.getModData().getInteger("xCoord");
              int y = ticket.getModData().getInteger("yCoord");
              int z = ticket.getModData().getInteger("zCoord");
              if (y >= 0) {
                  TileEntity tile = world.getTileEntity(x, y, z);
                  if (tile instanceof МойЛоадер) {
                     МойЛоадер loader = (МойЛоадер) tile;
                     //там мы обновляем тикет соотвественно
                     loader.forceChunkLoading(ticket);
                  }
               }
            }
        }
    }

Ну и не забыть так же в новый запрашиваемый тикет упаковать коорды своего лодера аналогичным способом, как и достаются тут.
 
Последнее редактирование:
236
4
22
К моему сожалению, чанк оказывается грузил setworldspawn, а не тайл. Конкретно тайл, увы, не грузит ничего. Возможно, я что-то не так делаю... В преините колбек я зарегистрировал, твой код вставил. Но что-то идёт не так. В консоле ошибок нет. Пожалуй, выложу полный код своего тайла(тут есть сторонние интерфейсы, для взаимодействия с энергией. На них можно не смотреть). На данный момент, я пытаюсь добиться, чтобы тайл грузил чанк где стоит, если isRunning == true (у меня другое условие будет, но пока так).
TileChunkLoader:
public class TileChunkLoader extends TilePowerAcceptor implements IWrenchable, IInventory, ForgeChunkManager.LoadingCallback{

    public Inventory inventory = new Inventory(1, "TileChunkLoader", 64);

    public boolean isRunning;
    public long tickTime;

    public int euTick = 32;

    public TileChunkLoader() {
        super(1);
    }

    private Ticket ticket;
    
    @Override
    public void writeToNBT(NBTTagCompound tag) {
    super.writeToNBT(tag);
    inventory.writeToNBT(tag);
    tag.setBoolean("isRunning", isRunning);
    tag.setLong("tickTime", tickTime);
    }
    
    @Override
    public void readFromNBT(NBTTagCompound tag) {
    super.readFromNBT(tag);
    isRunning = tag.getBoolean("isRunning");
    inventory.readFromNBT(tag);
    tickTime = tag.getLong("tickTime");
    }
    
    @Override
    public void updateEntity() {
        super.updateEntity();
    }
    
    @Override
    public void validate() {
        super.validate();
        if(worldObj.isRemote)
        {
        ticket = ForgeChunkManager.requestTicket(Core.INSTANCE, this.worldObj, ForgeChunkManager.Type.NORMAL);
        if(ticket != null)
        ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(this.xCoord, this.zCoord));
        }
    }
    
    
    @Override
    public void invalidate() {
        if(worldObj.isRemote)
        {
        if(ticket != null)
        ForgeChunkManager.releaseTicket(ticket);
        }
        super.invalidate();
    }
    
    @Override
    public boolean wrenchCanSetFacing(EntityPlayer entityPlayer, int side) {
        return false;
    }

    @Override
    public short getFacing() {
        return 0;
    }

    @Override
    public void setFacing(short facing) {
    }

    @Override
    public boolean wrenchCanRemove(EntityPlayer entityPlayer) {
        if (entityPlayer.isSneaking()) {
            return true;
        }
        return false;
    }

    @Override
    public float getWrenchDropRate() {
        return 1.0F;
    }

    @Override
    public ItemStack getWrenchDrop(EntityPlayer entityPlayer) {
        return new ItemStack(ModBlocks.ChunkLoader, 1);
    }

    public boolean isComplete() {
        return false;
    }

    @Override
    public int getSizeInventory() {
        return inventory.getSizeInventory();
    }

    @Override
    public ItemStack getStackInSlot(int slot) {
        return inventory.getStackInSlot(slot);
    }

    @Override
    public ItemStack decrStackSize(int slot, int amount) {
        return inventory.decrStackSize(slot, amount);
    }

    @Override
    public ItemStack getStackInSlotOnClosing(int slot) {
        return inventory.getStackInSlotOnClosing(slot);
    }

    @Override
    public void setInventorySlotContents(int slot, ItemStack stack) {
        inventory.setInventorySlotContents(slot, stack);
    }

    @Override
    public String getInventoryName() {
        return inventory.getInventoryName();
    }

    @Override
    public boolean hasCustomInventoryName() {
        return inventory.hasCustomInventoryName();
    }

    @Override
    public int getInventoryStackLimit() {
        return inventory.getInventoryStackLimit();
    }

    @Override
    public boolean isUseableByPlayer(EntityPlayer player) {
        return inventory.isUseableByPlayer(player);
    }

    @Override
    public void openInventory() {
        inventory.openInventory();
    }

    @Override
    public void closeInventory() {
        inventory.closeInventory();
    }

    @Override
    public boolean isItemValidForSlot(int slot, ItemStack stack) {
        return inventory.isItemValidForSlot(slot, stack);
    }

    @Override
    public double getMaxPower() {
        return 10000;
    }

    @Override
    public boolean canAcceptEnergy(ForgeDirection direction) {
        return true;
    }

    @Override
    public boolean canProvideEnergy(ForgeDirection direction) {
        return false;
    }

    @Override
    public double getMaxOutput() {
        return 0;
    }

    @Override
    public double getMaxInput() {
        return 32;
    }
    
    private void forceChunkLoading(Ticket ticket) {
        if(ticket != null)
        ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(this.xCoord, this.zCoord));
    }
    
    @Override
    public void ticketsLoaded(List<ForgeChunkManager.Ticket> tickets, World world) {
        for (ForgeChunkManager.Ticket ticket : tickets) {
           if (!ticket.isPlayerTicket()) {
              int x = ticket.getModData().getInteger("xCoord");
              int y = ticket.getModData().getInteger("yCoord");
              int z = ticket.getModData().getInteger("zCoord");
              if (y >= 0) {
                  TileEntity tile = world.getTileEntity(x, y, z);
                  if (tile instanceof TileChunkLoader) {
                      TileChunkLoader loader = (TileChunkLoader) tile;
                     loader.forceChunkLoading(ticket);
                  }
               }
            }
        }
    }
}
Наперёд, хочу спросить, как можно грузить несколько чанков 1 тайлом? Хранить несколько тикетов в ArrayList<Ticket>, или можно сразу несколько чанков в 1 тикет добавить?
 

Eifel

Модератор
1,623
78
608
Ты не обновляешь тикет, когда тот приходит из колбэка, добавляешь в тикет чанк с координатами тайла, а не с координатами чанка(!), так же, не записываешь коорды своего тайла в запрашиваемый тикет. Дополнительно, можешь еще в обновлялке своего тайла чекать тикет и соответственно запускать, если тот равен null. Если хочешь больше чанков - нет проблема, добавляешь больше: вызываешь forceChunk для чанков вокруг (в один и тот же тикет).
 
Последнее редактирование:
236
4
22
Записал координаты чанка через worldObj.getChunkFromBlockCoords(xCoord, zCoord).xPosition и worldObj.getChunkFromBlockCoords(xCoord, zCoord).zPosition . В данном случае чанк загрузился. Вопрос остался лишь 1 - как в тикет запихать корды тайла? Что-то не могу найти подходящий метод....
 

Eifel

Модератор
1,623
78
608
Java:
this.ticket.getModData().setInteger("xCoord", this.xCoord);
this.ticket.getModData().setInteger("yCoord", this.yCoord);
this.ticket.getModData().setInteger("zCoord", this.zCoord);

А чтобы получить коорды чанка, все что нужно - this.xCoord >> 4, this.zCoord >> 4, вместо этих оргомных методов. И быстро, и лаконично.
 
236
4
22
Написал логику лоадера, но что-то пошло не так. Вся логина работает за счёт моего метода updateChunks(radius, load)
Java:
    private void updateChunks(int radius, boolean load)
    {
        int startX = getChunk().xPosition - radius;
        int startZ = getChunk().zPosition - radius;
        if(load) {
            ticket = ForgeChunkManager.requestTicket(Core.INSTANCE, this.worldObj, ForgeChunkManager.Type.NORMAL);
            if(ticket!=null)
            {
                this.ticket.getModData().setInteger("xCoord", this.xCoord);
                this.ticket.getModData().setInteger("yCoord", this.yCoord);
                this.ticket.getModData().setInteger("zCoord", this.zCoord);
            }
        }
        for(int x = startX; x<=startX+radius*2; x++)
        {
            for(int z = startZ; z<=startZ+radius*2; z++)
            {
                if(load) {
                    ForgeChunkManager.forceChunk(ticket, new ChunkCoordIntPair(x, z));
                }else {
                    ForgeChunkManager.unforceChunk(ticket, new ChunkCoordIntPair(x, z));
                }
            }
        }
        if(!load)
        {
            ForgeChunkManager.releaseTicket(ticket);
        }
    }
Почему-то чанк даже не загружается. Я пытаюсь добиться такого, чтоб если вызвать с аргументом load = false - чанки все выгружаются для этого тайла, а если true - загружаются по указанному радиусу. (Радиус 0 - 1 чанк, 1 - 3х3, 2 - 5х5 и т.д.)
 
Сверху