mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-02-01 03:26:44 +01:00
Fixed dynamic lighting entities
This commit is contained in:
parent
6c1c3e17d2
commit
fb742644af
5 changed files with 273 additions and 22 deletions
|
@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.entity;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.server.world.LightSources;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
|
@ -22,35 +24,35 @@ public interface DynamicLightSource {
|
|||
|
||||
@SuppressWarnings("deprecation")
|
||||
void tick() {
|
||||
if (entity.getWorld().isClient) {
|
||||
if (entity.isRemoved()) {
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
if (entity.isRemoved()) {
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
int light = entity.getLightLevel();
|
||||
int light = entity.getLightLevel();
|
||||
|
||||
if (light <= 0) {
|
||||
return;
|
||||
}
|
||||
if (light <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos currentPos = entity.getBlockPos();
|
||||
BlockPos currentPos = entity.getBlockPos();
|
||||
|
||||
if (!currentPos.equals(lastPos) && entity.getWorld().isChunkLoaded(currentPos)) {
|
||||
try {
|
||||
if (lastPos != null) {
|
||||
entity.getWorld().getLightingProvider().checkBlock(lastPos);
|
||||
}
|
||||
// TODO: store this in the ether and inject into Chunk#forEachLightSource
|
||||
//entity.getWorld().getLightingProvider().addLightSource(currentPos, light);
|
||||
lastPos = currentPos;
|
||||
} catch (Exception ignored) { }
|
||||
}
|
||||
if (!currentPos.equals(lastPos) && entity.getWorld().isChunkLoaded(currentPos)) {
|
||||
LightSources.get(entity.getWorld()).addLightSource(entity);
|
||||
|
||||
try {
|
||||
if (lastPos != null) {
|
||||
entity.getWorld().getLightingProvider().checkBlock(lastPos);
|
||||
entity.getWorld().getLightingProvider().checkBlock(currentPos);
|
||||
}
|
||||
lastPos = currentPos;
|
||||
} catch (Exception ignored) { }
|
||||
}
|
||||
}
|
||||
|
||||
void remove() {
|
||||
if (entity.getWorld().isClient && lastPos != null) {
|
||||
LightSources.get(entity.getWorld()).removeLightSource(entity);
|
||||
if (lastPos != null) {
|
||||
try {
|
||||
entity.getWorld().getLightingProvider().checkBlock(lastPos);
|
||||
} catch (Exception ignored) {}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package com.minelittlepony.unicopia.mixin;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.minelittlepony.unicopia.server.world.LightSources;
|
||||
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.ChunkSectionPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
import net.minecraft.world.chunk.light.ChunkBlockLightProvider;
|
||||
import net.minecraft.world.chunk.light.ChunkLightProvider;
|
||||
import net.minecraft.world.chunk.light.LightStorage;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Mixin(ChunkBlockLightProvider.class)
|
||||
abstract class MixinChunkBlockLightProvider extends ChunkLightProvider {
|
||||
@Shadow
|
||||
private @Final BlockPos.Mutable mutablePos;
|
||||
|
||||
MixinChunkBlockLightProvider() { super(null, null); }
|
||||
|
||||
@Inject(method = "propagateLight", at = @At("TAIL"))
|
||||
private void onPropagateLight(ChunkPos chunkPos, CallbackInfo info) {
|
||||
if (chunkProvider.getChunk(chunkPos.x, chunkPos.z) instanceof WorldChunk chunk && chunk.getWorld() instanceof ServerWorld world) {
|
||||
LightSources.get(world).forEachLightSource(chunkPos, (pos, level) -> {
|
||||
method_51566(pos.asLong(), ChunkLightProvider.class_8531.method_51573(level, false));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@Inject(method = "getLightSourceLuminance", at = @At("RETURN"), cancellable = true)
|
||||
private void onGetLightSourceLuminance(long blockPos, BlockState blockState, CallbackInfoReturnable<Integer> info) {
|
||||
int x = ChunkSectionPos.getSectionCoord(BlockPos.unpackLongX(blockPos));
|
||||
int z = ChunkSectionPos.getSectionCoord(BlockPos.unpackLongZ(blockPos));
|
||||
if (chunkProvider.getChunk(x, z) instanceof WorldChunk chunk) {
|
||||
info.setReturnValue(Math.max(info.getReturnValue(), LightSources.get(chunk.getWorld()).getLuminance(blockPos)));
|
||||
}
|
||||
}*/
|
||||
|
||||
@Inject(method = "method_51529", at = @At("TAIL"))
|
||||
private void onMethod_51529(long blockPos, CallbackInfo info) {
|
||||
long sectionPos = ChunkSectionPos.fromBlockPos(blockPos);
|
||||
if (!((MutableBlockLightStorage)lightStorage).invokeHasSection(sectionPos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = ChunkSectionPos.getSectionCoord(BlockPos.unpackLongX(blockPos));
|
||||
int z = ChunkSectionPos.getSectionCoord(BlockPos.unpackLongZ(blockPos));
|
||||
if (chunkProvider.getChunk(x, z) instanceof WorldChunk chunk) {
|
||||
int lightSourceLight = LightSources.get(chunk.getWorld()).getLuminance(blockPos);
|
||||
if (lightSourceLight > 0) {
|
||||
method_51566(blockPos, ChunkLightProvider.class_8531.method_51573(lightSourceLight, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "method_51530", at = @At("TAIL"))
|
||||
private void onMethod_51530(long blockPos, long flags, CallbackInfo info) {
|
||||
int x = ChunkSectionPos.getSectionCoord(BlockPos.unpackLongX(blockPos));
|
||||
int z = ChunkSectionPos.getSectionCoord(BlockPos.unpackLongZ(blockPos));
|
||||
if (chunkProvider.getChunk(x, z) instanceof WorldChunk chunk) {
|
||||
int lightLevel = ChunkLightProvider.class_8531.getLightLevel(flags);
|
||||
for (Direction direction : DIRECTIONS) {
|
||||
int j;
|
||||
long m;
|
||||
if (!ChunkLightProvider.class_8531.isDirectionBitSet(flags, direction)
|
||||
|| !((MutableBlockLightStorage)lightStorage).invokeHasSection(ChunkSectionPos.fromBlockPos(m = BlockPos.offset(blockPos, direction)))
|
||||
|| (j = ((MutableBlockLightStorage)lightStorage).invokeGet(m)) == 0) continue;
|
||||
if (j <= lightLevel - 1) {
|
||||
int lightSourceLight = LightSources.get(chunk.getWorld()).getLuminance(blockPos);
|
||||
if (lightSourceLight <= 0) continue;
|
||||
method_51566(m, ChunkLightProvider.class_8531.method_51573(lightSourceLight, false));
|
||||
continue;
|
||||
}
|
||||
method_51566(m, ChunkLightProvider.class_8531.method_51579(j, false, direction.getOpposite()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Mixin(LightStorage.class)
|
||||
interface MutableBlockLightStorage {
|
||||
@Invoker("hasSection")
|
||||
boolean invokeHasSection(long sectionPos);
|
||||
@Invoker("get")
|
||||
int invokeGet(long blockPos);
|
||||
@Invoker("set")
|
||||
void invokeSet(long blockPos, int light);
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
package com.minelittlepony.unicopia.server.world;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.entity.DynamicLightSource;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.AbstractObject2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.PersistentState;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class LightSources extends PersistentState {
|
||||
private static final Identifier ID = Unicopia.id("light_sources");
|
||||
|
||||
private final Set<UUID> lightSourceIds = new HashSet<>();
|
||||
private final AbstractObject2IntMap<UUID> lightSourceClientIds = new Object2IntOpenHashMap<>();
|
||||
private volatile boolean empty = true;
|
||||
|
||||
private final World world;
|
||||
|
||||
private final Function<UUID, Entity> entitySupplier;
|
||||
|
||||
public static LightSources get(World world) {
|
||||
return WorldOverlay.getPersistableStorage(world, ID, LightSources::new, LightSources::new);
|
||||
}
|
||||
|
||||
LightSources(World world, NbtCompound compound) {
|
||||
this(world);
|
||||
}
|
||||
|
||||
LightSources(World world) {
|
||||
this.world = world;
|
||||
entitySupplier = world instanceof ServerWorld s ? s::getEntity : id -> world.getEntityById(lightSourceClientIds.getInt(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NbtCompound writeNbt(NbtCompound compound) {
|
||||
return compound;
|
||||
}
|
||||
|
||||
public void addLightSource(Entity entity) {
|
||||
synchronized (lightSourceIds) {
|
||||
lightSourceIds.add(entity.getUuid());
|
||||
lightSourceClientIds.put(entity.getUuid(), entity.getId());
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeLightSource(Entity entity) {
|
||||
if (empty) {
|
||||
return;
|
||||
}
|
||||
synchronized (lightSourceIds) {
|
||||
lightSourceIds.remove(entity.getUuid());
|
||||
lightSourceClientIds.removeInt(entity.getUuid());
|
||||
empty = lightSourceIds.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public int getLuminance(long blockPos) {
|
||||
if (empty) {
|
||||
return 0;
|
||||
}
|
||||
final int[] result = {0};
|
||||
forEachLightSource((pos, level) -> {
|
||||
if (pos.asLong() == blockPos) {
|
||||
result[0] += level;
|
||||
}
|
||||
});
|
||||
return result[0];
|
||||
}
|
||||
|
||||
public void forEachLightSource(ChunkPos chunkPos, LightSourceConsumer consumer) {
|
||||
forEachLightSource((pos, level) -> {
|
||||
if (checkPos(chunkPos, pos)) {
|
||||
consumer.accept(pos, level);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void forEachLightSource(LightSourceConsumer consumer) {
|
||||
if (empty) {
|
||||
return;
|
||||
}
|
||||
synchronized (lightSourceIds) {
|
||||
lightSourceIds.removeIf(id -> {
|
||||
Entity entity = entitySupplier.apply(id);
|
||||
if (entity instanceof DynamicLightSource source) {
|
||||
consumer.accept(entity.getBlockPos(), source.getLightLevel());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
empty = lightSourceIds.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkPos(ChunkPos chunkPos, BlockPos pos) {
|
||||
return world.isInBuildLimit(pos)
|
||||
&& checkPos(pos.getX(), chunkPos.getStartX(), chunkPos.getEndX())
|
||||
&& checkPos(pos.getZ(), chunkPos.getStartZ(), chunkPos.getEndZ());
|
||||
}
|
||||
|
||||
private boolean checkPos(int p, int min, int max) {
|
||||
return p >= min && p <= max;
|
||||
}
|
||||
|
||||
public interface LightSourceConsumer {
|
||||
void accept(BlockPos pos, int light);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package com.minelittlepony.unicopia.server.world;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.*;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -21,6 +24,7 @@ import net.minecraft.world.PersistentState;
|
|||
import net.minecraft.world.World;
|
||||
|
||||
public class WorldOverlay<T extends WorldOverlay.State> extends PersistentState implements Tickable {
|
||||
|
||||
private final World world;
|
||||
|
||||
private final Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
|
||||
|
@ -39,7 +43,8 @@ public class WorldOverlay<T extends WorldOverlay.State> extends PersistentState
|
|||
id.toString()
|
||||
);
|
||||
}
|
||||
return factory.apply(world);
|
||||
|
||||
return ClientInstance.of(world, id, factory).instance();
|
||||
}
|
||||
|
||||
public static <T extends State> WorldOverlay<T> getOverlay(World world, Identifier id, Supplier<T> factory, @Nullable BiConsumer<Long2ObjectMap<T>, List<ServerPlayerEntity>> updateSender) {
|
||||
|
@ -181,4 +186,26 @@ public class WorldOverlay<T extends WorldOverlay.State> extends PersistentState
|
|||
public interface State extends NbtSerialisable {
|
||||
boolean tick();
|
||||
}
|
||||
|
||||
record ClientInstance<T extends PersistentState>(WeakReference<World> world, T instance) {
|
||||
private static final Map<Identifier, ClientInstance<?>> INSTANCES = new HashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends PersistentState> ClientInstance<T> of(World world, Identifier id, Function<World, T> factory) {
|
||||
return (ClientInstance<T>)INSTANCES.compute(id, (i, instance) -> {
|
||||
if (instance == null || !instance.matches(world)) {
|
||||
return new ClientInstance<>(world, factory);
|
||||
}
|
||||
return instance;
|
||||
});
|
||||
}
|
||||
|
||||
public ClientInstance(World world, Function<World, T> factory) {
|
||||
this(new WeakReference<>(world), factory.apply(world));
|
||||
}
|
||||
|
||||
public boolean matches(World world) {
|
||||
return this.world().get() == world;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
"MixinBlockItem",
|
||||
"MixinBoatEntity",
|
||||
"MixinBrain",
|
||||
"MixinChunkBlockLightProvider",
|
||||
"MutableBlockLightStorage",
|
||||
"MixinDamageSource",
|
||||
"MixinFallLocation",
|
||||
"MixinEntity",
|
||||
|
|
Loading…
Reference in a new issue