-
-
Notifications
You must be signed in to change notification settings - Fork 89
/
inventory.go
330 lines (302 loc) · 11.9 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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
package protocol
import (
"bytes"
"encoding/binary"
"github.com/go-gl/mathgl/mgl32"
)
const (
InventoryActionSourceContainer = 0
InventoryActionSourceWorld = 2
InventoryActionSourceCreative = 3
InventoryActionSourceTODO = 99999
)
const (
WindowIDInventory = 0
WindowIDOffHand = 119
WindowIDArmour = 120
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
// StackNetworkID is the unique network ID of the new stack. This is always 0 when an InventoryTransaction
// packet is sent by the client. It is also always 0 when the HasNetworkIDs field in the
// InventoryTransaction packet is set to false.
StackNetworkID int32
}
// InvAction reads an inventory action from buffer src.
func InvAction(src *bytes.Buffer, action *InventoryAction, netIDs bool) 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)
}
}
if err := chainErr(
Varuint32(src, &action.InventorySlot),
Item(src, &action.OldItem),
Item(src, &action.NewItem),
); err != nil {
return err
}
if netIDs {
return Varint32(src, &action.StackNetworkID)
}
return nil
}
// WriteInvAction writes an inventory action to buffer dst.
func WriteInvAction(dst *bytes.Buffer, action InventoryAction, netIDs bool) 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)
}
}
if err := chainErr(
WriteVaruint32(dst, action.InventorySlot),
WriteItem(dst, action.OldItem),
WriteItem(dst, action.NewItem),
); err != nil {
return err
}
if netIDs {
return WriteVarint32(dst, action.StackNetworkID)
}
return nil
}
// 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
}
// LegacySetItemSlot represents a slot that was changed during an InventoryTransaction. These slots have to
// have their values set accordingly for actions such as when dropping an item out of the hotbar, where the
// inventory container and the slot that had its item dropped is passed.
type LegacySetItemSlot struct {
ContainerID byte
Slots []byte
}
// WriteSetItemSlot writes a LegacySetItemSlot x to Buffer dst.
func WriteSetItemSlot(dst *bytes.Buffer, x LegacySetItemSlot) error {
dst.WriteByte(x.ContainerID)
if err := WriteVaruint32(dst, uint32(len(x.Slots))); err != nil {
return err
}
for _, slot := range x.Slots {
dst.WriteByte(slot)
}
return nil
}
// SetItemSlot reads a LegacySetItemSlot x from Buffer src.
func SetItemSlot(src *bytes.Buffer, x *LegacySetItemSlot) error {
if err := binary.Read(src, binary.LittleEndian, &x.ContainerID); err != nil {
return wrap(err)
}
var length uint32
if err := Varuint32(src, &length); err != nil {
return err
}
x.Slots = make([]byte, length)
for i := uint32(0); i < length; i++ {
if err := binary.Read(src, binary.LittleEndian, &x.Slots[i]); err != nil {
return wrap(err)
}
}
return nil
}