Add 1.21.20

This commit is contained in:
AkmalFairuz
2025-01-01 12:34:03 +07:00
parent 93821ab4c6
commit 9677eef999
19 changed files with 7267 additions and 60 deletions

View File

@@ -8,7 +8,7 @@ A gophertunnel protocol interface implementation to support older Minecraft Bedr
| 766 | 1.21.50 | ✅ | | 766 | 1.21.50 | ✅ |
| 748 | 1.21.40 | ✅ | | 748 | 1.21.40 | ✅ |
| 729 | 1.21.30 | ✅ | | 729 | 1.21.30 | ✅ |
| 712 | 1.21.20 | 🚧 | | 712 | 1.21.20 | |
| 686 | 1.21.2 | 🚧 | | 686 | 1.21.2 | 🚧 |
| 685 | 1.21.0 | 🚧 | | 685 | 1.21.0 | 🚧 |

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// ContainerRegistryCleanup is sent by the server to trigger a client-side cleanup of the dynamic container
// registry.
type ContainerRegistryCleanup struct {
// RemovedContainers is a list of protocol.FullContainerName's that should be removed from the client-side
// container registry.
RemovedContainers []proto.FullContainerName
}
// ID ...
func (*ContainerRegistryCleanup) ID() uint32 {
return packet.IDContainerRegistryCleanup
}
func (pk *ContainerRegistryCleanup) Marshal(io protocol.IO) {
protocol.Slice(io, &pk.RemovedContainers)
}

View File

@@ -0,0 +1,49 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
const (
EmoteFlagServerSide = 1 << iota
EmoteFlagMuteChat
)
// Emote is sent by both the server and the client. When the client sends an emote, it sends this packet to
// the server, after which the server will broadcast the packet to other players online.
type Emote struct {
// EntityRuntimeID is the entity that sent the emote. When a player sends this packet, it has this field
// set as its own entity runtime ID.
EntityRuntimeID uint64
// EmoteLength is the number of ticks that the emote lasts for.
EmoteLength uint32
// EmoteID is the ID of the emote to send.
EmoteID string
// XUID is the Xbox User ID of the player that sent the emote. It is only set when the emote is used by a player that
// is authenticated with Xbox Live.
XUID string
// PlatformID is an identifier only set for particular platforms when using an emote (presumably only for Nintendo
// Switch). It is otherwise an empty string, and is used to decide which players are able to emote with each other.
PlatformID string
// Flags is a combination of flags that change the way the Emote packet operates. When the server sends
// this packet to other players, EmoteFlagServerSide must be present.
Flags byte
}
// ID ...
func (*Emote) ID() uint32 {
return packet.IDEmote
}
func (pk *Emote) Marshal(io protocol.IO) {
io.Varuint64(&pk.EntityRuntimeID)
io.String(&pk.EmoteID)
if proto.IsProtoGTE(io, proto.ID729) {
io.Varuint32(&pk.EmoteLength)
}
io.String(&pk.XUID)
io.String(&pk.PlatformID)
io.Uint8(&pk.Flags)
}

View File

@@ -17,7 +17,7 @@ type InventoryContent struct {
// the inventory window updated. // the inventory window updated.
Content []protocol.ItemInstance Content []protocol.ItemInstance
// Container is the protocol.FullContainerName that describes the container that the content is for. // Container is the protocol.FullContainerName that describes the container that the content is for.
Container protocol.FullContainerName Container proto.FullContainerName
// DynamicContainerSize ... // DynamicContainerSize ...
DynamicContainerSize uint32 DynamicContainerSize uint32
// StorageItem is the item that is acting as the storage container for the inventory. If the inventory is // StorageItem is the item that is acting as the storage container for the inventory. If the inventory is
@@ -34,7 +34,9 @@ func (*InventoryContent) ID() uint32 {
func (pk *InventoryContent) Marshal(io protocol.IO) { func (pk *InventoryContent) Marshal(io protocol.IO) {
io.Varuint32(&pk.WindowID) io.Varuint32(&pk.WindowID)
protocol.FuncSlice(io, &pk.Content, io.ItemInstance) protocol.FuncSlice(io, &pk.Content, io.ItemInstance)
if proto.IsProtoGTE(io, proto.ID729) {
protocol.Single(io, &pk.Container) protocol.Single(io, &pk.Container)
}
if proto.IsProtoGTE(io, proto.ID748) { if proto.IsProtoGTE(io, proto.ID748) {
io.ItemInstance(&pk.StorageItem) io.ItemInstance(&pk.StorageItem)
} else { } else {

View File

@@ -17,7 +17,7 @@ type InventorySlot struct {
// index. // index.
Slot uint32 Slot uint32
// Container is the protocol.FullContainerName that describes the container that the content is for. // Container is the protocol.FullContainerName that describes the container that the content is for.
Container protocol.FullContainerName Container proto.FullContainerName
// DynamicContainerSize ... // DynamicContainerSize ...
DynamicContainerSize uint32 DynamicContainerSize uint32
// StorageItem is the item that is acting as the storage container for the inventory. If the inventory is // StorageItem is the item that is acting as the storage container for the inventory. If the inventory is
@@ -37,7 +37,9 @@ func (*InventorySlot) ID() uint32 {
func (pk *InventorySlot) Marshal(io protocol.IO) { func (pk *InventorySlot) Marshal(io protocol.IO) {
io.Varuint32(&pk.WindowID) io.Varuint32(&pk.WindowID)
io.Varuint32(&pk.Slot) io.Varuint32(&pk.Slot)
if proto.IsProtoGTE(io, proto.ID729) {
protocol.Single(io, &pk.Container) protocol.Single(io, &pk.Container)
}
if proto.IsProtoGTE(io, proto.ID748) { if proto.IsProtoGTE(io, proto.ID748) {
io.ItemInstance(&pk.StorageItem) io.ItemInstance(&pk.StorageItem)
} else { } else {

View File

@@ -27,6 +27,10 @@ type ResourcePacksInfo struct {
// WorldTemplateVersion is the version of the world template that has been used to generate the world. If // 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. // the world was not generated from a template, this field is empty.
WorldTemplateVersion string WorldTemplateVersion string
// ForcingServerPacks ...
ForcingServerPacks bool
// BehaviourPacks ...
BehaviourPacks []proto.BehaviourPackInfo
// TexturePacks is a list of texture packs that the client needs to download before joining the server. // 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 // The order of these texture packs is not relevant in this packet. It is however important in the
// ResourcePackStack packet. // ResourcePackStack packet.
@@ -49,6 +53,12 @@ func (pk *ResourcePacksInfo) Marshal(io protocol.IO) {
io.UUID(&pk.WorldTemplateUUID) io.UUID(&pk.WorldTemplateUUID)
io.String(&pk.WorldTemplateVersion) io.String(&pk.WorldTemplateVersion)
} }
if proto.IsProtoLT(io, proto.ID729) {
io.Bool(&pk.ForcingServerPacks)
protocol.SliceUint16Length(io, &pk.BehaviourPacks)
} else {
proto.EmptySlice(io, &pk.BehaviourPacks)
}
protocol.SliceUint16Length(io, &pk.TexturePacks) protocol.SliceUint16Length(io, &pk.TexturePacks)
if proto.IsProtoLT(io, proto.ID748) { if proto.IsProtoLT(io, proto.ID748) {
protocol.Slice(io, &pk.PackURLs) protocol.Slice(io, &pk.PackURLs)

View File

@@ -0,0 +1,31 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// Transfer is sent by the server to transfer a player from the current server to another. Doing so will
// fully disconnect the client, bring it back to the main menu and make it connect to the next server.
type Transfer struct {
// Address is the address of the new server, which might be either a hostname or an actual IP address.
Address string
// Port is the UDP port of the new server.
Port uint16
// ReloadWorld currently has an unknown usage.
ReloadWorld bool
}
// ID ...
func (*Transfer) ID() uint32 {
return packet.IDTransfer
}
func (pk *Transfer) Marshal(io protocol.IO) {
io.String(&pk.Address)
io.Uint16(&pk.Port)
if proto.IsProtoGTE(io, proto.ID729) {
io.Bool(&pk.ReloadWorld)
}
}

View File

@@ -0,0 +1,32 @@
package legacypacket
import (
"github.com/akmalfairuz/legacy-version/legacyver/proto"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
// UpdateAttributes is sent by the server to update an amount of attributes of any entity in the world. These
// attributes include ones such as the health or the movement speed of the entity.
type UpdateAttributes struct {
// EntityRuntimeID is the runtime ID of the entity. The runtime ID is unique for each world session, and
// entities are generally identified in packets using this runtime ID.
EntityRuntimeID uint64
// Attributes is a slice of new attributes that the entity gets. It includes attributes such as its
// health, movement speed, etc. Note that only changed attributes have to be sent in this packet. It is
// not required to send attributes that did not have their values changed.
Attributes []proto.Attribute
// Tick is the server tick at which the packet was sent. It is used in relation to CorrectPlayerMovePrediction.
Tick uint64
}
// ID ...
func (*UpdateAttributes) ID() uint32 {
return packet.IDUpdateAttributes
}
func (pk *UpdateAttributes) Marshal(io protocol.IO) {
io.Varuint64(&pk.EntityRuntimeID)
protocol.Slice(io, &pk.Attributes)
io.Varuint64(&pk.Tick)
}

View File

@@ -0,0 +1,58 @@
package proto
import "github.com/sandertv/gophertunnel/minecraft/protocol"
// Attribute is an entity attribute, that holds specific data such as the health of the entity. Each attribute
// holds a default value, maximum and minimum value, name and its current value.
type Attribute struct {
protocol.AttributeValue
// DefaultMin is the default minimum value of the attribute. It's not clear why this field must be sent to
// the client, but it is required regardless.
DefaultMin float32
// DefaultMax is the default maximum value of the attribute. It's not clear why this field must be sent to
// the client, but it is required regardless.
DefaultMax float32
// Default is the default value of the attribute. It's not clear why this field must be sent to the
// client, but it is required regardless.
Default float32
// Modifiers is a slice of AttributeModifiers that are applied to the attribute.
Modifiers []protocol.AttributeModifier
}
func (x *Attribute) FromLatest(v protocol.Attribute) Attribute {
x.AttributeValue = v.AttributeValue
x.DefaultMin = v.DefaultMin
x.DefaultMax = v.DefaultMax
x.Default = v.Default
x.Modifiers = v.Modifiers
return *x
}
func (x *Attribute) ToLatest() protocol.Attribute {
return protocol.Attribute{
AttributeValue: protocol.AttributeValue{
Name: x.Name,
Value: x.Value,
Max: x.Max,
Min: x.Min,
},
DefaultMin: x.DefaultMin,
DefaultMax: x.DefaultMax,
Default: x.Default,
Modifiers: x.Modifiers,
}
}
// Marshal encodes/decodes an Attribute.
func (x *Attribute) Marshal(r protocol.IO) {
r.Float32(&x.Min)
r.Float32(&x.Max)
r.Float32(&x.Value)
if IsProtoGTE(r, ID729) {
r.Float32(&x.DefaultMin)
r.Float32(&x.DefaultMax)
}
r.Float32(&x.Default)
r.String(&x.Name)
protocol.Slice(r, &x.Modifiers)
}

View File

@@ -53,7 +53,7 @@ type CameraPreset struct {
AimAssist protocol.Optional[protocol.CameraPresetAimAssist] AimAssist protocol.Optional[protocol.CameraPresetAimAssist]
} }
func (x *CameraPreset) FromLatest(cp protocol.CameraPreset) { func (x *CameraPreset) FromLatest(cp protocol.CameraPreset) CameraPreset {
x.Name = cp.Name x.Name = cp.Name
x.Parent = cp.Parent x.Parent = cp.Parent
x.PosX = cp.PosX x.PosX = cp.PosX
@@ -74,6 +74,7 @@ func (x *CameraPreset) FromLatest(cp protocol.CameraPreset) {
x.PlayerEffects = cp.PlayerEffects x.PlayerEffects = cp.PlayerEffects
x.AlignTargetAndCameraForward = cp.AlignTargetAndCameraForward x.AlignTargetAndCameraForward = cp.AlignTargetAndCameraForward
x.AimAssist = cp.AimAssist x.AimAssist = cp.AimAssist
return *x
} }
func (x *CameraPreset) ToLatest() protocol.CameraPreset { func (x *CameraPreset) ToLatest() protocol.CameraPreset {
@@ -110,8 +111,10 @@ func (x *CameraPreset) Marshal(r protocol.IO) {
protocol.OptionalFunc(r, &x.PosZ, r.Float32) protocol.OptionalFunc(r, &x.PosZ, r.Float32)
protocol.OptionalFunc(r, &x.RotX, r.Float32) protocol.OptionalFunc(r, &x.RotX, r.Float32)
protocol.OptionalFunc(r, &x.RotY, r.Float32) protocol.OptionalFunc(r, &x.RotY, r.Float32)
if IsProtoGTE(r, ID729) {
protocol.OptionalFunc(r, &x.RotationSpeed, r.Float32) protocol.OptionalFunc(r, &x.RotationSpeed, r.Float32)
protocol.OptionalFunc(r, &x.SnapToTarget, r.Bool) protocol.OptionalFunc(r, &x.SnapToTarget, r.Bool)
}
if IsProtoGTE(r, ID748) { if IsProtoGTE(r, ID748) {
protocol.OptionalFunc(r, &x.HorizontalRotationLimit, r.Vec2) protocol.OptionalFunc(r, &x.HorizontalRotationLimit, r.Vec2)
protocol.OptionalFunc(r, &x.VerticalRotationLimit, r.Vec2) protocol.OptionalFunc(r, &x.VerticalRotationLimit, r.Vec2)
@@ -121,7 +124,9 @@ func (x *CameraPreset) Marshal(r protocol.IO) {
protocol.OptionalFunc(r, &x.TrackingRadius, r.Float32) protocol.OptionalFunc(r, &x.TrackingRadius, r.Float32)
} }
protocol.OptionalFunc(r, &x.ViewOffset, r.Vec2) protocol.OptionalFunc(r, &x.ViewOffset, r.Vec2)
if IsProtoGTE(r, ID729) {
protocol.OptionalFunc(r, &x.EntityOffset, r.Vec3) protocol.OptionalFunc(r, &x.EntityOffset, r.Vec3)
}
protocol.OptionalFunc(r, &x.Radius, r.Float32) protocol.OptionalFunc(r, &x.Radius, r.Float32)
protocol.OptionalFunc(r, &x.AudioListener, r.Uint8) protocol.OptionalFunc(r, &x.AudioListener, r.Uint8)
protocol.OptionalFunc(r, &x.PlayerEffects, r.Bool) protocol.OptionalFunc(r, &x.PlayerEffects, r.Bool)

View File

@@ -0,0 +1,38 @@
package proto
import "github.com/sandertv/gophertunnel/minecraft/protocol"
// FullContainerName contains information required to identify a container in a StackRequestSlotInfo.
type FullContainerName struct {
// ContainerID is the ID of the container that the slot was in.
ContainerID byte
// DynamicContainerID is the ID of the container if it is dynamic. If the container is not dynamic, this
// field should be left empty. A non-optional value of 0 is assumed to be non-empty.
DynamicContainerID protocol.Optional[uint32]
}
func (x *FullContainerName) FromLatest(v protocol.FullContainerName) FullContainerName {
x.ContainerID = v.ContainerID
x.DynamicContainerID = v.DynamicContainerID
return *x
}
func (x *FullContainerName) ToLatest() protocol.FullContainerName {
return protocol.FullContainerName{
ContainerID: x.ContainerID,
DynamicContainerID: x.DynamicContainerID,
}
}
func (x *FullContainerName) Marshal(r protocol.IO) {
r.Uint8(&x.ContainerID)
if IsProtoGTE(r, ID729) {
protocol.OptionalFunc(r, &x.DynamicContainerID, r.Uint32)
} else {
dynamicContainerID, _ := x.DynamicContainerID.Value()
r.Uint32(&dynamicContainerID)
if dynamicContainerID != 0 {
x.DynamicContainerID = protocol.Option(dynamicContainerID)
}
}
}

View File

@@ -3,12 +3,12 @@ package proto
import "github.com/sandertv/gophertunnel/minecraft/protocol" import "github.com/sandertv/gophertunnel/minecraft/protocol"
const ( const (
ID766 = 766 ID766 = 766 // v1.21.50
ID748 = 748 ID748 = 748 // v1.21.40
ID729 = 729 ID729 = 729 // v1.21.30
ID712 = 712 ID712 = 712 // v1.21.20
ID686 = 686 ID686 = 686 // v1.21.2
ID685 = 685 ID685 = 685 // v1.21.0
) )
func IsProtoGTE(io protocol.IO, proto int32) bool { func IsProtoGTE(io protocol.IO, proto int32) bool {

View File

@@ -226,6 +226,32 @@ type ItemStackResponse struct {
ContainerInfo []StackResponseContainerInfo ContainerInfo []StackResponseContainerInfo
} }
func (x *ItemStackResponse) FromLatest(y protocol.ItemStackResponse) ItemStackResponse {
ret := ItemStackResponse{
Status: y.Status,
RequestID: y.RequestID,
ContainerInfo: make([]StackResponseContainerInfo, len(y.ContainerInfo)),
}
for i, v := range y.ContainerInfo {
ret.ContainerInfo[i] = StackResponseContainerInfo{
Container: (&FullContainerName{}).FromLatest(v.Container),
SlotInfo: make([]StackResponseSlotInfo, len(v.SlotInfo)),
}
for j, w := range v.SlotInfo {
ret.ContainerInfo[i].SlotInfo[j] = StackResponseSlotInfo{
Slot: w.Slot,
HotbarSlot: w.HotbarSlot,
Count: w.Count,
StackNetworkID: w.StackNetworkID,
CustomName: w.CustomName,
FilteredCustomName: w.FilteredCustomName,
DurabilityCorrection: w.DurabilityCorrection,
}
}
}
return ret
}
func (x *ItemStackResponse) ToLatest() protocol.ItemStackResponse { func (x *ItemStackResponse) ToLatest() protocol.ItemStackResponse {
ret := protocol.ItemStackResponse{ ret := protocol.ItemStackResponse{
Status: x.Status, Status: x.Status,
@@ -234,7 +260,7 @@ func (x *ItemStackResponse) ToLatest() protocol.ItemStackResponse {
} }
for i, v := range x.ContainerInfo { for i, v := range x.ContainerInfo {
ret.ContainerInfo[i] = protocol.StackResponseContainerInfo{ ret.ContainerInfo[i] = protocol.StackResponseContainerInfo{
Container: v.Container, Container: v.Container.ToLatest(),
SlotInfo: make([]protocol.StackResponseSlotInfo, len(v.SlotInfo)), SlotInfo: make([]protocol.StackResponseSlotInfo, len(v.SlotInfo)),
} }
for j, w := range v.SlotInfo { for j, w := range v.SlotInfo {
@@ -266,7 +292,7 @@ type StackResponseContainerInfo struct {
// Container is the FullContainerName that describes the container that the slots that follow are in. For // 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 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. // the crafting grid, this value seems to be 0x0d.
Container protocol.FullContainerName Container FullContainerName
// SlotInfo holds information on what item stack should be present in specific slots in the container. // SlotInfo holds information on what item stack should be present in specific slots in the container.
SlotInfo []StackResponseSlotInfo SlotInfo []StackResponseSlotInfo
} }
@@ -635,7 +661,7 @@ func (a *CraftResultsDeprecatedStackRequestAction) Marshal(r protocol.IO) {
// StackRequestSlotInfo holds information on a specific slot client-side. // StackRequestSlotInfo holds information on a specific slot client-side.
type StackRequestSlotInfo struct { type StackRequestSlotInfo struct {
// Container is the FullContainerName that describes the container that the slot is in. // Container is the FullContainerName that describes the container that the slot is in.
Container protocol.FullContainerName Container FullContainerName
// Slot is the index of the slot within the container with the ContainerID above. // Slot is the index of the slot within the container with the ContainerID above.
Slot byte Slot byte
// StackNetworkID is the unique stack ID that the client assumes to be present in this slot. The server // StackNetworkID is the unique stack ID that the client assumes to be present in this slot. The server

View File

@@ -38,6 +38,20 @@ type TexturePackInfo struct {
DownloadURL string DownloadURL string
} }
func (x *TexturePackInfo) FromLatest(y protocol.TexturePackInfo) TexturePackInfo {
x.UUID = y.UUID
x.Version = y.Version
x.Size = y.Size
x.ContentKey = y.ContentKey
x.SubPackName = y.SubPackName
x.ContentIdentity = y.ContentIdentity
x.HasScripts = y.HasScripts
x.AddonPack = y.AddonPack
x.RTXEnabled = y.RTXEnabled
x.DownloadURL = y.DownloadURL
return *x
}
func (x *TexturePackInfo) ToLatest() protocol.TexturePackInfo { func (x *TexturePackInfo) ToLatest() protocol.TexturePackInfo {
return protocol.TexturePackInfo{ return protocol.TexturePackInfo{
UUID: x.UUID, UUID: x.UUID,
@@ -79,3 +93,42 @@ func (x *TexturePackInfo) Marshal(r protocol.IO) {
r.String(&x.DownloadURL) r.String(&x.DownloadURL)
} }
} }
// BehaviourPackInfo represents a behaviour pack's info sent over network. It holds information about the
// behaviour pack such as its name, description and version.
type BehaviourPackInfo struct {
// UUID is the UUID of the behaviour pack. Each behaviour pack downloaded must have a different UUID in
// order for the client to be able to handle them properly.
UUID string
// Version is the version of the behaviour pack. The client will cache behaviour packs sent by the server as
// long as they carry the same version. Sending a behaviour 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 behaviour pack occupies. This is the size of the compressed
// archive (zip) of the behaviour pack.
Size uint64
// ContentKey is the key used to decrypt the behaviour pack if it is encrypted. This is generally the case
// for marketplace behaviour packs.
ContentKey string
// SubPackName ...
SubPackName string
// ContentIdentity ...
ContentIdentity string
// HasScripts specifies if the behaviour 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
}
// Marshal encodes/decodes a BehaviourPackInfo.
func (x *BehaviourPackInfo) Marshal(r protocol.IO) {
r.String(&x.UUID)
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)
}

View File

@@ -28,6 +28,10 @@ func convertPacketFunc(pid uint32, cur func() packet.Packet) func() packet.Packe
return func() packet.Packet { return &legacypacket.CameraAimAssist{} } return func() packet.Packet { return &legacypacket.CameraAimAssist{} }
case packet.IDCameraPresets: case packet.IDCameraPresets:
return func() packet.Packet { return &legacypacket.CameraPresets{} } return func() packet.Packet { return &legacypacket.CameraPresets{} }
case packet.IDContainerRegistryCleanup:
return func() packet.Packet { return &legacypacket.ContainerRegistryCleanup{} }
case packet.IDEmote:
return func() packet.Packet { return &legacypacket.Emote{} }
case packet.IDInventoryContent: case packet.IDInventoryContent:
return func() packet.Packet { return &legacypacket.InventoryContent{} } return func() packet.Packet { return &legacypacket.InventoryContent{} }
case packet.IDInventorySlot: case packet.IDInventorySlot:
@@ -40,6 +44,10 @@ func convertPacketFunc(pid uint32, cur func() packet.Packet) func() packet.Packe
return func() packet.Packet { return &legacypacket.PlayerAuthInput{} } return func() packet.Packet { return &legacypacket.PlayerAuthInput{} }
case packet.IDResourcePacksInfo: case packet.IDResourcePacksInfo:
return func() packet.Packet { return &legacypacket.ResourcePacksInfo{} } return func() packet.Packet { return &legacypacket.ResourcePacksInfo{} }
case packet.IDTransfer:
return func() packet.Packet { return &legacypacket.Transfer{} }
case packet.IDUpdateAttributes:
return func() packet.Packet { return &legacypacket.UpdateAttributes{} }
default: default:
return cur return cur
} }
@@ -94,7 +102,7 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
case *packet.CameraPresets: case *packet.CameraPresets:
presets := make([]proto.CameraPreset, len(pk.Presets)) presets := make([]proto.CameraPreset, len(pk.Presets))
for i, p := range pk.Presets { for i, p := range pk.Presets {
presets[i].FromLatest(p) presets[i] = (&proto.CameraPreset{}).FromLatest(p)
} }
pks[pkIndex] = &legacypacket.CameraPresets{ pks[pkIndex] = &legacypacket.CameraPresets{
Presets: presets, Presets: presets,
@@ -133,49 +141,14 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
case *packet.ItemStackResponse: case *packet.ItemStackResponse:
responses := make([]proto.ItemStackResponse, len(pk.Responses)) responses := make([]proto.ItemStackResponse, len(pk.Responses))
for i, r := range pk.Responses { for i, r := range pk.Responses {
containerInfo := make([]proto.StackResponseContainerInfo, len(r.ContainerInfo)) responses[i] = (&proto.ItemStackResponse{}).FromLatest(r)
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} pks[pkIndex] = &legacypacket.ItemStackResponse{Responses: responses}
case *packet.ResourcePacksInfo: case *packet.ResourcePacksInfo:
texturePacks := make([]proto.TexturePackInfo, len(pk.TexturePacks)) texturePacks := make([]proto.TexturePackInfo, len(pk.TexturePacks))
packURLs := make([]protocol.PackURL, 0) packURLs := make([]protocol.PackURL, 0)
for i, t := range pk.TexturePacks { for i, t := range pk.TexturePacks {
texturePacks[i] = proto.TexturePackInfo{ texturePacks[i] = (&proto.TexturePackInfo{}).FromLatest(t)
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,
}
if t.DownloadURL != "" { if t.DownloadURL != "" {
packURLs = append(packURLs, protocol.PackURL{ packURLs = append(packURLs, protocol.PackURL{
UUIDVersion: t.UUID.String() + "_" + t.Version, UUIDVersion: t.UUID.String() + "_" + t.Version,
@@ -196,7 +169,7 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
pks[pkIndex] = &legacypacket.InventorySlot{ pks[pkIndex] = &legacypacket.InventorySlot{
WindowID: pk.WindowID, WindowID: pk.WindowID,
Slot: pk.Slot, Slot: pk.Slot,
Container: pk.Container, Container: (&proto.FullContainerName{}).FromLatest(pk.Container),
DynamicContainerSize: 0, DynamicContainerSize: 0,
StorageItem: pk.StorageItem, StorageItem: pk.StorageItem,
NewItem: pk.NewItem, NewItem: pk.NewItem,
@@ -205,7 +178,7 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
pks[pkIndex] = &legacypacket.InventoryContent{ pks[pkIndex] = &legacypacket.InventoryContent{
WindowID: pk.WindowID, WindowID: pk.WindowID,
Content: pk.Content, Content: pk.Content,
Container: pk.Container, Container: (&proto.FullContainerName{}).FromLatest(pk.Container),
DynamicContainerSize: 0, DynamicContainerSize: 0,
StorageItem: pk.StorageItem, StorageItem: pk.StorageItem,
} }
@@ -227,6 +200,39 @@ func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) [
TargetMode: pk.TargetMode, TargetMode: pk.TargetMode,
Action: pk.Action, Action: pk.Action,
} }
case *packet.UpdateAttributes:
attributes := make([]proto.Attribute, len(pk.Attributes))
for i, a := range pk.Attributes {
attributes[i] = (&proto.Attribute{}).FromLatest(a)
}
pks[pkIndex] = &legacypacket.UpdateAttributes{
EntityRuntimeID: pk.EntityRuntimeID,
Attributes: attributes,
Tick: pk.Tick,
}
case *packet.ContainerRegistryCleanup:
removedContainers := make([]proto.FullContainerName, len(pk.RemovedContainers))
for i, c := range pk.RemovedContainers {
removedContainers[i] = (&proto.FullContainerName{}).FromLatest(c)
}
pks[pkIndex] = &legacypacket.ContainerRegistryCleanup{
RemovedContainers: removedContainers,
}
case *packet.Emote:
pks[pkIndex] = &legacypacket.Emote{
EntityRuntimeID: pk.EntityRuntimeID,
EmoteLength: pk.EmoteLength,
EmoteID: pk.EmoteID,
XUID: pk.XUID,
PlatformID: pk.PlatformID,
Flags: pk.Flags,
}
case *packet.Transfer:
pks[pkIndex] = &legacypacket.Transfer{
Address: pk.Address,
Port: pk.Port,
ReloadWorld: pk.ReloadWorld,
}
} }
} }
@@ -304,7 +310,7 @@ func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []p
pks[pkIndex] = &packet.InventorySlot{ pks[pkIndex] = &packet.InventorySlot{
WindowID: pk.WindowID, WindowID: pk.WindowID,
Slot: pk.Slot, Slot: pk.Slot,
Container: pk.Container, Container: pk.Container.ToLatest(),
StorageItem: pk.StorageItem, StorageItem: pk.StorageItem,
NewItem: pk.NewItem, NewItem: pk.NewItem,
} }
@@ -312,7 +318,7 @@ func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []p
pks[pkIndex] = &packet.InventoryContent{ pks[pkIndex] = &packet.InventoryContent{
WindowID: pk.WindowID, WindowID: pk.WindowID,
Content: pk.Content, Content: pk.Content,
Container: pk.Container, Container: pk.Container.ToLatest(),
StorageItem: pk.StorageItem, StorageItem: pk.StorageItem,
} }
case *legacypacket.MobEffect: case *legacypacket.MobEffect:
@@ -333,6 +339,39 @@ func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []p
TargetMode: pk.TargetMode, TargetMode: pk.TargetMode,
Action: pk.Action, Action: pk.Action,
} }
case *legacypacket.UpdateAttributes:
attributes := make([]protocol.Attribute, len(pk.Attributes))
for i, a := range pk.Attributes {
attributes[i] = a.ToLatest()
}
pks[pkIndex] = &packet.UpdateAttributes{
EntityRuntimeID: pk.EntityRuntimeID,
Attributes: attributes,
Tick: pk.Tick,
}
case *legacypacket.ContainerRegistryCleanup:
removedContainers := make([]protocol.FullContainerName, len(pk.RemovedContainers))
for i, c := range pk.RemovedContainers {
removedContainers[i] = c.ToLatest()
}
pks[pkIndex] = &packet.ContainerRegistryCleanup{
RemovedContainers: removedContainers,
}
case *legacypacket.Emote:
pks[pkIndex] = &packet.Emote{
EntityRuntimeID: pk.EntityRuntimeID,
EmoteLength: pk.EmoteLength,
EmoteID: pk.EmoteID,
XUID: pk.XUID,
PlatformID: pk.PlatformID,
Flags: pk.Flags,
}
case *legacypacket.Transfer:
pks[pkIndex] = &packet.Transfer{
Address: pk.Address,
Port: pk.Port,
ReloadWorld: pk.ReloadWorld,
}
} }
} }
return pks return pks

36
legacyver/v712.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 (
// ItemVersion712 ...
ItemVersion712 = 201
// BlockVersion712 ...
BlockVersion712 int32 = (1 << 24) | (21 << 16) | (20 << 8)
)
var (
//go:embed data/item_runtime_ids_712.nbt
itemRuntimeIDData712 []byte
//go:embed data/required_item_list_712.json
requiredItemList712 []byte
//go:embed data/block_states_712.nbt
blockStateData712 []byte
)
func New712(direct bool) *Protocol {
itemMapping := mapping.NewItemMapping(itemRuntimeIDData712, requiredItemList712, ItemVersion712, false)
blockMapping := mapping.NewBlockMapping(blockStateData712)
return &Protocol{
ver: "1.21.20",
id: proto.ID712,
blockTranslator: NewBlockTranslator(blockMapping, latestBlockMapping, chunk.NewNetworkPersistentEncoding(blockMapping, BlockVersion712), chunk.NewBlockPaletteEncoding(blockMapping, BlockVersion712), false),
itemTranslator: NewItemTranslator(itemMapping, itemMappingLatest, blockMapping, blockMappingLatest),
}
}