Skip to content

Commit

Permalink
Spawn provider system
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebemish committed Dec 23, 2023
1 parent 35ff32b commit cc7232f
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 9 deletions.
16 changes: 14 additions & 2 deletions common/src/main/java/dev/lukebemish/tempest/api/WeatherStatus.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package dev.lukebemish.tempest.api;

import com.mojang.serialization.Codec;
import dev.lukebemish.tempest.impl.Services;
import net.minecraft.core.BlockPos;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.Vec2;
import org.jetbrains.annotations.NotNull;

import java.util.Locale;

@SuppressWarnings("unused")
public final class WeatherStatus {
Expand Down Expand Up @@ -70,7 +75,7 @@ public static WeatherStatus atPosition(Level level, BlockPos position) {
return data.makeApiStatus(WeatherStatus::new, position);
}

public enum Kind {
public enum Kind implements StringRepresentable {
/**
* No precipitation.
*/
Expand All @@ -90,6 +95,13 @@ public enum Kind {
/**
* Freezing rain; produced at medium-cold temperatures. Coats the ground in ice and freezes up redstone components.
*/
SLEET
SLEET;

@Override
public @NotNull String getSerializedName() {
return name().toLowerCase(Locale.ROOT);
}

public static final Codec<Kind> CODEC = StringRepresentable.fromEnum(Kind::values);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -21,6 +22,8 @@ public final class Constants {
public static final TagKey<Block> FREEZES_UP = TagKey.create(Registries.BLOCK, id("freezes_up"));
public static final TagKey<Block> BREAKS_WITH_HAIL = TagKey.create(Registries.BLOCK, id("breaks_with_hail"));
public static final TagKey<Block> SAFE_WITH_HAIL = TagKey.create(Registries.BLOCK, id("safe_with_hail"));
public static final TagKey<EntityType<?>> DAMAGED_BY_HAIL = TagKey.create(Registries.ENTITY_TYPE, id("damaged_by_hail"));
public static final TagKey<EntityType<?>> IMMUNE_TO_HAIL = TagKey.create(Registries.ENTITY_TYPE, id("damaged_by_hail"));

public static final ResourceKey<DamageType> HAIL_DAMAGE_TYPE = ResourceKey.create(Registries.DAMAGE_TYPE, id("hail"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public void renderWeather(LightTexture lightTexture, float partialTick, double c
RenderSystem.enableDepthTest();

int layers = 10;
int belowAbove = 5;
int belowOffset = 5;

int rendering = -1;

Expand All @@ -142,8 +142,8 @@ public void renderWeather(LightTexture lightTexture, float partialTick, double c
if (status != null) {
float precipLevel = status.intensity;
int lowerY = level.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z);
int minY = Math.max(floorY - belowAbove, lowerY);
int maxY = Math.max(floorY + belowAbove, lowerY);
int minY = Math.max(floorY - belowOffset, lowerY);
int maxY = Math.max(floorY + layers, lowerY);

int upperY = Math.max(floorY, lowerY);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package dev.lukebemish.tempest.impl.data;

import com.google.gson.JsonElement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dev.lukebemish.tempest.api.WeatherStatus;
import dev.lukebemish.tempest.impl.Constants;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.biome.MobSpawnSettings;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public record WeatherSpawnProvider(WeatherStatus.Kind kind, List<MobSpawnSettings.SpawnerData> spawners) {
public static final Codec<WeatherSpawnProvider> CODEC = RecordCodecBuilder.create(i -> i.group(
WeatherStatus.Kind.CODEC.comapFlatMap(kind -> kind == WeatherStatus.Kind.CLEAR ? DataResult.error(() -> "Weather kind 'clear' is not valid here") : DataResult.success(kind), Function.identity()).fieldOf("kind").forGetter(WeatherSpawnProvider::kind),
MobSpawnSettings.SpawnerData.CODEC.listOf().fieldOf("spawners").forGetter(WeatherSpawnProvider::spawners)
).apply(i, WeatherSpawnProvider::new));

public static WeightedRandomList<MobSpawnSettings.SpawnerData> extendList(WeightedRandomList<MobSpawnSettings.SpawnerData> original, ServerLevel level, BlockPos pos, MobCategory mobCategory) {
if (level.canSeeSky(pos)) {
var kind = WeatherStatus.atPosition(level, pos).kind();
if (kind == WeatherStatus.Kind.CLEAR) {
return original;
}
var list = new ArrayList<>(original.unwrap());
list.addAll(ReloadListener.PROVIDERS.getOrDefault(kind, Map.of()).getOrDefault(mobCategory, List.of()));
return WeightedRandomList.create(list);
}
return original;
}

public static class ReloadListener extends SimpleJsonResourceReloadListener {
public static final String DIRECTORY = Constants.MOD_ID + "/spawn_providers";

public ReloadListener() {
super(Constants.GSON, DIRECTORY);
}

public static final Map<WeatherStatus.Kind, Map<MobCategory, List<MobSpawnSettings.SpawnerData>>> PROVIDERS = new EnumMap<>(WeatherStatus.Kind.class);

@Override
protected void apply(Map<ResourceLocation, JsonElement> object, ResourceManager resourceManager, ProfilerFiller profiler) {
PROVIDERS.clear();
object.forEach((id, element) -> {
var result = CODEC.parse(JsonOps.INSTANCE, element);
if (result.result().isEmpty()) {
Constants.LOGGER.error("Failed to decode spawn provider {}: {}", id, result.error().orElseThrow().message());
} else {
var provider = result.result().get();
var map = PROVIDERS.computeIfAbsent(provider.kind(), k -> new EnumMap<>(MobCategory.class));
provider.spawners().forEach(spawner -> {
var list = map.computeIfAbsent(spawner.type.getCategory(), k -> new ArrayList<>());
list.add(spawner);
});
}
});
Constants.LOGGER.info("Loaded {} weather spawn providers", object.size());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ public abstract class LivingEntityMixin extends Entity {
if (!this.level().isClientSide()) {
if ((this.tickCount & 8) == 0 && status != null && status.category == WeatherCategory.HAIL) {
var source = new DamageSource(this.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(Constants.HAIL_DAMAGE_TYPE));
//noinspection ConstantValue
if ((Object) this instanceof Player) {
if (this.getType().is(Constants.DAMAGED_BY_HAIL)) {
this.hurt(source, status.intensity / 3);
} else {
} else if (!this.getType().is(Constants.IMMUNE_TO_HAIL)) {
this.hurt(source, 0);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.lukebemish.tempest.impl.mixin;

import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import dev.lukebemish.tempest.impl.data.WeatherSpawnProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.NaturalSpawner;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.MobSpawnSettings;
import net.minecraft.world.level.chunk.ChunkGenerator;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(NaturalSpawner.class)
public class NaturalSpawnerMixin {
@ModifyExpressionValue(
method = "mobsAt(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/world/entity/MobCategory;Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Holder;)Lnet/minecraft/util/random/WeightedRandomList;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/level/chunk/ChunkGenerator;getMobsAt(Lnet/minecraft/core/Holder;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/entity/MobCategory;Lnet/minecraft/core/BlockPos;)Lnet/minecraft/util/random/WeightedRandomList;"
)
)
private static WeightedRandomList<MobSpawnSettings.SpawnerData> tempest$mobsAt(
WeightedRandomList<MobSpawnSettings.SpawnerData> original,
ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, BlockPos pos, @Nullable Holder<Biome> biome
) {
return WeatherSpawnProvider.extendList(original, level, pos, category);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"values": [
"minecraft:player"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"values": [
"minecraft:snow_golem",
"minecraft:polar_bear",
"minecraft:fox"
]
}
3 changes: 2 additions & 1 deletion common/src/main/resources/mixin.tempest.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"FoxSeekShelterMixin",
"LightningRodBlockMixin",
"PandaMixin",
"ThrownTridentMixin"
"ThrownTridentMixin",
"NaturalSpawnerMixin"
],
"client": [
"client.DispatchRenderChunkAccessor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

import dev.lukebemish.tempest.impl.Constants;
import dev.lukebemish.tempest.impl.data.AttachedWeatherMapReloadListener;
import dev.lukebemish.tempest.impl.data.WeatherSpawnProvider;
import dev.lukebemish.tempest.impl.fabriquilt.client.ClientEntrypoint;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;

public class ModEntrypoint implements ModInitializer {
@Override
Expand All @@ -19,8 +24,17 @@ public void onInitialize() {

ServerLifecycleEvents.SERVER_STARTED.register(AttachedWeatherMapReloadListener::applyToServer);

ResourceManagerHelper.get(PackType.SERVER_DATA).registerReloadListener(new IdentifiableWeatherSpawnProviderListener());

if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) {
ClientEntrypoint.init();
}
}

private static final class IdentifiableWeatherSpawnProviderListener extends WeatherSpawnProvider.ReloadListener implements IdentifiableResourceReloadListener {
@Override
public ResourceLocation getFabricId() {
return Constants.id("spawn_providers");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.lukebemish.tempest.impl.Constants;
import dev.lukebemish.tempest.impl.Services;
import dev.lukebemish.tempest.impl.data.AttachedWeatherMapReloadListener;
import dev.lukebemish.tempest.impl.data.WeatherSpawnProvider;
import dev.lukebemish.tempest.impl.forge.client.ClientEntrypoint;
import dev.lukebemish.tempest.impl.forge.compat.embeddium.EmbeddiumCompat;
import net.minecraft.world.level.chunk.LevelChunk;
Expand Down Expand Up @@ -58,6 +59,7 @@ private void onChunkSend(ChunkWatchEvent.Watch event) {

private void addReloadListener(AddReloadListenerEvent event) {
event.addListener(new AttachedWeatherMapReloadListener(event.getRegistryAccess()));
event.addListener(new WeatherSpawnProvider.ReloadListener());
}

private void onDatapackSync(OnDatapackSyncEvent event) {
Expand Down

0 comments on commit cc7232f

Please sign in to comment.