Как сохранять данные в мир что бы при выходе из игры они не сбрасывались?

Версия Minecraft
1.21.1
API
Fabric
35
0
Я не могу понять, как делать сохранение данных в мир. Мне надо сохранить список ChunkPos что бы даже при выходе игры данные не сбросились. Можете отправить пример кода и объяснить как все работает.
 
Я решил попробовать на легком и взял код с Fabric Wiki и там была строчка кода ServerPlayNetworking.send(playerEntity, DIRT_BROKEN, data); а у меня ServerPlayNetworking.send требует
@NotNull net.minecraft.server.network.ServerPlayerEntity player,
@NotNull net.minecraft.network.packet.CustomPayload payload

какой то CustomPayload, как в него запихнуть какие то данные не знаю
 
Это пакеты, т.е совсем не то.

Тебе нужен PersistentState(Смотри RaidManager, он сохраняет целые рейды), туда ты сохраняешь(а потом и оттуда загружаешь) то что тебе нужно.
Зарегать это можно только через миксины вроде как.

Для регистрации:

1. Создаешь интерфейс с метод getData(с помощью него будешь получать свой PersistentState)
2. Создаешь миксин на ServerWorld который наследует твой интерфейс.
3. В конструктор @Inject и определяешь свой PersistentState.
4. ((YourPersistentStateGetter) someServerWorld).getData()...

Такие данные доступны для одного мира измерения.
 
Последнее редактирование:
Я написал код по аналогии с RaidManager, ну и как мне теперь его использовать?
OpenChunkManager:
package net.catg.zonedefender.Objects;

import net.minecraft.datafixer.DataFixTypes;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.PersistentState;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.registry.RegistryWrapper;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class OpenChunkManager extends PersistentState {
    private final Map<ChunkPos, OpenChunk> openChunks = new HashMap<>();
    private final ServerWorld world;

    // Конструктор для инициализации с миром
    public OpenChunkManager(ServerWorld world) {
        this.world = world;
    }

    // Статический метод для получения типа состояния PersistentState
    public static PersistentState.Type<OpenChunkManager> getPersistentStateType(ServerWorld world) {
        return new PersistentState.Type<>(
                () -> new OpenChunkManager(world),
                (nbt, registryLookup) -> fromNbt(world, nbt),
                DataFixTypes.CHUNK
        );
    }

    // Метод для загрузки данных из NBT
    public static OpenChunkManager fromNbt(ServerWorld world, NbtCompound nbt) {
        OpenChunkManager openChunkManager = new OpenChunkManager(world);
        NbtList nbtList = nbt.getList("OpenChunks", 10);

        for (int i = 0; i < nbtList.size(); ++i) {
            NbtCompound chunkNbt = nbtList.getCompound(i);
            ChunkPos chunkPos = new ChunkPos(chunkNbt.getInt("ChunkX"), chunkNbt.getInt("ChunkZ"));
            OpenChunk openChunk = new OpenChunk(chunkPos, chunkNbt.getBoolean("IsActive"));
            openChunkManager.openChunks.put(chunkPos, openChunk);
        }

        return openChunkManager;
    }

    // Метод для сохранения состояния в NBT
    public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
        NbtList nbtList = new NbtList();
        for (OpenChunk openChunk : openChunks.values()) {
            NbtCompound chunkNbt = new NbtCompound();
            chunkNbt.putInt("ChunkX", openChunk.getChunkPos().x);
            chunkNbt.putInt("ChunkZ", openChunk.getChunkPos().z);
            chunkNbt.putBoolean("IsActive", openChunk.isActive());
            nbtList.add(chunkNbt);
        }
        nbt.put("OpenChunks", nbtList);
        return nbt;
    }

    // Метод для добавления чанк в список
    public void addOpenChunk(ChunkPos chunkPos) {
        if (!openChunks.containsKey(chunkPos)) {
            openChunks.put(chunkPos, new OpenChunk(chunkPos, true));
            this.markDirty();
        }
    }

    // Метод для удаления чанк из списка
    public void removeOpenChunk(ChunkPos chunkPos) {
        if (openChunks.containsKey(chunkPos)) {
            openChunks.remove(chunkPos);
            this.markDirty();
        }
    }

    // Метод для обновления чанков каждый тик
    public void tick() {
        for (OpenChunk openChunk : openChunks.values()) {
            openChunk.tick(world);
        }
    }

    // Геттеры
    public Map<ChunkPos, OpenChunk> getOpenChunks() {
        return openChunks;
    }

    public ServerWorld getWorld() {
        return world;
    }
}
 
А еще вот код OpenChunk
OpenChunk:
package net.catg.zonedefender.Objects;

import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.particle.DustParticleEffect;
import org.joml.Vector3f;

public class OpenChunk {
    private ChunkPos chunkPos; // Позиция чанка
    private boolean isActive;
    private int tickCounter = 0; // Счётчик тиков

    // Конструктор для создания объекта с конкретными данными
    public OpenChunk(ChunkPos chunkPos, boolean isActive) {
        this.chunkPos = chunkPos;
        this.isActive = isActive;
    }

    // Конструктор для загрузки из NBT
    public OpenChunk(NbtCompound nbt) {
        this.chunkPos = new ChunkPos(nbt.getInt("ChunkX"), nbt.getInt("ChunkZ"));
        this.isActive = nbt.getBoolean("IsActive");
    }

    // Метод для сохранения данных в NBT
    public NbtCompound toNbt(NbtCompound nbt) {
        nbt.putInt("ChunkX", this.chunkPos.x);
        nbt.putInt("ChunkZ", this.chunkPos.z);
        nbt.putBoolean("IsActive", this.isActive);
        return nbt;
    }

    // Метод для генерации частиц на границе чанка
    public void generateParticles(World world) {
        int minX = this.chunkPos.getStartX();
        int minZ = this.chunkPos.getStartZ();
        int maxX = this.chunkPos.getEndX();
        int maxZ = this.chunkPos.getEndZ();

        for (int x = minX; x <= maxX; x++) {
            for (int z = minZ; z <= maxZ; z++) {
                // Определяем высоту поверхности на текущей позиции
                int surfaceY = world.getTopY(net.minecraft.world.Heightmap.Type.MOTION_BLOCKING, x, z);

                // Если точка на границе чанка
                if (x == minX || x == maxX || z == minZ || z == maxZ) {
                    world.addParticle(
                            new DustParticleEffect(new Vector3f(0.0F, 1.0F, 0.0F), 1.0F), // Зелёные частицы
                            x + 0.5, surfaceY + 0.5, z + 0.5, // Координаты
                            0, 0, 0 // Скорость
                    );
                }
            }
        }
    }

    // Метод tick, вызывающий частицы каждые 5 тиков
    public void tick(World world) {
        if (++tickCounter >= 5) {
            tickCounter = 0; // Сброс счётчика
            generateParticles(world); // Вызываем генерацию частиц
        }
    }

    // Геттеры
    public ChunkPos getChunkPos() {
        return chunkPos;
    }

    public boolean isActive() {
        return isActive;
    }
}
вот, я же сделал сохранение и что, как мне загружать все данные при заходе в мир, или все уже сделано? Не очень понял как работает, пришлось даже ChatGpt спрашивать как все нормально сделать
 
в главном классе мода?
Нет.

Для регистрации:

1. Создаешь интерфейс с метод getData(с помощью него будешь получать свой PersistentState)
2. Создаешь миксин на ServerWorld который наследует твой интерфейс.
3. В конструктор @Inject и определяешь свой PersistentState.
4. ((YourPersistentStateGetter) someServerWorld).getData()...
 
Что то я сделал не так, после перезахода в мир не сохраняется.
ZoneDefender:
package net.catg.zonedefender;

import net.catg.zonedefender.Objects.OpenChunkManager;
import net.catg.zonedefender.PersistentStateGetter;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.ChunkPos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;

public class ZoneDefender implements ModInitializer {
    public static final String MOD_ID = "zonedefender";
    public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
    private boolean initialChunkCreated = false; // Флаг для проверки, создан ли начальный OpenChunk

    @Override
    public void onInitialize() {
        // Регистрация событий тика
        LOGGER.info("Иницилизируем!");
        ServerTickEvents.END_WORLD_TICK.register(this::onWorldTick);
    }

    private void onWorldTick(ServerWorld world) {
        PersistentStateGetter getter = (PersistentStateGetter) world;
        OpenChunkManager chunkManager = getter.getData();

        // Создаем начальный OpenChunk, если он еще не был создан
        if (!initialChunkCreated) {
            LOGGER.info("Вызываем!");
            createInitialChunk(chunkManager);
            initialChunkCreated = true; // Помечаем, что начальный OpenChunk создан
        }

        // Вызываем метод tick для обновления OpenChunks
        chunkManager.tick();
    }

    private void createInitialChunk(OpenChunkManager chunkManager) {
        ZoneDefender.LOGGER.info("Чанки: " + chunkManager.getOpenChunks());
        LOGGER.info("Начинаем создание...");
        if (!chunkManager.containsChunk(new ChunkPos(0, 0))) {
            chunkManager.addChunk(new ChunkPos(0, 0));
            chunkManager.addChunk(new ChunkPos(1, 0));
            chunkManager.addChunk(new ChunkPos(-1, 0));
            LOGGER.info("Создан начальный OpenChunk на начальной позиции");
        }
        else {
            LOGGER.info("Уже ЕЕЕЕСТЬ");
        }
    }
}

OpenChunk:
package net.catg.zonedefender.Objects;

import net.catg.zonedefender.ZoneDefender;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.Heightmap;
import net.minecraft.world.World;
import net.minecraft.particle.DustParticleEffect;
import org.joml.Vector3f;

public class OpenChunk {
    private ChunkPos chunkPos; // Позиция чанка
    private boolean isActive;
    private int tickCounter = 0; // Счётчик тиков

    // Конструктор для создания объекта с конкретными данными
    public OpenChunk(ChunkPos chunkPos, boolean isActive) {
        this.chunkPos = chunkPos;
        this.isActive = isActive;
    }

    // Конструктор для загрузки из NBT
    public OpenChunk(NbtCompound nbt) {
        this.chunkPos = new ChunkPos(nbt.getInt("ChunkX"), nbt.getInt("ChunkZ"));
        this.isActive = nbt.getBoolean("IsActive");
    }

    // Метод для сохранения данных в NBT
    public NbtCompound toNbt(NbtCompound nbt) {
        nbt.putInt("ChunkX", this.chunkPos.x);
        nbt.putInt("ChunkZ", this.chunkPos.z);
        nbt.putBoolean("IsActive", this.isActive);
        return nbt;
    }

    // Метод для генерации частиц на границе чанка
    public void generateParticles(ServerWorld world) {
        int minX = this.chunkPos.getStartX();
        int minZ = this.chunkPos.getStartZ();
        int maxX = this.chunkPos.getEndX();
        int maxZ = this.chunkPos.getEndZ();

        for (int x = minX; x <= maxX; x++) {
            for (int z = minZ; z <= maxZ; z++) {
                // Определяем высоту поверхности на текущей позиции
                int surfaceY = world.getTopY(Heightmap.Type.MOTION_BLOCKING, x, z);

                // Если точка на границе чанка
                if (x == minX || x == maxX || z == minZ || z == maxZ) {
                    // Выводим позицию частицы (BlockPos)
                    BlockPos particlePos = new BlockPos(x, surfaceY, z);


                    // Серверный вызов spawnParticles
                    world.spawnParticles(
                            new DustParticleEffect(new Vector3f(0.0F, 1.0F, 0.0F), 1.0F), // Зелёные частицы
                            x + 0.5, surfaceY + 0.5, z + 0.5, // Координаты
                            1, // Количество частиц
                            0.0, 0.0, 0.0, // Разброс
                            0.0 // Скорость
                    );
                }
            }
        }
    }



    // Метод tick, вызывающий частицы каждые 5 тиков
    public void tick(World world) {

        if (++tickCounter >= 5) {
            tickCounter = 0; // Сброс счётчика
            generateParticles((ServerWorld) world); // Вызываем генерацию частиц
        }
    }

    // Геттеры
    public ChunkPos getChunkPos() {
        return chunkPos;
    }

    public boolean isActive() {
        return isActive;
    }
}
OpenChunkManager:
package net.catg.zonedefender.Objects;

import net.catg.zonedefender.ZoneDefender;
import net.minecraft.datafixer.DataFixTypes;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.PersistentState;
import net.minecraft.registry.RegistryWrapper;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class OpenChunkManager extends PersistentState {
    private final Map<ChunkPos, OpenChunk> openChunks = new HashMap<>();
    private final ServerWorld world;

    // Конструктор для инициализации с миром
    public OpenChunkManager(ServerWorld world) {
        this.world = world;
        this.createInitialChunk(); // Создаем начальный OpenChunk
    }

    // Статический метод для получения типа состояния PersistentState
    public static PersistentState.Type<OpenChunkManager> getPersistentStateType(ServerWorld world) {
        return new PersistentState.Type<>(
                () -> new OpenChunkManager(world),
                (nbt, registryLookup) -> fromNbt(world, nbt),
                DataFixTypes.SAVED_DATA_RAIDS
        );
    }

    // Метод для загрузки данных из NBT
    public static OpenChunkManager fromNbt(ServerWorld world, NbtCompound nbt) {
        OpenChunkManager openChunkManager = new OpenChunkManager(world);
        NbtList nbtList = nbt.getList("OpenChunks", 10);

        for (int i = 0; i < nbtList.size(); ++i) {
            NbtCompound chunkNbt = nbtList.getCompound(i);
            ChunkPos chunkPos = new ChunkPos(chunkNbt.getInt("ChunkX"), chunkNbt.getInt("ChunkZ"));
            OpenChunk openChunk = new OpenChunk(chunkPos, chunkNbt.getBoolean("IsActive"));
            openChunkManager.openChunks.put(chunkPos, openChunk);
        }

        return openChunkManager;
    }

    // Метод для сохранения состояния в NBT
    public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) {
        NbtList nbtList = new NbtList();
        for (OpenChunk openChunk : openChunks.values()) {
            NbtCompound chunkNbt = new NbtCompound();
            chunkNbt.putInt("ChunkX", openChunk.getChunkPos().x);
            chunkNbt.putInt("ChunkZ", openChunk.getChunkPos().z);
            chunkNbt.putBoolean("IsActive", openChunk.isActive());
            nbtList.add(chunkNbt);
        }
        nbt.put("OpenChunks", nbtList);
        return nbt;
    }

    // Метод для создания начального чанка
    private void createInitialChunk() {
        Random random = new Random();
        ChunkPos chunkPos;

        // Генерация случайного чанка в диапазоне от -10 до 10
        do {
            int x = random.nextInt(21) - 10; // Диапазон от -10 до 10
            int z = random.nextInt(21) - 10;
            chunkPos = new ChunkPos(x, z);
        } while (this.containsChunk(chunkPos)); // Проверяем, существует ли такой OpenChunk

        // Добавляем новый OpenChunk в менеджер
        this.addChunk(chunkPos);
    }

    // Метод для добавления чанка
    public void addChunk(ChunkPos chunkPos) {
        if (!openChunks.containsKey(chunkPos)) {
            openChunks.put(chunkPos, new OpenChunk(chunkPos, true));
            this.markDirty();
        }
    }

    // Метод для проверки наличия чанка
    public boolean containsChunk(ChunkPos chunkPos) {
        return openChunks.containsKey(chunkPos);
    }

    // Метод для обновления чанков каждый тик
    public void tick() {
        for (OpenChunk openChunk : openChunks.values()) {
            openChunk.tick(world);
        }
    }

    // Геттеры
    public Map<ChunkPos, OpenChunk> getOpenChunks() {

        return openChunks;
    }

    public ServerWorld getWorld() {
        return world;
    }
}
ServerWorldMixin:
package net.catg.zonedefender.mixin;

import net.catg.zonedefender.PersistentStateGetter;
import net.catg.zonedefender.Objects.OpenChunkManager;
import net.catg.zonedefender.ZoneDefender;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.PersistentState;
import net.minecraft.nbt.NbtCompound;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(ServerWorld.class)
public abstract class ServerWorldMixin implements PersistentStateGetter {
    private OpenChunkManager openChunkManager;

    // Внедрение конструктора и инициализация PersistentState

    @Inject(method = "<init>", at = @At("RETURN"))
    private void onWorldInitialized(CallbackInfo ci) {
        this.openChunkManager = new OpenChunkManager((ServerWorld) (Object) this); // Инициализация OpenChunkManager
    }

    @Override
    public OpenChunkManager getData() {
        return openChunkManager; // Возвращаем экземпляр OpenChunkManager
    }

    // Сохранение состояния OpenChunkManager в NBT
    public void saveToNBT(NbtCompound nbt) {
        if (openChunkManager != null) {
            openChunkManager.writeNbt(nbt, null); // Сохраняем состояние
        }
    }
}
PersistentStateGetter:
package net.catg.zonedefender;

import net.catg.zonedefender.Objects.OpenChunkManager;

public interface PersistentStateGetter {
    OpenChunkManager getData();
}
 
Назад
Сверху