/
inventory.go
276 lines (251 loc) · 10.3 KB
/
inventory.go
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package protocol
import (
"bytes"
"github.com/go-gl/mathgl/mgl32"
)
const (
InventoryActionSourceContainer = 0
InventoryActionSourceWorld = 2
InventoryActionSourceCreative = 3
InventoryActionSourceTODO = 99999
)
const (
WindowIDInventory = 0
WindowIDOffHand = 119
WindowIDArmour = 120
WindowIDCreative = 121
WindowIDUI = 124
)
// InventoryAction represents a single action that took place during an inventory transaction. On itself, this
// inventory action is always unbalanced: It must be combined with other actions in an inventory transaction
// to form a balanced transaction.
type InventoryAction struct {
// SourceType is the source type of the inventory action. It is one of the constants above.
SourceType uint32
// WindowID is the ID of the window that the client has opened. The window ID is not set if the SourceType
// is InventoryActionSourceWorld.
WindowID int32
// SourceFlags is a combination of flags that is only set if the SourceType is InventoryActionSourceWorld.
SourceFlags uint32
// InventorySlot is the slot in which the action took place. Each action only describes the change of item
// in a single slot.
InventorySlot uint32
// OldItem is the item that was present in the slot before the inventory action. It should be checked by
// the server to ensure the inventories were not out of sync.
OldItem ItemStack
// NewItem is the new item that was put in the InventorySlot that the OldItem was in. It must be checked
// in combination with other inventory actions to ensure that the transaction is balanced.
NewItem ItemStack
}
// InvAction reads an inventory action from buffer src.
func InvAction(src *bytes.Buffer, action *InventoryAction) error {
if err := Varuint32(src, &action.SourceType); err != nil {
return wrap(err)
}
switch action.SourceType {
case InventoryActionSourceContainer, InventoryActionSourceTODO:
if err := Varint32(src, &action.WindowID); err != nil {
return wrap(err)
}
case InventoryActionSourceWorld:
if err := Varuint32(src, &action.SourceFlags); err != nil {
return wrap(err)
}
}
return chainErr(
Varuint32(src, &action.InventorySlot),
Item(src, &action.OldItem),
Item(src, &action.NewItem),
)
}
// WriteInvAction writes an inventory action to buffer dst.
func WriteInvAction(dst *bytes.Buffer, action InventoryAction) error {
if err := WriteVaruint32(dst, action.SourceType); err != nil {
return wrap(err)
}
switch action.SourceType {
case InventoryActionSourceContainer, InventoryActionSourceTODO:
if err := WriteVarint32(dst, action.WindowID); err != nil {
return wrap(err)
}
case InventoryActionSourceWorld:
if err := WriteVaruint32(dst, action.SourceFlags); err != nil {
return wrap(err)
}
}
return chainErr(
WriteVaruint32(dst, action.InventorySlot),
WriteItem(dst, action.OldItem),
WriteItem(dst, action.NewItem),
)
}
// InventoryTransactionData represents an object that holds data specific to an inventory transaction type.
// The data it holds depends on the type.
type InventoryTransactionData interface {
// Marshal encodes the inventory transaction data to its binary representation into buf.
Marshal(buf *bytes.Buffer)
// Unmarshal decodes a serialised inventory transaction data object in buf into the
// InventoryTransactionData instance.
Unmarshal(buf *bytes.Buffer) error
}
// NormalTransactionData represents an inventory transaction data object for normal transactions, such as
// crafting. It has no content.
type NormalTransactionData struct{}
// MismatchTransactionData represents a mismatched inventory transaction's data object.
type MismatchTransactionData struct{}
const (
UseItemActionClickBlock = iota
UseItemActionClickAir
UseItemActionBreakBlock
)
// UseItemTransactionData represents an inventory transaction data object sent when the client uses an item on
// a block.
type UseItemTransactionData struct {
// ActionType is the type of the UseItem inventory transaction. It is one of the action types found above,
// and specifies the way the player interacted with the block.
ActionType uint32
// BlockPosition is the position of the block that was interacted with. This is only really a correct
// block position if ActionType is not UseItemActionClickAir.
BlockPosition BlockPos
// BlockFace is the face of the block that was interacted with. When clicking the block, it is the face
// clicked. When breaking the block, it is the face that was last being hit until the block broke.
BlockFace int32
// HotBarSlot is the hot bar slot that the player was holding while clicking the block. It should be used
// to ensure that the hot bar slot and held item are correctly synchronised with the server.
HotBarSlot int32
// HeldItem is the item that was held to interact with the block. The server should check if this item
// is actually present in the HotBarSlot.
HeldItem ItemStack
// Position is the position of the player at the time of interaction. For clicking a block, this is the
// position at that time, whereas for breaking the block it is the position at the time of breaking.
Position mgl32.Vec3
// ClickedPosition is the position that was clicked relative to the block's base coordinate. It can be
// used to find out exactly where a player clicked the block.
ClickedPosition mgl32.Vec3
// BlockRuntimeID is the runtime ID of the block that was clicked. It may be used by the server to verify
// that the player's world client-side is synchronised with the server's.
BlockRuntimeID uint32
}
const (
UseItemOnEntityActionInteract = iota
UseItemOnEntityActionAttack
)
// UseItemOnEntityTransactionData represents an inventory transaction data object sent when the client uses
// an item on an entity.
type UseItemOnEntityTransactionData struct {
// TargetEntityRuntimeID is the entity runtime ID of the target that was clicked. It is the runtime ID
// that was assigned to it in the AddEntity packet.
TargetEntityRuntimeID uint64
// ActionType is the type of the UseItemOnEntity inventory transaction. It is one of the action types
// found in the constants above, and specifies the way the player interacted with the entity.
ActionType uint32
// HotBarSlot is the hot bar slot that the player was holding while clicking the entity. It should be used
// to ensure that the hot bar slot and held item are correctly synchronised with the server.
HotBarSlot int32
// HeldItem is the item that was held to interact with the entity. The server should check if this item
// is actually present in the HotBarSlot.
HeldItem ItemStack
// Position is the position of the player at the time of clicking the entity.
Position mgl32.Vec3
// ClickedPosition is the position that was clicked relative to the entity's base coordinate. It can be
// used to find out exactly where a player clicked the entity.
ClickedPosition mgl32.Vec3
}
const (
ReleaseItemActionRelease = iota
ReleaseItemActionConsume
)
// ReleaseItemTransactionData represents an inventory transaction data object sent when the client releases
// the item it was using, for example when stopping while eating or stopping the charging of a bow.
type ReleaseItemTransactionData struct {
// ActionType is the type of the ReleaseItem inventory transaction. It is one of the action types found
// in the constants above, and specifies the way the item was released.
// As of 1.13, the ActionType is always 0. This field can be ignored, because releasing food (by consuming
// it) or releasing a bow (to shoot an arrow) is essentially the same.
ActionType uint32
// HotBarSlot is the hot bar slot that the player was holding while releasing the item. It should be used
// to ensure that the hot bar slot and held item are correctly synchronised with the server.
HotBarSlot int32
// HeldItem is the item that was released. The server should check if this item is actually present in the
// HotBarSlot.
HeldItem ItemStack
// HeadPosition is the position of the player's head at the time of releasing the item. This is used
// mainly for purposes such as spawning eating particles at that position.
HeadPosition mgl32.Vec3
}
// Marshal ...
func (data *UseItemTransactionData) Marshal(buf *bytes.Buffer) {
_ = WriteVaruint32(buf, data.ActionType)
_ = WriteUBlockPosition(buf, data.BlockPosition)
_ = WriteVarint32(buf, data.BlockFace)
_ = WriteVarint32(buf, data.HotBarSlot)
_ = WriteItem(buf, data.HeldItem)
_ = WriteVec3(buf, data.Position)
_ = WriteVec3(buf, data.ClickedPosition)
_ = WriteVaruint32(buf, data.BlockRuntimeID)
}
// Unmarshal ...
func (data *UseItemTransactionData) Unmarshal(buf *bytes.Buffer) error {
return chainErr(
Varuint32(buf, &data.ActionType),
UBlockPosition(buf, &data.BlockPosition),
Varint32(buf, &data.BlockFace),
Varint32(buf, &data.HotBarSlot),
Item(buf, &data.HeldItem),
Vec3(buf, &data.Position),
Vec3(buf, &data.ClickedPosition),
Varuint32(buf, &data.BlockRuntimeID),
)
}
// Marshal ...
func (data *UseItemOnEntityTransactionData) Marshal(buf *bytes.Buffer) {
_ = WriteVaruint64(buf, data.TargetEntityRuntimeID)
_ = WriteVaruint32(buf, data.ActionType)
_ = WriteVarint32(buf, data.HotBarSlot)
_ = WriteItem(buf, data.HeldItem)
_ = WriteVec3(buf, data.Position)
_ = WriteVec3(buf, data.ClickedPosition)
}
// Unmarshal ...
func (data *UseItemOnEntityTransactionData) Unmarshal(buf *bytes.Buffer) error {
return chainErr(
Varuint64(buf, &data.TargetEntityRuntimeID),
Varuint32(buf, &data.ActionType),
Varint32(buf, &data.HotBarSlot),
Item(buf, &data.HeldItem),
Vec3(buf, &data.Position),
Vec3(buf, &data.ClickedPosition),
)
}
// Marshal ...
func (data *ReleaseItemTransactionData) Marshal(buf *bytes.Buffer) {
_ = WriteVaruint32(buf, data.ActionType)
_ = WriteVarint32(buf, data.HotBarSlot)
_ = WriteItem(buf, data.HeldItem)
_ = WriteVec3(buf, data.HeadPosition)
}
// Unmarshal ...
func (data *ReleaseItemTransactionData) Unmarshal(buf *bytes.Buffer) error {
return chainErr(
Varuint32(buf, &data.ActionType),
Varint32(buf, &data.HotBarSlot),
Item(buf, &data.HeldItem),
Vec3(buf, &data.HeadPosition),
)
}
// Marshal ...
func (*NormalTransactionData) Marshal(*bytes.Buffer) {
// No payload.
}
// Unmarshal ...
func (*NormalTransactionData) Unmarshal(*bytes.Buffer) error {
return nil
}
// Marshal ...
func (*MismatchTransactionData) Marshal(*bytes.Buffer) {
// No payload.
}
// Unmarshal ...
func (*MismatchTransactionData) Unmarshal(*bytes.Buffer) error {
return nil
}