### 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
{ width=450 }
```
```xml
```
--------------------------------
### 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 extends ServerShip> 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 extends ServerShip> 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 extends Vector3dc, ? extends Vector3dc> 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.