package net.minecraft.world.entity.raid; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; import java.util.Iterator; import java.util.List; import java.util.OptionalInt; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.network.protocol.game.DebugPackets; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.PoiTypeTags; import net.minecraft.util.datafix.DataFixTypes; import net.minecraft.world.entity.ai.village.poi.PoiRecord; import net.minecraft.world.entity.ai.village.poi.PoiManager.Occupancy; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.dimension.BuiltinDimensionTypes; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.saveddata.SavedData; import net.minecraft.world.level.saveddata.SavedDataType; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class Raids extends SavedData { private static final String RAID_FILE_ID = "raids"; public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( Raids.RaidWithId.CODEC .listOf() .optionalFieldOf("raids", List.of()) .forGetter(raids -> raids.raidMap.int2ObjectEntrySet().stream().map(Raids.RaidWithId::from).toList()), Codec.INT.fieldOf("next_id").forGetter(raids -> raids.nextId), Codec.INT.fieldOf("tick").forGetter(raids -> raids.tick) ) .apply(instance, Raids::new) ); public static final SavedDataType TYPE = new SavedDataType<>("raids", Raids::new, CODEC, DataFixTypes.SAVED_DATA_RAIDS); public static final SavedDataType TYPE_END = new SavedDataType<>("raids_end", Raids::new, CODEC, DataFixTypes.SAVED_DATA_RAIDS); private final Int2ObjectMap raidMap = new Int2ObjectOpenHashMap<>(); private int nextId = 1; private int tick; public static SavedDataType getType(Holder dimension) { return dimension.is(BuiltinDimensionTypes.END) ? TYPE_END : TYPE; } public Raids() { this.setDirty(); } private Raids(List raids, int nextId, int tick) { for (Raids.RaidWithId raidWithId : raids) { this.raidMap.put(raidWithId.id, raidWithId.raid); } this.nextId = nextId; this.tick = tick; } @Nullable public Raid get(int id) { return this.raidMap.get(id); } public OptionalInt getId(Raid raid) { for (Entry entry : this.raidMap.int2ObjectEntrySet()) { if (entry.getValue() == raid) { return OptionalInt.of(entry.getIntKey()); } } return OptionalInt.empty(); } public void tick(ServerLevel level) { this.tick++; Iterator iterator = this.raidMap.values().iterator(); while (iterator.hasNext()) { Raid raid = (Raid)iterator.next(); if (level.getGameRules().getBoolean(GameRules.RULE_DISABLE_RAIDS)) { raid.stop(); } if (raid.isStopped()) { iterator.remove(); this.setDirty(); } else { raid.tick(level); } } if (this.tick % 200 == 0) { this.setDirty(); } DebugPackets.sendRaids(level, this.raidMap.values()); } public static boolean canJoinRaid(Raider raider) { return raider.isAlive() && raider.canJoinRaid() && raider.getNoActionTime() <= 2400; } @Nullable public Raid createOrExtendRaid(ServerPlayer player, BlockPos pos) { if (player.isSpectator()) { return null; } else { ServerLevel serverLevel = player.serverLevel(); if (serverLevel.getGameRules().getBoolean(GameRules.RULE_DISABLE_RAIDS)) { return null; } else { DimensionType dimensionType = serverLevel.dimensionType(); if (!dimensionType.hasRaids()) { return null; } else { List list = serverLevel.getPoiManager().getInRange(holder -> holder.is(PoiTypeTags.VILLAGE), pos, 64, Occupancy.IS_OCCUPIED).toList(); int i = 0; Vec3 vec3 = Vec3.ZERO; for (PoiRecord poiRecord : list) { BlockPos blockPos = poiRecord.getPos(); vec3 = vec3.add(blockPos.getX(), blockPos.getY(), blockPos.getZ()); i++; } BlockPos blockPos2; if (i > 0) { vec3 = vec3.scale(1.0 / i); blockPos2 = BlockPos.containing(vec3); } else { blockPos2 = pos; } Raid raid = this.getOrCreateRaid(serverLevel, blockPos2); if (!raid.isStarted() && !this.raidMap.containsValue(raid)) { this.raidMap.put(this.getUniqueId(), raid); } if (!raid.isStarted() || raid.getRaidOmenLevel() < raid.getMaxRaidOmenLevel()) { raid.absorbRaidOmen(player); } this.setDirty(); return raid; } } } } private Raid getOrCreateRaid(ServerLevel serverLevel, BlockPos pos) { Raid raid = serverLevel.getRaidAt(pos); return raid != null ? raid : new Raid(pos, serverLevel.getDifficulty()); } public static Raids load(CompoundTag tag) { return (Raids)CODEC.parse(NbtOps.INSTANCE, tag).resultOrPartial().orElseGet(Raids::new); } private int getUniqueId() { return ++this.nextId; } @Nullable public Raid getNearbyRaid(BlockPos pos, int distance) { Raid raid = null; double d = distance; for (Raid raid2 : this.raidMap.values()) { double e = raid2.getCenter().distSqr(pos); if (raid2.isActive() && e < d) { raid = raid2; d = e; } } return raid; } record RaidWithId(int id, Raid raid) { public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group(Codec.INT.fieldOf("id").forGetter(Raids.RaidWithId::id), Raid.MAP_CODEC.forGetter(Raids.RaidWithId::raid)) .apply(instance, Raids.RaidWithId::new) ); public static Raids.RaidWithId from(Entry entry) { return new Raids.RaidWithId(entry.getIntKey(), (Raid)entry.getValue()); } } }