### Implement ICopyableBlock in MyBearingBlockEntity Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock This example demonstrates how to save and load block entity data, specifically handling connected ship positions to prevent precision issues during migration. ```java public class MyBearingBlockEntity extends BlockEntity { // Assume these are set from somewhere not shown public BlockPos connectedPosition = null; public long connectedShip = -1; public MyBearingBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { super(type, pos, blockState); } @Override protected void saveAdditional(CompoundTag tag) { tag.putLong("connectedShip", connectedShip); if (connectedPosition == null) return; // Using the center prevents precision issues // from when we migrate the position to a new ship tag.putDouble("x", connectedPosition.getCenter().x); tag.putDouble("y", connectedPosition.getCenter().y); tag.putDouble("z", connectedPosition.getCenter().z); } @Override public void load(CompoundTag tag) { connectedShip = tag.getLong("connectedShip"); if (!tag.contains("x")) return; double x = tag.getDouble("x"); double y = tag.getDouble("y"); double z = tag.getDouble("z"); connectedPosition = BlockPos.containing(x, y, z); } } ``` -------------------------------- ### Add Tabs with Markdown and Semantic Markup Source: https://docs.valkyrienskies.org/starter Examples of how to add switchable content using Markdown syntax versus semantic XML markup. ```markdown ![Alt Text](new_topic_options.png){ width=450 } ``` ```xml Alt text ``` -------------------------------- ### Get the attachment from a ship Source: https://docs.valkyrienskies.org/how-to-setup-an-attachment Retrieve the attachment from a LoadedServerShip instance. ```Kotlin ship.getAttachment() ``` ```Java ship.getAttachment(MyAttachment.class); ``` -------------------------------- ### Get the ship by ID Source: https://docs.valkyrienskies.org/how-to-get-a-ship Retrieves a specific ship instance using its unique ShipId. ```kotlin val level: Level = ... val id: ShipId = ... val ship: Ship? = level.getShipById(id) ``` ```java Level level = ... long id = ... @Nullable Ship ship = ValkyrienSkies.getShipById(level, id); ``` -------------------------------- ### Register attachments with useLegacySerializer Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Kotlin and Java examples for registering attachments with the legacy serializer. Use useLegacySerializer() for backwards compatibility with legacy attachments, or useTransientSerializer() if you previously used setAttachment without serialization. ```kotlin vsApi.registerAttachment(MyAttachment::class.java) { useLegacySerializer() } ``` ```java AttachmentRegistration registration = ValkyrienSkies.api() .newAttachmentRegistrationBuilder(MyAttachment.class) .useLegacySerializer() .build(); ValkyrienSkies.api().registerAttachment(registration); ``` -------------------------------- ### Get ships containing a position Source: https://docs.valkyrienskies.org/how-to-get-a-ship Returns an Iterable of all ships whose bounding boxes contain the given world position. ```kotlin val level: Level = ... val ships: Iterable = level.getShipsIntersecting(x, y, z) ``` ```java Level level = ... Iterable ships = ValkyrienSkies.getShipsIntersecting(level, x, y, z); ``` -------------------------------- ### Get the ships intersecting a bounding box Source: https://docs.valkyrienskies.org/how-to-get-a-ship Returns an Iterable of all ships whose bounding boxes intersect the provided AABB. ```kotlin val level: Level = ... val aabb: AABB = ... val ships: Iterable = level.getShipsIntersecting(aabb) ``` ```java Level level = ... AABB aabb = ... Iterable ships = ValkyrienSkies.getShipsIntersecting(level, aabb); ``` -------------------------------- ### Get the ship managing a chunk position Source: https://docs.valkyrienskies.org/how-to-get-a-ship Retrieves the ship whose unique chunk claim contains a specific chunk position. ```kotlin import org.valkyrienskies.mod.api.getShipManagingChunk val level: Level = ... val ship: Ship? = level.getShipManagingChunk(chunkX, chunkZ) ``` ```java import org.valkyrienskies.mod.api.ValkyrienSkies Level level = ... @Nullable Ship ship = ValkyrienSkies.getShipManagingChunk(level, chunkX, chunkZ); ``` -------------------------------- ### Get the ship managing a block position Source: https://docs.valkyrienskies.org/how-to-get-a-ship Retrieves the ship whose unique chunk claim contains a specific block position. ```kotlin import org.valkyrienskies.mod.api.getShipManagingBlock val level: Level = ... val ship: Ship? = level.getShipManagingBlock(x, y, z) ``` ```java import org.valkyrienskies.mod.api.ValkyrienSkies Level level = ... @Nullable Ship ship = ValkyrienSkies.getShipManagingBlock(level, x, y, z); ``` -------------------------------- ### Create fixed joint Source: https://docs.valkyrienskies.org/how-to-use-joints Example function to create a fixed joint between two ships at their center of mass using current rotation. Uses VSJointPose for each body and adds the joint via GameToPhysicsAdapter addJoint on the game tick. The joint subclass is VSFixedJoint. Two versions: Kotlin and Java. In Kotlin, the ship AABB is used; in Java, the ship's getShipAABB and getTransform methods are called. The joint is added with compliance VSJoint.DEFAULT_COMPLIANCE in Java. ```kotlin fun makeJoint(level: Level, ship0: Ship, ship1: Ship) { val pose0 = VSJointPose( ship0.shipAABB?.center(Vector3d()) ?: Vector3d(), ship0.transform.rotation ) val pose1 = VSJointPose( ship1.shipAABB?.center(Vector3d()) ?: Vector3d(), ship1.transform.rotation ) val gtpa = getOrCreateGTPA(level.dimensionId) val joint = VSFixedJoint(ship0.id, pose0, ship1.id, pose1) gtpa.addJoint(joint, 0) {} } ``` ```java public void makeJoint(Level level, Ship ship0, Ship ship1) { VSJointPose pose0 = new VSJointPose( ship0.getShipAABB().center(new Vector3d()), ship0.getTransform().getRotation() ); VSJointPose pose1 = new VSJointPose( ship1.getShipAABB().center(new Vector3d()), ship1.getTransform().getRotation() ); GameToPhysicsAdapter gtpa = ValkyrienSkiesMod.getOrCreateGTPA( VSGameUtilsKt.getDimensionId(level) ); VSFixedJoint joint = new VSFixedJoint(ship0.getId(), pose0, ship1.getId(), pose1, null, VSJoint.DEFAULT_COMPLIANCE); gtpa.addJoint(joint, 0, (id) -> {}); } ``` -------------------------------- ### Java: Replace ShipTransform.copy with toBuilder Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Use toBuilder and BodyTransform instead of ShipTransform.copy for better transform management. ```java ShipTransform transform = ... ShipTransform newTransform = transform.copy( new Vector3d(123), transform.getPositionInShip(), transform.getRotation(), transform.getScaling() ); ``` ```java ShipTransform transform = ... BodyTransform newTransform = transform.toBuilder() .position(new Vector3d(123)) .build(); ``` -------------------------------- ### Implement onCopy and onPaste methods Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock Define the onCopy and onPaste methods to handle data serialization and deserialization during block transitions. ```kotlin class MyCustomBlock(properties: Properties): Block(properties), ICopyableBlock { override fun onCopy( level: ServerLevel, pos: BlockPos, state: BlockState, be: BlockEntity?, shipsBeingCopied: List, centerPositions: Map ): CompoundTag? { return null } override fun onPaste( level: ServerLevel, pos: BlockPos, state: BlockState, oldShipIdToNewId: Map, centerPositions: Map>, tag: CompoundTag? ): CompoundTag? { return null } } ``` -------------------------------- ### Registering attachments with migration updater (Java) Source: https://docs.valkyrienskies.org/how-to-migrate-an-attachment Registering the legacy class with an updater to convert data to the new class, and registering the new class with a unique key in Java. ```java AttachmentRegistration r1 = ValkyrienSkies.api() .newAttachmentRegistrationBuilder(RocketShipAttachmentV1.class) // Explicitly specify the serialization key, // since we renamed the class .key("my.packagename.RocketShipAttachment") .useUpdater( RocketShipAttachment.class, // Convert the v1 attachment into the new version v1 -> new RocketShipAttachment(Double.parseDouble(v1.getFuel())) ) .build(); ValkyrienSkies.api().registerAttachment(r1); AttachmentRegistration r2 = ValkyrienSkies.api() .newAttachmentRegistrationBuilder(RocketShipAttachment.class) // We need a new, unique serialization key, // so we'll append :2 to the end of the original key .key("my.packagename.RocketShipAttachment:2") .build(); ValkyrienSkies.api().registerAttachment(r2); ``` -------------------------------- ### Implement onCopy and onPaste methods Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock Define the onCopy and onPaste methods to handle data serialization and deserialization during block transitions. ```java public class MyCustomBlock extends Block implements ICopyableBlock { public MyCustomBlock(Properties properties) { super(properties); } @Override public CompoundTag onCopy( ServerLevel serverLevel, BlockPos blockPos, BlockState blockState, BlockEntity blockEntity, List shipsBeingCopied, Map centerPositions ) { return null; } @Override public CompoundTag onPaste( ServerLevel serverLevel, BlockPos blockPos, BlockState blockState, Map oldShipIdToNewId, Map> centerPositions, CompoundTag tag ) { return null; } } ``` -------------------------------- ### Add Valkyrien Skies to repositories block Source: https://docs.valkyrienskies.org/how-to-add-valkyrien-skies-to-your-dev-environment Add the Valkyrien Skies maven repository to your build.gradle or build.gradle.kts file. ```groovy repositories { // ... your other repositories maven { url = 'https://maven.valkyrienskies.org' } } ``` ```kotlin repositories { // ... your other repositories maven { url = uri("https://maven.valkyrienskies.org") } } ``` -------------------------------- ### Kotlin: Replace ShipTransform.copy with rebuild Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Use rebuild and BodyTransform instead of ShipTransform.copy for better transform management. ```kotlin val transform: ShipTransform = ... val newTransform: ShipTransform = transform.copy( positionInWorld = Vector3d(123) ) ``` ```kotlin val transform: ShipTransform = ... val newTransform: BodyTransform = transform.rebuild { position.set(Vector3d(123)) } ``` -------------------------------- ### Registering attachments with migration updater Source: https://docs.valkyrienskies.org/how-to-migrate-an-attachment Registering the legacy class with an updater to convert data to the new class, and registering the new class with a unique key. ```kotlin vsApi.registerAttachment(RocketShipAttachmentV1::class.java) { // Explicitly specify the serialization key, // since we renamed the class key = "my.packagename.RocketShipAttachment" useUpdater(RocketShipAttachment::class.java) { v1 -> // Convert the v1 attachment into the new version RocketShipAttachment(v1.fuel.toDouble()) } } vsApi.registerAttachment(RocketShipAttachment::class.java) { // We need a new, unique serialization key, // so we'll append :2 to the end of the original key key = "my.packagename.RocketShipAttachment:2" } ``` -------------------------------- ### Create a primitive compound body with VsCoreApi Source: https://docs.valkyrienskies.org/how-to-create-a-custom-collision-body Builds a compound collision shape from a box and a rotated capsule, then creates a standalone VSBody with explicit inertia data. Requires Valkyrien Skies in the dev environment, a server-side ServerShipWorld, and known initial inertia data. Each child has its own body-local transform, so the capsule can be rotated independently of the box. ```kotlin import org.joml.Quaterniond import org.joml.Vector3d import org.valkyrienskies.core.api.bodies.VsBodyCreateData import org.valkyrienskies.core.api.bodies.properties.BodyInertia import org.valkyrienskies.core.api.world.ServerShipWorld import org.valkyrienskies.mod.api.ValkyrienSkies val vsCore = ValkyrienSkies.api val shipWorld: ServerShipWorld = ... val inertiaData: BodyInertia = ... val compoundShape = vsCore.newCompoundBodyShape( listOf( vsCore.newCompoundBodyShapeChild( shape = vsCore.newBoxBodyShape(Vector3d(1.0, 1.0, 1.0)), position = Vector3d(1.5, 0.0, 0.0), ), vsCore.newCompoundBodyShapeChild( shape = vsCore.newCapsuleBodyShape(radius = 0.25, halfLength = 1.0), position = Vector3d(-1.5, 0.0, 0.0), rotation = Quaterniond().rotateZ(Math.PI / 2.0), ), ) ) val body = shipWorld.createBody( VsBodyCreateData( dimensionId = "overworld", inertiaData = inertiaData, kinematics = vsCore.newBodyKinematics( velocity = Vector3d(), angularVelocity = Vector3d(), position = Vector3d(10.0, 64.0, 10.0), rotation = Quaterniond(), scaling = Vector3d(1.0), positionInModel = Vector3d(), ), collisionShape = compoundShape, isStatic = false, ) ) ``` -------------------------------- ### Implement ICopyableBlock in Java Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock A Java implementation of a bearing block that supports copying and pasting by migrating position data. ```java public class MyBearingBlock extends Block implements ICopyableBlock { public MyBearingBlock(Properties properties) { super(properties); } @Override public CompoundTag onCopy( ServerLevel serverLevel, BlockPos blockPos, BlockState blockState, BlockEntity blockEntity, List shipsBeingCopied, Map centerPositions ) { // Let the block entity save our data return null; } @Override public CompoundTag onPaste( ServerLevel serverLevel, BlockPos blockPos, BlockState blockState, Map oldShipIdToNewId, Map> centerPositions, CompoundTag tag ) { if (tag == null) return null; if (!tag.contains("x")) return null; double x = tag.getDouble("x"); double y = tag.getDouble("y"); double z = tag.getDouble("z"); Vector3dc oldPosition = new Vector3d(x, y, z); long oldShip = tag.getLong("connectedShip"); Pair centerMigrate = centerPositions.get(oldShip); // Our old ship wasn't copied if (centerMigrate == null) return null; // offset between old position to old center position Vector3dc offset = oldPosition.sub(centerMigrate.getFirst(), new Vector3d()); Vector3dc newPosition = centerMigrate.getSecond().add(offset, new Vector3d()); // Write our new position back into the tag tag.putDouble("x", newPosition.x()); tag.putDouble("y", newPosition.y()); tag.putDouble("z", newPosition.z()); return tag; } } ``` -------------------------------- ### Java: Replace VSEvents with ValkyrienSkies.api events Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Migrate from static VSEvents to the ValkyrienSkies.api event system. ```java VSEvents.ShipLoadEvent.Companion.on(ev -> {}); ``` ```java ValkyrienSkies.api().getShipLoadEvent().on(ev -> {}); ``` -------------------------------- ### Subscribe to shipLoadEvent with lambda (Kotlin) Source: https://docs.valkyrienskies.org/how-to-subscribe-to-vs-events Registers a lambda to the shipLoadEvent using the vsCore singleton. Commonly done in the mod constructor. Prints the ship ID when the event fires. ```Kotlin import org.valkyrienskies.mod.common.vsCore fun init() { vsCore.shipLoadEvent.on { event -> println("Ship ${event.ship.id} loaded!") } } ``` -------------------------------- ### Implement the physTick method in Kotlin and Java Source: https://docs.valkyrienskies.org/how-to-use-a-physics-tick-attachment Define the physTick method to execute logic during the physics tick, receiving PhysShip and PhysLevel instances. ```kotlin package my.packagename class MyAttachment(): ShipPhysicsListener { override fun physTick( physShip: PhysShip, physLevel: PhysLevel ) { // do stuff } } ``` ```java package my.packagename; public final class MyAttachment implements ShipPhysicsListener { public MyAttachment() {} @Override public void physTick(@NotNull PhysShip physShip, @NotNull PhysLevel physLevel) { // do stuff } } ``` -------------------------------- ### Build and apply a VoxelUpdate to a voxel segment Source: https://docs.valkyrienskies.org/how-to-create-a-custom-collision-body Creates a sparse voxel update builder, adds a block with explicit mass, builds the update, and applies it to the voxel segment ID. Use the explicit mass overload when the segment should contribute to the body's mass, center of mass, and inertia. Without mass, VS uses VoxelType.mass, which defaults to zero. ```kotlin import org.valkyrienskies.core.api.bodies.shape.VoxelType val blockType: VoxelType = ... val update = vsCore.newSparseVoxelUpdateBuilder(chunkX = 0, chunkY = 0, chunkZ = 0).apply { addBlock(x = 4, y = 5, z = 6, block = blockType, mass = 2.0) }.build() body.applyVoxelSegmentUpdate(voxelSegmentId, update) ``` -------------------------------- ### Register the attachment class Source: https://docs.valkyrienskies.org/how-to-setup-an-attachment Register your attachment in your mod's constructor to prevent crashes when adding it to a ship. ```Kotlin import org.valkyrienskies.mod.common.vsCore fun init() { vsCore.registerAttachment(MyAttachment::class.java) } ``` ```Java public MyMod() { ValkyrienSkiesMod.getApi().registerAttachment(MyAttachment.class); } ``` -------------------------------- ### Implement ShipPhysicsListener in Kotlin and Java Source: https://docs.valkyrienskies.org/how-to-use-a-physics-tick-attachment Modify your attachment class to implement the ShipPhysicsListener interface. ```kotlin package my.packagename class MyAttachment(): ShipPhysicsListener ``` ```java package my.packagename; public final class MyAttachment implements ShipPhysicsListener { public MyAttachment() {} } ``` -------------------------------- ### Add Valkyrien Skies to dependencies block Source: https://docs.valkyrienskies.org/how-to-add-valkyrien-skies-to-your-dev-environment Add Valkyrien Skies as a dependency in your build.gradle or build.gradle.kts file. ```groovy dependencies { // ... your other dependencies implementation("2.5.0-beta.3") { transitive = false } } ``` ```kotlin dependencies { // ... your other dependencies implementation("") { isTransitive = false } } ``` -------------------------------- ### Original RocketShipAttachment class (Java) Source: https://docs.valkyrienskies.org/how-to-migrate-an-attachment The initial implementation of the attachment storing fuel as a String in Java. ```java package my.packagename; public final class RocketShipAttachment { private String fuel; public RocketShipAttachment(String fuel) { this.fuel = fuel; } public void setFuel(String fuel) { this.fuel = fuel; } public String getFuel() { return fuel; } } ``` -------------------------------- ### Implement ICopyableBlock in Kotlin Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock A Kotlin implementation of a bearing block that supports copying and pasting by migrating position data. ```kotlin class MyBearingBlock(properties: Properties) : Block(properties), ICopyableBlock { override fun onCopy( level: ServerLevel, pos: BlockPos, state: BlockState, be: BlockEntity?, shipsBeingCopied: List, centerPositions: Map ): CompoundTag? { return null } override fun onPaste( level: ServerLevel, pos: BlockPos, state: BlockState, oldShipIdToNewId: Map, centerPositions: Map>, tag: CompoundTag? ): CompoundTag? { tag ?: return null if (!tag.contains("x")) return null val x = tag.getDouble("x") val y = tag.getDouble("y") val z = tag.getDouble("z") val oldPosition = Vector3d(x, y, z) val oldShip = tag.getLong("connectedShip") // null if our old ship wasn't copied val centerMigrate: Pair = centerPositions[oldShip] ?: return null // offset between old position to old center position val offset = oldPosition.sub(centerMigrate.first, Vector3d()) val newPosition = centerMigrate.second.add(offset, Vector3d()) // Write our new position back into the tag tag.putDouble("x", newPosition.x()) tag.putDouble("y", newPosition.y()) tag.putDouble("z", newPosition.z()) return tag } } ``` -------------------------------- ### Renamed legacy and new attachment classes Source: https://docs.valkyrienskies.org/how-to-migrate-an-attachment Renaming the original class to RocketShipAttachmentV1 and creating the new RocketShipAttachment class. ```kotlin package my.packagename class RocketShipAttachmentV1(var fuel: String) class RocketShipAttachment(var fuel: Double) ``` -------------------------------- ### Subscribe to shipLoadEvent with lambda (Java) Source: https://docs.valkyrienskies.org/how-to-subscribe-to-vs-events Registers a lambda to the shipLoadEvent using ValkyrienSkiesMod.getApi(). Commonly done in the mod constructor. Prints the ship ID when the event fires. ```Java public MyMod() { ValkyrienSkiesMod.getApi().getShipLoadEvent().on((event) -> { System.out.println( "Ship "+event.getShip().getId()+" loaded!" ); }); } ``` -------------------------------- ### Original RocketShipAttachment class Source: https://docs.valkyrienskies.org/how-to-migrate-an-attachment The initial implementation of the attachment storing fuel as a String. ```kotlin package my.packagename class RocketShipAttachment(var fuel: String) ``` -------------------------------- ### Kotlin: Replace VSEvents with vsApi events Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Migrate from static VSEvents to the vsApi event system. ```kotlin VSEvents.ShipLoadEvent.on { ev -> } ``` ```kotlin vsApi.shipLoadEvent.on { ev -> } ``` -------------------------------- ### Renamed legacy and new attachment classes (Java) Source: https://docs.valkyrienskies.org/how-to-migrate-an-attachment Renaming the original class to RocketShipAttachmentV1 and creating the new RocketShipAttachment class in Java. ```java package my.packagename; public final class RocketShipAttachmentV1 { private String fuel; public RocketShipAttachmentV1(String fuel) { this.fuel = fuel; } public void setFuel(String fuel) { this.fuel = fuel; } public String getFuel() { return fuel; } } public final class RocketShipAttachment { private double fuel; public RocketShipAttachment(double fuel) { this.fuel = fuel; } public void setFuel(double fuel) { this.fuel = fuel; } public double getFuel() { return fuel; } } ``` -------------------------------- ### Add data to the attachment class Source: https://docs.valkyrienskies.org/how-to-setup-an-attachment Store data by adding properties and constructor parameters. Note that values may be accessed on different threads, so consider using volatile or concurrent structures. ```Kotlin package my.packagename class MyAttachment(var turboMode: Boolean) ``` ```Java package my.packagename; public final class MyAttachment { public boolean turboMode; public MyAttachment(boolean turboMode) { this.turboMode = turboMode; } } ``` -------------------------------- ### Java: Migrate attachment calls to LoadedServerShip Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Before and after Java code for setting attachments on a loaded server ship. The legacy saveAttachment call is replaced with an instanceof check for LoadedServerShip and setAttachment. ```java ServerShip ship = ... ship.saveAttachment(new MyAttachment()) ``` ```java ServerShip ship = ... if (ship instanceof LoadedServerShip loadedShip) { loadedShip.setAttachment(new MyAttachment()); } ``` -------------------------------- ### Kotlin: Replace getShipManagingPos with getShipManagingBlock Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Update to the new API method for retrieving the ship managing a block position. ```kotlin import org.valkyrienskies.mod.common.getShipManagingPos val level: Level = ... val pos: BlockPos = ... val ship = level.getShipManagingPos(level, pos) ``` ```kotlin import org.valkyrienskies.mod.api.getShipManagingBlock val level: Level = ... val pos: BlockPos = ... val ship = level.getShipManagingBlock(level, pos) ``` -------------------------------- ### Java: Replace getShipManagingPos with getShipManagingBlock Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Update to the new API method for retrieving the ship managing a block position. ```java import org.valkyrienskies.mod.common.VSGameUtilsKt Level level = ... BlockPos pos = ... Ship ship = VSGameUtilsKt.getShipManagingPos(level, pos); ``` ```java import org.valkyrienskies.mod.api.ValkyrienSkies Level level = ... BlockPos pos = ... Ship ship = ValkyrienSkies.getShipManagingBlock(level, pos); ``` -------------------------------- ### Transform a Minecraft Vec3 position using Ship Source: https://docs.valkyrienskies.org/how-to-transform-coordinates-from-shipyard-to-world Use the Ship API to transform a Minecraft Vec3 position from shipyard to world coordinates. Requires obtaining the ship managing the position. ```kotlin val shipPos: Vec3 = ... val ship: Ship = ... val worldPos: Vector3d = ship.positionToWorld(shipPos) ``` ```java Vec3 shipPos = ... Ship ship = ... Vec3 worldPos = ValkyrienSkies.positionToWorld(ship, shipPos); ``` -------------------------------- ### Transform a JOML Vector3d position using Ship Source: https://docs.valkyrienskies.org/how-to-transform-coordinates-from-shipyard-to-world Use the Ship API to transform a JOML Vector3d position from shipyard to world coordinates. Requires obtaining the ship managing the position. ```kotlin val shipPos: Vector3dc = ... val ship: Ship = ... val worldPos: Vector3d = ship.positionToWorld(shipPos, Vector3d()) ``` ```java Vector3dc shipPos = ... Ship ship = ... Vector3d worldPos = ValkyrienSkies.positionToWorld(ship, shipPos, new Vector3d()); ``` -------------------------------- ### Abstract event handler to method (Java) Source: https://docs.valkyrienskies.org/how-to-subscribe-to-vs-events Passes a method reference to the physTickEvent instead of an anonymous lambda. The method prints a message when the event fires. ```Java public class MyMod { public MyMod() { ValkyrienSkiesMod.getApi().getPhysTickEvent() .on(this::physTick); } private void physTick(PhysTickEvent event) { System.out.println("Physics tick!"); } } ``` -------------------------------- ### Add the attachment to a ship Source: https://docs.valkyrienskies.org/how-to-setup-an-attachment Attachments can only be added to LoadedServerShip instances. ```Kotlin ship.setAttachment(MyAttachment(true)) // turboMode = true ``` ```Java ship.setAttachment(new MyAttachment(true)); // turboMode = true ``` -------------------------------- ### Abstract event handler to method (Kotlin) Source: https://docs.valkyrienskies.org/how-to-subscribe-to-vs-events Passes a method reference to the physTickEvent instead of an anonymous lambda. The method prints a message when the event fires. ```Kotlin import org.valkyrienskies.mod.common.vsCore class MyMod { fun init() { vsCore.physTickEvent.on(::physTick) } private fun physTick(event: PhysTickEvent) { println("Physics tick!") } } ``` -------------------------------- ### Update the attachment of a ship Source: https://docs.valkyrienskies.org/how-to-setup-an-attachment To update data, retrieve the attachment, modify the value, and set the attachment back to the ship. ```Kotlin val attachment = ship.getAttachment() // make a new attachment if the ship didn't have one before ?: MyAttachment(true) attachment.turboMode = !attachment.turboMode ship.setAttachment(attachment) ``` ```Java MyAttachment attachment = ship.getAttachment(MyAttachment.class); if (attachment == null) { // make a new attachment if the ship didn't have one before attachment = new MyAttachment(true); } attachment.turboMode = !attachment.turboMode; ship.setAttachment(attachment); ``` -------------------------------- ### Kotlin: Migrate attachment calls to LoadedServerShip Source: https://docs.valkyrienskies.org/how-to-migrate-to-vs-2-4 Before and after Kotlin code for setting attachments on a loaded server ship. The legacy saveAttachment call is replaced with a type check for LoadedServerShip and setAttachment. ```kotlin val ship: ServerShip = ... ship.saveAttachment(MyAttachment()) ``` ```kotlin val ship: ServerShip = ... if (ship is LoadedServerShip) { ship.setAttachment(MyAttachment()) } ``` -------------------------------- ### Transform a JOML Vector3d position using Level Source: https://docs.valkyrienskies.org/how-to-transform-coordinates-from-shipyard-to-world Use the Level API to transform a JOML Vector3d position from shipyard to world coordinates. ```kotlin val level: Level = ... val shipPos: Vector3dc = ... val worldPos = level.positionToWorld(shipPos, Vector3d()) ``` ```java Level level = ... Vector3dc shipPos = ... Vector3d worldPos = ValkyrienSkies.positionToWorld(level, shipPos, new Vector3d()); ``` -------------------------------- ### Transform a Minecraft Vec3 position using Level Source: https://docs.valkyrienskies.org/how-to-transform-coordinates-from-shipyard-to-world Use the Level API to transform a Minecraft Vec3 position from shipyard to world coordinates. ```kotlin val level: Level = ... val shipPos: Vec3 = ... val worldPos = level.positionToWorld(shipPos) ``` ```java Level level = ... Vec3 shipPos = ... Vec3 worldPos = ValkyrienSkies.positionToWorld(level, shipPos); ``` -------------------------------- ### Implement ICopyableBlock interface Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock Add the ICopyableBlock interface to your custom block class. Ensure it is implemented on the Block class, not the BlockEntity. ```kotlin class MyCustomBlock(properties: Properties): Block(properties), ICopyableBlock { // ... } ``` -------------------------------- ### Implement BlockEntity for ICopyableBlock in Kotlin Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock A Kotlin BlockEntity implementation that saves and loads position data used by the ICopyableBlock logic. ```kotlin class MyBearingBlockEntity(type: BlockEntityType<*>, pos: BlockPos, blockState: BlockState) : BlockEntity(type, pos, blockState) { // Assume these are set from somewhere not shown var connectedPosition: BlockPos? = null var connectedShip: Long = -1 override fun saveAdditional(tag: CompoundTag) { tag.putLong("connectedShip", connectedShip) connectedPosition?.let { pos -> // Using the center prevents precision issues // from when we migrate the position to a new ship tag.putDouble("x", pos.center.x) tag.putDouble("y", pos.center.y) tag.putDouble("z", pos.center.z) } } override fun load(tag: CompoundTag) { connectedShip = tag.getLong("connectedShip") if (!tag.contains("x")) return val x = tag.getDouble("x") val y = tag.getDouble("y") val z = tag.getDouble("z") connectedPosition = BlockPos.containing(x, y, z) } } ``` -------------------------------- ### Unsubscribe from event using RegisteredListener (Kotlin) Source: https://docs.valkyrienskies.org/how-to-subscribe-to-vs-events Subscribes to physTickEvent with a method that takes a RegisteredListener. The listener fires once, then unregisters itself and will not trigger again. ```Kotlin import org.valkyrienskies.mod.common.vsCore class MyMod { fun init() { vsCore.physTickEvent.on(::physTick) } private fun physTick(event: PhysTickEvent, handler: RegisteredListener) { println("Single physics tick") handler.unregister() } } ``` -------------------------------- ### Implement ICopyableBlock interface Source: https://docs.valkyrienskies.org/how-to-use-icopyableblock Add the ICopyableBlock interface to your custom block class. Ensure it is implemented on the Block class, not the BlockEntity. ```java public class MyCustomBlock extends Block implements ICopyableBlock { public MyCustomBlock(Properties properties) { super(properties); } // ... } ``` -------------------------------- ### Unsubscribe from event using RegisteredListener (Java) Source: https://docs.valkyrienskies.org/how-to-subscribe-to-vs-events Subscribes to physTickEvent with a method that takes a RegisteredListener. The listener fires once, then unregisters itself and will not trigger again. ```Java public class MyMod { public MyMod() { ValkyrienSkiesMod.getApi().getPhysTickEvent() .on(this::physTick); } private void physTick(PhysTickEvent event, RegisteredListener handler) { System.out.println("Single physics tick"); handler.unregister(); } } ``` -------------------------------- ### Update a Valkyrien Skies Joint Source: https://docs.valkyrienskies.org/how-to-use-joints Updates an existing joint by copying its properties into a new VSFixedJoint, optionally changing the first pose position, then wraps it in VSJointAndId for gtpa.updateJoint. ```kotlin fun updateJoint(level: Level, newPos0: Vector3d, jointId: VSJointId) { val gtpa = getOrCreateGTPA(level.dimensionId) val existingJoint = gtpa.getJointById(jointId) ?: return val newPose0 = VSJointPose( newPos0, existingJoint.pose0.rot ) val newJoint = VSFixedJoint( existingJoint.shipId0, newPose0, existingJoint.shipId1, existingJoint.pose1 ) val jointAndId = VSJointAndId(jointId, newJoint) gtpa.updateJoint(jointAndId) } ``` ```java public void updateJoint(Level level, Vector3d newPos0, int jointId) { GameToPhysicsAdapter gtpa = ValkyrieSkiesMod.getOrCreateGTPA( VSGameUtilsKt.getDimensionId(level) ); VSFixedJoint existingJoint = (VSFixedJoint) gtpa.getJointById(jointId); if (existingJoint == null) return; VSJointPose newPose0 = new VSJointPose( newPos0, existingJoint.getPose0().getRot() ); VSFixedJoint newJoint = new VSFixedJoint( existingJoint.getShipId0(), newPose0, existingJoint.getShipId1(), existingJoint.getPose1(), existingJoint.getMaxForceTorque(), existingJoint.getCompliance() ); VSJointAndId jointAndId = new VSJointAndId(jointId, newJoint); gtpa.updateJoint(jointAndId); } ``` -------------------------------- ### Add and remove segments: addCollisionSegment and removeCollisionSegment Source: https://docs.valkyrienskies.org/how-to-create-a-custom-collision-body Adds a sphere collision segment to an existing body and then removes it. The returned segment ID is the only ID to use for later updates to that segment. ```kotlin val sphereSegmentId = body.addCollisionSegment( shape = vsCore.newSphereBodyShape(radius = 0.75), position = Vector3d(0.0, 2.0, 0.0), rotation = Quaterniond(), ) body.removeCollisionSegment(sphereSegmentId) ``` -------------------------------- ### Define a public final attachment class Source: https://docs.valkyrienskies.org/how-to-setup-an-attachment The attachment class must be public and final. In Kotlin, classes are final by default; in Java, use the final keyword. ```Kotlin package my.packagename class MyAttachment() ``` ```Java package my.packagename; public final class MyAttachment { public MyAttachment() {} } ``` -------------------------------- ### Add a voxel segment: newVoxelBodyShape Source: https://docs.valkyrienskies.org/how-to-create-a-custom-collision-body Adds a voxel collision segment with a defined voxel region. `minDefined` and `maxDefined` specify the defined bounds in local voxel coordinates; the default shape uses one 16x16x16 chunk. ```kotlin import org.joml.Vector3i import org.joml.primitives.AABBi val voxelSegmentId = body.addCollisionSegment( shape = vsCore.newVoxelBodyShape( minDefined = Vector3i(0, 0, 0), maxDefined = Vector3i(15, 15, 15), totalVoxelRegion = AABBi(0, 0, 0, 15, 15, 15), voxelsFullyLoaded = true, ), position = Vector3d(3.0, 0.0, 0.0), rotation = Quaterniond(), ) ``` -------------------------------- ### Use segment IDs: setCollisionSegmentTransform Source: https://docs.valkyrienskies.org/how-to-create-a-custom-collision-body Retrieves a segment ID from a compound shape's children and applies a transform to that segment. Segment IDs are local to a body and are not reused after removal. ```kotlin val capsuleSegmentId = compoundShape.children[1].segmentId body.setCollisionSegmentTransform( segmentId = capsuleSegmentId, position = Vector3d(-2.0, 0.0, 0.0), rotation = Quaterniond().rotateX(0.5), ) ``` -------------------------------- ### Store joint id with overlap lambda Source: https://docs.valkyrienskies.org/how-to-use-joints Shows how to assign the JointId passed to the overlap to a variable for later updates or removal. Use this when you need to refer to the joint after physics tick. In Kotlin, the variable is assigned inside the lambda; in Java, the lambda param is an Integer and the assignment uses a setter. ```kotlin private var jointId: VSJointId? = null fun makeJoint(level: Level, ship0: Ship, ship1: Ship) { // ... gtpa.addJoint(joint, 0) { id -> jointId = id } } ``` ```java private @Nullable Integer jointId = null; public void makeJoint(Level level, Ship ship0, Ship ship1) { // ... gtpa.addJoint(joint, 0, (id) -> { jointId = id; }); } ``` -------------------------------- ### Remove a Joint from the GTPA Source: https://docs.valkyrienskies.org/how-to-use-joints Removes a joint by passing its VSJointId to the GameToPhysicsAdapter's removeJoint method. The Kotlin and Java versions both require a Level to obtain the dimension ID for the adapter. ```kotlin fun removeJoint(level: Level, jointId: VSJointId) { val gtpa = getOrCreateGTPA(level.dimensionId) gtpa.removeJoint(jointId) } ``` ```java public void removeJoint(Level level, int jointId) { GameToPhysicsAdapter gtpa = ValkyrieSkiesMod.getOrCreateGTPA( VSGameUtilsKt.getDimensionId(level) ); gtpa.removeJoint(jointId); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.