initial commit

This commit is contained in:
AkmalFairuz
2024-12-31 20:57:21 +07:00
commit 0de5415f05
72 changed files with 20340 additions and 0 deletions

22
legacyver/bitset.go Normal file
View File

@@ -0,0 +1,22 @@
package legacyver
import "github.com/sandertv/gophertunnel/minecraft/protocol"
func fitBitset(b protocol.Bitset, size int) protocol.Bitset {
if b.Len() != size {
ret := protocol.NewBitset(size)
copySize := b.Len()
if copySize > size {
copySize = size
}
for i := 0; i < copySize; i++ {
if b.Load(i) {
ret.Set(i)
}
}
return ret
}
return b
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,340 @@
package legacyver
import (
"bytes"
_ "embed"
"github.com/akmalfairuz/legacy-version/internal/chunk"
"github.com/akmalfairuz/legacy-version/mapping"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/nbt"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
const (
BlockVersionLatest int32 = (1 << 24) | (21 << 16) | (50 << 8) | 29
)
var (
//go:embed block_states_766.nbt
latestBlockStateData []byte
// latestBlockMapping is the BlockMapping used for translating blocks between versions.
latestBlockMapping = mapping.NewBlockMapping(latestBlockStateData)
// LatestNetworkPersistentEncoding is the Encoding used for sending a Chunk over network. It uses NBT, unlike NetworkEncoding.
LatestNetworkPersistentEncoding = chunk.NewNetworkPersistentEncoding(latestBlockMapping, BlockVersionLatest)
// LatestBlockPaletteEncoding is the paletteEncoding used for encoding a palette of block states encoded as NBT.
LatestBlockPaletteEncoding = chunk.NewBlockPaletteEncoding(latestBlockMapping, BlockVersionLatest)
)
type BlockTranslator interface {
// DowngradeBlockPackets downgrades the input block packets to legacy block packets.
DowngradeBlockPackets([]packet.Packet, *minecraft.Conn) (result []packet.Packet)
// UpgradeBlockPackets upgrades the input block packets to the latest block packets.
UpgradeBlockPackets([]packet.Packet, *minecraft.Conn) (result []packet.Packet)
}
type DefaultBlockTranslator struct {
mapping mapping.Block
latest mapping.Block
pse chunk.Encoding
pe chunk.PaletteEncoding
oldFormat bool
}
func NewBlockTranslator(mapping mapping.Block, latestMapping mapping.Block, pse chunk.Encoding, pe chunk.PaletteEncoding, oldFormat bool) *DefaultBlockTranslator {
return &DefaultBlockTranslator{mapping: mapping, latest: latestMapping, pse: pse, pe: pe, oldFormat: oldFormat}
}
func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn *minecraft.Conn) (result []packet.Packet) {
for _, pk := range pks {
switch pk := pk.(type) {
case *packet.LevelChunk:
count := int(pk.SubChunkCount)
if count == protocol.SubChunkRequestModeLimitless || count == protocol.SubChunkRequestModeLimited {
break
}
buf := bytes.NewBuffer(pk.RawPayload)
writeBuf := bytes.NewBuffer(nil)
if !pk.CacheEnabled {
c, err := chunk.NetworkDecode(t.latest.Air(), buf, count, false, world.Overworld.Range(), LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
if err != nil {
//fmt.Println(err)
break
}
c = t.DowngradeChunk(c)
payload, err := chunk.NetworkEncode(t.mapping.Air(), c, t.oldFormat, t.pe)
if err != nil {
//fmt.Println(err)
break
}
writeBuf.Write(payload)
pk.SubChunkCount = uint32(len(c.Sub()))
}
safeBytes := buf.Bytes()
countBorder, err := buf.ReadByte()
if err != nil {
pk.RawPayload = append(writeBuf.Bytes(), safeBytes...)
break
}
borderBytes := make([]byte, countBorder)
if _, err = buf.Read(borderBytes); err != nil {
pk.RawPayload = append(writeBuf.Bytes(), safeBytes...)
break
}
writeBuf.WriteByte(countBorder)
writeBuf.Write(borderBytes)
enc := nbt.NewEncoderWithEncoding(writeBuf, nbt.NetworkLittleEndian)
dec := nbt.NewDecoderWithEncoding(buf, nbt.NetworkLittleEndian)
for {
var decNbt map[string]any
if err = dec.Decode(&decNbt); err != nil {
break
}
t.mapping.DowngradeBlockActorData(decNbt)
if err = enc.Encode(decNbt); err != nil {
break
}
}
pk.RawPayload = append(writeBuf.Bytes(), buf.Bytes()...)
case *packet.SubChunk:
r := world.Overworld.Range()
if t.oldFormat {
r = cube.Range{0, 255}
}
for i, entry := range pk.SubChunkEntries {
if entry.Result == protocol.SubChunkResultSuccess {
buf := bytes.NewBuffer(entry.RawPayload)
writeBuf := bytes.NewBuffer(nil)
if !pk.CacheEnabled && !conn.ClientCacheEnabled() {
ind := byte(i)
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
if err != nil {
//fmt.Println(err)
continue
}
t.DowngradeSubChunk(subChunk)
writeBuf.Write(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind)))
}
enc := nbt.NewEncoderWithEncoding(writeBuf, nbt.NetworkLittleEndian)
dec := nbt.NewDecoderWithEncoding(buf, nbt.NetworkLittleEndian)
for {
var decNbt map[string]any
if err := dec.Decode(&decNbt); err != nil {
break
}
t.mapping.DowngradeBlockActorData(decNbt)
if err := enc.Encode(decNbt); err != nil {
break
}
}
entry.RawPayload = append(writeBuf.Bytes(), buf.Bytes()...)
pk.SubChunkEntries[i] = entry
}
}
case *packet.ClientCacheMissResponse:
r := world.Overworld.Range()
if t.oldFormat {
r = cube.Range{0, 255}
}
for i, blob := range pk.Blobs {
buf := bytes.NewBuffer(blob.Payload)
ind := byte(0)
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
if err != nil {
// Has a possibility to be a biome, ignore then
continue
}
t.DowngradeSubChunk(subChunk)
blob.Payload = append(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind)), buf.Bytes()...)
pk.Blobs[i] = blob
}
case *packet.UpdateSubChunkBlocks:
for i, block := range pk.Blocks {
block.BlockRuntimeID = t.DowngradeBlockRuntimeID(block.BlockRuntimeID)
pk.Blocks[i] = block
}
for i, block := range pk.Extra {
block.BlockRuntimeID = t.DowngradeBlockRuntimeID(block.BlockRuntimeID)
pk.Extra[i] = block
}
case *packet.UpdateBlock:
pk.NewBlockRuntimeID = t.DowngradeBlockRuntimeID(pk.NewBlockRuntimeID)
case *packet.UpdateBlockSynced:
pk.NewBlockRuntimeID = t.DowngradeBlockRuntimeID(pk.NewBlockRuntimeID)
case *packet.InventoryTransaction:
if transactionData, ok := pk.TransactionData.(*protocol.UseItemTransactionData); ok {
transactionData.BlockRuntimeID = t.DowngradeBlockRuntimeID(transactionData.BlockRuntimeID)
pk.TransactionData = transactionData
}
case *packet.LevelEvent:
switch pk.EventType {
case packet.LevelEventParticleLegacyEvent | 20: // terrain
fallthrough
case packet.LevelEventParticlesDestroyBlock:
fallthrough
case packet.LevelEventParticlesDestroyBlockNoSound:
pk.EventData = int32(t.DowngradeBlockRuntimeID(uint32(pk.EventData)))
case packet.LevelEventParticlesCrackBlock:
face := pk.EventData >> 24
rid := t.DowngradeBlockRuntimeID(uint32(pk.EventData & 0xffff))
pk.EventData = int32(rid) | (face << 24)
}
case *packet.LevelSoundEvent:
switch pk.SoundType {
case packet.SoundEventBreak:
fallthrough
case packet.SoundEventPlace:
fallthrough
case packet.SoundEventHit:
fallthrough
case packet.SoundEventLand:
fallthrough
case packet.SoundEventItemUseOn:
pk.ExtraData = int32(t.DowngradeBlockRuntimeID(uint32(pk.ExtraData)))
}
case *packet.AddActor:
if pk.EntityType == "minecraft:falling_block" {
pk.EntityMetadata = t.downgradeEntityMetadata(pk.EntityMetadata)
}
case *packet.SetActorData:
pk.EntityMetadata = t.downgradeEntityMetadata(pk.EntityMetadata)
case *packet.StartGame:
t.latest.Adjust(pk.Blocks)
t.mapping.Adjust(pk.Blocks)
case *packet.ResourcePackStack:
var packs []protocol.StackResourcePack
for _, pack := range pk.TexturePacks {
if pack.UUID == "0fba4063-dba1-4281-9b89-ff9390653530" {
continue
}
packs = append(packs, pack)
}
pk.TexturePacks = packs
}
result = append(result, pk)
}
return result
}
func (t *DefaultBlockTranslator) UpgradeBlockPackets(pks []packet.Packet, conn *minecraft.Conn) (result []packet.Packet) {
for _, pk := range pks {
switch pk := pk.(type) {
case *packet.InventoryTransaction:
if transactionData, ok := pk.TransactionData.(*protocol.UseItemTransactionData); ok {
transactionData.BlockRuntimeID = t.UpgradeBlockRuntimeID(transactionData.BlockRuntimeID)
pk.TransactionData = transactionData
}
case *packet.SetActorData:
pk.EntityMetadata = t.upgradeEntityMetadata(pk.EntityMetadata)
}
result = append(result, pk)
}
return result
}
func (t *DefaultBlockTranslator) DowngradeBlockRuntimeID(input uint32) uint32 {
if t.latest == t.mapping {
return input
}
state, ok := t.latest.RuntimeIDToState(input)
if !ok {
return t.mapping.Air()
}
runtimeID, ok := t.mapping.StateToRuntimeID(state)
if !ok {
return t.mapping.Air()
}
return runtimeID
}
func (t *DefaultBlockTranslator) DowngradeChunk(input *chunk.Chunk) *chunk.Chunk {
if t.latest == t.mapping {
return input
}
start := 0
r := world.Overworld.Range()
if t.oldFormat {
start = 4
r = cube.Range{0, 255}
}
downgraded := chunk.New(t.mapping.Air(), r)
i := 0
// First downgrade the blocks.
for _, sub := range input.Sub()[start : len(input.Sub())-start] {
t.DowngradeSubChunk(sub)
downgraded.Sub()[i] = sub
i += 1
}
i = 0
// Then downgrade the biome ids.
for _, sub := range input.BiomeSub()[start : len(input.BiomeSub())-start] {
// todo
sub.Palette().Replace(func(v uint32) uint32 {
return 0 // at least the client doesn't crash now
})
downgraded.BiomeSub()[i] = sub
i += 1
}
return downgraded
}
func (t *DefaultBlockTranslator) DowngradeSubChunk(input *chunk.SubChunk) {
if t.latest == t.mapping {
return
}
for _, storage := range input.Layers() {
storage.Palette().Replace(t.DowngradeBlockRuntimeID)
}
}
func (t *DefaultBlockTranslator) downgradeEntityMetadata(metadata map[uint32]any) map[uint32]any {
if t.latest == t.mapping {
return metadata
}
if latestRID, ok := metadata[protocol.EntityDataKeyVariant]; ok {
metadata[protocol.EntityDataKeyVariant] = int32(t.DowngradeBlockRuntimeID(uint32(latestRID.(int32))))
}
return metadata
}
func (t *DefaultBlockTranslator) UpgradeBlockRuntimeID(input uint32) uint32 {
if t.latest == t.mapping {
return input
}
state, ok := t.mapping.RuntimeIDToState(input)
if !ok {
return t.latest.Air()
}
runtimeID, ok := t.latest.StateToRuntimeID(state)
if !ok {
return t.latest.Air()
}
return runtimeID
}
func (t *DefaultBlockTranslator) upgradeEntityMetadata(metadata map[uint32]any) map[uint32]any {
if t.latest == t.mapping {
return metadata
}
if latestRID, ok := metadata[protocol.EntityDataKeyVariant]; ok {
metadata[protocol.EntityDataKeyVariant] = int32(t.UpgradeBlockRuntimeID(uint32(latestRID.(int32))))
}
return metadata
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,767 @@
package legacyver
import (
"fmt"
"github.com/akmalfairuz/legacy-version/internal/item"
"github.com/akmalfairuz/legacy-version/mapping"
"github.com/akmalfairuz/legacy-version/packbuilder"
"github.com/df-mc/dragonfly/server/world"
"github.com/samber/lo"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
type ItemTranslator interface {
// DowngradeItemType downgrades the input item type to a legacy item type.
DowngradeItemType(input protocol.ItemType) protocol.ItemType
// DowngradeItemStack downgrades the input item stack to a legacy item stack.
DowngradeItemStack(input protocol.ItemStack) protocol.ItemStack
// DowngradeItemInstance downgrades the input item instance to a legacy item instance.
DowngradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance
// DowngradeItemDescriptor downgrades the input item descriptor to a legacy item descriptor.
DowngradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor
// DowngradeItemDescriptorCount downgrades the input item descriptor (with count) to a legacy item descriptor (with count).
DowngradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount
DowngradeItemPackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet
// UpgradeItemType upgrades the input item type to the latest item type.
UpgradeItemType(input protocol.ItemType) protocol.ItemType
// UpgradeItemStack upgrades the input item stack to the latest item stack.
UpgradeItemStack(input protocol.ItemStack) protocol.ItemStack
// UpgradeItemInstance upgrades the input item instance to the latest item instance.
UpgradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance
// UpgradeItemDescriptor upgrades the input item descriptor to the latest item descriptor.
UpgradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor
// UpgradeItemDescriptorCount upgrades the input item descriptor (with count) to the latest item descriptor (with count).
UpgradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount
UpgradeItemPackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet
// Register registers a custom item entry.
Register(item world.CustomItem, replacement string)
// CustomItems lists all custom items used as substitutes, with the runtime id as the key
CustomItems() map[int32]world.CustomItem
}
type DefaultItemTranslator struct {
mapping mapping.Item
latest mapping.Item
blockMapping mapping.Block
blockMappingLatest mapping.Block
ridToCustomItem map[int32]world.CustomItem
originalToCustom map[int32]int32
customToOriginal map[int32]int32
}
func NewItemTranslator(mapping mapping.Item, latestMapping mapping.Item, blockMapping mapping.Block, blockMappingLatest mapping.Block) *DefaultItemTranslator {
return &DefaultItemTranslator{mapping: mapping, latest: latestMapping, blockMapping: blockMapping, blockMappingLatest: blockMappingLatest,
ridToCustomItem: make(map[int32]world.CustomItem), originalToCustom: make(map[int32]int32), customToOriginal: make(map[int32]int32)}
}
func (t *DefaultItemTranslator) DowngradeItemType(input protocol.ItemType) protocol.ItemType {
if t.latest == t.mapping {
return input
}
if input.NetworkID == t.latest.Air() || input.NetworkID == 0 {
return protocol.ItemType{
NetworkID: t.mapping.Air(),
}
}
networkID := input.NetworkID
metadata := input.MetadataValue
var ok bool
if networkID, ok = t.originalToCustom[input.NetworkID]; !ok {
name, _ := t.latest.ItemRuntimeIDToName(input.NetworkID)
i := item.Downgrade(item.Item{
Name: name,
Metadata: input.MetadataValue,
Version: t.latest.ItemVersion(),
}, t.mapping.ItemVersion())
metadata = i.Metadata
if i.Name == "minecraft:netherstar" {
i.Name = "minecraft:nether_star"
}
networkID, ok = t.mapping.ItemNameToRuntimeID(i.Name)
if !ok {
networkID, _ = t.mapping.ItemNameToRuntimeID("minecraft:info_update")
}
}
return protocol.ItemType{
NetworkID: networkID,
MetadataValue: metadata,
}
}
func (t *DefaultItemTranslator) DowngradeItemStack(input protocol.ItemStack) protocol.ItemStack {
if t.latest == t.mapping {
return input
}
input.ItemType = t.DowngradeItemType(input.ItemType)
blockRuntimeId := uint32(0)
if input.NetworkID != t.mapping.Air() {
name, _ := t.mapping.ItemRuntimeIDToName(input.NetworkID)
if latestBlockState, ok := item.BlockStateFromItemName(name, input.MetadataValue); ok {
var found bool
if blockRuntimeId, found = t.blockMapping.StateToRuntimeID(latestBlockState); !found {
blockRuntimeId = t.blockMapping.Air()
}
}
}
return protocol.ItemStack{
ItemType: input.ItemType,
BlockRuntimeID: int32(blockRuntimeId),
Count: input.Count,
NBTData: input.NBTData,
CanBePlacedOn: input.CanBePlacedOn,
CanBreak: input.CanBreak,
HasNetworkID: input.HasNetworkID,
}
}
func (t *DefaultItemTranslator) DowngradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance {
if t.latest == t.mapping {
return input
}
input.Stack = t.DowngradeItemStack(input.Stack)
return input
}
func (t *DefaultItemTranslator) DowngradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor {
if t.latest == t.mapping {
return input
}
switch descriptor := input.(type) {
case *protocol.InvalidItemDescriptor:
return input
case *protocol.DefaultItemDescriptor:
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: int32(descriptor.NetworkID), MetadataValue: uint32(descriptor.MetadataValue)})
descriptor.NetworkID, descriptor.MetadataValue = int16(itemType.NetworkID), int16(itemType.MetadataValue)
return descriptor
case *protocol.MoLangItemDescriptor:
return input
case *protocol.ItemTagItemDescriptor:
return input
case *protocol.DeferredItemDescriptor:
rid, ok := t.latest.ItemNameToRuntimeID(descriptor.Name)
descriptor.Name = "minecraft:air"
if !ok {
descriptor.MetadataValue = 0
return descriptor
}
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: rid, MetadataValue: uint32(descriptor.MetadataValue)})
descriptor.MetadataValue = int16(itemType.MetadataValue)
if name, ok := t.mapping.ItemRuntimeIDToName(itemType.NetworkID); ok {
descriptor.Name = name
}
return descriptor
case *protocol.ComplexAliasItemDescriptor:
rid, ok := t.latest.ItemNameToRuntimeID(descriptor.Name)
descriptor.Name = "minecraft:air"
if !ok {
return descriptor
}
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: rid})
if name, ok := t.mapping.ItemRuntimeIDToName(itemType.NetworkID); ok {
descriptor.Name = name
}
return descriptor
}
panic("unknown item descriptor")
}
func (t *DefaultItemTranslator) DowngradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount {
if t.latest == t.mapping {
return input
}
input.Descriptor = t.DowngradeItemDescriptor(input.Descriptor)
return input
}
func (t *DefaultItemTranslator) UpgradeItemType(input protocol.ItemType) protocol.ItemType {
if t.latest == t.mapping {
return input
}
if input.NetworkID == t.mapping.Air() || input.NetworkID == 0 {
return protocol.ItemType{
NetworkID: t.latest.Air(),
}
}
networkID := input.NetworkID
metadata := input.MetadataValue
var ok bool
if networkID, ok = t.customToOriginal[input.NetworkID]; !ok {
name, _ := t.mapping.ItemRuntimeIDToName(input.NetworkID)
i := item.Upgrade(item.Item{
Name: name,
Metadata: input.MetadataValue,
Version: t.mapping.ItemVersion(),
}, t.latest.ItemVersion())
networkID, ok = t.latest.ItemNameToRuntimeID(i.Name)
if !ok {
networkID, _ = t.latest.ItemNameToRuntimeID("minecraft:info_update")
}
}
return protocol.ItemType{
NetworkID: networkID,
MetadataValue: metadata,
}
}
func (t *DefaultItemTranslator) UpgradeItemStack(input protocol.ItemStack) protocol.ItemStack {
if t.latest == t.mapping {
return input
}
input.ItemType = t.UpgradeItemType(input.ItemType)
blockRuntimeId := uint32(0)
if input.NetworkID != t.latest.Air() {
name, _ := t.latest.ItemRuntimeIDToName(input.NetworkID)
if latestBlockState, ok := item.BlockStateFromItemName(name, input.MetadataValue); ok {
blockRuntimeId, _ = t.blockMappingLatest.StateToRuntimeID(latestBlockState)
}
}
return protocol.ItemStack{
ItemType: input.ItemType,
BlockRuntimeID: int32(blockRuntimeId),
Count: input.Count,
NBTData: input.NBTData,
CanBePlacedOn: input.CanBePlacedOn,
CanBreak: input.CanBreak,
HasNetworkID: input.HasNetworkID,
}
}
func (t *DefaultItemTranslator) UpgradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance {
if t.latest == t.mapping {
return input
}
input.Stack = t.UpgradeItemStack(input.Stack)
return input
}
func (t *DefaultItemTranslator) UpgradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor {
if t.latest == t.mapping {
return input
}
switch descriptor := input.(type) {
case *protocol.InvalidItemDescriptor:
return input
case *protocol.DefaultItemDescriptor:
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: int32(descriptor.NetworkID), MetadataValue: uint32(descriptor.MetadataValue)})
descriptor.NetworkID, descriptor.MetadataValue = int16(itemType.NetworkID), int16(itemType.MetadataValue)
return descriptor
case *protocol.MoLangItemDescriptor:
return input
case *protocol.ItemTagItemDescriptor:
return input
case *protocol.DeferredItemDescriptor:
rid, ok := t.mapping.ItemNameToRuntimeID(descriptor.Name)
descriptor.Name = "minecraft:air"
if !ok {
descriptor.MetadataValue = 0
return descriptor
}
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: rid, MetadataValue: uint32(descriptor.MetadataValue)})
descriptor.MetadataValue = int16(itemType.MetadataValue)
if name, ok := t.latest.ItemRuntimeIDToName(itemType.NetworkID); ok {
descriptor.Name = name
}
return descriptor
case *protocol.ComplexAliasItemDescriptor:
rid, ok := t.mapping.ItemNameToRuntimeID(descriptor.Name)
descriptor.Name = "minecraft:air"
if !ok {
return descriptor
}
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: rid})
if name, ok := t.latest.ItemRuntimeIDToName(itemType.NetworkID); ok {
descriptor.Name = name
}
return descriptor
}
panic("unknown item descriptor")
}
func (t *DefaultItemTranslator) UpgradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount {
if t.latest == t.mapping {
return input
}
input.Descriptor = t.UpgradeItemDescriptor(input.Descriptor)
return input
}
func (t *DefaultItemTranslator) DowngradeItemPackets(pks []packet.Packet, _ *minecraft.Conn) (result []packet.Packet) {
for _, pk := range pks {
switch pk := pk.(type) {
case *packet.MobEquipment:
pk.NewItem = t.DowngradeItemInstance(pk.NewItem)
case *packet.MobArmourEquipment:
pk.Helmet = t.DowngradeItemInstance(pk.Helmet)
pk.Chestplate = t.DowngradeItemInstance(pk.Chestplate)
pk.Leggings = t.DowngradeItemInstance(pk.Leggings)
pk.Boots = t.DowngradeItemInstance(pk.Boots)
pk.Body = t.DowngradeItemInstance(pk.Body)
case *packet.ActorEvent:
if pk.EventType == packet.ActorEventFeed {
value := pk.EventData
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: value >> 16, MetadataValue: uint32(value & 0xf)})
pk.EventData = (itemType.NetworkID << 16) | int32(itemType.MetadataValue)
}
case *packet.AddItemActor:
pk.Item = t.DowngradeItemInstance(pk.Item)
case *packet.AddPlayer:
pk.HeldItem = t.DowngradeItemInstance(pk.HeldItem)
case *packet.InventorySlot:
pk.NewItem = t.DowngradeItemInstance(pk.NewItem)
case *packet.InventoryContent:
pk.Content = lo.Map(pk.Content, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
return t.DowngradeItemInstance(item)
})
case *packet.ItemStackRequest:
for i, request := range pk.Requests {
for i2, action := range request.Actions {
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
return t.DowngradeItemStack(item)
})
pk.Requests[i].Actions[i2] = act
}
}
}
case *packet.CraftingData:
for i, recipe := range pk.Recipes {
switch recipe := recipe.(type) {
case *protocol.ShapelessRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.DowngradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.ShapedRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.DowngradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.FurnaceRecipe:
recipe.InputType = t.DowngradeItemType(recipe.InputType)
recipe.Output = t.DowngradeItemStack(recipe.Output)
pk.Recipes[i] = recipe
case *protocol.FurnaceDataRecipe:
recipe.InputType = t.DowngradeItemType(recipe.InputType)
recipe.Output = t.DowngradeItemStack(recipe.Output)
pk.Recipes[i] = recipe
case *protocol.ShulkerBoxRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.DowngradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.ShapelessChemistryRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.DowngradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.ShapedChemistryRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.DowngradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.SmithingTransformRecipe:
recipe.Template = t.DowngradeItemDescriptorCount(recipe.Template)
recipe.Base = t.DowngradeItemDescriptorCount(recipe.Base)
recipe.Addition = t.DowngradeItemDescriptorCount(recipe.Addition)
recipe.Result = t.DowngradeItemStack(recipe.Result)
pk.Recipes[i] = recipe
case *protocol.SmithingTrimRecipe:
recipe.Template = t.DowngradeItemDescriptorCount(recipe.Template)
recipe.Base = t.DowngradeItemDescriptorCount(recipe.Base)
recipe.Addition = t.DowngradeItemDescriptorCount(recipe.Addition)
}
}
for i, recipe := range pk.PotionRecipes {
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.InputPotionID, MetadataValue: uint32(recipe.InputPotionMetadata)})
recipe.InputPotionID, recipe.InputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID, MetadataValue: uint32(recipe.ReagentItemMetadata)})
recipe.ReagentItemID, recipe.ReagentItemMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.OutputPotionID, MetadataValue: uint32(recipe.OutputPotionMetadata)})
recipe.OutputPotionID, recipe.OutputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
pk.PotionRecipes[i] = recipe
}
for i, recipe := range pk.PotionContainerChangeRecipes {
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.InputItemID})
recipe.InputItemID = itemType.NetworkID
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID})
recipe.ReagentItemID = itemType.NetworkID
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.OutputItemID})
recipe.OutputItemID = itemType.NetworkID
pk.PotionContainerChangeRecipes[i] = recipe
}
for i, recipe := range pk.MaterialReducers {
recipe.InputItem = t.DowngradeItemType(recipe.InputItem)
for i2, output := range recipe.Outputs {
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: output.NetworkID})
output.NetworkID = itemType.NetworkID
recipe.Outputs[i2] = output
}
pk.MaterialReducers[i] = recipe
}
//case *packet.CraftingEvent:
// pk.Input = lo.Map(pk.Input, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
// return t.DowngradeItemInstance(item)
// })
// pk.Output = lo.Map(pk.Output, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
// return t.DowngradeItemInstance(item)
// })
case *packet.PlayerAuthInput:
for i, action := range pk.ItemStackRequest.Actions {
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
return t.DowngradeItemStack(item)
})
pk.ItemStackRequest.Actions[i] = act
}
}
for i, action := range pk.ItemInteractionData.Actions {
action.OldItem = t.DowngradeItemInstance(action.OldItem)
action.NewItem = t.DowngradeItemInstance(action.NewItem)
pk.ItemInteractionData.Actions[i] = action
}
pk.ItemInteractionData.HeldItem = t.DowngradeItemInstance(pk.ItemInteractionData.HeldItem)
case *packet.CreativeContent:
for i, creativeItem := range pk.Items {
creativeItem.Item = t.DowngradeItemStack(creativeItem.Item)
pk.Items[i] = creativeItem
}
case *packet.InventoryTransaction:
for i, action := range pk.Actions {
action.OldItem = t.DowngradeItemInstance(action.OldItem)
action.NewItem = t.DowngradeItemInstance(action.NewItem)
pk.Actions[i] = action
}
switch transactionData := pk.TransactionData.(type) {
case *protocol.UseItemTransactionData:
transactionData.HeldItem = t.DowngradeItemInstance(transactionData.HeldItem)
for i, action := range transactionData.Actions {
action.OldItem = t.DowngradeItemInstance(action.OldItem)
action.NewItem = t.DowngradeItemInstance(action.NewItem)
transactionData.Actions[i] = action
}
case *protocol.UseItemOnEntityTransactionData:
transactionData.HeldItem = t.DowngradeItemInstance(transactionData.HeldItem)
case *protocol.ReleaseItemTransactionData:
transactionData.HeldItem = t.DowngradeItemInstance(transactionData.HeldItem)
}
case *packet.LevelEvent:
if pk.EventType == packet.LevelEventParticleLegacyEvent|14 { // egg crack
itemType := t.DowngradeItemType(protocol.ItemType{
NetworkID: pk.EventData >> 16,
MetadataValue: uint32(pk.EventData & 0xf),
})
pk.EventData = (itemType.NetworkID << 16) | int32(itemType.MetadataValue)
}
case *packet.StartGame:
for i, entry := range pk.Items {
if !entry.ComponentBased {
itemType := t.DowngradeItemType(protocol.ItemType{
NetworkID: int32(entry.RuntimeID),
MetadataValue: 0,
})
if itemType.NetworkID == t.mapping.Air() {
removeIndex(pk.Items, i)
continue
}
entry.RuntimeID = int16(itemType.NetworkID)
var ok bool
if entry.Name, ok = t.mapping.ItemRuntimeIDToName(itemType.NetworkID); !ok {
panic(itemType)
}
} else {
t.latest.RegisterEntry(entry.Name)
entry.RuntimeID = int16(t.mapping.RegisterEntry(entry.Name))
}
pk.Items[i] = entry
}
for rid, i := range t.CustomItems() {
name, _ := i.EncodeItem()
pk.Items = append(pk.Items, protocol.ItemEntry{
Name: name,
RuntimeID: int16(rid),
ComponentBased: true,
})
}
case *packet.ItemComponent:
for _, i := range t.CustomItems() {
name, _ := i.EncodeItem()
pk.Items = append(pk.Items, protocol.ItemComponentEntry{
Name: name,
Data: packbuilder.Components(i),
})
}
}
result = append(result, pk)
}
return result
}
func (t *DefaultItemTranslator) UpgradeItemPackets(pks []packet.Packet, _ *minecraft.Conn) (result []packet.Packet) {
for _, pk := range pks {
switch pk := pk.(type) {
case *packet.MobEquipment:
pk.NewItem = t.UpgradeItemInstance(pk.NewItem)
case *packet.MobArmourEquipment:
pk.Helmet = t.UpgradeItemInstance(pk.Helmet)
pk.Chestplate = t.UpgradeItemInstance(pk.Chestplate)
pk.Leggings = t.UpgradeItemInstance(pk.Leggings)
pk.Boots = t.UpgradeItemInstance(pk.Boots)
pk.Body = t.UpgradeItemInstance(pk.Body)
case *packet.AddItemActor:
pk.Item = t.UpgradeItemInstance(pk.Item)
case *packet.AddPlayer:
pk.HeldItem = t.UpgradeItemInstance(pk.HeldItem)
case *packet.InventorySlot:
pk.NewItem = t.UpgradeItemInstance(pk.NewItem)
case *packet.InventoryContent:
pk.Content = lo.Map(pk.Content, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
return t.UpgradeItemInstance(item)
})
case *packet.ItemStackRequest:
for i, request := range pk.Requests {
for i2, action := range request.Actions {
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
return t.UpgradeItemStack(item)
})
pk.Requests[i].Actions[i2] = act
}
}
}
case *packet.CraftingData:
for i, recipe := range pk.Recipes {
switch recipe := recipe.(type) {
case *protocol.ShapelessRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.UpgradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.ShapedRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.UpgradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.FurnaceRecipe:
recipe.InputType = t.UpgradeItemType(recipe.InputType)
recipe.Output = t.UpgradeItemStack(recipe.Output)
pk.Recipes[i] = recipe
case *protocol.FurnaceDataRecipe:
recipe.InputType = t.UpgradeItemType(recipe.InputType)
recipe.Output = t.UpgradeItemStack(recipe.Output)
pk.Recipes[i] = recipe
case *protocol.ShulkerBoxRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.UpgradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.ShapelessChemistryRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.UpgradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.ShapedChemistryRecipe:
for i2, input := range recipe.Input {
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
}
for i2, stack := range recipe.Output {
recipe.Output[i2] = t.UpgradeItemStack(stack)
}
pk.Recipes[i] = recipe
case *protocol.SmithingTransformRecipe:
recipe.Template = t.UpgradeItemDescriptorCount(recipe.Template)
recipe.Base = t.UpgradeItemDescriptorCount(recipe.Base)
recipe.Addition = t.UpgradeItemDescriptorCount(recipe.Addition)
recipe.Result = t.UpgradeItemStack(recipe.Result)
pk.Recipes[i] = recipe
case *protocol.SmithingTrimRecipe:
recipe.Template = t.UpgradeItemDescriptorCount(recipe.Template)
recipe.Base = t.UpgradeItemDescriptorCount(recipe.Base)
recipe.Addition = t.UpgradeItemDescriptorCount(recipe.Addition)
}
}
for i, recipe := range pk.PotionRecipes {
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.InputPotionID, MetadataValue: uint32(recipe.InputPotionMetadata)})
recipe.InputPotionID, recipe.InputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID, MetadataValue: uint32(recipe.ReagentItemMetadata)})
recipe.ReagentItemID, recipe.ReagentItemMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.OutputPotionID, MetadataValue: uint32(recipe.OutputPotionMetadata)})
recipe.OutputPotionID, recipe.OutputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
pk.PotionRecipes[i] = recipe
}
for i, recipe := range pk.PotionContainerChangeRecipes {
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.InputItemID})
recipe.InputItemID = itemType.NetworkID
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID})
recipe.ReagentItemID = itemType.NetworkID
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.OutputItemID})
recipe.OutputItemID = itemType.NetworkID
pk.PotionContainerChangeRecipes[i] = recipe
}
for i, recipe := range pk.MaterialReducers {
recipe.InputItem = t.UpgradeItemType(recipe.InputItem)
for i2, output := range recipe.Outputs {
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: output.NetworkID})
output.NetworkID = itemType.NetworkID
recipe.Outputs[i2] = output
}
pk.MaterialReducers[i] = recipe
}
//case *packet.CraftingEvent:
// pk.Input = lo.Map(pk.Input, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
// return t.UpgradeItemInstance(item)
// })
// pk.Output = lo.Map(pk.Output, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
// return t.UpgradeItemInstance(item)
// })
case *packet.PlayerAuthInput:
for i, action := range pk.ItemStackRequest.Actions {
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
return t.UpgradeItemStack(item)
})
pk.ItemStackRequest.Actions[i] = act
}
}
for i, action := range pk.ItemInteractionData.Actions {
action.OldItem = t.UpgradeItemInstance(action.OldItem)
action.NewItem = t.UpgradeItemInstance(action.NewItem)
pk.ItemInteractionData.Actions[i] = action
}
pk.ItemInteractionData.HeldItem = t.UpgradeItemInstance(pk.ItemInteractionData.HeldItem)
case *packet.CreativeContent:
for i, creativeItem := range pk.Items {
creativeItem.Item = t.UpgradeItemStack(creativeItem.Item)
pk.Items[i] = creativeItem
}
case *packet.InventoryTransaction:
for i, action := range pk.Actions {
action.OldItem = t.UpgradeItemInstance(action.OldItem)
action.NewItem = t.UpgradeItemInstance(action.NewItem)
pk.Actions[i] = action
}
switch transactionData := pk.TransactionData.(type) {
case *protocol.UseItemTransactionData:
transactionData.HeldItem = t.UpgradeItemInstance(transactionData.HeldItem)
for i, action := range transactionData.Actions {
action.OldItem = t.UpgradeItemInstance(action.OldItem)
action.NewItem = t.UpgradeItemInstance(action.NewItem)
transactionData.Actions[i] = action
}
case *protocol.UseItemOnEntityTransactionData:
transactionData.HeldItem = t.UpgradeItemInstance(transactionData.HeldItem)
case *protocol.ReleaseItemTransactionData:
transactionData.HeldItem = t.UpgradeItemInstance(transactionData.HeldItem)
}
case *packet.LevelEvent:
if pk.EventType == packet.LevelEventParticleLegacyEvent|14 { // egg crack
itemType := t.UpgradeItemType(protocol.ItemType{
NetworkID: pk.EventData >> 16,
MetadataValue: uint32(pk.EventData & 0xf),
})
pk.EventData = (itemType.NetworkID << 16) | int32(itemType.MetadataValue)
}
case *packet.StartGame:
for i, entry := range pk.Items {
if !entry.ComponentBased {
itemType := t.UpgradeItemType(protocol.ItemType{
NetworkID: int32(entry.RuntimeID),
MetadataValue: 0,
})
entry.RuntimeID = int16(itemType.NetworkID)
var ok bool
if entry.Name, ok = t.latest.ItemRuntimeIDToName(itemType.NetworkID); !ok {
panic(itemType)
}
} else {
t.latest.RegisterEntry(entry.Name)
entry.RuntimeID = int16(t.mapping.RegisterEntry(entry.Name))
}
pk.Items[i] = entry
}
for rid, i := range t.CustomItems() {
name, _ := i.EncodeItem()
pk.Items = append(pk.Items, protocol.ItemEntry{
Name: name,
RuntimeID: int16(rid),
ComponentBased: true,
})
}
case *packet.ItemComponent:
for _, i := range t.CustomItems() {
name, _ := i.EncodeItem()
pk.Items = append(pk.Items, protocol.ItemComponentEntry{
Name: name,
Data: packbuilder.Components(i),
})
}
}
result = append(result, pk)
}
return result
}
func (t *DefaultItemTranslator) Register(item world.CustomItem, replacement string) {
name, _ := item.EncodeItem()
originalRid, ok := t.latest.ItemNameToRuntimeID(replacement)
if !ok {
panic(fmt.Errorf("%v not found in latest items", replacement))
}
if _, ok := t.originalToCustom[originalRid]; ok {
panic(fmt.Errorf("%v is already mapped", replacement))
}
nextRID := t.mapping.RegisterEntry(name)
t.ridToCustomItem[nextRID] = item
t.originalToCustom[originalRid] = nextRID
t.customToOriginal[nextRID] = originalRid
}
func (t *DefaultItemTranslator) CustomItems() map[int32]world.CustomItem {
return t.ridToCustomItem
}
func removeIndex[T any](s []T, index int) []T {
ret := make([]T, 0)
ret = append(ret, s[:index]...)
return append(ret, s[index+1:]...)
}

View File

@@ -0,0 +1,27 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// ItemStackResponse is sent by the server in response to an ItemStackRequest packet from the client. This
// packet is used to either approve or reject ItemStackRequests from the client. If a request is approved, the
// client will simply continue as normal. If rejected, the client will undo the actions so that the inventory
// should be in sync with the server again.
type ItemStackResponse struct {
// Responses is a list of responses to ItemStackRequests sent by the client before. Responses either
// approve or reject a request from the client.
// Vanilla limits the size of this slice to 4096.
Responses []proto.ItemStackResponse
}
// ID ...
func (*ItemStackResponse) ID() uint32 {
return packet.IDItemStackResponse
}
func (pk *ItemStackResponse) Marshal(io protocol.IO) {
protocol.Slice(io, &pk.Responses)
}

View File

@@ -0,0 +1 @@
package legacypacket

View File

@@ -0,0 +1,210 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/go-gl/mathgl/mgl32"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
const PlayerAuthInputBitsetSize = 65
const (
InputFlagAscend = iota
InputFlagDescend
InputFlagNorthJump
InputFlagJumpDown
InputFlagSprintDown
InputFlagChangeHeight
InputFlagJumping
InputFlagAutoJumpingInWater
InputFlagSneaking
InputFlagSneakDown
InputFlagUp
InputFlagDown
InputFlagLeft
InputFlagRight
InputFlagUpLeft
InputFlagUpRight
InputFlagWantUp
InputFlagWantDown
InputFlagWantDownSlow
InputFlagWantUpSlow
InputFlagSprinting
InputFlagAscendBlock
InputFlagDescendBlock
InputFlagSneakToggleDown
InputFlagPersistSneak
InputFlagStartSprinting
InputFlagStopSprinting
InputFlagStartSneaking
InputFlagStopSneaking
InputFlagStartSwimming
InputFlagStopSwimming
InputFlagStartJumping
InputFlagStartGliding
InputFlagStopGliding
InputFlagPerformItemInteraction
InputFlagPerformBlockActions
InputFlagPerformItemStackRequest
InputFlagHandledTeleport
InputFlagEmoting
InputFlagMissedSwing
InputFlagStartCrawling
InputFlagStopCrawling
InputFlagStartFlying
InputFlagStopFlying
InputFlagClientAckServerData
InputFlagClientPredictedVehicle
InputFlagPaddlingLeft
InputFlagPaddlingRight
InputFlagBlockBreakingDelayEnabled
InputFlagHorizontalCollision
InputFlagVerticalCollision
InputFlagDownLeft
InputFlagDownRight
InputFlagStartUsingItem
InputFlagCameraRelativeMovementEnabled
InputFlagRotControlledByMoveDirection
InputFlagStartSpinAttack
InputFlagStopSpinAttack
InputFlagIsHotbarTouchOnly
InputFlagJumpReleasedRaw
InputFlagJumpPressedRaw
InputFlagJumpCurrentRaw
InputFlagSneakReleasedRaw
InputFlagSneakPressedRaw
InputFlagSneakCurrentRaw
)
const (
InputModeMouse = iota + 1
InputModeTouch
InputModeGamePad
InputModeMotionController
)
const (
PlayModeNormal = iota
PlayModeTeaser
PlayModeScreen
PlayModeViewer
PlayModeReality
PlayModePlacement
PlayModeLivingRoom
PlayModeExitLevel
PlayModeExitLevelLivingRoom
PlayModeNumModes
)
const (
InteractionModelTouch = iota
InteractionModelCrosshair
InteractionModelClassic
)
// PlayerAuthInput is sent by the client to allow for server authoritative movement. It is used to synchronise
// the player input with the position server-side.
// The client sends this packet when the ServerAuthoritativeMovementMode field in the StartGame packet is set
// to true, instead of the MovePlayer packet. The client will send this packet once every tick.
type PlayerAuthInput struct {
// Pitch and Yaw hold the rotation that the player reports it has.
Pitch, Yaw float32
// Position holds the position that the player reports it has.
Position mgl32.Vec3
// MoveVector is a Vec2 that specifies the direction in which the player moved, as a combination of X/Z
// values which are created using the WASD/controller stick state.
MoveVector mgl32.Vec2
// HeadYaw is the horizontal rotation of the head that the player reports it has.
HeadYaw float32
// InputData is a combination of bit flags that together specify the way the player moved last tick. It
// is a combination of the flags above.
InputData protocol.Bitset
// InputMode specifies the way that the client inputs data to the screen. It is one of the constants that
// may be found above.
InputMode uint32
// PlayMode specifies the way that the player is playing. The values it holds, which are rather random,
// may be found above.
PlayMode uint32
// InteractionModel is a constant representing the interaction model the player is using. It is one of the
// constants that may be found above.
InteractionModel uint32
// InteractPitch and interactYaw is the rotation the player is looking that they intend to use for
// interactions. This is only different to Pitch and Yaw in cases such as VR or when custom cameras
// being used.
InteractPitch, InteractYaw float32
// Tick is the server tick at which the packet was sent. It is used in relation to
// CorrectPlayerMovePrediction.
Tick uint64
// Delta was the delta between the old and the new position. There isn't any practical use for this field
// as it can be calculated by the server itself.
Delta mgl32.Vec3
// ItemInteractionData is the transaction data if the InputData includes an item interaction.
ItemInteractionData protocol.UseItemTransactionData
// ItemStackRequest is sent by the client to change an item in their inventory.
ItemStackRequest protocol.ItemStackRequest
// BlockActions is a slice of block actions that the client has interacted with.
BlockActions []protocol.PlayerBlockAction
// VehicleRotation is the rotation of the vehicle that the player is in, if any.
VehicleRotation mgl32.Vec2
// ClientPredictedVehicle is the unique ID of the vehicle that the client predicts the player to be in.
ClientPredictedVehicle int64
// AnalogueMoveVector is a Vec2 that specifies the direction in which the player moved, as a combination
// of X/Z values which are created using an analogue input.
AnalogueMoveVector mgl32.Vec2
// CameraOrientation is the vector that represents the camera's forward direction which can be used to
// transform movement to be camera relative.
CameraOrientation mgl32.Vec3
// RawMoveVector is the value of MoveVector before it is affected by input permissions, sneaking/fly
// speeds and isn't normalised for analogue inputs.
RawMoveVector mgl32.Vec2
}
// ID ...
func (pk *PlayerAuthInput) ID() uint32 {
return packet.IDPlayerAuthInput
}
func (pk *PlayerAuthInput) Marshal(io protocol.IO) {
io.Float32(&pk.Pitch)
io.Float32(&pk.Yaw)
io.Vec3(&pk.Position)
io.Vec2(&pk.MoveVector)
io.Float32(&pk.HeadYaw)
if proto.IsProtoGTE(io, proto.ID766) {
io.Bitset(&pk.InputData, PlayerAuthInputBitsetSize)
} else {
io.Bitset(&pk.InputData, 64)
}
io.Varuint32(&pk.InputMode)
io.Varuint32(&pk.PlayMode)
io.Varuint32(&pk.InteractionModel)
io.Float32(&pk.InteractPitch)
io.Float32(&pk.InteractYaw)
io.Varuint64(&pk.Tick)
io.Vec3(&pk.Delta)
if pk.InputData.Load(InputFlagPerformItemInteraction) {
io.PlayerInventoryAction(&pk.ItemInteractionData)
}
if pk.InputData.Load(InputFlagPerformItemStackRequest) {
protocol.Single(io, &pk.ItemStackRequest)
}
if pk.InputData.Load(InputFlagPerformBlockActions) {
protocol.SliceVarint32Length(io, &pk.BlockActions)
}
if pk.InputData.Load(InputFlagClientPredictedVehicle) {
io.Vec2(&pk.VehicleRotation)
io.Varint64(&pk.ClientPredictedVehicle)
}
io.Vec2(&pk.AnalogueMoveVector)
io.Vec3(&pk.CameraOrientation)
if proto.IsProtoGTE(io, proto.ID766) {
io.Vec2(&pk.RawMoveVector)
}
}

View File

@@ -0,0 +1,50 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// ResourcePacksInfo is sent by the server to inform the client on what resource packs the server has. It
// sends a list of the resource packs it has and basic information on them like the version and description.
type ResourcePacksInfo struct {
// TexturePackRequired specifies if the client must accept the texture packs the server has in order to
// join the server. If set to true, the client gets the option to either download the resource packs and
// join, or quit entirely. Behaviour packs never have to be downloaded.
TexturePackRequired bool
// HasAddons specifies if any of the resource packs contain addons in them. If set to true, only clients
// that support addons will be able to download them.
HasAddons bool
// HasScripts specifies if any of the resource packs contain scripts in them. If set to true, only clients
// that support scripts will be able to download them.
HasScripts bool
// WorldTemplateUUID is the UUID of the template that has been used to generate the world. Templates can
// be downloaded from the marketplace or installed via '.mctemplate' files. If the world was not generated
// from a template, this field is empty.
WorldTemplateUUID uuid.UUID
// WorldTemplateVersion is the version of the world template that has been used to generate the world. If
// the world was not generated from a template, this field is empty.
WorldTemplateVersion string
// TexturePacks is a list of texture packs that the client needs to download before joining the server.
// The order of these texture packs is not relevant in this packet. It is however important in the
// ResourcePackStack packet.
TexturePacks []proto.TexturePackInfo
}
// ID ...
func (*ResourcePacksInfo) ID() uint32 {
return packet.IDResourcePacksInfo
}
func (pk *ResourcePacksInfo) Marshal(io protocol.IO) {
io.Bool(&pk.TexturePackRequired)
io.Bool(&pk.HasAddons)
io.Bool(&pk.HasScripts)
if proto.IsProtoGTE(io, proto.ID766) {
io.UUID(&pk.WorldTemplateUUID)
io.String(&pk.WorldTemplateVersion)
}
protocol.SliceUint16Length(io, &pk.TexturePacks)
}

32
legacyver/proto/id.go Normal file
View File

@@ -0,0 +1,32 @@
package proto
import "github.com/sandertv/gophertunnel/minecraft/protocol"
const (
ID766 = 766
ID748 = 748
ID729 = 729
ID712 = 712
ID686 = 686
ID685 = 685
)
func IsProtoGTE(io protocol.IO, proto int32) bool {
return io.(IO).ProtocolID() >= proto
}
func IsProtoLTE(io protocol.IO, proto int32) bool {
return io.(IO).ProtocolID() <= proto
}
func IsProtoLT(io protocol.IO, proto int32) bool {
return io.(IO).ProtocolID() < proto
}
func IsProtoGT(io protocol.IO, proto int32) bool {
return io.(IO).ProtocolID() > proto
}
func FetchProtoID(io protocol.IO) int32 {
return io.(IO).ProtocolID()
}

53
legacyver/proto/io.go Normal file
View File

@@ -0,0 +1,53 @@
package proto
import (
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
type IO interface {
protocol.IO
SetProtocolID(protocolID int32)
ProtocolID() int32
}
type Reader struct {
*protocol.Reader
protocolID int32
}
func NewReader(r *protocol.Reader, protocolID int32) *Reader {
return &Reader{
Reader: r,
protocolID: protocolID,
}
}
func (r *Reader) SetProtocolID(protocolID int32) { r.protocolID = protocolID }
func (r *Reader) ProtocolID() int32 { return r.protocolID }
func (r *Reader) Reads() bool { return true }
type Writer struct {
*protocol.Writer
protocolID int32
}
func NewWriter(w *protocol.Writer, protocolID int32) *Writer {
return &Writer{w, protocolID}
}
func (w *Writer) SetProtocolID(protocolID int32) { w.protocolID = protocolID }
func (w *Writer) ProtocolID() int32 { return w.protocolID }
func (w *Writer) Reads() bool { return false }
func IsReader(r protocol.IO) bool {
_, ok := r.(*Reader)
return ok
}
func IsWriter(w protocol.IO) bool {
_, ok := w.(*Writer)
return ok
}

View File

@@ -0,0 +1,652 @@
package proto
import (
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
const (
FilterCauseServerChatPublic = iota
FilterCauseServerChatWhisper
FilterCauseSignText
FilterCauseAnvilText
FilterCauseBookAndQuillText
FilterCauseCommandBlockText
FilterCauseBlockActorDataText
FilterCauseJoinEventText
FilterCauseLeaveEventText
FilterCauseSlashCommandChat
FilterCauseCartographyText
FilterCauseKickCommand
FilterCauseTitleCommand
FilterCauseSummonCommand
)
// ItemStackRequest represents a single request present in an ItemStackRequest packet sent by the client to
// change an item in an inventory.
// Item stack requests are either approved or rejected by the server using the ItemStackResponse packet.
type ItemStackRequest struct {
// RequestID is a unique ID for the request. This ID is used by the server to send a response for this
// specific request in the ItemStackResponse packet.
RequestID int32
// Actions is a list of actions performed by the client. The actual type of the actions depends on which
// ID was present, and is one of the concrete types below.
Actions []protocol.StackRequestAction
// FilterStrings is a list of filter strings involved in the request. This is typically filled with one string
// when an anvil or cartography is used.
FilterStrings []string
// FilterCause represents the cause of any potential filtering. This is one of the constants above.
FilterCause int32
}
// Marshal encodes/decodes an ItemStackRequest.
func (x *ItemStackRequest) Marshal(r protocol.IO) {
r.Varint32(&x.RequestID)
protocol.FuncSlice(r, &x.Actions, r.StackRequestAction)
protocol.FuncSlice(r, &x.FilterStrings, r.String)
r.Int32(&x.FilterCause)
}
// lookupStackRequestActionType looks up the ID of a StackRequestAction.
func lookupStackRequestActionType(x StackRequestAction, id *uint8) bool {
switch x.(type) {
case *TakeStackRequestAction:
*id = StackRequestActionTake
case *PlaceStackRequestAction:
*id = StackRequestActionPlace
case *SwapStackRequestAction:
*id = StackRequestActionSwap
case *DropStackRequestAction:
*id = StackRequestActionDrop
case *DestroyStackRequestAction:
*id = StackRequestActionDestroy
case *ConsumeStackRequestAction:
*id = StackRequestActionConsume
case *CreateStackRequestAction:
*id = StackRequestActionCreate
case *LabTableCombineStackRequestAction:
*id = StackRequestActionLabTableCombine
case *BeaconPaymentStackRequestAction:
*id = StackRequestActionBeaconPayment
case *MineBlockStackRequestAction:
*id = StackRequestActionMineBlock
case *CraftRecipeStackRequestAction:
*id = StackRequestActionCraftRecipe
case *AutoCraftRecipeStackRequestAction:
*id = StackRequestActionCraftRecipeAuto
case *CraftCreativeStackRequestAction:
*id = StackRequestActionCraftCreative
case *CraftRecipeOptionalStackRequestAction:
*id = StackRequestActionCraftRecipeOptional
case *CraftGrindstoneRecipeStackRequestAction:
*id = StackRequestActionCraftGrindstone
case *CraftLoomRecipeStackRequestAction:
*id = StackRequestActionCraftLoom
case *CraftNonImplementedStackRequestAction:
*id = StackRequestActionCraftNonImplementedDeprecated
case *CraftResultsDeprecatedStackRequestAction:
*id = StackRequestActionCraftResultsDeprecated
default:
return false
}
return true
}
// lookupStackRequestAction looks up the StackRequestAction matching an ID.
func lookupStackRequestAction(id uint8, x *StackRequestAction) bool {
switch id {
case StackRequestActionTake:
*x = &TakeStackRequestAction{}
case StackRequestActionPlace:
*x = &PlaceStackRequestAction{}
case StackRequestActionSwap:
*x = &SwapStackRequestAction{}
case StackRequestActionDrop:
*x = &DropStackRequestAction{}
case StackRequestActionDestroy:
*x = &DestroyStackRequestAction{}
case StackRequestActionConsume:
*x = &ConsumeStackRequestAction{}
case StackRequestActionCreate:
*x = &CreateStackRequestAction{}
case StackRequestActionPlaceInContainer:
*x = &PlaceInContainerStackRequestAction{}
case StackRequestActionTakeOutContainer:
*x = &TakeOutContainerStackRequestAction{}
case StackRequestActionLabTableCombine:
*x = &LabTableCombineStackRequestAction{}
case StackRequestActionBeaconPayment:
*x = &BeaconPaymentStackRequestAction{}
case StackRequestActionMineBlock:
*x = &MineBlockStackRequestAction{}
case StackRequestActionCraftRecipe:
*x = &CraftRecipeStackRequestAction{}
case StackRequestActionCraftRecipeAuto:
*x = &AutoCraftRecipeStackRequestAction{}
case StackRequestActionCraftCreative:
*x = &CraftCreativeStackRequestAction{}
case StackRequestActionCraftRecipeOptional:
*x = &CraftRecipeOptionalStackRequestAction{}
case StackRequestActionCraftGrindstone:
*x = &CraftGrindstoneRecipeStackRequestAction{}
case StackRequestActionCraftLoom:
*x = &CraftLoomRecipeStackRequestAction{}
case StackRequestActionCraftNonImplementedDeprecated:
*x = &CraftNonImplementedStackRequestAction{}
case StackRequestActionCraftResultsDeprecated:
*x = &CraftResultsDeprecatedStackRequestAction{}
default:
return false
}
return true
}
const (
ItemStackResponseStatusOK = iota
ItemStackResponseStatusError
ItemStackResponseStatusInvalidRequestActionType
ItemStackResponseStatusActionRequestNotAllowed
ItemStackResponseStatusScreenHandlerEndRequestFailed
ItemStackResponseStatusItemRequestActionHandlerCommitFailed
ItemStackResponseStatusInvalidRequestCraftActionType
ItemStackResponseStatusInvalidCraftRequest
ItemStackResponseStatusInvalidCraftRequestScreen
ItemStackResponseStatusInvalidCraftResult
ItemStackResponseStatusInvalidCraftResultIndex
ItemStackResponseStatusInvalidCraftResultItem
ItemStackResponseStatusInvalidItemNetId
ItemStackResponseStatusMissingCreatedOutputContainer
ItemStackResponseStatusFailedToSetCreatedItemOutputSlot
ItemStackResponseStatusRequestAlreadyInProgress
ItemStackResponseStatusFailedToInitSparseContainer
ItemStackResponseStatusResultTransferFailed
ItemStackResponseStatusExpectedItemSlotNotFullyConsumed
ItemStackResponseStatusExpectedAnywhereItemNotFullyConsumed
ItemStackResponseStatusItemAlreadyConsumedFromSlot
ItemStackResponseStatusConsumedTooMuchFromSlot
ItemStackResponseStatusMismatchSlotExpectedConsumedItem
ItemStackResponseStatusMismatchSlotExpectedConsumedItemNetIdVariant
ItemStackResponseStatusFailedToMatchExpectedSlotConsumedItem
ItemStackResponseStatusFailedToMatchExpectedAllowedAnywhereConsumedItem
ItemStackResponseStatusConsumedItemOutOfAllowedSlotRange
ItemStackResponseStatusConsumedItemNotAllowed
ItemStackResponseStatusPlayerNotInCreativeMode
ItemStackResponseStatusInvalidExperimentalRecipeRequest
ItemStackResponseStatusFailedToCraftCreative
ItemStackResponseStatusFailedToGetLevelRecipe
ItemStackResponseStatusFailedToFindRecipeByNetId
ItemStackResponseStatusMismatchedCraftingSize
ItemStackResponseStatusMissingInputSparseContainer
ItemStackResponseStatusMismatchedRecipeForInputGridItems
ItemStackResponseStatusEmptyCraftResults
ItemStackResponseStatusFailedToEnchant
ItemStackResponseStatusMissingInputItem
ItemStackResponseStatusInsufficientPlayerLevelToEnchant
ItemStackResponseStatusMissingMaterialItem
ItemStackResponseStatusMissingActor
ItemStackResponseStatusUnknownPrimaryEffect
ItemStackResponseStatusPrimaryEffectOutOfRange
ItemStackResponseStatusPrimaryEffectUnavailable
ItemStackResponseStatusSecondaryEffectOutOfRange
ItemStackResponseStatusSecondaryEffectUnavailable
ItemStackResponseStatusDstContainerEqualToCreatedOutputContainer
ItemStackResponseStatusDstContainerAndSlotEqualToSrcContainerAndSlot
ItemStackResponseStatusFailedToValidateSrcSlot
ItemStackResponseStatusFailedToValidateDstSlot
ItemStackResponseStatusInvalidAdjustedAmount
ItemStackResponseStatusInvalidItemSetType
ItemStackResponseStatusInvalidTransferAmount
ItemStackResponseStatusCannotSwapItem
ItemStackResponseStatusCannotPlaceItem
ItemStackResponseStatusUnhandledItemSetType
ItemStackResponseStatusInvalidRemovedAmount
ItemStackResponseStatusInvalidRegion
ItemStackResponseStatusCannotDropItem
ItemStackResponseStatusCannotDestroyItem
ItemStackResponseStatusInvalidSourceContainer
ItemStackResponseStatusItemNotConsumed
ItemStackResponseStatusInvalidNumCrafts
ItemStackResponseStatusInvalidCraftResultStackSize
ItemStackResponseStatusCannotRemoveItem
ItemStackResponseStatusCannotConsumeItem
ItemStackResponseStatusScreenStackError
)
// ItemStackResponse is a response to an individual ItemStackRequest.
type ItemStackResponse struct {
// Status specifies if the request with the RequestID below was successful. If this is the case, the
// ContainerInfo below will have information on what slots ended up changing. If not, the container info
// will be empty.
// A non-0 status means an error occurred and will result in the action being reverted.
Status uint8
// RequestID is the unique ID of the request that this response is in reaction to. If rejected, the client
// will undo the actions from the request with this ID.
RequestID int32
// ContainerInfo holds information on the containers that had their contents changed as a result of the
// request.
ContainerInfo []StackResponseContainerInfo
}
func (x *ItemStackResponse) ToLatest() protocol.ItemStackResponse {
ret := protocol.ItemStackResponse{
Status: x.Status,
RequestID: x.RequestID,
ContainerInfo: make([]protocol.StackResponseContainerInfo, len(x.ContainerInfo)),
}
for i, v := range x.ContainerInfo {
ret.ContainerInfo[i] = protocol.StackResponseContainerInfo{
Container: v.Container,
SlotInfo: make([]protocol.StackResponseSlotInfo, len(v.SlotInfo)),
}
for j, w := range v.SlotInfo {
ret.ContainerInfo[i].SlotInfo[j] = protocol.StackResponseSlotInfo{
Slot: w.Slot,
HotbarSlot: w.HotbarSlot,
Count: w.Count,
StackNetworkID: w.StackNetworkID,
CustomName: w.CustomName,
FilteredCustomName: w.FilteredCustomName,
DurabilityCorrection: w.DurabilityCorrection,
}
}
}
return ret
}
// Marshal encodes/decodes an ItemStackResponse.
func (x *ItemStackResponse) Marshal(r protocol.IO) {
r.Uint8(&x.Status)
r.Varint32(&x.RequestID)
if x.Status == ItemStackResponseStatusOK {
protocol.Slice(r, &x.ContainerInfo)
}
}
// StackResponseContainerInfo holds information on what slots in a container have what item stack in them.
type StackResponseContainerInfo struct {
// Container is the FullContainerName that describes the container that the slots that follow are in. For
// the main inventory, the ContainerID seems to be 0x1b. Fur the cursor, this value seems to be 0x3a. For
// the crafting grid, this value seems to be 0x0d.
Container protocol.FullContainerName
// SlotInfo holds information on what item stack should be present in specific slots in the container.
SlotInfo []StackResponseSlotInfo
}
// Marshal encodes/decodes a StackResponseContainerInfo.
func (x *StackResponseContainerInfo) Marshal(r protocol.IO) {
protocol.Single(r, &x.Container)
protocol.Slice(r, &x.SlotInfo)
}
// StackResponseSlotInfo holds information on what item stack should be present in a specific slot.
type StackResponseSlotInfo struct {
// Slot and HotbarSlot seem to be the same value every time: The slot that was actually changed. I'm not
// sure if these slots ever differ.
Slot, HotbarSlot byte
// Count is the total count of the item stack. This count will be shown client-side after the response is
// sent to the client.
Count byte
// StackNetworkID is the network ID of the new stack at a specific slot.
StackNetworkID int32
// CustomName is the custom name of the item stack. It is used in relation to text filtering.
CustomName string
// FilteredCustomName is a filtered version of CustomName with all the profanity removed. The client will
// use this over CustomName if this field is not empty and they have the "Filter Profanity" setting enabled.
FilteredCustomName string
// DurabilityCorrection is the current durability of the item stack. This durability will be shown
// client-side after the response is sent to the client.
DurabilityCorrection int32
}
// Marshal encodes/decodes a StackResponseSlotInfo.
func (x *StackResponseSlotInfo) Marshal(r protocol.IO) {
r.Uint8(&x.Slot)
r.Uint8(&x.HotbarSlot)
r.Uint8(&x.Count)
r.Varint32(&x.StackNetworkID)
if x.Slot != x.HotbarSlot {
r.InvalidValue(x.HotbarSlot, "hotbar slot", "hot bar slot must be equal to normal slot")
}
r.String(&x.CustomName)
if IsProtoGTE(r, ID766) {
r.String(&x.FilteredCustomName)
}
r.Varint32(&x.DurabilityCorrection)
}
// StackRequestAction represents a single action related to the inventory present in an ItemStackRequest.
// The action is one of the concrete types below, each of which are indicative of a different action by the
// client, such as moving an item around the inventory or placing a block. It is an alias of Marshaler.
type StackRequestAction interface {
protocol.Marshaler
}
const (
StackRequestActionTake = iota
StackRequestActionPlace
StackRequestActionSwap
StackRequestActionDrop
StackRequestActionDestroy
StackRequestActionConsume
StackRequestActionCreate
StackRequestActionPlaceInContainer
StackRequestActionTakeOutContainer
StackRequestActionLabTableCombine
StackRequestActionBeaconPayment
StackRequestActionMineBlock
StackRequestActionCraftRecipe
StackRequestActionCraftRecipeAuto
StackRequestActionCraftCreative
StackRequestActionCraftRecipeOptional
StackRequestActionCraftGrindstone
StackRequestActionCraftLoom
StackRequestActionCraftNonImplementedDeprecated
StackRequestActionCraftResultsDeprecated
)
// transferStackRequestAction is the structure shared by StackRequestActions that transfer items from one
// slot into another.
type transferStackRequestAction struct {
// Count is the count of the item in the source slot that was taken towards the destination slot.
Count byte
// Source and Destination point to the source slot from which Count of the item stack were taken and the
// destination slot to which this item was moved.
Source, Destination StackRequestSlotInfo
}
// Marshal ...
func (a *transferStackRequestAction) Marshal(r protocol.IO) {
r.Uint8(&a.Count)
StackReqSlotInfo(r, &a.Source)
StackReqSlotInfo(r, &a.Destination)
}
// TakeStackRequestAction is sent by the client to the server to take x amount of items from one slot in a
// container to the cursor.
type TakeStackRequestAction struct {
transferStackRequestAction
}
// PlaceStackRequestAction is sent by the client to the server to place x amount of items from one slot into
// another slot, such as when shift clicking an item in the inventory to move it around or when moving an item
// in the cursor into a slot.
type PlaceStackRequestAction struct {
transferStackRequestAction
}
// SwapStackRequestAction is sent by the client to swap the item in its cursor with an item present in another
// container. The two item stacks swap places.
type SwapStackRequestAction struct {
// Source and Destination point to the source slot from which Count of the item stack were taken and the
// destination slot to which this item was moved.
Source, Destination StackRequestSlotInfo
}
// Marshal ...
func (a *SwapStackRequestAction) Marshal(r protocol.IO) {
StackReqSlotInfo(r, &a.Source)
StackReqSlotInfo(r, &a.Destination)
}
// DropStackRequestAction is sent by the client when it drops an item out of the inventory when it has its
// inventory opened. This action is not sent when a player drops an item out of the hotbar using the Q button
// (or the equivalent on mobile). The InventoryTransaction packet is still used for that action, regardless of
// whether the item stack network IDs are used or not.
type DropStackRequestAction struct {
// Count is the count of the item in the source slot that was taken towards the destination slot.
Count byte
// Source is the source slot from which items were dropped to the ground.
Source StackRequestSlotInfo
// Randomly seems to be set to false in most cases. I'm not entirely sure what this does, but this is what
// vanilla calls this field.
Randomly bool
}
// Marshal ...
func (a *DropStackRequestAction) Marshal(r protocol.IO) {
r.Uint8(&a.Count)
StackReqSlotInfo(r, &a.Source)
r.Bool(&a.Randomly)
}
// DestroyStackRequestAction is sent by the client when it destroys an item in creative mode by moving it
// back into the creative inventory.
type DestroyStackRequestAction struct {
// Count is the count of the item in the source slot that was destroyed.
Count byte
// Source is the source slot from which items came that were destroyed by moving them into the creative
// inventory.
Source StackRequestSlotInfo
}
// Marshal ...
func (a *DestroyStackRequestAction) Marshal(r protocol.IO) {
r.Uint8(&a.Count)
StackReqSlotInfo(r, &a.Source)
}
// ConsumeStackRequestAction is sent by the client when it uses an item to craft another item. The original
// item is 'consumed'.
type ConsumeStackRequestAction struct {
DestroyStackRequestAction
}
// CreateStackRequestAction is sent by the client when an item is created through being used as part of a
// recipe. For example, when milk is used to craft a cake, the buckets are leftover. The buckets are moved to
// the slot sent by the client here.
// Note that before this is sent, an action for consuming all items in the crafting table/grid is sent. Items
// that are not fully consumed when used for a recipe should not be destroyed there, but instead, should be
// turned into their respective resulting items.
type CreateStackRequestAction struct {
// ResultsSlot is the slot in the inventory in which the results of the crafting ingredients are to be
// placed.
ResultsSlot byte
}
// Marshal ...
func (a *CreateStackRequestAction) Marshal(r protocol.IO) {
r.Uint8(&a.ResultsSlot)
}
// PlaceInContainerStackRequestAction currently has no known purpose.
type PlaceInContainerStackRequestAction struct {
transferStackRequestAction
}
// TakeOutContainerStackRequestAction currently has no known purpose.
type TakeOutContainerStackRequestAction struct {
transferStackRequestAction
}
// LabTableCombineStackRequestAction is sent by the client when it uses a lab table to combine item stacks.
type LabTableCombineStackRequestAction struct{}
// Marshal ...
func (a *LabTableCombineStackRequestAction) Marshal(protocol.IO) {}
// BeaconPaymentStackRequestAction is sent by the client when it submits an item to enable effects from a
// beacon. These items will have been moved into the beacon item slot in advance.
type BeaconPaymentStackRequestAction struct {
// PrimaryEffect and SecondaryEffect are the effects that were selected from the beacon.
PrimaryEffect, SecondaryEffect int32
}
// Marshal ...
func (a *BeaconPaymentStackRequestAction) Marshal(r protocol.IO) {
r.Varint32(&a.PrimaryEffect)
r.Varint32(&a.SecondaryEffect)
}
// MineBlockStackRequestAction is sent by the client when it breaks a block.
type MineBlockStackRequestAction struct {
// HotbarSlot is the slot held by the player while mining a block.
HotbarSlot int32
// PredictedDurability is the durability of the item that the client assumes to be present at the time.
PredictedDurability int32
// StackNetworkID is the unique stack ID that the client assumes to be present at the time. The server
// must check if these IDs match. If they do not match, servers should reject the stack request that the
// action holding this info was in.
StackNetworkID int32
}
// Marshal ...
func (a *MineBlockStackRequestAction) Marshal(r protocol.IO) {
r.Varint32(&a.HotbarSlot)
r.Varint32(&a.PredictedDurability)
r.Varint32(&a.StackNetworkID)
}
// CraftRecipeStackRequestAction is sent by the client the moment it begins crafting an item. This is the
// first action sent, before the Consume and Create item stack request actions.
// This action is also sent when an item is enchanted. Enchanting should be treated mostly the same way as
// crafting, where the old item is consumed.
type CraftRecipeStackRequestAction struct {
// RecipeNetworkID is the network ID of the recipe that is about to be crafted. This network ID matches
// one of the recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
// of 1.16.
RecipeNetworkID uint32
// NumberOfCrafts is how many times the recipe was crafted. This field appears to be boilerplate and
// has no effect.
NumberOfCrafts byte
}
// Marshal ...
func (a *CraftRecipeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&a.RecipeNetworkID)
r.Uint8(&a.NumberOfCrafts)
}
// AutoCraftRecipeStackRequestAction is sent by the client similarly to the CraftRecipeStackRequestAction. The
// only difference is that the recipe is automatically created and crafted by shift clicking the recipe book.
type AutoCraftRecipeStackRequestAction struct {
// RecipeNetworkID is the network ID of the recipe that is about to be crafted. This network ID matches
// one of the recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
// of 1.16.
RecipeNetworkID uint32
// NumberOfCrafts is how many times the recipe was crafted. This field is just a duplicate of TimesCrafted.
NumberOfCrafts byte
// TimesCrafted is how many times the recipe was crafted.
TimesCrafted byte
// Ingredients is a slice of ItemDescriptorCount that contains the ingredients that were used to craft the recipe.
// It is not exactly clear what this is used for, but it is sent by the vanilla client.
Ingredients []protocol.ItemDescriptorCount
}
// Marshal ...
func (a *AutoCraftRecipeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&a.RecipeNetworkID)
r.Uint8(&a.NumberOfCrafts)
r.Uint8(&a.TimesCrafted)
protocol.FuncSlice(r, &a.Ingredients, r.ItemDescriptorCount)
}
// CraftCreativeStackRequestAction is sent by the client when it takes an item out fo the creative inventory.
// The item is thus not really crafted, but instantly created.
type CraftCreativeStackRequestAction struct {
// CreativeItemNetworkID is the network ID of the creative item that is being created. This is one of the
// creative item network IDs sent in the CreativeContent packet.
CreativeItemNetworkID uint32
// NumberOfCrafts is how many times the recipe was crafted. This field appears to be boilerplate and
// has no effect.
NumberOfCrafts byte
}
// Marshal ...
func (a *CraftCreativeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&a.CreativeItemNetworkID)
r.Uint8(&a.NumberOfCrafts)
}
// CraftRecipeOptionalStackRequestAction is sent when using an anvil. When this action is sent, the
// FilterStrings field in the respective stack request is non-empty and contains the name of the item created
// using the anvil or cartography table.
type CraftRecipeOptionalStackRequestAction struct {
// RecipeNetworkID is the network ID of the multi-recipe that is about to be crafted. This network ID matches
// one of the multi-recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
// of 1.16.
RecipeNetworkID uint32
// FilterStringIndex is the index of a filter string sent in a ItemStackRequest.
FilterStringIndex int32
}
// Marshal ...
func (c *CraftRecipeOptionalStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&c.RecipeNetworkID)
r.Int32(&c.FilterStringIndex)
}
// CraftGrindstoneRecipeStackRequestAction is sent when a grindstone recipe is crafted. It contains the RecipeNetworkID
// to identify the recipe crafted, and the cost for crafting the recipe.
type CraftGrindstoneRecipeStackRequestAction struct {
// RecipeNetworkID is the network ID of the recipe that is about to be crafted. This network ID matches
// one of the recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
// of 1.16.
RecipeNetworkID uint32
// NumberOfCrafts is how many times the recipe was crafted. This field appears to be boilerplate and
// has no effect.
NumberOfCrafts byte
// Cost is the cost of the recipe that was crafted.
Cost int32
}
// Marshal ...
func (c *CraftGrindstoneRecipeStackRequestAction) Marshal(r protocol.IO) {
r.Varuint32(&c.RecipeNetworkID)
r.Uint8(&c.NumberOfCrafts)
r.Varint32(&c.Cost)
}
// CraftLoomRecipeStackRequestAction is sent when a loom recipe is crafted. It simply contains the
// pattern identifier to figure out what pattern is meant to be applied to the item.
type CraftLoomRecipeStackRequestAction struct {
// Pattern is the pattern identifier for the loom recipe.
Pattern string
// TimesCrafted is how many times the recipe was crafted.
TimesCrafted byte
}
// Marshal ...
func (c *CraftLoomRecipeStackRequestAction) Marshal(r protocol.IO) {
r.String(&c.Pattern)
r.Uint8(&c.TimesCrafted)
}
// CraftNonImplementedStackRequestAction is an action sent for inventory actions that aren't yet implemented
// in the new system. These include, for example, anvils.
type CraftNonImplementedStackRequestAction struct{}
// Marshal ...
func (*CraftNonImplementedStackRequestAction) Marshal(protocol.IO) {}
// CraftResultsDeprecatedStackRequestAction is an additional, deprecated packet sent by the client after
// crafting. It holds the final results and the amount of times the recipe was crafted. It shouldn't be used.
// This action is also sent when an item is enchanted. Enchanting should be treated mostly the same way as
// crafting, where the old item is consumed.
type CraftResultsDeprecatedStackRequestAction struct {
ResultItems []protocol.ItemStack
TimesCrafted byte
}
// Marshal ...
func (a *CraftResultsDeprecatedStackRequestAction) Marshal(r protocol.IO) {
protocol.FuncSlice(r, &a.ResultItems, r.Item)
r.Uint8(&a.TimesCrafted)
}
// StackRequestSlotInfo holds information on a specific slot client-side.
type StackRequestSlotInfo struct {
// Container is the FullContainerName that describes the container that the slot is in.
Container protocol.FullContainerName
// Slot is the index of the slot within the container with the ContainerID above.
Slot byte
// StackNetworkID is the unique stack ID that the client assumes to be present in this slot. The server
// must check if these IDs match. If they do not match, servers should reject the stack request that the
// action holding this info was in.
StackNetworkID int32
}
// StackReqSlotInfo reads/writes a StackRequestSlotInfo x using IO r.
func StackReqSlotInfo(r protocol.IO, x *StackRequestSlotInfo) {
protocol.Single(r, &x.Container)
r.Uint8(&x.Slot)
r.Varint32(&x.StackNetworkID)
}

View File

@@ -0,0 +1,79 @@
package proto
import (
"github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft/protocol"
)
// TexturePackInfo represents a texture pack's info sent over network. It holds information about the
// texture pack such as its name, description and version.
type TexturePackInfo struct {
// UUID is the UUID of the texture pack. Each texture pack downloaded must have a different UUID in
// order for the client to be able to handle them properly.
UUID uuid.UUID
// Version is the version of the texture pack. The client will cache texture packs sent by the server as
// long as they carry the same version. Sending a texture pack with a different version than previously
// will force the client to re-download it.
Version string
// Size is the total size in bytes that the texture pack occupies. This is the size of the compressed
// archive (zip) of the texture pack.
Size uint64
// ContentKey is the key used to decrypt the behaviour pack if it is encrypted. This is generally the case
// for marketplace texture packs.
ContentKey string
// SubPackName ...
SubPackName string
// ContentIdentity is another UUID for the resource pack, and is generally set for marketplace texture
// packs. It is also required for client-side validations when the resource pack is encrypted.
ContentIdentity string
// HasScripts specifies if the texture packs has any scripts in it. A client will only download the
// behaviour pack if it supports scripts, which, up to 1.11, only includes Windows 10.
HasScripts bool
// AddonPack specifies if the texture pack is from an addon.
AddonPack bool
// RTXEnabled specifies if the texture pack uses the raytracing technology introduced in 1.16.200.
RTXEnabled bool
// DownloadURL is a URL that the client can use to download the pack instead of the server sending it in
// chunks, which it will continue to do if this field is left empty.
DownloadURL string
}
func (x *TexturePackInfo) ToLatest() protocol.TexturePackInfo {
return protocol.TexturePackInfo{
UUID: x.UUID,
Version: x.Version,
Size: x.Size,
ContentKey: x.ContentKey,
SubPackName: x.SubPackName,
ContentIdentity: x.ContentIdentity,
HasScripts: x.HasScripts,
AddonPack: x.AddonPack,
RTXEnabled: x.RTXEnabled,
DownloadURL: x.DownloadURL,
}
}
// Marshal encodes/decodes a TexturePackInfo.
func (x *TexturePackInfo) Marshal(r protocol.IO) {
if IsProtoGTE(r, ID766) {
r.UUID(&x.UUID)
} else {
if IsReader(r) {
uuidStr := ""
r.String(&uuidStr)
x.UUID = uuid.MustParse(uuidStr)
} else {
uuidStr := x.UUID.String()
r.String(&uuidStr)
}
}
r.String(&x.Version)
r.Uint64(&x.Size)
r.String(&x.ContentKey)
r.String(&x.SubPackName)
r.String(&x.ContentIdentity)
r.Bool(&x.HasScripts)
r.Bool(&x.AddonPack)
r.Bool(&x.RTXEnabled)
r.String(&x.DownloadURL)
}

5
legacyver/proto/type.go Normal file
View File

@@ -0,0 +1,5 @@
package proto
type legacyType struct {
ProtocolID int32
}

215
legacyver/protocol.go Normal file
View File

@@ -0,0 +1,215 @@
package legacyver
import (
"github.com/akmalfairuz/legacy-version/legacyver/legacypacket"
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
var (
packetPoolClient packet.Pool
packetPoolServer packet.Pool
)
func init() {
packetPoolClient = packet.NewClientPool()
packetPoolServer = packet.NewServerPool()
packetPoolServer[packet.IDItemStackResponse] = func() packet.Packet { return &legacypacket.ItemStackResponse{} }
packetPoolServer[packet.IDResourcePacksInfo] = func() packet.Packet { return &legacypacket.ResourcePacksInfo{} }
packetPoolClient[packet.IDPlayerAuthInput] = func() packet.Packet { return &legacypacket.PlayerAuthInput{} }
}
type Protocol struct {
ver string
id int32
blockTranslator BlockTranslator
itemTranslator ItemTranslator
}
func (p *Protocol) Ver() string {
return p.ver
}
func (p *Protocol) ID() int32 {
return p.id
}
func (p *Protocol) Packets(listener bool) packet.Pool {
if listener {
return packetPoolClient
}
return packetPoolServer
}
func (p *Protocol) NewReader(r minecraft.ByteReader, shieldID int32, enableLimits bool) protocol.IO {
return proto.NewReader(protocol.NewReader(r, shieldID, enableLimits), p.id)
}
func (p *Protocol) NewWriter(w minecraft.ByteWriter, shieldID int32) protocol.IO {
return proto.NewWriter(protocol.NewWriter(w, shieldID), p.id)
}
func (p *Protocol) ConvertToLatest(pk packet.Packet, conn *minecraft.Conn) []packet.Packet {
return p.blockTranslator.UpgradeBlockPackets(
p.itemTranslator.UpgradeItemPackets(p.upgradePackets([]packet.Packet{pk}, conn), conn),
conn)
}
func (p *Protocol) ConvertFromLatest(pk packet.Packet, conn *minecraft.Conn) []packet.Packet {
return p.downgradePackets(p.blockTranslator.DowngradeBlockPackets(
p.itemTranslator.DowngradeItemPackets([]packet.Packet{pk}, conn),
conn), conn)
}
func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet {
for pkIndex, pk := range pks {
switch pk := pk.(type) {
case *packet.StartGame:
pk.GameVersion = p.ver
pk.BaseGameVersion = p.ver
case *packet.PlayerAuthInput:
inputData := pk.InputData
if p.ID() < proto.ID766 {
inputData = fitBitset(inputData, 64)
}
pks[pkIndex] = &legacypacket.PlayerAuthInput{
Pitch: pk.Pitch,
Yaw: pk.Yaw,
Position: pk.Position,
MoveVector: pk.MoveVector,
HeadYaw: pk.HeadYaw,
InputData: inputData,
InputMode: pk.InputMode,
PlayMode: pk.PlayMode,
InteractionModel: pk.InteractionModel,
InteractPitch: pk.InteractPitch,
InteractYaw: pk.InteractYaw,
Tick: pk.Tick,
Delta: pk.Delta,
ItemInteractionData: pk.ItemInteractionData,
ItemStackRequest: pk.ItemStackRequest,
BlockActions: pk.BlockActions,
VehicleRotation: pk.VehicleRotation,
ClientPredictedVehicle: pk.ClientPredictedVehicle,
AnalogueMoveVector: pk.AnalogueMoveVector,
CameraOrientation: pk.CameraOrientation,
RawMoveVector: pk.RawMoveVector,
}
case *packet.ItemStackResponse:
responses := make([]proto.ItemStackResponse, len(pk.Responses))
for i, r := range pk.Responses {
containerInfo := make([]proto.StackResponseContainerInfo, len(r.ContainerInfo))
for j, c := range r.ContainerInfo {
slotInfo := make([]proto.StackResponseSlotInfo, len(c.SlotInfo))
for k, s := range c.SlotInfo {
slotInfo[k] = proto.StackResponseSlotInfo{
Slot: s.Slot,
HotbarSlot: s.HotbarSlot,
Count: s.Count,
StackNetworkID: s.StackNetworkID,
CustomName: s.CustomName,
FilteredCustomName: s.FilteredCustomName,
DurabilityCorrection: s.DurabilityCorrection,
}
}
containerInfo[j] = proto.StackResponseContainerInfo{
Container: c.Container,
SlotInfo: slotInfo,
}
}
responses[i] = proto.ItemStackResponse{
Status: r.Status,
RequestID: r.RequestID,
ContainerInfo: containerInfo,
}
}
pks[pkIndex] = &legacypacket.ItemStackResponse{Responses: responses}
case *packet.ResourcePacksInfo:
texturePacks := make([]proto.TexturePackInfo, len(pk.TexturePacks))
for i, t := range pk.TexturePacks {
texturePacks[i] = proto.TexturePackInfo{
UUID: t.UUID,
Version: t.Version,
Size: t.Size,
ContentKey: t.ContentKey,
SubPackName: t.SubPackName,
ContentIdentity: t.ContentIdentity,
HasScripts: t.HasScripts,
AddonPack: t.AddonPack,
RTXEnabled: t.RTXEnabled,
DownloadURL: t.DownloadURL,
}
}
pks[pkIndex] = &legacypacket.ResourcePacksInfo{
TexturePackRequired: pk.TexturePackRequired,
HasAddons: pk.HasAddons,
HasScripts: pk.HasScripts,
WorldTemplateUUID: pk.WorldTemplateUUID,
WorldTemplateVersion: pk.WorldTemplateVersion,
TexturePacks: texturePacks,
}
}
}
return pks
}
func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet {
for pkIndex, pk := range pks {
switch pk := pk.(type) {
case *packet.StartGame:
pk.GameVersion = p.ver
pk.BaseGameVersion = p.ver
case *legacypacket.PlayerAuthInput:
pks[pkIndex] = &packet.PlayerAuthInput{
Pitch: pk.Pitch,
Yaw: pk.Yaw,
Position: pk.Position,
MoveVector: pk.MoveVector,
HeadYaw: pk.HeadYaw,
InputData: fitBitset(pk.InputData, packet.PlayerAuthInputBitsetSize),
InputMode: pk.InputMode,
PlayMode: pk.PlayMode,
InteractionModel: pk.InteractionModel,
InteractPitch: pk.InteractPitch,
InteractYaw: pk.InteractYaw,
Tick: pk.Tick,
Delta: pk.Delta,
ItemInteractionData: pk.ItemInteractionData,
ItemStackRequest: pk.ItemStackRequest,
BlockActions: pk.BlockActions,
VehicleRotation: pk.VehicleRotation,
ClientPredictedVehicle: pk.ClientPredictedVehicle,
AnalogueMoveVector: pk.AnalogueMoveVector,
CameraOrientation: pk.CameraOrientation,
RawMoveVector: pk.RawMoveVector,
}
case *legacypacket.ItemStackResponse:
responses := make([]protocol.ItemStackResponse, len(pk.Responses))
for i, r := range pk.Responses {
responses[i] = r.ToLatest()
}
pks[pkIndex] = &packet.ItemStackResponse{Responses: responses}
case *legacypacket.ResourcePacksInfo:
texturePacks := make([]protocol.TexturePackInfo, len(pk.TexturePacks))
for i, t := range pk.TexturePacks {
texturePacks[i] = t.ToLatest()
}
pks[pkIndex] = &packet.ResourcePacksInfo{
TexturePackRequired: pk.TexturePackRequired,
HasAddons: pk.HasAddons,
HasScripts: pk.HasScripts,
WorldTemplateUUID: pk.WorldTemplateUUID,
WorldTemplateVersion: pk.WorldTemplateVersion,
TexturePacks: texturePacks,
}
}
}
return pks
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

36
legacyver/v748.go Normal file
View File

@@ -0,0 +1,36 @@
package legacyver
import (
_ "embed"
"github.com/akmalfairuz/legacy-version/internal/chunk"
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/akmalfairuz/legacy-version/mapping"
)
const (
// ItemVersion748 ...
ItemVersion748 = 221
// BlockVersion748 ...
BlockVersion748 int32 = (1 << 24) | (21 << 16) | (40 << 8)
)
var (
//go:embed item_runtime_ids_748.nbt
itemRuntimeIDData748 []byte
//go:embed required_item_list_748.json
requiredItemList748 []byte
//go:embed block_states_748.nbt
blockStateData748 []byte
)
func New748(direct bool) *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData748, requiredItemList748, ItemVersion748, false)
blockMapping := mapping.NewBlockMapping(blockStateData748)
return &Protocol{
ver: "1.21.40",
id: proto.ID748,
blockTranslator: NewBlockTranslator(blockMapping, latestBlockMapping, chunk.NewNetworkPersistentEncoding(blockMapping, BlockVersion748), chunk.NewBlockPaletteEncoding(blockMapping, BlockVersion748), false),
itemTranslator: NewItemTranslator(itemMapping, itemMappingLatest, blockMapping, blockMappingLatest),
}
}

25
legacyver/v766.go Normal file
View File

@@ -0,0 +1,25 @@
package legacyver
import (
_ "embed"
"github.com/akmalfairuz/legacy-version/mapping"
)
const (
// ItemVersion766 ...
ItemVersion766 = 231
// BlockVersion766 ...
BlockVersion766 int32 = (1 << 24) | (21 << 16) | (50 << 8)
)
var (
//go:embed item_runtime_ids_766.nbt
itemRuntimeIDData766 []byte
//go:embed required_item_list_766.json
requiredItemList766 []byte
//go:embed block_states_766.nbt
blockStateData766 []byte
itemMappingLatest = mapping.NewItemMapping(itemRuntimeIDData766, requiredItemList766, ItemVersion766, false)
blockMappingLatest = mapping.NewBlockMapping(blockStateData766)
)