api/Ships 6 R2


Ships 6 uses a compatibility layer to suit the need for both Sponge and Bukkit users alike.

Compatibility layer:

As this is on the Bukkit site, this will be focusing on the BukkitToCore compatibility layer and displaying how to convert from Bukkit to ShipsCore. With that in mind, SpongeToCore is not that different. 


In ShipsCore there is not a Location class, instead there is a Position class which is implemented by BlockPosition and ExactPosition.

BlockPosition is the position of a Block while an Exact Position is more exact on its location within the world. Both BlockPosition and ExactPosition can be converted to one and another by simply using the to() function, however both position types share the same functions so you will find that many functions that require a position will use the Position class. You can convert a Bukkit Block into a BlockPosition with the following code

org.bukkit.block.Block block;
BlockPosition position = new BBlockPosition(block);

Note that the implementation of BlockPosition has a B infront of it, this is to stand for Bukkit as appose to the Sponge variant in SpongeToCore which starts with S. You will find this a common trend within the implementation of the interfaces for Core. When it comes to ExactPosition, you require a Bukkit Location, it works in the same way as BlockPosition.

org.bukkit.Location location;
ExactPosition position = new BExactPosition(location);

 Sometimes you may want to write as much as you can just using ShipsCore to make your code work on any implementation of ShipsCore, to get a Position without Bukkit you require a Extent such as ExtentWorld which is ShipsCore's version of a World. When you have the world you need the X, Y, Z position that you want.

int x;
int y;
int z;
ExtentWorld world;
BlockPosition position = world.getPosition(x, y, z);

 Note that it returns a BlockPosition with int X, Y, Z. If you were to have double X, Y, Z then it would return a ExactPosition.


Block Details:

BlockDetails are ShipsCores version of BlockData which was added in 1.13. They hold all information about the block including TileEntity data in ShipsCore. 

Blocks can hold a lot of different information such as facing direction, if they are on fire, etc. All of this can be accessed by using the KeyedData class. KeyedData holds a single object that can only be accessed if the BlockType supports it, such as WaterLoggedKeyedData can only be accessed if the BlockType supports being WaterLogged.


You can get and set the KeyedData by the following

BlockDetails details;
boolean waterLogged = details.get(KeyedData.WATER_LOGGED).get(); 
details.set(KeyedData.WATER_LOGGED, !waterLogged);

 Note that .get() is on the end of get. This is because get returns a optional value in cases where the BlockType does not support the key. 


Block snapshot

A block snapshot is a BlockDetail with a position attached to it. As it extends the BlockDetails class it can act in every way like one. 

In the Bukkit edition of Core there are two Block Snapshot implementations, there is the ExtendedBlockDetails that - as you may have guessed - extends the implementation of BlockDetails and sticks a position onto it. The other implementation of a block snapshot is BlockStateSnapshot that takes Bukkits BlockState and uses it as a block snapshot, this is used primarily in events to change the outcome, however requesting GetBlockDetails() from a position will also return a BlockStateSnapshot. Changes that occur to a BlockStateSnapshot will affect the BlockState however due to the fact the BlockState function update() is never called, it will not affect the real world.

Tile Entity

A TileEntity is data of a block that is not normally supported by BlockDetails, in Bukkit this data is typically stored in BlockState. With ShipsCore a TileEntity comes in two forms, a LiveTileEntity and a TileEntitySnapshot. The LiveTileEntity is for if the TileEntity data has come directly from a block, this means any modifications you do to it will results in changes to the world. A TileEntitySnapshot is a snapshot, meaning that any changes that are done to it will not take effect on the world. If you wish to only get data from a TileEntity and do not care if the TileEntity is Live or a snapshot then you can as both TileEntitySnapshot and LiveTileEntity both extend TileEntity which is used as the base for each TileEntity for the block, after that then that interface is split into the TileEntitySnapshot and LiveTileEntity meaning that every TileEntity has a general interface, live interface and snapshot interface. For example.


BlockPosition position;
LiveTileEntity tileEntity = position.getTileEntity().get();
FurnaceTileEntity furnace = (FurnaceTileEntity)tileEntity; //this has all functions of a Live and Snapshot FurnaceTileEntity
LiveFurnaceTileEntity liveFurnace = (LiveFurnaceTileEntity)furnace;

One thing that is unique to ShipsCore is that you can set TileEntity data from a BlockDetails, so if you set a block with that same BlockDetails, the TileEntity contained in the BlockDetails to be transferred with it. This also allows the ease of creating a block snapshot like interface that contains every bit of data that can be applied to a block. 

For example, lets assume there is a furnace on the BlockPosition we will be using in the code below, this furnace has a item in the fuel slot, we want to get a BlockDetail of that furnace and read the item in the BlockDetail

BlockPosition position;
TiledBlockDetails tiledDetails = (TiledBlockDetails)position.getBlockDetails();
TileEntitySnapshot tileEntity = tiledDetails.getTileEntity();
FurnaceInventorySnapshot fis = (FurnaceInventorySnapshot)tileEntity.getInventory();
ItemStack item = fis.getFuelSlot().getItem().get();

 Note that we can get the LiveTileEntity of the BlockPosition which will be more accurate on the current data of the position, however it was an example. 



ItemStack, like everything else is split into Live and Snapshot. To create a ItemStack, first select the ItemType, then create the default ItemStack, after that modify that snapshot and then build the live item.


ItemType type = ItemType.HEAD;
ItemStackSnapshot snapshot = type.getDefaultItemStack();
LiveItemStack stack = snapshot.build();




Worlds within ShipsCore are called ExtentWorld. To convert a Bukkit World to a ExtentWorld is similar to everything else.

org.bukkit.World bWorld;
ExtentWorld world = new BExtentWorld(bWorld);

 However if you are working within ShipsCore only, you can get the world by its name or UUID. Note that you can get the World by what the platform prefers to use to identify the world, such as Bukkit prefers the world name and Sponge prefers the world UUID.

String worldName; 
CorePlugin.getServer().getWorld(worldName, true);


Much like how TileEntity is split into LiveTileEntity and EntitySnapshot, the same goes with Entities with some functions only being able to be used on snapshots, some only being able to be used on Live and most being able to be used on both. 

Live Entity

As it stands the only unique function given to a LiveEntity is the remove() function that removes it from the world and makes the LiveEntity object useless. 

Entity Snapshot

An entity Snapshot is a entity that has not been spawned yet. There are two ways that you can get a entity . The simplest way is by using a existing Entity object (either live or snapshot) and then create a snapshot of that entity, this will copy all data of the original entity and you will be able to receive the LiveEntity it is based on if it exists. 


The other way is to create a base entity snapshot, this will hold the default values of the entity you are attempting to create, from here you can change this values to what suits you and then spawn the entity. 


ExactPosition position;
ClassicZombieSnapshot zombie = position.createEntity(EntityTypes.CLASSIC_ZOMBIE);

LiveClassicZombie spawned = zombie.spawn();

Note how there are no castings done. This is unlike Bukkit and Sponge as both the similar functions require a cast to be made. 



The scheduler in ShipsCore is designed to be simple to use. Based of the Sponge scheduler, it allows you to either run a delayed task or a repeating task. Simply create a scheduler builder, set the executor and then you may set a delay and/or a iteration. By setting just a delay, it will only run the executor once after the time has passed. By setting a iteration then the task will repeat. 

The schedule builder allows you to set a time unit for both the delay and the iteration. As the TimeUnit class is part of Java itself and not Minecraft, it does not have the time unit of tick (1/20 of a second). You can still set this time unit by setting the time unit to null. 


Runnable executor;
Plugin yourPlugin;
Schedule schedule = CorePlugin.getSchedulerBuilder().setExecutor(executor).setDelay(1).setDelayUnit(TimeUnit.SECONDS).build();

 Note how creating the schedule does not automatically start the task like it does in Bukkit and Sponge. This is so you can make calls for the scheduler such as stop the schedule if you wish. 



In both Bukkit and Sponge you listen to events using a Listen class, ShipsCore is no exception. You can find a list of all ShipsCore events here

public void onBlockBreak(BlockBreakEvent.ByPlayer event){

 Note that the example listener is listening for a nested event, most events in ShipsCore have nested sub events of the main event. This means whenever a sub event fires, the main event also fires. In turn this means that you can listen to Event and every ShipsCore implemented event (including plugins that have ShipsCore events in them) will be fired to that event listener. 


Also note that the compatibility layer still relies on its implemention, im the case of Bukkit there is only one BlockBreakEvent to listen to and it needs to have a player, therefore BlockBreakEvent.ByPlugin will not fire on the Bukkit implementation however will on Sponges implementation.


Command Source:

By default, commands have the source of CommandSource however a command source can be many different objects including a ConsoleSource, LivePlayer or even CommandBlock. For your conviniance this has been broken down considerably. Any Source that has a text based output (such as Players chat, or Console messages) can be casted into a CommandViewer which allows the use of sending messages. Another benefit is if you wanted the location that a command was sent, this is where the CommandSource can be casted into Positionable which exposes the location that the source is at. Due to the fact a command block has a BlockPosition while a player has a ExactPosition the position given is not determined 


Getting a Vessel

When it comes to Ships 6 and Vessels, custom plugins can add there own type of vessel with its own code running in the background, this results in custom versions of vessels needing to be loaded and received in there own way. 

After the ship is loaded it will be registered within the Ships plugin. You can get the list of registered Ships by invoking the function found in the ShipsPlugin however if you wish to find a single Ship that has a set requirement then you can either search for it yourself using the previously mentioned list, or you can use one of the provided ShipFinder tools (Note, you may find a ShipLoader, these load from the file and should only be used to initially load the ship)