Add documentation for custom blocks and custom skulls (#115)
* Add custom blocks and skulls documentation * TOC * Typo * Fix JSON formatting * Correct configs and update custom skulls Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Skull hashes instead of URLs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Update docs for transform component & skull extraction * Update blocks docs Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Add some missing fields Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Better mobile experience with long list Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review from @onebeastchris Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com>
This commit is contained in:
1 parent
9376e2669a
commit
3aec522104
4 files changed
+641
No files matched your search
@@ -40,6 +40,22 @@
|
||||
url: "geyser/custom-items/#geyser-extensions"
|
||||
- title: Resource pack
|
||||
url: "geyser/custom-items/#resource-pack"
|
||||
- title: Custom blocks with Geyser
|
||||
url: "geyser/custom-blocks/"
|
||||
children:
|
||||
- title: Enabling custom blocks
|
||||
url: "geyser/custom-blocks/#enabling-custom-blocks"
|
||||
- title: JSON mappings
|
||||
url: "geyser/custom-blocks/#json-mappings"
|
||||
- title: Geyser extensions
|
||||
url: "geyser/custom-blocks/#geyser-extensions"
|
||||
- title: Custom skulls with Geyser
|
||||
url: "geyser/custom-skulls/"
|
||||
children:
|
||||
- title: Enabling custom skulls
|
||||
url: "geyser/custom-skulls/#enabling-custom-skulls"
|
||||
- title: custom-skulls.yml
|
||||
url: "geyser/custom-skulls/#custom-skulls.yml"
|
||||
- title: Geyser API
|
||||
url: "geyser/api/"
|
||||
children:
|
||||
|
||||
@@ -0,0 +1,471 @@
|
||||
---
|
||||
title: Custom blocks with Geyser
|
||||
---
|
||||
|
||||
# Custom blocks with Geyser
|
||||
|
||||
To setup custom blocks in geyser, you have to choose how you are going to register your blocks. The easiest is [using a json file](#json-mappings) but you can also [use a Geyser extension](#geyser-extensions).
|
||||
|
||||
It should be noted that blocks and their associated components are not very stable. Mojang tends to make changes to these much more often than they do for items. This means that any components Geyser allows you to register are liable to break in future versions of Bedrock.
|
||||
|
||||
## Enabling custom blocks
|
||||
|
||||
Before beginning, ensure that `add-non-bedrock-items` is set to `true` in your `config.yml` file.
|
||||
|
||||
```yml
|
||||
# Whether to add any items and blocks which normally does not exist in Bedrock Edition.
|
||||
# This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching.
|
||||
# If this is disabled, furnace minecart items will be mapped to hopper minecart items.
|
||||
# Geyser's block, item, and skull mappings systems will also be disabled.
|
||||
# This option requires a restart of Geyser in order to change its setting.
|
||||
add-non-bedrock-items: true
|
||||
```
|
||||
|
||||
## JSON mappings
|
||||
|
||||
The JSON mappings use a similar structure to that of behavior packs registering blocks. The equivalent components are listed in the [Minecraft: Bedrock Edition Creator Documentation](https://learn.microsoft.com/en-us/minecraft/creator/reference/content/blockreference/examples/blockcomponents/blockcomponentslist).
|
||||
|
||||
Custom mappings files that register blocks or items should be placed in the `custom_mappings` folder. This folder is created when you start your server. It is located in the same folder as the `Geyser-Standalone.jar` file for standalone and inside your Geyser data folder for a plugin. If you do not have this folder, ensure you are on the latest version of Geyser.
|
||||
|
||||
### Example mappings file
|
||||
|
||||
```json
|
||||
{
|
||||
"format_version": 1,
|
||||
"blocks": {
|
||||
"minecraft:granite_wall": {
|
||||
"name": "my_block",
|
||||
"display_name": "Custom Granite Wall",
|
||||
"geometry": "geometry.blocks.my_block_geo",
|
||||
"material_instances": {
|
||||
"*": {
|
||||
"texture": "some_texture",
|
||||
"render_method": "alpha_test",
|
||||
"face_dimming": true,
|
||||
"ambient_occlusion": true
|
||||
}
|
||||
},
|
||||
"tags": ["stone", "wall"],
|
||||
"state_overrides": {
|
||||
"east=none,north=none,south=none,up=true,waterlogged=true,west=none": {
|
||||
"geometry": "geometry.blocks.my_other_block_geo",
|
||||
"destructible_by_mining": 10,
|
||||
"place_air": false
|
||||
},
|
||||
"east=none,north=none,south=none,up=false,waterlogged=true,west=tall": {
|
||||
"friction": 0.6,
|
||||
"light_emission": 7,
|
||||
"light_dampening": 8,
|
||||
"transformation": {
|
||||
"scale": [0.5, 0.5, 0.5],
|
||||
"translation": [1, 0, 0],
|
||||
"rotation": [0, 90, 0]
|
||||
}
|
||||
},
|
||||
"east=none,north=none,south=low,up=true,waterlogged=true,west=tall": {
|
||||
"placement_filter": {
|
||||
"conditions": [{
|
||||
"allowed_faces": ["up", "down"],
|
||||
"block_filter": [{
|
||||
"tags": "!query.any_tag('stone')"
|
||||
},
|
||||
"minecraft:dirt"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Schema
|
||||
|
||||
The following details the schema for the mappings file. Only the `name` field is strictly required. All other fields are optional.
|
||||
|
||||
<div class="long-list" markdown="1">
|
||||
|
||||
- `format_version`:
|
||||
- Type: `integer`
|
||||
- Description: The version of the format of the mappings file.
|
||||
- `blocks`:
|
||||
- Type: `object`
|
||||
- Description: An object containing a list of block definitions.
|
||||
- `minecraft:some_block`:
|
||||
- Type: `object`
|
||||
- Description: A block definition to be used to override the specified java block.
|
||||
- `name`:
|
||||
- Type: `string`
|
||||
- Default: none
|
||||
- Description: The name of the custom block.
|
||||
- `collision_box`:
|
||||
- Type: `object`
|
||||
- `origin`:
|
||||
- Type: `array`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An array of x, y, and z origin values
|
||||
- Range: Must be in range `[-8, 0, -8]` to `[8, 16, 8]`, inclusive
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An origin value for a single axis
|
||||
- `size`:
|
||||
- Type: `array`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An array of x, y, and z size values
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An size value for a single axis
|
||||
- Range: Addittion of `origin` and `size` must be in range `[-8, 0, -8]` to `[8, 16, 8]`, inclusive.
|
||||
- `destructible_by_mining`:
|
||||
- Type: `integer`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: The time in seconds to mine the block with base tools.
|
||||
- `display_name`:
|
||||
- Type: `string`
|
||||
- Default: the name of the custom block
|
||||
- Description: The display name of the block.
|
||||
- `extended_collision_box`:
|
||||
- Type: `object`
|
||||
- `origin`:
|
||||
- Type: `array`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An array of x, y, and z origin values
|
||||
- Range: Must be in range `[-8, 0, -8]` to `[8, 16, 8]`, inclusive
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An origin value for a single axis
|
||||
- `size`:
|
||||
- Type: `array`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An array of x, y, and z size values
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An size value for a single axis
|
||||
- Range: Addittion of `origin` and `size` must be in range `[-8, 0, -8]` to `[8, 16, 8]`, inclusive.
|
||||
- `friction`:
|
||||
- Type: `float`
|
||||
- Range: `0.0` to `1.0`
|
||||
- Default: `0.4`
|
||||
- Description: The friction value for entities traversing the block.
|
||||
- `geometry`:
|
||||
- Type: `string`
|
||||
- Default: none
|
||||
- Description: The geometry identifier of the block.
|
||||
- Type: `object`
|
||||
- Description: The geometry of the block with bone visbility filters
|
||||
- `identifier`
|
||||
- Type: `string`
|
||||
- Default: none
|
||||
- Description: The geometry identifier of the block.
|
||||
- `bone_visibility`:
|
||||
- Type: `object`
|
||||
- Description: An object containing the bone visibility filters for the block.
|
||||
- `bone_name`:
|
||||
- Type: `string`
|
||||
- Default: none
|
||||
- Description: A molang string dictating whether the bone is visible.
|
||||
- Type: `boolean`
|
||||
- Default: none
|
||||
- Description: Whether the bone is visible.
|
||||
- `light_emission`:
|
||||
- Type: `integer`
|
||||
- Range: `0` to `15`
|
||||
- Default: `0`
|
||||
- Description: The amount of light emitted by the block.
|
||||
- `light_dampening`:
|
||||
- Type: `integer`
|
||||
- Range: `0` to `15`
|
||||
- Default: `15`
|
||||
- Description: The amount of light dampened by the block as it passes through.
|
||||
- `material_instances`:
|
||||
- Type: `object`
|
||||
- Description: An object containing the material instances for the block.
|
||||
- `key`:
|
||||
- Type: `object`
|
||||
- Description: The default material instance for the block. Other globs or specific instances can be used.
|
||||
- `texture`:
|
||||
- Type: `string`
|
||||
- Default: the name of the custom block
|
||||
- Description: The texture resource path of the block.
|
||||
- `render_method`:
|
||||
- Type: `string`
|
||||
- Default: `alpha_test`
|
||||
- Description: The render method used for the block.
|
||||
- `face_dimming`:
|
||||
- Type: `boolean`
|
||||
- Default: `false`
|
||||
- Description: Whether face dimming is enabled for the block.
|
||||
- `ambient_occlusion`:
|
||||
- Type: `boolean`
|
||||
- Default: `false`
|
||||
- Description: Whether ambient occlusion is enabled for the block.
|
||||
- `place_air`:
|
||||
- Type: `boolean`
|
||||
- Default: `true`
|
||||
- Description: Whether the block should place air to prevent double placement.
|
||||
- `placement_filter`:
|
||||
- Type: `object`
|
||||
- Description: An object containing the placement filter for the block.
|
||||
- `conditions`:
|
||||
- Type: `array`
|
||||
- Description: An array of conditions that must be met for the block to be placed.
|
||||
- `allowed_faces`:
|
||||
- Type: `array`
|
||||
- Description: An array of faces that the block can be placed on.
|
||||
- `items`:
|
||||
- Type: `string`
|
||||
- Range: `up`, `down`, `north`, `south`, `east`, `west`
|
||||
- Description: A face that the block can be placed on.
|
||||
- `block_filter`:
|
||||
- Type: `array`
|
||||
- Description: An array of blocks or true molang queries that the block can be placed on.
|
||||
- `items`:
|
||||
- Type: `string`
|
||||
- Description: A block that the block can be placed on.
|
||||
- Type `object`
|
||||
- Description: Holds a true molang query that the block can be placed on.
|
||||
- `tags`:
|
||||
- Type: `array`
|
||||
- Description: A true molang query that the block can be placed on.
|
||||
- `selection_box`:
|
||||
- Type: `object`
|
||||
- `origin`:
|
||||
- Type: `array`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An array of x, y, and z origin values
|
||||
- Range: Must be in range `[-8, 0, -8]` to `[8, 16, 8]`, inclusive
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An origin value for a single axis
|
||||
- `size`:
|
||||
- Type: `array`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An array of x, y, and z size values
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: Inferred from overridden block
|
||||
- Description: An size value for a single axis
|
||||
- Range: Addittion of `origin` and `size` must be in range `[-8, 0, -8]` to `[8, 16, 8]`, inclusive.
|
||||
- `tags`:
|
||||
- Type: `array`
|
||||
- Description: An array of tags associated with the block.
|
||||
- `items`:
|
||||
- Type: `string`
|
||||
- `transformation`:
|
||||
- Type: `object`
|
||||
- Description: Translation, scale, and rotation values to apply to the block.
|
||||
- `scale`
|
||||
- Type: `array`
|
||||
- Default: `[1, 1, 1]`
|
||||
- Description: An array of x, y, and z scale values
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: `1`
|
||||
- Description: A scale value for a single axis
|
||||
- `translation`:
|
||||
- Type: `array`
|
||||
- Default: `[0, 0, 0]`
|
||||
- Description: An array of x, y, and z translation values
|
||||
- `items`:
|
||||
- Type: `float`
|
||||
- Default: `0`
|
||||
- Description: A translation value for a single axis
|
||||
- `rotation`
|
||||
- Type: `array`
|
||||
- Default: `[0, 0, 0]`
|
||||
- Description: An array of x, y, and z rotation values in increments of 90 degrees (e.g. `[90, -180, 0]`)
|
||||
- `items`:
|
||||
- Type: `integer`
|
||||
- Range: `0`, `90`, `180`, & `270`
|
||||
- Default: `0`
|
||||
- Description: A rotation value for a single axis
|
||||
- `unit_cube`:
|
||||
- Type: `boolean`
|
||||
- Default: `false`
|
||||
- Description: Whether a unit cube is to be used with tessellation.
|
||||
- `creative_category`:
|
||||
- Type: `string`
|
||||
- Default: `building_blocks`
|
||||
- Description: The creative category to place the block in.
|
||||
- Range: See [Creative Categories](https://wiki.bedrock.dev/documentation/creative-categories.html#list-of-creative-categories) on the Bedrock Wiki.
|
||||
- `creative_group`:
|
||||
- Type: `string`
|
||||
- Default: none
|
||||
- Description: The creative group to place the block in.
|
||||
- Range: See [Creative Tabs](https://wiki.bedrock.dev/documentation/creative-categories.html#list-of-creative-tabs) on the Bedrock Wiki.
|
||||
- `included_in_creative_inventory`:
|
||||
- Type: `boolean`
|
||||
- Default: `true`
|
||||
- Description: Whether the block is included in the creative inventory.
|
||||
- `only_override_states`:
|
||||
- Type: `boolean`
|
||||
- Default: `false`
|
||||
- Description: Whether the block should only override the states specified in `state_overrides`.
|
||||
- `state_overrides`:
|
||||
- Type: `object`
|
||||
- Description: An object containing state overrides for the block.
|
||||
- `property1=value1,property2=value2,...`:
|
||||
- Type: `object`
|
||||
- Description: An override for a specific block state. Possible states are listed in Geyser's [Block Mappings](https://raw.githubusercontent.com/GeyserMC/mappings/master/blocks.json)
|
||||
- Accepts all of the same properties as a block definition except `creative_category`, `creative_group`, `included_in_creative_inventory`, `only_override_states`, and `state_overrides`.
|
||||
|
||||
</div>
|
||||
|
||||
## Geyser extensions
|
||||
|
||||
In this example, we will create a custom block that overrides the vanilla redstone dot. This block will be placed on top of a block and will emit a visible redstone signal when powered.
|
||||
|
||||
First, create a class that implements Geyser's Extension class:
|
||||
|
||||
```java
|
||||
public class RedstoneDot implements Extension {
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Next, create a method to register your blocks in the `GeyserDefineCustomBlocksEvent`:
|
||||
|
||||
```java
|
||||
public class RedstoneDot implements Extension {
|
||||
@Subscribe
|
||||
public void onDefineCustomBlocks(GeyserDefineCustomBlocksEvent event) {
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Build the `CustomBlockComponents`, `CustomBlockData`, and list of `CustomBlockPermutation` (if needed) for the block:
|
||||
|
||||
```java
|
||||
public class RedstoneDot implements Extension {
|
||||
@Subscribe
|
||||
public void onDefineCustomBlocks(GeyserDefineCustomBlocksEvent event) {
|
||||
BoxComponent selectionBox = new BoxComponent(-5, 0, -5, 10, 1f, 10);
|
||||
|
||||
CustomBlockComponents components = GeyserApi.api().provider(CustomBlockComponents.Builder.class)
|
||||
.collisionBox(BoxComponent.EMPTY_BOX)
|
||||
.selectionBox(selectionBox)
|
||||
.geometry(new GeometryComponentBuilder()
|
||||
.identifier("geometry.amberichu.redstone_dot")
|
||||
.build())
|
||||
.destroyTime(0f)
|
||||
.lightEmission(0)
|
||||
.lightDampening(0)
|
||||
.friction(1f)
|
||||
.build();
|
||||
|
||||
CustomBlockData redstoneDot = GeyserApi.api().provider(CustomBlockData.Builder.class)
|
||||
.name("redstone_dot")
|
||||
.intProperty(POWER_PROPERTY, IntStream.range(0, 16).boxed().toList())
|
||||
.components(components)
|
||||
.permutations(createRedstoneDotPermutations())
|
||||
.build();
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
private List<CustomBlockPermutation> createRedstoneDotPermutations() {
|
||||
List<CustomBlockPermutation> permutations = new ArrayList<>();
|
||||
for (int power = 0; power < 16; power++) {
|
||||
String texture = "amberichu.redstone_dot" + power;
|
||||
MaterialInstance invisMaterialInstance = new MaterialInstanceBuilder()
|
||||
.texture("amberichu.invisible")
|
||||
.renderMethod("alpha_test")
|
||||
.faceDimming(false)
|
||||
.ambientOcclusion(false)
|
||||
.build();
|
||||
CustomBlockComponents components = GeyserApi.api().provider(CustomBlockComponents.Builder.class)
|
||||
.materialInstance("up", new MaterialInstanceBuilder()
|
||||
.texture(texture)
|
||||
.renderMethod("alpha_test")
|
||||
.faceDimming(false)
|
||||
.ambientOcclusion(false)
|
||||
.build())
|
||||
.materialInstance("down", invisMaterialInstance)
|
||||
.materialInstance("north", invisMaterialInstance)
|
||||
.materialInstance("south", invisMaterialInstance)
|
||||
.materialInstance("east", invisMaterialInstance)
|
||||
.materialInstance("west", invisMaterialInstance)
|
||||
.build();
|
||||
String condition = String.format("query.block_property('%s') == %d", POWER_PROPERTY, power);
|
||||
permutations.add(new CustomBlockPermutation(components, condition));
|
||||
}
|
||||
return permutations;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally, register the custom block, block state overrides, and block item overrides:
|
||||
|
||||
```java
|
||||
public class RedstoneDot implements Extension {
|
||||
@Subscribe
|
||||
public void onDefineCustomBlocks(GeyserDefineCustomBlocksEvent event) {
|
||||
BoxComponent selectionBox = new BoxComponent(-5, 0, -5, 10, 1f, 10);
|
||||
|
||||
CustomBlockComponents components = GeyserApi.api().provider(CustomBlockComponents.Builder.class)
|
||||
.collisionBox(BoxComponent.EMPTY_BOX)
|
||||
.selectionBox(selectionBox)
|
||||
.geometry(new GeometryComponentBuilder()
|
||||
.identifier("geometry.amberichu.redstone_dot")
|
||||
.build())
|
||||
.destroyTime(0f)
|
||||
.lightEmission(0)
|
||||
.lightDampening(0)
|
||||
.friction(1f)
|
||||
.build();
|
||||
|
||||
CustomBlockData redstoneDot = GeyserApi.api().provider(CustomBlockData.Builder.class)
|
||||
.name("redstone_dot")
|
||||
.intProperty(POWER_PROPERTY, IntStream.range(0, 16).boxed().toList())
|
||||
.components(components)
|
||||
.permutations(createRedstoneDotPermutations())
|
||||
.build();
|
||||
|
||||
event.registerCustomBlock(redstoneDot);
|
||||
event.registerBlockItemOverride("minecraft:redstone_wire", redstoneDot);
|
||||
|
||||
for (int power = 0; power < 16; power++) {
|
||||
String javaIdentifier = String.format("minecraft:redstone_wire[east=none,north=none,power=%d,south=none,west=none]", power);
|
||||
event.registerBlockStateOverride(javaIdentifier, redstoneDot.blockStateBuilder()
|
||||
.intProperty(POWER_PROPERTY, power)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private List<CustomBlockPermutation> createRedstoneDotPermutations() {
|
||||
List<CustomBlockPermutation> permutations = new ArrayList<>();
|
||||
for (int power = 0; power < 16; power++) {
|
||||
String texture = "amberichu.redstone_dot" + power;
|
||||
MaterialInstance invisMaterialInstance = new MaterialInstanceBuilder()
|
||||
.texture("amberichu.invisible")
|
||||
.renderMethod("alpha_test")
|
||||
.faceDimming(false)
|
||||
.ambientOcclusion(false)
|
||||
.build();
|
||||
CustomBlockComponents components = GeyserApi.api().provider(CustomBlockComponents.Builder.class)
|
||||
.materialInstance("up", new MaterialInstanceBuilder()
|
||||
.texture(texture)
|
||||
.renderMethod("alpha_test")
|
||||
.faceDimming(false)
|
||||
.ambientOcclusion(false)
|
||||
.build())
|
||||
.materialInstance("down", invisMaterialInstance)
|
||||
.materialInstance("north", invisMaterialInstance)
|
||||
.materialInstance("south", invisMaterialInstance)
|
||||
.materialInstance("east", invisMaterialInstance)
|
||||
.materialInstance("west", invisMaterialInstance)
|
||||
.build();
|
||||
String condition = String.format("query.block_property('%s') == %d", POWER_PROPERTY, power);
|
||||
permutations.add(new CustomBlockPermutation(components, condition));
|
||||
}
|
||||
return permutations;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: Custom skulls with Geyser
|
||||
---
|
||||
|
||||
# Custom skulls with Geyser
|
||||
|
||||
Unlike Java Edition, Bedrock does not have native support for custom skull items. As a result, any method to display custom skulls with Geyser is, to some extent, a workaround. Geyser has long supported in-world custom skulls via the spawning of player entities. This, however, does not allow for the use of custom skulls in inventories, nor does it allow them to be worn by entities. To resolve this, Geyser now allows for the pre-registration of custom skulls via a config file. Geyser will then use this config file to generate a custom resource pack on start that contains the geometry and textures for the pre-registered custom skulls. To the client, these skulls are blocks. Therefore, they can be held in player inventories. In additional, attachables are defined for each skull block so that it is displayed correctly when worn and held by entities.
|
||||
|
||||
To setup custom skulls in geyser, you have to choose how you are going to register your blocks. The easiest is [using custom-skulls.yml](#custom-skulls.yml) but you can also [use a Geyser extension](#geyser-extensions).
|
||||
|
||||
## Enabling custom skulls
|
||||
|
||||
To enable custom skulls, you must set `add-non-bedrock-items` to `true` in the `config.yml` file. This will enable the generation of the custom resource pack and the translation of custom skulls to custom blocks. You can then add custom skulls to the `custom-skulls.yml` file.
|
||||
|
||||
```yaml
|
||||
# Whether to add any items and blocks which normally does not exist in Bedrock Edition.
|
||||
# This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching.
|
||||
# If this is disabled, furnace minecart items will be mapped to hopper minecart items.
|
||||
# Geyser's block, item, and skull mappings systems will also be disabled.
|
||||
# This option requires a restart of Geyser in order to change its setting.
|
||||
add-non-bedrock-items: true
|
||||
```
|
||||
|
||||
## custom-skulls.yml
|
||||
|
||||
The configuration file `custom-skulls.yml` is present in Geyser's configuration folder and is laid out as follows:
|
||||
|
||||
```yml
|
||||
# --------------------------------
|
||||
# Geyser Custom Skull Configuration Files
|
||||
#
|
||||
# This file is ignored with `add-custom-skull-blocks` disabled.
|
||||
# See `config.yml` for the main set of configuration values
|
||||
#
|
||||
# Custom skulls with the player username, UUID, or texture specified in this file
|
||||
# will be translated as custom blocks and be displayed in the inventory and on entities.
|
||||
# --------------------------------
|
||||
|
||||
# Java player usernames
|
||||
# Skins will be updated when Geyser starts and players will have to re-download
|
||||
# the resource pack if any players had changed their skin.
|
||||
player-usernames:
|
||||
- GeyserMC
|
||||
|
||||
# Java player UUIDs
|
||||
# Skins will be updated when Geyser starts and players will have to re-download
|
||||
# the resource pack if any players had changed their skin.
|
||||
player-uuids:
|
||||
- 8b8d8e8f-2759-47c6-acb5-5827de8a72b8
|
||||
|
||||
# The long string of characters found in the NBT of custom player heads
|
||||
player-profiles:
|
||||
- ewogICJ0aW1lc3RhbXAiIDogMTY1NzMyMjIzOTgzMywKICAicHJvZmlsZUlkIiA6ICJjZGRiZTUyMGQwNDM0YThiYTFjYzlmYzkyZmRlMmJjZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbWJlcmljaHUiLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTkwNzkwYzU3ZTE4MWVkMTNhZGVkMTRjNDdlZTJmN2M4ZGUzNTMzZTAxN2JhOTU3YWY3YmRmOWRmMWJkZTk0ZiIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9
|
||||
|
||||
# The hash of the skin on Minecraft's skin server
|
||||
skin-hashes:
|
||||
- a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f
|
||||
```
|
||||
|
||||
### Player usernames
|
||||
|
||||
Skulls can be registered in this section via player usernames. These will be updated when Geyser starts. As a result, they may change if the player's username or skin changes.
|
||||
|
||||
### Player UUIDs
|
||||
|
||||
Skulls can be registered in this section via player UUIDs. These will be updated when Geyser starts. As a result, they may change if skin changes.
|
||||
|
||||
### Player profiles
|
||||
|
||||
Skulls can be registered in this section via the texture string found in the NBT of custom player heads. Unless the value is changed manually, these will not be updated when Geyser starts. As a result, they will not change if the player's username or skin changes. The data is simply base64 encoded JSON. For instance, the example given in the config decodes as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp" : 1657322239833,
|
||||
"profileId" : "cddbe520d0434a8ba1cc9fc92fde2bcf",
|
||||
"profileName" : "Amberichu",
|
||||
"textures" : {
|
||||
"SKIN" : {
|
||||
"url" : "http://textures.minecraft.net/texture/a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f",
|
||||
"metadata" : {
|
||||
"model" : "slim"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If on a paper server, this data can be obtained for a skull by holding the item in hand, and running the command `/paper dumpitem`. This will output the NBT data of the item to chat and console. The texture string is found in the `SkullOwner` tag, under the `Properties` tag, under the `textures` tag, under the `Value` tag. For example:
|
||||
|
||||
```log
|
||||
[05:58:07 INFO]: .KastleFirefox issued server command: /paper dumpitem
|
||||
[05:58:07 INFO]: minecraft:player_head{display: {Name: '{"text":"Test"}'}, SkullOwner: {Properties: {textures: [{Value: "ewogICJ0aW1lc3RhbXAiIDogMTY1NzMyMjIzOTgzMywKICAicHJvZmlsZUlkIiA6ICJjZGRiZTUyMGQwNDM0YThiYTFjYzlmYzkyZmRlMmJjZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJkYXZjaG9vIiwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2E5MDc5MGM1N2UxODFlZDEzYWRlZDE0YzQ3ZWUyZjdjOGRlMzUzM2UwMTdiYTk1N2FmN2JkZjlkZjFiZGU5NGYiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ"}]}, Id: [I; -229048314, -553040501, -1407961158, 465313087]}}
|
||||
```
|
||||
|
||||
### Skin Hashes
|
||||
|
||||
Skulls can be registered in this section via the hash of the skin on Minecraft's skin server. This is found at the end of the URL. For example, in the URL `http://textures.minecraft.net/texture/a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f` the hash is `a90790c57e181ed13aded14c47ee2f7c8de3533e017ba957af7bdf9df1bde94f`.Unless the value is changed manually, these will not be updated when Geyser starts. As a result, they will not change if the player's username or skin changes.
|
||||
|
||||
This can be obtained by decoding the base64 data obtained from a player profile.
|
||||
|
||||
## Geyser extensions
|
||||
|
||||
In this example, we will register a custom skull from a player profile. We will use the player profile from the example above.
|
||||
|
||||
First, create a class that implements Geyser's Extension class:
|
||||
|
||||
```java
|
||||
public class RegisterCustomSkull implements Extension {
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Next, create a method to register your blocks in the `GeyserDefineCustomSkullsEvent`:
|
||||
|
||||
```java
|
||||
public class RegisterCustomSkull implements Extension {
|
||||
@Subscribe
|
||||
public void onDefineCustomSkulls(GeyserDefineCustomSkullsEvent event) {
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Finally, register your skull in the event. Use the enum SkullTextureType to specify that the value being passed is a player profile:
|
||||
|
||||
```java
|
||||
public class RegisterCustomSkull implements Extension {
|
||||
@Subscribe
|
||||
public void onDefineCustomSkulls(GeyserDefineCustomSkullsEvent event) {
|
||||
String profile = "ewogICJ0aW1lc3RhbXAiIDogMTY1NzMyMjIzOTgzMywKICAicHJvZmlsZUlkIiA6ICJjZGRiZTUyMGQwNDM0YThiYTFjYzlmYzkyZmRlMmJjZiIsCiAgInByb2ZpbGVOYW1lIiA6ICJkYXZjaG9vIiwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2E5MDc5MGM1N2UxODFlZDEzYWRlZDE0YzQ3ZWUyZjdjOGRlMzUzM2UwMTdiYTk1N2FmN2JkZjlkZjFiZGU5NGYiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ"
|
||||
event.registerCustomSkull(profile, SkullTextureType.PROFILE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Scraping custom skulls from a world
|
||||
|
||||
Custom skulls can be extracted from an existing world for easy implementation via mappings or API using the tool [HeadExtractor](https://github.com/davchoo/HeadExtractor) made by Amberichu. See the linked repository for usage instructions.
|
||||
@@ -54,8 +54,24 @@ div.highlight {
|
||||
|
||||
.highlight pre {
|
||||
margin: 0;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
|
||||
div.long-list {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.long-list > ul {
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
|
||||
.long-list ul {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
|
||||
.no-p-margin p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user