diff --git a/Writerside/topics/How-to-migrate-to-VS-2.5.md b/Writerside/topics/How-to-migrate-to-VS-2.5.md
index b1360a8..ed138ef 100644
--- a/Writerside/topics/How-to-migrate-to-VS-2.5.md
+++ b/Writerside/topics/How-to-migrate-to-VS-2.5.md
@@ -137,7 +137,7 @@ final class MyAttachment {
### Migrate from constraints to joints
-> Try to avoid using `apigame` - we don't make any [backwards compatibility](Compatibility.md#backwards-compatibility)
+> Try to avoid using `apigame` - we don't make any [backwards compatibility](todo/Compatibility.md#backwards-compatibility)
> guarantees for it.
>
{style="warning"}
diff --git a/Writerside/topics/Section-Starting-Page.topic b/Writerside/topics/Section-Starting-Page.topic
index 89e11c2..eefad36 100644
--- a/Writerside/topics/Section-Starting-Page.topic
+++ b/Writerside/topics/Section-Starting-Page.topic
@@ -23,8 +23,8 @@
- Custom card title
- Another custom title
+ Getting started
+ Migrating to VS 2.5
diff --git a/Writerside/topics/The-Attachment-System-Explained.md b/Writerside/topics/The-Attachment-System-Explained.md
deleted file mode 100644
index 28fe89d..0000000
--- a/Writerside/topics/The-Attachment-System-Explained.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# The Attachment System
-
-> Much of the attachment system is marked with `@VsBeta` and may change in the future.
->
-{style="note"}
-
-The **attachment system** is a part of vs-core which addons and third parties can use to attach data to ships and other
-objects managed by vs-core.
-
-## Use cases
-
-- You want to associate some data with a ship temporarily
-- You want to persist/serialize data with a ship
-- You want to apply forces to a ship
-
-## (Optional) Comparison of {(thing being compared)}
-
-{(Use this section to compare options or alternatives.)}
-
-Table: {(Table title which concisely explains the comparison.)}
-
-## (Optional) Related resources
-
-{(Use this section to provide links to documentation related to the concept that the user can read for more information.
-If you can name this section manually (it is not generated automatically or has a heading pre-agreed within a team),
-we recommend to use "Related concepts" or "Additional information" as more descriptive ones.)}
-
-If you would like to dive deeper or start implementing {(concept)},
-check out the following resources:
-
-How-to guides
-
-1. Item 1
-
-2. Item 2
-
-Linked concepts
-
-1. Concept 1
-
-2. Concept 2
-
-External resources
-
-1. Resource 1
-
-2. Resource 2
-
----
-
-> Explore other templates from [The Good Docs Project](https://thegooddocsproject.dev/). Use our [feedback form](https://thegooddocsproject.dev/feedback/?template=Concept%20template) to give feedback on this template.
diff --git a/Writerside/topics/Joints.md b/Writerside/topics/concepts/Joints.md
similarity index 100%
rename from Writerside/topics/Joints.md
rename to Writerside/topics/concepts/Joints.md
diff --git a/Writerside/topics/concepts/Ship-Chunk-Claims.md b/Writerside/topics/concepts/Ship-Chunk-Claims.md
new file mode 100644
index 0000000..b1b3069
--- /dev/null
+++ b/Writerside/topics/concepts/Ship-Chunk-Claims.md
@@ -0,0 +1,56 @@
+# Ship Chunk Claims
+Every ship has a chunk claim.
+This chunk claim specifies what section of the shipyard the ship "owns".
+Currently, chunk claims are hardcoded to 256 by 256 chunks, so ships can't be bigger than that.
+
+## `chunkClaim` field in `Ship`
+The function
+```kotlin
+fun getTotalVoxelRegion(yRange: LevelYRange, destination: AABBi = AABBi()): AABBi
+```
+in `ChunkClaim` can be used to get the AABB of the chunk claim (= Shipyard area)
+It takes an argument of the type `LevelYRange` which can be easily created from a level:
+
+
+
+
+```java
+new LevelYRange(level.getMinBuildHeight(), level.getMaxBuildHeight());
+```
+
+
+
+
+```kotlin
+LevelYRange(level.minBuildHeight, level.maxBuildHeight)
+```
+
+
+
+
+This creates a Y-Range using the build height of the level given.
+
+## `chunkClaimDimension` field in `Ship`
+
+
+
+
+```java
+String dim = ship.getChunkClaimDimension();
+```
+
+
+
+
+```kotlin
+var dim = ship.chunkClaimDimension
+```
+
+
+
+
+The dimension ID of the chunk claim.
+
+The ship always has to be in the same dimension as the chunk claim, so this is also the dimension ID of the ship.
+
+You can find more information about dimension IDs [here](Dimension-Ids.md)
\ No newline at end of file
diff --git a/Writerside/topics/The-Shipyard.md b/Writerside/topics/concepts/The-Shipyard.md
similarity index 94%
rename from Writerside/topics/The-Shipyard.md
rename to Writerside/topics/concepts/The-Shipyard.md
index 6e939d4..8db61c1 100644
--- a/Writerside/topics/The-Shipyard.md
+++ b/Writerside/topics/concepts/The-Shipyard.md
@@ -83,5 +83,8 @@ from existing performance mods like Lithium and Sodium.
## Chunk claims
-In the shipyard, each ship is assigned a **chunk claim** inside the shipyard. In VS2, each chunk claim is 256x256
-chunks. The chunks in the chunk claim are then "projected" into the world using the corresponding ship's transform.
\ No newline at end of file
+In the shipyard, each ship is assigned a **chunk claim**.
+The chunks in the chunk claim are then "projected" into the world using the ship's transform.
+> See [Ship Chunk Claims](Ship-Chunk-Claims.md) for more info on how chunk claims are used
+>
+{style="note"}
\ No newline at end of file
diff --git a/Writerside/topics/docs/Dimension-Ids.md b/Writerside/topics/docs/Dimension-Ids.md
new file mode 100644
index 0000000..2da9b7a
--- /dev/null
+++ b/Writerside/topics/docs/Dimension-Ids.md
@@ -0,0 +1,58 @@
+# Dimension IDs
+Dimension IDs are slightly different in VS2, so you need to keep that in mind when using vanilla dimension ids
+
+VS Dimension IDs are in the format `minecraft:dimension:[mod]:[dimension name]`.
+To convert this to a `ResourceKey` (which is how Minecraft stores level IDs), you can do this:
+
+
+
+
+```java
+public static ResourceKey VSToDimensionKey(String vsDimId) {
+ // Split 'minecraft:dimension:namespace:dimension_name' into
+ // [minecraft, dimension, namespace, dimension_name]
+ String split = vsDimId.split(":");
+ ResourceLocation re = ResourceLocation(split[split.size - 2], split[split.size - 1]);
+ return ResourceKey.create(Registry.DIMENSION_REGISTRY, rl);
+}
+```
+
+
+
+
+```kotlin
+/* DimensionId in VS2 is a type alias for String */
+fun DimensionId.toDimensionKey() {
+ // Split 'minecraft:dimension:namespace:dimension_name' into
+ // [minecraft, dimension, namespace, dimension_name]
+ val split = this.split(":")
+ val rl = ResourceLocation(split[split.size - 2], split[split.size - 1])
+ return ResourceKey.create(Registry.DIMENSION_REGISTRY, rl)
+}
+```
+
+
+
+
+This can now be used to, for example, get the `ServerLevel` of a ship when given a `MinecraftServer` object. Something like:
+
+
+
+
+```java
+public static ServerLevel fromLevelRL(ResourceKey rl) {
+ return server.getLevel(rl);
+}
+```
+
+
+
+
+```kotlin
+fun fromLevelRL(rl: ResourceKey?): ServerLevel {
+ return server.getLevel(rl)
+}
+```
+
+
+
\ No newline at end of file
diff --git a/Writerside/topics/docs/Events.md b/Writerside/topics/docs/Events.md
new file mode 100644
index 0000000..e660c79
--- /dev/null
+++ b/Writerside/topics/docs/Events.md
@@ -0,0 +1,77 @@
+
+# Events
+VS2 provides some useful events in `VSEvents` and `VSGameEvents`
+
+## All events
+
+`org.valkyrienskies.core.impl.hooks.VSEvents`:
+
+| Name | Description | Arguments |
+|-----------------------|--------------------------------------------------------------|--------------------------------|
+| `shipLoadEvent` | Will be called once a ship gets (chunk) loaded on the server | `ship: ShipObjectServer` |
+| `shipLoadEventClient` | Will be called once a ship gets (chunk) loaded on the client | `ship: ShipObjectClient` |
+| `tickEndEvent` | Will be called at the end of the server ticking a world | `world: ShipObjectServerWorld` |
+
+
+`org.valkyrienskies.mod.common.hooks.VSGameEvents`:
+
+| Name | Description | Arguments |
+|-----------------------|------------------------------------------------------------------------|--------------------------------------------|
+| `registriesCompleted` | Will be called once the mass & similar registries are filled with data | |
+| `tagsAreLoaded` | Will be called after Minecraft tags are loaded | |
+| `renderShip` | Will be called before a ship is rendered | `event: VSGameEvents.ShipRenderEvent` |
+| `postRenderShip` | Will be called after a ship is rendered | `event: VSGameEvents.ShipRenderEvent` |
+| `shipsStartRendering` | Will be called before all ships are rendered | `event: VSGameEvents.ShipStartRenderEvent` |
+
+## How to listen to events
+There is a `on` method in all of those events. Register event listeners to the event through this method.
+Its recommended to do this in your mods on-load (init) method. Use it like so:
+
+
+
+
+```java
+[DesiredEvent].Companion.on((event) -> {
+ // ...
+});
+```
+
+
+
+
+```kotlin
+[DesiredEvent].on {(event) -> {
+ // ...
+}}
+```
+
+
+
+
+## Example
+Print the slug (name) of every ship in a world every tick:
+
+
+
+
+```java
+VSEvents.TickEndEvent.Companion.on((e) -> {
+ e.getWorld().getAllShips().forEach((ship) -> {
+ System.out.println(ship.getSlug());
+ });
+});
+```
+
+
+
+
+```kotlin
+VSEvents.tickEndEvent.on { e ->
+ e.world.allShips.forEach { ship ->
+ println(ship.slug)
+ }
+}
+```
+
+
+
\ No newline at end of file
diff --git a/Writerside/topics/docs/Ship-Attachments-Pre-2.5.0.md b/Writerside/topics/docs/Ship-Attachments-Pre-2.5.0.md
new file mode 100644
index 0000000..bb6481a
--- /dev/null
+++ b/Writerside/topics/docs/Ship-Attachments-Pre-2.5.0.md
@@ -0,0 +1,275 @@
+# The Attachment System (Pre-VS 2.5.0)
+
+This page is just for information on attachments in VS 2.3.0,
+mostly for addon development while 2.5.0 is still in development.
+
+You can view more up-to-date information on the ship attachment system [here](Ship-Attachments.md).
+
+Also check out [Migrating to VS 2.5.0](How-to-migrate-to-VS-2.5.md) for how to update your 2.3
+attachments to VS 2.5
+
+## Saving, Loading and removing
+
+In the package `org.valkyrienskies.core.api.ships`
+there are two extension functions for saving and loading attachments.
+
+To save an attachment you can do:
+
+
+
+
+```java
+ serverShip.saveAttachment(MyAttachmentClass.class, new MyAttachmentClass());
+```
+
+
+
+
+```kotlin
+ serverShip.saveAttachment(MyAttachmentClass())
+```
+
+
+
+
+And to load the attachment:
+
+
+
+
+```java
+var attachment = serverShip.getAttachment(MyAttachmentClass.class);
+```
+
+
+
+
+```kotlin
+val attachment = serverShip.getAttachment()
+```
+
+
+
+
+> The attachment class needs to have a constructor without arguments.
+> You can use a constructor with arguments for `saveAttachment` if you wish,
+> but you still need one without arguments.
+>
+{style="note"}
+
+And finally, to remove the attachment:
+
+
+
+
+```java
+serverShip.saveAttachment(MyAttachmentClass.class, null);
+```
+
+
+
+
+```kotlin
+serverShip.saveAttachment(null)
+```
+
+
+
+
+## Attachment class fields
+Every class you use as an attachment needs to follow these rules:
+
+- All fields that you don't want to be saved in the attachment need to be marked
+ with `@com.fasterxml.jackson.annotation.JsonIgnore`. This includes ships because otherwise they would recursively save themselves.
+- All types of the fields that are not marked with
+ `@com.fasterxml.jackson.annotation.JsonIgnore` need to be classes with a default
+ constructor without arguments. This means that you can't use interfaces as types of these fields
+
+## World "corruption"
+If you change anything related to fields that get saved in a ship attachment class,
+you will get an error when loading ships with the old data. This is because the ship attachment
+class is not compatible with the old data anymore. To fix this, you should create a new ship attachment class
+and make the old one copy everything over to the new one, and then delete the old one from a ship.
+
+To migrate or remove old attachments,
+you can register an event listener to migrate ships as soon as they are loaded.
+Simply hook into the ShipLoadEvent in your mods on-load (init) method:
+
+> For ship force inducers, you can also use the `applyForces` method as your event instead.
+>
+{style="tip"}
+
+
+
+
+```java
+VSEvents.ShipLoadEvent.Companion.on((shipLoadEvent) -> {
+ ServerShip serverShip = shipLoadEvent.getShip();
+ // Update / remove your attachments here
+});
+```
+
+
+
+
+```kotlin
+VSEvents.shipLoadEvent.on {(serverShip) -> {
+ // Update / remove your attachments here
+}}
+```
+
+
+
+
+
+> For more information about events in VS2, go to [Events](Events.md)
+>
+{style="tip"}
+
+## Force inducers
+To apply forces to a ship, you need a force inducer class. This class will extend the `ShipForcesInducer` interface,
+and override the `applyForces(PhysShip)` method. Then, once you save this class as an attachment to a ship, the `applyForces`
+method will be called each physics tick. You can then use the PhysShip object in `applyForces` to apply forces to your ship.
+
+The `ShipForcesInducer` interface class:
+```kotlin
+package org.valkyrienskies.core.api.ships
+
+import org.valkyrienskies.core.api.ships.PhysShip
+import org.valkyrienskies.core.api.ships.properties.ShipId
+
+@Deprecated("sus")
+interface ShipForcesInducer {
+ fun applyForces(
+ physShip: PhysShip
+ )
+
+ fun applyForcesAndLookupPhysShips(
+ physShip: PhysShip,
+ lookupPhysShip: (ShipId) -> PhysShip?
+ ) {
+ // Default implementation to not break existing implementations
+ }
+}
+```
+{collapsible="true" collapsed-title="ShipForcesInducer.kt"}
+
+> VS2 physic ticks run in a different thread than the minecraft server thread.
+> This means that you need to be careful when accessing the same fields in applyForces and elsewhere
+>
+{style="warning" title="Thread Safety"}
+
+## Examples
+
+### Ship fuel tracking
+
+
+
+```java
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.valkyrienskies.core.api.ships.ServerShip;
+import org.valkyrienskies.core.impl.api.ServerShipUser;
+
+public class ShipFuelStorage {
+ @JsonIgnore
+ private ServerShip ship = null;
+ int fuel = 0;
+
+ public ShipFuelStorage() {}
+
+ public ShipFuelStorage(ServerShip ship) {
+ this.ship = ship;
+ }
+
+ public static ShipFuelStorage getOrCreate(ServerShip ship) {
+ ShipFuelStorage attachment = ship.getAttachment(ShipFuelStorage.class);
+ if (attachment == null) {
+ attachment = new ShipFuelStorage(ship);
+ ship.saveAttachment(ShipFuelStorage.class, attachment);
+ }
+ return attachment;
+ }
+}
+```
+
+
+
+
+```kotlin
+import com.fasterxml.jackson.annotation.JsonIgnore
+import org.valkyrienskies.core.api.ships.ServerShip
+import org.valkyrienskies.core.impl.api.ServerShipUser
+
+class ShipFuelStorage(
+ @JsonIgnore
+ var ship: ServerShip? = null
+) {
+ var fuel: Int = 0
+
+ companion object {
+ fun getOrCreate(ship: ServerShip): ShipFuelStorage =
+ ship.getAttachment()
+ ?: ShipFuelStorage(ship).also {
+ ship.saveAttachment(it)
+ }
+ }
+}
+```
+
+
+
+
+
+### Ship thruster force inducer
+
+
+
+
+```java
+import org.valkyrienskies.core.api.ships.PhysShip;
+import org.valkyrienskies.core.api.ships.ShipForcesInducer;
+
+public class ShipThrusterController implements ShipForcesInducer {
+
+ @Override
+ public void applyForces(@NotNull PhysShip physShip) {
+ // go through each thruster on ship and apply force
+ }
+
+ public static ShipThrusterController getOrCreate(ServerShip ship) {
+ ShipThrusterController attachment = ship.getAttachment(ShipThrusterController.class);
+ if (attachment == null) {
+ attachment = new ShipThrusterController(ship);
+ ship.saveAttachment(ShipThrusterController.class, attachment);
+ }
+ return attachment;
+ }
+}
+```
+
+
+
+
+```kotlin
+import org.valkyrienskies.core.api.ships.PhysShip
+import org.valkyrienskies.core.api.ships.ShipForcesInducer
+
+class ShipThrusterController: ShipForcesInducer {
+
+ override fun applyForces(physShip: PhysShip) {
+ // go through each thruster on ship and apply force
+ }
+
+ companion object {
+ fun getOrCreate(ship: ServerShip): ShipThrusterControler =
+ ship.getAttachment()
+ ?: ShipThrusterControler().also {
+ ship.saveAttachment(it)
+ }
+ }
+}
+```
+
+
+
+
diff --git a/Writerside/topics/docs/Ship-Attachments.md b/Writerside/topics/docs/Ship-Attachments.md
new file mode 100644
index 0000000..382c623
--- /dev/null
+++ b/Writerside/topics/docs/Ship-Attachments.md
@@ -0,0 +1,367 @@
+# The Attachment System
+
+> Much of the attachment system is marked with `@VsBeta` and may change in the future.
+>
+{style="note"}
+
+
+The **attachment system** is a part of vs-core which addons and third parties can use to attach data to ships and other
+objects managed by vs-core.
+
+## Use cases
+
+- You want to associate some data with a ship temporarily
+- You want to persist/serialize data with a ship
+- You want to apply forces to a ship
+
+> You can view the documentation for ship attachments pre-VS 2.5.0 [here](Ship-Attachments-Pre-2.5.0.md)
+>
+{style="tip"}
+
+## Registering your attachment
+
+
+
+Setup your attachment class. This must be a `final` class, but it doesn't need to extend/implement anything.
+(Unless your making a force inducer).
+
+
+ Make sure your class has a constructor with no arguments.
+
+ Example minimum class:
+
+
+
+ ```java
+ public final class MyAttachment {
+ public MyAttachment() {}
+ }
+ ```
+
+
+
+
+ ```kotlin
+ class MyAttachment() {
+
+ }
+ ```
+
+
+
+
+ Register your attachment (usually in your mod constructor)
+
+
+
+ ```java
+AttachmentRegistration registration = ValkyrienSkies.api()
+ .newAttachmentRegistrationBuilder(MyAttachment.class)
+ // Use if your migrating a pre-2.5 attachment
+ .useLegacySerializer()
+ .build();
+
+ValkyrienSkies.api().registerAttachment(registration);
+ ```
+
+
+
+
+ ```kotlin
+vsApi.registerAttachment(MyAttachment::class.java) {
+ // Use if your migrating a pre-2.5 attachment
+ useLegacySerializer()
+}
+ ```
+
+
+
+
+
+
+
+
+## Saving, Loading and removing
+
+In the package `org.valkyrienskies.core.api.attachments`
+there are two extension functions for saving and loading attachments.
+
+> You can only get and save attachments from a LoadedServerShip
+>
+{style="warning"}
+
+To save an attachment you can do:
+
+
+
+
+```java
+ ServerShip ship = ...
+ if (ship instanceof LoadedServerShip loadedShip) {
+ loadedShip.setAttachment(MyAttachment.class, new MyAttachment());
+ }
+```
+
+
+
+
+```kotlin
+val ship: ServerShip = ...
+if (ship is LoadedServerShip) {
+ ship.setAttachment(MyAttachment())
+}
+```
+
+
+
+
+And to load the attachment:
+
+
+
+
+```java
+ServerShip ship = ...
+if (ship instanceof LoadedServerShip loadedShip) {
+ var attachment = serverShip.getAttachment(MyAttachment.class);
+}
+```
+
+
+
+
+```kotlin
+val attachment = serverShip.getAttachment()
+```
+
+
+
+
+> The attachment class needs to have a constructor without arguments.
+> You can use a constructor with arguments for `setAttachment` if you wish,
+> but you still need one without arguments.
+>
+{style="note"}
+
+And finally, to remove the attachment:
+
+
+
+
+```java
+ServerShip ship = ...
+if (ship instanceof LoadedServerShip loadedShip) {
+ loadedShip.setAttachment(MyAttachment.class, null);
+}
+```
+
+
+
+
+```kotlin
+val ship: ServerShip = ...
+if (ship is LoadedServerShip) {
+ ship.setAttachment(null)
+}
+```
+
+
+
+
+## Attachment class fields
+Every class you use as an attachment needs to follow these rules:
+
+- All fields that you don't want to be saved in the attachment need to be marked
+ with `@com.fasterxml.jackson.annotation.JsonIgnore`. This includes ships because otherwise they would recursively save themselves.
+- All types of the fields that are not marked with
+ `@com.fasterxml.jackson.annotation.JsonIgnore` need to be classes with a default
+ constructor without arguments. This means that you can't use interfaces as types of these fields
+
+## World "corruption"
+If you change anything related to fields that get saved in a ship attachment class,
+you will get an error when loading ships with the old data. This is because the ship attachment
+class is not compatible with the old data anymore. To fix this, you should create a new ship attachment class
+and make the old one copy everything over to the new one, and then delete the old one from a ship.
+
+To migrate or remove old attachments,
+you can register an event listener to migrate ships as soon as they are loaded.
+Simply hook into the ShipLoadEvent in your mods on-load (init) method:
+
+> For ship force inducers, you can also use the `applyForces` method as your event instead.
+>
+{style="tip"}
+
+
+
+
+```java
+VSEvents.ShipLoadEvent.Companion.on((shipLoadEvent) -> {
+ ServerShip serverShip = shipLoadEvent.getShip();
+ // Update / remove your attachments here
+});
+```
+
+
+
+
+```kotlin
+VSEvents.shipLoadEvent.on {(serverShip) -> {
+ // Update / remove your attachments here
+}}
+```
+
+
+
+
+
+> For more information about events in VS2, go to [Events](Events.md)
+>
+{style="tip"}
+
+## Force inducers
+To apply forces to a ship, you need a force inducer class. This class will extend the `ShipForcesInducer` interface,
+and override the `applyForces(PhysShip)` method. Then, once you save this class as an attachment to a ship, the `applyForces`
+method will be called each physics tick. You can then use the PhysShip object in `applyForces` to apply forces to your ship.
+
+The `ShipForcesInducer` interface class:
+```kotlin
+package org.valkyrienskies.core.api.ships
+
+import org.valkyrienskies.core.api.ships.PhysShip
+import org.valkyrienskies.core.api.ships.properties.ShipId
+
+@Deprecated("sus")
+interface ShipForcesInducer {
+ fun applyForces(
+ physShip: PhysShip
+ )
+
+ fun applyForcesAndLookupPhysShips(
+ physShip: PhysShip,
+ lookupPhysShip: (ShipId) -> PhysShip?
+ ) {
+ // Default implementation to not break existing implementations
+ }
+}
+```
+{collapsible="true" collapsed-title="ShipForcesInducer.kt"}
+
+> VS2 physic ticks run in a different thread than the minecraft server thread.
+> This means that you need to be careful when accessing the same fields in applyForces and elsewhere
+>
+{style="warning" title="Thread Safety"}
+
+## Examples
+
+### Ship fuel tracking
+
+
+
+```java
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.valkyrienskies.core.api.ships.ServerShip;
+import org.valkyrienskies.core.impl.api.ServerShipUser;
+
+public final class ShipFuelStorage {
+ @JsonIgnore
+ private LoadedServerShip ship = null;
+ int fuel = 0;
+
+ public ShipFuelStorage() {}
+
+ public ShipFuelStorage(LoadedServerShip ship) {
+ this.ship = ship;
+ }
+
+ public static ShipFuelStorage getOrCreate(LoadedServerShip ship) {
+ ShipFuelStorage attachment = ship.getAttachment(ShipFuelStorage.class);
+ if (attachment == null) {
+ attachment = new ShipFuelStorage(ship);
+ ship.setAttachment(ShipFuelStorage.class, attachment);
+ }
+ return attachment;
+ }
+}
+```
+
+
+
+
+```kotlin
+import com.fasterxml.jackson.annotation.JsonIgnore
+import org.valkyrienskies.core.api.ships.ServerShip
+import org.valkyrienskies.core.impl.api.ServerShipUser
+
+class ShipFuelStorage(
+ @JsonIgnore
+ var ship: LoadedServerShip? = null
+) {
+ var fuel: Int = 0
+
+ companion object {
+ fun getOrCreate(ship: LoadedServerShip): ShipFuelStorage =
+ ship.getAttachment()
+ ?: ShipFuelStorage(ship).also {
+ ship.setAttachment(it)
+ }
+ }
+}
+```
+
+
+
+
+
+### Ship thruster force inducer
+
+
+
+
+```java
+import org.valkyrienskies.core.api.ships.PhysShip;
+import org.valkyrienskies.core.api.ships.ShipForcesInducer;
+
+public class ShipThrusterController implements ShipForcesInducer {
+
+ @Override
+ public void applyForces(@NotNull PhysShip physShip) {
+ // go through each thruster on ship and apply force
+ }
+
+ public static ShipThrusterController getOrCreate(ServerShip ship) {
+ ShipThrusterController attachment = ship.getAttachment(ShipThrusterController.class);
+ if (attachment == null) {
+ attachment = new ShipThrusterController(ship);
+ ship.saveAttachment(ShipThrusterController.class, attachment);
+ }
+ return attachment;
+ }
+}
+```
+
+
+
+
+```kotlin
+import org.valkyrienskies.core.api.ships.PhysShip
+import org.valkyrienskies.core.api.ships.ShipForcesInducer
+
+class ShipThrusterController: ShipForcesInducer {
+
+ override fun applyForces(physShip: PhysShip) {
+ // go through each thruster on ship and apply force
+ }
+
+ companion object {
+ fun getOrCreate(ship: ServerShip): ShipThrusterControler =
+ ship.getAttachment()
+ ?: ShipThrusterControler().also {
+ ship.saveAttachment(it)
+ }
+ }
+}
+```
+
+
+
+
diff --git a/Writerside/topics/How-to-get-the-ship-managing-a-shipyard-position.md b/Writerside/topics/guides/How-to-get-a-ship-from-a-block.md
similarity index 60%
rename from Writerside/topics/How-to-get-the-ship-managing-a-shipyard-position.md
rename to Writerside/topics/guides/How-to-get-a-ship-from-a-block.md
index fbb5a0a..d0076fe 100644
--- a/Writerside/topics/How-to-get-the-ship-managing-a-shipyard-position.md
+++ b/Writerside/topics/guides/How-to-get-a-ship-from-a-block.md
@@ -1,6 +1,8 @@
-# How to get the ship managing a shipyard position
+# How to get a ship from a block
-Every ship has a chunk claim in the [shipyard](The-Shipyard.md) which contains the ship's chunks.
+Every ship has a [chunk claim](Ship-Chunk-Claims.md) in the [shipyard](The-Shipyard.md) which contains the ship's blocks.
+If you are given one of these shipyard blocks, for example if a `level.clip()` hits a ship, you may need to get the Ship
+object so that you can transform that shipyard position back to a world position.
> **Highlight important information**
>
diff --git a/Writerside/topics/How-to-transform-coordinates-from-shipyard-to-world.md b/Writerside/topics/guides/How-to-transform-coordinates-from-shipyard-to-world.md
similarity index 100%
rename from Writerside/topics/How-to-transform-coordinates-from-shipyard-to-world.md
rename to Writerside/topics/guides/How-to-transform-coordinates-from-shipyard-to-world.md
diff --git a/Writerside/topics/Compatibility.md b/Writerside/topics/todo/Compatibility.md
similarity index 100%
rename from Writerside/topics/Compatibility.md
rename to Writerside/topics/todo/Compatibility.md
diff --git a/Writerside/topics/Dependency-Architecture.md b/Writerside/topics/todo/Dependency-Architecture.md
similarity index 100%
rename from Writerside/topics/Dependency-Architecture.md
rename to Writerside/topics/todo/Dependency-Architecture.md
diff --git a/Writerside/topics/How-Valkyrien-Skies-Works-From-a-Birds-Eye.md b/Writerside/topics/todo/How-Valkyrien-Skies-Works-From-a-Birds-Eye.md
similarity index 100%
rename from Writerside/topics/How-Valkyrien-Skies-Works-From-a-Birds-Eye.md
rename to Writerside/topics/todo/How-Valkyrien-Skies-Works-From-a-Birds-Eye.md
diff --git a/Writerside/topics/starter.md b/Writerside/topics/todo/starter.md
similarity index 99%
rename from Writerside/topics/starter.md
rename to Writerside/topics/todo/starter.md
index b24eda4..9b8daf4 100644
--- a/Writerside/topics/starter.md
+++ b/Writerside/topics/todo/starter.md
@@ -31,7 +31,7 @@ For example, this is how you inject a procedure:
### Tabs
To add switchable content, you can make use of tabs (inject them by starting to type `tab` on a new line):
-
+
{ width=450 }
diff --git a/Writerside/vs.tree b/Writerside/vs.tree
index 377b49f..15ba4cb 100644
--- a/Writerside/vs.tree
+++ b/Writerside/vs.tree
@@ -8,7 +8,7 @@
-
+
@@ -19,7 +19,12 @@
-
+
+
+
+
+
+
@@ -27,4 +32,5 @@
+
\ No newline at end of file