added chunk_loaded event and fixed handling these events on 1.17

This commit is contained in:
gnembon committed 2021-06-26 23:20:13 -03:00
1 parent 7a3ad7f0b8
commit b1b7956ea9
5 files changed
+82 -20

No files matched your search

+10 -3
View File
@@ -70,10 +70,17 @@ Duplicate of `tick`, just automatically located in the nether. Use `__on_tick()
### `__on_tick_ender()` (Deprecated)
Duplicate of `tick`, just automatically located in the end. Use `__on_tick() -> in_dimension('end', ... ` instead.
### `__on_chunk_generated(x,z)`
### `__on_chunk_generated(x, z)`
Called right after a chunk at a given coordinate is full generated. `x` and `z` correspond
to the lowest x and z coords in the chunk. Event may (or may not) work with Optifine installed
at the same time.
to the lowest x and z coords in the chunk. Handling of this event is scheduled as an off-tick task happening after the
chunk is confirmed to be generated and loaded to the game, so
handling of this event is not technically guaranteed, if the game crashes while players are moving for example, and can
be missed on the next start, if the game successfully saves it and fails to execute all remaining tasks. In normal operation
this should not happen, but be warned.
### `__on_chunk_loaded(x, z)`
Called right after a chunk at a given coordinate is loaded. All newly generated chunks are loaded as well.
`x` and `z` correspond to the lowest x and z coordinates in the chunk.
### `__on_lightning(block, mode)`
Triggered right after a lightning strikes. Lightning entity as well as potential horseman trap would
@@ -1,6 +1,7 @@
__command() ->
__config() ->
__on_chunk_generated(x, z) ->
__on_chunk_loaded(x, z) ->
__on_close() ->
__on_player_attacks_entity(player, entity) ->
__on_player_breaks_block(player, block) ->
@@ -100,6 +100,7 @@
__command
__config
__on_chunk_generated
__on_chunk_loaded
__on_close
__on_player_attacks_entity
__on_player_breaks_block
@@ -12,6 +12,8 @@ import java.util.concurrent.Future;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTask;
import org.apache.commons.lang3.tuple.Pair;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@@ -50,6 +52,7 @@ import net.minecraft.world.chunk.WorldChunk;
import net.minecraft.world.storage.RegionFile;
import static carpet.script.CarpetEventServer.Event.CHUNK_GENERATED;
import static carpet.script.CarpetEventServer.Event.CHUNK_LOADED;
@Mixin(ThreadedAnvilChunkStorage.class)
public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implements ThreadedAnvilChunkStorageInterface
@@ -98,21 +101,61 @@ public abstract class ThreadedAnvilChunkStorage_scarpetChunkCreationMixin implem
@Shadow
protected abstract Iterable<ChunkHolder> entryIterator();
//in method_20617
//method_19534(Lnet/minecraft/server/world/ChunkHolder;Lnet/minecraft/world/chunk/Chunk;)Ljava/util/concurrent/CompletableFuture;
// incmopatibility with optifine makes this mixin fail.
// lambda for convertToFullChunk
ThreadLocal<Boolean> generated = ThreadLocal.withInitial(() -> null);
// in convertToFullChunk
// fancier version of the one below, ensuring that the event is triggered when the chunk is actually loaded.
@SuppressWarnings("UnresolvedMixinReference")
@Inject(method = "method_19534", require = 0, at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/world/ThreadedAnvilChunkStorage;convertToFullChunk(Lnet/minecraft/server/world/ChunkHolder;)Ljava/util/concurrent/CompletableFuture;",
shift = At.Shift.AFTER
))
private void onChunkGenerated(final ChunkHolder chunkHolder, final Chunk chunk, final CallbackInfoReturnable<CompletableFuture> cir)
@Inject(method = "method_20460", at = @At("HEAD"))
private void onChunkGeneratedStart(ChunkHolder chunkHolder, Either<Chunk, Unloaded> chunk, CallbackInfoReturnable<CompletableFuture<Either<Chunk, Unloaded>>> cir)
{
if (CHUNK_GENERATED.isNeeded())
this.world.getServer().execute(() -> CHUNK_GENERATED.onChunkGenerated(this.world, chunk));
if (CHUNK_GENERATED.isNeeded() || CHUNK_LOADED.isNeeded())
{
generated.set(chunkHolder.getCurrentChunk().getStatus() != ChunkStatus.FULL);
}
else
{
generated.set(null);
}
}
@SuppressWarnings("UnresolvedMixinReference")
@Inject(method = "method_20460", at = @At("RETURN"))
private void onChunkGeneratedEnd(ChunkHolder chunkHolder, Either<Chunk, Unloaded> chunk, CallbackInfoReturnable<CompletableFuture<Either<Chunk, Unloaded>>> cir)
{
Boolean localGenerated= generated.get();
if (localGenerated != null)
{
MinecraftServer server = this.world.getServer();
int ticks = server.getTicks();
ChunkPos chpos = chunkHolder.getPos();
// need to send these because if an app does something with that event, it may lock the thread
// so better be safe and schedule it for later, aSaP
if (CHUNK_GENERATED.isNeeded() && localGenerated)
server.send(new ServerTask(ticks, () -> CHUNK_GENERATED.onChunkEvent(this.world, chpos, true)));
if (CHUNK_LOADED.isNeeded())
server.send(new ServerTask(ticks, () -> CHUNK_LOADED.onChunkEvent(this.world, chpos, localGenerated)));
}
}
/* simple but a version that doesn't guarantee that the chunk is actually loaded
@Inject(method = "convertToFullChunk", at = @At("HEAD"))
private void onChunkGeneratedEnd(ChunkHolder chunkHolder, CallbackInfoReturnable<CompletableFuture<Either<Chunk, Unloaded>>> cir)
{
if (CHUNK_GENERATED.isNeeded() && chunkHolder.getCurrentChunk().getStatus() != ChunkStatus.FULL)
{
ChunkPos chpos = chunkHolder.getPos();
this.world.getServer().execute(() -> CHUNK_GENERATED.onChunkEvent(this.world, chpos, true));
}
if (CHUNK_LOADED.isNeeded())
{
boolean generated = chunkHolder.getCurrentChunk().getStatus() != ChunkStatus.FULL;
ChunkPos chpos = chunkHolder.getPos();
this.world.getServer().execute(() -> CHUNK_LOADED.onChunkEvent(this.world, chpos, generated));
}
}
*/
@Unique
private void addTicket(final ChunkPos pos, final ChunkStatus status)
@@ -45,7 +45,6 @@ import net.minecraft.util.registry.RegistryKey;
import net.minecraft.village.Merchant;
import net.minecraft.village.TradeOffer;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.explosion.Explosion;
import org.apache.commons.lang3.tuple.Pair;
@@ -417,12 +416,23 @@ public class CarpetEventServer
public static final Event CHUNK_GENERATED = new Event("chunk_generated", 2, true)
{
@Override
public void onChunkGenerated(ServerWorld world, Chunk chunk)
public void onChunkEvent(ServerWorld world, ChunkPos chPos, boolean generated)
{
handler.call( () ->
{
ChunkPos pos = chunk.getPos();
return Arrays.asList(new NumericValue(pos.x << 4), new NumericValue(pos.z << 4));
return Arrays.asList(new NumericValue(chPos.x << 4), new NumericValue(chPos.z << 4));
}, () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world)
);
}
};
public static final Event CHUNK_LOADED = new Event("chunk_loaded", 2, true)
{
@Override
public void onChunkEvent(ServerWorld world, ChunkPos chPos, boolean generated)
{
handler.call( () ->
{
return Arrays.asList(new NumericValue(chPos.x << 4), new NumericValue(chPos.z << 4));
}, () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world)
);
}
@@ -1050,7 +1060,7 @@ public class CarpetEventServer
public boolean deprecated() {return false;}
//stubs for calls just to ease calls in vanilla code so they don't need to deal with scarpet value types
public void onTick() { }
public void onChunkGenerated(ServerWorld world, Chunk chunk) { }
public void onChunkEvent(ServerWorld world, ChunkPos chPos, boolean generated) { }
public void onPlayerEvent(ServerPlayerEntity player) { }
public void onPlayerMessage(ServerPlayerEntity player, String message) { }
public void onPlayerStatistic(ServerPlayerEntity player, Stat<?> stat, int amount) { }