Блоки-контейнери
Це гарна практика під час створення блоків, які можуть зберігати предмети, як-от скрині та печі, реалізувати Контейнер. Це дає можливість, наприклад, взаємодіяти з блоком за допомогою лійок.
У цьому посібнику ми створимо блок, який використовує свій контейнер для дублювання будь-яких розміщених у ньому предметів.
Створення блока
Це повинно бути знайоме читачеві, якщо він дотримувався посібників створення вашого першого блока і блока-сутності. Ми створимо DuplicatorBlock, який розширює BaseEntityBlock і реалізує EntityBlock.
java
No lines matched.1
Потім нам потрібно створити DuplicatorBlockEntity, який має реалізувати інтерфейс Container. Оскільки зазвичай очікується, що більшість контейнерів працюватимуть однаково, ви можете скопіювати та вставити помічник під назвою ImplementedContainer, який виконує більшу частину роботи, залишаючи нам лише кілька методів для реалізації.
Показати ImplementedContainer
java
package com.example.docs.container;
import net.minecraft.core.NonNullList;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
/**
* A simple {@link Container} implementation with only default methods + an item list getter.
*
* @author Juuz
*/
public interface ImplementedContainer extends Container {
/**
* Retrieves the item list of this container.
* Must return the same instance every time it's called.
*/
NonNullList<ItemStack> getItems();
/**
* Creates a container from the item list.
*/
static ImplementedContainer of(NonNullList<ItemStack> items) {
return () -> items;
}
/**
* Creates a new container with the specified size.
*/
static ImplementedContainer ofSize(int size) {
return of(NonNullList.withSize(size, ItemStack.EMPTY));
}
/**
* Returns the container size.
*/
@Override
default int getContainerSize() {
return getItems().size();
}
/**
* Checks if the container is empty.
* @return true if this container has only empty stacks, false otherwise.
*/
@Override
default boolean isEmpty() {
for (int i = 0; i < getContainerSize(); i++) {
ItemStack stack = getItem(i);
if (!stack.isEmpty()) {
return false;
}
}
return true;
}
/**
* Retrieves the item in the slot.
*/
@Override
default ItemStack getItem(int slot) {
return getItems().get(slot);
}
/**
* Removes items from a container slot.
* @param slot The slot to remove from.
* @param count How many items to remove. If there are fewer items in the slot than what are requested,
* takes all items in that slot.
*/
@Override
default ItemStack removeItem(int slot, int count) {
ItemStack result = ContainerHelper.removeItem(getItems(), slot, count);
if (!result.isEmpty()) {
setChanged();
}
return result;
}
/**
* Removes all items from a container slot.
* @param slot The slot to remove from.
*/
@Override
default ItemStack removeItemNoUpdate(int slot) {
return ContainerHelper.takeItem(getItems(), slot);
}
/**
* Replaces the current stack in an container slot with the provided stack.
* @param slot The container slot of which to replace the item stack.
* @param stack The replacing item stack. If the stack is too big for
* this container ({@link Container#getMaxStackSize()}),
* it gets resized to this container's maximum amount.
*/
@Override
default void setItem(int slot, ItemStack stack) {
getItems().set(slot, stack);
if (stack.getCount() > stack.getMaxStackSize()) {
stack.setCount(stack.getMaxStackSize());
}
}
/**
* Clears the container.
*/
@Override
default void clearContent() {
getItems().clear();
}
/**
* Marks that the state has changed.
* Must be called after changes in the container, so that the game can properly save
* the container contents and notify neighboring blocks of container changes.
*/
@Override
default void setChanged() {
// Override if you want behavior.
}
/**
* @return true if the player can use the container, false otherwise.
*/
@Override
default boolean stillValid(Player player) {
return true;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
java
No lines matched.1
У списку items зберігається вміст контейнера. Для цього блока ми встановили розмір 1 слота для входу.
Не забудьте зареєструвати блок і сутність блока у відповідних класах!
Збереження і завантаження
Якщо ми хочемо, щоби вміст зберігався між перезавантаженнями гри, як стандартний BlockEntity, нам потрібно зберегти його як NBT. На щастя, Mojang надає допоміжний клас під назвою ContainerHelper з усією необхідною логікою.
java
No lines matched.1
Взаємодія з контейнером
Технічно контейнер вже справний. Однак для вставлення предметів нам наразі потрібні лійки. Зробімо так, щоби ми могли вставляти предмети, натиснувши ПКМ по блоку.
Для цього нам потрібно перевизначити метод useItemOn у DuplicatorBlock:
java
No lines matched.1
Тут, якщо гравець тримає предмет і є порожній слот, ми переміщуємо предмет з руки гравця в контейнер блока та повертаємо InteractionResult.SUCCESS.
Тепер, коли ви натискаєте ПКМ по блоку предметом, його більше не буде! Якщо ви запустите /data get block для блока, ви побачите предмет у полі Items у NBT.

Дублювання предметів
Зробімо тепер так, щоби блок дублював стіс, який ви в нього кинули, але лише два предмети одночасно. І нехай кожен раз чекає секунду, щоби не спамити гравця предметами!
Для цього ми додамо функцію tick до DuplicatorBlockEntity і поле для збереження того, скільки ми чекали:
java
No lines matched.1
DuplicatorBlock тепер має мати метод getTicker, який повертає посилання на DuplicatorBlockEntity::tick.
Світові контейнери
Усталено ви можете вставляти та витягувати предмети з контейнера з будь-якого боку. Однак інколи це може бути не бажаною поведінкою: наприклад, піч приймає паливо лише збоку, а предмети — зверху.
Щоби створити таку поведінку, нам потрібно реалізувати інтерфейс WorldlyContainer в BlockEntity. Цей інтерфейс має три методи:
getSlotsForFace(Direction)дозволяє контролювати, з якими слотами можна взаємодіяти з певного боку.canPlaceItemThroughFace(int, ItemStack, Direction)дозволяє контролювати, чи можна вставляти предмет у слот з певного боку.canTakeItemThroughFace(int, ItemStack, Direction)дозволяє контролювати, чи можна витягти предмет зі слота з певного боку.
Змінімо DuplicatorBlockEntity, щоб приймати лише елементи згори:
java
No lines matched.1
getSlotsForFace повертає масив індексів слотів, з якими можна взаємодіяти з даної сторони. У цьому випадку ми маємо лише один слот (0), тому ми повертаємо масив лише з цим індексом.
Крім того, ми повинні змінити метод useItemOn DuplicatorBlock, щоби фактично дотримуватися нової поведінки:
java
No lines matched.1
Тепер, якщо ми спробуємо вставити предмети збоку, а не зверху, це не спрацює!
Меню
Щоб отримати доступ до нового блока-контейнера через меню, подібно до скрині, зверніться до посібника меню контейнерів.

