Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added afterPlace event #7

Merged
merged 5 commits into from
Oct 21, 2023

Conversation

pietro-lopes
Copy link
Contributor

@pietro-lopes pietro-lopes commented Sep 27, 2023

Proposed Changes

  • added new structure event that triggers when afterPlace is called, useful when you need to tweak the world around the structure, not the structure itself (you still can, but the load event is more appropriated for that)

My use case was that I needed to place a feature for each chunk that a structure was generated.
Tested single player and multiplayer, it is working fine (didn't test fabric multiplayer tho)

Here are a few methods and usage:

MoreJSEvents.structureAfterPlace((event) => {
  /**
   * `event.id`: The id of the structure.
   * `event.type`: The type of the structure (jigsaw, mineshaft, stronghold...).
   * `event.genStep`: The generation step of the structure. 
   * `event.structure`: The structure being generated.
   * `event.structureManager`: The structure manager, required for some methods.
   * `event.structureBoundingBox`: The full structure bounding box.
   * `event.chunkBoundingBox`: The full chunk bounding box.
   * `event.chunkGenerator`: The chunk generator, required for some methods.
   * `event.chunkPos`: The coordinate of the current chunk.
   * `event.randomSource`: The random source, required for some methods.
   * `event.worldGenLevel`: The world gen level, required for some methods.
   * `event.piecesContainer`: The collection of structure pieces.
   * `event.intersectionBoxes`: The collection of EACH PIECE's bounding boxes that intersects current chunk.
   * `event.intersectionPieces`: The collection of structure pieces that intersects current chunk.
   * `event.intersectionMap`: The map of both above, useful when you need both values at same time.
   * `event.getPieceType(StructurePieceType)`: Returns the structure piece type.
   */
  
  if (event.id == "minecraft:mineshaft") return // checking ids
  if (event.type == "minecraft:jigsaw") return // checking types
  if (event.genStep == "underground_structures") return // checking generation step
  console.log(event.structure.terrainAdaptation().name()) // printing terrain adaptation of the structure
  let registryAccess = event.structureManager.registryAccess() // getting registry access from struc manager
  let structureCenter = event.structureBoundingBox.center // getting structure center
  let isCenterChunk = event.chunkBoundingBox.isInside(structureCenter) // check if we are at center chunk
  let seaLevel = event.chunkGenerator.seaLevel // getting sea level
  let newY = event.randomSource.nextIntBetweenInclusive(0,10) + seaLevel // using randomSource to give random number between 0 and 10
  let pieces = event.piecesContainer.pieces() // getting a list of structure pieces
  let chunkAccess = event.worldGenLevel.getChunk(event.chunkPos.x, event.chunkPos.z) // getting chunk access to do some stuff you can't with others
  if (event.intersectionBoxes.empty) return // checking if any bounding box of the pieces intersected current chunk
  event.intersectionPieces.forEach((sp) => console.log(event.getPieceType(sp.type))) // printing the type of the structure pieces

  // loops each bounding box of current chunk and replaces all blocks with glass
  event.intersectionBoxes.forEach((bb) => {
    BlockPos.betweenClosed(bb.minX(), bb.minY(), bb.minZ(), bb.maxX(), bb.maxY(), bb.maxZ()).forEach((pos) =>
      event.worldGenLevel.setBlock(pos, Blocks.GLASS.defaultBlockState(), 2)
    )
  })
})

You can directly reach me at KubeJS Discord, I'm very active there, Uncandango.

@pietro-lopes
Copy link
Contributor Author

pietro-lopes commented Sep 28, 2023

Proof of concept, adding a sign when it generates an underground structure
https://streamable.com/4rkbf8

const $Heightmap$Types = Java.loadClass("net.minecraft.world.level.levelgen.Heightmap$Types")

MoreJSEvents.structureAfterPlace((event) => {
  let currChunkBB = event.chunkBoundingBox
  let strucBB = event.structureBoundingBox
  if (!currChunkBB.isInside(strucBB.center)) return
  if (strucBB.maxY() > 64) return
  let strucTitle = Utils.snakeCaseToTitleCase(event.id.path)
  let groundY = event.worldGenLevel.getChunk(strucBB.center).getHeight($Heightmap$Types.WORLD_SURFACE_WG, strucBB.center.x, strucBB.center.z)
  let signPos = strucBB.center.mutable().setY(groundY + 1)
  let signBlockState = Blocks.OAK_SIGN.defaultBlockState()
  let canSurvive = signBlockState.canSurvive(event.worldGenLevel, signPos)
  if (!canSurvive) event.worldGenLevel.setBlock(signPos.below(), Blocks.SANDSTONE.defaultBlockState(), 2)
  event.worldGenLevel.setBlock(signPos, signBlockState, 2)
  /** @type {Internal.SignBlockEntity} */
  let sign = event.worldGenLevel.getBlockEntity(signPos)
  let nbt = sign.getUpdateTag()
  nbt["front_text"].messages[1] = Text.darkGray("Structure Name").toJson().toString()
  nbt["front_text"].messages[2] = Text.red(strucTitle).toJson().toString()
  nbt["front_text"]["has_glowing_text"] = NBT.b(1)
  sign.load(nbt)
  event.server.tell(Text.of("Generated underground structure: ").append(Text.green(strucTitle).clickRunCommand(`/tp @s ${sign.blockPos.x} ${sign.blockPos.y+2} ${sign.blockPos.z}`).hover(Text.of("Click to teleport"))))
})
Streamable
Watch "Proof of concept, MoreJS - Structures AfterPlace event" on Streamable.

@pietro-lopes
Copy link
Contributor Author

I can't add the beforePlace because I coudn't solve some issues like:
if I injected as soon as it validates the structure and run a script that is very aggressive on cancelling (like all strongholds) and if you try to /locate that you would end up with your server hanged (no error, just hangs trying to find structure forever)
If I injected too late, you will have ghost locations on /locate, that it says there is a structure there, but actually it doesnt.

Couldn't find a middle term so I gave up, already spent too much time on that and I'm having trouble trying to debug because it says source code doesn't match bytecode for most of it...

With that said, the afterPlace is working great.

@stale
Copy link

stale bot commented Oct 15, 2023

This submission has been automatically marked as abandoned because it has not had recent activity. It will be closed in 3 days. If you want to prevent that, leave a new comment.

@rlnt
Copy link
Member

rlnt commented Oct 15, 2023

This submission has been automatically marked as abandoned because it has not had recent activity. It will be closed in 3 days. If you want to prevent that, leave a new comment.

shoo

@LLytho
Copy link
Member

LLytho commented Oct 17, 2023

I can't add the beforePlace because I coudn't solve some issues like: if I injected as soon as it validates the structure and run a script that is very aggressive on cancelling (like all strongholds) and if you try to /locate that you would end up with your server hanged (no error, just hangs trying to find structure forever) If I injected too late, you will have ghost locations on /locate, that it says there is a structure there, but actually it doesnt.

Couldn't find a middle term so I gave up, already spent too much time on that and I'm having trouble trying to debug because it says source code doesn't match bytecode for most of it...

With that said, the afterPlace is working great.

Yeah we will just skip beforePlace then.

But I also see that there are potential cases where people can lock their world in after place too as the structure generation happens on different threads.

But other than this, lgtm

@LLytho LLytho merged commit d8fc623 into AlmostReliable:1.20.1 Oct 21, 2023
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants